From b57c72beb3cbb28655fd6f3b0a12042c827c1f4f Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 28 Feb 2024 14:51:30 -0800 Subject: [PATCH 01/82] Phase 1 of modernize our static variable handling - Delete DomainLocalModule and ThreadLocalModule - Replumb the JIT to use a new set of helpers (in progress) - Allocate static data on a per type basis instead of a per module basis - Thread Local Statics are now stored in the same structures that the JIT can optimize (in progress) - More scenarios can support pre-init, notably support for pre-init for cases with valuetype statics, but no Cctor - Remove ModuleForStatics concept - Remove ModuleId concept - Remove ModuleIndex concept - Remove ClassDomainID concept Work still to be done 1. Finish support for R2R, and see if we can make it backcompat with the old R2R version 2. Support for the more optimized helpers (dynamic and pinned) 3. Re-enable jit helper expansions 4. Make sure SOS and the debugger continue to work --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 12 +- src/coreclr/debug/daccess/request.cpp | 70 +- src/coreclr/inc/CrstTypes.def | 4 + src/coreclr/inc/corcompile.h | 2 - src/coreclr/inc/corinfo.h | 59 +- src/coreclr/inc/crsttypes_generated.h | 27 +- src/coreclr/inc/dacvars.h | 3 +- src/coreclr/inc/enum_class_flags.h | 6 + src/coreclr/inc/icorjitinfoimpl_generated.h | 12 +- src/coreclr/inc/jiteeversionguid.h | 10 +- src/coreclr/inc/jithelpers.h | 42 +- src/coreclr/inc/readytorunhelpers.h | 8 +- src/coreclr/jit/ICorJitInfo_names_generated.h | 2 - .../jit/ICorJitInfo_wrapper_generated.hpp | 26 +- src/coreclr/jit/compiler.cpp | 2 +- src/coreclr/jit/compiler.hpp | 30 +- src/coreclr/jit/emit.cpp | 4 +- src/coreclr/jit/flowgraph.cpp | 85 +- src/coreclr/jit/helperexpansion.cpp | 9 +- src/coreclr/jit/importer.cpp | 14 +- src/coreclr/jit/utils.cpp | 42 +- src/coreclr/jit/valuenum.cpp | 85 +- src/coreclr/jit/valuenumfuncs.h | 41 +- .../Common/JitInterface/CorInfoHelpFunc.cs | 43 +- .../tools/Common/JitInterface/CorInfoImpl.cs | 14 +- .../JitInterface/CorInfoImpl_generated.cs | 282 +++--- .../tools/Common/JitInterface/CorInfoTypes.cs | 2 - .../ThunkGenerator/ThunkInput.txt | 4 +- .../ReadyToRunMetadataFieldLayoutAlgorithm.cs | 228 +---- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 16 +- .../aot/jitinterface/jitinterface_generated.h | 30 +- .../tools/superpmi/superpmi-shared/agnostic.h | 2 - .../tools/superpmi/superpmi-shared/lwmlist.h | 1 - .../superpmi-shared/methodcontext.cpp | 51 +- .../superpmi/superpmi-shared/methodcontext.h | 10 +- .../superpmi-shim-collector/icorjitinfo.cpp | 25 +- .../icorjitinfo_generated.cpp | 22 +- .../icorjitinfo_generated.cpp | 20 +- .../tools/superpmi/superpmi/icorjitinfo.cpp | 19 +- .../vm/amd64/JitHelpers_SingleAppDomain.asm | 42 +- src/coreclr/vm/amd64/JitHelpers_Slow.asm | 3 - src/coreclr/vm/amd64/asmconstants.h | 23 +- src/coreclr/vm/amd64/cgencpu.h | 8 +- src/coreclr/vm/appdomain.cpp | 394 +-------- src/coreclr/vm/appdomain.hpp | 415 +-------- src/coreclr/vm/appdomain.inl | 9 - src/coreclr/vm/array.cpp | 4 +- src/coreclr/vm/ceeload.cpp | 604 +------------ src/coreclr/vm/ceeload.h | 106 +-- src/coreclr/vm/ceeload.inl | 15 - src/coreclr/vm/ceemain.cpp | 19 +- src/coreclr/vm/class.h | 19 - src/coreclr/vm/class.inl | 1 - src/coreclr/vm/common.h | 1 - src/coreclr/vm/eventtrace_bulktype.cpp | 7 +- src/coreclr/vm/field.h | 26 +- src/coreclr/vm/gcenv.ee.cpp | 2 + src/coreclr/vm/generics.cpp | 15 +- src/coreclr/vm/jithelpers.cpp | 703 +++------------ src/coreclr/vm/jitinterface.cpp | 384 ++------- src/coreclr/vm/jitinterface.h | 82 +- src/coreclr/vm/loaderallocator.cpp | 34 + src/coreclr/vm/loaderallocator.hpp | 2 + src/coreclr/vm/method.hpp | 2 +- src/coreclr/vm/methodtable.cpp | 279 +++--- src/coreclr/vm/methodtable.h | 280 ++++-- src/coreclr/vm/methodtable.inl | 145 +--- src/coreclr/vm/methodtablebuilder.cpp | 81 +- src/coreclr/vm/methodtablebuilder.h | 2 +- src/coreclr/vm/prestub.cpp | 87 +- src/coreclr/vm/proftoeeinterfaceimpl.inl | 2 - src/coreclr/vm/threads.cpp | 53 +- src/coreclr/vm/threads.h | 77 +- src/coreclr/vm/threadstatics.cpp | 815 +++++++----------- src/coreclr/vm/threadstatics.h | 694 +++------------ src/coreclr/vm/vars.cpp | 2 - src/coreclr/vm/vars.hpp | 28 - 77 files changed, 1586 insertions(+), 5243 deletions(-) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 759c6998bb436..f9d362de4bcc4 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -1584,16 +1584,8 @@ void DacDbiInterfaceImpl::GetStaticsBases(TypeHandle thExact, PTR_BYTE * ppNonGCStaticsBase) { MethodTable * pMT = thExact.GetMethodTable(); - Module * pModuleForStatics = pMT->GetModuleForStatics(); - if (pModuleForStatics != NULL) - { - PTR_DomainLocalModule pLocalModule = pModuleForStatics->GetDomainLocalModule(); - if (pLocalModule != NULL) - { - *ppGCStaticsBase = pLocalModule->GetGCStaticsBasePointer(pMT); - *ppNonGCStaticsBase = pLocalModule->GetNonGCStaticsBasePointer(pMT); - } - } + *ppGCStaticsBase = pMT->GetGCStaticsBasePointer(); + *ppNonGCStaticsBase = pMT->GetNonGCStaticsBasePointer(); } // DacDbiInterfaceImpl::GetStaticsBases //----------------------------------------------------------------------------- diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 3df729cd8cf79..7136a25ae0c00 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -1640,8 +1640,8 @@ ClrDataAccess::GetModuleData(CLRDATA_ADDRESS addr, struct DacpModuleData *Module ModuleData->bIsReflection = pModule->IsReflection(); ModuleData->bIsPEFile = pModule->IsPEFile(); ModuleData->Assembly = HOST_CDADDR(pModule->GetAssembly()); - ModuleData->dwModuleID = pModule->GetModuleID(); - ModuleData->dwModuleIndex = pModule->GetModuleIndex().m_dwIndex; + ModuleData->dwModuleID = 0; // CoreCLR no longer has this concept + ModuleData->dwModuleIndex = 0; // CoreCLR no longer has this concept ModuleData->dwTransientFlags = pModule->m_dwTransientFlags; ModuleData->LoaderAllocator = HOST_CDADDR(pModule->m_loaderAllocator); ModuleData->ThunkHeap = HOST_CDADDR(pModule->m_pThunkHeap); @@ -3171,47 +3171,16 @@ ClrDataAccess::GetNestedExceptionData(CLRDATA_ADDRESS exception, CLRDATA_ADDRESS HRESULT ClrDataAccess::GetDomainLocalModuleData(CLRDATA_ADDRESS addr, struct DacpDomainLocalModuleData *pLocalModuleData) { - if (addr == 0 || pLocalModuleData == NULL) - return E_INVALIDARG; - - SOSDacEnter(); - - DomainLocalModule* pLocalModule = PTR_DomainLocalModule(TO_TADDR(addr)); - - pLocalModuleData->pGCStaticDataStart = TO_CDADDR(PTR_TO_TADDR(pLocalModule->GetPrecomputedGCStaticsBasePointer())); - pLocalModuleData->pNonGCStaticDataStart = TO_CDADDR(pLocalModule->GetPrecomputedNonGCStaticsBasePointer()); - pLocalModuleData->pDynamicClassTable = PTR_CDADDR(pLocalModule->m_pDynamicClassTable.Load()); - pLocalModuleData->pClassData = (TADDR) (PTR_HOST_MEMBER_TADDR(DomainLocalModule, pLocalModule, m_pDataBlob)); - - SOSDacLeave(); - return hr; + // CoreCLR does not use domain local modules anymore + return E_NOTIMPL; } HRESULT ClrDataAccess::GetDomainLocalModuleDataFromModule(CLRDATA_ADDRESS addr, struct DacpDomainLocalModuleData *pLocalModuleData) { - if (addr == 0 || pLocalModuleData == NULL) - return E_INVALIDARG; - - SOSDacEnter(); - - Module* pModule = PTR_Module(TO_TADDR(addr)); - DomainLocalModule* pLocalModule = PTR_DomainLocalModule(pModule->GetDomainLocalModule()); - if (!pLocalModule) - { - hr = E_INVALIDARG; - } - else - { - pLocalModuleData->pGCStaticDataStart = TO_CDADDR(PTR_TO_TADDR(pLocalModule->GetPrecomputedGCStaticsBasePointer())); - pLocalModuleData->pNonGCStaticDataStart = TO_CDADDR(pLocalModule->GetPrecomputedNonGCStaticsBasePointer()); - pLocalModuleData->pDynamicClassTable = PTR_CDADDR(pLocalModule->m_pDynamicClassTable.Load()); - pLocalModuleData->pClassData = (TADDR) (PTR_HOST_MEMBER_TADDR(DomainLocalModule, pLocalModule, m_pDataBlob)); - } - - SOSDacLeave(); - return hr; + // CoreCLR does not use domain local modules anymore + return E_NOTIMPL; } HRESULT @@ -3224,31 +3193,8 @@ ClrDataAccess::GetDomainLocalModuleDataFromAppDomain(CLRDATA_ADDRESS appDomainAd HRESULT ClrDataAccess::GetThreadLocalModuleData(CLRDATA_ADDRESS thread, unsigned int index, struct DacpThreadLocalModuleData *pLocalModuleData) { - if (pLocalModuleData == NULL) - return E_INVALIDARG; - - SOSDacEnter(); - - pLocalModuleData->threadAddr = thread; - pLocalModuleData->ModuleIndex = index; - - PTR_Thread pThread = PTR_Thread(TO_TADDR(thread)); - PTR_ThreadLocalBlock pLocalBlock = ThreadStatics::GetCurrentTLB(pThread); - PTR_ThreadLocalModule pLocalModule = pLocalBlock->GetTLMIfExists(ModuleIndex(index)); - if (!pLocalModule) - { - hr = E_INVALIDARG; - } - else - { - pLocalModuleData->pGCStaticDataStart = TO_CDADDR(PTR_TO_TADDR(pLocalModule->GetPrecomputedGCStaticsBasePointer())); - pLocalModuleData->pNonGCStaticDataStart = TO_CDADDR(pLocalModule->GetPrecomputedNonGCStaticsBasePointer()); - pLocalModuleData->pDynamicClassTable = PTR_CDADDR(pLocalModule->m_pDynamicClassTable); - pLocalModuleData->pClassData = (TADDR) (PTR_HOST_MEMBER_TADDR(ThreadLocalModule, pLocalModule, m_pDataBlob)); - } - - SOSDacLeave(); - return hr; + // CoreCLR does not use thread local modules anymore + return E_NOTIMPL; } diff --git a/src/coreclr/inc/CrstTypes.def b/src/coreclr/inc/CrstTypes.def index 3bccb73e03a59..d3930a8d214d3 100644 --- a/src/coreclr/inc/CrstTypes.def +++ b/src/coreclr/inc/CrstTypes.def @@ -479,6 +479,10 @@ End Crst SystemDomainDelayedUnloadList End +Crst ThreadLocalStorageLock + AcquiredBefore ModuleLookupTable +End + Crst ThreadIdDispenser End diff --git a/src/coreclr/inc/corcompile.h b/src/coreclr/inc/corcompile.h index e4baf3423fca2..e5214faaa76dd 100644 --- a/src/coreclr/inc/corcompile.h +++ b/src/coreclr/inc/corcompile.h @@ -163,8 +163,6 @@ enum CORCOMPILE_FIXUP_BLOB_KIND ENCODE_VERIFY_IL_BODY, /* Verify an IL body is defined the same at compile time and runtime. A failed match will cause a hard runtime failure. */ ENCODE_MODULE_HANDLE = 0x50, /* Module token */ - ENCODE_MODULE_ID_FOR_GENERIC_STATICS, /* For accessing static fields */ - ENCODE_CLASS_ID_FOR_STATICS, /* For accessing static fields */ ENCODE_SYNC_LOCK, /* For synchronizing access to a type */ ENCODE_PROFILING_HANDLE, /* For the method's profiling counter */ ENCODE_VARARGS_METHODDEF, /* For calling a varargs method */ diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 45b520935b043..59ca06ba49d62 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -528,28 +528,30 @@ enum CorInfoHelpFunc // ICorClassInfo::getSharedStaticsOrCCtorHelper to determine which helper to use // Helpers for regular statics - CORINFO_HELP_GETGENERICS_GCSTATIC_BASE, - CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE, - CORINFO_HELP_GETSHARED_GCSTATIC_BASE, - CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE, - CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR, - CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR, - CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS, - CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_DYNAMICCLASS, - // Helper to class initialize shared generic with dynamicclass, but not get static field address - CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS, + CORINFO_HELP_GET_GCSTATIC_BASE, + CORINFO_HELP_GET_NONGCSTATIC_BASE, + CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE, + CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE, + CORINFO_HELP_GETPINNED_GCSTATIC_BASE, + CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE, + CORINFO_HELP_GET_GCSTATIC_BASE_NOCTOR, + CORINFO_HELP_GET_NONGCSTATIC_BASE_NOCTOR, + CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE_NOCTOR, + CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE_NOCTOR, + CORINFO_HELP_GETPINNED_GCSTATIC_BASE_NOCTOR, + CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE_NOCTOR, // Helpers for thread statics - CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE, - CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE, - CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE, - CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE, - CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR, - CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR, - CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS, - CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS, - CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, - CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, + CORINFO_HELP_GET_GCTHREADSTATIC_BASE, + CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE, + CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE, + CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE, + CORINFO_HELP_GET_GCTHREADSTATIC_BASE_NOCTOR, + CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE_NOCTOR, + CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR, + CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR, + CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, + CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, /* Debugger */ @@ -1734,9 +1736,7 @@ struct CORINFO_THREAD_STATIC_BLOCKS_INFO void* tlsIndexObject; // linux/x64 specific - address of tls_index object void* threadVarsSection; // osx x64/arm64 specific - address of __thread_vars section of `t_ThreadStatics` uint32_t offsetOfThreadLocalStoragePointer; // windows specific - uint32_t offsetOfMaxThreadStaticBlocks; uint32_t offsetOfThreadStaticBlocks; - uint32_t offsetOfGCDataPointer; }; //---------------------------------------------------------------------------- @@ -2419,12 +2419,6 @@ class ICorStaticInfo virtual void* LongLifetimeMalloc(size_t sz) = 0; virtual void LongLifetimeFree(void* obj) = 0; - virtual size_t getClassModuleIdForStatics ( - CORINFO_CLASS_HANDLE cls, - CORINFO_MODULE_HANDLE * pModule, - void ** ppIndirection - ) = 0; - virtual bool getIsClassInitedFlagAddress( CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, @@ -2828,8 +2822,7 @@ class ICorStaticInfo // Returns the thread static block information like offsets, etc. from current TLS. virtual void getThreadLocalStaticBlocksInfo ( - CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, - bool isGCType + CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo ) = 0; // Returns true iff "fldHnd" represents a static field. @@ -3234,12 +3227,6 @@ class ICorDynamicInfo : public ICorStaticInfo CORINFO_CALL_INFO *pResult ) = 0; - // Returns the class's domain ID for accessing shared statics - virtual unsigned getClassDomainID ( - CORINFO_CLASS_HANDLE cls, - void **ppIndirection = NULL - ) = 0; - //------------------------------------------------------------------------------ // getStaticFieldContent: returns true and the actual field's value if the given // field represents a statically initialized readonly field of any type. diff --git a/src/coreclr/inc/crsttypes_generated.h b/src/coreclr/inc/crsttypes_generated.h index 70847a5b367fc..b212928a89d95 100644 --- a/src/coreclr/inc/crsttypes_generated.h +++ b/src/coreclr/inc/crsttypes_generated.h @@ -123,18 +123,19 @@ enum CrstType CrstSystemDomain = 105, CrstSystemDomainDelayedUnloadList = 106, CrstThreadIdDispenser = 107, - CrstThreadStore = 108, - CrstTieredCompilation = 109, - CrstTypeEquivalenceMap = 110, - CrstTypeIDMap = 111, - CrstUMEntryThunkCache = 112, - CrstUMEntryThunkFreeListLock = 113, - CrstUniqueStack = 114, - CrstUnresolvedClassLock = 115, - CrstUnwindInfoTableLock = 116, - CrstVSDIndirectionCellLock = 117, - CrstWrapperTemplate = 118, - kNumberOfCrstTypes = 119 + CrstThreadLocalStorageLock = 108, + CrstThreadStore = 109, + CrstTieredCompilation = 110, + CrstTypeEquivalenceMap = 111, + CrstTypeIDMap = 112, + CrstUMEntryThunkCache = 113, + CrstUMEntryThunkFreeListLock = 114, + CrstUniqueStack = 115, + CrstUnresolvedClassLock = 116, + CrstUnwindInfoTableLock = 117, + CrstVSDIndirectionCellLock = 118, + CrstWrapperTemplate = 119, + kNumberOfCrstTypes = 120 }; #endif // __CRST_TYPES_INCLUDED @@ -253,6 +254,7 @@ int g_rgCrstLevelMap[] = 15, // CrstSystemDomain 0, // CrstSystemDomainDelayedUnloadList 0, // CrstThreadIdDispenser + 5, // CrstThreadLocalStorageLock 14, // CrstThreadStore 8, // CrstTieredCompilation 4, // CrstTypeEquivalenceMap @@ -377,6 +379,7 @@ LPCSTR g_rgCrstNameMap[] = "CrstSystemDomain", "CrstSystemDomainDelayedUnloadList", "CrstThreadIdDispenser", + "CrstThreadLocalStorageLock", "CrstThreadStore", "CrstTieredCompilation", "CrstTypeEquivalenceMap", diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h index b632887e86d0f..888a271408dd0 100644 --- a/src/coreclr/inc/dacvars.h +++ b/src/coreclr/inc/dacvars.h @@ -102,6 +102,8 @@ DEFINE_DACVAR(PTR_ThreadStore, ThreadStore__s_pThreadStore, ThreadStore::s_pThre DEFINE_DACVAR(PTR_Thread, dac__g_pFinalizerThread, ::g_pFinalizerThread) DEFINE_DACVAR(PTR_Thread, dac__g_pSuspensionThread, ::g_pSuspensionThread) +DEFINE_DACVAR(DPTR(LookupMap), dac__g_pThreadStaticTypeIndices, ::g_pThreadStaticTypeIndices) + DEFINE_DACVAR(DWORD, dac__g_heap_type, g_heap_type) DEFINE_DACVAR(PTR_GcDacVars, dac__g_gcDacGlobals, g_gcDacGlobals) @@ -146,7 +148,6 @@ DEFINE_DACVAR(gc_alloc_context, dac__g_global_alloc_context, ::g_global_alloc_co DEFINE_DACVAR(IGCHeap, dac__g_pGCHeap, ::g_pGCHeap) DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pThinLockThreadIdDispenser, ::g_pThinLockThreadIdDispenser) -DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pModuleIndexDispenser, ::g_pModuleIndexDispenser) DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pObjectClass, ::g_pObjectClass) DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pRuntimeTypeClass, ::g_pRuntimeTypeClass) DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pCanonMethodTableClass, ::g_pCanonMethodTableClass) diff --git a/src/coreclr/inc/enum_class_flags.h b/src/coreclr/inc/enum_class_flags.h index 8d2a1924360e8..12c62113aa884 100644 --- a/src/coreclr/inc/enum_class_flags.h +++ b/src/coreclr/inc/enum_class_flags.h @@ -49,4 +49,10 @@ inline auto operator ^= (T& left, T right) -> const decltype(T::support_use_as_f return left; } +template +inline bool HasFlag(T value, decltype(T::support_use_as_flags) flag) +{ + return (value & flag) == flag; +} + #endif /* ENUM_CLASS_FLAGS_OPERATORS */ \ No newline at end of file diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 3ea40bbc29b16..704061d924e51 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -211,11 +211,6 @@ void* LongLifetimeMalloc( void LongLifetimeFree( void* obj) override; -size_t getClassModuleIdForStatics( - CORINFO_CLASS_HANDLE cls, - CORINFO_MODULE_HANDLE* pModule, - void** ppIndirection) override; - bool getIsClassInitedFlagAddress( CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, @@ -399,8 +394,7 @@ uint32_t getThreadLocalFieldInfo( bool isGCtype) override; void getThreadLocalStaticBlocksInfo( - CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, - bool isGCType) override; + CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) override; bool isFieldStatic( CORINFO_FIELD_HANDLE fldHnd) override; @@ -582,10 +576,6 @@ void getCallInfo( CORINFO_CALLINFO_FLAGS flags, CORINFO_CALL_INFO* pResult) override; -unsigned getClassDomainID( - CORINFO_CLASS_HANDLE cls, - void** ppIndirection) override; - bool getStaticFieldContent( CORINFO_FIELD_HANDLE field, uint8_t* buffer, diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 5f4c584fc93a5..1bc5c8a1de9a7 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 0cb8113f-52e5-444f-b713-dcb749b5d211 */ - 0x0cb8113f, - 0x52e5, - 0x444f, - {0xb7, 0x13, 0xdc, 0xb7, 0x49, 0xb5, 0xd2, 0x11} +constexpr GUID JITEEVersionIdentifier = { /* 930abea7-44fd-4f45-9826-8568480e8008 */ + 0x930abea7, + 0x44fd, + 0x4f45, + {0x98, 0x26, 0x85, 0x68, 0x48, 0x0e, 0x80, 0x08} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h index 82253956949b5..b5e17b6323c5e 100644 --- a/src/coreclr/inc/jithelpers.h +++ b/src/coreclr/inc/jithelpers.h @@ -178,35 +178,37 @@ JITHELPER(CORINFO_HELP_GETSTATICFIELDADDR, JIT_GetStaticFieldAddr,CORINFO_HELP_SIG_REG_ONLY) JITHELPER(CORINFO_HELP_GETSTATICFIELDADDR_TLS, NULL, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) - JITHELPER(CORINFO_HELP_GETGENERICS_GCSTATIC_BASE, JIT_GetGenericsGCStaticBase,CORINFO_HELP_SIG_REG_ONLY) - JITHELPER(CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE, JIT_GetGenericsNonGCStaticBase,CORINFO_HELP_SIG_REG_ONLY) - #ifdef TARGET_X86 DYNAMICJITHELPER(CORINFO_HELP_GETSHARED_GCSTATIC_BASE, NULL, CORINFO_HELP_SIG_REG_ONLY) DYNAMICJITHELPER(CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE, NULL, CORINFO_HELP_SIG_REG_ONLY) DYNAMICJITHELPER(CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR, NULL, CORINFO_HELP_SIG_REG_ONLY) DYNAMICJITHELPER(CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR,NULL, CORINFO_HELP_SIG_REG_ONLY) #else - DYNAMICJITHELPER(CORINFO_HELP_GETSHARED_GCSTATIC_BASE, JIT_GetSharedGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) - DYNAMICJITHELPER(CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE, JIT_GetSharedNonGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) - DYNAMICJITHELPER(CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR, JIT_GetSharedGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) - DYNAMICJITHELPER(CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR,JIT_GetSharedNonGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_GET_GCSTATIC_BASE, JIT_GetGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_GET_NONGCSTATIC_BASE, JIT_GetNonGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE, JIT_GetDynamicGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE, JIT_GetDynamicNonGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_GETPINNED_GCSTATIC_BASE, JIT_GetDynamicGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE, JIT_GetDynamicNonGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_GET_GCSTATIC_BASE_NOCTOR, JIT_GetGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_GET_NONGCSTATIC_BASE_NOCTOR, JIT_GetNonGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE_NOCTOR, JIT_GetDynamicGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE_NOCTOR,JIT_GetDynamicNonGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_GETPINNED_GCSTATIC_BASE_NOCTOR, JIT_GetDynamicGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + DYNAMICJITHELPER(CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE_NOCTOR,JIT_GetDynamicNonGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) #endif - JITHELPER(CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS, JIT_GetSharedGCStaticBaseDynamicClass,CORINFO_HELP_SIG_REG_ONLY) - JITHELPER(CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_DYNAMICCLASS, JIT_GetSharedNonGCStaticBaseDynamicClass,CORINFO_HELP_SIG_REG_ONLY) - JITHELPER(CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS, JIT_ClassInitDynamicClass,CORINFO_HELP_SIG_REG_ONLY) // Thread statics - JITHELPER(CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE, JIT_GetGenericsGCThreadStaticBase,CORINFO_HELP_SIG_REG_ONLY) - JITHELPER(CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE, JIT_GetGenericsNonGCThreadStaticBase,CORINFO_HELP_SIG_REG_ONLY) - JITHELPER(CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE, JIT_GetSharedGCThreadStaticBase, CORINFO_HELP_SIG_REG_ONLY) - JITHELPER(CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE, JIT_GetSharedNonGCThreadStaticBase, CORINFO_HELP_SIG_REG_ONLY) - JITHELPER(CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR, JIT_GetSharedGCThreadStaticBase, CORINFO_HELP_SIG_REG_ONLY) - JITHELPER(CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR, JIT_GetSharedNonGCThreadStaticBase, CORINFO_HELP_SIG_REG_ONLY) - JITHELPER(CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS, JIT_GetSharedGCThreadStaticBaseDynamicClass, CORINFO_HELP_SIG_REG_ONLY) - JITHELPER(CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS, JIT_GetSharedNonGCThreadStaticBaseDynamicClass, CORINFO_HELP_SIG_REG_ONLY) - JITHELPER(CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, JIT_GetSharedGCThreadStaticBaseOptimized, CORINFO_HELP_SIG_REG_ONLY) - JITHELPER(CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, JIT_GetSharedNonGCThreadStaticBaseOptimized, CORINFO_HELP_SIG_REG_ONLY) + JITHELPER(CORINFO_HELP_GET_GCTHREADSTATIC_BASE, JIT_GetGCThreadStaticBase,CORINFO_HELP_SIG_REG_ONLY) + JITHELPER(CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE, JIT_GetNonGCThreadStaticBase,CORINFO_HELP_SIG_REG_ONLY) + JITHELPER(CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE, JIT_GetDynamicGCThreadStaticBase,CORINFO_HELP_SIG_REG_ONLY) + JITHELPER(CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE, JIT_GetDynamicNonGCThreadStaticBase,CORINFO_HELP_SIG_REG_ONLY) + JITHELPER(CORINFO_HELP_GET_GCTHREADSTATIC_BASE_NOCTOR, JIT_GetGCThreadStaticBase, CORINFO_HELP_SIG_REG_ONLY) + JITHELPER(CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE_NOCTOR, JIT_GetNonGCThreadStaticBase, CORINFO_HELP_SIG_REG_ONLY) + JITHELPER(CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR, JIT_GetDynamicGCThreadStaticBase, CORINFO_HELP_SIG_REG_ONLY) + JITHELPER(CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR,JIT_GetDynamicNonGCThreadStaticBase, CORINFO_HELP_SIG_REG_ONLY) + JITHELPER(CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED,JIT_GetGCThreadStaticBaseOptimized, CORINFO_HELP_SIG_REG_ONLY) + JITHELPER(CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED,JIT_GetNonGCThreadStaticBaseOptimized, CORINFO_HELP_SIG_REG_ONLY) // Debugger JITHELPER(CORINFO_HELP_DBG_IS_JUST_MY_CODE, JIT_DbgIsJustMyCode,CORINFO_HELP_SIG_REG_ONLY) diff --git a/src/coreclr/inc/readytorunhelpers.h b/src/coreclr/inc/readytorunhelpers.h index 8691f9b9cb8c0..01e0b58fc7473 100644 --- a/src/coreclr/inc/readytorunhelpers.h +++ b/src/coreclr/inc/readytorunhelpers.h @@ -48,10 +48,10 @@ HELPER(READYTORUN_HELPER_NewArray, CORINFO_HELP_NEWARR_1_DIRECT HELPER(READYTORUN_HELPER_CheckCastAny, CORINFO_HELP_CHKCASTANY, ) HELPER(READYTORUN_HELPER_CheckInstanceAny, CORINFO_HELP_ISINSTANCEOFANY, ) -HELPER(READYTORUN_HELPER_GenericGcStaticBase, CORINFO_HELP_GETGENERICS_GCSTATIC_BASE, ) -HELPER(READYTORUN_HELPER_GenericNonGcStaticBase, CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE, ) -HELPER(READYTORUN_HELPER_GenericGcTlsBase, CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE, ) -HELPER(READYTORUN_HELPER_GenericNonGcTlsBase, CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE,) +HELPER(READYTORUN_HELPER_GenericGcStaticBase, CORINFO_HELP_GET_GCSTATIC_BASE, ) +HELPER(READYTORUN_HELPER_GenericNonGcStaticBase, CORINFO_HELP_GET_NONGCSTATIC_BASE, ) +HELPER(READYTORUN_HELPER_GenericGcTlsBase, CORINFO_HELP_GET_GCTHREADSTATIC_BASE, ) +HELPER(READYTORUN_HELPER_GenericNonGcTlsBase, CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE, ) HELPER(READYTORUN_HELPER_VirtualFuncPtr, CORINFO_HELP_VIRTUAL_FUNC_PTR, ) HELPER(READYTORUN_HELPER_IsInstanceOfException, CORINFO_HELP_ISINSTANCEOF_EXCEPTION, ) diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h index 51779cdf94bfe..56ea3b81dea40 100644 --- a/src/coreclr/jit/ICorJitInfo_names_generated.h +++ b/src/coreclr/jit/ICorJitInfo_names_generated.h @@ -51,7 +51,6 @@ DEF_CLR_API(getModuleAssembly) DEF_CLR_API(getAssemblyName) DEF_CLR_API(LongLifetimeMalloc) DEF_CLR_API(LongLifetimeFree) -DEF_CLR_API(getClassModuleIdForStatics) DEF_CLR_API(getIsClassInitedFlagAddress) DEF_CLR_API(getStaticBaseAddress) DEF_CLR_API(getClassSize) @@ -144,7 +143,6 @@ DEF_CLR_API(canGetCookieForPInvokeCalliSig) DEF_CLR_API(getJustMyCodeHandle) DEF_CLR_API(GetProfilingHandle) DEF_CLR_API(getCallInfo) -DEF_CLR_API(getClassDomainID) DEF_CLR_API(getStaticFieldContent) DEF_CLR_API(getObjectContent) DEF_CLR_API(getStaticFieldCurrentClass) diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index c90ae13375c8f..165a13f6beaef 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -469,17 +469,6 @@ void WrapICorJitInfo::LongLifetimeFree( API_LEAVE(LongLifetimeFree); } -size_t WrapICorJitInfo::getClassModuleIdForStatics( - CORINFO_CLASS_HANDLE cls, - CORINFO_MODULE_HANDLE* pModule, - void** ppIndirection) -{ - API_ENTER(getClassModuleIdForStatics); - size_t temp = wrapHnd->getClassModuleIdForStatics(cls, pModule, ppIndirection); - API_LEAVE(getClassModuleIdForStatics); - return temp; -} - bool WrapICorJitInfo::getIsClassInitedFlagAddress( CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, @@ -942,11 +931,10 @@ uint32_t WrapICorJitInfo::getThreadLocalFieldInfo( } void WrapICorJitInfo::getThreadLocalStaticBlocksInfo( - CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, - bool isGCType) + CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) { API_ENTER(getThreadLocalStaticBlocksInfo); - wrapHnd->getThreadLocalStaticBlocksInfo(pInfo, isGCType); + wrapHnd->getThreadLocalStaticBlocksInfo(pInfo); API_LEAVE(getThreadLocalStaticBlocksInfo); } @@ -1380,16 +1368,6 @@ void WrapICorJitInfo::getCallInfo( API_LEAVE(getCallInfo); } -unsigned WrapICorJitInfo::getClassDomainID( - CORINFO_CLASS_HANDLE cls, - void** ppIndirection) -{ - API_ENTER(getClassDomainID); - unsigned temp = wrapHnd->getClassDomainID(cls, ppIndirection); - API_LEAVE(getClassDomainID); - return temp; -} - bool WrapICorJitInfo::getStaticFieldContent( CORINFO_FIELD_HANDLE field, uint8_t* buffer, diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index fcc8da7b792cf..a74ea2df9832d 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -1970,7 +1970,7 @@ void Compiler::compInit(ArenaAllocator* pAlloc, // check that HelperCallProperties are initialized - assert(s_helperCallProperties.IsPure(CORINFO_HELP_GETSHARED_GCSTATIC_BASE)); + assert(s_helperCallProperties.IsPure(CORINFO_HELP_GET_GCSTATIC_BASE)); assert(!s_helperCallProperties.IsPure(CORINFO_HELP_GETFIELDOBJ)); // quick sanity check // We start with the flow graph in tree-order diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 4acf9046caaa9..39014631be044 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -3877,13 +3877,13 @@ inline bool Compiler::IsStaticHelperEligibleForExpansion(GenTree* tree, bool* is switch (eeGetHelperNum(tree->AsCall()->gtCallMethHnd)) { case CORINFO_HELP_READYTORUN_GCSTATIC_BASE: - case CORINFO_HELP_GETSHARED_GCSTATIC_BASE: + case CORINFO_HELP_GET_GCSTATIC_BASE: result = true; gc = true; retVal = SHRV_STATIC_BASE_PTR; break; case CORINFO_HELP_READYTORUN_NONGCSTATIC_BASE: - case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE: + case CORINFO_HELP_GET_NONGCSTATIC_BASE: result = true; gc = false; retVal = SHRV_STATIC_BASE_PTR; @@ -3921,29 +3921,15 @@ inline bool Compiler::IsSharedStaticHelper(GenTree* tree) helper == CORINFO_HELP_STRCNS || helper == CORINFO_HELP_BOX || // helpers being added to IsSharedStaticHelper - helper == CORINFO_HELP_GETSTATICFIELDADDR_TLS || helper == CORINFO_HELP_GETGENERICS_GCSTATIC_BASE || - helper == CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE || helper == CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE || - helper == CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE || - - helper == CORINFO_HELP_GETSHARED_GCSTATIC_BASE || helper == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE || - helper == CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR || - helper == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR || - helper == CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS || - helper == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_DYNAMICCLASS || - helper == CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE || - helper == CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE || - helper == CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR || - helper == CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED || - helper == CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR || - helper == CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED || - helper == CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS || - helper == CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS || + helper == CORINFO_HELP_GETSTATICFIELDADDR_TLS || + + (helper >= CORINFO_HELP_GET_GCSTATIC_BASE && helper <= CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) #ifdef FEATURE_READYTORUN - helper == CORINFO_HELP_READYTORUN_GENERIC_STATIC_BASE || helper == CORINFO_HELP_READYTORUN_GCSTATIC_BASE || + || helper == CORINFO_HELP_READYTORUN_GENERIC_STATIC_BASE || helper == CORINFO_HELP_READYTORUN_GCSTATIC_BASE || helper == CORINFO_HELP_READYTORUN_NONGCSTATIC_BASE || helper == CORINFO_HELP_READYTORUN_THREADSTATIC_BASE || - helper == CORINFO_HELP_READYTORUN_NONGCTHREADSTATIC_BASE || + helper == CORINFO_HELP_READYTORUN_NONGCTHREADSTATIC_BASE #endif - helper == CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS; + || helper == CORINFO_HELP_INITCLASS; #if 0 // See above TODO-Cleanup bool result2 = s_helperCallProperties.IsPure(helper) && s_helperCallProperties.NonNullReturn(helper); diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 89b2b983f1711..bd6c2ca551669 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -2856,8 +2856,8 @@ bool emitter::emitNoGChelper(CorInfoHelpFunc helpFunc) case CORINFO_HELP_CHECKED_ASSIGN_REF: case CORINFO_HELP_ASSIGN_BYREF: - case CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR: - case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GET_GCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GET_NONGCSTATIC_BASE_NOCTOR: case CORINFO_HELP_INIT_PINVOKE_FRAME: diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index fdf58c20855ea..ecfe8d355e525 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -708,57 +708,47 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo // We need the return type. switch (helper) { - case CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR: - case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED: - bNeedClassID = false; - FALLTHROUGH; - - case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GET_GCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GET_NONGCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GET_GCTHREADSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED: + case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED: callFlags |= GTF_CALL_HOISTABLE; FALLTHROUGH; - case CORINFO_HELP_GETSHARED_GCSTATIC_BASE: - case CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS: - case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_DYNAMICCLASS: - case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE: - case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS: - case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS: + case CORINFO_HELP_GET_GCSTATIC_BASE: + case CORINFO_HELP_GET_NONGCSTATIC_BASE: + case CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE: + case CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE: + case CORINFO_HELP_GET_GCTHREADSTATIC_BASE: + case CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE: + case CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE: + case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE: // type = TYP_BYREF; break; - case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED: - case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR: - bNeedClassID = false; - FALLTHROUGH; - - case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETPINNED_GCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE_NOCTOR: callFlags |= GTF_CALL_HOISTABLE; FALLTHROUGH; - case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE: - case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE: - case CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS: + case CORINFO_HELP_GETPINNED_GCSTATIC_BASE: + case CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE: type = TYP_I_IMPL; break; + case CORINFO_HELP_INITCLASS: + type = TYP_VOID; + break; + default: assert(!"unknown shared statics helper"); break; } - GenTree* opModuleIDArg; - GenTree* opClassIDArg; - - // Get the class ID - unsigned clsID; - size_t moduleID; - void* pclsID; - void* pmoduleID; - - clsID = info.compCompHnd->getClassDomainID(cls, &pclsID); - - moduleID = info.compCompHnd->getClassModuleIdForStatics(cls, nullptr, &pmoduleID); - if (!(callFlags & GTF_CALL_HOISTABLE)) { if (info.compCompHnd->getClassAttribs(cls) & CORINFO_FLG_BEFOREFIELDINIT) @@ -767,37 +757,16 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo } } - if (pmoduleID) - { - opModuleIDArg = gtNewIndOfIconHandleNode(TYP_I_IMPL, (size_t)pmoduleID, GTF_ICON_CIDMID_HDL, true); - } - else - { - opModuleIDArg = gtNewIconNode((size_t)moduleID, TYP_I_IMPL); - } - GenTreeCall* result; - if (bNeedClassID) - { - if (pclsID) - { - opClassIDArg = gtNewIndOfIconHandleNode(TYP_INT, (size_t)pclsID, GTF_ICON_CIDMID_HDL, true); - } - else - { - opClassIDArg = gtNewIconNode(clsID, TYP_INT); - } - result = gtNewHelperCallNode(helper, type, opModuleIDArg, opClassIDArg); - } - else if ((helper == CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) || - (helper == CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED)) + if ((helper == CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) || + (helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED)) { result = gtNewHelperCallNode(helper, type, gtNewIconNode(typeIndex)); } else { - result = gtNewHelperCallNode(helper, type, opModuleIDArg); + result = gtNewHelperCallNode(helper, type, gtNewIconEmbClsHndNode(cls)); // TODO, handle the PINNED/DYNAMIC cases } if (IsStaticHelperEligibleForExpansion(result)) diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index bcc6982d8e1b8..4c2eb8a327439 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -489,12 +489,15 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* CorInfoHelpFunc helper = call->GetHelperNum(); - if ((helper != CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) && - (helper != CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED)) + if ((helper != CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) && + (helper != CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED)) { return false; } + // TODO ... actually implement this stuff. + return false; + /* assert(!opts.IsReadyToRun()); if (TargetOS::IsUnix) @@ -832,7 +835,7 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* assert(BasicBlock::sameEHRegion(prevBb, threadStaticBlockNullCondBB)); assert(BasicBlock::sameEHRegion(prevBb, fastPathBb)); - return true; + return true;*/ } //------------------------------------------------------------------------------ diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 2ba0643887987..5034ea8d7a502 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -3801,12 +3801,10 @@ GenTree* Compiler::impImportStaticFieldAddress(CORINFO_RESOLVED_TOKEN* pResolved switch (pFieldInfo->helper) { - case CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE: - type = TYP_I_IMPL; - break; - case CORINFO_HELP_GETGENERICS_GCSTATIC_BASE: - case CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE: - case CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE: + case CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE: + case CORINFO_HELP_GET_GCSTATIC_BASE: + case CORINFO_HELP_GET_NONGCSTATIC_BASE: + case CORINFO_HELP_GET_GCTHREADSTATIC_BASE: break; default: assert(!"unknown generic statics helper"); @@ -3822,13 +3820,13 @@ GenTree* Compiler::impImportStaticFieldAddress(CORINFO_RESOLVED_TOKEN* pResolved case CORINFO_FIELD_STATIC_TLS_MANAGED: - if (pFieldInfo->helper == CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) + if (pFieldInfo->helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) { typeIndex = info.compCompHnd->getThreadLocalFieldInfo(pResolvedToken->hField, false); } else { - assert(pFieldInfo->helper == CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED); + assert(pFieldInfo->helper == CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED); typeIndex = info.compCompHnd->getThreadLocalFieldInfo(pResolvedToken->hField, true); } diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index 56e7673e5b8dd..65bfea3ce5609 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -1523,20 +1523,18 @@ void HelperCallProperties::init() // Helpers that load the base address for static variables. // We divide these between those that may and may not invoke // static class constructors. - case CORINFO_HELP_GETSHARED_GCSTATIC_BASE: - case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE: - case CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS: - case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_DYNAMICCLASS: - case CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE: - case CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE: - case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE: - case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE: - case CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS: - case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS: - case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS: + // TODO, logic previously had the dynamic class init helper but not the non-dynamic ones, now the dynamic helper is gone, what to do? + case CORINFO_HELP_GET_GCSTATIC_BASE: + case CORINFO_HELP_GET_NONGCSTATIC_BASE: + case CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE: + case CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE: + case CORINFO_HELP_GETPINNED_GCSTATIC_BASE: + case CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE: + case CORINFO_HELP_GET_GCTHREADSTATIC_BASE: + case CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE: + case CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE: + case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE: case CORINFO_HELP_GETSTATICFIELDADDR_TLS: - case CORINFO_HELP_GETGENERICS_GCSTATIC_BASE: - case CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE: case CORINFO_HELP_READYTORUN_GCSTATIC_BASE: case CORINFO_HELP_READYTORUN_NONGCSTATIC_BASE: case CORINFO_HELP_READYTORUN_THREADSTATIC_BASE: @@ -1551,12 +1549,18 @@ void HelperCallProperties::init() mayRunCctor = true; break; - case CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR: - case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR: - case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR: - case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED: - case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR: - case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED: + case CORINFO_HELP_GET_GCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GET_NONGCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETPINNED_GCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GET_GCTHREADSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED: + case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED: // These do not invoke static class constructors // diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 797abbc7e93ed..83a83320a9165 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -12712,23 +12712,35 @@ VNFunc Compiler::fgValueNumberJitHelperMethodVNFunc(CorInfoHelpFunc helpFunc) vnf = opts.IsReadyToRun() ? VNF_JitReadyToRunNewArr : VNF_JitNewArr; break; - case CORINFO_HELP_GETGENERICS_GCSTATIC_BASE: - vnf = VNF_GetgenericsGcstaticBase; + case CORINFO_HELP_GET_GCSTATIC_BASE: + vnf = VNF_GetGcstaticBase; break; - case CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE: - vnf = VNF_GetgenericsNongcstaticBase; + case CORINFO_HELP_GET_NONGCSTATIC_BASE: + vnf = VNF_GetNongcstaticBase; break; - case CORINFO_HELP_GETSHARED_GCSTATIC_BASE: - vnf = VNF_GetsharedGcstaticBase; + case CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE: + vnf = VNF_GetdynamicGcstaticBase; break; - case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE: - vnf = VNF_GetsharedNongcstaticBase; + case CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE: + vnf = VNF_GetdynamicNongcstaticBase; break; - case CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR: - vnf = VNF_GetsharedGcstaticBaseNoctor; + case CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE_NOCTOR: + vnf = VNF_GetdynamicGcstaticBaseNoctor; break; - case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR: - vnf = VNF_GetsharedNongcstaticBaseNoctor; + case CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE_NOCTOR: + vnf = VNF_GetdynamicNongcstaticBaseNoctor; + break; + case CORINFO_HELP_GETPINNED_GCSTATIC_BASE: + vnf = VNF_GetpinnedGcstaticBase; + break; + case CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE: + vnf = VNF_GetpinnedNongcstaticBase; + break; + case CORINFO_HELP_GETPINNED_GCSTATIC_BASE_NOCTOR: + vnf = VNF_GetpinnedGcstaticBaseNoctor; + break; + case CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE_NOCTOR: + vnf = VNF_GetpinnedNongcstaticBaseNoctor; break; case CORINFO_HELP_READYTORUN_GCSTATIC_BASE: vnf = VNF_ReadyToRunStaticBaseGC; @@ -12745,44 +12757,35 @@ VNFunc Compiler::fgValueNumberJitHelperMethodVNFunc(CorInfoHelpFunc helpFunc) case CORINFO_HELP_READYTORUN_GENERIC_STATIC_BASE: vnf = VNF_ReadyToRunGenericStaticBase; break; - case CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS: - vnf = VNF_GetsharedGcstaticBaseDynamicclass; - break; - case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_DYNAMICCLASS: - vnf = VNF_GetsharedNongcstaticBaseDynamicclass; - break; - case CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS: - vnf = VNF_ClassinitSharedDynamicclass; - break; - case CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE: - vnf = VNF_GetgenericsGcthreadstaticBase; + case CORINFO_HELP_GET_GCTHREADSTATIC_BASE: + vnf = VNF_GetGcthreadstaticBase; break; - case CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE: - vnf = VNF_GetgenericsNongcthreadstaticBase; + case CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE: + vnf = VNF_GetNongcthreadstaticBase; break; - case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE: - vnf = VNF_GetsharedGcthreadstaticBase; + case CORINFO_HELP_GET_GCTHREADSTATIC_BASE_NOCTOR: + vnf = VNF_GetGcthreadstaticBaseNoctor; break; - case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE: - vnf = VNF_GetsharedNongcthreadstaticBase; + case CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE_NOCTOR: + vnf = VNF_GetNongcthreadstaticBaseNoctor; break; - case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR: - vnf = VNF_GetsharedGcthreadstaticBaseNoctor; + case CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE: + vnf = VNF_GetdynamicGcthreadstaticBase; break; - case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED: - vnf = VNF_GetsharedGcthreadstaticBaseNoctorOptimized; + case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE: + vnf = VNF_GetdynamicNongcthreadstaticBase; break; - case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR: - vnf = VNF_GetsharedNongcthreadstaticBaseNoctor; + case CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR: + vnf = VNF_GetdynamicGcthreadstaticBaseNoctor; break; - case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED: - vnf = VNF_GetsharedNongcthreadstaticBaseNoctorOptimized; + case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR: + vnf = VNF_GetdynamicNongcthreadstaticBaseNoctor; break; - case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS: - vnf = VNF_GetsharedGcthreadstaticBaseDynamicclass; + case CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED: + vnf = VNF_GetdynamicGcthreadstaticBaseNoctorOptimized; break; - case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS: - vnf = VNF_GetsharedNongcthreadstaticBaseDynamicclass; + case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED: + vnf = VNF_GetdynamicNongcthreadstaticBaseNoctorOptimized; break; case CORINFO_HELP_GETSTATICFIELDADDR_TLS: vnf = VNF_GetStaticAddrTLS; diff --git a/src/coreclr/jit/valuenumfuncs.h b/src/coreclr/jit/valuenumfuncs.h index f875f3aadaf43..d5cd0e71cafa1 100644 --- a/src/coreclr/jit/valuenumfuncs.h +++ b/src/coreclr/jit/valuenumfuncs.h @@ -113,31 +113,32 @@ ValueNumFuncDef(Truncate, 1, false, false, false, false) ValueNumFuncDef(ManagedThreadId, 0, false, false, false, false) ValueNumFuncDef(ObjGetType, 1, false, true, false, false) -ValueNumFuncDef(GetgenericsGcstaticBase, 1, false, true, true, false) -ValueNumFuncDef(GetgenericsNongcstaticBase, 1, false, true, true, false) -ValueNumFuncDef(GetsharedGcstaticBase, 2, false, true, true, false) -ValueNumFuncDef(GetsharedNongcstaticBase, 2, false, true, true, false) -ValueNumFuncDef(GetsharedGcstaticBaseNoctor, 1, false, true, true, false) -ValueNumFuncDef(GetsharedNongcstaticBaseNoctor, 1, false, true, true, false) +ValueNumFuncDef(GetGcstaticBase, 1, false, true, true, false) +ValueNumFuncDef(GetNongcstaticBase, 1, false, true, true, false) +ValueNumFuncDef(GetdynamicGcstaticBase, 1, false, true, true, false) +ValueNumFuncDef(GetdynamicNongcstaticBase, 1, false, true, true, false) +ValueNumFuncDef(GetdynamicGcstaticBaseNoctor, 1, false, true, true, false) +ValueNumFuncDef(GetdynamicNongcstaticBaseNoctor, 1, false, true, true, false) ValueNumFuncDef(ReadyToRunStaticBaseGC, 1, false, true, true, false) ValueNumFuncDef(ReadyToRunStaticBaseNonGC, 1, false, true, true, false) ValueNumFuncDef(ReadyToRunStaticBaseThread, 1, false, true, true, false) ValueNumFuncDef(ReadyToRunStaticBaseThreadNonGC, 1, false, true, true, false) ValueNumFuncDef(ReadyToRunGenericStaticBase, 2, false, true, true, false) -ValueNumFuncDef(GetsharedGcstaticBaseDynamicclass, 2, false, true, true, false) -ValueNumFuncDef(GetsharedNongcstaticBaseDynamicclass, 2, false, true, true, false) -ValueNumFuncDef(GetgenericsGcthreadstaticBase, 1, false, true, true, false) -ValueNumFuncDef(GetgenericsNongcthreadstaticBase, 1, false, true, true, false) -ValueNumFuncDef(GetsharedGcthreadstaticBase, 2, false, true, true, false) -ValueNumFuncDef(GetsharedNongcthreadstaticBase, 2, false, true, true, false) -ValueNumFuncDef(GetsharedGcthreadstaticBaseNoctor, 2, false, true, true, false) -ValueNumFuncDef(GetsharedGcthreadstaticBaseNoctorOptimized, 1, false, true, true, false) -ValueNumFuncDef(GetsharedNongcthreadstaticBaseNoctor, 2, false, true, true, false) -ValueNumFuncDef(GetsharedNongcthreadstaticBaseNoctorOptimized, 1, false, true, true, false) -ValueNumFuncDef(GetsharedGcthreadstaticBaseDynamicclass, 2, false, true, true, false) -ValueNumFuncDef(GetsharedNongcthreadstaticBaseDynamicclass, 2, false, true, true, false) - -ValueNumFuncDef(ClassinitSharedDynamicclass, 2, false, false, false, false) +ValueNumFuncDef(GetpinnedGcstaticBase, 1, false, true, true, false) +ValueNumFuncDef(GetpinnedNongcstaticBase, 1, false, true, true, false) +ValueNumFuncDef(GetpinnedGcstaticBaseNoctor, 1, false, true, true, false) +ValueNumFuncDef(GetpinnedNongcstaticBaseNoctor, 1, false, true, true, false) +ValueNumFuncDef(GetGcthreadstaticBase, 1, false, true, true, false) +ValueNumFuncDef(GetNongcthreadstaticBase, 1, false, true, true, false) +ValueNumFuncDef(GetGcthreadstaticBaseNoctor, 1, false, true, true, false) +ValueNumFuncDef(GetNongcthreadstaticBaseNoctor, 1, false, true, true, false) +ValueNumFuncDef(GetdynamicGcthreadstaticBase, 1, false, true, true, false) +ValueNumFuncDef(GetdynamicNongcthreadstaticBase, 1, false, true, true, false) +ValueNumFuncDef(GetdynamicGcthreadstaticBaseNoctor, 1, false, true, true, false) +ValueNumFuncDef(GetdynamicGcthreadstaticBaseNoctorOptimized, 1, false, true, true, false) +ValueNumFuncDef(GetdynamicNongcthreadstaticBaseNoctor, 1, false, true, true, false) +ValueNumFuncDef(GetdynamicNongcthreadstaticBaseNoctorOptimized, 1, false, true, true, false) + ValueNumFuncDef(RuntimeHandleMethod, 2, false, true, false, false) ValueNumFuncDef(RuntimeHandleClass, 2, false, true, false, false) ValueNumFuncDef(ReadyToRunGenericHandle, 2, false, true, false, false) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs index f2bc04fb1a442..1c8e927c6499b 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs @@ -170,29 +170,30 @@ which is the right helper to use to allocate an object of a given type. */ // ICorClassInfo::getSharedStaticsOrCCtorHelper to determine which helper to use // Helpers for regular statics - CORINFO_HELP_GETGENERICS_GCSTATIC_BASE, - CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE, - CORINFO_HELP_GETSHARED_GCSTATIC_BASE, - CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE, - CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR, - CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR, - CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS, - CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_DYNAMICCLASS, - // Helper to class initialize shared generic with dynamicclass, but not get static field address - CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS, + CORINFO_HELP_GET_GCSTATIC_BASE, + CORINFO_HELP_GET_NONGCSTATIC_BASE, + CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE, + CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE, + CORINFO_HELP_GETPINNED_GCSTATIC_BASE, + CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE, + CORINFO_HELP_GET_GCSTATIC_BASE_NOCTOR, + CORINFO_HELP_GET_NONGCSTATIC_BASE_NOCTOR, + CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE_NOCTOR, + CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE_NOCTOR, + CORINFO_HELP_GETPINNED_GCSTATIC_BASE_NOCTOR, + CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE_NOCTOR, // Helpers for thread statics - CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE, - CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE, - CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE, - CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE, - CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR, - CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, - CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR, - CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, - CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS, - CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS, - + CORINFO_HELP_GET_GCTHREADSTATIC_BASE, + CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE, + CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE, + CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE, + CORINFO_HELP_GET_GCTHREADSTATIC_BASE_NOCTOR, + CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE_NOCTOR, + CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR, + CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR, + CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, + CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, /* Debugger */ CORINFO_HELP_DBG_IS_JUST_MY_CODE, // Check if this is "JustMyCode" and needs to be stepped through. diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 19faf31a20867..fd726f34ece14 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2124,9 +2124,6 @@ private void LongLifetimeFree(void* obj) Marshal.FreeCoTaskMem((IntPtr)obj); } - private UIntPtr getClassModuleIdForStatics(CORINFO_CLASS_STRUCT_* cls, CORINFO_MODULE_STRUCT_** pModule, void** ppIndirection) - { throw new NotImplementedException("getClassModuleIdForStatics"); } - private uint getClassSize(CORINFO_CLASS_STRUCT_* cls) { TypeDesc type = HandleToObject(cls); @@ -2638,17 +2635,15 @@ private CorInfoInitClassResult initClass(CORINFO_FIELD_STRUCT_* field, CORINFO_M MethodDesc md = method == null ? MethodBeingCompiled : HandleToObject(method); TypeDesc type = fd != null ? fd.OwningType : typeFromContext(context); +#if !READYTORUN if ( -#if READYTORUN - IsClassPreInited(type) -#else _isFallbackBodyCompilation || !_compilation.HasLazyStaticConstructor(type) -#endif ) { return CorInfoInitClassResult.CORINFO_INITCLASS_NOT_REQUIRED; } +#endif MetadataType typeToInit = (MetadataType)type; @@ -3036,7 +3031,7 @@ private uint getThreadLocalFieldInfo(CORINFO_FIELD_STRUCT_* fld, bool isGCType) } #pragma warning disable CA1822 // Mark members as static - private void getThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, bool isGCType) + private void getThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) #pragma warning restore CA1822 // Mark members as static { // Implemented for JIT only for now. @@ -3524,9 +3519,6 @@ private CORINFO_CONST_LOOKUP CreateConstLookupToSymbol(ISymbolNode symbol) return constLookup; } - private uint getClassDomainID(CORINFO_CLASS_STRUCT_* cls, ref void* ppIndirection) - { throw new NotImplementedException("getClassDomainID"); } - private CORINFO_CLASS_STRUCT_* getStaticFieldCurrentClass(CORINFO_FIELD_STRUCT_* field, byte* pIsSpeculative) { if (pIsSpeculative != null) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index ae4da22557a0d..b0fa6b09ff8e6 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -702,21 +702,6 @@ private static void _LongLifetimeFree(IntPtr thisHandle, IntPtr* ppException, vo } } - [UnmanagedCallersOnly] - private static UIntPtr _getClassModuleIdForStatics(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* cls, CORINFO_MODULE_STRUCT_** pModule, void** ppIndirection) - { - var _this = GetThis(thisHandle); - try - { - return _this.getClassModuleIdForStatics(cls, pModule, ppIndirection); - } - catch (Exception ex) - { - *ppException = _this.AllocException(ex); - return default; - } - } - [UnmanagedCallersOnly] private static byte _getIsClassInitedFlagAddress(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* cls, CORINFO_CONST_LOOKUP* addr, int* offset) { @@ -1420,12 +1405,12 @@ private static uint _getThreadLocalFieldInfo(IntPtr thisHandle, IntPtr* ppExcept } [UnmanagedCallersOnly] - private static void _getThreadLocalStaticBlocksInfo(IntPtr thisHandle, IntPtr* ppException, CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, byte isGCType) + private static void _getThreadLocalStaticBlocksInfo(IntPtr thisHandle, IntPtr* ppException, CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) { var _this = GetThis(thisHandle); try { - _this.getThreadLocalStaticBlocksInfo(pInfo, isGCType != 0); + _this.getThreadLocalStaticBlocksInfo(pInfo); } catch (Exception ex) { @@ -2079,21 +2064,6 @@ private static void _getCallInfo(IntPtr thisHandle, IntPtr* ppException, CORINFO } } - [UnmanagedCallersOnly] - private static uint _getClassDomainID(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* cls, void** ppIndirection) - { - var _this = GetThis(thisHandle); - try - { - return _this.getClassDomainID(cls, ref *ppIndirection); - } - catch (Exception ex) - { - *ppException = _this.AllocException(ex); - return default; - } - } - [UnmanagedCallersOnly] private static byte _getStaticFieldContent(IntPtr thisHandle, IntPtr* ppException, CORINFO_FIELD_STRUCT_* field, byte* buffer, int bufferSize, int valueOffset, byte ignoreMovableObjects) { @@ -2537,7 +2507,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_ private static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 171); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 169); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_notifyMethodInfoUsage; @@ -2586,130 +2556,128 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[44] = (delegate* unmanaged)&_getAssemblyName; callbacks[45] = (delegate* unmanaged)&_LongLifetimeMalloc; callbacks[46] = (delegate* unmanaged)&_LongLifetimeFree; - callbacks[47] = (delegate* unmanaged)&_getClassModuleIdForStatics; - callbacks[48] = (delegate* unmanaged)&_getIsClassInitedFlagAddress; - callbacks[49] = (delegate* unmanaged)&_getStaticBaseAddress; - callbacks[50] = (delegate* unmanaged)&_getClassSize; - callbacks[51] = (delegate* unmanaged)&_getHeapClassSize; - callbacks[52] = (delegate* unmanaged)&_canAllocateOnStack; - callbacks[53] = (delegate* unmanaged)&_getClassAlignmentRequirement; - callbacks[54] = (delegate* unmanaged)&_getClassGClayout; - callbacks[55] = (delegate* unmanaged)&_getClassNumInstanceFields; - callbacks[56] = (delegate* unmanaged)&_getFieldInClass; - callbacks[57] = (delegate* unmanaged)&_getTypeLayout; - callbacks[58] = (delegate* unmanaged)&_checkMethodModifier; - callbacks[59] = (delegate* unmanaged)&_getNewHelper; - callbacks[60] = (delegate* unmanaged)&_getNewArrHelper; - callbacks[61] = (delegate* unmanaged)&_getCastingHelper; - callbacks[62] = (delegate* unmanaged)&_getSharedCCtorHelper; - callbacks[63] = (delegate* unmanaged)&_getTypeForBox; - callbacks[64] = (delegate* unmanaged)&_getBoxHelper; - callbacks[65] = (delegate* unmanaged)&_getUnBoxHelper; - callbacks[66] = (delegate* unmanaged)&_getRuntimeTypePointer; - callbacks[67] = (delegate* unmanaged)&_isObjectImmutable; - callbacks[68] = (delegate* unmanaged)&_getStringChar; - callbacks[69] = (delegate* unmanaged)&_getObjectType; - callbacks[70] = (delegate* unmanaged)&_getReadyToRunHelper; - callbacks[71] = (delegate* unmanaged)&_getReadyToRunDelegateCtorHelper; - callbacks[72] = (delegate* unmanaged)&_initClass; - callbacks[73] = (delegate* unmanaged)&_classMustBeLoadedBeforeCodeIsRun; - callbacks[74] = (delegate* unmanaged)&_getBuiltinClass; - callbacks[75] = (delegate* unmanaged)&_getTypeForPrimitiveValueClass; - callbacks[76] = (delegate* unmanaged)&_getTypeForPrimitiveNumericClass; - callbacks[77] = (delegate* unmanaged)&_canCast; - callbacks[78] = (delegate* unmanaged)&_compareTypesForCast; - callbacks[79] = (delegate* unmanaged)&_compareTypesForEquality; - callbacks[80] = (delegate* unmanaged)&_isMoreSpecificType; - callbacks[81] = (delegate* unmanaged)&_isEnum; - callbacks[82] = (delegate* unmanaged)&_getParentType; - callbacks[83] = (delegate* unmanaged)&_getChildType; - callbacks[84] = (delegate* unmanaged)&_isSDArray; - callbacks[85] = (delegate* unmanaged)&_getArrayRank; - callbacks[86] = (delegate* unmanaged)&_getArrayIntrinsicID; - callbacks[87] = (delegate* unmanaged)&_getArrayInitializationData; - callbacks[88] = (delegate* unmanaged)&_canAccessClass; - callbacks[89] = (delegate* unmanaged)&_printFieldName; - callbacks[90] = (delegate* unmanaged)&_getFieldClass; - callbacks[91] = (delegate* unmanaged)&_getFieldType; - callbacks[92] = (delegate* unmanaged)&_getFieldOffset; - callbacks[93] = (delegate* unmanaged)&_getFieldInfo; - callbacks[94] = (delegate* unmanaged)&_getThreadLocalFieldInfo; - callbacks[95] = (delegate* unmanaged)&_getThreadLocalStaticBlocksInfo; - callbacks[96] = (delegate* unmanaged)&_isFieldStatic; - callbacks[97] = (delegate* unmanaged)&_getArrayOrStringLength; - callbacks[98] = (delegate* unmanaged)&_getBoundaries; - callbacks[99] = (delegate* unmanaged)&_setBoundaries; - callbacks[100] = (delegate* unmanaged)&_getVars; - callbacks[101] = (delegate* unmanaged)&_setVars; - callbacks[102] = (delegate* unmanaged)&_reportRichMappings; - callbacks[103] = (delegate* unmanaged)&_allocateArray; - callbacks[104] = (delegate* unmanaged)&_freeArray; - callbacks[105] = (delegate* unmanaged)&_getArgNext; - callbacks[106] = (delegate* unmanaged)&_getArgType; - callbacks[107] = (delegate* unmanaged)&_getExactClasses; - callbacks[108] = (delegate* unmanaged)&_getArgClass; - callbacks[109] = (delegate* unmanaged)&_getHFAType; - callbacks[110] = (delegate* unmanaged)&_runWithErrorTrap; - callbacks[111] = (delegate* unmanaged)&_runWithSPMIErrorTrap; - callbacks[112] = (delegate* unmanaged)&_getEEInfo; - callbacks[113] = (delegate* unmanaged)&_getJitTimeLogFilename; - callbacks[114] = (delegate* unmanaged)&_getMethodDefFromMethod; - callbacks[115] = (delegate* unmanaged)&_printMethodName; - callbacks[116] = (delegate* unmanaged)&_getMethodNameFromMetadata; - callbacks[117] = (delegate* unmanaged)&_getMethodHash; - callbacks[118] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; - callbacks[119] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; - callbacks[120] = (delegate* unmanaged)&_getRISCV64PassStructInRegisterFlags; - callbacks[121] = (delegate* unmanaged)&_getThreadTLSIndex; - callbacks[122] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; - callbacks[123] = (delegate* unmanaged)&_getHelperFtn; - callbacks[124] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[125] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; - callbacks[126] = (delegate* unmanaged)&_getMethodSync; - callbacks[127] = (delegate* unmanaged)&_getLazyStringLiteralHelper; - callbacks[128] = (delegate* unmanaged)&_embedModuleHandle; - callbacks[129] = (delegate* unmanaged)&_embedClassHandle; - callbacks[130] = (delegate* unmanaged)&_embedMethodHandle; - callbacks[131] = (delegate* unmanaged)&_embedFieldHandle; - callbacks[132] = (delegate* unmanaged)&_embedGenericHandle; - callbacks[133] = (delegate* unmanaged)&_getLocationOfThisType; - callbacks[134] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; - callbacks[135] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[136] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; - callbacks[137] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[138] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[139] = (delegate* unmanaged)&_getCallInfo; - callbacks[140] = (delegate* unmanaged)&_getClassDomainID; - callbacks[141] = (delegate* unmanaged)&_getStaticFieldContent; - callbacks[142] = (delegate* unmanaged)&_getObjectContent; - callbacks[143] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[144] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[145] = (delegate* unmanaged)&_canGetVarArgsHandle; - callbacks[146] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[147] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[148] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[149] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[150] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[151] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[152] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[153] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[154] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[155] = (delegate* unmanaged)&_allocMem; - callbacks[156] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[157] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[158] = (delegate* unmanaged)&_allocGCInfo; - callbacks[159] = (delegate* unmanaged)&_setEHcount; - callbacks[160] = (delegate* unmanaged)&_setEHinfo; - callbacks[161] = (delegate* unmanaged)&_logMsg; - callbacks[162] = (delegate* unmanaged)&_doAssert; - callbacks[163] = (delegate* unmanaged)&_reportFatalError; - callbacks[164] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[165] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[166] = (delegate* unmanaged)&_recordCallSite; - callbacks[167] = (delegate* unmanaged)&_recordRelocation; - callbacks[168] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[169] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[170] = (delegate* unmanaged)&_getJitFlags; + callbacks[47] = (delegate* unmanaged)&_getIsClassInitedFlagAddress; + callbacks[48] = (delegate* unmanaged)&_getStaticBaseAddress; + callbacks[49] = (delegate* unmanaged)&_getClassSize; + callbacks[50] = (delegate* unmanaged)&_getHeapClassSize; + callbacks[51] = (delegate* unmanaged)&_canAllocateOnStack; + callbacks[52] = (delegate* unmanaged)&_getClassAlignmentRequirement; + callbacks[53] = (delegate* unmanaged)&_getClassGClayout; + callbacks[54] = (delegate* unmanaged)&_getClassNumInstanceFields; + callbacks[55] = (delegate* unmanaged)&_getFieldInClass; + callbacks[56] = (delegate* unmanaged)&_getTypeLayout; + callbacks[57] = (delegate* unmanaged)&_checkMethodModifier; + callbacks[58] = (delegate* unmanaged)&_getNewHelper; + callbacks[59] = (delegate* unmanaged)&_getNewArrHelper; + callbacks[60] = (delegate* unmanaged)&_getCastingHelper; + callbacks[61] = (delegate* unmanaged)&_getSharedCCtorHelper; + callbacks[62] = (delegate* unmanaged)&_getTypeForBox; + callbacks[63] = (delegate* unmanaged)&_getBoxHelper; + callbacks[64] = (delegate* unmanaged)&_getUnBoxHelper; + callbacks[65] = (delegate* unmanaged)&_getRuntimeTypePointer; + callbacks[66] = (delegate* unmanaged)&_isObjectImmutable; + callbacks[67] = (delegate* unmanaged)&_getStringChar; + callbacks[68] = (delegate* unmanaged)&_getObjectType; + callbacks[69] = (delegate* unmanaged)&_getReadyToRunHelper; + callbacks[70] = (delegate* unmanaged)&_getReadyToRunDelegateCtorHelper; + callbacks[71] = (delegate* unmanaged)&_initClass; + callbacks[72] = (delegate* unmanaged)&_classMustBeLoadedBeforeCodeIsRun; + callbacks[73] = (delegate* unmanaged)&_getBuiltinClass; + callbacks[74] = (delegate* unmanaged)&_getTypeForPrimitiveValueClass; + callbacks[75] = (delegate* unmanaged)&_getTypeForPrimitiveNumericClass; + callbacks[76] = (delegate* unmanaged)&_canCast; + callbacks[77] = (delegate* unmanaged)&_compareTypesForCast; + callbacks[78] = (delegate* unmanaged)&_compareTypesForEquality; + callbacks[79] = (delegate* unmanaged)&_isMoreSpecificType; + callbacks[80] = (delegate* unmanaged)&_isEnum; + callbacks[81] = (delegate* unmanaged)&_getParentType; + callbacks[82] = (delegate* unmanaged)&_getChildType; + callbacks[83] = (delegate* unmanaged)&_isSDArray; + callbacks[84] = (delegate* unmanaged)&_getArrayRank; + callbacks[85] = (delegate* unmanaged)&_getArrayIntrinsicID; + callbacks[86] = (delegate* unmanaged)&_getArrayInitializationData; + callbacks[87] = (delegate* unmanaged)&_canAccessClass; + callbacks[88] = (delegate* unmanaged)&_printFieldName; + callbacks[89] = (delegate* unmanaged)&_getFieldClass; + callbacks[90] = (delegate* unmanaged)&_getFieldType; + callbacks[91] = (delegate* unmanaged)&_getFieldOffset; + callbacks[92] = (delegate* unmanaged)&_getFieldInfo; + callbacks[93] = (delegate* unmanaged)&_getThreadLocalFieldInfo; + callbacks[94] = (delegate* unmanaged)&_getThreadLocalStaticBlocksInfo; + callbacks[95] = (delegate* unmanaged)&_isFieldStatic; + callbacks[96] = (delegate* unmanaged)&_getArrayOrStringLength; + callbacks[97] = (delegate* unmanaged)&_getBoundaries; + callbacks[98] = (delegate* unmanaged)&_setBoundaries; + callbacks[99] = (delegate* unmanaged)&_getVars; + callbacks[100] = (delegate* unmanaged)&_setVars; + callbacks[101] = (delegate* unmanaged)&_reportRichMappings; + callbacks[102] = (delegate* unmanaged)&_allocateArray; + callbacks[103] = (delegate* unmanaged)&_freeArray; + callbacks[104] = (delegate* unmanaged)&_getArgNext; + callbacks[105] = (delegate* unmanaged)&_getArgType; + callbacks[106] = (delegate* unmanaged)&_getExactClasses; + callbacks[107] = (delegate* unmanaged)&_getArgClass; + callbacks[108] = (delegate* unmanaged)&_getHFAType; + callbacks[109] = (delegate* unmanaged)&_runWithErrorTrap; + callbacks[110] = (delegate* unmanaged)&_runWithSPMIErrorTrap; + callbacks[111] = (delegate* unmanaged)&_getEEInfo; + callbacks[112] = (delegate* unmanaged)&_getJitTimeLogFilename; + callbacks[113] = (delegate* unmanaged)&_getMethodDefFromMethod; + callbacks[114] = (delegate* unmanaged)&_printMethodName; + callbacks[115] = (delegate* unmanaged)&_getMethodNameFromMetadata; + callbacks[116] = (delegate* unmanaged)&_getMethodHash; + callbacks[117] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; + callbacks[118] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; + callbacks[119] = (delegate* unmanaged)&_getRISCV64PassStructInRegisterFlags; + callbacks[120] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[121] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[122] = (delegate* unmanaged)&_getHelperFtn; + callbacks[123] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[124] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[125] = (delegate* unmanaged)&_getMethodSync; + callbacks[126] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[127] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[128] = (delegate* unmanaged)&_embedClassHandle; + callbacks[129] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[130] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[131] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[132] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[133] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[134] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[135] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[136] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[137] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[138] = (delegate* unmanaged)&_getCallInfo; + callbacks[139] = (delegate* unmanaged)&_getStaticFieldContent; + callbacks[140] = (delegate* unmanaged)&_getObjectContent; + callbacks[141] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[142] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[143] = (delegate* unmanaged)&_canGetVarArgsHandle; + callbacks[144] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[145] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[146] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[147] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[148] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[149] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[150] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[151] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[152] = (delegate* unmanaged)&_updateEntryPointForTailCall; + callbacks[153] = (delegate* unmanaged)&_allocMem; + callbacks[154] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[155] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[156] = (delegate* unmanaged)&_allocGCInfo; + callbacks[157] = (delegate* unmanaged)&_setEHcount; + callbacks[158] = (delegate* unmanaged)&_setEHinfo; + callbacks[159] = (delegate* unmanaged)&_logMsg; + callbacks[160] = (delegate* unmanaged)&_doAssert; + callbacks[161] = (delegate* unmanaged)&_reportFatalError; + callbacks[162] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[163] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[164] = (delegate* unmanaged)&_recordCallSite; + callbacks[165] = (delegate* unmanaged)&_recordRelocation; + callbacks[166] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[167] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[168] = (delegate* unmanaged)&_getJitFlags; return (IntPtr)callbacks; } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index b75bf623f346f..ce87f8d21dc96 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1152,9 +1152,7 @@ public unsafe struct CORINFO_THREAD_STATIC_BLOCKS_INFO public nuint tlsIndexObject; public nuint threadVarsSection; public uint offsetOfThreadLocalStoragePointer; - public uint offsetOfMaxThreadStaticBlocks; public uint offsetOfThreadStaticBlocks; - public uint offsetOfGCDataPointer; }; // System V struct passing diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index 91808b2878b12..b18b8797cc926 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -208,7 +208,6 @@ FUNCTIONS const char* getAssemblyName(CORINFO_ASSEMBLY_HANDLE assem) void* LongLifetimeMalloc(size_t sz) void LongLifetimeFree(void* obj) - size_t getClassModuleIdForStatics(CORINFO_CLASS_HANDLE cls, CORINFO_MODULE_HANDLE* pModule, VOIDSTARSTAR ppIndirection) bool getIsClassInitedFlagAddress(CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, int* offset) bool getStaticBaseAddress(CORINFO_CLASS_HANDLE cls, bool isGc, CORINFO_CONST_LOOKUP* addr) unsigned getClassSize(CORINFO_CLASS_HANDLE cls) @@ -256,7 +255,7 @@ FUNCTIONS unsigned getFieldOffset(CORINFO_FIELD_HANDLE field) void getFieldInfo(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CORINFO_ACCESS_FLAGS flags, CORINFO_FIELD_INFO* pResult) uint32_t getThreadLocalFieldInfo (CORINFO_FIELD_HANDLE field, bool isGCtype) - void getThreadLocalStaticBlocksInfo (CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, bool isGCType) + void getThreadLocalStaticBlocksInfo (CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) bool isFieldStatic(CORINFO_FIELD_HANDLE fldHnd) int getArrayOrStringLength(CORINFO_OBJECT_HANDLE objHnd) void getBoundaries(CORINFO_METHOD_HANDLE ftn, unsigned int* cILOffsets, uint32_t** pILOffsets, ICorDebugInfo::BoundaryTypes* implicitBoundaries) @@ -301,7 +300,6 @@ FUNCTIONS CORINFO_JUST_MY_CODE_HANDLE getJustMyCodeHandle(CORINFO_METHOD_HANDLE method, CORINFO_JUST_MY_CODE_HANDLE**ppIndirection); void GetProfilingHandle(bool* pbHookFunction, void **pProfilerHandle, bool* pbIndirectedHandles); void getCallInfo(CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_RESOLVED_TOKEN_PTR pConstrainedResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CORINFO_CALLINFO_FLAGS flags, CORINFO_CALL_INFO *pResult); - unsigned getClassDomainID (CORINFO_CLASS_HANDLE cls, void **ppIndirection); bool getStaticFieldContent(CORINFO_FIELD_HANDLE field, uint8_t *buffer, int bufferSize, int valueOffset, bool ignoreMovableObjects); bool getObjectContent(CORINFO_OBJECT_HANDLE obj, uint8_t *buffer, int bufferSize, int valueOffset); CORINFO_CLASS_HANDLE getStaticFieldCurrentClass(CORINFO_FIELD_HANDLE field, BoolStar pIsSpeculative); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs index 4cf6d65ea4390..0eaf7d58e10ae 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs @@ -57,26 +57,7 @@ public override ComputedStaticFieldLayout ComputeStaticFieldLayout(DefType defTy { // ECMA types are the only ones that can have statics ModuleFieldLayout moduleFieldLayout = _moduleFieldLayoutMap.GetOrCreateValue(ecmaType.EcmaModule); - layout.GcStatics = moduleFieldLayout.GcStatics; - layout.NonGcStatics = moduleFieldLayout.NonGcStatics; - layout.ThreadGcStatics = moduleFieldLayout.ThreadGcStatics; - layout.ThreadNonGcStatics = moduleFieldLayout.ThreadNonGcStatics; - if (defType is EcmaType nonGenericType) - { - OffsetsForType offsetsForType; - if (moduleFieldLayout.TypeOffsets.TryGetValue(nonGenericType.Handle, out offsetsForType)) - { - layout.Offsets = _moduleFieldLayoutMap.CalculateTypeLayout(defType, moduleFieldLayout.Module, offsetsForType); - } - } - else if (defType is InstantiatedType instantiatedType) - { - layout.Offsets = _moduleFieldLayoutMap.GetOrAddDynamicLayout(defType, moduleFieldLayout); - } - else - { - throw new NotImplementedException(); - } + layout.Offsets = _moduleFieldLayoutMap.GetOrAddDynamicLayout(defType, moduleFieldLayout); } return layout; } @@ -86,46 +67,6 @@ public override ComputedStaticFieldLayout ComputeStaticFieldLayout(DefType defTy /// private class ModuleFieldLayoutMap : LockFreeReaderHashtable { - /// - /// CoreCLR DomainLocalModule::OffsetOfDataBlob() / sizeof(void *) - /// - private const int DomainLocalModuleDataBlobOffsetAsIntPtrCount = 6; - - /// - /// CoreCLR ThreadLocalModule::OffsetOfDataBlob() / sizeof(void *) - /// - private const int ThreadLocalModuleDataBlobOffsetAsIntPtrCount = 3; - - /// - /// CoreCLR DomainLocalModule::NormalDynamicEntry::OffsetOfDataBlob for X86 - /// - private const int DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobX86 = 4; - - /// - /// CoreCLR DomainLocalModule::NormalDynamicEntry::OffsetOfDataBlob for Amd64 - /// - private const int DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobAmd64 = 8; - - /// - /// CoreCLR DomainLocalModule::NormalDynamicEntry::OffsetOfDataBlob for Arm64 - /// - private const int DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobArm64 = 8; - - /// - /// CoreCLR DomainLocalModule::NormalDynamicEntry::OffsetOfDataBlob for Arm - /// - private const int DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobArm = 8; - - /// - /// CoreCLR DomainLocalModule::NormalDynamicEntry::OffsetOfDataBlob for LoongArch64 - /// - private const int DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobLoongArch64 = 8; - - /// - /// CoreCLR DomainLocalModule::NormalDynamicEntry::OffsetOfDataBlob for RISCV64 - /// - private const int DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobRISCV64 = 8; - protected override bool CompareKeyToValue(EcmaModule key, ModuleFieldLayout value) { return key == value.Module; @@ -138,107 +79,7 @@ protected override bool CompareValueToValue(ModuleFieldLayout value1, ModuleFiel protected override ModuleFieldLayout CreateValueFromKey(EcmaModule module) { - int typeCountInModule = module.MetadataReader.GetTableRowCount(TableIndex.TypeDef); - int pointerSize = module.Context.Target.PointerSize; - - // 0 corresponds to "normal" statics, 1 to thread-local statics - LayoutInt[] gcStatics = new LayoutInt[StaticIndex.Count] - { - LayoutInt.Zero, - LayoutInt.Zero - }; - - LayoutInt[] nonGcStatics = new LayoutInt[StaticIndex.Count] - { - new LayoutInt(DomainLocalModuleDataBlobOffsetAsIntPtrCount * pointerSize + typeCountInModule), - new LayoutInt(ThreadLocalModuleDataBlobOffsetAsIntPtrCount * pointerSize + typeCountInModule), - }; - - Dictionary typeOffsets = new Dictionary(); - - foreach (TypeDefinitionHandle typeDefHandle in module.MetadataReader.TypeDefinitions) - { - TypeDefinition typeDef = module.MetadataReader.GetTypeDefinition(typeDefHandle); - if (typeDef.GetGenericParameters().Count != 0) - { - // Generic types are exempt from the static field layout algorithm, see - // this check. - continue; - } - - // 0 corresponds to "normal" statics, 1 to thread-local statics - int[] nonGcAlignment = new int[StaticIndex.Count] { 1, 1, }; - int[] nonGcBytes = new int[StaticIndex.Count] { 0, 0, }; - int[] gcBytes = new int[StaticIndex.Count] { 0, 0, }; - - foreach (FieldDefinitionHandle fieldDefHandle in typeDef.GetFields()) - { - FieldDefinition fieldDef = module.MetadataReader.GetFieldDefinition(fieldDefHandle); - if ((fieldDef.Attributes & (FieldAttributes.Static | FieldAttributes.Literal)) == FieldAttributes.Static) - { - // Static RVA fields are included when approximating offsets and sizes for the module field layout, see - // this loop. - - int index = (IsFieldThreadStatic(in fieldDef, module.MetadataReader) ? StaticIndex.ThreadLocal : StaticIndex.Regular); - int alignment; - int size; - bool isGcPointerField; - bool isGcBoxedField; - - CorElementType corElementType; - EntityHandle valueTypeHandle; - - GetFieldElementTypeAndValueTypeHandle(in fieldDef, module.MetadataReader, out corElementType, out valueTypeHandle); - FieldDesc fieldDesc = module.GetField(fieldDefHandle); - - GetElementTypeInfo(module, fieldDesc, valueTypeHandle, corElementType, pointerSize, moduleLayout: true, - out alignment, out size, out isGcPointerField, out isGcBoxedField); - - if (size != 0) - { - nonGcBytes[index] += size; - nonGcAlignment[index] = Math.Max(nonGcAlignment[index], alignment); - } - if (isGcPointerField || isGcBoxedField) - { - gcBytes[index] += pointerSize; - } - } - } - - if (nonGcBytes[StaticIndex.Regular] != 0 || - nonGcBytes[StaticIndex.ThreadLocal] != 0 || - gcBytes[StaticIndex.Regular] != 0 || - gcBytes[StaticIndex.ThreadLocal] != 0) - { - OffsetsForType offsetsForType = new OffsetsForType(LayoutInt.Indeterminate, LayoutInt.Indeterminate, LayoutInt.Indeterminate, LayoutInt.Indeterminate); - for (int staticIndex = 0; staticIndex < StaticIndex.Count; staticIndex++) - { - if (nonGcBytes[staticIndex] != 0) - { - offsetsForType.NonGcOffsets[staticIndex] = LayoutInt.AlignUp(nonGcStatics[staticIndex], new LayoutInt(nonGcAlignment[staticIndex]), module.Context.Target); - nonGcStatics[staticIndex] = offsetsForType.NonGcOffsets[staticIndex] + new LayoutInt(nonGcBytes[staticIndex]); - } - if (gcBytes[staticIndex] != 0) - { - offsetsForType.GcOffsets[staticIndex] = gcStatics[staticIndex]; - gcStatics[staticIndex] += new LayoutInt(gcBytes[staticIndex]); - } - } - - typeOffsets.Add(typeDefHandle, offsetsForType); - } - } - - LayoutInt blockAlignment = new LayoutInt(TargetDetails.MaximumPrimitiveSize); - - return new ModuleFieldLayout( - module, - gcStatics: new StaticsBlock() { Size = gcStatics[StaticIndex.Regular], LargestAlignment = blockAlignment }, - nonGcStatics: new StaticsBlock() { Size = nonGcStatics[StaticIndex.Regular], LargestAlignment = blockAlignment }, - threadGcStatics: new StaticsBlock() { Size = gcStatics[StaticIndex.ThreadLocal], LargestAlignment = blockAlignment }, - threadNonGcStatics: new StaticsBlock() { Size = nonGcStatics[StaticIndex.ThreadLocal], LargestAlignment = blockAlignment }, - typeOffsets: typeOffsets); + return new ModuleFieldLayout(module); } private void GetElementTypeInfoGeneric( @@ -405,40 +246,9 @@ public FieldAndOffset[] GetOrAddDynamicLayout(DefType defType, ModuleFieldLayout FieldAndOffset[] fieldsForType; if (!moduleFieldLayout.TryGetDynamicLayout(defType, out fieldsForType)) { - int nonGcOffset; - switch (moduleFieldLayout.Module.Context.Target.Architecture) - { - case TargetArchitecture.X86: - nonGcOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobX86; - break; - - case TargetArchitecture.X64: - nonGcOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobAmd64; - break; - - case TargetArchitecture.ARM64: - nonGcOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobArm64; - break; - - case TargetArchitecture.ARM: - nonGcOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobArm; - break; - - case TargetArchitecture.LoongArch64: - nonGcOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobLoongArch64; - break; - - case TargetArchitecture.RiscV64: - nonGcOffset = DomainLocalModuleNormalDynamicEntryOffsetOfDataBlobRISCV64; - break; - - default: - throw new NotImplementedException(); - } - OffsetsForType offsetsForType = new OffsetsForType( - nonGcOffset: new LayoutInt(nonGcOffset), - tlsNonGcOffset: new LayoutInt(nonGcOffset), + nonGcOffset: LayoutInt.Zero, + tlsNonGcOffset: LayoutInt.Zero, gcOffset: LayoutInt.Zero, tlsGcOffset: LayoutInt.Zero); @@ -773,44 +583,24 @@ private class ModuleFieldLayout { public EcmaModule Module { get; } - public StaticsBlock GcStatics { get; } - - public StaticsBlock NonGcStatics { get; } - - public StaticsBlock ThreadGcStatics { get; } - - public StaticsBlock ThreadNonGcStatics { get; } - - public IReadOnlyDictionary TypeOffsets { get; } - - private ConcurrentDictionary _genericTypeToFieldMap; + private ConcurrentDictionary _typeToFieldMap; public ModuleFieldLayout( - EcmaModule module, - StaticsBlock gcStatics, - StaticsBlock nonGcStatics, - StaticsBlock threadGcStatics, - StaticsBlock threadNonGcStatics, - IReadOnlyDictionary typeOffsets) + EcmaModule module) { Module = module; - GcStatics = gcStatics; - NonGcStatics = nonGcStatics; - ThreadGcStatics = threadGcStatics; - ThreadNonGcStatics = threadNonGcStatics; - TypeOffsets = typeOffsets; - _genericTypeToFieldMap = new ConcurrentDictionary(); + _typeToFieldMap = new ConcurrentDictionary(); } public bool TryGetDynamicLayout(DefType instantiatedType, out FieldAndOffset[] fieldMap) { - return _genericTypeToFieldMap.TryGetValue(instantiatedType, out fieldMap); + return _typeToFieldMap.TryGetValue(instantiatedType, out fieldMap); } public FieldAndOffset[] GetOrAddDynamicLayout(DefType instantiatedType, FieldAndOffset[] fieldMap) { - return _genericTypeToFieldMap.GetOrAdd(instantiatedType, fieldMap); + return _typeToFieldMap.GetOrAdd(instantiatedType, fieldMap); } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index d0b5413f985e0..3e8ca0bbe7df7 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -1008,19 +1008,19 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum) break; - case CorInfoHelpFunc.CORINFO_HELP_GETGENERICS_GCSTATIC_BASE: + case CorInfoHelpFunc.CORINFO_HELP_GET_GCSTATIC_BASE: id = ReadyToRunHelper.GenericGcStaticBase; break; - case CorInfoHelpFunc.CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE: + case CorInfoHelpFunc.CORINFO_HELP_GET_NONGCSTATIC_BASE: id = ReadyToRunHelper.GenericNonGcStaticBase; break; - case CorInfoHelpFunc.CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE: + case CorInfoHelpFunc.CORINFO_HELP_GET_GCTHREADSTATIC_BASE: id = ReadyToRunHelper.GenericGcTlsBase; break; - case CorInfoHelpFunc.CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE: + case CorInfoHelpFunc.CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE: id = ReadyToRunHelper.GenericNonGcTlsBase; break; @@ -1704,14 +1704,14 @@ private void getFieldInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_MET if (field.IsThreadStatic) { pResult->helper = (field.HasGCStaticBase ? - CorInfoHelpFunc.CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE : - CorInfoHelpFunc.CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE); + CorInfoHelpFunc.CORINFO_HELP_GET_GCTHREADSTATIC_BASE : + CorInfoHelpFunc.CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE); } else { pResult->helper = (field.HasGCStaticBase ? - CorInfoHelpFunc.CORINFO_HELP_GETGENERICS_GCSTATIC_BASE : - CorInfoHelpFunc.CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE); + CorInfoHelpFunc.CORINFO_HELP_GET_GCSTATIC_BASE : + CorInfoHelpFunc.CORINFO_HELP_GET_NONGCSTATIC_BASE); } if (_compilation.SymbolNodeFactory.VerifyTypeAndFieldLayout && (fieldOffset <= FieldFixupSignature.MaxCheckableOffset)) diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index 0a9fce19e412a..95d8df175c1f0 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -58,7 +58,6 @@ struct JitInterfaceCallbacks const char* (* getAssemblyName)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_ASSEMBLY_HANDLE assem); void* (* LongLifetimeMalloc)(void * thisHandle, CorInfoExceptionClass** ppException, size_t sz); void (* LongLifetimeFree)(void * thisHandle, CorInfoExceptionClass** ppException, void* obj); - size_t (* getClassModuleIdForStatics)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls, CORINFO_MODULE_HANDLE* pModule, void** ppIndirection); bool (* getIsClassInitedFlagAddress)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, int* offset); bool (* getStaticBaseAddress)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls, bool isGc, CORINFO_CONST_LOOKUP* addr); unsigned (* getClassSize)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls); @@ -106,7 +105,7 @@ struct JitInterfaceCallbacks unsigned (* getFieldOffset)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_FIELD_HANDLE field); void (* getFieldInfo)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CORINFO_ACCESS_FLAGS flags, CORINFO_FIELD_INFO* pResult); uint32_t (* getThreadLocalFieldInfo)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_FIELD_HANDLE field, bool isGCtype); - void (* getThreadLocalStaticBlocksInfo)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, bool isGCType); + void (* getThreadLocalStaticBlocksInfo)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo); bool (* isFieldStatic)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_FIELD_HANDLE fldHnd); int (* getArrayOrStringLength)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_OBJECT_HANDLE objHnd); void (* getBoundaries)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, unsigned int* cILOffsets, uint32_t** pILOffsets, ICorDebugInfo::BoundaryTypes* implicitBoundaries); @@ -151,7 +150,6 @@ struct JitInterfaceCallbacks CORINFO_JUST_MY_CODE_HANDLE (* getJustMyCodeHandle)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE method, CORINFO_JUST_MY_CODE_HANDLE** ppIndirection); void (* GetProfilingHandle)(void * thisHandle, CorInfoExceptionClass** ppException, bool* pbHookFunction, void** pProfilerHandle, bool* pbIndirectedHandles); void (* getCallInfo)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CORINFO_CALLINFO_FLAGS flags, CORINFO_CALL_INFO* pResult); - unsigned (* getClassDomainID)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls, void** ppIndirection); bool (* getStaticFieldContent)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_FIELD_HANDLE field, uint8_t* buffer, int bufferSize, int valueOffset, bool ignoreMovableObjects); bool (* getObjectContent)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_OBJECT_HANDLE obj, uint8_t* buffer, int bufferSize, int valueOffset); CORINFO_CLASS_HANDLE (* getStaticFieldCurrentClass)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_FIELD_HANDLE field, bool* pIsSpeculative); @@ -654,17 +652,6 @@ class JitInterfaceWrapper : public ICorJitInfo if (pException != nullptr) throw pException; } - virtual size_t getClassModuleIdForStatics( - CORINFO_CLASS_HANDLE cls, - CORINFO_MODULE_HANDLE* pModule, - void** ppIndirection) -{ - CorInfoExceptionClass* pException = nullptr; - size_t temp = _callbacks->getClassModuleIdForStatics(_thisHandle, &pException, cls, pModule, ppIndirection); - if (pException != nullptr) throw pException; - return temp; -} - virtual bool getIsClassInitedFlagAddress( CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, @@ -1127,11 +1114,10 @@ class JitInterfaceWrapper : public ICorJitInfo } virtual void getThreadLocalStaticBlocksInfo( - CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, - bool isGCType) + CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) { CorInfoExceptionClass* pException = nullptr; - _callbacks->getThreadLocalStaticBlocksInfo(_thisHandle, &pException, pInfo, isGCType); + _callbacks->getThreadLocalStaticBlocksInfo(_thisHandle, &pException, pInfo); if (pException != nullptr) throw pException; } @@ -1553,16 +1539,6 @@ class JitInterfaceWrapper : public ICorJitInfo if (pException != nullptr) throw pException; } - virtual unsigned getClassDomainID( - CORINFO_CLASS_HANDLE cls, - void** ppIndirection) -{ - CorInfoExceptionClass* pException = nullptr; - unsigned temp = _callbacks->getClassDomainID(_thisHandle, &pException, cls, ppIndirection); - if (pException != nullptr) throw pException; - return temp; -} - virtual bool getStaticFieldContent( CORINFO_FIELD_HANDLE field, uint8_t* buffer, diff --git a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h index ec8f9a9b04110..c312591cf9567 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h @@ -528,9 +528,7 @@ struct Agnostic_GetThreadLocalStaticBlocksInfo DWORDLONG tlsIndexObject; DWORDLONG threadVarsSection; DWORD offsetOfThreadLocalStoragePointer; - DWORD offsetOfMaxThreadStaticBlocks; DWORD offsetOfThreadStaticBlocks; - DWORD offsetOfGCDataPointer; }; struct Agnostic_GetThreadLocalFieldInfo diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index 70c754aef50b5..da51749cac62e 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -60,7 +60,6 @@ LWM(GetCastingHelper, Agnostic_GetCastingHelper, DWORD) LWM(GetChildType, DWORDLONG, DLD) LWM(GetClassAlignmentRequirement, DLD, DWORD) LWM(GetClassAttribs, DWORDLONG, DWORD) -LWM(GetClassDomainID, DWORDLONG, DLD) LWM(GetClassGClayout, DWORDLONG, Agnostic_GetClassGClayout) LWM(GetClassModuleIdForStatics, DWORDLONG, Agnostic_GetClassModuleIdForStatics) LWM(GetIsClassInitedFlagAddress, DWORDLONG, Agnostic_GetIsClassInitedFlagAddress) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index a6d49f08219b5..31a1c79f8bb68 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -3605,7 +3605,7 @@ uint32_t MethodContext::repGetThreadLocalFieldInfo(CORINFO_FIELD_HANDLE field, b return value; } -void MethodContext::recGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, bool isGCType) +void MethodContext::recGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) { if (GetThreadLocalStaticBlocksInfo == nullptr) GetThreadLocalStaticBlocksInfo = new LightWeightMap(); @@ -3618,12 +3618,10 @@ void MethodContext::recGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOC value.tlsIndexObject = CastPointer(pInfo->tlsIndexObject); value.threadVarsSection = CastPointer(pInfo->threadVarsSection); value.offsetOfThreadLocalStoragePointer = pInfo->offsetOfThreadLocalStoragePointer; - value.offsetOfMaxThreadStaticBlocks = pInfo->offsetOfMaxThreadStaticBlocks; value.offsetOfThreadStaticBlocks = pInfo->offsetOfThreadStaticBlocks; - value.offsetOfGCDataPointer = pInfo->offsetOfGCDataPointer; // This data is same for entire process, so just add it against key '0'. - DWORD key = isGCType ? 0 : 1; + DWORD key = 0; GetThreadLocalStaticBlocksInfo->Add(key, value); DEBUG_REC(dmpGetThreadLocalStaticBlocksInfo(key, value)); } @@ -3633,16 +3631,16 @@ void MethodContext::dmpGetThreadLocalStaticBlocksInfo(DWORD key, const Agnostic_ printf("GetThreadLocalStaticBlocksInfo key %u, tlsIndex-%s, " ", tlsGetAddrFtnPtr-%016" PRIX64 ", tlsIndexObject - %016" PRIX64 ", threadVarsSection - %016" PRIX64 - ", offsetOfThreadLocalStoragePointer-%u, offsetOfMaxThreadStaticBlocks-%u" - ", offsetOfThreadStaticBlocks-%u, offsetOfGCDataPointer-%u", + ", offsetOfThreadLocalStoragePointer-%u" + ", offsetOfThreadStaticBlocks-%u", key, SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(value.tlsIndex).c_str(), value.tlsGetAddrFtnPtr, value.tlsIndexObject, value.threadVarsSection, value.offsetOfThreadLocalStoragePointer, - value.offsetOfMaxThreadStaticBlocks, value.offsetOfThreadStaticBlocks, value.offsetOfGCDataPointer); + value.offsetOfThreadStaticBlocks); } -void MethodContext::repGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, bool isGCType) +void MethodContext::repGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) { - int key = isGCType ? 0 : 1; + int key = 0; Agnostic_GetThreadLocalStaticBlocksInfo value = LookupByKeyOrMiss(GetThreadLocalStaticBlocksInfo, key, ": key %u", key); DEBUG_REP(dmpGetThreadLocalStaticBlocksInfo(key, value)); @@ -3652,9 +3650,7 @@ void MethodContext::repGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOC pInfo->tlsIndexObject = (void*)value.tlsIndexObject; pInfo->threadVarsSection = (void*)value.threadVarsSection; pInfo->offsetOfThreadLocalStoragePointer = value.offsetOfThreadLocalStoragePointer; - pInfo->offsetOfMaxThreadStaticBlocks = value.offsetOfMaxThreadStaticBlocks; pInfo->offsetOfThreadStaticBlocks = value.offsetOfThreadStaticBlocks; - pInfo->offsetOfGCDataPointer = value.offsetOfGCDataPointer; } void MethodContext::recEmbedMethodHandle(CORINFO_METHOD_HANDLE handle, @@ -4613,39 +4609,6 @@ int32_t* MethodContext::repGetAddrOfCaptureThreadGlobal(void** ppIndirection) return (int32_t*)value.B; } -void MethodContext::recGetClassDomainID(CORINFO_CLASS_HANDLE cls, void** ppIndirection, unsigned result) -{ - if (GetClassDomainID == nullptr) - GetClassDomainID = new LightWeightMap(); - - DLD value; - - if (ppIndirection != nullptr) - value.A = CastPointer(*ppIndirection); - else - value.A = 0; - value.B = (DWORD)result; - - DWORDLONG key = CastHandle(cls); - GetClassDomainID->Add(key, value); - DEBUG_REC(dmpGetClassDomainID(key, value)); -} -void MethodContext::dmpGetClassDomainID(DWORDLONG key, DLD value) -{ - printf("GetClassDomainID key cls-%016" PRIX64 ", value pp-%016" PRIX64 " res-%u", key, value.A, value.B); -} -unsigned MethodContext::repGetClassDomainID(CORINFO_CLASS_HANDLE cls, void** ppIndirection) -{ - DWORDLONG key = CastHandle(cls); - DLD value = LookupByKeyOrMiss(GetClassDomainID, key, ": key %016" PRIX64 "", key); - - DEBUG_REP(dmpGetClassDomainID(key, value)); - - if (ppIndirection != nullptr) - *ppIndirection = (void*)value.A; - return (unsigned)value.B; -} - void MethodContext::recGetLocationOfThisType(CORINFO_METHOD_HANDLE context, CORINFO_LOOKUP_KIND* result) { if (GetLocationOfThisType == nullptr) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index c9465f2dc5082..fb6dae6bc60c2 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -478,9 +478,9 @@ class MethodContext void dmpGetThreadLocalFieldInfo(DLD key, DWORD value); uint32_t repGetThreadLocalFieldInfo(CORINFO_FIELD_HANDLE field, bool isGCType); - void recGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, bool isGCType); + void recGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo); void dmpGetThreadLocalStaticBlocksInfo(DWORD key, const Agnostic_GetThreadLocalStaticBlocksInfo& value); - void repGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, bool isGCType); + void repGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo); void recEmbedMethodHandle(CORINFO_METHOD_HANDLE handle, void** ppIndirection, CORINFO_METHOD_HANDLE result); void dmpEmbedMethodHandle(DWORDLONG key, DLDL value); @@ -582,10 +582,6 @@ class MethodContext void dmpGetAddrOfCaptureThreadGlobal(DWORD key, DLDL value); int32_t* repGetAddrOfCaptureThreadGlobal(void** ppIndirection); - void recGetClassDomainID(CORINFO_CLASS_HANDLE cls, void** ppIndirection, unsigned result); - void dmpGetClassDomainID(DWORDLONG key, DLD value); - unsigned repGetClassDomainID(CORINFO_CLASS_HANDLE cls, void** ppIndirection); - void recGetLocationOfThisType(CORINFO_METHOD_HANDLE context, CORINFO_LOOKUP_KIND* result); void dmpGetLocationOfThisType(DWORDLONG key, const Agnostic_CORINFO_LOOKUP_KIND& value); void repGetLocationOfThisType(CORINFO_METHOD_HANDLE context, CORINFO_LOOKUP_KIND* pLookupKind); @@ -975,7 +971,7 @@ enum mcPackets Packet_GetChildType = 39, Packet_GetClassAlignmentRequirement = 40, Packet_GetClassAttribs = 41, - Packet_GetClassDomainID = 42, + //Packet_GetClassDomainID = 42, Packet_GetClassGClayout = 43, Packet_GetClassModuleIdForStatics = 44, Packet_GetClassName = 45, diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index d0e7b69cdb8c3..371e9cd8148a1 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -541,16 +541,6 @@ void interceptor_ICJI::LongLifetimeFree(void* obj) original_ICorJitInfo->LongLifetimeFree(obj); } -size_t interceptor_ICJI::getClassModuleIdForStatics(CORINFO_CLASS_HANDLE cls, - CORINFO_MODULE_HANDLE* pModule, - void** ppIndirection) -{ - mc->cr->AddCall("getClassModuleIdForStatics"); - size_t temp = original_ICorJitInfo->getClassModuleIdForStatics(cls, pModule, ppIndirection); - mc->recGetClassModuleIdForStatics(cls, pModule, ppIndirection, temp); - return temp; -} - bool interceptor_ICJI::getIsClassInitedFlagAddress(CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, int* offset) @@ -1073,11 +1063,11 @@ uint32_t interceptor_ICJI::getThreadLocalFieldInfo(CORINFO_FIELD_HANDLE field, b return result; } -void interceptor_ICJI::getThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, bool isGCType) +void interceptor_ICJI::getThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) { mc->cr->AddCall("getThreadLocalStaticBlocksInfo"); - original_ICorJitInfo->getThreadLocalStaticBlocksInfo(pInfo, isGCType); - mc->recGetThreadLocalStaticBlocksInfo(pInfo, isGCType); + original_ICorJitInfo->getThreadLocalStaticBlocksInfo(pInfo); + mc->recGetThreadLocalStaticBlocksInfo(pInfo); } // Returns true iff "fldHnd" represents a static field. @@ -1610,15 +1600,6 @@ void interceptor_ICJI::getCallInfo( }); } -// returns the class's domain ID for accessing shared statics -unsigned interceptor_ICJI::getClassDomainID(CORINFO_CLASS_HANDLE cls, void** ppIndirection) -{ - mc->cr->AddCall("getClassDomainID"); - unsigned temp = original_ICorJitInfo->getClassDomainID(cls, ppIndirection); - mc->recGetClassDomainID(cls, ppIndirection, temp); - return temp; -} - bool interceptor_ICJI::getStaticFieldContent(CORINFO_FIELD_HANDLE field, uint8_t* buffer, int bufferSize, int valueOffset, bool ignoreMovableObjects) { mc->cr->AddCall("getStaticFieldContent"); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp index ddce7e7e18241..ad36fa70d579e 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -390,15 +390,6 @@ void interceptor_ICJI::LongLifetimeFree( original_ICorJitInfo->LongLifetimeFree(obj); } -size_t interceptor_ICJI::getClassModuleIdForStatics( - CORINFO_CLASS_HANDLE cls, - CORINFO_MODULE_HANDLE* pModule, - void** ppIndirection) -{ - mcs->AddCall("getClassModuleIdForStatics"); - return original_ICorJitInfo->getClassModuleIdForStatics(cls, pModule, ppIndirection); -} - bool interceptor_ICJI::getIsClassInitedFlagAddress( CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, @@ -770,11 +761,10 @@ uint32_t interceptor_ICJI::getThreadLocalFieldInfo( } void interceptor_ICJI::getThreadLocalStaticBlocksInfo( - CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, - bool isGCType) + CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) { mcs->AddCall("getThreadLocalStaticBlocksInfo"); - original_ICorJitInfo->getThreadLocalStaticBlocksInfo(pInfo, isGCType); + original_ICorJitInfo->getThreadLocalStaticBlocksInfo(pInfo); } bool interceptor_ICJI::isFieldStatic( @@ -1133,14 +1123,6 @@ void interceptor_ICJI::getCallInfo( original_ICorJitInfo->getCallInfo(pResolvedToken, pConstrainedResolvedToken, callerHandle, flags, pResult); } -unsigned interceptor_ICJI::getClassDomainID( - CORINFO_CLASS_HANDLE cls, - void** ppIndirection) -{ - mcs->AddCall("getClassDomainID"); - return original_ICorJitInfo->getClassDomainID(cls, ppIndirection); -} - bool interceptor_ICJI::getStaticFieldContent( CORINFO_FIELD_HANDLE field, uint8_t* buffer, diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp index 4577a26e2292f..a850a094fedf7 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -343,14 +343,6 @@ void interceptor_ICJI::LongLifetimeFree( original_ICorJitInfo->LongLifetimeFree(obj); } -size_t interceptor_ICJI::getClassModuleIdForStatics( - CORINFO_CLASS_HANDLE cls, - CORINFO_MODULE_HANDLE* pModule, - void** ppIndirection) -{ - return original_ICorJitInfo->getClassModuleIdForStatics(cls, pModule, ppIndirection); -} - bool interceptor_ICJI::getIsClassInitedFlagAddress( CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, @@ -675,10 +667,9 @@ uint32_t interceptor_ICJI::getThreadLocalFieldInfo( } void interceptor_ICJI::getThreadLocalStaticBlocksInfo( - CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, - bool isGCType) + CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) { - original_ICorJitInfo->getThreadLocalStaticBlocksInfo(pInfo, isGCType); + original_ICorJitInfo->getThreadLocalStaticBlocksInfo(pInfo); } bool interceptor_ICJI::isFieldStatic( @@ -993,13 +984,6 @@ void interceptor_ICJI::getCallInfo( original_ICorJitInfo->getCallInfo(pResolvedToken, pConstrainedResolvedToken, callerHandle, flags, pResult); } -unsigned interceptor_ICJI::getClassDomainID( - CORINFO_CLASS_HANDLE cls, - void** ppIndirection) -{ - return original_ICorJitInfo->getClassDomainID(cls, ppIndirection); -} - bool interceptor_ICJI::getStaticFieldContent( CORINFO_FIELD_HANDLE field, uint8_t* buffer, diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index f2e2305dc45aa..77a2afbc0b91a 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -464,14 +464,6 @@ void MyICJI::LongLifetimeFree(void* obj) DebugBreakorAV(33); } -size_t MyICJI::getClassModuleIdForStatics(CORINFO_CLASS_HANDLE cls, - CORINFO_MODULE_HANDLE* pModule, - void** ppIndirection) -{ - jitInstance->mc->cr->AddCall("getClassModuleIdForStatics"); - return jitInstance->mc->repGetClassModuleIdForStatics(cls, pModule, ppIndirection); -} - bool MyICJI::getIsClassInitedFlagAddress(CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, int* offset) @@ -891,10 +883,10 @@ uint32_t MyICJI::getThreadLocalFieldInfo(CORINFO_FIELD_HANDLE field, bool isGCTy return jitInstance->mc->repGetThreadLocalFieldInfo(field, isGCType); } -void MyICJI::getThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, bool isGCType) +void MyICJI::getThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) { jitInstance->mc->cr->AddCall("getThreadLocalStaticBlocksInfo"); - jitInstance->mc->repGetThreadLocalStaticBlocksInfo(pInfo, isGCType); + jitInstance->mc->repGetThreadLocalStaticBlocksInfo(pInfo); } // Returns true iff "fldHnd" represents a static field. @@ -1382,13 +1374,6 @@ void MyICJI::getCallInfo( ThrowRecordedException(exceptionCode); } -// returns the class's domain ID for accessing shared statics -unsigned MyICJI::getClassDomainID(CORINFO_CLASS_HANDLE cls, void** ppIndirection) -{ - jitInstance->mc->cr->AddCall("getClassDomainID"); - return jitInstance->mc->repGetClassDomainID(cls, ppIndirection); -} - bool MyICJI::getStaticFieldContent(CORINFO_FIELD_HANDLE field, uint8_t* buffer, int bufferSize, int valueOffset, bool ignoreMovableObjects) { jitInstance->mc->cr->AddCall("getStaticFieldContent"); diff --git a/src/coreclr/vm/amd64/JitHelpers_SingleAppDomain.asm b/src/coreclr/vm/amd64/JitHelpers_SingleAppDomain.asm index 724e6888d7612..5f9b58dc2b17a 100644 --- a/src/coreclr/vm/amd64/JitHelpers_SingleAppDomain.asm +++ b/src/coreclr/vm/amd64/JitHelpers_SingleAppDomain.asm @@ -3,9 +3,6 @@ ; *********************************************************************** ; File: JitHelpers_SingleAppDomain.asm -; -; Notes: JIT Static access helpers when coreclr host specifies single -; appdomain flag ; *********************************************************************** include AsmMacros.inc @@ -14,45 +11,34 @@ include asmconstants.inc ; Min amount of stack space that a nested function should allocate. MIN_SIZE equ 28h -extern JIT_GetSharedNonGCStaticBase_Helper:proc -extern JIT_GetSharedGCStaticBase_Helper:proc +extern JIT_GetDynamicNonGCStaticBase_Portable:proc +extern JIT_GetDynamicGCStaticBase_Portable:proc -LEAF_ENTRY JIT_GetSharedNonGCStaticBase_SingleAppDomain, _TEXT +LEAF_ENTRY JIT_GetDynamicNonGCStaticBase_SingleAppDomain, _TEXT ; If class is not initialized, bail to C++ helper - test byte ptr [rcx + OFFSETOF__DomainLocalModule__m_pDataBlob + rdx], 1 + test byte ptr [rcx + OFFSETOF__DynamicStaticsInfo__m_AuxData__m_dwFlags], 1 jz CallHelper - mov rax, rcx + mov rax, [rcx + OFFSETOF__DynamicStaticsInfo__m_pNonGCStatics] REPRET align 16 CallHelper: - ; Tail call JIT_GetSharedNonGCStaticBase_Helper - jmp JIT_GetSharedNonGCStaticBase_Helper -LEAF_END JIT_GetSharedNonGCStaticBase_SingleAppDomain, _TEXT + ; Tail call JIT_GetDynamicNonGCStaticBase_Portable + jmp JIT_GetDynamicNonGCStaticBase_Portable +LEAF_END JIT_GetDynamicNonGCStaticBase_SingleAppDomain, _TEXT -LEAF_ENTRY JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT - mov rax, rcx - ret -LEAF_END JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT - -LEAF_ENTRY JIT_GetSharedGCStaticBase_SingleAppDomain, _TEXT +LEAF_ENTRY JIT_GetDynamicGCStaticBase_SingleAppDomain, _TEXT ; If class is not initialized, bail to C++ helper - test byte ptr [rcx + OFFSETOF__DomainLocalModule__m_pDataBlob + rdx], 1 + test byte ptr [rcx + OFFSETOF__DynamicStaticsInfo__m_AuxData__m_dwFlags], 1 jz CallHelper - - mov rax, [rcx + OFFSETOF__DomainLocalModule__m_pGCStatics] + mov rax, [rcx + OFFSETOF__DynamicStaticsInfo__m_pGCStatics] REPRET align 16 CallHelper: - ; Tail call Jit_GetSharedGCStaticBase_Helper - jmp JIT_GetSharedGCStaticBase_Helper -LEAF_END JIT_GetSharedGCStaticBase_SingleAppDomain, _TEXT - -LEAF_ENTRY JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT - mov rax, [rcx + OFFSETOF__DomainLocalModule__m_pGCStatics] - ret -LEAF_END JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT + ; Tail call JIT_GetDynamicGCStaticBase_Portable + jmp JIT_GetDynamicGCStaticBase_Portable +LEAF_END JIT_GetDynamicGCStaticBase_SingleAppDomain, _TEXT end diff --git a/src/coreclr/vm/amd64/JitHelpers_Slow.asm b/src/coreclr/vm/amd64/JitHelpers_Slow.asm index b864801bfd60c..e2f58ac6618db 100644 --- a/src/coreclr/vm/amd64/JitHelpers_Slow.asm +++ b/src/coreclr/vm/amd64/JitHelpers_Slow.asm @@ -55,9 +55,6 @@ extern g_pStringClass:QWORD extern FramedAllocateString:proc extern JIT_NewArr1:proc -extern JIT_GetSharedNonGCStaticBase_Helper:proc -extern JIT_GetSharedGCStaticBase_Helper:proc - extern JIT_InternalThrow:proc ifdef _DEBUG diff --git a/src/coreclr/vm/amd64/asmconstants.h b/src/coreclr/vm/amd64/asmconstants.h index e12f3e1eafd26..6dc007a6a638e 100644 --- a/src/coreclr/vm/amd64/asmconstants.h +++ b/src/coreclr/vm/amd64/asmconstants.h @@ -202,22 +202,19 @@ ASMCONSTANTS_C_ASSERT(OFFSETOF__InterfaceInfo_t__m_pMethodTable ASMCONSTANTS_C_ASSERT(SIZEOF__InterfaceInfo_t == sizeof(InterfaceInfo_t)); -#define OFFSETOF__DomainLocalModule__m_pDataBlob 0x030 -ASMCONSTANTS_C_ASSERT(OFFSETOF__DomainLocalModule__m_pDataBlob - == offsetof(DomainLocalModule, m_pDataBlob)); +#define OFFSETOF__DynamicStaticsInfo__m_AuxData__m_dwFlags 0x18 +ASMCONSTANTS_C_ASSERT(OFFSETOF__DynamicStaticsInfo__m_AuxData__m_dwFlags + == offsetof(DynamicStaticsInfo, m_AuxData) + offsetof(MethodTableAuxiliaryData, m_dwFlags)); -// If this changes then we can't just test one bit in the assembly code. -ASMCONSTANTS_C_ASSERT(ClassInitFlags::INITIALIZED_FLAG == 1); +ASMCONSTANTS_C_ASSERT(MethodTableAuxiliaryData::enum_flag_Initialized == 0x1); -// End for JIT_GetSharedNonGCStaticBaseWorker +#define OFFSETOF__DynamicStaticsInfo__m_pNonGCStatics 0x8 +ASMCONSTANTS_C_ASSERT(OFFSETOF__DynamicStaticsInfo__m_pNonGCStatics + == offsetof(DynamicStaticsInfo, m_pNonGCStatics)); -// For JIT_GetSharedGCStaticBaseWorker - -#define OFFSETOF__DomainLocalModule__m_pGCStatics 0x020 -ASMCONSTANTS_C_ASSERT(OFFSETOF__DomainLocalModule__m_pGCStatics - == offsetof(DomainLocalModule, m_pGCStatics)); - -// End for JIT_GetSharedGCStaticBaseWorker +#define OFFSETOF__DynamicStaticsInfo__m_pGCStatics 0 +ASMCONSTANTS_C_ASSERT(OFFSETOF__DynamicStaticsInfo__m_pGCStatics + == offsetof(DynamicStaticsInfo, m_pGCStatics)); #define CORINFO_NullReferenceException_ASM 0 ASMCONSTANTS_C_ASSERT( CORINFO_NullReferenceException_ASM diff --git a/src/coreclr/vm/amd64/cgencpu.h b/src/coreclr/vm/amd64/cgencpu.h index 5dcaf12dfb812..a9f21cc148770 100644 --- a/src/coreclr/vm/amd64/cgencpu.h +++ b/src/coreclr/vm/amd64/cgencpu.h @@ -591,11 +591,7 @@ inline BOOL ClrFlushInstructionCache(LPCVOID pCodeAddr, size_t sizeOfCode, bool // // Create alias for optimized implementations of helpers provided on this platform // -#define JIT_GetSharedGCStaticBase JIT_GetSharedGCStaticBase_SingleAppDomain -#define JIT_GetSharedNonGCStaticBase JIT_GetSharedNonGCStaticBase_SingleAppDomain -#define JIT_GetSharedGCStaticBaseNoCtor JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain -#define JIT_GetSharedNonGCStaticBaseNoCtor JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain - - +#define JIT_GetDynamicGCStaticBase JIT_GetDynamicGCStaticBase_SingleAppDomain +#define JIT_GetDynamicNonGCStaticBase JIT_GetDynamicNonGCStaticBase_SingleAppDomain #endif // __cgencpu_h__ diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index 2bfdd6cf29422..9e82a6ff9efbe 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -665,14 +665,6 @@ void BaseDomain::InitVSD() GetLoaderAllocator()->InitVirtualCallStubManager(this); } -void BaseDomain::InitThreadStaticBlockTypeMap() -{ - STANDARD_VM_CONTRACT; - - m_NonGCThreadStaticBlockTypeIDMap.Init(); - m_GCThreadStaticBlockTypeIDMap.Init(); -} - void BaseDomain::ClearBinderContext() { CONTRACTL @@ -793,7 +785,7 @@ void AppDomain::SetNativeDllSearchDirectories(LPCWSTR wszNativeDllSearchDirector } } -OBJECTREF* BaseDomain::AllocateObjRefPtrsInLargeTable(int nRequested, OBJECTREF** ppLazyAllocate) +OBJECTREF* BaseDomain::AllocateObjRefPtrsInLargeTable(int nRequested, OBJECTREF** ppLazyAllocate, MethodTable *pMTToFillWithStaticBoxes) { CONTRACTL { @@ -819,6 +811,12 @@ OBJECTREF* BaseDomain::AllocateObjRefPtrsInLargeTable(int nRequested, OBJECTREF* // Allocate the handles. OBJECTREF* result = m_pPinnedHeapHandleTable->AllocateHandles(nRequested); + if (pMTToFillWithStaticBoxes != NULL) + { + GCPROTECT_BEGININTERIOR(result); + pMTToFillWithStaticBoxes->AllocateRegularStaticBoxes(&result); + GCPROTECT_END(); + } if (ppLazyAllocate) { // race with other threads that might be doing the same concurrent allocation @@ -970,9 +968,6 @@ void SystemDomain::Attach() m_SystemDomainCrst.Init(CrstSystemDomain, (CrstFlags)(CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN)); m_DelayedUnloadCrst.Init(CrstSystemDomainDelayedUnloadList, CRST_UNSAFE_COOPGC); - // Initialize the ID dispenser that is used for domain neutral module IDs - g_pModuleIndexDispenser = new IdDispenser(); - // Create the global SystemDomain and initialize it. m_pSystemDomain = new (&g_pSystemDomainMemory[0]) SystemDomain(); // No way it can fail since g_pSystemDomainMemory is a static array. @@ -1325,9 +1320,6 @@ void SystemDomain::LoadBaseSystemClasses() // Load the Object array class. g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT] = ClassLoader::LoadArrayTypeThrowing(TypeHandle(g_pObjectClass)); - // We have delayed allocation of CoreLib's static handles until we load the object class - CoreLibBinder::GetModule()->AllocateRegularStaticHandles(DefaultDomain()); - // Boolean has to be loaded first to break cycle in IComparisonOperations and IEqualityOperators CoreLibBinder::LoadPrimitiveType(ELEMENT_TYPE_BOOLEAN); @@ -1775,9 +1767,6 @@ void AppDomain::Create() // allocate a Virtual Call Stub Manager for the default domain pDomain->InitVSD(); - // allocate a thread static block to index map - pDomain->InitThreadStaticBlockTypeMap(); - pDomain->SetStage(AppDomain::STAGE_OPEN); pDomain->CreateDefaultBinder(); @@ -3052,18 +3041,16 @@ void AppDomain::SetupSharedStatics() // Because we are allocating/referencing objects, need to be in cooperative mode GCX_COOP(); - DomainLocalModule *pLocalModule = CoreLibBinder::GetModule()->GetDomainLocalModule(); - // This is a convenient place to initialize String.Empty. // It is treated as intrinsic by the JIT as so the static constructor would never run. // Leaving it uninitialized would confuse debuggers. - // String should not have any static constructors. - _ASSERTE(g_pStringClass->IsClassPreInited()); + // String should not have any static constructors, so this should be safe. It will just ensure that statics are allocated + g_pStringClass->CheckRunClassInitThrowing(); FieldDesc * pEmptyStringFD = CoreLibBinder::GetField(FIELD__STRING__EMPTY); OBJECTREF* pEmptyStringHandle = (OBJECTREF*) - ((TADDR)pLocalModule->GetPrecomputedGCStaticsBasePointer()+pEmptyStringFD->GetOffset()); + ((TADDR)g_pStringClass->GetDynamicStaticsInfo()->m_pGCStatics+pEmptyStringFD->GetOffset()); SetObjectReference( pEmptyStringHandle, StringObject::GetEmptyString()); } @@ -4079,284 +4066,8 @@ void AppDomain::ExceptionUnwind(Frame *pFrame) #endif // !DACCESS_COMPILE -DWORD DomainLocalModule::GetClassFlags(MethodTable* pMT, DWORD iClassIndex /*=(DWORD)-1*/) -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - } CONTRACTL_END; - - { - CONSISTENCY_CHECK(GetDomainAssembly()->GetModule() == pMT->GetModuleForStatics()); - } - - if (pMT->IsDynamicStatics()) - { - _ASSERTE(!pMT->ContainsGenericVariables()); - DWORD dynamicClassID = pMT->GetModuleDynamicEntryID(); - if(m_aDynamicEntries <= dynamicClassID) - return FALSE; - return (m_pDynamicClassTable[dynamicClassID].m_dwFlags); - } - else - { - if (iClassIndex == (DWORD)-1) - iClassIndex = pMT->GetClassIndex(); - return GetPrecomputedStaticsClassData()[iClassIndex]; - } -} - #ifndef DACCESS_COMPILE -void DomainLocalModule::SetClassInitialized(MethodTable* pMT) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - BaseDomain::DomainLocalBlockLockHolder lh(GetDomainAssembly()->GetAppDomain()); - - _ASSERTE(!IsClassInitialized(pMT)); - _ASSERTE(!IsClassInitError(pMT)); - - SetClassFlags(pMT, ClassInitFlags::INITIALIZED_FLAG); -} - -void DomainLocalModule::SetClassInitError(MethodTable* pMT) -{ - WRAPPER_NO_CONTRACT; - - BaseDomain::DomainLocalBlockLockHolder lh(GetDomainAssembly()->GetAppDomain()); - - SetClassFlags(pMT, ClassInitFlags::ERROR_FLAG); -} - -void DomainLocalModule::SetClassFlags(MethodTable* pMT, DWORD dwFlags) -{ - CONTRACTL { - THROWS; - GC_TRIGGERS; - PRECONDITION(GetDomainAssembly()->GetModule() == pMT->GetModuleForStatics()); - // Assumes BaseDomain::DomainLocalBlockLockHolder is taken - PRECONDITION(GetDomainAssembly()->GetAppDomain()->OwnDomainLocalBlockLock()); - } CONTRACTL_END; - - if (pMT->IsDynamicStatics()) - { - _ASSERTE(!pMT->ContainsGenericVariables()); - DWORD dwID = pMT->GetModuleDynamicEntryID(); - EnsureDynamicClassIndex(dwID); - m_pDynamicClassTable[dwID].m_dwFlags |= dwFlags; - } - else - { - GetPrecomputedStaticsClassData()[pMT->GetClassIndex()] |= dwFlags; - } -} - -void DomainLocalModule::EnsureDynamicClassIndex(DWORD dwID) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM();); - // Assumes BaseDomain::DomainLocalBlockLockHolder is taken - PRECONDITION(GetDomainAssembly()->GetAppDomain()->OwnDomainLocalBlockLock()); - } - CONTRACTL_END; - - SIZE_T oldDynamicEntries = m_aDynamicEntries.Load(); - - if (dwID < oldDynamicEntries) - { - _ASSERTE(m_pDynamicClassTable.Load() != NULL); - return; - } - - SIZE_T aDynamicEntries = max(16, oldDynamicEntries); - while (aDynamicEntries <= dwID) - { - aDynamicEntries *= 2; - } - - DynamicClassInfo* pNewDynamicClassTable; - pNewDynamicClassTable = (DynamicClassInfo*) - (void*)GetDomainAssembly()->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem( - S_SIZE_T(sizeof(DynamicClassInfo)) * S_SIZE_T(aDynamicEntries)); - - if (oldDynamicEntries != 0) - { - memcpy((void*)pNewDynamicClassTable, m_pDynamicClassTable, sizeof(DynamicClassInfo) * oldDynamicEntries); - } - - // Note: Memory allocated on loader heap is zero filled - // memset(pNewDynamicClassTable + m_aDynamicEntries, 0, (aDynamicEntries - m_aDynamicEntries) * sizeof(DynamicClassInfo)); - - _ASSERTE(m_aDynamicEntries%2 == 0); - - // Commit new dynamic table. The lock-free helpers depend on the order. - MemoryBarrier(); - m_pDynamicClassTable = pNewDynamicClassTable; - MemoryBarrier(); - m_aDynamicEntries = aDynamicEntries; -} - -void DomainLocalModule::AllocateDynamicClass(MethodTable *pMT) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - // Assumes BaseDomain::DomainLocalBlockLockHolder is taken - PRECONDITION(GetDomainAssembly()->GetAppDomain()->OwnDomainLocalBlockLock()); - } - CONTRACTL_END; - - _ASSERTE(!pMT->ContainsGenericVariables()); - _ASSERTE(!pMT->IsSharedByGenericInstantiations()); - _ASSERTE(GetDomainAssembly()->GetModule() == pMT->GetModuleForStatics()); - _ASSERTE(pMT->IsDynamicStatics()); - - DWORD dynamicEntryIDIndex = pMT->GetModuleDynamicEntryID(); - - EnsureDynamicClassIndex(dynamicEntryIDIndex); - - _ASSERTE(m_aDynamicEntries > dynamicEntryIDIndex); - - EEClass *pClass = pMT->GetClass(); - - DWORD dwStaticBytes = pClass->GetNonGCRegularStaticFieldBytes(); - DWORD dwNumHandleStatics = pClass->GetNumHandleRegularStatics(); - - _ASSERTE(!IsClassAllocated(pMT)); - _ASSERTE(!IsClassInitialized(pMT)); - _ASSERTE(!IsClassInitError(pMT)); - - DynamicEntry *pDynamicStatics = m_pDynamicClassTable[dynamicEntryIDIndex].m_pDynamicEntry; - - // We need this check because maybe a class had a cctor but no statics - if (dwStaticBytes > 0 || dwNumHandleStatics > 0) - { - if (pDynamicStatics == NULL) - { - LoaderHeap * pLoaderAllocator = GetDomainAssembly()->GetLoaderAllocator()->GetHighFrequencyHeap(); - - if (pMT->Collectible()) - { - pDynamicStatics = (DynamicEntry*)(void*)pLoaderAllocator->AllocMem(S_SIZE_T(sizeof(CollectibleDynamicEntry))); - } - else - { - SIZE_T dynamicEntrySize = DynamicEntry::GetOffsetOfDataBlob() + dwStaticBytes; - -#ifdef FEATURE_64BIT_ALIGNMENT - // Allocate memory with extra alignment only if it is really necessary - if (dwStaticBytes >= MAX_PRIMITIVE_FIELD_SIZE) - { - static_assert_no_msg(sizeof(NormalDynamicEntry) % MAX_PRIMITIVE_FIELD_SIZE == 0); - pDynamicStatics = (DynamicEntry*)(void*)pLoaderAllocator->AllocAlignedMem(dynamicEntrySize, MAX_PRIMITIVE_FIELD_SIZE); - } - else -#endif - pDynamicStatics = (DynamicEntry*)(void*)pLoaderAllocator->AllocMem(S_SIZE_T(dynamicEntrySize)); - } - - // Note: Memory allocated on loader heap is zero filled - - m_pDynamicClassTable[dynamicEntryIDIndex].m_pDynamicEntry = pDynamicStatics; - } - - if (pMT->Collectible() && (dwStaticBytes != 0)) - { - GCX_COOP(); - OBJECTREF nongcStaticsArray = NULL; - GCPROTECT_BEGIN(nongcStaticsArray); -#ifdef FEATURE_64BIT_ALIGNMENT - // Allocate memory with extra alignment only if it is really necessary - if (dwStaticBytes >= MAX_PRIMITIVE_FIELD_SIZE) - nongcStaticsArray = AllocatePrimitiveArray(ELEMENT_TYPE_I8, (dwStaticBytes + (sizeof(CLR_I8)-1)) / (sizeof(CLR_I8))); - else -#endif - nongcStaticsArray = AllocatePrimitiveArray(ELEMENT_TYPE_U1, dwStaticBytes); - ((CollectibleDynamicEntry *)pDynamicStatics)->m_hNonGCStatics = GetDomainAssembly()->GetModule()->GetLoaderAllocator()->AllocateHandle(nongcStaticsArray); - GCPROTECT_END(); - } - if (dwNumHandleStatics > 0) - { - if (!pMT->Collectible()) - { - GetAppDomain()->AllocateStaticFieldObjRefPtrs(dwNumHandleStatics, - &((NormalDynamicEntry *)pDynamicStatics)->m_pGCStatics); - } - else - { - GCX_COOP(); - OBJECTREF gcStaticsArray = NULL; - GCPROTECT_BEGIN(gcStaticsArray); - gcStaticsArray = AllocateObjectArray(dwNumHandleStatics, g_pObjectClass); - ((CollectibleDynamicEntry *)pDynamicStatics)->m_hGCStatics = GetDomainAssembly()->GetModule()->GetLoaderAllocator()->AllocateHandle(gcStaticsArray); - GCPROTECT_END(); - } - } - } -} - - -void DomainLocalModule::PopulateClass(MethodTable *pMT) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - } - CONTRACTL_END; - - _ASSERTE(!pMT->ContainsGenericVariables()); - - // the only work actually done here for non-dynamics is the freezing related work. - // See if we can eliminate this and make this a dynamic-only path - DWORD iClassIndex = pMT->GetClassIndex(); - - if (!IsClassAllocated(pMT, iClassIndex)) - { - BaseDomain::DomainLocalBlockLockHolder lh(GetDomainAssembly()->GetAppDomain()); - - if (!IsClassAllocated(pMT, iClassIndex)) - { - // Allocate dynamic space if necessary - if (pMT->IsDynamicStatics()) - AllocateDynamicClass(pMT); - - // determine flags to set on the statics block - DWORD dwFlags = ClassInitFlags::ALLOCATECLASS_FLAG; - - if (!pMT->HasClassConstructor() && !pMT->HasBoxedRegularStatics()) - { - _ASSERTE(!IsClassInitialized(pMT)); - _ASSERTE(!IsClassInitError(pMT)); - dwFlags |= ClassInitFlags::INITIALIZED_FLAG; - } - - if (pMT->Collectible()) - { - dwFlags |= ClassInitFlags::COLLECTIBLE_FLAG; - } - - // Set all flags at the same time to avoid races - SetClassFlags(pMT, dwFlags); - } - } - - return; -} - - DomainAssembly* AppDomain::RaiseTypeResolveEventThrowing(DomainAssembly* pAssembly, LPCSTR szName, ASSEMBLYREF *pResultingAssemblyRef) { CONTRACTL @@ -4666,57 +4377,6 @@ PTR_MethodTable BaseDomain::LookupType(UINT32 id) { return pMT; } -//------------------------------------------------------------------------ -UINT32 BaseDomain::GetNonGCThreadStaticTypeIndex(PTR_MethodTable pMT) -{ - CONTRACTL { - THROWS; - GC_TRIGGERS; - PRECONDITION(pMT->GetDomain() == this); - } CONTRACTL_END; - - return m_NonGCThreadStaticBlockTypeIDMap.GetTypeID(pMT, false); -} - -//------------------------------------------------------------------------ -PTR_MethodTable BaseDomain::LookupNonGCThreadStaticBlockType(UINT32 id) { - CONTRACTL { - NOTHROW; - WRAPPER(GC_TRIGGERS); - CONSISTENCY_CHECK(id != TYPE_ID_THIS_CLASS); - } CONTRACTL_END; - - PTR_MethodTable pMT = m_NonGCThreadStaticBlockTypeIDMap.LookupType(id); - - CONSISTENCY_CHECK(CheckPointer(pMT)); - return pMT; -} -//------------------------------------------------------------------------ -UINT32 BaseDomain::GetGCThreadStaticTypeIndex(PTR_MethodTable pMT) -{ - CONTRACTL { - THROWS; - GC_TRIGGERS; - PRECONDITION(pMT->GetDomain() == this); - } CONTRACTL_END; - - return m_GCThreadStaticBlockTypeIDMap.GetTypeID(pMT, false); -} - -//------------------------------------------------------------------------ -PTR_MethodTable BaseDomain::LookupGCThreadStaticBlockType(UINT32 id) { - CONTRACTL { - NOTHROW; - WRAPPER(GC_TRIGGERS); - CONSISTENCY_CHECK(id != TYPE_ID_THIS_CLASS); - } CONTRACTL_END; - - PTR_MethodTable pMT = m_GCThreadStaticBlockTypeIDMap.LookupType(id); - - CONSISTENCY_CHECK(CheckPointer(pMT)); - return pMT; -} - #ifndef DACCESS_COMPILE //--------------------------------------------------------------------------------------- void BaseDomain::RemoveTypesFromTypeIDMap(LoaderAllocator* pLoaderAllocator) @@ -5121,40 +4781,6 @@ size_t AppDomain::EstimateSize() #ifdef DACCESS_COMPILE -void -DomainLocalModule::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) -{ - SUPPORTS_DAC; - - // Enumerate the DomainLocalModule itself. DLMs are allocated to be larger than - // sizeof(DomainLocalModule) to make room for ClassInit flags and non-GC statics. - // "DAC_ENUM_DTHIS()" probably does not account for this, so we might not enumerate - // all of the ClassInit flags and non-GC statics. - // sizeof(DomainLocalModule) == 0x28 - DAC_ENUM_DTHIS(); - - if (m_pDomainAssembly.IsValid()) - { - m_pDomainAssembly->EnumMemoryRegions(flags); - } - - if (m_pDynamicClassTable.Load().IsValid()) - { - DacEnumMemoryRegion(dac_cast(m_pDynamicClassTable.Load()), - m_aDynamicEntries * sizeof(DynamicClassInfo)); - - for (SIZE_T i = 0; i < m_aDynamicEntries; i++) - { - PTR_DynamicEntry entry = dac_cast(m_pDynamicClassTable[i].m_pDynamicEntry.Load()); - if (entry.IsValid()) - { - // sizeof(DomainLocalModule::DynamicEntry) == 8 - entry.EnumMem(); - } - } - } -} - void AppDomain::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, bool enumThis) { diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp index 34218bde8bdd6..bf455d37fb16b 100644 --- a/src/coreclr/vm/appdomain.hpp +++ b/src/coreclr/vm/appdomain.hpp @@ -59,406 +59,6 @@ class RCWRefCache; #endif -GPTR_DECL(IdDispenser, g_pModuleIndexDispenser); - -// We would like *ALLOCATECLASS_FLAG to AV (in order to catch errors), so don't change it -struct ClassInitFlags { - enum - { - INITIALIZED_FLAG_BIT = 0, - INITIALIZED_FLAG = 1<(dynamicClassInfoParam);\ - DomainLocalModule::PTR_DynamicEntry pDynamicEntry = dac_cast((DomainLocalModule::DynamicEntry*)dynamicClassInfo->m_pDynamicEntry.Load()); \ - if ((dynamicClassInfo->m_dwFlags) & ClassInitFlags::COLLECTIBLE_FLAG) \ - {\ - PTRARRAYREF objArray;\ - objArray = (PTRARRAYREF)pLoaderAllocator->GetHandleValueFastCannotFailType2( \ - (dac_cast(pDynamicEntry))->m_hGCStatics);\ - *(pGCStatics) = dac_cast(PTR_READ(PTR_TO_TADDR(OBJECTREFToObject( objArray )) + offsetof(PtrArray, m_Array), objArray->GetNumComponents() * sizeof(void*))) ;\ - }\ - else\ - {\ - *(pGCStatics) = (dac_cast(pDynamicEntry))->GetGCStaticsBasePointer();\ - }\ - }\ - -#define GET_DYNAMICENTRY_NONGCSTATICS_BASEPOINTER(pLoaderAllocator, dynamicClassInfoParam, pNonGCStatics) \ - {\ - DomainLocalModule::PTR_DynamicClassInfo dynamicClassInfo = dac_cast(dynamicClassInfoParam);\ - DomainLocalModule::PTR_DynamicEntry pDynamicEntry = dac_cast((DomainLocalModule::DynamicEntry*)(dynamicClassInfo)->m_pDynamicEntry.Load()); \ - if (((dynamicClassInfo)->m_dwFlags) & ClassInitFlags::COLLECTIBLE_FLAG) \ - {\ - if ((dac_cast(pDynamicEntry))->m_hNonGCStatics != 0) \ - { \ - U1ARRAYREF objArray;\ - objArray = (U1ARRAYREF)pLoaderAllocator->GetHandleValueFastCannotFailType2( \ - (dac_cast(pDynamicEntry))->m_hNonGCStatics);\ - *(pNonGCStatics) = dac_cast(PTR_READ( \ - PTR_TO_TADDR(OBJECTREFToObject( objArray )) + sizeof(ArrayBase) - DomainLocalModule::DynamicEntry::GetOffsetOfDataBlob(), \ - objArray->GetNumComponents() * (DWORD)objArray->GetComponentSize() + DomainLocalModule::DynamicEntry::GetOffsetOfDataBlob())); \ - } else (*pNonGCStatics) = NULL; \ - }\ - else\ - {\ - *(pNonGCStatics) = dac_cast(pDynamicEntry)->GetNonGCStaticsBasePointer();\ - }\ - }\ - - struct DynamicEntry - { - static DWORD GetOffsetOfDataBlob(); - }; - typedef DPTR(DynamicEntry) PTR_DynamicEntry; - - struct CollectibleDynamicEntry : public DynamicEntry - { - LOADERHANDLE m_hGCStatics; - LOADERHANDLE m_hNonGCStatics; - }; - typedef DPTR(CollectibleDynamicEntry) PTR_CollectibleDynamicEntry; - - struct NormalDynamicEntry : public DynamicEntry - { - PTR_OBJECTREF m_pGCStatics; -#ifdef FEATURE_64BIT_ALIGNMENT - // Padding to make m_pDataBlob aligned at MAX_PRIMITIVE_FIELD_SIZE - // code:MethodTableBuilder::PlaceRegularStaticFields assumes that the start of the data blob is aligned - SIZE_T m_padding; -#endif - BYTE m_pDataBlob[0]; - - inline PTR_BYTE GetGCStaticsBasePointer() - { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - return dac_cast(m_pGCStatics); - } - inline PTR_BYTE GetNonGCStaticsBasePointer() - { - LIMITED_METHOD_CONTRACT - SUPPORTS_DAC; - return dac_cast(this); - } - }; - typedef DPTR(NormalDynamicEntry) PTR_NormalDynamicEntry; - - struct DynamicClassInfo - { - VolatilePtr m_pDynamicEntry; - Volatile m_dwFlags; - }; - typedef DPTR(DynamicClassInfo) PTR_DynamicClassInfo; - - inline UMEntryThunk * GetADThunkTable() - { - LIMITED_METHOD_CONTRACT - return m_pADThunkTable; - } - - inline void SetADThunkTable(UMEntryThunk* pADThunkTable) - { - LIMITED_METHOD_CONTRACT - InterlockedCompareExchangeT(m_pADThunkTable.GetPointer(), pADThunkTable, NULL); - } - - // Note the difference between: - // - // GetPrecomputedNonGCStaticsBasePointer() and - // GetPrecomputedStaticsClassData() - // - // GetPrecomputedNonGCStaticsBasePointer returns the pointer that should be added to field offsets to retrieve statics - // GetPrecomputedStaticsClassData returns a pointer to the first byte of the precomputed statics block - inline TADDR GetPrecomputedNonGCStaticsBasePointer() - { - LIMITED_METHOD_CONTRACT - return dac_cast(this); - } - - inline PTR_BYTE GetPrecomputedStaticsClassData() - { - LIMITED_METHOD_CONTRACT - return dac_cast(this) + offsetof(DomainLocalModule, m_pDataBlob); - } - - static SIZE_T GetOffsetOfDataBlob() { return offsetof(DomainLocalModule, m_pDataBlob); } - static SIZE_T GetOffsetOfGCStaticPointer() { return offsetof(DomainLocalModule, m_pGCStatics); } - - inline DomainAssembly* GetDomainAssembly() - { - LIMITED_METHOD_CONTRACT - SUPPORTS_DAC; - return m_pDomainAssembly; - } - -#ifndef DACCESS_COMPILE - inline void SetDomainAssembly(DomainAssembly* pDomainAssembly) - { - LIMITED_METHOD_CONTRACT - m_pDomainAssembly = pDomainAssembly; - } -#endif - - inline PTR_OBJECTREF GetPrecomputedGCStaticsBasePointer() - { - LIMITED_METHOD_CONTRACT - return m_pGCStatics; - } - - inline PTR_OBJECTREF * GetPrecomputedGCStaticsBasePointerAddress() - { - LIMITED_METHOD_CONTRACT - return &m_pGCStatics; - } - - // Returns bytes so we can add offsets - inline PTR_BYTE GetGCStaticsBasePointer(MethodTable * pMT) - { - WRAPPER_NO_CONTRACT - SUPPORTS_DAC; - - if (pMT->IsDynamicStatics()) - { - _ASSERTE(GetDomainAssembly()->GetModule() == pMT->GetModuleForStatics()); - return GetDynamicEntryGCStaticsBasePointer(pMT->GetModuleDynamicEntryID(), pMT->GetLoaderAllocator()); - } - else - { - return dac_cast(m_pGCStatics); - } - } - - inline PTR_BYTE GetNonGCStaticsBasePointer(MethodTable * pMT) - { - WRAPPER_NO_CONTRACT - SUPPORTS_DAC; - - if (pMT->IsDynamicStatics()) - { - _ASSERTE(GetDomainAssembly()->GetModule() == pMT->GetModuleForStatics()); - return GetDynamicEntryNonGCStaticsBasePointer(pMT->GetModuleDynamicEntryID(), pMT->GetLoaderAllocator()); - } - else - { - return dac_cast(this); - } - } - - inline DynamicClassInfo* GetDynamicClassInfo(DWORD n) - { - LIMITED_METHOD_CONTRACT - SUPPORTS_DAC; - _ASSERTE(m_pDynamicClassTable.Load() && m_aDynamicEntries > n); - dac_cast(m_pDynamicClassTable[n].m_pDynamicEntry.Load()); - - return &m_pDynamicClassTable[n]; - } - - // These helpers can now return null, as the debugger may do queries on a type - // before the calls to PopulateClass happen - inline PTR_BYTE GetDynamicEntryGCStaticsBasePointer(DWORD n, PTR_LoaderAllocator pLoaderAllocator) - { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - SUPPORTS_DAC; - } - CONTRACTL_END; - - - if (n >= m_aDynamicEntries) - { - return NULL; - } - - DynamicClassInfo* pClassInfo = GetDynamicClassInfo(n); - if (!pClassInfo->m_pDynamicEntry) - { - return NULL; - } - - PTR_BYTE retval = NULL; - - GET_DYNAMICENTRY_GCSTATICS_BASEPOINTER(pLoaderAllocator, pClassInfo, &retval); - - return retval; - } - - inline PTR_BYTE GetDynamicEntryNonGCStaticsBasePointer(DWORD n, PTR_LoaderAllocator pLoaderAllocator) - { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - SUPPORTS_DAC; - } - CONTRACTL_END; - - - if (n >= m_aDynamicEntries) - { - return NULL; - } - - DynamicClassInfo* pClassInfo = GetDynamicClassInfo(n); - if (!pClassInfo->m_pDynamicEntry) - { - return NULL; - } - - PTR_BYTE retval = NULL; - - GET_DYNAMICENTRY_NONGCSTATICS_BASEPOINTER(pLoaderAllocator, pClassInfo, &retval); - - return retval; - } - - FORCEINLINE PTR_DynamicClassInfo GetDynamicClassInfoIfInitialized(DWORD n) - { - WRAPPER_NO_CONTRACT; - - // m_aDynamicEntries is set last, it needs to be checked first - if (n >= m_aDynamicEntries) - { - return NULL; - } - - _ASSERTE(m_pDynamicClassTable.Load() != NULL); - PTR_DynamicClassInfo pDynamicClassInfo = (PTR_DynamicClassInfo)(m_pDynamicClassTable.Load() + n); - - // INITIALIZED_FLAG is set last, it needs to be checked first - if ((pDynamicClassInfo->m_dwFlags & ClassInitFlags::INITIALIZED_FLAG) == 0) - { - return NULL; - } - - PREFIX_ASSUME(pDynamicClassInfo != NULL); - return pDynamicClassInfo; - } - - // iClassIndex is slightly expensive to compute, so if we already know - // it, we can use this helper - inline BOOL IsClassInitialized(MethodTable* pMT, DWORD iClassIndex = (DWORD)-1) - { - WRAPPER_NO_CONTRACT; - return (GetClassFlags(pMT, iClassIndex) & ClassInitFlags::INITIALIZED_FLAG) != 0; - } - - inline BOOL IsPrecomputedClassInitialized(DWORD classID) - { - return GetPrecomputedStaticsClassData()[classID] & ClassInitFlags::INITIALIZED_FLAG; - } - - inline BOOL IsClassAllocated(MethodTable* pMT, DWORD iClassIndex = (DWORD)-1) - { - WRAPPER_NO_CONTRACT; - return (GetClassFlags(pMT, iClassIndex) & ClassInitFlags::ALLOCATECLASS_FLAG) != 0; - } - - BOOL IsClassInitError(MethodTable* pMT, DWORD iClassIndex = (DWORD)-1) - { - WRAPPER_NO_CONTRACT; - return (GetClassFlags(pMT, iClassIndex) & ClassInitFlags::ERROR_FLAG) != 0; - } - - void SetClassInitialized(MethodTable* pMT); - void SetClassInitError(MethodTable* pMT); - - void EnsureDynamicClassIndex(DWORD dwID); - - void AllocateDynamicClass(MethodTable *pMT); - - void PopulateClass(MethodTable *pMT); - -#ifdef DACCESS_COMPILE - void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); -#endif - - static DWORD OffsetOfDataBlob() - { - LIMITED_METHOD_CONTRACT; - return offsetof(DomainLocalModule, m_pDataBlob); - } - - FORCEINLINE MethodTable * GetMethodTableFromClassDomainID(DWORD dwClassDomainID) - { - DWORD rid = (DWORD)(dwClassDomainID) + 1; - TypeHandle th = GetDomainAssembly()->GetModule()->LookupTypeDef(TokenFromRid(rid, mdtTypeDef)); - _ASSERTE(!th.IsNull()); - MethodTable * pMT = th.AsMethodTable(); - PREFIX_ASSUME(pMT != NULL); - return pMT; - } - -private: - friend void EmitFastGetSharedStaticBase(CPUSTUBLINKER *psl, CodeLabel *init, bool bCCtorCheck); - - void SetClassFlags(MethodTable* pMT, DWORD dwFlags); - DWORD GetClassFlags(MethodTable* pMT, DWORD iClassIndex); - - PTR_DomainAssembly m_pDomainAssembly; - VolatilePtr m_pDynamicClassTable; // used for generics and reflection.emit in memory - Volatile m_aDynamicEntries; // number of entries in dynamic table - VolatilePtr m_pADThunkTable; - PTR_OBJECTREF m_pGCStatics; // Handle to GC statics of the module - - // In addition to storing the ModuleIndex in the Module class, we also - // keep a copy of the ModuleIndex in the DomainLocalModule class. This - // allows the thread static JIT helpers to quickly convert a pointer to - // a DomainLocalModule into a ModuleIndex. - ModuleIndex m_ModuleIndex; - - // Note that the static offset calculation in code:Module::BuildStaticsOffsets takes the offset m_pDataBlob - // into consideration for alignment so we do not need any padding to ensure that the start of the data blob is aligned - - BYTE m_pDataBlob[0]; // First byte of the statics blob - - // Layout of m_pDataBlob is: - // ClassInit bytes (hold flags for cctor run, cctor error, etc) - // Non GC Statics - -public: - - // The Module class need to be able to initialized ModuleIndex, - // so for now I will make it a friend.. - friend class Module; - - FORCEINLINE ModuleIndex GetModuleIndex() - { - LIMITED_METHOD_DAC_CONTRACT; - return m_ModuleIndex; - } - -}; // struct DomainLocalModule - -#define OFFSETOF__DomainLocalModule__m_pDataBlob_ (6 * TARGET_POINTER_SIZE) -#ifdef FEATURE_64BIT_ALIGNMENT -#define OFFSETOF__DomainLocalModule__NormalDynamicEntry__m_pDataBlob (TARGET_POINTER_SIZE /* m_pGCStatics */ + TARGET_POINTER_SIZE /* m_padding */) -#else -#define OFFSETOF__DomainLocalModule__NormalDynamicEntry__m_pDataBlob TARGET_POINTER_SIZE /* m_pGCStatics */ -#endif - #ifdef _MSC_VER #pragma warning(pop) #endif @@ -984,7 +584,7 @@ class BaseDomain // Statics and reflection info (Types, MemberInfo,..) are stored this way // If ppLazyAllocate != 0, allocation will only take place if *ppLazyAllocate != 0 (and the allocation // will be properly serialized) - OBJECTREF *AllocateObjRefPtrsInLargeTable(int nRequested, OBJECTREF** ppLazyAllocate = NULL); + OBJECTREF *AllocateObjRefPtrsInLargeTable(int nRequested, OBJECTREF** ppLazyAllocate = NULL, MethodTable *pMTToFillWithStaticBoxes = NULL); //**************************************************************************************** // Handles @@ -1214,21 +814,8 @@ class BaseDomain private: TypeIDMap m_typeIDMap; - // MethodTable to `typeIndex` map. `typeIndex` is embedded in the code during codegen. - // During execution corresponding thread static data blocks are stored in `t_NonGCThreadStaticBlocks` - // and `t_GCThreadStaticBlocks` array at the `typeIndex`. - TypeIDMap m_NonGCThreadStaticBlockTypeIDMap; - TypeIDMap m_GCThreadStaticBlockTypeIDMap; - public: - void InitThreadStaticBlockTypeMap(); - - UINT32 GetNonGCThreadStaticTypeIndex(PTR_MethodTable pMT); - UINT32 GetGCThreadStaticTypeIndex(PTR_MethodTable pMT); - - PTR_MethodTable LookupNonGCThreadStaticBlockType(UINT32 id); - PTR_MethodTable LookupGCThreadStaticBlockType(UINT32 id); UINT32 GetTypeID(PTR_MethodTable pMT); UINT32 LookupTypeID(PTR_MethodTable pMT); diff --git a/src/coreclr/vm/appdomain.inl b/src/coreclr/vm/appdomain.inl index 46f079f4b49c6..ee77268eae68f 100644 --- a/src/coreclr/vm/appdomain.inl +++ b/src/coreclr/vm/appdomain.inl @@ -78,14 +78,5 @@ inline PTR_LoaderHeap AppDomain::GetStubHeap() return GetLoaderAllocator()->GetStubHeap(); } -/* static */ -inline DWORD DomainLocalModule::DynamicEntry::GetOffsetOfDataBlob() -{ - LIMITED_METHOD_CONTRACT; - _ASSERTE(DWORD(offsetof(NormalDynamicEntry, m_pDataBlob)) == offsetof(NormalDynamicEntry, m_pDataBlob)); - return (DWORD)offsetof(NormalDynamicEntry, m_pDataBlob); -} - - #endif // _APPDOMAIN_I diff --git a/src/coreclr/vm/array.cpp b/src/coreclr/vm/array.cpp index 25f9c64165a7c..9bed362795c69 100644 --- a/src/coreclr/vm/array.cpp +++ b/src/coreclr/vm/array.cpp @@ -334,7 +334,7 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy MethodTable* pMT = (MethodTable *) pMTHead; // Allocate the private data block ("private" during runtime in the ngen'ed case). - pMT->AllocateAuxiliaryData(pAllocator, this, pamTracker, false, static_cast(numNonVirtualSlots)); + pMT->AllocateAuxiliaryData(pAllocator, this, pamTracker, MethodTableStaticsFlags::None, static_cast(numNonVirtualSlots)); pMT->SetLoaderAllocator(pAllocator); pMT->SetModule(elemTypeHnd.GetModule()); @@ -408,7 +408,7 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy } #endif // FEATURE_TYPEEQUIVALENCE - _ASSERTE(pMT->IsClassPreInited()); + pMT->SetClassInited(); // Set BaseSize to be size of non-data portion of the array DWORD baseSize = ARRAYBASE_BASESIZE; diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index 17c79007f43db..4f64f7c5d6b4b 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -473,19 +473,12 @@ void Module::Initialize(AllocMemTracker *pamTracker, LPCWSTR szName) m_pInstMethodHashTable = InstMethodHashTable::Create(GetLoaderAllocator(), this, PARAMMETHODS_HASH_BUCKETS, pamTracker); } - // this will be initialized a bit later. - m_ModuleID = NULL; - m_ModuleIndex.m_dwIndex = (SIZE_T)-1; - // These will be initialized in NotifyProfilerLoadFinished, set them to // a safe initial value now. m_dwTypeCount = 0; m_dwExportedTypeCount = 0; m_dwCustomAttributeCount = 0; - // Prepare statics that are known at module load time - AllocateStatics(pamTracker); - if (m_AssemblyRefByNameTable == NULL) { Module::CreateAssemblyRefByNameTable(pamTracker); @@ -770,10 +763,6 @@ void Module::Destruct() #if defined(PROFILING_SUPPORTED) delete m_pJitInlinerTrackingMap; #endif - - // If this module was loaded as domain-specific, then - // we must free its ModuleIndex so that it can be reused - FreeModuleIndex(); } bool Module::NeedsGlobalMethodTable() @@ -858,365 +847,12 @@ DomainAssembly* Module::GetDomainAssembly() { LIMITED_METHOD_DAC_CONTRACT; - return dac_cast(m_ModuleID->GetDomainAssembly()); + return m_pDomainAssembly; } #ifndef DACCESS_COMPILE #include "staticallocationhelpers.inl" -// Parses metadata and initializes offsets of per-class static blocks. -void Module::BuildStaticsOffsets(AllocMemTracker *pamTracker) -{ - STANDARD_VM_CONTRACT; - - // Trade off here. We want a slot for each type. That way we can get to 2 bits per class and - // index directly and not need a mapping from ClassID to MethodTable (we will use the RID - // as the mapping) - IMDInternalImport *pImport = GetMDImport(); - - DWORD * pRegularStaticOffsets = NULL; - DWORD * pThreadStaticOffsets = NULL; - - // Get the number of types/classes defined in this module. Add 1 to count the module itself - DWORD dwNumTypes = pImport->GetCountWithTokenKind(mdtTypeDef) + 1; // +1 for module type - - // [0] covers regular statics, [1] covers thread statics - DWORD dwGCHandles[2] = { 0, 0 }; - - // Organization in memory of the static block - // - // - // | GC Statics | - // | - // | - // | Class Data (one byte per class) | pointer to gc statics | primitive type statics | - // - // -#ifndef CROSSBITNESS_COMPILE - // The assertions must hold in every non-crossbitness scenario - _ASSERTE(OFFSETOF__DomainLocalModule__m_pDataBlob_ == DomainLocalModule::OffsetOfDataBlob()); - _ASSERTE(OFFSETOF__ThreadLocalModule__m_pDataBlob == ThreadLocalModule::OffsetOfDataBlob()); -#endif - - DWORD dwNonGCBytes[2] = { - DomainLocalModule::OffsetOfDataBlob() + (DWORD)(sizeof(BYTE)*dwNumTypes), - ThreadLocalModule::OffsetOfDataBlob() + (DWORD)(sizeof(BYTE)*dwNumTypes) - }; - - HENUMInternalHolder hTypeEnum(pImport); - hTypeEnum.EnumAllInit(mdtTypeDef); - - mdTypeDef type; - // Parse each type of the class - while (pImport->EnumNext(&hTypeEnum, &type)) - { - // Set offset for this type - DWORD dwIndex = RidFromToken(type) - 1; - - // [0] covers regular statics, [1] covers thread statics - DWORD dwAlignment[2] = { 1, 1 }; - DWORD dwClassNonGCBytes[2] = { 0, 0 }; - DWORD dwClassGCHandles[2] = { 0, 0 }; - - // need to check if the type is generic and if so exclude it from iteration as we don't know the size - if (!m_pTypeGenericInfoMap->IsGeneric(type, pImport)) - { - HENUMInternalHolder hFieldEnum(pImport); - hFieldEnum.EnumInit(mdtFieldDef, type); - - mdFieldDef field; - // Parse each field of the type - while (pImport->EnumNext(&hFieldEnum, &field)) - { - BOOL fSkip = FALSE; - - CorElementType ElementType = ELEMENT_TYPE_END; - mdToken tkValueTypeToken = 0; - int kk; // Use one set of variables for regular statics, and the other set for thread statics - - fSkip = GetStaticFieldElementTypeForFieldDef(this, pImport, field, &ElementType, &tkValueTypeToken, &kk); - if (fSkip) - continue; - - // We account for "regular statics" and "thread statics" separately. - // Currently we are lumping RVA into "regular statics", - // but we probably shouldn't. - switch (ElementType) - { - case ELEMENT_TYPE_I1: - case ELEMENT_TYPE_U1: - case ELEMENT_TYPE_BOOLEAN: - dwClassNonGCBytes[kk] += 1; - break; - case ELEMENT_TYPE_I2: - case ELEMENT_TYPE_U2: - case ELEMENT_TYPE_CHAR: - dwAlignment[kk] = max(2, dwAlignment[kk]); - dwClassNonGCBytes[kk] += 2; - break; - case ELEMENT_TYPE_I4: - case ELEMENT_TYPE_U4: - case ELEMENT_TYPE_R4: - dwAlignment[kk] = max(4, dwAlignment[kk]); - dwClassNonGCBytes[kk] += 4; - break; - case ELEMENT_TYPE_FNPTR: - case ELEMENT_TYPE_PTR: - case ELEMENT_TYPE_I: - case ELEMENT_TYPE_U: - dwAlignment[kk] = max((1 << LOG2_PTRSIZE), dwAlignment[kk]); - dwClassNonGCBytes[kk] += (1 << LOG2_PTRSIZE); - break; - case ELEMENT_TYPE_I8: - case ELEMENT_TYPE_U8: - case ELEMENT_TYPE_R8: - dwAlignment[kk] = max(8, dwAlignment[kk]); - dwClassNonGCBytes[kk] += 8; - break; - case ELEMENT_TYPE_VAR: - case ELEMENT_TYPE_MVAR: - case ELEMENT_TYPE_STRING: - case ELEMENT_TYPE_SZARRAY: - case ELEMENT_TYPE_ARRAY: - case ELEMENT_TYPE_CLASS: - case ELEMENT_TYPE_OBJECT: - dwClassGCHandles[kk] += 1; - break; - case ELEMENT_TYPE_VALUETYPE: - // Statics for valuetypes where the valuetype is defined in this module are handled here. Other valuetype statics utilize the pessimistic model below. - dwClassGCHandles[kk] += 1; - break; - case ELEMENT_TYPE_END: - default: - // The actual element type was ELEMENT_TYPE_VALUETYPE, but the as we don't want to load additional assemblies - // to determine these static offsets, we've fallen back to a pessimistic model. - if (tkValueTypeToken != 0) - { - // We'll have to be pessimistic here - dwClassNonGCBytes[kk] += MAX_PRIMITIVE_FIELD_SIZE; - dwAlignment[kk] = max(MAX_PRIMITIVE_FIELD_SIZE, dwAlignment[kk]); - - dwClassGCHandles[kk] += 1; - break; - } - else - { - // field has an unexpected type - ThrowHR(VER_E_FIELD_SIG); - break; - } - } - } - - if (pRegularStaticOffsets == NULL && (dwClassGCHandles[0] != 0 || dwClassNonGCBytes[0] != 0)) - { - // Lazily allocate table for offsets. We need offsets for GC and non GC areas. We add +1 to use as a sentinel. - pRegularStaticOffsets = (PTR_DWORD)pamTracker->Track( - GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem( - (S_SIZE_T(2 * sizeof(DWORD))*(S_SIZE_T(dwNumTypes)+S_SIZE_T(1))))); - - for (DWORD i = 0; i < dwIndex; i++) { - pRegularStaticOffsets[i * 2 ] = dwGCHandles[0]*TARGET_POINTER_SIZE; - pRegularStaticOffsets[i * 2 + 1] = dwNonGCBytes[0]; - } - } - - if (pThreadStaticOffsets == NULL && (dwClassGCHandles[1] != 0 || dwClassNonGCBytes[1] != 0)) - { - // Lazily allocate table for offsets. We need offsets for GC and non GC areas. We add +1 to use as a sentinel. - pThreadStaticOffsets = (PTR_DWORD)pamTracker->Track( - GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem( - (S_SIZE_T(2 * sizeof(DWORD))*(S_SIZE_T(dwNumTypes)+S_SIZE_T(1))))); - - for (DWORD i = 0; i < dwIndex; i++) { - pThreadStaticOffsets[i * 2 ] = dwGCHandles[1]*TARGET_POINTER_SIZE; - pThreadStaticOffsets[i * 2 + 1] = dwNonGCBytes[1]; - } - } - } - - if (pRegularStaticOffsets != NULL) - { - // Align the offset of non gc statics - dwNonGCBytes[0] = (DWORD) ALIGN_UP(dwNonGCBytes[0], dwAlignment[0]); - - // Save current offsets - pRegularStaticOffsets[dwIndex*2] = dwGCHandles[0]*TARGET_POINTER_SIZE; - pRegularStaticOffsets[dwIndex*2 + 1] = dwNonGCBytes[0]; - - // Increment for next class - dwGCHandles[0] += dwClassGCHandles[0]; - dwNonGCBytes[0] += dwClassNonGCBytes[0]; - } - - if (pThreadStaticOffsets != NULL) - { - // Align the offset of non gc statics - dwNonGCBytes[1] = (DWORD) ALIGN_UP(dwNonGCBytes[1], dwAlignment[1]); - - // Save current offsets - pThreadStaticOffsets[dwIndex*2] = dwGCHandles[1]*TARGET_POINTER_SIZE; - pThreadStaticOffsets[dwIndex*2 + 1] = dwNonGCBytes[1]; - - // Increment for next class - dwGCHandles[1] += dwClassGCHandles[1]; - dwNonGCBytes[1] += dwClassNonGCBytes[1]; - } - } - - m_maxTypeRidStaticsAllocated = dwNumTypes; - - if (pRegularStaticOffsets != NULL) - { - pRegularStaticOffsets[dwNumTypes*2] = dwGCHandles[0]*TARGET_POINTER_SIZE; - pRegularStaticOffsets[dwNumTypes*2 + 1] = dwNonGCBytes[0]; - } - - if (pThreadStaticOffsets != NULL) - { - pThreadStaticOffsets[dwNumTypes*2] = dwGCHandles[1]*TARGET_POINTER_SIZE; - pThreadStaticOffsets[dwNumTypes*2 + 1] = dwNonGCBytes[1]; - } - - m_pRegularStaticOffsets = pRegularStaticOffsets; - m_pThreadStaticOffsets = pThreadStaticOffsets; - - m_dwMaxGCRegularStaticHandles = dwGCHandles[0]; - m_dwMaxGCThreadStaticHandles = dwGCHandles[1]; - - m_dwRegularStaticsBlockSize = dwNonGCBytes[0]; - m_dwThreadStaticsBlockSize = dwNonGCBytes[1]; -} - -void Module::GetOffsetsForRegularStaticData( - mdToken cl, - BOOL bDynamic, DWORD dwGCStaticHandles, - DWORD dwNonGCStaticBytes, - DWORD * pOutStaticHandleOffset, - DWORD * pOutNonGCStaticOffset) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - INJECT_FAULT(COMPlusThrowOM()); - } - CONTRACTL_END - - *pOutStaticHandleOffset = 0; - *pOutNonGCStaticOffset = 0; - - if (!dwGCStaticHandles && !dwNonGCStaticBytes) - { - return; - } - -#ifndef CROSSBITNESS_COMPILE - _ASSERTE(OFFSETOF__DomainLocalModule__NormalDynamicEntry__m_pDataBlob == DomainLocalModule::DynamicEntry::GetOffsetOfDataBlob()); -#endif - // Statics for instantiated types are allocated dynamically per-instantiation - if (bDynamic) - { - // Non GC statics are embedded in the Dynamic Entry. - *pOutNonGCStaticOffset = OFFSETOF__DomainLocalModule__NormalDynamicEntry__m_pDataBlob; - return; - } - - if (m_pRegularStaticOffsets == NULL) - { - THROW_BAD_FORMAT(BFA_METADATA_CORRUPT, this); - } - _ASSERTE(m_pRegularStaticOffsets != (PTR_DWORD) NGEN_STATICS_ALLCLASSES_WERE_LOADED); - - // We allocate in the big blob. - DWORD index = RidFromToken(cl) - 1; - - *pOutStaticHandleOffset = m_pRegularStaticOffsets[index*2]; - - *pOutNonGCStaticOffset = m_pRegularStaticOffsets[index*2 + 1]; -#ifdef CROSSBITNESS_COMPILE - *pOutNonGCStaticOffset += OFFSETOF__DomainLocalModule__m_pDataBlob_ - DomainLocalModule::OffsetOfDataBlob(); -#endif - - // Check we didnt go out of what we predicted we would need for the class - if (*pOutStaticHandleOffset + TARGET_POINTER_SIZE*dwGCStaticHandles > - m_pRegularStaticOffsets[(index+1)*2] || - *pOutNonGCStaticOffset + dwNonGCStaticBytes > - m_pRegularStaticOffsets[(index+1)*2 + 1]) - { // It's most likely that this is due to bad metadata, thus the exception. However, the - // previous comments for this bit of code mentioned that this could be a corner case bug - // with static field size estimation, though this is entirely unlikely since the code has - // been this way for at least two releases. - THROW_BAD_FORMAT(BFA_METADATA_CORRUPT, this); - } -} - - -void Module::GetOffsetsForThreadStaticData( - mdToken cl, - BOOL bDynamic, DWORD dwGCStaticHandles, - DWORD dwNonGCStaticBytes, - DWORD * pOutStaticHandleOffset, - DWORD * pOutNonGCStaticOffset) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - INJECT_FAULT(COMPlusThrowOM()); - } - CONTRACTL_END - - *pOutStaticHandleOffset = 0; - *pOutNonGCStaticOffset = 0; - - if (!dwGCStaticHandles && !dwNonGCStaticBytes) - { - return; - } - -#ifndef CROSSBITNESS_COMPILE - _ASSERTE(OFFSETOF__ThreadLocalModule__DynamicEntry__m_pDataBlob == ThreadLocalModule::DynamicEntry::GetOffsetOfDataBlob()); -#endif - // Statics for instantiated types are allocated dynamically per-instantiation - if (bDynamic) - { - // Non GC thread statics are embedded in the Dynamic Entry. - *pOutNonGCStaticOffset = OFFSETOF__ThreadLocalModule__DynamicEntry__m_pDataBlob; - return; - } - - if (m_pThreadStaticOffsets == NULL) - { - THROW_BAD_FORMAT(BFA_METADATA_CORRUPT, this); - } - _ASSERTE(m_pThreadStaticOffsets != (PTR_DWORD) NGEN_STATICS_ALLCLASSES_WERE_LOADED); - - // We allocate in the big blob. - DWORD index = RidFromToken(cl) - 1; - - *pOutStaticHandleOffset = m_pThreadStaticOffsets[index*2]; - - *pOutNonGCStaticOffset = m_pThreadStaticOffsets[index*2 + 1]; -#ifdef CROSSBITNESS_COMPILE - *pOutNonGCStaticOffset += OFFSETOF__ThreadLocalModule__m_pDataBlob - ThreadLocalModule::GetOffsetOfDataBlob(); -#endif - - // Check we didnt go out of what we predicted we would need for the class - if (*pOutStaticHandleOffset + TARGET_POINTER_SIZE*dwGCStaticHandles > - m_pThreadStaticOffsets[(index+1)*2] || - *pOutNonGCStaticOffset + dwNonGCStaticBytes > - m_pThreadStaticOffsets[(index+1)*2 + 1]) - { - // It's most likely that this is due to bad metadata, thus the exception. However, the - // previous comments for this bit of code mentioned that this could be a corner case bug - // with static field size estimation, though this is entirely unlikely since the code has - // been this way for at least two releases. - THROW_BAD_FORMAT(BFA_METADATA_CORRUPT, this); - } -} - - // initialize Crst controlling the Dynamic IL hashtable void Module::InitializeDynamicILCrst() { @@ -1537,240 +1173,11 @@ BOOL Module::IsRuntimeMarshallingEnabled() return hr != S_OK; } - -DWORD Module::AllocateDynamicEntry(MethodTable *pMT) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - PRECONDITION(pMT->GetModuleForStatics() == this); - PRECONDITION(pMT->IsDynamicStatics()); - PRECONDITION(!pMT->ContainsGenericVariables()); - } - CONTRACTL_END; - - CrstHolder ch(&m_Crst); - DWORD newId = (LONG)m_cDynamicEntries++; - - if (newId >= m_maxDynamicEntries) - { - SIZE_T maxDynamicEntries = max(16, m_maxDynamicEntries); - while (maxDynamicEntries <= newId) - { - maxDynamicEntries *= 2; - } - - DynamicStaticsInfo* pNewDynamicStaticsInfo = (DynamicStaticsInfo*) - (void*)GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(DynamicStaticsInfo)) * S_SIZE_T(maxDynamicEntries)); - - if (m_pDynamicStaticsInfo) - memcpy(pNewDynamicStaticsInfo, m_pDynamicStaticsInfo, sizeof(DynamicStaticsInfo) * m_maxDynamicEntries); - - m_pDynamicStaticsInfo = pNewDynamicStaticsInfo; - m_maxDynamicEntries = maxDynamicEntries; - } - - m_pDynamicStaticsInfo[newId].pEnclosingMT = pMT; - - LOG((LF_CLASSLOADER, LL_INFO10000, "STATICS: Assigned dynamic ID %d to %s\n", newId, pMT->GetDebugClassName())); - - return newId; -} - -void Module::FreeModuleIndex() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - if (m_ModuleID != NULL) - { - _ASSERTE(m_ModuleIndex == m_ModuleID->GetModuleIndex()); - - if (IsCollectible()) - { - ThreadStoreLockHolder tsLock; - Thread *pThread = NULL; - while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL) - { - pThread->DeleteThreadStaticData(m_ModuleIndex); - } - } - - // Get the ModuleIndex from the DLM and free it - Module::FreeModuleIndex(m_ModuleIndex); - } - else - { - // This was an empty, short-lived Module object that - // was never assigned a ModuleIndex... - } -} - - - - -ModuleIndex Module::AllocateModuleIndex() -{ - DWORD val; - g_pModuleIndexDispenser->NewId(NULL, val); - - // For various reasons, the IDs issued by the IdDispenser start at 1. - // Domain neutral module IDs have historically started at 0, and we - // have always assigned ID 0 to CoreLib. Thus, to make it so that - // domain neutral module IDs start at 0, we will subtract 1 from the - // ID that we got back from the ID dispenser. - ModuleIndex index((SIZE_T)(val-1)); - - return index; -} - -void Module::FreeModuleIndex(ModuleIndex index) -{ - WRAPPER_NO_CONTRACT; - // We subtracted 1 after we allocated this ID, so we need to - // add 1 before we free it. - SIZE_T val = index.m_dwIndex + 1; - - _ASSERTE(val <= MAXDWORD); - g_pModuleIndexDispenser->DisposeId((DWORD)val); -} - - -void Module::AllocateRegularStaticHandles(AppDomain* pDomain) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - } - CONTRACTL_END; - - // Allocate the handles we will need. Note that AllocateStaticFieldObjRefPtrs will only - // allocate if pModuleData->GetGCStaticsBasePointerAddress(pMT) != 0, avoiding creating - // handles more than once for a given MT or module - - DomainLocalModule *pModuleData = GetDomainLocalModule(); - - _ASSERTE(pModuleData->GetPrecomputedGCStaticsBasePointerAddress() != NULL); - if (this->m_dwMaxGCRegularStaticHandles > 0) - { - pDomain->AllocateStaticFieldObjRefPtrs(this->m_dwMaxGCRegularStaticHandles, - pModuleData->GetPrecomputedGCStaticsBasePointerAddress()); - - // We should throw if we fail to allocate and never hit this assert - _ASSERTE(pModuleData->GetPrecomputedGCStaticsBasePointer() != NULL); - } -} - -BOOL Module::IsStaticStoragePrepared(mdTypeDef tkType) -{ - LIMITED_METHOD_CONTRACT; - - // Right now the design is that we do one static allocation pass during NGEN, - // and a 2nd pass for it at module init time for modules that weren't NGENed or the NGEN - // pass was unsuccessful. If we are loading types after that then we must use dynamic - // static storage. These dynamic statics require an additional indirection so they - // don't perform quite as well. - // - // This check was created for the scenario where a profiler adds additional types - // however it seems likely this check would also accurately handle other dynamic - // scenarios such as ref.emit and EnC as long as they are adding new types and - // not new statics to existing types. - _ASSERTE(TypeFromToken(tkType) == mdtTypeDef); - return m_maxTypeRidStaticsAllocated >= RidFromToken(tkType); -} - -void Module::AllocateStatics(AllocMemTracker *pamTracker) -{ - STANDARD_VM_CONTRACT; - - LOG((LF_CLASSLOADER, LL_INFO10000, "STATICS: Allocating statics for module %s\n", GetSimpleName())); - - // Build the offset table, which will tell us what the offsets for the statics of each class are (one offset for gc handles, one offset - // for non gc types) - BuildStaticsOffsets(pamTracker); -} - void Module::SetDomainAssembly(DomainAssembly *pDomainAssembly) { - CONTRACTL - { - INSTANCE_CHECK; - PRECONDITION(CheckPointer(pDomainAssembly)); - PRECONDITION(IsManifest()); - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - DomainLocalModule* pModuleData = 0; - - // Do we need to allocate memory for the non GC statics? - if (m_ModuleID == NULL) - { - // Allocate memory for the module statics. - LoaderAllocator *pLoaderAllocator = NULL; - if (GetAssembly()->IsCollectible()) - { - pLoaderAllocator = GetAssembly()->GetLoaderAllocator(); - } - else - { - pLoaderAllocator = pDomainAssembly->GetAppDomain()->GetLoaderAllocator(); - } - - SIZE_T size = GetDomainLocalModuleSize(); - - LOG((LF_CLASSLOADER, LL_INFO10, "STATICS: Allocating %zi bytes for precomputed statics in module %s in LoaderAllocator %p\n", - size, this->GetDebugName(), pLoaderAllocator)); - - // We guarantee alignment for 64-bit regular statics on 32-bit platforms even without FEATURE_64BIT_ALIGNMENT for performance reasons. - - _ASSERTE(size >= DomainLocalModule::OffsetOfDataBlob()); - - pModuleData = (DomainLocalModule*)(void*) - pLoaderAllocator->GetHighFrequencyHeap()->AllocAlignedMem( - size, MAX_PRIMITIVE_FIELD_SIZE); - - // Note: Memory allocated on loader heap is zero filled - // memset(pModuleData, 0, size); - - // Verify that the space is really zero initialized - _ASSERTE(pModuleData->GetPrecomputedGCStaticsBasePointer() == NULL); - - // If the module was loaded as domain-specific, then we need to assign - // this module a domain-neutral module ID. - pModuleData->m_ModuleIndex = Module::AllocateModuleIndex(); - m_ModuleIndex = pModuleData->m_ModuleIndex; - } - else - { - pModuleData = this->m_ModuleID; - LOG((LF_CLASSLOADER, LL_INFO10, "STATICS: Allocation not needed for ngened non shared module %s in Appdomain %08x\n")); - } - - // Non shared case, module points directly to the statics. In ngen case - // m_pDomainModule is already set for the non shared case - if (m_ModuleID == NULL) - { - m_ModuleID = pModuleData; - } - - m_ModuleID->SetDomainAssembly(pDomainAssembly); + LIMITED_METHOD_CONTRACT; - // Allocate static handles now. - // NOTE: Bootstrapping issue with CoreLib - we will manually allocate later - // If the assembly is collectible, we don't initialize static handles for them - // as it is currently initialized through the DomainLocalModule::PopulateClass in MethodTable::CheckRunClassInitThrowing - // (If we don't do this, it would allocate here unused regular static handles that will be overridden later) - if (g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT] != NULL && !GetAssembly()->IsCollectible()) - AllocateRegularStaticHandles(pDomainAssembly->GetAppDomain()); + m_pDomainAssembly = pDomainAssembly; } OBJECTREF Module::GetExposedObject() @@ -5007,10 +4414,9 @@ void Module::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, EMEM_OUT(("MEM: %p Module\n", dac_cast(this))); } - //Save module id data only if it a real pointer, not a tagged sugestion to use ModuleIndex. - if (m_ModuleID.IsValid()) + if (m_pDomainAssembly.IsValid()) { - m_ModuleID->EnumMemoryRegions(flags); + m_pDomainAssembly->EnumMemoryRegions(flags); } if (m_pPEAssembly.IsValid()) { diff --git a/src/coreclr/vm/ceeload.h b/src/coreclr/vm/ceeload.h index 540d386fa6e45..d8cc1720992f9 100644 --- a/src/coreclr/vm/ceeload.h +++ b/src/coreclr/vm/ceeload.h @@ -56,7 +56,6 @@ class Assembly; class BaseDomain; class AppDomain; class DomainModule; -struct DomainLocalModule; class SystemDomain; class Module; class SString; @@ -423,8 +422,6 @@ typedef DPTR(DynamicILBlobTable) PTR_DynamicILBlobTable; typedef DPTR(class ReadyToRunInfo) PTR_ReadyToRunInfo; #endif -struct ThreadLocalModule; - // A ModuleBase represents the ability to reference code via tokens // This abstraction exists to allow the R2R manifest metadata to have // tokens which can be resolved at runtime. @@ -1523,73 +1520,11 @@ class Module : public ModuleBase InstrumentedILOffsetMapping GetInstrumentedILOffsetMapping(mdMethodDef token); public: - // This helper returns to offsets for the slots/bytes/handles. They return the offset in bytes from the beginning - // of the 1st GC pointer in the statics block for the module. - void GetOffsetsForRegularStaticData( - mdTypeDef cl, - BOOL bDynamic, - DWORD dwGCStaticHandles, - DWORD dwNonGCStaticBytes, - DWORD * pOutStaticHandleOffset, - DWORD * pOutNonGCStaticOffset); - - void GetOffsetsForThreadStaticData( - mdTypeDef cl, - BOOL bDynamic, - DWORD dwGCStaticHandles, - DWORD dwNonGCStaticBytes, - DWORD * pOutStaticHandleOffset, - DWORD * pOutNonGCStaticOffset); - - - BOOL IsStaticStoragePrepared(mdTypeDef tkType); - - DWORD GetNumGCThreadStaticHandles() - { - return m_dwMaxGCThreadStaticHandles;; - } - CrstBase* GetFixupCrst() { return &m_FixupCrst; } - void AllocateRegularStaticHandles(AppDomain* pDomainMT); - - void FreeModuleIndex(); - - DWORD GetDomainLocalModuleSize() - { - return m_dwRegularStaticsBlockSize; - } - - DWORD GetThreadLocalModuleSize() - { - return m_dwThreadStaticsBlockSize; - } - - DWORD AllocateDynamicEntry(MethodTable *pMT); - - // We need this for the jitted shared case, - inline MethodTable* GetDynamicClassMT(DWORD dynamicClassID); - - static ModuleIndex AllocateModuleIndex(); - static void FreeModuleIndex(ModuleIndex index); - - ModuleIndex GetModuleIndex() - { - LIMITED_METHOD_DAC_CONTRACT; - return m_ModuleIndex; - } - - SIZE_T GetModuleID() - { - LIMITED_METHOD_DAC_CONTRACT; - return dac_cast(m_ModuleID); - } - - PTR_DomainLocalModule GetDomainLocalModule(); - // LoaderHeap for storing IJW thunks PTR_LoaderHeap m_pThunkHeap; @@ -1603,46 +1538,7 @@ class Module : public ModuleBase protected: - void BuildStaticsOffsets (AllocMemTracker *pamTracker); - void AllocateStatics (AllocMemTracker *pamTracker); - - // ModuleID is quite ugly. We should try to switch to using ModuleIndex instead - // where appropriate, and we should clean up code that uses ModuleID - PTR_DomainLocalModule m_ModuleID; // MultiDomain case: tagged (low bit 1) ModuleIndex - // SingleDomain case: pointer to domain local module - - ModuleIndex m_ModuleIndex; - - // reusing the statics area of a method table to store - // these for the non domain neutral case, but they're now unified - // it so that we don't have different code paths for this. - PTR_DWORD m_pRegularStaticOffsets; // Offset of statics in each class - PTR_DWORD m_pThreadStaticOffsets; // Offset of ThreadStatics in each class - - // All types with RID <= this value have static storage allocated within the module by AllocateStatics - // If AllocateStatics hasn't run yet, the value is 0 - RID m_maxTypeRidStaticsAllocated; - - // @NICE: see if we can remove these fields - DWORD m_dwMaxGCRegularStaticHandles; // Max number of handles we can have. - DWORD m_dwMaxGCThreadStaticHandles; - - // Size of the precomputed statics block. This includes class init bytes, gc handles and non gc statics - DWORD m_dwRegularStaticsBlockSize; - DWORD m_dwThreadStaticsBlockSize; - - // For 'dynamic' statics (Reflection and generics) - SIZE_T m_cDynamicEntries; // Number of used entries in DynamicStaticsInfo table - SIZE_T m_maxDynamicEntries; // Size of table itself, including unused entries - - // Info we need for dynamic statics that we can store per-module (ie, no need for it to be duplicated - // per appdomain) - struct DynamicStaticsInfo - { - MethodTable* pEnclosingMT; // Enclosing type; necessarily in this loader module - }; - DynamicStaticsInfo* m_pDynamicStaticsInfo; // Table with entry for each dynamic ID - + PTR_DomainAssembly m_pDomainAssembly; public: //----------------------------------------------------------------------------------------- diff --git a/src/coreclr/vm/ceeload.inl b/src/coreclr/vm/ceeload.inl index 8cd5189791b8c..eece02084e339 100644 --- a/src/coreclr/vm/ceeload.inl +++ b/src/coreclr/vm/ceeload.inl @@ -321,14 +321,6 @@ inline mdAssemblyRef Module::FindAssemblyRef(Assembly *targetAssembly) #endif //DACCESS_COMPILE -FORCEINLINE PTR_DomainLocalModule Module::GetDomainLocalModule() -{ - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - - return m_ModuleID; -} - #include "nibblestream.h" FORCEINLINE BOOL Module::FixupDelayList(TADDR pFixupList, BOOL mayUsePrecompiledNDirectMethods) @@ -459,13 +451,6 @@ BOOL Module::FixupDelayListAux(TADDR pFixupList, return TRUE; } -inline MethodTable* Module::GetDynamicClassMT(DWORD dynamicClassID) -{ - LIMITED_METHOD_CONTRACT; - _ASSERTE(m_cDynamicEntries > dynamicClassID); - return m_pDynamicStaticsInfo[dynamicClassID].pEnclosingMT; -} - #ifdef FEATURE_CODE_VERSIONING inline CodeVersionManager * Module::GetCodeVersionManager() { diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 0e73a9bc94b1a..c65e355c99555 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -626,6 +626,7 @@ void EEStartupHelper() IfFailGo(ExecutableAllocator::StaticInitialize(FatalErrorHandler)); Thread::StaticInitialize(); + InitializeThreadStaticData(); JITInlineTrackingMap::StaticInitialize(); MethodDescBackpatchInfoTracker::StaticInitialize(); @@ -1768,28 +1769,12 @@ void EnsureTlsDestructionMonitor() tls_destructionMonitor.Activate(); } -#ifdef _MSC_VER -__declspec(thread) ThreadStaticBlockInfo t_ThreadStatics; -#else -__thread ThreadStaticBlockInfo t_ThreadStatics; -#endif // _MSC_VER - // Delete the thread local memory only if we the current thread // is the one executing this code. If we do not guard it, it will // end up deleting the thread local memory of the calling thread. void DeleteThreadLocalMemory() { - t_NonGCThreadStaticBlocksSize = 0; - t_GCThreadStaticBlocksSize = 0; - - t_ThreadStatics.NonGCMaxThreadStaticBlocks = 0; - t_ThreadStatics.GCMaxThreadStaticBlocks = 0; - - delete[] t_ThreadStatics.NonGCThreadStaticBlocks; - t_ThreadStatics.NonGCThreadStaticBlocks = nullptr; - - delete[] t_ThreadStatics.GCThreadStaticBlocks; - t_ThreadStatics.GCThreadStaticBlocks = nullptr; + FreeCurrentThreadStaticData(); } #ifdef DEBUGGING_SUPPORTED diff --git a/src/coreclr/vm/class.h b/src/coreclr/vm/class.h index 8d5785dd48a64..05d20da40f933 100644 --- a/src/coreclr/vm/class.h +++ b/src/coreclr/vm/class.h @@ -591,9 +591,6 @@ class EEClassOptionalFields // MISC FIELDS // - #define MODULE_NON_DYNAMIC_STATICS ((DWORD)-1) - DWORD m_cbModuleDynamicID; - #if defined(UNIX_AMD64_ABI) // Number of eightBytes in the following arrays int m_numberEightBytes; @@ -949,22 +946,6 @@ class EEClass // DO NOT CREATE A NEW EEClass USING NEW! SetPackableField(EEClass_Field_NumThreadStaticFields, wNumThreadStaticFields); } - // Statics are stored in a big chunk inside the module - - inline DWORD GetModuleDynamicID() - { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - return HasOptionalFields() ? GetOptionalFields()->m_cbModuleDynamicID : MODULE_NON_DYNAMIC_STATICS; - } - - inline void SetModuleDynamicID(DWORD cbModuleDynamicID) - { - LIMITED_METHOD_CONTRACT; - _ASSERTE(HasOptionalFields()); - GetOptionalFields()->m_cbModuleDynamicID = cbModuleDynamicID; - } - /* * Difference between the InterfaceMap ptr and Vtable in the * MethodTable used to indicate the number of static bytes diff --git a/src/coreclr/vm/class.inl b/src/coreclr/vm/class.inl index 92426a304f790..ed65ee3cb5363 100644 --- a/src/coreclr/vm/class.inl +++ b/src/coreclr/vm/class.inl @@ -28,7 +28,6 @@ inline void EEClassOptionalFields::Init() m_pClassFactory = NULL; #endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION #endif // FEATURE_COMINTEROP - m_cbModuleDynamicID = MODULE_NON_DYNAMIC_STATICS; #if defined(UNIX_AMD64_ABI) m_numberEightBytes = 0; #endif // UNIX_AMD64_ABI diff --git a/src/coreclr/vm/common.h b/src/coreclr/vm/common.h index f0edc0f15cd00..78c020bb407e9 100644 --- a/src/coreclr/vm/common.h +++ b/src/coreclr/vm/common.h @@ -116,7 +116,6 @@ typedef DPTR(struct FailedAssembly) PTR_FailedAssembly; typedef VPTR(class EditAndContinueModule) PTR_EditAndContinueModule; typedef DPTR(class EEClass) PTR_EEClass; typedef DPTR(class DelegateEEClass) PTR_DelegateEEClass; -typedef DPTR(struct DomainLocalModule) PTR_DomainLocalModule; typedef VPTR(class EECodeManager) PTR_EECodeManager; typedef DPTR(class RangeSectionMap) PTR_RangeSectionMap; typedef DPTR(class EEConfig) PTR_EEConfig; diff --git a/src/coreclr/vm/eventtrace_bulktype.cpp b/src/coreclr/vm/eventtrace_bulktype.cpp index 8e5a24e00c89f..4fddac0287ec3 100644 --- a/src/coreclr/vm/eventtrace_bulktype.cpp +++ b/src/coreclr/vm/eventtrace_bulktype.cpp @@ -509,6 +509,7 @@ void BulkStaticsLogger::LogAllStatics() CONTRACTL_END; { + // TODO: This code does not appear to find all generic instantiations of types, and thus does not log ALL statics AppDomain *domain = ::GetAppDomain(); // There is only 1 AppDomain, so no iterator here. AppDomain::AssemblyIterator assemblyIter = domain->IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoaded|kIncludeExecution)); @@ -533,10 +534,6 @@ void BulkStaticsLogger::LogAllStatics() if (!domainAssembly->IsActive()) continue; - DomainLocalModule *domainModule = module->GetDomainLocalModule(); - if (domainModule == NULL) - continue; - // Now iterate all types with LookupMap::Iterator mtIter = module->EnumerateTypeDefs(); while (mtIter.Next()) @@ -566,7 +563,7 @@ void BulkStaticsLogger::LogAllStatics() if (fieldType != ELEMENT_TYPE_CLASS && fieldType != ELEMENT_TYPE_VALUETYPE) continue; - BYTE *base = field->GetBaseInDomainLocalModule(domainModule); + BYTE *base = field->GetBase(); if (base == NULL) continue; diff --git a/src/coreclr/vm/field.h b/src/coreclr/vm/field.h index e2324787febaf..69066b9e17cb6 100644 --- a/src/coreclr/vm/field.h +++ b/src/coreclr/vm/field.h @@ -384,20 +384,6 @@ class FieldDesc return GetApproxEnclosingMethodTable()->GetNumGenericArgs(); } - PTR_BYTE GetBaseInDomainLocalModule(DomainLocalModule * pLocalModule) - { - WRAPPER_NO_CONTRACT; - - if (GetFieldType() == ELEMENT_TYPE_CLASS || GetFieldType() == ELEMENT_TYPE_VALUETYPE) - { - return pLocalModule->GetGCStaticsBasePointer(GetEnclosingMethodTable()); - } - else - { - return pLocalModule->GetNonGCStaticsBasePointer(GetEnclosingMethodTable()); - } - } - PTR_BYTE GetBase() { CONTRACTL @@ -409,7 +395,14 @@ class FieldDesc MethodTable *pMT = GetEnclosingMethodTable(); - return GetBaseInDomainLocalModule(pMT->GetDomainLocalModule()); + if (GetFieldType() == ELEMENT_TYPE_CLASS || GetFieldType() == ELEMENT_TYPE_VALUETYPE) + { + return pMT->GetGCStaticsBasePointer(); + } + else + { + return pMT->GetNonGCStaticsBasePointer(); + } } // returns the address of the field @@ -513,7 +506,10 @@ class FieldDesc else { PTR_BYTE base = 0; if (!IsRVA()) // for RVA the base is ignored + { + GetEnclosingMethodTable()->EnsureStaticDataAllocated(); base = GetBase(); + } return GetStaticAddress((void *)dac_cast(base)); } } diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index a4a538780aa4a..3b1a7fa3d28b7 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -302,6 +302,7 @@ void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen, #endif // FEATURE_EVENT_TRACE ScanStackRoots(pThread, fn, sc); ScanTailCallArgBufferRoots(pThread, fn, sc); + ScanThreadStaticRoots(pThread->GetThreadLocalDataPtr(), fn, sc); #ifdef FEATURE_EVENT_TRACE sc->dwEtwRootKind = kEtwGCRootKindOther; #endif // FEATURE_EVENT_TRACE @@ -620,6 +621,7 @@ void GcScanRootsForProfilerAndETW(promote_func* fn, int condemned, int max_gen, #endif // FEATURE_EVENT_TRACE ScanStackRoots(pThread, fn, sc); ScanTailCallArgBufferRoots(pThread, fn, sc); + ScanThreadStaticRoots(pThread->GetThreadLocalDataPtr(), fn, sc); #ifdef FEATURE_EVENT_TRACE sc->dwEtwRootKind = kEtwGCRootKindOther; #endif // FEATURE_EVENT_TRACE diff --git a/src/coreclr/vm/generics.cpp b/src/coreclr/vm/generics.cpp index 4b13e18dc8794..51b6fc0866ebb 100644 --- a/src/coreclr/vm/generics.cpp +++ b/src/coreclr/vm/generics.cpp @@ -278,7 +278,13 @@ ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation( memcpy((BYTE*)pMT - cbGC, (BYTE*) pOldMT - cbGC, cbGC); // Allocate the private data block - pMT->AllocateAuxiliaryData(pAllocator, pLoaderModule, pamTracker, fHasGenericsStaticsInfo); + MethodTableStaticsFlags staticsFlags = MethodTableStaticsFlags::None; + if (fHasGenericsStaticsInfo) + staticsFlags |= MethodTableStaticsFlags::Generic | MethodTableStaticsFlags::Present; + if (pOldMT->GetNumThreadStaticFields() > 0) + staticsFlags |= MethodTableStaticsFlags::Thread; + + pMT->AllocateAuxiliaryData(pAllocator, pLoaderModule, pamTracker, staticsFlags); pMT->SetModule(pOldMT->GetModule()); // This also disables IBC logging until the type is sufficiently initialized so @@ -336,15 +342,16 @@ ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation( if (fContainsGenericVariables) pMT->SetContainsGenericVariables(); - if (fHasGenericsStaticsInfo) - pMT->SetDynamicStatics(TRUE); - // Since we are fabricating a new MT based on an existing one, the per-inst info should // be non-null _ASSERTE(pOldMT->HasPerInstInfo()); // Fill in per-inst map pointer (which points to the array of generic dictionary pointers) pMT->SetPerInstInfo((MethodTable::PerInstInfoElem_t *) (pMemory + cbMT + cbOptional + cbIMap + sizeof(GenericsDictInfo))); + + if (fHasGenericsStaticsInfo) + pMT->SetDynamicStatics(); + _ASSERTE(FitsIn(pOldMT->GetNumDicts())); _ASSERTE(FitsIn(pOldMT->GetNumGenericArgs())); pMT->SetDictInfo(static_cast(pOldMT->GetNumDicts()), static_cast(pOldMT->GetNumGenericArgs())); diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index d41861f6c3567..b463c2f43d739 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1296,9 +1296,8 @@ HCIMPL1(void, JIT_InitClass, CORINFO_CLASS_HANDLE typeHnd_) TypeHandle typeHnd(typeHnd_); MethodTable *pMT = typeHnd.AsMethodTable(); - _ASSERTE(!pMT->IsClassPreInited()); - if (pMT->GetDomainLocalModule()->IsClassInitialized(pMT)) + if (pMT->IsClassInited()) return; // Tailcall to the slow helper @@ -1349,362 +1348,139 @@ HCIMPLEND #include -HCIMPL2(void*, JIT_GetSharedNonGCStaticBase_Portable, DomainLocalModule *pLocalModule, DWORD dwClassDomainID) +HCIMPL1(void*, JIT_GetNonGCStaticBase_Portable, MethodTable* pMT) { FCALL_CONTRACT; - // If type doesn't have a class constructor, the contents of this if statement may - // still get executed. JIT_GetSharedNonGCStaticBaseNoCtor should be used in this case. - if (pLocalModule->IsPrecomputedClassInitialized(dwClassDomainID)) + if (pMT->IsClassInited()) { - return (void*)pLocalModule->GetPrecomputedNonGCStaticsBasePointer(); + return pMT->GetDynamicStaticsInfo()->m_pNonGCStatics; } // Tailcall to the slow helper ENDFORBIDGC(); - return HCCALL2(JIT_GetSharedNonGCStaticBase_Helper, pLocalModule, dwClassDomainID); + return HCCALL1(JIT_GetNonGCStaticBase_Helper, pMT); } HCIMPLEND -// No constructor version of JIT_GetSharedNonGCStaticBase. Does not check if class has -// been initialized. -HCIMPL1(void*, JIT_GetSharedNonGCStaticBaseNoCtor_Portable, DomainLocalModule *pLocalModule) -{ - FCALL_CONTRACT; - return (void*)pLocalModule->GetPrecomputedNonGCStaticsBasePointer(); -} -HCIMPLEND - -HCIMPL2(void*, JIT_GetSharedGCStaticBase_Portable, DomainLocalModule *pLocalModule, DWORD dwClassDomainID) +HCIMPL1(void*, JIT_GetDynamicNonGCStaticBase_Portable, DynamicStaticsInfo* pStaticsInfo) { FCALL_CONTRACT; - // If type doesn't have a class constructor, the contents of this if statement may - // still get executed. JIT_GetSharedGCStaticBaseNoCtor should be used in this case. - if (pLocalModule->IsPrecomputedClassInitialized(dwClassDomainID)) + if (pStaticsInfo->IsClassInited()) { - return (void*)pLocalModule->GetPrecomputedGCStaticsBasePointer(); + return pStaticsInfo->m_pNonGCStatics; } // Tailcall to the slow helper ENDFORBIDGC(); - return HCCALL2(JIT_GetSharedGCStaticBase_Helper, pLocalModule, dwClassDomainID); -} -HCIMPLEND - -// No constructor version of JIT_GetSharedGCStaticBase. Does not check if class has been -// initialized. -HCIMPL1(void*, JIT_GetSharedGCStaticBaseNoCtor_Portable, DomainLocalModule *pLocalModule) -{ - FCALL_CONTRACT; - - return (void*)pLocalModule->GetPrecomputedGCStaticsBasePointer(); + return HCCALL1(JIT_GetNonGCStaticBase_Helper, pStaticsInfo->GetMethodTable()); } HCIMPLEND - -#include - - -// The following two functions can be tail called from platform dependent versions of -// JIT_GetSharedGCStaticBase and JIT_GetShareNonGCStaticBase -HCIMPL2(void*, JIT_GetSharedNonGCStaticBase_Helper, DomainLocalModule *pLocalModule, DWORD dwClassDomainID) -{ - FCALL_CONTRACT; - - HELPER_METHOD_FRAME_BEGIN_RET_0(); - - // Obtain Method table - MethodTable * pMT = pLocalModule->GetMethodTableFromClassDomainID(dwClassDomainID); - - PREFIX_ASSUME(pMT != NULL); - pMT->CheckRunClassInitThrowing(); - HELPER_METHOD_FRAME_END(); - - return (void*)pLocalModule->GetPrecomputedNonGCStaticsBasePointer(); -} -HCIMPLEND - -HCIMPL2(void*, JIT_GetSharedGCStaticBase_Helper, DomainLocalModule *pLocalModule, DWORD dwClassDomainID) +// No constructor version of JIT_GetSharedNonGCStaticBase. Does not check if class has +// been initialized. +HCIMPL1(void*, JIT_GetNonGCStaticBaseNoCtor_Portable, MethodTable* pMT) { FCALL_CONTRACT; - HELPER_METHOD_FRAME_BEGIN_RET_0(); - - // Obtain Method table - MethodTable * pMT = pLocalModule->GetMethodTableFromClassDomainID(dwClassDomainID); - - PREFIX_ASSUME(pMT != NULL); - pMT->CheckRunClassInitThrowing(); - HELPER_METHOD_FRAME_END(); - - return (void*)pLocalModule->GetPrecomputedGCStaticsBasePointer(); + return pMT->GetDynamicStaticsInfo()->m_pNonGCStatics; } HCIMPLEND -/*********************************************************************/ -// Slow helper to tail call from the fast one -HCIMPL2(void*, JIT_GetSharedNonGCStaticBaseDynamicClass_Helper, DomainLocalModule *pLocalModule, DWORD dwDynamicClassDomainID) +// No constructor version of JIT_GetSharedNonGCStaticBase. Does not check if class has +// been initialized. +HCIMPL1(void*, JIT_GetDynamicNonGCStaticBaseNoCtor_Portable, DynamicStaticsInfo* pDynamicStaticsInfo) { FCALL_CONTRACT; - void* result = NULL; - - HELPER_METHOD_FRAME_BEGIN_RET_0(); - - MethodTable *pMT = pLocalModule->GetDomainAssembly()->GetModule()->GetDynamicClassMT(dwDynamicClassDomainID); - _ASSERTE(pMT); - - pMT->CheckRunClassInitThrowing(); - - result = (void*)pLocalModule->GetDynamicEntryNonGCStaticsBasePointer(dwDynamicClassDomainID, pMT->GetLoaderAllocator()); - HELPER_METHOD_FRAME_END(); - - return result; + return pDynamicStaticsInfo->m_pNonGCStatics; } HCIMPLEND -/*************************************************************/ -#include -HCIMPL2(void*, JIT_GetSharedNonGCStaticBaseDynamicClass, DomainLocalModule *pLocalModule, DWORD dwDynamicClassDomainID) +HCIMPL1(void*, JIT_GetGCStaticBase_Portable, MethodTable* pMT) { FCALL_CONTRACT; - DomainLocalModule::PTR_DynamicClassInfo pLocalInfo = pLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID); - if (pLocalInfo != NULL) + if (pMT->IsClassInited()) { - PTR_BYTE retval; - GET_DYNAMICENTRY_NONGCSTATICS_BASEPOINTER(pLocalModule->GetDomainAssembly()->GetModule()->GetLoaderAllocator(), - pLocalInfo, - &retval); - - return retval; + return pMT->GetDynamicStaticsInfo()->m_pGCStatics; } // Tailcall to the slow helper ENDFORBIDGC(); - return HCCALL2(JIT_GetSharedNonGCStaticBaseDynamicClass_Helper, pLocalModule, dwDynamicClassDomainID); -} -HCIMPLEND -#include - -/*************************************************************/ -// Slow helper to tail call from the fast one -HCIMPL2(void, JIT_ClassInitDynamicClass_Helper, DomainLocalModule *pLocalModule, DWORD dwDynamicClassDomainID) -{ - FCALL_CONTRACT; - - HELPER_METHOD_FRAME_BEGIN_0(); - - MethodTable *pMT = pLocalModule->GetDomainAssembly()->GetModule()->GetDynamicClassMT(dwDynamicClassDomainID); - _ASSERTE(pMT); - - pMT->CheckRunClassInitThrowing(); - - HELPER_METHOD_FRAME_END(); - - return; + return HCCALL1(JIT_GetGCStaticBase_Helper, pMT); } HCIMPLEND -#include -HCIMPL2(void, JIT_ClassInitDynamicClass, DomainLocalModule *pLocalModule, DWORD dwDynamicClassDomainID) +HCIMPL1(void*, JIT_GetDynamicGCStaticBase_Portable, DynamicStaticsInfo* pStaticsInfo) { FCALL_CONTRACT; - DomainLocalModule::PTR_DynamicClassInfo pLocalInfo = pLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID); - if (pLocalInfo != NULL) + if (pStaticsInfo->IsClassInited()) { - return; + return pStaticsInfo->m_pGCStatics; } // Tailcall to the slow helper ENDFORBIDGC(); - return HCCALL2(JIT_ClassInitDynamicClass_Helper, pLocalModule, dwDynamicClassDomainID); + return HCCALL1(JIT_GetGCStaticBase_Helper, pStaticsInfo->GetMethodTable()); } HCIMPLEND -#include -/*************************************************************/ -// Slow helper to tail call from the fast one -HCIMPL2(void*, JIT_GetSharedGCStaticBaseDynamicClass_Helper, DomainLocalModule *pLocalModule, DWORD dwDynamicClassDomainID) +// No constructor version of JIT_GetSharedGCStaticBase. Does not check if class has been +// initialized. +HCIMPL1(void*, JIT_GetGCStaticBaseNoCtor_Portable, MethodTable* pMT) { FCALL_CONTRACT; - void* result = NULL; - - HELPER_METHOD_FRAME_BEGIN_RET_0(); - - MethodTable *pMT = pLocalModule->GetDomainAssembly()->GetModule()->GetDynamicClassMT(dwDynamicClassDomainID); - _ASSERTE(pMT); - - pMT->CheckRunClassInitThrowing(); - - result = (void*)pLocalModule->GetDynamicEntryGCStaticsBasePointer(dwDynamicClassDomainID, pMT->GetLoaderAllocator()); - HELPER_METHOD_FRAME_END(); - - return result; + return pMT->GetDynamicStaticsInfo()->m_pGCStatics; } HCIMPLEND -/*************************************************************/ -#include -HCIMPL2(void*, JIT_GetSharedGCStaticBaseDynamicClass, DomainLocalModule *pLocalModule, DWORD dwDynamicClassDomainID) +// No constructor version of JIT_GetSharedGCStaticBase. Does not check if class has been +// initialized. +HCIMPL1(void*, JIT_GetDynamicGCStaticBaseNoCtor_Portable, DynamicStaticsInfo* pDynamicStaticsInfo) { FCALL_CONTRACT; - DomainLocalModule::PTR_DynamicClassInfo pLocalInfo = pLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID); - if (pLocalInfo != NULL) - { - PTR_BYTE retval; - GET_DYNAMICENTRY_GCSTATICS_BASEPOINTER(pLocalModule->GetDomainAssembly()->GetModule()->GetLoaderAllocator(), - pLocalInfo, - &retval); - - return retval; - } - - // Tailcall to the slow helper - ENDFORBIDGC(); - return HCCALL2(JIT_GetSharedGCStaticBaseDynamicClass_Helper, pLocalModule, dwDynamicClassDomainID); + return pDynamicStaticsInfo->m_pGCStatics; } HCIMPLEND + #include -/*********************************************************************/ -// Slow helper to tail call from the fast one -NOINLINE HCIMPL1(void*, JIT_GetGenericsGCStaticBase_Framed, MethodTable *pMT) -{ - CONTRACTL { - FCALL_CHECK; - PRECONDITION(CheckPointer(pMT)); - PRECONDITION(pMT->HasGenericsStaticsInfo()); - } CONTRACTL_END; - void* base = NULL; +// The following two functions can be tail called from platform dependent versions of +// JIT_GetSharedGCStaticBase and JIT_GetShareNonGCStaticBase +HCIMPL1(void*, JIT_GetNonGCStaticBase_Helper, MethodTable* pMT) +{ + FCALL_CONTRACT; HELPER_METHOD_FRAME_BEGIN_RET_0(); - pMT->CheckRestore(); - + PREFIX_ASSUME(pMT != NULL); pMT->CheckRunClassInitThrowing(); - - base = (void*) pMT->GetGCStaticsBasePointer(); - CONSISTENCY_CHECK(base != NULL); - HELPER_METHOD_FRAME_END(); - return base; + return (void*)pMT->GetDynamicStaticsInfo()->m_pNonGCStatics; } HCIMPLEND -/*********************************************************************/ -#include -HCIMPL1(void*, JIT_GetGenericsGCStaticBase, MethodTable *pMT) +HCIMPL1(void*, JIT_GetGCStaticBase_Helper, MethodTable* pMT) { - CONTRACTL { - FCALL_CHECK; - PRECONDITION(CheckPointer(pMT)); - PRECONDITION(pMT->HasGenericsStaticsInfo()); - } CONTRACTL_END; - - DWORD dwDynamicClassDomainID; - PTR_Module pModuleForStatics = pMT->GetGenericsStaticsModuleAndID(&dwDynamicClassDomainID); - - DomainLocalModule *pLocalModule = pModuleForStatics->GetDomainLocalModule(); - _ASSERTE(pLocalModule); - - DomainLocalModule::PTR_DynamicClassInfo pLocalInfo = pLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID); - if (pLocalInfo != NULL) - { - PTR_BYTE retval; - GET_DYNAMICENTRY_GCSTATICS_BASEPOINTER(pMT->GetLoaderAllocator(), - pLocalInfo, - &retval); - - return retval; - } - - // Tailcall to the slow helper - ENDFORBIDGC(); - return HCCALL1(JIT_GetGenericsGCStaticBase_Framed, pMT); -} -HCIMPLEND -#include - -/*********************************************************************/ -// Slow helper to tail call from the fast one -NOINLINE HCIMPL1(void*, JIT_GetGenericsNonGCStaticBase_Framed, MethodTable *pMT) -{ - CONTRACTL { - FCALL_CHECK; - PRECONDITION(CheckPointer(pMT)); - PRECONDITION(pMT->HasGenericsStaticsInfo()); - } CONTRACTL_END; - - void* base = NULL; + FCALL_CONTRACT; HELPER_METHOD_FRAME_BEGIN_RET_0(); - pMT->CheckRestore(); - - // If pMT refers to a method table that requires some initialization work, - // then pMT cannot to a method table that is shared by generic instantiations, - // because method tables that are shared by generic instantiations do not have - // a base for statics to live in. - _ASSERTE(pMT->IsClassPreInited() || !pMT->IsSharedByGenericInstantiations()); - + PREFIX_ASSUME(pMT != NULL); pMT->CheckRunClassInitThrowing(); - - // We could just return null here instead of returning base when this helper is called just to trigger the cctor - base = (void*) pMT->GetNonGCStaticsBasePointer(); - HELPER_METHOD_FRAME_END(); - return base; + return (void*)pMT->GetDynamicStaticsInfo()->m_pGCStatics; } HCIMPLEND -/*********************************************************************/ -#include -HCIMPL1(void*, JIT_GetGenericsNonGCStaticBase, MethodTable *pMT) -{ - CONTRACTL { - FCALL_CHECK; - PRECONDITION(CheckPointer(pMT)); - PRECONDITION(pMT->HasGenericsStaticsInfo()); - } CONTRACTL_END; - - // This fast path will typically always be taken once the slow framed path below - // has executed once. Sometimes the slow path will be executed more than once, - // e.g. if static fields are accessed during the call to CheckRunClassInitThrowing() - // in the slow path. - - DWORD dwDynamicClassDomainID; - PTR_Module pModuleForStatics = pMT->GetGenericsStaticsModuleAndID(&dwDynamicClassDomainID); - - DomainLocalModule *pLocalModule = pModuleForStatics->GetDomainLocalModule(); - _ASSERTE(pLocalModule); - - DomainLocalModule::PTR_DynamicClassInfo pLocalInfo = pLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID); - if (pLocalInfo != NULL) - { - PTR_BYTE retval; - GET_DYNAMICENTRY_NONGCSTATICS_BASEPOINTER(pMT->GetLoaderAllocator(), - pLocalInfo, - &retval); - - return retval; - } - - // Tailcall to the slow helper - ENDFORBIDGC(); - return HCCALL1(JIT_GetGenericsNonGCStaticBase_Framed, pMT); -} -HCIMPLEND -#include - - //======================================================================== // // THREAD STATIC FIELD HELPERS @@ -1726,16 +1502,8 @@ HCIMPL1(void*, JIT_GetNonGCThreadStaticBase_Helper, MethodTable * pMT) HELPER_METHOD_FRAME_BEGIN_RET_0(); - // For generics, we need to call CheckRestore() for some reason - if (pMT->HasGenericsStaticsInfo()) - pMT->CheckRestore(); - - // Get the TLM - ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLM(pMT); - _ASSERTE(pThreadLocalModule != NULL); - // Check if the class constructor needs to be run - pThreadLocalModule->CheckRunClassInitThrowing(pMT); + pMT->CheckRunClassInitThrowing(); // Lookup the non-GC statics base pointer base = (void*) pMT->GetNonGCThreadStaticsBasePointer(); @@ -1758,16 +1526,8 @@ HCIMPL1(void*, JIT_GetGCThreadStaticBase_Helper, MethodTable * pMT) HELPER_METHOD_FRAME_BEGIN_RET_0(); - // For generics, we need to call CheckRestore() for some reason - if (pMT->HasGenericsStaticsInfo()) - pMT->CheckRestore(); - - // Get the TLM - ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLM(pMT); - _ASSERTE(pThreadLocalModule != NULL); - // Check if the class constructor needs to be run - pThreadLocalModule->CheckRunClassInitThrowing(pMT); + pMT->CheckRunClassInitThrowing(); // Lookup the GC statics base pointer base = (void*) pMT->GetGCThreadStaticsBasePointer(); @@ -1779,14 +1539,6 @@ HCIMPL1(void*, JIT_GetGCThreadStaticBase_Helper, MethodTable * pMT) } HCIMPLEND -#ifdef _MSC_VER -__declspec(thread) uint32_t t_NonGCThreadStaticBlocksSize; -__declspec(thread) uint32_t t_GCThreadStaticBlocksSize; -#else -__thread uint32_t t_NonGCThreadStaticBlocksSize; -__thread uint32_t t_GCThreadStaticBlocksSize; -#endif // !_MSC_VER - // *** This helper corresponds to both CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE and // CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR. Even though we always check // if the class constructor has been run, we have a separate helper ID for the "no ctor" @@ -1794,30 +1546,33 @@ __thread uint32_t t_GCThreadStaticBlocksSize; // possible. #include -HCIMPL2(void*, JIT_GetSharedNonGCThreadStaticBase, DomainLocalModule *pDomainLocalModule, DWORD dwClassDomainID) +HCIMPL1(void*, JIT_GetNonGCThreadStaticBase, MethodTable *pMT) { FCALL_CONTRACT; - // Get the ModuleIndex - ModuleIndex index = pDomainLocalModule->GetModuleIndex(); - - // Get the relevant ThreadLocalModule - ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index); + void* pThreadStaticBase = GetThreadLocalStaticBaseIfExistsAndInitialized(pMT->GetThreadStaticsInfo()->NonGCTlsIndex); + if (pThreadStaticBase != NULL) + { + return pThreadStaticBase; + } - // If the TLM has been allocated and the class has been marked as initialized, - // get the pointer to the non-GC statics base and return - if (pThreadLocalModule != NULL && pThreadLocalModule->IsPrecomputedClassInitialized(dwClassDomainID)) - return (void*)pThreadLocalModule->GetPrecomputedNonGCStaticsBasePointer(); + ENDFORBIDGC(); + return HCCALL1(JIT_GetNonGCThreadStaticBase_Helper, pMT); +} +HCIMPLEND - // If the TLM was not allocated or if the class was not marked as initialized - // then we have to go through the slow path +HCIMPL1(void*, JIT_GetDynamicNonGCThreadStaticBase, ThreadStaticsInfo *pThreadStaticsInfo) +{ + FCALL_CONTRACT; - // Obtain the MethodTable - MethodTable * pMT = pDomainLocalModule->GetMethodTableFromClassDomainID(dwClassDomainID); - _ASSERTE(!pMT->HasGenericsStaticsInfo()); + void* pThreadStaticBase = GetThreadLocalStaticBaseIfExistsAndInitialized(pThreadStaticsInfo->NonGCTlsIndex); + if (pThreadStaticBase != NULL) + { + return pThreadStaticBase; + } ENDFORBIDGC(); - return HCCALL1(JIT_GetNonGCThreadStaticBase_Helper, pMT); + return HCCALL1(JIT_GetNonGCThreadStaticBase_Helper, pThreadStaticsInfo->m_genericStatics.m_DynamicStatics.GetMethodTable()); } HCIMPLEND @@ -1825,53 +1580,20 @@ HCIMPLEND // Even though we always check if the class constructor has been run, we have a separate // helper ID for the "no ctor" version because it allows the JIT to do some reordering that // otherwise wouldn't be possible. -HCIMPL1(void*, JIT_GetSharedNonGCThreadStaticBaseOptimized, UINT32 staticBlockIndex) +HCIMPL1(void*, JIT_GetNonGCThreadStaticBaseOptimized, UINT32 staticBlockIndex) { void* staticBlock = nullptr; FCALL_CONTRACT; HELPER_METHOD_FRAME_BEGIN_RET_0(); // Set up a frame - - MethodTable * pMT = AppDomain::GetCurrentDomain()->LookupNonGCThreadStaticBlockType(staticBlockIndex); - _ASSERTE(!pMT->HasGenericsStaticsInfo()); - - // Get the TLM - ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLM(pMT); - _ASSERTE(pThreadLocalModule != NULL); - + TLSIndex tlsIndex(staticBlockIndex); // Check if the class constructor needs to be run - pThreadLocalModule->CheckRunClassInitThrowing(pMT); + MethodTable *pMT = LookupMethodTableForThreadStatic(tlsIndex); + pMT->CheckRunClassInitThrowing(); // Lookup the non-GC statics base pointer staticBlock = (void*) pMT->GetNonGCThreadStaticsBasePointer(); - CONSISTENCY_CHECK(staticBlock != NULL); - - if (t_NonGCThreadStaticBlocksSize <= staticBlockIndex) - { - UINT32 newThreadStaticBlocksSize = max(2 * t_NonGCThreadStaticBlocksSize, staticBlockIndex + 1); - void** newThreadStaticBlocks = (void**) new PTR_BYTE[newThreadStaticBlocksSize * sizeof(PTR_BYTE)]; - memset(newThreadStaticBlocks + t_NonGCThreadStaticBlocksSize, 0, (newThreadStaticBlocksSize - t_NonGCThreadStaticBlocksSize) * sizeof(PTR_BYTE)); - - if (t_NonGCThreadStaticBlocksSize > 0) - { - memcpy(newThreadStaticBlocks, t_ThreadStatics.NonGCThreadStaticBlocks, t_NonGCThreadStaticBlocksSize * sizeof(PTR_BYTE)); - delete[] t_ThreadStatics.NonGCThreadStaticBlocks; - } - - t_NonGCThreadStaticBlocksSize = newThreadStaticBlocksSize; - t_ThreadStatics.NonGCThreadStaticBlocks = newThreadStaticBlocks; - } - - void* currentEntry = t_ThreadStatics.NonGCThreadStaticBlocks[staticBlockIndex]; - // We could be coming here 2nd time after running the ctor when we try to get the static block. - // In such case, just avoid adding the same entry. - if (currentEntry != staticBlock) - { - _ASSERTE(currentEntry == nullptr); - t_ThreadStatics.NonGCThreadStaticBlocks[staticBlockIndex] = staticBlock; - t_ThreadStatics.NonGCMaxThreadStaticBlocks = max(t_ThreadStatics.NonGCMaxThreadStaticBlocks, staticBlockIndex); - } HELPER_METHOD_FRAME_END(); return staticBlock; @@ -1887,39 +1609,43 @@ HCIMPLEND // possible. #include -HCIMPL2(void*, JIT_GetSharedGCThreadStaticBase, DomainLocalModule *pDomainLocalModule, DWORD dwClassDomainID) +HCIMPL1(void*, JIT_GetGCThreadStaticBase, MethodTable *pMT) { FCALL_CONTRACT; - // Get the ModuleIndex - ModuleIndex index = pDomainLocalModule->GetModuleIndex(); - - // Get the relevant ThreadLocalModule - ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index); + void* pThreadStaticBase = GetThreadLocalStaticBaseIfExistsAndInitialized(pMT->GetThreadStaticsInfo()->GCTlsIndex); + if (pThreadStaticBase != NULL) + { + return pThreadStaticBase; + } - // If the TLM has been allocated and the class has been marked as initialized, - // get the pointer to the GC statics base and return - if (pThreadLocalModule != NULL && pThreadLocalModule->IsPrecomputedClassInitialized(dwClassDomainID)) - return (void*)pThreadLocalModule->GetPrecomputedGCStaticsBasePointer(); + ENDFORBIDGC(); + return HCCALL1(JIT_GetGCThreadStaticBase_Helper, pMT); +} +HCIMPLEND - // If the TLM was not allocated or if the class was not marked as initialized - // then we have to go through the slow path +HCIMPL1(void*, JIT_GetDynamicGCThreadStaticBase, ThreadStaticsInfo *pThreadStaticsInfo) +{ + FCALL_CONTRACT; - // Obtain the MethodTable - MethodTable * pMT = pDomainLocalModule->GetMethodTableFromClassDomainID(dwClassDomainID); - _ASSERTE(!pMT->HasGenericsStaticsInfo()); + void* pThreadStaticBase = GetThreadLocalStaticBaseIfExistsAndInitialized(pThreadStaticsInfo->GCTlsIndex); + if (pThreadStaticBase != NULL) + { + return pThreadStaticBase; + } ENDFORBIDGC(); - return HCCALL1(JIT_GetGCThreadStaticBase_Helper, pMT); + return HCCALL1(JIT_GetGCThreadStaticBase_Helper, pThreadStaticsInfo->m_genericStatics.m_DynamicStatics.GetMethodTable()); } HCIMPLEND + #include // *** This helper corresponds CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED. // Even though we always check if the class constructor has been run, we have a separate // helper ID for the "no ctor" version because it allows the JIT to do some reordering that // otherwise wouldn't be possible. -HCIMPL1(void*, JIT_GetSharedGCThreadStaticBaseOptimized, UINT32 staticBlockIndex) +HCIMPL1(void*, JIT_GetGCThreadStaticBaseOptimized, UINT32 staticBlockIndex) { void* staticBlock = nullptr; @@ -1927,254 +1653,19 @@ HCIMPL1(void*, JIT_GetSharedGCThreadStaticBaseOptimized, UINT32 staticBlockIndex HELPER_METHOD_FRAME_BEGIN_RET_0(); // Set up a frame - MethodTable * pMT = AppDomain::GetCurrentDomain()->LookupGCThreadStaticBlockType(staticBlockIndex); - _ASSERTE(!pMT->HasGenericsStaticsInfo()); - - // Get the TLM - ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLM(pMT); - _ASSERTE(pThreadLocalModule != NULL); - + TLSIndex tlsIndex(staticBlockIndex); // Check if the class constructor needs to be run - pThreadLocalModule->CheckRunClassInitThrowing(pMT); - - // Lookup the GC statics base handle and cache it - staticBlock = (void*) pMT->GetGCThreadStaticsBaseHandle(); - CONSISTENCY_CHECK(staticBlock != NULL); - - if (t_GCThreadStaticBlocksSize <= staticBlockIndex) - { - UINT32 newThreadStaticBlocksSize = max(2 * t_GCThreadStaticBlocksSize, staticBlockIndex + 1); - void** newThreadStaticBlocks = (void**) new PTR_BYTE[newThreadStaticBlocksSize * sizeof(PTR_BYTE)]; - memset(newThreadStaticBlocks + t_GCThreadStaticBlocksSize, 0, (newThreadStaticBlocksSize - t_GCThreadStaticBlocksSize) * sizeof(PTR_BYTE)); - - if (t_GCThreadStaticBlocksSize > 0) - { - memcpy(newThreadStaticBlocks, t_ThreadStatics.GCThreadStaticBlocks, t_GCThreadStaticBlocksSize * sizeof(PTR_BYTE)); - delete[] t_ThreadStatics.GCThreadStaticBlocks; - } - - t_GCThreadStaticBlocksSize = newThreadStaticBlocksSize; - t_ThreadStatics.GCThreadStaticBlocks = newThreadStaticBlocks; - } - - void* currentEntry = t_ThreadStatics.GCThreadStaticBlocks[staticBlockIndex]; - // We could be coming here 2nd time after running the ctor when we try to get the static block. - // In such case, just avoid adding the same entry. - if (currentEntry != staticBlock) - { - _ASSERTE(currentEntry == nullptr); - t_ThreadStatics.GCThreadStaticBlocks[staticBlockIndex] = staticBlock; - t_ThreadStatics.GCMaxThreadStaticBlocks = max(t_ThreadStatics.GCMaxThreadStaticBlocks, staticBlockIndex); - } + MethodTable *pMT = LookupMethodTableForThreadStatic(tlsIndex); + pMT->CheckRunClassInitThrowing(); - // Get the data pointer of static block + // Lookup the non-GC statics base pointer staticBlock = (void*) pMT->GetGCThreadStaticsBasePointer(); - HELPER_METHOD_FRAME_END(); return staticBlock; } HCIMPLEND -// *** This helper corresponds to CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS - -#include -HCIMPL2(void*, JIT_GetSharedNonGCThreadStaticBaseDynamicClass, DomainLocalModule *pDomainLocalModule, DWORD dwDynamicClassDomainID) -{ - FCALL_CONTRACT; - - // Get the ModuleIndex - ModuleIndex index = pDomainLocalModule->GetModuleIndex(); - - // Get the relevant ThreadLocalModule - ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index); - - // If the TLM has been allocated and the class has been marked as initialized, - // get the pointer to the non-GC statics base and return - if (pThreadLocalModule != NULL) - { - ThreadLocalModule::PTR_DynamicClassInfo pLocalInfo = pThreadLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID); - if (pLocalInfo != NULL) - { - PTR_BYTE retval; - GET_DYNAMICENTRY_NONGCTHREADSTATICS_BASEPOINTER(pDomainLocalModule->GetDomainAssembly()->GetModule()->GetLoaderAllocator(), - pLocalInfo, - &retval); - return retval; - } - } - - // If the TLM was not allocated or if the class was not marked as initialized - // then we have to go through the slow path - - // Obtain the Module - Module * pModule = pDomainLocalModule->GetDomainAssembly()->GetModule(); - - // Obtain the MethodTable - MethodTable * pMT = pModule->GetDynamicClassMT(dwDynamicClassDomainID); - _ASSERTE(pMT != NULL); - _ASSERTE(!pMT->IsSharedByGenericInstantiations()); - - // Tailcall to the slow helper - ENDFORBIDGC(); - - return HCCALL1(JIT_GetNonGCThreadStaticBase_Helper, pMT); - -} -HCIMPLEND -#include - -// *** This helper corresponds to CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS - -#include -HCIMPL2(void*, JIT_GetSharedGCThreadStaticBaseDynamicClass, DomainLocalModule *pDomainLocalModule, DWORD dwDynamicClassDomainID) -{ - FCALL_CONTRACT; - - // Get the ModuleIndex - ModuleIndex index = pDomainLocalModule->GetModuleIndex(); - - // Get the relevant ThreadLocalModule - ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index); - - // If the TLM has been allocated and the class has been marked as initialized, - // get the pointer to the GC statics base and return - if (pThreadLocalModule != NULL) - { - ThreadLocalModule::PTR_DynamicClassInfo pLocalInfo = pThreadLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID); - if (pLocalInfo != NULL) - { - PTR_BYTE retval; - GET_DYNAMICENTRY_GCTHREADSTATICS_BASEPOINTER(pDomainLocalModule->GetDomainAssembly()->GetModule()->GetLoaderAllocator(), - pLocalInfo, - &retval); - - return retval; - } - } - - // If the TLM was not allocated or if the class was not marked as initialized - // then we have to go through the slow path - - // Obtain the Module - Module * pModule = pDomainLocalModule->GetDomainAssembly()->GetModule(); - - // Obtain the MethodTable - MethodTable * pMT = pModule->GetDynamicClassMT(dwDynamicClassDomainID); - _ASSERTE(pMT != NULL); - _ASSERTE(!pMT->IsSharedByGenericInstantiations()); - - // Tailcall to the slow helper - ENDFORBIDGC(); - return HCCALL1(JIT_GetGCThreadStaticBase_Helper, pMT); -} -HCIMPLEND -#include - -// *** This helper corresponds to CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE - -#include -HCIMPL1(void*, JIT_GetGenericsNonGCThreadStaticBase, MethodTable *pMT) -{ - CONTRACTL { - FCALL_CHECK; - PRECONDITION(CheckPointer(pMT)); - PRECONDITION(pMT->HasGenericsStaticsInfo()); - } CONTRACTL_END; - - // This fast path will typically always be taken once the slow framed path below - // has executed once. Sometimes the slow path will be executed more than once, - // e.g. if static fields are accessed during the call to CheckRunClassInitThrowing() - // in the slow path. - - // Get the Module and dynamic class ID - DWORD dwDynamicClassDomainID; - PTR_Module pModule = pMT->GetGenericsStaticsModuleAndID(&dwDynamicClassDomainID); - - // Get ModuleIndex - ModuleIndex index = pModule->GetModuleIndex(); - - // Get the relevant ThreadLocalModule - ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index); - - // If the TLM has been allocated and the class has been marked as initialized, - // get the pointer to the non-GC statics base and return - if (pThreadLocalModule != NULL) - { - ThreadLocalModule::PTR_DynamicClassInfo pLocalInfo = pThreadLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID); - if (pLocalInfo != NULL) - { - PTR_BYTE retval; - GET_DYNAMICENTRY_NONGCSTATICS_BASEPOINTER(pMT->GetLoaderAllocator(), - pLocalInfo, - &retval); - - return retval; - } - } - - // If the TLM was not allocated or if the class was not marked as initialized - // then we have to go through the slow path - - // Tailcall to the slow helper - ENDFORBIDGC(); - return HCCALL1(JIT_GetNonGCThreadStaticBase_Helper, pMT); -} -HCIMPLEND -#include - -// *** This helper corresponds to CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE - -#include -HCIMPL1(void*, JIT_GetGenericsGCThreadStaticBase, MethodTable *pMT) -{ - CONTRACTL { - FCALL_CHECK; - PRECONDITION(CheckPointer(pMT)); - PRECONDITION(pMT->HasGenericsStaticsInfo()); - } CONTRACTL_END; - - // This fast path will typically always be taken once the slow framed path below - // has executed once. Sometimes the slow path will be executed more than once, - // e.g. if static fields are accessed during the call to CheckRunClassInitThrowing() - // in the slow path. - - // Get the Module and dynamic class ID - DWORD dwDynamicClassDomainID; - PTR_Module pModule = pMT->GetGenericsStaticsModuleAndID(&dwDynamicClassDomainID); - - // Get ModuleIndex - ModuleIndex index = pModule->GetModuleIndex(); - - // Get the relevant ThreadLocalModule - ThreadLocalModule * pThreadLocalModule = ThreadStatics::GetTLMIfExists(index); - - // If the TLM has been allocated and the class has been marked as initialized, - // get the pointer to the GC statics base and return - if (pThreadLocalModule != NULL) - { - ThreadLocalModule::PTR_DynamicClassInfo pLocalInfo = pThreadLocalModule->GetDynamicClassInfoIfInitialized(dwDynamicClassDomainID); - if (pLocalInfo != NULL) - { - PTR_BYTE retval; - GET_DYNAMICENTRY_GCTHREADSTATICS_BASEPOINTER(pMT->GetLoaderAllocator(), - pLocalInfo, - &retval); - - return retval; - } - } - - // If the TLM was not allocated or if the class was not marked as initialized - // then we have to go through the slow path - - // Tailcall to the slow helper - ENDFORBIDGC(); - return HCCALL1(JIT_GetGCThreadStaticBase_Helper, pMT); -} -HCIMPLEND -#include - //======================================================================== // // STATIC FIELD DYNAMIC HELPERS @@ -2186,7 +1677,7 @@ HCIMPL1_RAW(TADDR, JIT_StaticFieldAddress_Dynamic, StaticFieldAddressArgs * pArg { FCALL_CONTRACT; - TADDR base = HCCALL2(pArgs->staticBaseHelper, pArgs->arg0, pArgs->arg1); + TADDR base = HCCALL1(pArgs->staticBaseHelper, pArgs->arg0); return base + pArgs->offset; } HCIMPLEND_RAW @@ -2197,7 +1688,7 @@ HCIMPL1_RAW(TADDR, JIT_StaticFieldAddressUnbox_Dynamic, StaticFieldAddressArgs * { FCALL_CONTRACT; - TADDR base = HCCALL2(pArgs->staticBaseHelper, pArgs->arg0, pArgs->arg1); + TADDR base = HCCALL1(pArgs->staticBaseHelper, pArgs->arg0); return *(TADDR *)(base + pArgs->offset) + Object::GetOffsetOfFirstField(); } HCIMPLEND_RAW diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 56d6b8af11903..9948e62354ef7 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -1154,22 +1154,20 @@ static CorInfoHelpFunc getGenericStaticsHelper(FieldDesc * pField) { STANDARD_VM_CONTRACT; - int helper = CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE; + int helper = CORINFO_HELP_GET_NONGCSTATIC_BASE; if (pField->GetFieldType() == ELEMENT_TYPE_CLASS || pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE) { - helper = CORINFO_HELP_GETGENERICS_GCSTATIC_BASE; + helper = CORINFO_HELP_GET_GCSTATIC_BASE; } if (pField->IsThreadStatic()) { - const int delta = CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE - CORINFO_HELP_GETGENERICS_GCSTATIC_BASE; - - static_assert_no_msg(CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE - == CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE + delta); - - helper += (CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE - CORINFO_HELP_GETGENERICS_GCSTATIC_BASE); + if (helper == CORINFO_HELP_GET_NONGCSTATIC_BASE) + helper = CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE; + else + helper = CORINFO_HELP_GET_GCTHREADSTATIC_BASE; } return (CorInfoHelpFunc)helper; @@ -1179,53 +1177,48 @@ CorInfoHelpFunc CEEInfo::getSharedStaticsHelper(FieldDesc * pField, MethodTable { STANDARD_VM_CONTRACT; - int helper = CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE; - - if (pField->GetFieldType() == ELEMENT_TYPE_CLASS || - pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE) - { - helper = CORINFO_HELP_GETSHARED_GCSTATIC_BASE; - } - - if (pFieldMT->IsDynamicStatics()) + bool GCStatic = (pField->GetFieldType() == ELEMENT_TYPE_CLASS || + pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE); + bool noCtor = ((!pFieldMT->HasClassConstructor() && !pFieldMT->HasBoxedRegularStatics()) || pFieldMT->IsClassInited()); + bool threadStatic = pField->IsThreadStatic(); + CorInfoHelpFunc helper; + + if (threadStatic) { - const int delta = CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS - CORINFO_HELP_GETSHARED_GCSTATIC_BASE; - - static_assert_no_msg(CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_DYNAMICCLASS - == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE + delta); - - helper += delta; + if (GCStatic) + { + if (noCtor) + helper = CORINFO_HELP_GET_GCTHREADSTATIC_BASE_NOCTOR; + else + helper = CORINFO_HELP_GET_GCTHREADSTATIC_BASE; + } + else + { + if (noCtor) + helper = CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE_NOCTOR; + else + helper = CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE; + } } else - if ((!pFieldMT->HasClassConstructor() && !pFieldMT->HasBoxedRegularStatics()) || pFieldMT->IsClassInited()) - { - const int delta = CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR - CORINFO_HELP_GETSHARED_GCSTATIC_BASE; - - static_assert_no_msg(CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR - == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE + delta); - - helper += delta; - } - - if (pField->IsThreadStatic()) { - const int delta = CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE - CORINFO_HELP_GETSHARED_GCSTATIC_BASE; - - static_assert_no_msg(CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE - == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE + delta); - static_assert_no_msg(CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR - == CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR + delta); - static_assert_no_msg(CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR - == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR + delta); - static_assert_no_msg(CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS - == CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS + delta); - static_assert_no_msg(CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS - == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_DYNAMICCLASS + delta); - - helper += delta; + if (GCStatic) + { + if (noCtor) + helper = CORINFO_HELP_GET_GCSTATIC_BASE_NOCTOR; + else + helper = CORINFO_HELP_GET_GCSTATIC_BASE; + } + else + { + if (noCtor) + helper = CORINFO_HELP_GET_NONGCSTATIC_BASE_NOCTOR; + else + helper = CORINFO_HELP_GET_NONGCSTATIC_BASE; + } } - return (CorInfoHelpFunc)helper; + return helper; } static CorInfoHelpFunc getInstanceFieldHelper(FieldDesc * pField, CORINFO_ACCESS_FLAGS flags) @@ -1307,14 +1300,16 @@ uint32_t CEEInfo::getThreadLocalFieldInfo (CORINFO_FIELD_HANDLE field, bool isG FieldDesc* fieldDesc = (FieldDesc*)field; _ASSERTE(fieldDesc->IsThreadStatic()); + MethodTable *pMT = fieldDesc->GetEnclosingMethodTable(); + pMT->EnsureTlsIndexAllocated(); if (isGCType) { - typeIndex = AppDomain::GetCurrentDomain()->GetGCThreadStaticTypeIndex(fieldDesc->GetEnclosingMethodTable()); + typeIndex = MethodTableAuxiliaryData::GetThreadStaticsInfo(pMT->GetAuxiliaryData())->GCTlsIndex.TLSIndexRawIndex; } else { - typeIndex = AppDomain::GetCurrentDomain()->GetNonGCThreadStaticTypeIndex(fieldDesc->GetEnclosingMethodTable()); + typeIndex = MethodTableAuxiliaryData::GetThreadStaticsInfo(pMT->GetAuxiliaryData())->NonGCTlsIndex.TLSIndexRawIndex; } assert(typeIndex != TypeIDProvider::INVALID_TYPE_ID); @@ -1323,91 +1318,7 @@ uint32_t CEEInfo::getThreadLocalFieldInfo (CORINFO_FIELD_HANDLE field, bool isG return typeIndex; } -#if defined(TARGET_WINDOWS) -/*********************************************************************/ -static uint32_t ThreadLocalOffset(void* p) -{ - PTEB Teb = NtCurrentTeb(); - uint8_t** pTls = (uint8_t**)Teb->ThreadLocalStoragePointer; - uint8_t* pOurTls = pTls[_tls_index]; - return (uint32_t)((uint8_t*)p - pOurTls); -} -#elif defined(TARGET_OSX) -extern "C" void* GetThreadVarsAddress(); - -static void* GetThreadVarsSectionAddressFromDesc(uint8_t* p) -{ - _ASSERT(p[0] == 0x48 && p[1] == 0x8d && p[2] == 0x3d); - - // At this point, `p` contains the instruction pointer and is pointing to the above opcodes. - // These opcodes are patched by the dynamic linker. - // Move beyond the opcodes that we have already checked above. - p += 3; - - // The descriptor address is located at *p at this point. - // (p + 4) below skips the descriptor address bytes embedded in the instruction and - // add it to the `instruction pointer` to find out the address. - return *(uint32_t*)p + (p + 4); -} - -static void* GetThreadVarsSectionAddress() -{ -#ifdef TARGET_AMD64 - // On x64, the address is related to rip, so, disassemble the function, - // read the offset, and then relative to the IP, find the final address of - // __thread_vars section. - uint8_t* p = reinterpret_cast(&GetThreadVarsAddress); - return GetThreadVarsSectionAddressFromDesc(p); -#else - return GetThreadVarsAddress(); -#endif // TARGET_AMD64 -} - -#else - -// Linux - -#ifdef TARGET_AMD64 - -extern "C" void* GetTlsIndexObjectDescOffset(); - -static void* GetThreadStaticDescriptor(uint8_t* p) -{ - if (!(p[0] == 0x66 && p[1] == 0x48 && p[2] == 0x8d && p[3] == 0x3d)) - { - // The optimization is disabled if coreclr is not compiled in .so format. - _ASSERTE(false && "Unexpected code sequence"); - return nullptr; - } - - // At this point, `p` contains the instruction pointer and is pointing to the above opcodes. - // These opcodes are patched by the dynamic linker. - // Move beyond the opcodes that we have already checked above. - p += 4; - - // The descriptor address is located at *p at this point. Read that and add - // it to the instruction pointer to locate the address of `ti` that will be used - // to pass to __tls_get_addr during execution. - // (p + 4) below skips the descriptor address bytes embedded in the instruction and - // add it to the `instruction pointer` to find out the address. - return *(uint32_t*)p + (p + 4); -} - -static void* GetTlsIndexObjectAddress() -{ - uint8_t* p = reinterpret_cast(&GetTlsIndexObjectDescOffset); - return GetThreadStaticDescriptor(p); -} - -#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - -extern "C" size_t GetThreadStaticsVariableOffset(); - -#endif // TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64 -#endif // TARGET_WINDOWS - - -void CEEInfo::getThreadLocalStaticBlocksInfo (CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, bool isGCType) +void CEEInfo::getThreadLocalStaticBlocksInfo (CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) { CONTRACTL { NOTHROW; @@ -1416,50 +1327,7 @@ void CEEInfo::getThreadLocalStaticBlocksInfo (CORINFO_THREAD_STATIC_BLOCKS_INFO* } CONTRACTL_END; JIT_TO_EE_TRANSITION_LEAF(); - - size_t threadStaticBaseOffset = 0; - -#if defined(TARGET_WINDOWS) - pInfo->tlsIndex.addr = (void*)static_cast(_tls_index); - pInfo->tlsIndex.accessType = IAT_VALUE; - - pInfo->offsetOfThreadLocalStoragePointer = offsetof(_TEB, ThreadLocalStoragePointer); - threadStaticBaseOffset = ThreadLocalOffset(&t_ThreadStatics); - -#elif defined(TARGET_OSX) - - pInfo->threadVarsSection = GetThreadVarsSectionAddress(); - -#elif defined(TARGET_AMD64) - - // For Linux/x64, get the address of tls_get_addr system method and the base address - // of struct that we will pass to it. - pInfo->tlsGetAddrFtnPtr = reinterpret_cast(&__tls_get_addr); - pInfo->tlsIndexObject = GetTlsIndexObjectAddress(); - -#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - - // For Linux arm64/loongarch64/riscv64, just get the offset of thread static variable, and during execution, - // this offset, arm64 taken from trpid_elp0 system register gives back the thread variable address. - // this offset, loongarch64 taken from $tp register gives back the thread variable address. - threadStaticBaseOffset = GetThreadStaticsVariableOffset(); - -#else - _ASSERTE_MSG(false, "Unsupported scenario of optimizing TLS access on Linux Arm32/x86"); -#endif // TARGET_WINDOWS - - if (isGCType) - { - pInfo->offsetOfMaxThreadStaticBlocks = (uint32_t)(threadStaticBaseOffset + offsetof(ThreadStaticBlockInfo, GCMaxThreadStaticBlocks)); - pInfo->offsetOfThreadStaticBlocks = (uint32_t)(threadStaticBaseOffset + offsetof(ThreadStaticBlockInfo, GCThreadStaticBlocks)); - } - else - { - pInfo->offsetOfMaxThreadStaticBlocks = (uint32_t)(threadStaticBaseOffset + offsetof(ThreadStaticBlockInfo, NonGCMaxThreadStaticBlocks)); - pInfo->offsetOfThreadStaticBlocks = (uint32_t)(threadStaticBaseOffset + offsetof(ThreadStaticBlockInfo, NonGCThreadStaticBlocks)); - } - pInfo->offsetOfGCDataPointer = static_cast(PtrArray::GetDataOffset()); - + GetThreadLocalStaticBlocksInfo(pInfo); EE_TO_JIT_TRANSITION_LEAF(); } @@ -1587,17 +1455,21 @@ void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken, // For windows x64/x86/arm64, linux x64/arm64/loongarch64/riscv64: // We convert the TLS access to the optimized helper where we will store // the static blocks in TLS directly and access them via inline code. - if ((pResult->helper == CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR) || - (pResult->helper == CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE)) + if ((pResult->helper == CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE_NOCTOR) || + (pResult->helper == CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE) || + (pResult->helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR) || + (pResult->helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE)) { fieldAccessor = CORINFO_FIELD_STATIC_TLS_MANAGED; - pResult->helper = CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED; + pResult->helper = CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED; } - else if ((pResult->helper == CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR) || - (pResult->helper == CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE)) + else if ((pResult->helper == CORINFO_HELP_GET_GCTHREADSTATIC_BASE_NOCTOR) || + (pResult->helper == CORINFO_HELP_GET_GCTHREADSTATIC_BASE) || + (pResult->helper == CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR) || + (pResult->helper == CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE)) { fieldAccessor = CORINFO_FIELD_STATIC_TLS_MANAGED; - pResult->helper = CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED; + pResult->helper = CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED; } } #endif // TARGET_ARM @@ -1608,8 +1480,7 @@ void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken, // Allocate space for the local class if necessary, but don't trigger // class construction. - DomainLocalModule* pLocalModule = pFieldMT->GetDomainLocalModule(); - pLocalModule->PopulateClass(pFieldMT); + pFieldMT->EnsureStaticDataAllocated(); // We are not going through a helper. The constructor has to be triggered explicitly. if (!pFieldMT->IsClassInited()) @@ -3692,37 +3563,6 @@ void CEEInfo::LongLifetimeFree(void* obj) EE_TO_JIT_TRANSITION_LEAF(); } -/*********************************************************************/ -size_t CEEInfo::getClassModuleIdForStatics(CORINFO_CLASS_HANDLE clsHnd, CORINFO_MODULE_HANDLE *pModuleHandle, void **ppIndirection) -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - } CONTRACTL_END; - - size_t result = 0; - - JIT_TO_EE_TRANSITION_LEAF(); - - TypeHandle VMClsHnd(clsHnd); - Module *pModule = VMClsHnd.AsMethodTable()->GetModuleForStatics(); - - if (ppIndirection != NULL) - *ppIndirection = NULL; - - // The zapper needs the module handle. The jit should not use it at all. - if (pModuleHandle) - *pModuleHandle = CORINFO_MODULE_HANDLE(pModule); - - result = pModule->GetModuleID(); - - _ASSERTE(result); - - EE_TO_JIT_TRANSITION_LEAF(); - - return result; -} /*********************************************************************/ bool CEEInfo::getIsClassInitedFlagAddress(CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, int* offset) @@ -3739,20 +3579,7 @@ bool CEEInfo::getIsClassInitedFlagAddress(CORINFO_CLASS_HANDLE cls, CORINFO_CONS TypeHandle clsTypeHandle(cls); PTR_MethodTable pMT = clsTypeHandle.AsMethodTable(); - - // Impl is based on IsPrecomputedClassInitialized() - UINT32 clsIndex = 0; - if (pMT->IsDynamicStatics()) - { - clsIndex = (UINT32)pMT->GetModuleDynamicEntryID(); - } - else - { - clsIndex = (UINT32)pMT->GetClassIndex(); - } - - size_t moduleId = pMT->GetModuleForStatics()->GetModuleID(); - addr->addr = (UINT8*)moduleId + DomainLocalModule::GetOffsetOfDataBlob() + clsIndex; + addr->addr = (UINT8*)pMT->getIsClassInitedFlagAddress(); addr->accessType = IAT_VALUE; *offset = 0; @@ -3963,6 +3790,7 @@ CorInfoInitClassResult CEEInfo::initClass( MethodTable *pTypeToInitMT = typeToInitTH.AsMethodTable(); + pTypeToInitMT->AttemptToPreinit(); if (pTypeToInitMT->IsClassInited()) { // If the type is initialized there really is nothing to do. @@ -4076,8 +3904,7 @@ CorInfoInitClassResult CEEInfo::initClass( // Allocate space for the local class if necessary, but don't trigger // class construction. - DomainLocalModule *pModule = pTypeToInitMT->GetDomainLocalModule(); - pModule->PopulateClass(pTypeToInitMT); + pTypeToInitMT->EnsureStaticDataAllocated(); if (pTypeToInitMT->IsClassInited()) { @@ -5665,39 +5492,6 @@ void CEEInfo::getCallInfo( } -/***********************************************************************/ -unsigned CEEInfo::getClassDomainID (CORINFO_CLASS_HANDLE clsHnd, - void **ppIndirection) -{ - CONTRACTL { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } CONTRACTL_END; - - unsigned result = 0; - - if (ppIndirection != NULL) - *ppIndirection = NULL; - - JIT_TO_EE_TRANSITION(); - - TypeHandle VMClsHnd(clsHnd); - - if (VMClsHnd.AsMethodTable()->IsDynamicStatics()) - { - result = (unsigned)VMClsHnd.AsMethodTable()->GetModuleDynamicEntryID(); - } - else - { - result = (unsigned)VMClsHnd.AsMethodTable()->GetClassIndex(); - } - - EE_TO_JIT_TRANSITION(); - - return result; -} - //--------------------------------------------------------------------------------------- // // Used by the JIT to determine whether the profiler or IBC is tracking object @@ -6051,22 +5845,13 @@ CorInfoHelpFunc CEEInfo::getSharedCCtorHelper(CORINFO_CLASS_HANDLE clsHnd) MODE_PREEMPTIVE; } CONTRACTL_END; - CorInfoHelpFunc result = CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE; - - JIT_TO_EE_TRANSITION_LEAF(); - - TypeHandle cls(clsHnd); - MethodTable* pMT = cls.AsMethodTable(); - - if (pMT->IsDynamicStatics()) - { - _ASSERTE(!cls.ContainsGenericVariables()); - _ASSERTE(pMT->GetModuleDynamicEntryID() != (unsigned) -1); - - result = CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS; - } + TypeHandle VMClsHnd(clsHnd); - EE_TO_JIT_TRANSITION_LEAF(); + CorInfoHelpFunc result; + if (VMClsHnd.GetMethodTable()->IsDynamicStatics()) + result = CORINFO_HELP_GET_NONGCSTATIC_BASE; + else + result = CORINFO_HELP_INITCLASS; return result; } @@ -11643,8 +11428,7 @@ bool CEEInfo::getStaticFieldContent(CORINFO_FIELD_HANDLE fieldHnd, uint8_t* buff // Allocate space for the local class if necessary, but don't trigger // class construction. - DomainLocalModule* pLocalModule = pEnclosingMT->GetDomainLocalModule(); - pLocalModule->PopulateClass(pEnclosingMT); + pEnclosingMT->EnsureStaticDataAllocated(); if (!field->IsThreadStatic() && pEnclosingMT->IsClassInited() && IsFdInitOnly(field->GetAttributes())) { @@ -11826,8 +11610,7 @@ CORINFO_CLASS_HANDLE CEEJitInfo::getStaticFieldCurrentClass(CORINFO_FIELD_HANDLE { // Allocate space for the local class if necessary, but don't trigger // class construction. - DomainLocalModule *pLocalModule = pEnclosingMT->GetDomainLocalModule(); - pLocalModule->PopulateClass(pEnclosingMT); + pEnclosingMT->EnsureStaticDataAllocated(); GCX_COOP(); @@ -13587,33 +13370,6 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, result = pMgr->GetCallStub(ownerType, slot); } break; - - case ENCODE_CLASS_ID_FOR_STATICS: - { - TypeHandle th = ZapSig::DecodeType(currentModule, pInfoModule, pBlob); - - MethodTable * pMT = th.AsMethodTable(); - if (pMT->IsDynamicStatics()) - { - result = pMT->GetModuleDynamicEntryID(); - } - else - { - result = pMT->GetClassIndex(); - } - } - break; - - case ENCODE_MODULE_ID_FOR_GENERIC_STATICS: - { - TypeHandle th = ZapSig::DecodeType(currentModule, pInfoModule, pBlob); - - MethodTable * pMT = th.AsMethodTable(); - - result = pMT->GetModuleForStatics()->GetModuleID(); - } - break; - #ifdef FEATURE_READYTORUN case ENCODE_READYTORUN_HELPER: { diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index d1ae48e2df5ca..389aa84db2b03 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -101,26 +101,6 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, #endif // TARGET_X86 -// thread local struct to store the "thread static blocks" -struct ThreadStaticBlockInfo -{ - uint32_t NonGCMaxThreadStaticBlocks; - void** NonGCThreadStaticBlocks; - - uint32_t GCMaxThreadStaticBlocks; - void** GCThreadStaticBlocks; -}; - -#ifdef _MSC_VER -EXTERN_C __declspec(thread) ThreadStaticBlockInfo t_ThreadStatics; -EXTERN_C __declspec(thread) uint32_t t_NonGCThreadStaticBlocksSize; -EXTERN_C __declspec(thread) uint32_t t_GCThreadStaticBlocksSize; -#else -EXTERN_C __thread ThreadStaticBlockInfo t_ThreadStatics; -EXTERN_C __thread uint32_t t_NonGCThreadStaticBlocksSize; -EXTERN_C __thread uint32_t t_GCThreadStaticBlocksSize; -#endif // _MSC_VER - // // JIT HELPER ALIASING FOR PORTABILITY. // @@ -175,30 +155,53 @@ EXTERN_C FCDECL_MONHELPER(JIT_MonEnterStatic_Portable, AwareLock *lock); EXTERN_C FCDECL_MONHELPER(JIT_MonExitStatic, AwareLock *lock); EXTERN_C FCDECL_MONHELPER(JIT_MonExitStatic_Portable, AwareLock *lock); +#ifndef JIT_GetGCStaticBase +#define JIT_GetGCStaticBase JIT_GetGCStaticBase_Portable +#endif +EXTERN_C FCDECL1(void*, JIT_GetGCStaticBase, MethodTable *pMT); +EXTERN_C FCDECL1(void*, JIT_GetGCStaticBase_Portable, MethodTable *pMT); + +#ifndef JIT_GetNonGCStaticBase +#define JIT_GetNonGCStaticBase JIT_GetNonGCStaticBase_Portable +#endif +EXTERN_C FCDECL1(void*, JIT_GetNonGCStaticBase, MethodTable *pMT); +EXTERN_C FCDECL1(void*, JIT_GetNonGCStaticBase_Portable, MethodTable *pMT); + +#ifndef JIT_GetGCStaticBaseNoCtor +#define JIT_GetGCStaticBaseNoCtor JIT_GetGCStaticBaseNoCtor_Portable +#endif +EXTERN_C FCDECL1(void*, JIT_GetGCStaticBaseNoCtor, MethodTable *pMT); +EXTERN_C FCDECL1(void*, JIT_GetGCStaticBaseNoCtor_Portable, MethodTable *pMT); + +#ifndef JIT_GetNonGCStaticBaseNoCtor +#define JIT_GetNonGCStaticBaseNoCtor JIT_GetNonGCStaticBaseNoCtor_Portable +#endif +EXTERN_C FCDECL1(void*, JIT_GetNonGCStaticBaseNoCtor, MethodTable *pMT); +EXTERN_C FCDECL1(void*, JIT_GetNonGCStaticBaseNoCtor_Portable, MethodTable *pMT); -#ifndef JIT_GetSharedGCStaticBase -#define JIT_GetSharedGCStaticBase JIT_GetSharedGCStaticBase_Portable +#ifndef JIT_GetDynamicGCStaticBase +#define JIT_GetDynamicGCStaticBase JIT_GetDynamicGCStaticBase_Portable #endif -EXTERN_C FCDECL2(void*, JIT_GetSharedGCStaticBase, DomainLocalModule *pLocalModule, DWORD dwModuleClassID); -EXTERN_C FCDECL2(void*, JIT_GetSharedGCStaticBase_Portable, DomainLocalModule *pLocalModule, DWORD dwModuleClassID); +EXTERN_C FCDECL1(void*, JIT_GetDynamicGCStaticBase, DynamicStaticsInfo* pStaticsInfo); +EXTERN_C FCDECL1(void*, JIT_GetDynamicGCStaticBase_Portable, DynamicStaticsInfo* pStaticsInfo); -#ifndef JIT_GetSharedNonGCStaticBase -#define JIT_GetSharedNonGCStaticBase JIT_GetSharedNonGCStaticBase_Portable +#ifndef JIT_GetDynamicNonGCStaticBase +#define JIT_GetDynamicNonGCStaticBase JIT_GeDynamictNonGCStaticBase_Portable #endif -EXTERN_C FCDECL2(void*, JIT_GetSharedNonGCStaticBase, DomainLocalModule *pLocalModule, DWORD dwModuleClassID); -EXTERN_C FCDECL2(void*, JIT_GetSharedNonGCStaticBase_Portable, DomainLocalModule *pLocalModule, DWORD dwModuleClassID); +EXTERN_C FCDECL1(void*, JIT_GetDynamicNonGCStaticBase, DynamicStaticsInfo* pStaticsInfo); +EXTERN_C FCDECL1(void*, JIT_GetDynamicNonGCStaticBase_Portable, DynamicStaticsInfo* pStaticsInfo); -#ifndef JIT_GetSharedGCStaticBaseNoCtor -#define JIT_GetSharedGCStaticBaseNoCtor JIT_GetSharedGCStaticBaseNoCtor_Portable +#ifndef JIT_GetDynamicGCStaticBaseNoCtor +#define JIT_GetDynamicGCStaticBaseNoCtor JIT_GetDynamicGCStaticBaseNoCtor_Portable #endif -EXTERN_C FCDECL1(void*, JIT_GetSharedGCStaticBaseNoCtor, DomainLocalModule *pLocalModule); -EXTERN_C FCDECL1(void*, JIT_GetSharedGCStaticBaseNoCtor_Portable, DomainLocalModule *pLocalModule); +EXTERN_C FCDECL1(void*, JIT_GetDynamicGCStaticBaseNoCtor, DynamicStaticsInfo* pStaticsInfo); +EXTERN_C FCDECL1(void*, JIT_GetDynamicGCStaticBaseNoCtor_Portable, DynamicStaticsInfo* pStaticsInfo); -#ifndef JIT_GetSharedNonGCStaticBaseNoCtor -#define JIT_GetSharedNonGCStaticBaseNoCtor JIT_GetSharedNonGCStaticBaseNoCtor_Portable +#ifndef JIT_GetDynamicNonGCStaticBaseNoCtor +#define JIT_GetDynamicNonGCStaticBaseNoCtor JIT_GetDynamicNonGCStaticBaseNoCtor_Portable #endif -EXTERN_C FCDECL1(void*, JIT_GetSharedNonGCStaticBaseNoCtor, DomainLocalModule *pLocalModule); -EXTERN_C FCDECL1(void*, JIT_GetSharedNonGCStaticBaseNoCtor_Portable, DomainLocalModule *pLocalModule); +EXTERN_C FCDECL1(void*, JIT_GetDynamicNonGCStaticBaseNoCtor, DynamicStaticsInfo* pStaticsInfo); +EXTERN_C FCDECL1(void*, JIT_GetDynamicNonGCStaticBaseNoCtor_Portable, DynamicStaticsInfo* pStaticsInfo); extern FCDECL1(Object*, JIT_NewS_MP_FastPortable, CORINFO_CLASS_HANDLE typeHnd_); extern FCDECL1(Object*, JIT_New, CORINFO_CLASS_HANDLE typeHnd_); @@ -219,8 +222,8 @@ EXTERN_C FCDECL_MONHELPER(JITutil_MonSignal, AwareLock* lock); EXTERN_C FCDECL_MONHELPER(JITutil_MonContention, AwareLock* awarelock); EXTERN_C FCDECL2(void, JITutil_MonReliableContention, AwareLock* awarelock, BYTE* pbLockTaken); -EXTERN_C FCDECL2(void*, JIT_GetSharedNonGCStaticBase_Helper, DomainLocalModule *pLocalModule, DWORD dwClassDomainID); -EXTERN_C FCDECL2(void*, JIT_GetSharedGCStaticBase_Helper, DomainLocalModule *pLocalModule, DWORD dwClassDomainID); +EXTERN_C FCDECL1(void*, JIT_GetNonGCStaticBase_Helper, MethodTable *pMT); +EXTERN_C FCDECL1(void*, JIT_GetGCStaticBase_Helper, MethodTable *pMT); EXTERN_C void DoJITFailFast (); EXTERN_C FCDECL0(void, JIT_FailFast); @@ -1113,13 +1116,12 @@ struct VirtualFunctionPointerArgs FCDECL2(CORINFO_MethodPtr, JIT_VirtualFunctionPointer_Dynamic, Object * objectUNSAFE, VirtualFunctionPointerArgs * pArgs); -typedef HCCALL2_PTR(TADDR, FnStaticBaseHelper, TADDR arg0, TADDR arg1); +typedef HCCALL1_PTR(TADDR, FnStaticBaseHelper, TADDR arg0); struct StaticFieldAddressArgs { FnStaticBaseHelper staticBaseHelper; TADDR arg0; - TADDR arg1; SIZE_T offset; }; diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index 6a0f42ac7b0ac..c8fa155ca112f 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -2198,3 +2198,37 @@ PTR_OnStackReplacementManager LoaderAllocator::GetOnStackReplacementManager() #endif // #endif // FEATURE_ON_STACK_REPLACEMENT +#ifndef DACCESS_COMPILE +void LoaderAllocator::AllocateBytesForStaticVariables(uint8_t** ppbMem, uint32_t cbMem) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM()); + } + CONTRACTL_END; + + if (cbMem > 0) + { + uint8_t* pbMem = (uint8_t*)(void*)GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbMem)); + InterlockedCompareExchangeT(ppbMem, pbMem, NULL); + } +} + +void LoaderAllocator::AllocateGCHandlesBytesForStaticVariables(PTR_OBJECTREF* ppbObjectMem, uint32_t cSlots, MethodTable* pMTWithStaticBoxes) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM()); + } + CONTRACTL_END; + + if (cSlots > 0) + { + GetDomain()->AllocateObjRefPtrsInLargeTable(cSlots, (OBJECTREF**)ppbObjectMem, pMTWithStaticBoxes); + } +} +#endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/loaderallocator.hpp b/src/coreclr/vm/loaderallocator.hpp index 365063b0b5220..630476935ffc1 100644 --- a/src/coreclr/vm/loaderallocator.hpp +++ b/src/coreclr/vm/loaderallocator.hpp @@ -730,6 +730,8 @@ class LoaderAllocator LIMITED_METHOD_CONTRACT; return m_nGCCount; } + void AllocateBytesForStaticVariables(uint8_t** ppbMem, uint32_t cbMem); + void AllocateGCHandlesBytesForStaticVariables(PTR_OBJECTREF* ppbObjectMem, uint32_t cSlots, MethodTable* pMTWithStaticBoxes); static BOOL Destroy(QCall::LoaderAllocatorHandle pLoaderAllocator); diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index bf1414acda216..133ceac2d95d1 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -3006,7 +3006,7 @@ class NDirectMethodDesc : public MethodDesc LIMITED_METHOD_CONTRACT; MethodTable * pMT = GetMethodTable(); // Try to avoid touching the EEClass if possible - if (pMT->IsClassPreInited()) + if (!pMT->HasClassConstructor()) return FALSE; return !pMT->GetClass()->IsBeforeFieldInit(); } diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index fee5bc2078ba4..de7c7298369f3 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -370,51 +370,6 @@ BOOL MethodTable::ValidateWithPossibleAV() #ifndef DACCESS_COMPILE -//========================================================================================== -BOOL MethodTable::IsClassInited() -{ - WRAPPER_NO_CONTRACT; - - if (IsClassPreInited()) - return TRUE; - - if (IsSharedByGenericInstantiations()) - return FALSE; - - DomainLocalModule *pLocalModule = GetDomainLocalModule(); - - _ASSERTE(pLocalModule != NULL); - - return pLocalModule->IsClassInitialized(this); -} - -//========================================================================================== -BOOL MethodTable::IsInitError() -{ - WRAPPER_NO_CONTRACT; - - DomainLocalModule *pLocalModule = GetDomainLocalModule(); - _ASSERTE(pLocalModule != NULL); - - return pLocalModule->IsClassInitError(this); -} - -//========================================================================================== -// mark the class as having its .cctor run -void MethodTable::SetClassInited() -{ - WRAPPER_NO_CONTRACT; - _ASSERTE(!IsClassPreInited()); - GetDomainLocalModule()->SetClassInitialized(this); -} - -//========================================================================================== -void MethodTable::SetClassInitError() -{ - WRAPPER_NO_CONTRACT; - GetDomainLocalModule()->SetClassInitError(this); -} - //========================================================================================== // mark the class as having been restored. void MethodTable::SetIsRestored() @@ -703,16 +658,30 @@ MethodDesc *MethodTable::GetMethodDescForComInterfaceMethod(MethodDesc *pItfMD, } #endif // FEATURE_COMINTEROP -void MethodTable::AllocateAuxiliaryData(LoaderAllocator *pAllocator, Module *pLoaderModule, AllocMemTracker *pamTracker, bool hasGenericStatics, WORD nonVirtualSlots, S_SIZE_T extraAllocation) +void MethodTable::AllocateAuxiliaryData(LoaderAllocator *pAllocator, Module *pLoaderModule, AllocMemTracker *pamTracker, MethodTableStaticsFlags staticsFlags, WORD nonVirtualSlots, S_SIZE_T extraAllocation) { S_SIZE_T cbAuxiliaryData = S_SIZE_T(sizeof(MethodTableAuxiliaryData)); - size_t prependedAllocationSpace = 0; + size_t prependedAllocationSpace = nonVirtualSlots * sizeof(TADDR); - prependedAllocationSpace = nonVirtualSlots * sizeof(TADDR); + int16_t sizeofStaticsStructure = 0; - if (hasGenericStatics) - prependedAllocationSpace = prependedAllocationSpace + sizeof(GenericsStaticsInfo); + if (HasFlag(staticsFlags, MethodTableStaticsFlags::Thread)) + { + _ASSERTE(HasFlag(staticsFlags, MethodTableStaticsFlags::Present)); + sizeofStaticsStructure = sizeof(ThreadStaticsInfo); + } + else if (HasFlag(staticsFlags, MethodTableStaticsFlags::Generic)) + { + _ASSERTE(HasFlag(staticsFlags, MethodTableStaticsFlags::Present)); + sizeofStaticsStructure = sizeof(GenericsStaticsInfo); + } + else if (HasFlag(staticsFlags, MethodTableStaticsFlags::Present)) + { + sizeofStaticsStructure = sizeof(DynamicStaticsInfo); + } + + prependedAllocationSpace = prependedAllocationSpace + sizeofStaticsStructure; cbAuxiliaryData = cbAuxiliaryData + S_SIZE_T(prependedAllocationSpace) + extraAllocation; if (cbAuxiliaryData.IsOverflow()) @@ -725,8 +694,13 @@ void MethodTable::AllocateAuxiliaryData(LoaderAllocator *pAllocator, Module *pLo pMTAuxiliaryData = (MethodTableAuxiliaryData *)(pAuxiliaryDataRegion + prependedAllocationSpace); pMTAuxiliaryData->SetLoaderModule(pLoaderModule); - pMTAuxiliaryData->SetOffsetToNonVirtualSlots(hasGenericStatics ? -(int16_t)sizeof(GenericsStaticsInfo) : 0); + pMTAuxiliaryData->SetOffsetToNonVirtualSlots(-sizeofStaticsStructure); m_pAuxiliaryData = pMTAuxiliaryData; + + if (HasFlag(staticsFlags, MethodTableStaticsFlags::Present)) + { + MethodTableAuxiliaryData::GetDynamicStaticsInfo(pMTAuxiliaryData)->m_pMethodTable = this; + } } @@ -1071,22 +1045,7 @@ void MethodTable::SetupGenericsStaticsInfo(FieldDesc* pStaticFieldDescs) } CONTRACTL_END; - // No need to generate IDs for open types. Indeed since we don't save them - // in the NGEN image it would be actively incorrect to do so. However - // we still leave the optional member in the MethodTable holding the value -1 for the ID. - GenericsStaticsInfo *pInfo = GetGenericsStaticsInfo(); - if (!ContainsGenericVariables() && !IsSharedByGenericInstantiations()) - { - Module * pModuleForStatics = GetLoaderModule(); - - pInfo->m_DynamicTypeID = pModuleForStatics->AllocateDynamicEntry(this); - } - else - { - pInfo->m_DynamicTypeID = (SIZE_T)-1; - } - pInfo->m_pFieldDescs = pStaticFieldDescs; } @@ -1125,43 +1084,6 @@ void MethodTable::EnumMemoryRegionsForExtraInterfaceInfo() } #endif // DACCESS_COMPILE -//========================================================================================== -Module* MethodTable::GetModuleForStatics() -{ - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - - if (HasGenericsStaticsInfo()) - { - DWORD dwDynamicClassDomainID; - return GetGenericsStaticsModuleAndID(&dwDynamicClassDomainID); - } - else - { - return GetLoaderModule(); - } -} - -//========================================================================================== -DWORD MethodTable::GetModuleDynamicEntryID() -{ - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - - _ASSERTE(IsDynamicStatics() && "Only memory reflection emit types and generics can have a dynamic ID"); - - if (HasGenericsStaticsInfo()) - { - DWORD dwDynamicClassDomainID; - GetGenericsStaticsModuleAndID(&dwDynamicClassDomainID); - return dwDynamicClassDomainID; - } - else - { - return GetClass()->GetModuleDynamicID(); - } -} - #ifndef DACCESS_COMPILE #ifdef FEATURE_TYPEEQUIVALENCE @@ -2053,42 +1975,6 @@ DWORD MethodTable::GetIndexForFieldDesc(FieldDesc *pField) } } -//========================================================================================== -#ifdef _MSC_VER -#pragma optimize("t", on) -#endif // _MSC_VER -// compute whether the type can be considered to have had its -// static initialization run without doing anything at all, i.e. whether we know -// immediately that the type requires nothing to do for initialization -// -// If a type used as a representiative during JITting is PreInit then -// any types that it may represent within a code-sharing -// group are also PreInit. For example, if List is PreInit then List -// and List are also PreInit. This is because the dynamicStatics, staticRefHandles -// and hasCCtor are all identical given a head type, and weakening the domainNeutrality -// to DomainSpecific only makes more types PreInit. -BOOL MethodTable::IsClassPreInited() -{ - LIMITED_METHOD_CONTRACT; - - if (ContainsGenericVariables()) - return TRUE; - - if (HasClassConstructor()) - return FALSE; - - if (HasBoxedRegularStatics()) - return FALSE; - - if (IsDynamicStatics()) - return FALSE; - - return TRUE; -} -#ifdef _MSC_VER -#pragma optimize("", on) -#endif // _MSC_VER - //======================================================================================== namespace @@ -4065,26 +3951,20 @@ int MethodTable::GetRiscV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) #if !defined(DACCESS_COMPILE) //========================================================================================== -void MethodTable::AllocateRegularStaticBoxes() +void MethodTable::AllocateRegularStaticBoxes(OBJECTREF** ppStaticBase) { CONTRACTL { THROWS; GC_TRIGGERS; PRECONDITION(!ContainsGenericVariables()); - PRECONDITION(HasBoxedRegularStatics()); - MODE_ANY; + MODE_COOPERATIVE; } CONTRACTL_END; LOG((LF_CLASSLOADER, LL_INFO10000, "STATICS: Instantiating static handles for %s\n", GetDebugClassName())); - GCX_COOP(); - - PTR_BYTE pStaticBase = GetGCStaticsBasePointer(); - - GCPROTECT_BEGININTERIOR(pStaticBase); - + if (HasBoxedRegularStatics()) { FieldDesc *pField = HasGenericsStaticsInfo() ? GetGenericsStaticFieldDescs() : (GetApproxFieldDescListRaw() + GetNumIntroducedInstanceFields()); @@ -4096,13 +3976,12 @@ void MethodTable::AllocateRegularStaticBoxes() if (!pField->IsSpecialStatic() && pField->IsByValue()) { - AllocateRegularStaticBox(pField, (Object**)(pStaticBase + pField->GetOffset())); + AllocateRegularStaticBox(pField, (Object**)(((PTR_BYTE)*ppStaticBase) + pField->GetOffset())); } pField++; } } - GCPROTECT_END(); } void MethodTable::AllocateRegularStaticBox(FieldDesc* pField, Object** boxedStaticHandle) @@ -4396,11 +4275,7 @@ void MethodTable::DoRunClassInitThrowing() { if (pEntry->m_hrResultCode == S_FALSE) { - if (HasBoxedRegularStatics()) - { - // First, instantiate any objects needed for value type statics - AllocateRegularStaticBoxes(); - } + EnsureStaticDataAllocated(); // Nobody has run the .cctor yet if (HasClassConstructor()) @@ -4470,9 +4345,7 @@ void MethodTable::DoRunClassInitThrowing() pEntry->m_hrResultCode = S_OK; - // Set the initialization flags in the DLS and on domain-specific types. - // Note we also set the flag for dynamic statics, which use the DynamicStatics part - // of the DLS irrespective of whether the type is domain neutral or not. + // Set the initialization flag SetClassInited(); } @@ -4535,24 +4408,78 @@ void MethodTable::CheckRunClassInitThrowing() // To find GC hole easier... TRIGGERSGC(); - if (IsClassPreInited()) - return; - // Don't initialize shared generic instantiations (e.g. MyClass<__Canon>) if (IsSharedByGenericInstantiations()) return; - DomainLocalModule *pLocalModule = GetDomainLocalModule(); - _ASSERTE(pLocalModule); + EnsureStaticDataAllocated(); + DoRunClassInitThrowing(); +} + +void MethodTable::EnsureStaticDataAllocated() +{ + WRAPPER_NO_CONTRACT; + PTR_MethodTableAuxiliaryData pAuxiliaryData = GetAuxiliaryDataForWrite(); + if (!pAuxiliaryData->IsStaticDataAllocated() && IsDynamicStatics()) + { + DynamicStaticsInfo *pDynamicStaticsInfo = GetDynamicStaticsInfo(); + // Allocate space for normal statics if we might have them + if (pDynamicStaticsInfo->m_pNonGCStatics == NULL) + GetLoaderAllocator()->AllocateBytesForStaticVariables(&pDynamicStaticsInfo->m_pNonGCStatics, GetClass()->GetNonGCRegularStaticFieldBytes()); + + if (pDynamicStaticsInfo->m_pGCStatics == NULL) + GetLoaderAllocator()->AllocateGCHandlesBytesForStaticVariables(&pDynamicStaticsInfo->m_pGCStatics, GetClass()->GetNumHandleRegularStatics(), this); + } + pAuxiliaryData->SetIsStaticDataAllocated(); +} + +void MethodTable::AttemptToPreinit() +{ + if (IsClassInited()) + return; + + if (HasClassConstructor()) + { + // If there is a class constructor, then the class cannot be preinitted. + return; + } + + if (GetClass()->GetNonGCRegularStaticFieldBytes() == 0 && GetClass()->GetNumHandleRegularStatics() == 0) + { + // If there are static fields that are not thread statics, then the class is preinitted. + SetClassInited(); + return; + } + + // At this point, we are looking at a class that has no class constructor, but does have static fields - DWORD iClassIndex = GetClassIndex(); + if (IsSharedByGenericInstantiations()) + { + // If we don't know the exact type, we can't pre-allocate the fields + return; + } - // Check to see if we have already run the .cctor for this class. - if (!pLocalModule->IsClassAllocated(this, iClassIndex)) - pLocalModule->PopulateClass(this); + // All this class needs to be initialized is to allocate the memory for the static fields. Do so, and mark the type as initialized + EnsureStaticDataAllocated(); + SetClassInited(); + return; +} - if (!pLocalModule->IsClassInitialized(this, iClassIndex)) - DoRunClassInitThrowing(); +void MethodTable::EnsureTlsIndexAllocated() +{ + WRAPPER_NO_CONTRACT; + PTR_MethodTableAuxiliaryData pAuxiliaryData = GetAuxiliaryDataForWrite(); + if (!pAuxiliaryData->IsTlsIndexAllocated() && GetNumThreadStaticFields() > 0) + { + ThreadStaticsInfo *pThreadStaticsInfo = MethodTableAuxiliaryData::GetThreadStaticsInfo(GetAuxiliaryDataForWrite()); + // Allocate space for normal statics if we might have them + if (!pThreadStaticsInfo->NonGCTlsIndex.IsAllocated() && GetClass()->GetNonGCThreadStaticFieldBytes() > 0) + GetTLSIndexForThreadStatic(this, false, &pThreadStaticsInfo->NonGCTlsIndex); + + if (!pThreadStaticsInfo->GCTlsIndex.IsAllocated() && GetClass()->GetNumHandleThreadStatics() > 0) + GetTLSIndexForThreadStatic(this, true, &pThreadStaticsInfo->GCTlsIndex); + } + pAuxiliaryData->SetIsTlsIndexAllocated(); } //========================================================================================== @@ -9413,3 +9340,17 @@ PTR_MethodTable MethodTable::InterfaceMapIterator::GetInterface(MethodTable* pMT RETURN (pResult); } #endif // DACCESS_COMPILE + +void MethodTable::GetStaticsOffsets(StaticsOffsetType offsetType, bool fGenericStatics, uint32_t *dwGCOffset, uint32_t *dwNonGCOffset) +{ + if (offsetType == StaticsOffsetType::Normal) + { + *dwNonGCOffset = 0; + *dwGCOffset = 0; + } + else + { + *dwNonGCOffset = (uint32_t)sizeof(TADDR) * 2; + *dwGCOffset = (uint32_t)sizeof(TADDR) * 2; + } +} \ No newline at end of file diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index a862a2c4e883e..d6582a37d136e 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -26,6 +26,7 @@ #include "generics.h" #include "gcinfotypes.h" #include "enum_class_flags.h" +#include "threadstatics.h" /* * Forward Declarations @@ -68,6 +69,12 @@ struct MethodTableAuxiliaryData; typedef DPTR(MethodTableAuxiliaryData) PTR_MethodTableAuxiliaryData; typedef DPTR(MethodTableAuxiliaryData const) PTR_Const_MethodTableAuxiliaryData; +enum class StaticsOffsetType +{ + Normal, + ThreadLocal +}; + enum class ResolveVirtualStaticMethodFlags { None = 0, @@ -90,6 +97,16 @@ enum class FindDefaultInterfaceImplementationFlags support_use_as_flags // Enable the template functions in enum_class_flags.h }; +enum class MethodTableStaticsFlags +{ + None = 0, + Present = 0x1, + Generic = 0x2, + Thread = 0x4, + + support_use_as_flags // Enable the template functions in enum_class_flags.h +}; + //============================================================================ // This is the in-memory structure of a class and it will evolve. //============================================================================ @@ -271,15 +288,16 @@ struct GenericsDictInfo }; // struct GenericsDictInfo typedef DPTR(GenericsDictInfo) PTR_GenericsDictInfo; -struct GenericsStaticsInfo -{ - // Pointer to field descs for statics - PTR_FieldDesc m_pFieldDescs; - // Method table ID for statics - SIZE_T m_DynamicTypeID; +// These various statics structures exist directly before the MethodTableAuxiliaryData -}; // struct GenericsStaticsInfo +// Any MethodTable which has static variables has this structure +struct DynamicStaticsInfo; +struct ThreadStaticsInfo; +struct GenericsStaticsInfo; + +typedef DPTR(DynamicStaticsInfo) PTR_DynamicStaticsInfo; +typedef DPTR(ThreadStaticsInfo) PTR_ThreadStaticsInfo; typedef DPTR(GenericsStaticsInfo) PTR_GenericsStaticsInfo; // @@ -301,7 +319,7 @@ struct MethodTableAuxiliaryData // TO BE UPDATED IN ORDER TO ENSURE THAT METHODTABLES DUPLICATED FOR GENERIC INSTANTIATIONS // CARRY THE CORRECT INITIAL FLAGS. - enum_flag_CanCompareBitsOrUseFastGetHashCode = 0x0001, // Is any field type or sub field type overrode Equals or GetHashCode + enum_flag_Initialized = 0x0001, enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode = 0x0002, // Whether we have checked the overridden Equals or GetHashCode enum_flag_Unrestored = 0x0004, @@ -310,10 +328,10 @@ struct MethodTableAuxiliaryData enum_flag_IsNotFullyLoaded = 0x0040, enum_flag_DependenciesLoaded = 0x0080, // class and all dependencies loaded up to CLASS_LOADED_BUT_NOT_VERIFIED - // enum_unused = 0x0100, - // enum_unused = 0x0200, - // enum_unused = 0x0400, - // enum_unused = 0x0800, + enum_flag_IsInitError = 0x0100, + enum_flag_IsStaticDataAllocated = 0x0200, + enum_flag_CanCompareBitsOrUseFastGetHashCode = 0x0400, // Is any field type or sub field type overrode Equals or GetHashCode + enum_flag_IsTlsIndexAllocated = 0x0800, // enum_unused = 0x1000, // enum_unused = 0x2000, @@ -380,6 +398,67 @@ struct MethodTableAuxiliaryData } #endif + inline BOOL IsInitError() const + { + LIMITED_METHOD_DAC_CONTRACT; + return (VolatileLoad(&m_dwFlags) & enum_flag_IsInitError); + } + +#ifndef DACCESS_COMPILE + inline void SetInitError() + { + LIMITED_METHOD_CONTRACT; + InterlockedOr((LONG*)&m_dwFlags, (LONG)enum_flag_IsInitError); + } +#endif + + inline BOOL IsTlsIndexAllocated() const + { + LIMITED_METHOD_DAC_CONTRACT; + return (VolatileLoad(&m_dwFlags) & enum_flag_IsTlsIndexAllocated); + } + +#ifndef DACCESS_COMPILE + inline void SetIsTlsIndexAllocated() + { + LIMITED_METHOD_CONTRACT; + InterlockedOr((LONG*)&m_dwFlags, (LONG)enum_flag_IsTlsIndexAllocated); + } +#endif + + DWORD* getIsClassInitedFlagAddress() + { + LIMITED_METHOD_DAC_CONTRACT; + _ASSERTE(enum_flag_Initialized == 1); // This is an assumption in the JIT and in hand-written assembly at this time. + return &m_dwFlags; + } + + inline BOOL IsClassInited() const + { + return VolatileLoad(&m_dwFlags) & enum_flag_Initialized; + } + +#ifndef DACCESS_COMPILE + inline void SetClassInited() + { + LIMITED_METHOD_CONTRACT; + InterlockedOr((LONG*)&m_dwFlags, (LONG)enum_flag_Initialized); + } +#endif + + inline BOOL IsStaticDataAllocated() const + { + LIMITED_METHOD_DAC_CONTRACT; + return (VolatileLoad(&m_dwFlags) & enum_flag_IsStaticDataAllocated); + } + +#ifndef DACCESS_COMPILE + inline void SetIsStaticDataAllocated() + { + LIMITED_METHOD_CONTRACT; + InterlockedOr((LONG*)&m_dwFlags, (LONG)enum_flag_IsStaticDataAllocated); + } +#endif inline RUNTIMETYPEHANDLE GetExposedClassObjectHandle() const { @@ -437,12 +516,57 @@ struct MethodTableAuxiliaryData m_offsetToNonVirtualSlots = offset; } - static inline PTR_GenericsStaticsInfo GetGenericStaticsInfo(PTR_Const_MethodTableAuxiliaryData pAuxiliaryData) - { - return dac_cast(dac_cast(pAuxiliaryData) - sizeof(GenericsStaticsInfo)); - } + static inline PTR_DynamicStaticsInfo GetDynamicStaticsInfo(PTR_Const_MethodTableAuxiliaryData pAuxiliaryData); + static inline PTR_GenericsStaticsInfo GetGenericStaticsInfo(PTR_Const_MethodTableAuxiliaryData pAuxiliaryData); + static inline PTR_ThreadStaticsInfo GetThreadStaticsInfo(PTR_Const_MethodTableAuxiliaryData pAuxiliaryData); }; // struct MethodTableAuxiliaryData +// Any Generic MethodTable which has static variables has this structure. Note that it ends +// with a DynamicStatics structure so that lookups for just DynamicStatics will find that structure +// when looking for statics pointers +struct DynamicStaticsInfo +{ + PTR_OBJECTREF m_pGCStatics; + PTR_BYTE m_pNonGCStatics; + PTR_MethodTable m_pMethodTable; + MethodTableAuxiliaryData m_AuxData; + bool IsClassInited() const { return m_AuxData.IsClassInited(); } + PTR_MethodTable GetMethodTable() const { return m_pMethodTable; } +}; + +/* static */ inline PTR_DynamicStaticsInfo MethodTableAuxiliaryData::GetDynamicStaticsInfo(PTR_Const_MethodTableAuxiliaryData pAuxiliaryData) +{ + return dac_cast(dac_cast(pAuxiliaryData) - sizeof(DynamicStaticsInfo)); +} + +struct GenericsStaticsInfo +{ + // Pointer to field descs for statics + PTR_FieldDesc m_pFieldDescs; + + DynamicStaticsInfo m_DynamicStatics; +}; // struct GenericsStaticsInfo + +/* static */ inline PTR_GenericsStaticsInfo MethodTableAuxiliaryData::GetGenericStaticsInfo(PTR_Const_MethodTableAuxiliaryData pAuxiliaryData) +{ + return dac_cast(dac_cast(pAuxiliaryData) - sizeof(GenericsStaticsInfo)); +} + +// And MethodTable with Thread Statics has this structure. NOTE: This structure includes +// GenericsStatics which may not actually have the m_pFieldDescs filled in if the MethodTable +// is not actually Generic +struct ThreadStaticsInfo +{ + TLSIndex NonGCTlsIndex; + TLSIndex GCTlsIndex; + GenericsStaticsInfo m_genericStatics; +}; + +/* static */ inline PTR_ThreadStaticsInfo MethodTableAuxiliaryData::GetThreadStaticsInfo(PTR_Const_MethodTableAuxiliaryData pAuxiliaryData) +{ + return dac_cast(dac_cast(pAuxiliaryData) - sizeof(ThreadStaticsInfo)); +} + #ifdef UNIX_AMD64_ABI_ITF inline SystemVClassificationType CorInfoType2UnixAmd64Classification(CorElementType eeType) @@ -640,16 +764,11 @@ class MethodTable // is accessed lives in a "loader module". The rule for determining the loader module must ensure // that a type never outlives its loader module with respect to app-domain unloading // - // GetModuleForStatics() is the third kind of module. GetModuleForStatics() is module that - // statics are attached to. PTR_Module GetLoaderModule(); PTR_LoaderAllocator GetLoaderAllocator(); void SetLoaderAllocator(LoaderAllocator* pAllocator); - // Get the domain local module - useful for static init checks - PTR_DomainLocalModule GetDomainLocalModule(); - MethodTable *LoadEnclosingMethodTable(ClassLoadLevel targetLevel = CLASS_DEPENDENCIES_LOADED); LPCWSTR GetPathForErrorMessages(); @@ -808,14 +927,6 @@ class MethodTable BOOL HasExplicitOrImplicitPublicDefaultConstructor(); - //------------------------------------------------------------------- - // THE CLASS INITIALIZATION CONDITION - // (and related DomainLocalModule storage) - // - // - populate the DomainLocalModule if needed - // - run the cctor - // - public: // checks whether the class initialiser should be run on this class, and runs it if necessary @@ -853,15 +964,35 @@ class MethodTable // Init the m_dwFlags field for an array void SetIsArray(CorElementType arrayType); - BOOL IsClassPreInited(); - // mark the class as having its cctor run. #ifndef DACCESS_COMPILE - void SetClassInited(); - BOOL IsClassInited(); + void SetClassInited() + { + GetAuxiliaryDataForWrite()->SetClassInited(); + } + + void AttemptToPreinit(); +#endif + + BOOL IsClassInited() + { + return GetAuxiliaryDataForWrite()->IsClassInited(); + } + + // Allocate any memory needed for statics, acquire TLSIndex for TLS statics, and check to see if the class can be considered pre-inited, and if so, set the initialized flag + void EnsureStaticDataAllocated(); + void EnsureTlsIndexAllocated(); + + BOOL IsInitError() + { + return GetAuxiliaryData()->IsInitError(); + } - BOOL IsInitError(); - void SetClassInitError(); +#ifndef DACCESS_COMPILE + void SetClassInitError() + { + return GetAuxiliaryDataForWrite()->SetInitError(); + } #endif inline BOOL IsGlobalClass() @@ -870,9 +1001,6 @@ class MethodTable return (GetTypeDefRid() == RidFromToken(COR_GLOBAL_PARENT_TOKEN)); } - // uniquely identifes this type in the Domain table - DWORD GetClassIndex(); - private: #if defined(UNIX_AMD64_ABI_ITF) @@ -881,12 +1009,6 @@ class MethodTable bool ClassifyEightBytesWithManagedLayout(SystemVStructRegisterPassingHelperPtr helperPtr, unsigned int nestingLevel, unsigned int startOffsetOfStruct, bool isNativeStruct, MethodTable** pByValueClassCache); #endif // defined(UNIX_AMD64_ABI_ITF) - DWORD GetClassIndexFromToken(mdTypeDef typeToken) - { - LIMITED_METHOD_CONTRACT; - return RidFromToken(typeToken) - 1; - } - // called from CheckRunClassInitThrowing(). The type wasn't marked as // inited while we were there, so let's attempt to do the work. void DoRunClassInitThrowing(); @@ -914,7 +1036,7 @@ class MethodTable // The pending list is required for restoring types that reference themselves through // instantiations of the superclass or interfaces e.g. System.Int32 : IComparable - void AllocateRegularStaticBoxes(); + void AllocateRegularStaticBoxes(OBJECTREF** ppStaticBase); void AllocateRegularStaticBox(FieldDesc* pField, Object** boxedStaticHandle); static OBJECTREF AllocateStaticBox(MethodTable* pFieldMT, BOOL fPinned, bool canBeFrozen = false); @@ -2267,27 +2389,27 @@ class MethodTable DWORD GetOffsetOfFirstStaticHandle(); DWORD GetOffsetOfFirstStaticMT(); -#ifndef DACCESS_COMPILE inline PTR_BYTE GetNonGCStaticsBasePointer(); inline PTR_BYTE GetGCStaticsBasePointer(); +#ifndef DACCESS_COMPILE inline PTR_BYTE GetNonGCThreadStaticsBasePointer(); inline PTR_BYTE GetGCThreadStaticsBasePointer(); - inline PTR_BYTE GetGCThreadStaticsBaseHandle(); #endif //!DACCESS_COMPILE + // Do not use except in DAC and profiler scenarios inline PTR_BYTE GetNonGCThreadStaticsBasePointer(PTR_Thread pThread); inline PTR_BYTE GetGCThreadStaticsBasePointer(PTR_Thread pThread); inline DWORD IsDynamicStatics() { LIMITED_METHOD_DAC_CONTRACT; - return !TestFlagWithMask(enum_flag_StaticsMask, enum_flag_StaticsMask_NonDynamic); + return GetFlag(enum_flag_DynamicStatics); } - inline void SetDynamicStatics(BOOL fGeneric) + inline void SetDynamicStatics() { LIMITED_METHOD_CONTRACT; - SetFlag(fGeneric ? enum_flag_StaticsMask_Generics : enum_flag_StaticsMask_Dynamic); + SetFlag(enum_flag_DynamicStatics); } inline void SetHasBoxedRegularStatics() @@ -2317,7 +2439,7 @@ class MethodTable BOOL HasGenericsStaticsInfo() { LIMITED_METHOD_DAC_CONTRACT; - return GetFlag(enum_flag_StaticsMask_Generics); + return IsDynamicStatics() && HasInstantiation(); } PTR_FieldDesc GetGenericsStaticFieldDescs() @@ -2327,27 +2449,10 @@ class MethodTable return GetGenericsStaticsInfo()->m_pFieldDescs; } - BOOL HasCrossModuleGenericStaticsInfo() - { - LIMITED_METHOD_DAC_CONTRACT; - return TestFlagWithMask(enum_flag_StaticsMask, enum_flag_StaticsMask_CrossModuleGenerics); - } - - PTR_Module GetGenericsStaticsModuleAndID(DWORD * pID); - WORD GetNumHandleRegularStatics(); - WORD GetNumBoxedRegularStatics (); WORD GetNumBoxedThreadStatics (); - //------------------------------------------------------------------- - // DYNAMIC ID - // - - // Used for generics and reflection emit in memory - DWORD GetModuleDynamicEntryID(); - Module* GetModuleForStatics(); - //------------------------------------------------------------------- // GENERICS DICT INFO // @@ -2709,7 +2814,7 @@ class MethodTable // ------------------------------------------------------------------ #ifndef DACCESS_COMPILE - void AllocateAuxiliaryData(LoaderAllocator *pAllocator, Module *pLoaderModule, AllocMemTracker *pamTracker, bool hasGenericStatics = false, WORD nonVirtualSlots = 0, S_SIZE_T extraAllocation = S_SIZE_T(0)); + void AllocateAuxiliaryData(LoaderAllocator *pAllocator, Module *pLoaderModule, AllocMemTracker *pamTracker, MethodTableStaticsFlags staticsFlags = MethodTableStaticsFlags::None, WORD nonVirtualSlots = 0, S_SIZE_T extraAllocation = S_SIZE_T(0)); #endif inline PTR_Const_MethodTableAuxiliaryData GetAuxiliaryData() const @@ -2724,6 +2829,12 @@ class MethodTable return MethodTable::m_pAuxiliaryData; } + DWORD* getIsClassInitedFlagAddress() + { + LIMITED_METHOD_DAC_CONTRACT; + return GetAuxiliaryDataForWrite()->getIsClassInitedFlagAddress(); + } + //------------------------------------------------------------------- // The GUID Info // Used by COM interop to get GUIDs (IIDs and CLSIDs) @@ -3282,12 +3393,6 @@ public : enum_flag_UNUSED_ComponentSize_1 = 0x00000001, // GC depends on this bit enum_flag_HasCriticalFinalizer = 0x00000002, // finalizer must be run on Appdomain Unload - enum_flag_StaticsMask = 0x0000000C, - enum_flag_StaticsMask_NonDynamic = 0x00000000, - enum_flag_StaticsMask_Dynamic = 0x00000008, // dynamic statics (EnC, reflection.emit) - enum_flag_StaticsMask_Generics = 0x00000004, // generics statics - enum_flag_StaticsMask_CrossModuleGenerics = 0x0000000C, // cross module generics statics (NGen) - enum_flag_StaticsMask_IfGenericsThenCrossModule = 0x00000008, // helper constant to get rid of unnecessary check enum_flag_GenericsMask = 0x00000030, @@ -3333,7 +3438,6 @@ public : // to be up to date to reflect the default values of those flags for the // case where this MethodTable is for a String or Array enum_flag_StringArrayValues = SET_FALSE(enum_flag_HasCriticalFinalizer) | - SET_TRUE(enum_flag_StaticsMask_NonDynamic) | SET_FALSE(enum_flag_NotInPZM) | SET_TRUE(enum_flag_GenericsMask_NonGeneric) | SET_FALSE(enum_flag_HasVariance) | @@ -3426,9 +3530,8 @@ public : // TO BE UPDATED IN ORDER TO ENSURE THAT METHODTABLES DUPLICATED FOR GENERIC INSTANTIATIONS // CARRY THE CORECT FLAGS. - // The following bits describe usage of optional slots. They have to stay - // together because of we index using them into offset arrays. - enum_flag_HasPerInstInfo = 0x0001, + enum_flag_DynamicStatics = 0x0001, + enum_flag_HasPerInstInfo = 0x0002, enum_flag_HasDispatchMapSlot = 0x0004, enum_flag_HasBoxedRegularStatics = 0x0008, // GetNumBoxedRegularStatics() != 0 @@ -3580,9 +3683,26 @@ public : PTR_GenericsStaticsInfo GetGenericsStaticsInfo() { PTR_MethodTableAuxiliaryData AuxiliaryData = GetAuxiliaryDataForWrite(); + _ASSERTE(HasGenericsStaticsInfo()); return MethodTableAuxiliaryData::GetGenericStaticsInfo(AuxiliaryData); } +public: + PTR_DynamicStaticsInfo GetDynamicStaticsInfo() + { + PTR_MethodTableAuxiliaryData AuxiliaryData = GetAuxiliaryDataForWrite(); + _ASSERTE(IsDynamicStatics()); + return MethodTableAuxiliaryData::GetDynamicStaticsInfo(AuxiliaryData); + } + + PTR_ThreadStaticsInfo GetThreadStaticsInfo() + { + PTR_MethodTableAuxiliaryData AuxiliaryData = GetAuxiliaryDataForWrite(); + _ASSERTE(GetNumThreadStaticFields() > 0); + return MethodTableAuxiliaryData::GetThreadStaticsInfo(AuxiliaryData); + } +private: + // Optional members. These are used for fields in the data structure where // the fields are (a) known when MT is created and (b) there is a default // value for the field in the common case. That is, they are normally used @@ -3660,6 +3780,8 @@ public : public: BOOL Validate (); + + static void GetStaticsOffsets(StaticsOffsetType staticsOffsetType, bool fGenericsStatics, uint32_t *dwGCOffset, uint32_t *dwNonGCOffset); }; // class MethodTable #ifndef CROSSBITNESS_COMPILE diff --git a/src/coreclr/vm/methodtable.inl b/src/coreclr/vm/methodtable.inl index 8ca224fcfba7c..e7ce1549bcd9c 100644 --- a/src/coreclr/vm/methodtable.inl +++ b/src/coreclr/vm/methodtable.inl @@ -1078,72 +1078,50 @@ inline DWORD MethodTable::GetOptionalMembersSize() return GetEndOffsetOfOptionalMembers() - GetStartOffsetOfOptionalMembers(); } -#ifndef DACCESS_COMPILE //========================================================================================== inline PTR_BYTE MethodTable::GetNonGCStaticsBasePointer() { WRAPPER_NO_CONTRACT; - return GetDomainLocalModule()->GetNonGCStaticsBasePointer(this); + if (!IsDynamicStatics()) + { + return NULL; + } + else + { + return GetDynamicStaticsInfo()->m_pNonGCStatics; + } } //========================================================================================== inline PTR_BYTE MethodTable::GetGCStaticsBasePointer() { WRAPPER_NO_CONTRACT; - return GetDomainLocalModule()->GetGCStaticsBasePointer(this); -} - -//========================================================================================== -inline PTR_BYTE MethodTable::GetNonGCThreadStaticsBasePointer() -{ - CONTRACTL + if (!IsDynamicStatics()) { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - // Get the current thread - PTR_Thread pThread = dac_cast(GetThread()); - - // Get the current module's ModuleIndex - ModuleIndex index = GetModuleForStatics()->GetModuleIndex(); - - PTR_ThreadLocalBlock pTLB = ThreadStatics::GetCurrentTLB(pThread); - - PTR_ThreadLocalModule pTLM = pTLB->GetTLMIfExists(index); - if (pTLM == NULL) return NULL; - - return pTLM->GetNonGCStaticsBasePointer(this); + } + else + { + return (PTR_BYTE)GetDynamicStaticsInfo()->m_pGCStatics; + } } +#ifndef DACCESS_COMPILE //========================================================================================== -inline PTR_BYTE MethodTable::GetGCThreadStaticsBaseHandle() +inline PTR_BYTE MethodTable::GetNonGCThreadStaticsBasePointer() { CONTRACTL { - NOTHROW; - GC_NOTRIGGER; + THROWS; + GC_TRIGGERS; MODE_COOPERATIVE; } CONTRACTL_END; - // Get the current thread - PTR_Thread pThread = dac_cast(GetThread()); - - // Get the current module's ModuleIndex - ModuleIndex index = GetModuleForStatics()->GetModuleIndex(); - - PTR_ThreadLocalBlock pTLB = ThreadStatics::GetCurrentTLB(pThread); - - PTR_ThreadLocalModule pTLM = pTLB->GetTLMIfExists(index); - if (pTLM == NULL) - return NULL; - - return dac_cast(pTLM->GetPrecomputedGCStaticsBaseHandle()); + EnsureTlsIndexAllocated(); + TLSIndex tlsIndex = GetThreadStaticsInfo()->NonGCTlsIndex; + return (PTR_BYTE)GetThreadLocalStaticBase(tlsIndex); } //========================================================================================== @@ -1151,25 +1129,15 @@ inline PTR_BYTE MethodTable::GetGCThreadStaticsBasePointer() { CONTRACTL { - NOTHROW; - GC_NOTRIGGER; + THROWS; + GC_TRIGGERS; MODE_COOPERATIVE; } CONTRACTL_END; - // Get the current thread - PTR_Thread pThread = dac_cast(GetThread()); - - // Get the current module's ModuleIndex - ModuleIndex index = GetModuleForStatics()->GetModuleIndex(); - - PTR_ThreadLocalBlock pTLB = ThreadStatics::GetCurrentTLB(pThread); - - PTR_ThreadLocalModule pTLM = pTLB->GetTLMIfExists(index); - if (pTLM == NULL) - return NULL; - - return pTLM->GetGCStaticsBasePointer(this); + EnsureTlsIndexAllocated(); + TLSIndex tlsIndex = GetThreadStaticsInfo()->GCTlsIndex; + return (PTR_BYTE)GetThreadLocalStaticBase(tlsIndex); } #endif //!DACCESS_COMPILE @@ -1179,16 +1147,15 @@ inline PTR_BYTE MethodTable::GetNonGCThreadStaticsBasePointer(PTR_Thread pThread { LIMITED_METHOD_DAC_CONTRACT; - // Get the current module's ModuleIndex - ModuleIndex index = GetModuleForStatics()->GetModuleIndex(); - - PTR_ThreadLocalBlock pTLB = ThreadStatics::GetCurrentTLB(pThread); + TLSIndex tlsIndex = GetThreadStaticsInfo()->NonGCTlsIndex; + if (!tlsIndex.IsAllocated()) + return NULL; - PTR_ThreadLocalModule pTLM = pTLB->GetTLMIfExists(index); - if (pTLM == NULL) + PTR_ThreadLocalData pThreadLocalData = pThread->GetThreadLocalDataPtr(); + if (pThreadLocalData == NULL) return NULL; - return pTLM->GetNonGCStaticsBasePointer(this); + return (PTR_BYTE)GetThreadLocalStaticBaseNoCreate(pThreadLocalData, tlsIndex); } //========================================================================================== @@ -1196,23 +1163,15 @@ inline PTR_BYTE MethodTable::GetGCThreadStaticsBasePointer(PTR_Thread pThread) { LIMITED_METHOD_DAC_CONTRACT; - // Get the current module's ModuleIndex - ModuleIndex index = GetModuleForStatics()->GetModuleIndex(); - - PTR_ThreadLocalBlock pTLB = ThreadStatics::GetCurrentTLB(pThread); - - PTR_ThreadLocalModule pTLM = pTLB->GetTLMIfExists(index); - if (pTLM == NULL) + TLSIndex tlsIndex = GetThreadStaticsInfo()->GCTlsIndex; + if (!tlsIndex.IsAllocated()) return NULL; - return pTLM->GetGCStaticsBasePointer(this); -} + PTR_ThreadLocalData pThreadLocalData = pThread->GetThreadLocalDataPtr(); + if (pThreadLocalData == NULL) + return NULL; -//========================================================================================== -inline PTR_DomainLocalModule MethodTable::GetDomainLocalModule() -{ - WRAPPER_NO_CONTRACT; - return GetModuleForStatics()->GetDomainLocalModule(); + return (PTR_BYTE)GetThreadLocalStaticBaseNoCreate(pThreadLocalData, tlsIndex); } //========================================================================================== @@ -1237,13 +1196,6 @@ inline OBJECTREF MethodTable::AllocateNoChecks() } -//========================================================================================== -inline DWORD MethodTable::GetClassIndex() -{ - WRAPPER_NO_CONTRACT; - return GetClassIndexFromToken(GetCl()); -} - #ifndef DACCESS_COMPILE //========================================================================================== // unbox src into dest, making sure src is of the correct type. @@ -1297,27 +1249,6 @@ inline void MethodTable::UnBoxIntoUnchecked(void *dest, OBJECTREF src) } #endif -//========================================================================================== -FORCEINLINE PTR_Module MethodTable::GetGenericsStaticsModuleAndID(DWORD * pID) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - SUPPORTS_DAC; - } - CONTRACTL_END - - _ASSERTE(HasGenericsStaticsInfo()); - - PTR_MethodTableAuxiliaryData AuxiliaryData = GetAuxiliaryDataForWrite(); - PTR_GenericsStaticsInfo staticsInfo = MethodTableAuxiliaryData::GetGenericStaticsInfo(AuxiliaryData); - _ASSERTE(FitsIn(staticsInfo->m_DynamicTypeID) || staticsInfo->m_DynamicTypeID == (SIZE_T)-1); - *pID = (DWORD)staticsInfo->m_DynamicTypeID; - return AuxiliaryData->GetLoaderModule(); -} - //========================================================================================== inline OBJECTHANDLE MethodTable::GetLoaderAllocatorObjectHandle() { diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 134997874c184..9f984d147d618 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -1674,21 +1674,10 @@ MethodTableBuilder::BuildMethodTableThrowing( // // We decide here if we need a dynamic entry for our statics. We need it here because - // the offsets of our fields will depend on this. For the dynamic case (which requires - // an extra indirection (indirect depending of methodtable) we'll allocate the slot - // in setupmethodtable - if (((pAllocator->IsCollectible() || pModule->IsReflection() || bmtGenerics->HasInstantiation() || !pModule->IsStaticStoragePrepared(cl)) && - (bmtVT->GetClassCtorSlotIndex() != INVALID_SLOT_INDEX || bmtEnumFields->dwNumStaticFields !=0)) -#ifdef FEATURE_METADATA_UPDATER - // Classes in modules that have been edited (would do on class level if there were a - // way to tell if the class had been edited) also have dynamic statics as the number - // of statics might have changed, so can't use the static module-wide storage - || (pModule->IsEditAndContinueEnabled() && - ((EditAndContinueModule*)pModule)->GetApplyChangesCount() > CorDB_DEFAULT_ENC_FUNCTION_VERSION) -#endif // FEATURE_METADATA_UPDATER - ) + // the offsets of our fields will depend on this. + if (bmtEnumFields->dwNumStaticFields != 0) { - // We will need a dynamic id + // We will need statics bmtProp->fDynamicStatics = true; if (bmtGenerics->HasInstantiation()) @@ -1901,15 +1890,6 @@ MethodTableBuilder::BuildMethodTableThrowing( pMT->SetupGenericsStaticsInfo(pStaticFieldDescs); } - else - { - // Get an id for the dynamic class. We store it in the class because - // no class that is persisted in ngen should have it (ie, if the class is ngened - // The id is stored in an optional field so we need to ensure an optional field descriptor has - // been allocated for this EEClass instance. - EnsureOptionalFieldsAreAllocated(GetHalfBakedClass(), m_pAllocMemTracker, pAllocator->GetLowFrequencyHeap()); - SetModuleDynamicID(GetModule()->AllocateDynamicEntry(pMT)); - } } // @@ -7903,11 +7883,8 @@ VOID MethodTableBuilder::PlaceRegularStaticFields() // Tell the module to give us the offsets we'll be using and commit space for us // if necessary - DWORD dwNonGCOffset, dwGCOffset; - GetModule()->GetOffsetsForRegularStaticData(bmtInternal->pType->GetTypeDefToken(), - bmtProp->fDynamicStatics, - GetNumHandleRegularStatics(), dwCumulativeStaticFieldPos, - &dwGCOffset, &dwNonGCOffset); + uint32_t dwNonGCOffset, dwGCOffset; + MethodTable::GetStaticsOffsets(StaticsOffsetType::Normal, bmtProp->fGenericsStatics, &dwGCOffset, &dwNonGCOffset); // Allocate boxed statics first ("x << LOG2_PTRSIZE" is equivalent to "x * sizeof(void *)") dwCumulativeStaticGCFieldPos = bmtFP->NumRegularStaticGCBoxedFields<m_debugName, pCurField->GetOffset())); } - if (bmtProp->fDynamicStatics) - { - _ASSERTE(dwNonGCOffset == 0 || // no statics at all - dwNonGCOffset == OFFSETOF__DomainLocalModule__NormalDynamicEntry__m_pDataBlob); // We need space to point to the GC statics - bmtProp->dwNonGCRegularStaticFieldBytes = dwCumulativeStaticFieldPos; - } - else - { - bmtProp->dwNonGCRegularStaticFieldBytes = 0; // Non dynamics shouldnt be using this - } - LOG((LF_CLASSLOADER, LL_INFO10000, "STATICS: Static field bytes needed (0 is normal for non dynamic case)%i\n", bmtProp->dwNonGCRegularStaticFieldBytes)); + _ASSERTE(dwNonGCOffset == 0 || (dwNonGCOffset == sizeof(TADDR) * 2)); + bmtProp->dwNonGCRegularStaticFieldBytes = dwCumulativeStaticFieldPos; + LOG((LF_CLASSLOADER, LL_INFO10000, "STATICS: Static field bytes needed %i\n", bmtProp->dwNonGCRegularStaticFieldBytes)); } @@ -8024,12 +7993,9 @@ VOID MethodTableBuilder::PlaceThreadStaticFields() // Tell the module to give us the offsets we'll be using and commit space for us // if necessary - DWORD dwNonGCOffset, dwGCOffset; + uint32_t dwNonGCOffset, dwGCOffset; - GetModule()->GetOffsetsForThreadStaticData(bmtInternal->pType->GetTypeDefToken(), - bmtProp->fDynamicStatics, - GetNumHandleThreadStatics(), dwCumulativeStaticFieldPos, - &dwGCOffset, &dwNonGCOffset); + MethodTable::GetStaticsOffsets(StaticsOffsetType::ThreadLocal, bmtProp->fGenericsStatics, &dwGCOffset, &dwNonGCOffset); // Allocate boxed statics first ("x << LOG2_PTRSIZE" is equivalent to "x * sizeof(void *)") dwCumulativeStaticGCFieldPos = bmtFP->NumThreadStaticGCBoxedFields<m_debugName, pCurField->GetOffset())); } - if (bmtProp->fDynamicStatics) + if (dwCumulativeStaticFieldPos != 0) { - _ASSERTE(dwNonGCOffset == 0 || // no thread statics at all - dwNonGCOffset == OFFSETOF__ThreadLocalModule__DynamicEntry__m_pDataBlob); // We need space to point to the GC statics + _ASSERTE(bmtProp->fDynamicStatics); bmtProp->dwNonGCThreadStaticFieldBytes = dwCumulativeStaticFieldPos; } else { - bmtProp->dwNonGCThreadStaticFieldBytes = 0; // Non dynamics shouldnt be using this + bmtProp->dwNonGCThreadStaticFieldBytes = 0; } LOG((LF_CLASSLOADER, LL_INFO10000, "STATICS: ThreadStatic field bytes needed (0 is normal for non dynamic case)%i\n", bmtProp->dwNonGCThreadStaticFieldBytes)); } @@ -10253,6 +10218,7 @@ MethodTable * MethodTableBuilder::AllocateNewMT( BOOL isInterface, BOOL fDynamicStatics, BOOL fHasGenericsStaticsInfo, + bool fHasThreadStatics, BOOL fHasVirtualStaticMethods #ifdef FEATURE_COMINTEROP , BOOL fHasDynamicInterfaceMap @@ -10355,7 +10321,20 @@ MethodTable * MethodTableBuilder::AllocateNewMT( pMT->SetFlag(MethodTable::enum_flag_HasPerInstInfo); } - pMT->AllocateAuxiliaryData(pAllocator, pLoaderModule, pamTracker, fHasGenericsStaticsInfo, static_cast(dwNonVirtualSlots), S_SIZE_T(dispatchMapAllocationSize)); + MethodTableStaticsFlags staticsFlags = MethodTableStaticsFlags::None; + if (fDynamicStatics) + staticsFlags |= MethodTableStaticsFlags::Present; + + if (fHasGenericsStaticsInfo) + { + _ASSERTE(fDynamicStatics); + staticsFlags |= MethodTableStaticsFlags::Generic; + } + + if (fHasThreadStatics) + staticsFlags |= MethodTableStaticsFlags::Thread; + + pMT->AllocateAuxiliaryData(pAllocator, pLoaderModule, pamTracker, staticsFlags, static_cast(dwNonVirtualSlots), S_SIZE_T(dispatchMapAllocationSize)); // This also disables IBC logging until the type is sufficiently initialized so // it needs to be done early @@ -10453,7 +10432,7 @@ MethodTable * MethodTableBuilder::AllocateNewMT( if (fDynamicStatics) { - pMT->SetDynamicStatics(fHasGenericsStaticsInfo); + pMT->SetDynamicStatics(); } // the dictionary pointers follow the interface map @@ -10557,6 +10536,7 @@ MethodTableBuilder::SetupMethodTable2( IsInterface(), bmtProp->fDynamicStatics, bmtProp->fGenericsStatics, + bmtEnumFields->dwNumThreadStaticFields != 0, bmtProp->fHasVirtualStaticMethods, #ifdef FEATURE_COMINTEROP fHasDynamicInterfaceMap, @@ -10646,6 +10626,7 @@ MethodTableBuilder::SetupMethodTable2( pMT->SetHasClassConstructor(); CONSISTENCY_CHECK(pMT->GetClassConstructorSlot() == bmtVT->pCCtor->GetSlotIndex()); } + if (bmtVT->pDefaultCtor != NULL) { pMT->SetHasDefaultConstructor(); diff --git a/src/coreclr/vm/methodtablebuilder.h b/src/coreclr/vm/methodtablebuilder.h index a017e3c8d5561..ca41ae414f931 100644 --- a/src/coreclr/vm/methodtablebuilder.h +++ b/src/coreclr/vm/methodtablebuilder.h @@ -206,7 +206,6 @@ class MethodTableBuilder void SetUnsafeValueClass() { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetUnsafeValueClass(); } void SetHasFieldsWhichMustBeInited() { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetHasFieldsWhichMustBeInited(); } void SetHasNonPublicFields() { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetHasNonPublicFields(); } - void SetModuleDynamicID(DWORD x) { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetModuleDynamicID(x); } void SetNumHandleRegularStatics(WORD x) { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetNumHandleRegularStatics(x); } void SetNumHandleThreadStatics(WORD x) { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetNumHandleThreadStatics(x); } void SetNumBoxedRegularStatics(WORD x) { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetNumBoxedRegularStatics(x); } @@ -2954,6 +2953,7 @@ class MethodTableBuilder BOOL isIFace, BOOL fDynamicStatics, BOOL fHasGenericsStaticsInfo, + bool fHasThreadStatics, BOOL fHasVirtualStaticMethods #ifdef FEATURE_COMINTEROP , BOOL bHasDynamicInterfaceMap diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 90768a47b5157..d587365880c57 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -3206,21 +3206,6 @@ static PCODE getHelperForSharedStatic(Module * pModule, CORCOMPILE_FIXUP_BLOB_KI CorInfoHelpFunc helpFunc = CEEInfo::getSharedStaticsHelper(pFD, pMT); - TADDR moduleID = pMT->GetModuleForStatics()->GetModuleID(); - - TADDR classID = 0; - if (helpFunc != CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR && helpFunc != CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR) - { - if (pMT->IsDynamicStatics()) - { - classID = pMT->GetModuleDynamicEntryID(); - } - else - { - classID = pMT->GetClassIndex(); - } - } - bool fUnbox = (pFD->GetFieldType() == ELEMENT_TYPE_VALUETYPE); AllocMemTracker amTracker; @@ -3230,8 +3215,7 @@ static PCODE getHelperForSharedStatic(Module * pModule, CORCOMPILE_FIXUP_BLOB_KI AllocMem(S_SIZE_T(sizeof(StaticFieldAddressArgs)))); pArgs->staticBaseHelper = (FnStaticBaseHelper)CEEJitInfo::getHelperFtnStatic((CorInfoHelpFunc)helpFunc); - pArgs->arg0 = moduleID; - pArgs->arg1 = classID; + pArgs->arg0 = (TADDR)pMT; pArgs->offset = pFD->GetOffset(); PCODE pHelper = DynamicHelpers::CreateHelper(pModule->GetLoaderAllocator(), (TADDR)pArgs, @@ -3246,53 +3230,50 @@ static PCODE getHelperForStaticBase(Module * pModule, CORCOMPILE_FIXUP_BLOB_KIND { STANDARD_VM_CONTRACT; - int helpFunc = CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE; - - if (kind == ENCODE_STATIC_BASE_GC_HELPER || kind == ENCODE_THREAD_STATIC_BASE_GC_HELPER) - { - helpFunc = CORINFO_HELP_GETSHARED_GCSTATIC_BASE; - } - - if (pMT->IsDynamicStatics()) - { - const int delta = CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS - CORINFO_HELP_GETSHARED_GCSTATIC_BASE; - helpFunc += delta; - } - else - if (!pMT->HasClassConstructor() && !pMT->HasBoxedRegularStatics()) - { - const int delta = CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR - CORINFO_HELP_GETSHARED_GCSTATIC_BASE; - helpFunc += delta; - } + bool GCStatic = (kind == ENCODE_STATIC_BASE_GC_HELPER || kind == ENCODE_THREAD_STATIC_BASE_GC_HELPER); + bool noCtor = (!pMT->HasClassConstructor() && !pMT->HasBoxedRegularStatics()); + bool threadStatic = (kind == ENCODE_THREAD_STATIC_BASE_NONGC_HELPER || kind == ENCODE_THREAD_STATIC_BASE_GC_HELPER); - if (kind == ENCODE_THREAD_STATIC_BASE_NONGC_HELPER || kind == ENCODE_THREAD_STATIC_BASE_GC_HELPER) + CorInfoHelpFunc helper; + + if (threadStatic) { - const int delta = CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE - CORINFO_HELP_GETSHARED_GCSTATIC_BASE; - helpFunc += delta; - } - - PCODE pHelper; - if (helpFunc == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR || helpFunc == CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR) - { - pHelper = DynamicHelpers::CreateHelper(pModule->GetLoaderAllocator(), pMT->GetModule()->GetModuleID(), CEEJitInfo::getHelperFtnStatic((CorInfoHelpFunc)helpFunc)); + if (GCStatic) + { + if (noCtor) + helper = CORINFO_HELP_GET_GCTHREADSTATIC_BASE_NOCTOR; + else + helper = CORINFO_HELP_GET_GCTHREADSTATIC_BASE; + } + else + { + if (noCtor) + helper = CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE_NOCTOR; + else + helper = CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE; + } } else { - TADDR moduleID = pMT->GetModuleForStatics()->GetModuleID(); - - TADDR classID; - if (pMT->IsDynamicStatics()) + if (GCStatic) { - classID = pMT->GetModuleDynamicEntryID(); + if (noCtor) + helper = CORINFO_HELP_GET_GCSTATIC_BASE_NOCTOR; + else + helper = CORINFO_HELP_GET_GCSTATIC_BASE; } else { - classID = pMT->GetClassIndex(); + if (noCtor) + helper = CORINFO_HELP_GET_NONGCSTATIC_BASE_NOCTOR; + else + helper = CORINFO_HELP_GET_NONGCSTATIC_BASE; } - - pHelper = DynamicHelpers::CreateHelper(pModule->GetLoaderAllocator(), moduleID, classID, CEEJitInfo::getHelperFtnStatic((CorInfoHelpFunc)helpFunc)); } + PCODE pHelper; + pHelper = DynamicHelpers::CreateHelper(pModule->GetLoaderAllocator(), (TADDR)pMT, CEEJitInfo::getHelperFtnStatic(helper)); + return pHelper; } @@ -3848,11 +3829,9 @@ extern "C" SIZE_T STDCALL DynamicHelperWorker(TransitionBlock * pTransitionBlock result = (SIZE_T)th.AsMethodTable()->GetGCStaticsBasePointer(); break; case ENCODE_THREAD_STATIC_BASE_NONGC_HELPER: - ThreadStatics::GetTLM(th.AsMethodTable())->EnsureClassAllocated(th.AsMethodTable()); result = (SIZE_T)th.AsMethodTable()->GetNonGCThreadStaticsBasePointer(); break; case ENCODE_THREAD_STATIC_BASE_GC_HELPER: - ThreadStatics::GetTLM(th.AsMethodTable())->EnsureClassAllocated(th.AsMethodTable()); result = (SIZE_T)th.AsMethodTable()->GetGCThreadStaticsBasePointer(); break; case ENCODE_CCTOR_TRIGGER: diff --git a/src/coreclr/vm/proftoeeinterfaceimpl.inl b/src/coreclr/vm/proftoeeinterfaceimpl.inl index 18404dfe91920..6d26815f1ac1e 100644 --- a/src/coreclr/vm/proftoeeinterfaceimpl.inl +++ b/src/coreclr/vm/proftoeeinterfaceimpl.inl @@ -181,8 +181,6 @@ inline BOOL IsClassOfMethodTableInited(MethodTable * pMethodTable) LIMITED_METHOD_CONTRACT; return (pMethodTable->IsRestored() && - (pMethodTable->GetModuleForStatics() != NULL) && - (pMethodTable->GetDomainLocalModule() != NULL) && pMethodTable->IsClassInited()); } diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 507a8eb0ea2ca..645ca5e45576f 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -110,24 +110,6 @@ BYTE* ThreadStore::s_pOSContextBuffer = NULL; CLREvent *ThreadStore::s_pWaitForStackCrawlEvent; -PTR_ThreadLocalModule ThreadLocalBlock::GetTLMIfExists(ModuleIndex index) -{ - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - - if (index.m_dwIndex >= m_TLMTableSize) - return NULL; - - return m_pTLMTable[index.m_dwIndex].pTLM; -} - -PTR_ThreadLocalModule ThreadLocalBlock::GetTLMIfExists(MethodTable* pMT) -{ - WRAPPER_NO_CONTRACT; - ModuleIndex index = pMT->GetModuleForStatics()->GetModuleIndex(); - return GetTLMIfExists(index); -} - #ifndef DACCESS_COMPILE BOOL Thread::s_fCleanFinalizedThread = FALSE; @@ -369,6 +351,7 @@ void SetThread(Thread* t) gCurrentThreadInfo.m_pThread = t; if (t != NULL) { + InitializeCurrentThreadsStaticData(t); EnsureTlsDestructionMonitor(); } } @@ -1396,6 +1379,8 @@ Thread::Thread() m_fPreemptiveGCDisabled = 0; + m_pThreadLocalData = NULL; + #ifdef _DEBUG m_ulForbidTypeLoad = 0; m_GCOnTransitionsOK = TRUE; @@ -2954,7 +2939,7 @@ void Thread::OnThreadTerminate(BOOL holdingLock) SafeSetThrowables(NULL); // Free all structures related to thread statics for this thread - DeleteThreadStaticData(); + DeleteThreadStaticData(this == GetThreadNULLOk()); } @@ -7593,10 +7578,6 @@ LPVOID Thread::GetStaticFieldAddress(FieldDesc *pFD) // for static field the MethodTable is exact even for generic classes MethodTable *pMT = pFD->GetEnclosingMethodTable(); - // We need to make sure that the class has been allocated, however - // we should not call the class constructor - ThreadStatics::GetTLM(pMT)->EnsureClassAllocated(pMT); - PTR_BYTE base = NULL; if (pFD->GetFieldType() == ELEMENT_TYPE_CLASS || @@ -7749,7 +7730,7 @@ Frame * Thread::NotifyFrameChainOfExceptionUnwind(Frame* pStartFrame, LPVOID pvL // //+---------------------------------------------------------------------------- -void Thread::DeleteThreadStaticData() +void Thread::DeleteThreadStaticData(bool forCurrentThread) { CONTRACTL { NOTHROW; @@ -7757,22 +7738,12 @@ void Thread::DeleteThreadStaticData() } CONTRACTL_END; - m_ThreadLocalBlock.FreeTable(); -} - -//+---------------------------------------------------------------------------- -// -// Method: Thread::DeleteThreadStaticData public -// -// Synopsis: Delete the static data for the given module. This is called -// when the AssemblyLoadContext unloads. -// -// -//+---------------------------------------------------------------------------- - -void Thread::DeleteThreadStaticData(ModuleIndex index) -{ - m_ThreadLocalBlock.FreeTLM(index.m_dwIndex, FALSE /* isThreadShuttingDown */); + if (m_pThreadLocalData != NULL) + { + if (forCurrentThread) + FreeCurrentThreadStaticData(); + m_pThreadLocalData = NULL; + } } OBJECTREF Thread::GetCulture(BOOL bUICulture) @@ -8341,7 +8312,7 @@ Thread::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) m_ExceptionState.EnumChainMemoryRegions(flags); - m_ThreadLocalBlock.EnumMemoryRegions(flags); + EnumThreadMemoryRegions(GetThreadLocalDataPtr(), flags); if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE) { diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index bfb154b0e539d..b684497688365 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -145,10 +145,6 @@ class PendingTypeLoadHolder; class PrepareCodeConfig; class NativeCodeVersion; -struct ThreadLocalBlock; -typedef DPTR(struct ThreadLocalBlock) PTR_ThreadLocalBlock; -typedef DPTR(PTR_ThreadLocalBlock) PTR_PTR_ThreadLocalBlock; - typedef void(*ADCallBackFcnType)(LPVOID); #include "stackwalktypes.h" @@ -165,73 +161,9 @@ typedef void(*ADCallBackFcnType)(LPVOID); #include "eventpipeadaptertypes.h" #endif // FEATURE_PERFTRACING -struct TLMTableEntry; - -typedef DPTR(struct TLMTableEntry) PTR_TLMTableEntry; -typedef DPTR(struct ThreadLocalModule) PTR_ThreadLocalModule; - class ThreadStaticHandleTable; -struct ThreadLocalModule; class Module; -struct ThreadLocalBlock -{ - friend class ClrDataAccess; - -private: - PTR_TLMTableEntry m_pTLMTable; // Table of ThreadLocalModules - SIZE_T m_TLMTableSize; // Current size of table - SpinLock m_TLMTableLock; // Spinlock used to synchronize growing the table and freeing TLM by other threads - - // Each ThreadLocalBlock has its own ThreadStaticHandleTable. The ThreadStaticHandleTable works - // by allocating Object arrays on the GC heap and keeping them alive with pinning handles. - // - // We use the ThreadStaticHandleTable to allocate space for GC thread statics. A GC thread - // static is thread static that is either a reference type or a value type whose layout - // contains a pointer to a reference type. - - ThreadStaticHandleTable * m_pThreadStaticHandleTable; - -public: - -#ifndef DACCESS_COMPILE - void AllocateThreadStaticHandles(Module * pModule, ThreadLocalModule * pThreadLocalModule); - OBJECTHANDLE AllocateStaticFieldObjRefPtrs(int nRequested, OBJECTHANDLE* ppLazyAllocate = NULL); - void InitThreadStaticHandleTable(); - - void AllocateThreadStaticBoxes(MethodTable* pMT); -#endif - -public: // used by code generators - static SIZE_T GetOffsetOfModuleSlotsPointer() { return offsetof(ThreadLocalBlock, m_pTLMTable); } - -public: - -#ifndef DACCESS_COMPILE - ThreadLocalBlock() - : m_pTLMTable(NULL), m_TLMTableSize(0), m_pThreadStaticHandleTable(NULL) - { - m_TLMTableLock.Init(LOCK_TYPE_DEFAULT); - } - - void FreeTLM(SIZE_T i, BOOL isThreadShuttingDown); - - void FreeTable(); - - void EnsureModuleIndex(ModuleIndex index); - -#endif - - void SetModuleSlot(ModuleIndex index, PTR_ThreadLocalModule pLocalModule); - - PTR_ThreadLocalModule GetTLMIfExists(ModuleIndex index); - PTR_ThreadLocalModule GetTLMIfExists(MethodTable* pMT); - -#ifdef DACCESS_COMPILE - void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); -#endif -}; - // TailCallArgBuffer states #define TAILCALLARGBUFFER_ACTIVE 0 #define TAILCALLARGBUFFER_INSTARG_ONLY 1 @@ -3842,17 +3774,14 @@ class Thread } #endif //DACCESS_COMPILE - ThreadLocalBlock m_ThreadLocalBlock; - - // Called during AssemblyLoadContext teardown to clean up all structures - // associated with thread statics for the specific Module - void DeleteThreadStaticData(ModuleIndex index); + PTR_ThreadLocalData m_pThreadLocalData; + PTR_ThreadLocalData GetThreadLocalDataPtr() { LIMITED_METHOD_DAC_CONTRACT; return m_pThreadLocalData; } private: // Called during Thread death to clean up all structures // associated with thread statics - void DeleteThreadStaticData(); + void DeleteThreadStaticData(bool forCurrentThread); private: TailCallTls m_tailCallTls; diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index d09d90c83c12e..ec629fec6e379 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -1,361 +1,170 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// ThreadStatics.cpp -// - -// -// - - #include "common.h" - #include "threadstatics.h" -#include "field.h" - +struct TLSArray +{ + int32_t cTLSData; // Size in bytes of offset into the TLS array which is valid + TADDR pTLSArrayData; // Points at the Thread local array data. +}; +typedef DPTR(TLSArray) PTR_TLSArray; + +// Used to store access to TLS data for a single index when the TLS is accessed while the class constructor is running +struct InFlightTLSData; +typedef DPTR(InFlightTLSData) PTR_InFlightTLSData; +struct InFlightTLSData +{ #ifndef DACCESS_COMPILE + InFlightTLSData(TLSIndex index, TADDR pTLSData) : pNext(NULL), tlsIndex(index), pTLSData(pTLSData) { } +#endif // !DACCESS_COMPILE + PTR_InFlightTLSData pNext; // Points at the next in-flight TLS data + TLSIndex tlsIndex; // The TLS index for the static + TADDR pTLSData; // The TLS data for the static +}; -void ThreadLocalBlock::FreeTLM(SIZE_T i, BOOL isThreadShuttingdown) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - PTR_ThreadLocalModule pThreadLocalModule; +struct ThreadLocalLoaderAllocator +{ + ThreadLocalLoaderAllocator* pNext; // Points at the next thread local loader allocator + LoaderAllocator* pLoaderAllocator; // The loader allocator that has a TLS used in this thread +}; +typedef DPTR(ThreadLocalLoaderAllocator) PTR_ThreadLocalLoaderAllocator; - { - SpinLock::Holder lock(&m_TLMTableLock); +struct ThreadLocalData +{ + TLSArray tlsArray; // TLS data + Thread *pThread; + PTR_InFlightTLSData pInFlightData; // Points at the in-flight TLS data (TLS data that exists before the class constructor finishes running) +}; - if ((m_pTLMTable == NULL) || (i >= m_TLMTableSize)) - { - return; - } - pThreadLocalModule = m_pTLMTable[i].pTLM; - m_pTLMTable[i].pTLM = NULL; - } +// This can be used for out of thread access to TLS data. Since that isn't safe in general, we only support it for DAC. +PTR_VOID GetThreadLocalStaticBaseNoCreate(PTR_ThreadLocalData pThreadLocalData, TLSIndex index) +{ + LIMITED_METHOD_CONTRACT; + TADDR pTLSBaseAddress = NULL; + PTR_TLSArray pTLSArray = dac_cast(dac_cast(pThreadLocalData) + index.GetTLSArrayOffset()); - if (pThreadLocalModule != NULL) + int32_t cTLSData = pTLSArray->cTLSData; + if (cTLSData < index.GetByteIndex()) { - if (pThreadLocalModule->m_pDynamicClassTable != NULL) - { - for (DWORD k = 0; k < pThreadLocalModule->m_aDynamicEntries; ++k) - { - if (pThreadLocalModule->m_pDynamicClassTable[k].m_pDynamicEntry != NULL) - { - if (isThreadShuttingdown && (pThreadLocalModule->m_pDynamicClassTable[k].m_dwFlags & ClassInitFlags::COLLECTIBLE_FLAG)) - { - ThreadLocalModule::CollectibleDynamicEntry *entry = (ThreadLocalModule::CollectibleDynamicEntry*)pThreadLocalModule->m_pDynamicClassTable[k].m_pDynamicEntry; - PTR_LoaderAllocator pLoaderAllocator = entry->m_pLoaderAllocator; - - if (entry->m_hGCStatics != NULL) - { - pLoaderAllocator->FreeHandle(entry->m_hGCStatics); - } - if (entry->m_hNonGCStatics != NULL) - { - pLoaderAllocator->FreeHandle(entry->m_hNonGCStatics); - } - } - delete pThreadLocalModule->m_pDynamicClassTable[k].m_pDynamicEntry; - pThreadLocalModule->m_pDynamicClassTable[k].m_pDynamicEntry = NULL; - } - } - delete[] pThreadLocalModule->m_pDynamicClassTable; - pThreadLocalModule->m_pDynamicClassTable = NULL; - } - - delete pThreadLocalModule; + return NULL; } -} -void ThreadLocalBlock::FreeTable() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - } - CONTRACTL_END; - // Free the TLM table - if (m_pTLMTable != NULL) + TADDR pTLSArrayData = pTLSArray->pTLSArrayData; + pTLSBaseAddress = *dac_cast(dac_cast(pTLSArrayData) + index.GetByteIndex()); + if (pTLSBaseAddress == NULL) { - // Iterate over the table and free each TLM - for (SIZE_T i = 0; i < m_TLMTableSize; ++i) + // Maybe it is in the InFlightData + PTR_InFlightTLSData pInFlightData = pThreadLocalData->pInFlightData; + while (pInFlightData != NULL) { - if (m_pTLMTable[i].pTLM != NULL) + if (pInFlightData->tlsIndex == index) { - FreeTLM(i, TRUE /* isThreadShuttingDown */); + pTLSBaseAddress = pInFlightData->pTLSData; + break; } + pInFlightData = pInFlightData->pNext; } - - SpinLock::Holder lock(&m_TLMTableLock); - - // Free the table itself - delete[] m_pTLMTable; - m_pTLMTable = NULL; - } - - // Set table size to zero - m_TLMTableSize = 0; - - // Free the ThreadStaticHandleTable - if (m_pThreadStaticHandleTable != NULL) - { - delete m_pThreadStaticHandleTable; - m_pThreadStaticHandleTable = NULL; } + return dac_cast(pTLSBaseAddress); } -void ThreadLocalBlock::EnsureModuleIndex(ModuleIndex index) +GPTR_IMPL(TLSIndexToMethodTableMap, g_pThreadStaticTypeIndices); + +PTR_MethodTable LookupMethodTableForThreadStatic(TLSIndex index) { - CONTRACTL { - THROWS; - GC_NOTRIGGER; - } CONTRACTL_END; - - if (m_TLMTableSize > index.m_dwIndex) - { - _ASSERTE(m_pTLMTable != NULL); - return; - } - - SIZE_T aModuleIndices = max(16, m_TLMTableSize); - while (aModuleIndices <= index.m_dwIndex) - { - aModuleIndices *= 2; - } - - // If this allocation fails, we will throw. If it succeeds, - // then we are good to go - PTR_TLMTableEntry pNewModuleSlots = new TLMTableEntry[aModuleIndices]; - - // Zero out the new TLM table - memset(pNewModuleSlots, 0 , sizeof(TLMTableEntry) * aModuleIndices); - - PTR_TLMTableEntry pOldModuleSlots = m_pTLMTable; - - { - SpinLock::Holder lock(&m_TLMTableLock); - - if (m_pTLMTable != NULL) - { - memcpy(pNewModuleSlots, m_pTLMTable, sizeof(TLMTableEntry) * m_TLMTableSize); - } - else - { - _ASSERTE(m_TLMTableSize == 0); - } - - m_pTLMTable = pNewModuleSlots; - m_TLMTableSize = aModuleIndices; - } - - if (pOldModuleSlots != NULL) - delete[] pOldModuleSlots; + // TODO, if and when we get array indices, we should be pickier. + TADDR flagsUnused; + return g_pThreadStaticTypeIndices->GetElement(index.TLSIndexRawIndex, &flagsUnused); } -#endif +TADDR isGCFlag = 0x1; -void ThreadLocalBlock::SetModuleSlot(ModuleIndex index, PTR_ThreadLocalModule pLocalModule) +PTR_MethodTable LookupMethodTableAndFlagForThreadStatic(TLSIndex index, bool *pIsGCStatic) { - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - } CONTRACTL_END; - - // This method will not grow the table. You need to grow - // the table explicitly before calling SetModuleSlot() - - _ASSERTE(index.m_dwIndex < m_TLMTableSize); - - m_pTLMTable[index.m_dwIndex].pTLM = pLocalModule; + // TODO, if and when we get array indices, we should be pickier. + TADDR flags; + PTR_MethodTable retVal = g_pThreadStaticTypeIndices->GetElement(index.TLSIndexRawIndex, &flags); + *pIsGCStatic = flags == isGCFlag; + return retVal; } -#ifdef DACCESS_COMPILE - -void -ThreadLocalModule::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) +void ScanThreadStaticRoots(PTR_ThreadLocalData pThreadLocalData, promote_func* fn, ScanContext* sc) { - SUPPORTS_DAC; - - // Enumerate the ThreadLocalModule itself. TLMs are allocated to be larger than - // sizeof(ThreadLocalModule) to make room for ClassInit flags and non-GC statics. - // "DAC_ENUM_DTHIS()" probably does not account for this, so we might not enumerate - // all of the ClassInit flags and non-GC statics. - DAC_ENUM_DTHIS(); - - if (m_pDynamicClassTable != NULL) + PTR_InFlightTLSData pInFlightData = pThreadLocalData->pInFlightData; + while (pInFlightData != NULL) { - DacEnumMemoryRegion(dac_cast(m_pDynamicClassTable), - m_aDynamicEntries * sizeof(DynamicClassInfo)); - - for (SIZE_T i = 0; i < m_aDynamicEntries; i++) - { - PTR_DynamicEntry entry = dac_cast(m_pDynamicClassTable[i].m_pDynamicEntry); - if (entry.IsValid()) - { - entry.EnumMem(); - } - } + fn(dac_cast(pInFlightData->pTLSData), sc, 0 /* could be GC_CALL_INTERIOR or GC_CALL_PINNED */); + pInFlightData = pInFlightData->pNext; } -} - -void -ThreadLocalBlock::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) -{ - SUPPORTS_DAC; - - // Enumerate the ThreadLocalBlock itself - DAC_ENUM_DTHIS(); - - if (m_pTLMTable.IsValid()) + PTR_BYTE pTLSArrayData = dac_cast(pThreadLocalData->tlsArray.pTLSArrayData); + int32_t cTLSData = pThreadLocalData->tlsArray.cTLSData; + for (int32_t i = 0; i < cTLSData; i += sizeof(TADDR)) { - DacEnumMemoryRegion(dac_cast(m_pTLMTable), - m_TLMTableSize * sizeof(TADDR)); - - for (SIZE_T i = 0; i < m_TLMTableSize; i++) + TLSIndex index(i); + bool isGCStatic; + MethodTable *pMT = LookupMethodTableAndFlagForThreadStatic(index, &isGCStatic); + if (pMT == NULL) { - PTR_ThreadLocalModule domMod = m_pTLMTable[i].pTLM; - if (domMod.IsValid()) - { - domMod->EnumMemoryRegions(flags); - } + continue; + } + TADDR *pTLSBaseAddress = dac_cast(pTLSArrayData + i); + if (pTLSBaseAddress != NULL) + { + fn(dac_cast(pTLSBaseAddress), sc, 0 /* could be GC_CALL_INTERIOR or GC_CALL_PINNED */); } - } -} - -#endif - -DWORD ThreadLocalModule::GetClassFlags(MethodTable* pMT, DWORD iClassIndex) // iClassIndex defaults to (DWORD)-1 -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - } CONTRACTL_END; - - if (pMT->IsDynamicStatics()) - { - DWORD dynamicClassID = pMT->GetModuleDynamicEntryID(); - if(m_aDynamicEntries <= dynamicClassID) - return FALSE; - return (m_pDynamicClassTable[dynamicClassID].m_dwFlags); - } - else - { - if (iClassIndex == (DWORD)-1) - iClassIndex = pMT->GetClassIndex(); - return GetPrecomputedStaticsClassData()[iClassIndex]; } } #ifndef DACCESS_COMPILE +#ifdef _MSC_VER +__declspec(thread) ThreadLocalData t_ThreadStatics; +#else +__thread ThreadLocalData t_ThreadStatics; +#endif // _MSC_VER -void ThreadLocalModule::SetClassFlags(MethodTable* pMT, DWORD dwFlags) +void* GetThreadLocalStaticBaseIfExistsAndInitialized(TLSIndex index) { - CONTRACTL { - THROWS; - GC_NOTRIGGER; - } CONTRACTL_END; + LIMITED_METHOD_CONTRACT; + TADDR pTLSBaseAddress = NULL; + TLSArray* pTLSArray = reinterpret_cast((uint8_t*)&t_ThreadStatics + index.GetTLSArrayOffset()); - if (pMT->IsDynamicStatics()) - { - DWORD dwID = pMT->GetModuleDynamicEntryID(); - EnsureDynamicClassIndex(dwID); - m_pDynamicClassTable[dwID].m_dwFlags |= dwFlags; - } - else + int32_t cTLSData = pTLSArray->cTLSData; + if (cTLSData < index.GetByteIndex()) { - GetPrecomputedStaticsClassData()[pMT->GetClassIndex()] |= dwFlags; + return NULL; } -} - -void ThreadLocalBlock::AllocateThreadStaticHandles(Module * pModule, PTR_ThreadLocalModule pThreadLocalModule) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - } - CONTRACTL_END; - - _ASSERTE(pThreadLocalModule->GetPrecomputedGCStaticsBaseHandleAddress() != NULL); - _ASSERTE(pThreadLocalModule->GetPrecomputedGCStaticsBaseHandle() == NULL); - - if (pModule->GetNumGCThreadStaticHandles() > 0) - { - AllocateStaticFieldObjRefPtrs(pModule->GetNumGCThreadStaticHandles(), - pThreadLocalModule->GetPrecomputedGCStaticsBaseHandleAddress()); - // We should throw if we fail to allocate and never hit this assert - _ASSERTE(pThreadLocalModule->GetPrecomputedGCStaticsBaseHandle() != NULL); - _ASSERTE(pThreadLocalModule->GetPrecomputedGCStaticsBasePointer() != NULL); - } + TADDR pTLSArrayData = pTLSArray->pTLSArrayData; + pTLSBaseAddress = *reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetByteIndex()); + return reinterpret_cast(pTLSBaseAddress); } -OBJECTHANDLE ThreadLocalBlock::AllocateStaticFieldObjRefPtrs(int nRequested, OBJECTHANDLE * ppLazyAllocate) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION((nRequested > 0)); - INJECT_FAULT(COMPlusThrowOM();); - } - CONTRACTL_END; - - if (ppLazyAllocate && *ppLazyAllocate) - { - // Allocation already happened - return *ppLazyAllocate; - } - - // Make sure the large heap handle table is initialized. - if (!m_pThreadStaticHandleTable) - InitThreadStaticHandleTable(); - - // Allocate the handles. - OBJECTHANDLE result = m_pThreadStaticHandleTable->AllocateHandles(nRequested); +uint32_t g_NextTLSSlot = (uint32_t)sizeof(TADDR); +CrstStatic g_TLSCrst; - if (ppLazyAllocate) - { - *ppLazyAllocate = result; - } - - return result; +void InitializeThreadStaticData() +{ + g_pThreadStaticTypeIndices = new TLSIndexToMethodTableMap(); + g_pThreadStaticTypeIndices->supportedFlags = isGCFlag; + g_TLSCrst.Init(CrstThreadLocalStorageLock, CRST_UNSAFE_ANYMODE); } -void ThreadLocalBlock::InitThreadStaticHandleTable() +void InitializeCurrentThreadsStaticData(Thread* pThread) { - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(m_pThreadStaticHandleTable==NULL); - INJECT_FAULT(COMPlusThrowOM();); - } - CONTRACTL_END; - - // If the allocation fails this will throw; callers need - // to account for this possibility - m_pThreadStaticHandleTable = new ThreadStaticHandleTable(GetAppDomain()); + pThread->m_pThreadLocalData = &t_ThreadStatics; + t_ThreadStatics.pThread = pThread; } -void ThreadLocalBlock::AllocateThreadStaticBoxes(MethodTable * pMT) +void AllocateThreadStaticBoxes(MethodTable *pMT, PTRARRAYREF *ppRef) { CONTRACTL { THROWS; GC_TRIGGERS; MODE_COOPERATIVE; - PRECONDITION(pMT->GetNumBoxedThreadStatics() > 0); + PRECONDITION(pMT->GetClass()->GetNumBoxedThreadStatics() > 0); // TODO, should be HasBoxedThreadStatics() INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; @@ -379,289 +188,281 @@ void ThreadLocalBlock::AllocateThreadStaticBoxes(MethodTable * pMT) MethodTable* pFieldMT = th.GetMethodTable(); OBJECTREF obj = MethodTable::AllocateStaticBox(pFieldMT, pMT->HasFixedAddressVTStatics()); - - PTR_BYTE pStaticBase = pMT->GetGCThreadStaticsBasePointer(); - _ASSERTE(pStaticBase != NULL); - - SetObjectReference( (OBJECTREF*)(pStaticBase + pField->GetOffset()), obj ); + (*ppRef)->SetAt(pField->GetOffset(), obj); } pField++; } } -#endif - -#ifndef DACCESS_COMPILE - -void ThreadLocalModule::EnsureDynamicClassIndex(DWORD dwID) +void FreeCurrentThreadStaticData() { - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM();); - } - CONTRACTL_END; - - if (dwID < m_aDynamicEntries) - { - _ASSERTE(m_pDynamicClassTable != NULL); - return; - } - - SIZE_T aDynamicEntries = max(16, m_aDynamicEntries); - while (aDynamicEntries <= dwID) - { - aDynamicEntries *= 2; - } - - DynamicClassInfo* pNewDynamicClassTable; - - // If this allocation fails, we throw. If it succeeds, - // then we are good to go - pNewDynamicClassTable = new DynamicClassInfo[aDynamicEntries]; + delete[] (uint8_t*)t_ThreadStatics.tlsArray.pTLSArrayData; - // Zero out the dynamic class table - memset(pNewDynamicClassTable, 0, sizeof(DynamicClassInfo) * aDynamicEntries); + t_ThreadStatics.tlsArray.pTLSArrayData = 0; - // We might always be guaranteed that this will be non-NULL, but just to be safe - if (m_pDynamicClassTable != NULL) + while (t_ThreadStatics.pInFlightData != NULL) { - memcpy(pNewDynamicClassTable, m_pDynamicClassTable, sizeof(DynamicClassInfo) * m_aDynamicEntries); + InFlightTLSData* pInFlightData = t_ThreadStatics.pInFlightData; + t_ThreadStatics.pInFlightData = pInFlightData->pNext; + delete pInFlightData; } - else - { - _ASSERTE(m_aDynamicEntries == 0); - } - - _ASSERTE(m_aDynamicEntries%2 == 0); - - DynamicClassInfo* pOldDynamicClassTable = m_pDynamicClassTable; - m_pDynamicClassTable = pNewDynamicClassTable; - m_aDynamicEntries = aDynamicEntries; - - if (pOldDynamicClassTable != NULL) - delete[] pOldDynamicClassTable; + t_ThreadStatics.pThread = NULL; } -void ThreadLocalModule::AllocateDynamicClass(MethodTable *pMT) +void* GetThreadLocalStaticBase(TLSIndex index) { CONTRACTL { THROWS; GC_TRIGGERS; MODE_COOPERATIVE; - INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; - _ASSERTE(!pMT->IsSharedByGenericInstantiations()); - _ASSERTE(pMT->IsDynamicStatics()); - - DWORD dwID = pMT->GetModuleDynamicEntryID(); - - EnsureDynamicClassIndex(dwID); + TLSArray* pTLSArray = reinterpret_cast((uint8_t*)&t_ThreadStatics + index.GetTLSArrayOffset()); - _ASSERTE(m_aDynamicEntries > dwID); - - EEClass *pClass = pMT->GetClass(); - DWORD dwStaticBytes = pClass->GetNonGCThreadStaticFieldBytes(); - DWORD dwNumHandleStatics = pClass->GetNumHandleThreadStatics(); - - _ASSERTE(!IsClassAllocated(pMT)); - _ASSERTE(!IsClassInitialized(pMT)); - _ASSERTE(!IsClassInitError(pMT)); + int32_t cTLSData = pTLSArray->cTLSData; + if (cTLSData < index.GetByteIndex()) + { + // Grow the underlying TLS array + CrstHolder ch(&g_TLSCrst); + int32_t newcTLSData = index.GetByteIndex() + sizeof(TADDR) * 8; // Leave a bit of margin + uint8_t* pNewTLSArrayData = new uint8_t[newcTLSData]; + memset(pNewTLSArrayData, 0, newcTLSData); + if (cTLSData > 0) + memcpy(pNewTLSArrayData, (void*)pTLSArray->pTLSArrayData, cTLSData + 1); + uint8_t* pOldArray = (uint8_t*)pTLSArray->pTLSArrayData; + pTLSArray->pTLSArrayData = (TADDR)pNewTLSArrayData; + cTLSData = newcTLSData - 1; + pTLSArray->cTLSData = cTLSData; + delete[] pOldArray; + } - DynamicEntry *pDynamicStatics = m_pDynamicClassTable[dwID].m_pDynamicEntry; + TADDR pTLSArrayData = pTLSArray->pTLSArrayData; + TADDR *ppTLSBaseAddress = reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetByteIndex()); + TADDR pTLSBaseAddress = *ppTLSBaseAddress; - // We need this check because maybe a class had a cctor but no statics - if (dwStaticBytes > 0 || dwNumHandleStatics > 0) + if (pTLSBaseAddress == NULL) { - if (pDynamicStatics == NULL) + // Maybe it is in the InFlightData + InFlightTLSData* pInFlightData = t_ThreadStatics.pInFlightData; + InFlightTLSData** ppOldNextPtr = &t_ThreadStatics.pInFlightData; + while (pInFlightData != NULL) { - // If these allocations fail, we will throw - if (pMT->Collectible()) + if (pInFlightData->tlsIndex == index) { - pDynamicStatics = new CollectibleDynamicEntry(pMT->GetLoaderAllocator()); - } - else - { - pDynamicStatics = new({dwStaticBytes}) NormalDynamicEntry(); + pTLSBaseAddress = pInFlightData->pTLSData; + MethodTable *pMT = LookupMethodTableForThreadStatic(index); + if (pMT->IsClassInited()) + { + *ppTLSBaseAddress = pTLSBaseAddress; + *ppOldNextPtr = pInFlightData->pNext; + delete pInFlightData; + } + break; } - - -#ifdef FEATURE_64BIT_ALIGNMENT - // The memory block has be aligned at MAX_PRIMITIVE_FIELD_SIZE to guarantee alignment of statics - static_assert_no_msg(sizeof(NormalDynamicEntry) % MAX_PRIMITIVE_FIELD_SIZE == 0); - _ASSERTE(IS_ALIGNED(pDynamicStatics, MAX_PRIMITIVE_FIELD_SIZE)); -#endif - - // Save the DynamicEntry in the DynamicClassTable - m_pDynamicClassTable[dwID].m_pDynamicEntry = pDynamicStatics; + ppOldNextPtr = &pInFlightData->pNext; + pInFlightData = pInFlightData->pNext; } - - if (pMT->Collectible() && (dwStaticBytes != 0)) + if (pTLSBaseAddress == NULL) { - OBJECTREF nongcStaticsArray = NULL; - GCPROTECT_BEGIN(nongcStaticsArray); -#ifdef FEATURE_64BIT_ALIGNMENT - // Allocate memory with extra alignment only if it is really necessary - if (dwStaticBytes >= MAX_PRIMITIVE_FIELD_SIZE) - nongcStaticsArray = AllocatePrimitiveArray(ELEMENT_TYPE_I8, (dwStaticBytes + (sizeof(CLR_I8) - 1)) / (sizeof(CLR_I8))); - else -#endif - nongcStaticsArray = AllocatePrimitiveArray(ELEMENT_TYPE_U1, dwStaticBytes); - - ((CollectibleDynamicEntry *)pDynamicStatics)->m_hNonGCStatics = pMT->GetLoaderAllocator()->AllocateHandle(nongcStaticsArray); - GCPROTECT_END(); - } - - if (dwNumHandleStatics > 0) - { - if (!pMT->Collectible()) + // Now we need to actually allocate the TLS data block + bool isGCStatic; + MethodTable *pMT = LookupMethodTableAndFlagForThreadStatic(index, &isGCStatic); + struct + { + PTRARRAYREF ptrRef; + OBJECTREF tlsEntry; + } gc; + memset(&gc, 0, sizeof(gc)); + GCPROTECT_BEGIN(gc); + if (isGCStatic) { - GetThread()->m_ThreadLocalBlock.AllocateStaticFieldObjRefPtrs(dwNumHandleStatics, - &((NormalDynamicEntry *)pDynamicStatics)->m_pGCStatics); + gc.ptrRef = AllocateObjectArray(pMT->GetClass()->GetNumHandleThreadStatics(), g_pObjectClass); + if (pMT->GetClass()->GetNumBoxedThreadStatics() > 0) + { + AllocateThreadStaticBoxes(pMT, &gc.ptrRef); + } + gc.tlsEntry = (OBJECTREF)gc.ptrRef; } else { - OBJECTREF gcStaticsArray = NULL; - GCPROTECT_BEGIN(gcStaticsArray); - gcStaticsArray = AllocateObjectArray(dwNumHandleStatics, g_pObjectClass); - ((CollectibleDynamicEntry *)pDynamicStatics)->m_hGCStatics = pMT->GetLoaderAllocator()->AllocateHandle(gcStaticsArray); - GCPROTECT_END(); + gc.tlsEntry = AllocatePrimitiveArray(ELEMENT_TYPE_I1, static_cast(pMT->GetClass()->GetNonGCThreadStaticFieldBytes())); } + + { + GCX_FORBID(); + pTLSBaseAddress = (TADDR)OBJECTREFToObject(gc.tlsEntry); + if (pMT->IsClassInited()) + { + *ppTLSBaseAddress = pTLSBaseAddress; + } + else + { + InFlightTLSData* pInFlightData = new InFlightTLSData(index, pTLSBaseAddress); + pInFlightData->pNext = t_ThreadStatics.pInFlightData; + t_ThreadStatics.pInFlightData = pInFlightData; + } + } + GCPROTECT_END(); } } + _ASSERTE(pTLSBaseAddress != NULL); + return reinterpret_cast(pTLSBaseAddress); } -void ThreadLocalModule::PopulateClass(MethodTable *pMT) +void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pIndex) { - CONTRACTL + WRAPPER_NO_CONTRACT; + CrstHolder ch(&g_TLSCrst); + if (pIndex->IsAllocated()) { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - INJECT_FAULT(COMPlusThrowOM();); + return; } - CONTRACTL_END; - _ASSERTE(this != NULL); - _ASSERTE(pMT != NULL); - _ASSERTE(!IsClassAllocated(pMT)); + uint32_t tlsRawIndex = g_NextTLSSlot; + g_NextTLSSlot += (uint32_t)sizeof(TADDR); + g_pThreadStaticTypeIndices->AddElement(g_pObjectClass->GetModule(), tlsRawIndex, pMT, (gcStatic ? isGCFlag : 0)); - // If this is a dynamic class then we need to allocate - // an entry in our dynamic class table - if (pMT->IsDynamicStatics()) - AllocateDynamicClass(pMT); - - if (pMT->Collectible()) - { - SetClassFlags(pMT, ClassInitFlags::COLLECTIBLE_FLAG); - } - - // We need to allocate boxes any value-type statics that are not - // primitives or enums, because these statics may contain references - // to objects on the GC heap - if (pMT->GetNumBoxedThreadStatics() > 0) - { - PTR_ThreadLocalBlock pThreadLocalBlock = ThreadStatics::GetCurrentTLB(); - _ASSERTE(pThreadLocalBlock != NULL); - pThreadLocalBlock->AllocateThreadStaticBoxes(pMT); - } + // TODO Handle collectible cases + *pIndex = TLSIndex(tlsRawIndex); +} - // Mark the class as allocated - SetClassAllocated(pMT); +#if defined(TARGET_WINDOWS) +EXTERN_C uint32_t _tls_index; +/*********************************************************************/ +static uint32_t ThreadLocalOffset(void* p) +{ + PTEB Teb = NtCurrentTeb(); + uint8_t** pTls = (uint8_t**)Teb->ThreadLocalStoragePointer; + uint8_t* pOurTls = pTls[_tls_index]; + return (uint32_t)((uint8_t*)p - pOurTls); } +#elif defined(TARGET_OSX) +extern "C" void* GetThreadVarsAddress(); -PTR_ThreadLocalModule ThreadStatics::AllocateAndInitTLM(ModuleIndex index, PTR_ThreadLocalBlock pThreadLocalBlock, Module * pModule) //static +static void* GetThreadVarsSectionAddressFromDesc(uint8_t* p) { - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - INJECT_FAULT(COMPlusThrowOM();); - } - CONTRACTL_END; + _ASSERT(p[0] == 0x48 && p[1] == 0x8d && p[2] == 0x3d); - pThreadLocalBlock->EnsureModuleIndex(index); + // At this point, `p` contains the instruction pointer and is pointing to the above opcodes. + // These opcodes are patched by the dynamic linker. + // Move beyond the opcodes that we have already checked above. + p += 3; - _ASSERTE(pThreadLocalBlock != NULL); - _ASSERTE(pModule != NULL); + // The descriptor address is located at *p at this point. + // (p + 4) below skips the descriptor address bytes embedded in the instruction and + // add it to the `instruction pointer` to find out the address. + return *(uint32_t*)p + (p + 4); +} - NewHolder pThreadLocalModule = AllocateTLM(pModule); +static void* GetThreadVarsSectionAddress() +{ +#ifdef TARGET_AMD64 + // On x64, the address is related to rip, so, disassemble the function, + // read the offset, and then relative to the IP, find the final address of + // __thread_vars section. + uint8_t* p = reinterpret_cast(&GetThreadVarsAddress); + return GetThreadVarsSectionAddressFromDesc(p); +#else + return GetThreadVarsAddress(); +#endif // TARGET_AMD64 +} - pThreadLocalBlock->AllocateThreadStaticHandles(pModule, pThreadLocalModule); +#else - pThreadLocalBlock->SetModuleSlot(index, pThreadLocalModule); - pThreadLocalModule.SuppressRelease(); +// Linux - return pThreadLocalModule; -} +#ifdef TARGET_AMD64 +extern "C" void* GetTlsIndexObjectDescOffset(); -PTR_ThreadLocalModule ThreadStatics::GetTLM(ModuleIndex index, Module * pModule) //static +static void* GetThreadStaticDescriptor(uint8_t* p) { - CONTRACTL + if (!(p[0] == 0x66 && p[1] == 0x48 && p[2] == 0x8d && p[3] == 0x3d)) { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; + // The optimization is disabled if coreclr is not compiled in .so format. + _ASSERTE(false && "Unexpected code sequence"); + return nullptr; } - CONTRACTL_END; - // Get the TLM if it already exists - PTR_ThreadLocalModule pThreadLocalModule = ThreadStatics::GetTLMIfExists(index); + // At this point, `p` contains the instruction pointer and is pointing to the above opcodes. + // These opcodes are patched by the dynamic linker. + // Move beyond the opcodes that we have already checked above. + p += 4; - // If the TLM does not exist, create it now - if (pThreadLocalModule == NULL) - { - // Get the current ThreadLocalBlock - PTR_ThreadLocalBlock pThreadLocalBlock = ThreadStatics::GetCurrentTLB(); - _ASSERTE(pThreadLocalBlock != NULL); - - // Allocate and initialize the TLM, and add it to the TLB's table - pThreadLocalModule = AllocateAndInitTLM(index, pThreadLocalBlock, pModule); - } - - return pThreadLocalModule; + // The descriptor address is located at *p at this point. Read that and add + // it to the instruction pointer to locate the address of `ti` that will be used + // to pass to __tls_get_addr during execution. + // (p + 4) below skips the descriptor address bytes embedded in the instruction and + // add it to the `instruction pointer` to find out the address. + return *(uint32_t*)p + (p + 4); } -PTR_ThreadLocalModule ThreadStatics::GetTLM(MethodTable * pMT) //static +static void* GetTlsIndexObjectAddress() { - Module * pModule = pMT->GetModuleForStatics(); - return GetTLM(pModule->GetModuleIndex(), pModule); + uint8_t* p = reinterpret_cast(&GetTlsIndexObjectDescOffset); + return GetThreadStaticDescriptor(p); } -PTR_ThreadLocalModule ThreadStatics::AllocateTLM(Module * pModule) +#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) + +extern "C" size_t GetThreadStaticsVariableOffset(); + +#endif // TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64 +#endif // TARGET_WINDOWS + +void GetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) { - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM();); - } - CONTRACTL_END; + STANDARD_VM_CONTRACT; + size_t threadStaticBaseOffset = 0; + +#if defined(TARGET_WINDOWS) + pInfo->tlsIndex.addr = (void*)static_cast(_tls_index); + pInfo->tlsIndex.accessType = IAT_VALUE; + + pInfo->offsetOfThreadLocalStoragePointer = offsetof(_TEB, ThreadLocalStoragePointer); + threadStaticBaseOffset = ThreadLocalOffset(&t_ThreadStatics); +#elif defined(TARGET_OSX) - SIZE_T size = pModule->GetThreadLocalModuleSize(); + pInfo->threadVarsSection = GetThreadVarsSectionAddress(); - PTR_ThreadLocalModule pThreadLocalModule = new({ pModule }) ThreadLocalModule; +#elif defined(TARGET_AMD64) - // We guarantee alignment for 64-bit regular thread statics on 32-bit platforms even without FEATURE_64BIT_ALIGNMENT for performance reasons. + // For Linux/x64, get the address of tls_get_addr system method and the base address + // of struct that we will pass to it. + pInfo->tlsGetAddrFtnPtr = reinterpret_cast(&__tls_get_addr); + pInfo->tlsIndexObject = GetTlsIndexObjectAddress(); - // The memory block has to be aligned at MAX_PRIMITIVE_FIELD_SIZE to guarantee alignment of statics - _ASSERTE(IS_ALIGNED(pThreadLocalModule, MAX_PRIMITIVE_FIELD_SIZE)); +#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - // Zero out the part of memory where the TLM resides - memset(pThreadLocalModule, 0, size); + // For Linux arm64/loongarch64/riscv64, just get the offset of thread static variable, and during execution, + // this offset, arm64 taken from trpid_elp0 system register gives back the thread variable address. + // this offset, loongarch64 taken from $tp register gives back the thread variable address. + threadStaticBaseOffset = GetThreadStaticsVariableOffset(); - return pThreadLocalModule; +#else + _ASSERTE_MSG(false, "Unsupported scenario of optimizing TLS access on Linux Arm32/x86"); +#endif // TARGET_WINDOWS + + pInfo->offsetOfThreadStaticBlocks = (uint32_t)threadStaticBaseOffset; } +#endif // !DACCESS_COMPILE -#endif +#ifdef DACCESS_COMPILE +void EnumThreadMemoryRegions(PTR_ThreadLocalData pThreadLocalData, CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + DacEnumMemoryRegion(dac_cast(pThreadLocalData), sizeof(ThreadLocalData), flags); + DacEnumMemoryRegion(dac_cast(pThreadLocalData->tlsArray.pTLSArrayData), pThreadLocalData->tlsArray.cTLSData, flags); + PTR_InFlightTLSData pInFlightData = pThreadLocalData->pInFlightData; + while (pInFlightData != NULL) + { + DacEnumMemoryRegion(dac_cast(pInFlightData), sizeof(InFlightTLSData), flags); + pInFlightData = pInFlightData->pNext; + } +} +#endif // DACCESS_COMPILE diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index f002f0059ce6f..0d462a3372907 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -1,607 +1,129 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// ThreadStatics.h -// +// Thread local storage is designed to be as efficient as possible. +// This leads to several different access patterns. // +// There shall be a global TLS data structure used for all threads. This is initialized before any managed code is permitted to run on a thread +// struct TLSArray +// { +// int32_t cTLSData; // Size in bytes of offset into the TLS array which is valid +// void* pTLSArrayData; // Points at the Thread local array data. +// }; // -// Classes can contain instance fields and statics fields. In addition to regular statics, .NET offers -// several types of special statics. In IL, thread static fields are marked with the ThreadStaticAttribute, -// distinguishing them from regular statics and other types of special statics. A thread static field is -// not shared between threads. Each executing thread has a separate instance of the field, and independently -// sets and gets values for that field. +// Used to store access to TLS data for a single index when the TLS is accessed while the class constructor is running +// struct InFlightTLSData +// { +// InFlightTLSData* pNext; // Points at the next in-flight TLS data +// TLSIndex tlsIndex; // The TLS index for the static +// void* pTLSData; // The TLS data for the static +// }; // -// This implementation of thread statics closely parallels the implementation for regular statics. Regular -// statics use the DomainLocalModule structure to allocate space for statics. +// struct ThreadLocalLoaderAllocator +// { +// ThreadLocalLoaderAllocator* pNext; // Points at the next thread local loader allocator +// LoaderAllocator* pLoaderAllocator; // The loader allocator that has a TLS used in this thread +// bool ReportToGC(PromoteFunction* fn, ScanContext* sc, int flags); // Reports the thread local loader allocator state to the GC, returns true if the ThreadLocalLoaderAllocator structure should be removed from the linked list. This is what allows the GC statics for collectible types to actually exist on the nonGC thread local storage array +// }; // - +// struct ThreadLocalData +// { +// TLSArray nongcArray; // Array for nonGC data, as well as collectible GC static. cTLSData is initialized to PRE_ALLOCATED_TLS_NONGC_SLOT_COUNT * sizeof(void*) - 1, and pTLSArrayData points at memory of size PRE_ALLOCATED_TLS_NONGC_SLOT_COUNT * sizeof(void*) at thread startup +// TLSArray gcArray; // Array for non-collectible GC pointers. cTLSData is initialized to PRE_ALLOCATED_TLS_GC_SLOT_COUNT * sizeof(OBJECTREF) + sizeof(void*) * 2 - 1, and pTLSArrayData points at a managed object[], initialized to an object array of size PRE_ALLOCATED_TLS_GC_SLOT_COUNT at thread startup +// InFlightTLSData* pNext; // Points at the next in-flight TLS data +// }; // +// struct TLSIndex +// { +// int32_t TLSIndexRawIndex; +// int32_t GetByteIndex() { return TLSIndexRawIndex & 0xFFFFFF; } +// int8_t GetTLSArrayOffset() { return TLSIndexRawIndex >> 24; } +// }; +// +// thread_local ThreadLocalData t_ThreadStatics; +// SArray* g_pNonGCTLSIndexToMethodTable; +// int g_maxNonGCTlsSize; +// SArray* g_pGCTLSIndexToMethodTable; +// int g_maxGCTlsSlots; +// +// Access pattern for a TLS static +// 0. Get the TLS index somehow +// 1. Get TLS pointer to OS managed TLS block for the current thread ie. pThreadLocalData = &t_ThreadStatics +// 2. Get the TLSArray for the TLS index (pTLSArray = ((uint8_t*)pThreadLocalData) + index.GetTLSArrayOffset()) +// 3. Read 1 integer value (cTLSData=pThreadLocalData->cTLSData) +// 4. Compare cTLSData against the index we're looking up (if (cTLSData < index.GetByteIndex())) +// 5. If the index is not within range, jump to step 10. +// 6. Read 1 pointer value from TLS block (pTLSArrayData=pThreadLocalData->pTLSArrayData) +// 7. Read 1 pointer from within the TLS Array. (pTLSBaseAddress = *(intptr_t*)(((uint8_t*)pTLSArrayData) + index.GetByteIndex()); +// 8. If pointer is NULL jump to step 10 (if pTLSBaseAddress == NULL) +// 9. Return pTLSBaseAddress +// 10. Tail-call a helper (return GetThreadLocalStaticBase(index)) +// +// The Runtime shall define a couple of well known TLS indices. These are used for the most common +// TLS statics, and are used to avoid the overhead of checking for the index being in range, and +// the class constructor for having run, so that we can skip steps 3, 4, 5, and 8. It shall do this +// by allocating the associated memory before permitting any code to run on the thread. +// +// Psuedocode for +// ref byte GetThreadLocalStaticBase(uint index) +// { +// Do the access pattern above, but if the TLS array is too small, allocate a new one, and if the base pointer is NULL, call the class constructor for the static. +// if After all that the base pointer is still NULL, walk the InFlightTLSData chain to see if it exists in there. +// If the InFlightTLSData chain has a value +// check to see if the class constructor has run. If it has completed, update the base pointer in the TLS array, and delete the InFlightTLSData entry. +// return the found value +// ELSE +// allocate a new InFlightTLSData entry, and return the address of the pTLSData field. +// } +// +// Rationale for basic decisions here +// 1. We want access to TLS statics to be as fast as possible, especially for extremely common +// thread statics like the ones used for async, and memory allocation. +// 2. We want access to TLS statics for shared generic types to be nearly fully inlineable. This +// is why the variation between collectible and non-collectible gc statics access is handled by +// a single byte in the index itself. The intent is that access to statics shall be as simple as +// reading the index from a MethodTable, and then using a very straightforward pattern from there. -#ifndef __threadstatics_h__ -#define __threadstatics_h__ - -#include "vars.hpp" -#include "util.hpp" - -#include "appdomain.hpp" -#include "field.h" -#include "methodtable.h" -#include "threads.h" -#include "spinlock.h" - -// Defines ObjectHandeList type -#include "specialstatics.h" - - -typedef DPTR(struct ThreadLocalModule) PTR_ThreadLocalModule; - -struct ThreadLocalModule -{ - friend class ClrDataAccess; - friend class CheckAsmOffsets; - friend struct ThreadLocalBlock; - - // After these macros complete, they may have returned an interior pointer into a gc object. This pointer will have been cast to a byte pointer - // It is critically important that no GC is allowed to occur before this pointer is used. -#define GET_DYNAMICENTRY_GCTHREADSTATICS_BASEPOINTER(pLoaderAllocator, dynamicClassInfoParam, pGCStatics) \ - {\ - ThreadLocalModule::PTR_DynamicClassInfo dynamicClassInfo = dac_cast(dynamicClassInfoParam);\ - ThreadLocalModule::PTR_DynamicEntry pDynamicEntry = dac_cast((ThreadLocalModule::DynamicEntry*)dynamicClassInfo->m_pDynamicEntry); \ - if ((dynamicClassInfo->m_dwFlags) & ClassInitFlags::COLLECTIBLE_FLAG) \ - {\ - PTRARRAYREF objArray;\ - objArray = (PTRARRAYREF)pLoaderAllocator->GetHandleValueFastCannotFailType2( \ - (dac_cast(pDynamicEntry))->m_hGCStatics);\ - *(pGCStatics) = dac_cast(PTR_READ(PTR_TO_TADDR(OBJECTREFToObject( objArray )) + offsetof(PtrArray, m_Array), objArray->GetNumComponents() * sizeof(void*))) ;\ - }\ - else\ - {\ - *(pGCStatics) = (dac_cast(pDynamicEntry))->GetGCStaticsBasePointer();\ - }\ - }\ - -#define GET_DYNAMICENTRY_NONGCTHREADSTATICS_BASEPOINTER(pLoaderAllocator, dynamicClassInfoParam, pNonGCStatics) \ - {\ - ThreadLocalModule::PTR_DynamicClassInfo dynamicClassInfo = dac_cast(dynamicClassInfoParam);\ - ThreadLocalModule::PTR_DynamicEntry pDynamicEntry = dac_cast((ThreadLocalModule::DynamicEntry*)(dynamicClassInfo)->m_pDynamicEntry); \ - if (((dynamicClassInfo)->m_dwFlags) & ClassInitFlags::COLLECTIBLE_FLAG) \ - {\ - if ((dac_cast(pDynamicEntry))->m_hNonGCStatics != 0) \ - { \ - U1ARRAYREF objArray;\ - objArray = (U1ARRAYREF)pLoaderAllocator->GetHandleValueFastCannotFailType2( \ - (dac_cast(pDynamicEntry))->m_hNonGCStatics);\ - *(pNonGCStatics) = dac_cast(PTR_READ( \ - PTR_TO_TADDR(OBJECTREFToObject( objArray )) + sizeof(ArrayBase) - ThreadLocalModule::DynamicEntry::GetOffsetOfDataBlob(), \ - objArray->GetNumComponents() * (DWORD)objArray->GetComponentSize() + ThreadLocalModule::DynamicEntry::GetOffsetOfDataBlob())); \ - } else (*pNonGCStatics) = NULL; \ - }\ - else\ - {\ - *(pNonGCStatics) = dac_cast(pDynamicEntry)->GetNonGCStaticsBasePointer();\ - }\ - }\ - - struct DynamicEntry - { - static DWORD GetOffsetOfDataBlob(); - }; - typedef DPTR(DynamicEntry) PTR_DynamicEntry; - - struct CollectibleDynamicEntry : public DynamicEntry - { - CollectibleDynamicEntry(PTR_LoaderAllocator pLoaderAllocator) - :m_pLoaderAllocator(pLoaderAllocator) - { - LIMITED_METHOD_CONTRACT; - } - - LOADERHANDLE m_hGCStatics = NULL; - LOADERHANDLE m_hNonGCStatics = NULL; - PTR_LoaderAllocator m_pLoaderAllocator = NULL; - }; - typedef DPTR(CollectibleDynamicEntry) PTR_CollectibleDynamicEntry; - - struct NormalDynamicEntry : public DynamicEntry - { - OBJECTHANDLE m_pGCStatics; -#ifdef FEATURE_64BIT_ALIGNMENT - // Padding to make m_pDataBlob aligned at MAX_PRIMITIVE_FIELD_SIZE. - // code:MethodTableBuilder::PlaceThreadStaticFields assumes that the start of the data blob is aligned - SIZE_T m_padding; -#endif - BYTE m_pDataBlob[0]; - - inline PTR_BYTE GetGCStaticsBasePointer() - { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - SUPPORTS_DAC; - } - CONTRACTL_END; - - _ASSERTE(m_pGCStatics != NULL); - - return dac_cast(((PTRARRAYREF)ObjectFromHandle(m_pGCStatics))->GetDataPtr()); - } - inline PTR_BYTE GetGCStaticsBaseHandle() - { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - return dac_cast(m_pGCStatics); - } - inline PTR_BYTE GetNonGCStaticsBasePointer() - { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - return dac_cast(this); - } - - struct DynamicEntryStaticBytes - { - DWORD m_bytes; - }; - - static void* operator new(size_t) = delete; - - static void* operator new(size_t baseSize, DynamicEntryStaticBytes dataBlobSize) - { - void* memory = ::operator new(baseSize + dataBlobSize.m_bytes); - // We want to zero out the data blob memory as the NormalDynamicEntry constructor - // will not zero it as it is outsize of the object. - memset((int8_t*)memory + baseSize, 0, dataBlobSize.m_bytes); - return memory; - } - }; - typedef DPTR(NormalDynamicEntry) PTR_NormalDynamicEntry; - - struct DynamicClassInfo - { - PTR_DynamicEntry m_pDynamicEntry; - DWORD m_dwFlags; - }; - typedef DPTR(DynamicClassInfo) PTR_DynamicClassInfo; - - // Note the difference between: - // - // GetPrecomputedNonGCStaticsBasePointer() and - // GetPrecomputedStaticsClassData() - // - // GetPrecomputedNonGCStaticsBasePointer returns the pointer that should be added to field offsets to retrieve statics - // GetPrecomputedStaticsClassData returns a pointer to the first byte of the precomputed statics block - inline TADDR GetPrecomputedNonGCStaticsBasePointer() - { - LIMITED_METHOD_CONTRACT - return dac_cast(this); - } - - static SIZE_T GetOffsetOfDataBlob() { return offsetof(ThreadLocalModule, m_pDataBlob); } - static SIZE_T GetOffsetOfGCStaticHandle() { return offsetof(ThreadLocalModule, m_pGCStatics); } - - inline PTR_OBJECTREF GetPrecomputedGCStaticsBasePointer() - { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - SUPPORTS_DAC; - } - CONTRACTL_END; - - _ASSERTE(m_pGCStatics != NULL); - - return ((PTRARRAYREF)ObjectFromHandle(m_pGCStatics))->GetDataPtr(); - } - - inline OBJECTHANDLE GetPrecomputedGCStaticsBaseHandle() - { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - return m_pGCStatics; - } - - inline OBJECTHANDLE * GetPrecomputedGCStaticsBaseHandleAddress() - { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - return &m_pGCStatics; - } - - // Returns bytes so we can add offsets - inline PTR_BYTE GetGCStaticsBasePointer(MethodTable * pMT) - { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - SUPPORTS_DAC; - } - CONTRACTL_END; - - if (pMT->IsDynamicStatics()) - { - return GetDynamicEntryGCStaticsBasePointer(pMT->GetModuleDynamicEntryID(), pMT->GetLoaderAllocator()); - } - else - { - return dac_cast(GetPrecomputedGCStaticsBasePointer()); - } - } - - inline PTR_BYTE GetNonGCStaticsBasePointer(MethodTable * pMT) - { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - SUPPORTS_DAC; - } - CONTRACTL_END; - - if (pMT->IsDynamicStatics()) - { - return GetDynamicEntryNonGCStaticsBasePointer(pMT->GetModuleDynamicEntryID(), pMT->GetLoaderAllocator()); - } - else - { - return dac_cast(this); - } - } - - inline DynamicEntry* GetDynamicEntry(DWORD n) - { - LIMITED_METHOD_CONTRACT - SUPPORTS_DAC; - _ASSERTE(m_pDynamicClassTable && m_aDynamicEntries > n); - DynamicEntry* pEntry = m_pDynamicClassTable[n].m_pDynamicEntry; - - return pEntry; - } - - inline DynamicClassInfo* GetDynamicClassInfo(DWORD n) - { - LIMITED_METHOD_CONTRACT - SUPPORTS_DAC; - _ASSERTE(m_pDynamicClassTable && m_aDynamicEntries > n); - dac_cast(m_pDynamicClassTable[n].m_pDynamicEntry); - - return &m_pDynamicClassTable[n]; - } - - // These helpers can now return null, as the debugger may do queries on a type - // before the calls to PopulateClass happen - inline PTR_BYTE GetDynamicEntryGCStaticsBasePointer(DWORD n, PTR_LoaderAllocator pLoaderAllocator) - { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - SUPPORTS_DAC; - } - CONTRACTL_END; - - if (n >= m_aDynamicEntries) - { - return NULL; - } - - DynamicClassInfo* pClassInfo = GetDynamicClassInfo(n); - if (!pClassInfo->m_pDynamicEntry) - { - return NULL; - } - - PTR_BYTE retval = NULL; - - GET_DYNAMICENTRY_GCTHREADSTATICS_BASEPOINTER(pLoaderAllocator, pClassInfo, &retval); - - return retval; - } - - inline PTR_BYTE GetDynamicEntryNonGCStaticsBasePointer(DWORD n, PTR_LoaderAllocator pLoaderAllocator) - { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - SUPPORTS_DAC; - } - CONTRACTL_END; - - if (n >= m_aDynamicEntries) - { - return NULL; - } - - DynamicClassInfo* pClassInfo = GetDynamicClassInfo(n); - if (!pClassInfo->m_pDynamicEntry) - { - return NULL; - } - - PTR_BYTE retval = NULL; - - GET_DYNAMICENTRY_NONGCTHREADSTATICS_BASEPOINTER(pLoaderAllocator, pClassInfo, &retval); - - return retval; - } - - FORCEINLINE PTR_DynamicClassInfo GetDynamicClassInfoIfInitialized(DWORD n) - { - WRAPPER_NO_CONTRACT; - - // m_aDynamicEntries is set last, it needs to be checked first - if (n >= m_aDynamicEntries) - { - return NULL; - } - - _ASSERTE(m_pDynamicClassTable != NULL); - PTR_DynamicClassInfo pDynamicClassInfo = (PTR_DynamicClassInfo)(m_pDynamicClassTable + n); - - // ClassInitFlags::INITIALIZED_FLAG is set last, it needs to be checked first - if ((pDynamicClassInfo->m_dwFlags & ClassInitFlags::INITIALIZED_FLAG) == 0) - { - return NULL; - } - - PREFIX_ASSUME(pDynamicClassInfo != NULL); - return pDynamicClassInfo; - } - - // iClassIndex is slightly expensive to compute, so if we already know - // it, we can use this helper - - inline BOOL IsClassInitialized(MethodTable* pMT, DWORD iClassIndex = (DWORD)-1) - { - WRAPPER_NO_CONTRACT; - return (GetClassFlags(pMT, iClassIndex) & ClassInitFlags::INITIALIZED_FLAG) != 0; - } - - inline BOOL IsClassAllocated(MethodTable* pMT, DWORD iClassIndex = (DWORD)-1) - { - WRAPPER_NO_CONTRACT; - return (GetClassFlags(pMT, iClassIndex) & ClassInitFlags::ALLOCATECLASS_FLAG) != 0; - } - - BOOL IsClassInitError(MethodTable* pMT, DWORD iClassIndex = (DWORD)-1) - { - WRAPPER_NO_CONTRACT; - return (GetClassFlags(pMT, iClassIndex) & ClassInitFlags::ERROR_FLAG) != 0; - } - - void SetClassInitialized(MethodTable* pMT) - { - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - _ASSERTE(!IsClassInitialized(pMT)); - _ASSERTE(!IsClassInitError(pMT)); - - SetClassFlags(pMT, ClassInitFlags::INITIALIZED_FLAG); - } - - void SetClassAllocated(MethodTable* pMT) - { - WRAPPER_NO_CONTRACT; - - SetClassFlags(pMT, ClassInitFlags::ALLOCATECLASS_FLAG); - } - - void SetClassInitError(MethodTable* pMT) - { - WRAPPER_NO_CONTRACT; - - SetClassFlags(pMT, ClassInitFlags::ERROR_FLAG); - } - -#ifndef DACCESS_COMPILE - - void EnsureDynamicClassIndex(DWORD dwID); - - void AllocateDynamicClass(MethodTable *pMT); - - void PopulateClass(MethodTable *pMT); - -#endif - -#ifdef DACCESS_COMPILE - void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); -#endif - - static DWORD OffsetOfDataBlob() - { - LIMITED_METHOD_CONTRACT; - return offsetof(ThreadLocalModule, m_pDataBlob); - } - -private: - - void SetClassFlags(MethodTable* pMT, DWORD dwFlags); - - DWORD GetClassFlags(MethodTable* pMT, DWORD iClassIndex); - - - PTR_DynamicClassInfo m_pDynamicClassTable; // used for generics and reflection.emit in memory - SIZE_T m_aDynamicEntries; // number of entries in dynamic table - OBJECTHANDLE m_pGCStatics; // Handle to GC statics of the module - - // Note that the static offset calculation in code:Module::BuildStaticsOffsets takes the offset m_pDataBlob - // into consideration so we do not need any padding to ensure that the start of the data blob is aligned - - BYTE m_pDataBlob[0]; // First byte of the statics blob - - // Layout of m_pDataBlob is: - // ClassInit bytes (hold flags for cctor run, cctor error, etc) - // Non GC Statics - -public: - inline PTR_BYTE GetPrecomputedStaticsClassData() - { - LIMITED_METHOD_CONTRACT - return dac_cast(this) + offsetof(ThreadLocalModule, m_pDataBlob); - } - - inline BOOL IsPrecomputedClassInitialized(DWORD classID) - { - return GetPrecomputedStaticsClassData()[classID] & ClassInitFlags::INITIALIZED_FLAG; - } - - void* operator new(size_t) = delete; - - struct ParentModule { PTR_Module pModule; }; - - void* operator new(size_t baseSize, ParentModule parentModule) - { - size_t size = parentModule.pModule->GetThreadLocalModuleSize(); - - _ASSERTE(size >= baseSize); - _ASSERTE(size >= ThreadLocalModule::OffsetOfDataBlob()); - - return ::operator new(size); - } - -#ifndef DACCESS_COMPILE - - FORCEINLINE void EnsureClassAllocated(MethodTable * pMT) - { - _ASSERTE(this != NULL); - - // Check if the class needs to be allocated - if (!IsClassAllocated(pMT)) - PopulateClass(pMT); - - // If PopulateClass() does not throw, then we are guaranteed - // that the class has been allocated - _ASSERTE(IsClassAllocated(pMT)); - } - - FORCEINLINE void CheckRunClassInitThrowing(MethodTable * pMT) - { - _ASSERTE(this != NULL); - - // Check if the class has been marked as inited in the ThreadLocalModule - if (!IsClassInitialized(pMT)) - { - // Ensure that the class has been allocated - EnsureClassAllocated(pMT); - - // Check if the class has been marked as inited in the DomainLocalModule, - // if not we must call CheckRunClassInitThrowing() - if (!pMT->IsClassInited()) - pMT->CheckRunClassInitThrowing(); - - // We cannot mark the class as inited in the TLM until it has been marked - // as inited in the DLM. MethodTable::CheckRunClassInitThrowing() can return - // before the class constructor has finished running (because of recursion), - // so we actually need to check if the class has been marked as inited in the - // DLM before marking it as inited in the TLM. - if (pMT->IsClassInited()) - SetClassInitialized(pMT); - } - } - -#endif -}; // struct ThreadLocalModule - - -#define OFFSETOF__ThreadLocalModule__m_pDataBlob (3 * TARGET_POINTER_SIZE /* m_pDynamicClassTable + m_aDynamicEntries + m_pGCStatics */) -#ifdef FEATURE_64BIT_ALIGNMENT -#define OFFSETOF__ThreadLocalModule__DynamicEntry__m_pDataBlob (TARGET_POINTER_SIZE /* m_pGCStatics */ + TARGET_POINTER_SIZE /* m_padding */) -#else -#define OFFSETOF__ThreadLocalModule__DynamicEntry__m_pDataBlob TARGET_POINTER_SIZE /* m_pGCStatics */ -#endif -typedef DPTR(struct TLMTableEntry) PTR_TLMTableEntry; +#ifndef __THREADLOCALSTORAGE_H__ +#define __THREADLOCALSTORAGE_H__ -struct TLMTableEntry +struct TLSIndex { - PTR_ThreadLocalModule pTLM; + TLSIndex(uint32_t rawIndex) : TLSIndexRawIndex(rawIndex) { } + uint32_t TLSIndexRawIndex; + int32_t GetByteIndex() { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex & 0xFFFFFF; } + int8_t GetTLSArrayOffset() { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex >> 24; } + bool IsAllocated() { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex != 0;} + bool operator == (TLSIndex index) { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex == index.TLSIndexRawIndex; } + bool operator != (TLSIndex index) { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex != index.TLSIndexRawIndex; } }; +struct ThreadLocalData; +typedef DPTR(ThreadLocalData) PTR_ThreadLocalData; -typedef DPTR(struct ThreadLocalBlock) PTR_ThreadLocalBlock; -typedef DPTR(PTR_ThreadLocalBlock) PTR_PTR_ThreadLocalBlock; +template +struct LookupMap; -class ThreadStatics -{ - public: - -#ifndef DACCESS_COMPILE - static PTR_ThreadLocalModule AllocateTLM(Module * pModule); - static PTR_ThreadLocalModule AllocateAndInitTLM(ModuleIndex index, PTR_ThreadLocalBlock pThreadLocalBlock, Module * pModule); - - static PTR_ThreadLocalModule GetTLM(ModuleIndex index, Module * pModule); - static PTR_ThreadLocalModule GetTLM(MethodTable * pMT); -#endif - - FORCEINLINE static PTR_ThreadLocalBlock GetCurrentTLB(PTR_Thread pThread) - { - SUPPORTS_DAC; +typedef LookupMap TLSIndexToMethodTableMap; +typedef DPTR(TLSIndexToMethodTableMap) PTR_TLSIndexToMethodTableMap; +GPTR_DECL(TLSIndexToMethodTableMap, g_pThreadStaticTypeIndices); - return dac_cast(PTR_TO_MEMBER_TADDR(Thread, pThread, m_ThreadLocalBlock)); - } +PTR_MethodTable LookupMethodTableForThreadStatic(TLSIndex index); +PTR_VOID GetThreadLocalStaticBaseNoCreate(PTR_ThreadLocalData pThreadLocalData, TLSIndex index); +void ScanThreadStaticRoots(PTR_ThreadLocalData pThreadLocalData, promote_func* fn, ScanContext* sc); #ifndef DACCESS_COMPILE - FORCEINLINE static ThreadLocalBlock* GetCurrentTLB() - { - // Get the current thread - Thread * pThread = GetThread(); - return &pThread->m_ThreadLocalBlock; - } - - FORCEINLINE static ThreadLocalModule* GetTLMIfExists(ModuleIndex index) - { - // Get the current ThreadLocalBlock - PTR_ThreadLocalBlock pThreadLocalBlock = GetCurrentTLB(); - - // Get the TLM from the ThreadLocalBlock's table - return pThreadLocalBlock->GetTLMIfExists(index); - } - - FORCEINLINE static ThreadLocalModule* GetTLMIfExists(MethodTable * pMT) - { - // Get the current ThreadLocalBlock - ThreadLocalBlock* pThreadLocalBlock = GetCurrentTLB(); - - // Get the TLM from the ThreadLocalBlock's table - return pThreadLocalBlock->GetTLMIfExists(pMT); - } +void InitializeThreadStaticData(); +void InitializeCurrentThreadsStaticData(Thread* pThread); +void FreeCurrentThreadStaticData(); +void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pIndex); +void FreeTLSIndexForThreadStatic(TLSIndex index); +void* GetThreadLocalStaticBase(TLSIndex index); +void* GetThreadLocalStaticBaseIfExistsAndInitialized(TLSIndex index); +void GetThreadLocalStaticBlocksInfo (CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo); +#else +void EnumThreadMemoryRegions(PTR_ThreadLocalData pThreadLocalData, CLRDataEnumMemoryFlags flags); #endif -}; - -/* static */ -inline DWORD ThreadLocalModule::DynamicEntry::GetOffsetOfDataBlob() -{ - LIMITED_METHOD_CONTRACT; - _ASSERTE(DWORD(offsetof(NormalDynamicEntry, m_pDataBlob)) == offsetof(NormalDynamicEntry, m_pDataBlob)); - return (DWORD)offsetof(NormalDynamicEntry, m_pDataBlob); -} - -#endif +#endif // __THREADLOCALSTORAGE_H__ \ No newline at end of file diff --git a/src/coreclr/vm/vars.cpp b/src/coreclr/vm/vars.cpp index b5e9263e35ce0..d27101c3300c9 100644 --- a/src/coreclr/vm/vars.cpp +++ b/src/coreclr/vm/vars.cpp @@ -43,8 +43,6 @@ void * g_LastAccessViolationEIP; // The EIP of the place we last #endif // #ifndef DACCESS_COMPILE GPTR_IMPL(IdDispenser, g_pThinLockThreadIdDispenser); -GPTR_IMPL(IdDispenser, g_pModuleIndexDispenser); - // For [ Date: Wed, 28 Feb 2024 15:15:33 -0800 Subject: [PATCH 02/82] Followup commit --- src/coreclr/inc/jiteeversionguid.h | 10 +++++----- src/coreclr/jit/helperexpansion.cpp | 3 --- src/coreclr/jit/importer.cpp | 1 - 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 1bc5c8a1de9a7..2b42b2715f9e2 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 930abea7-44fd-4f45-9826-8568480e8008 */ - 0x930abea7, - 0x44fd, - 0x4f45, - {0x98, 0x26, 0x85, 0x68, 0x48, 0x0e, 0x80, 0x08} +constexpr GUID JITEEVersionIdentifier = { /* f3e0e34f-4a40-4de5-ba3f-cc17abae3812 */ + 0xf3e0e34f, + 0x4a40, + 0x4de5, + {0xba, 0x3f, 0xcc, 0x17, 0xab, 0xae, 0x38, 0x12} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index 1eaeeaa0c3f01..f90119914bc08 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -800,14 +800,11 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* return false; } -<<<<<<< HEAD // TODO ... actually implement this stuff. return false; /* assert(!opts.IsReadyToRun()); -======= ->>>>>>> 00474fcb63082b51a0615039c8c1b5d4666f91b2 if (TargetOS::IsUnix) { #if defined(TARGET_ARM) || !defined(TARGET_64BIT) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 1cf46351f4688..2150a65fa8a66 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -3821,7 +3821,6 @@ GenTree* Compiler::impImportStaticFieldAddress(CORINFO_RESOLVED_TOKEN* pResolved case CORINFO_FIELD_STATIC_TLS_MANAGED: -<<<<<<< HEAD #ifdef FEATURE_READYTORUN if (!opts.IsReadyToRun()) #endif // FEATURE_READYTORUN From 3cb032457dd56baea156b658a4ec66ea1df4dace Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 28 Feb 2024 15:48:02 -0800 Subject: [PATCH 03/82] Fix merge conflict --- src/coreclr/vm/threadstatics.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index ec629fec6e379..a87ff5d183a31 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -164,7 +164,7 @@ void AllocateThreadStaticBoxes(MethodTable *pMT, PTRARRAYREF *ppRef) THROWS; GC_TRIGGERS; MODE_COOPERATIVE; - PRECONDITION(pMT->GetClass()->GetNumBoxedThreadStatics() > 0); // TODO, should be HasBoxedThreadStatics() + PRECONDITION(pMT->HasBoxedThreadStatics()); INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; @@ -281,7 +281,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) if (isGCStatic) { gc.ptrRef = AllocateObjectArray(pMT->GetClass()->GetNumHandleThreadStatics(), g_pObjectClass); - if (pMT->GetClass()->GetNumBoxedThreadStatics() > 0) + if (pMT->HasBoxedThreadStatics()) { AllocateThreadStaticBoxes(pMT, &gc.ptrRef); } From 37e2555eb636fbc53d17cfdc38ed822de719ffe1 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 28 Feb 2024 16:09:44 -0800 Subject: [PATCH 04/82] Tweak so R2R works for now. May need further adjustment. --- .../Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs index 0eaf7d58e10ae..a388e57cb9253 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs @@ -248,9 +248,9 @@ public FieldAndOffset[] GetOrAddDynamicLayout(DefType defType, ModuleFieldLayout { OffsetsForType offsetsForType = new OffsetsForType( nonGcOffset: LayoutInt.Zero, - tlsNonGcOffset: LayoutInt.Zero, + tlsNonGcOffset: new LayoutInt(2 * defType.Context.Target.PointerSize), gcOffset: LayoutInt.Zero, - tlsGcOffset: LayoutInt.Zero); + tlsGcOffset: new LayoutInt(2 * defType.Context.Target.PointerSize)); fieldsForType = moduleFieldLayout.GetOrAddDynamicLayout( defType, From 78720e11bd7307dd0e959301521c6229b00fcde6 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 28 Feb 2024 17:14:23 -0800 Subject: [PATCH 05/82] Enable using the dynamic and pinned helpers for jitted code. Should be a smidge faster --- src/coreclr/inc/corinfo.h | 8 + src/coreclr/inc/icorjitinfoimpl_generated.h | 6 + src/coreclr/inc/jiteeversionguid.h | 10 +- src/coreclr/jit/ICorJitInfo_names_generated.h | 2 + .../jit/ICorJitInfo_wrapper_generated.hpp | 18 ++ src/coreclr/jit/flowgraph.cpp | 20 +- .../tools/Common/JitInterface/CorInfoImpl.cs | 5 + .../JitInterface/CorInfoImpl_generated.cs | 282 ++++++++++-------- .../ThunkGenerator/ThunkInput.txt | 2 + .../aot/jitinterface/jitinterface_generated.h | 20 ++ .../tools/superpmi/superpmi-shared/lwmlist.h | 2 + .../superpmi-shared/methodcontext.cpp | 56 ++++ .../superpmi/superpmi-shared/methodcontext.h | 10 + .../superpmi-shim-collector/icorjitinfo.cpp | 17 ++ .../icorjitinfo_generated.cpp | 14 + .../icorjitinfo_generated.cpp | 12 + .../tools/superpmi/superpmi/icorjitinfo.cpp | 12 + src/coreclr/vm/jitinterface.cpp | 84 +++++- src/coreclr/vm/prestub.cpp | 3 +- 19 files changed, 442 insertions(+), 141 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 1f954b17cd248..4b130ced6c3d5 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -2424,6 +2424,14 @@ class ICorStaticInfo int* offset ) = 0; + virtual size_t getClassStaticDynamicInfo ( + CORINFO_CLASS_HANDLE cls + ) = 0; + + virtual size_t getClassThreadStaticDynamicInfo ( + CORINFO_CLASS_HANDLE cls + ) = 0; + virtual bool getStaticBaseAddress( CORINFO_CLASS_HANDLE cls, bool isGc, diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 51dacb338adae..612bd81e53605 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -212,6 +212,12 @@ bool getIsClassInitedFlagAddress( CORINFO_CONST_LOOKUP* addr, int* offset) override; +size_t getClassThreadStaticDynamicInfo( + CORINFO_CLASS_HANDLE clr) override; + +size_t getClassStaticDynamicInfo( + CORINFO_CLASS_HANDLE clr) override; + bool getStaticBaseAddress( CORINFO_CLASS_HANDLE cls, bool isGc, diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 2b42b2715f9e2..a3ea293f1e5bb 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* f3e0e34f-4a40-4de5-ba3f-cc17abae3812 */ - 0xf3e0e34f, - 0x4a40, - 0x4de5, - {0xba, 0x3f, 0xcc, 0x17, 0xab, 0xae, 0x38, 0x12} +constexpr GUID JITEEVersionIdentifier = { /* 4500b78e-8610-48db-a045-7085b6762beb */ + 0x4500b78e, + 0x8610, + 0x48db, + {0xa0, 0x45, 0x70, 0x85, 0xb6, 0x76, 0x2b, 0xeb} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h index ca1eb68c55b15..ba65a071e68a0 100644 --- a/src/coreclr/jit/ICorJitInfo_names_generated.h +++ b/src/coreclr/jit/ICorJitInfo_names_generated.h @@ -51,6 +51,8 @@ DEF_CLR_API(getAssemblyName) DEF_CLR_API(LongLifetimeMalloc) DEF_CLR_API(LongLifetimeFree) DEF_CLR_API(getIsClassInitedFlagAddress) +DEF_CLR_API(getClassThreadStaticDynamicInfo) +DEF_CLR_API(getClassStaticDynamicInfo) DEF_CLR_API(getStaticBaseAddress) DEF_CLR_API(getClassSize) DEF_CLR_API(getHeapClassSize) diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index d4845bd87e755..c888308646b3a 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -470,6 +470,24 @@ bool WrapICorJitInfo::getIsClassInitedFlagAddress( return temp; } +size_t WrapICorJitInfo::getClassThreadStaticDynamicInfo( + CORINFO_CLASS_HANDLE clr) +{ + API_ENTER(getClassThreadStaticDynamicInfo); + size_t temp = wrapHnd->getClassThreadStaticDynamicInfo(clr); + API_LEAVE(getClassThreadStaticDynamicInfo); + return temp; +} + +size_t WrapICorJitInfo::getClassStaticDynamicInfo( + CORINFO_CLASS_HANDLE clr) +{ + API_ENTER(getClassStaticDynamicInfo); + size_t temp = wrapHnd->getClassStaticDynamicInfo(clr); + API_LEAVE(getClassStaticDynamicInfo); + return temp; +} + bool WrapICorJitInfo::getStaticBaseAddress( CORINFO_CLASS_HANDLE cls, bool isGc, diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index ac073c4e3d521..f81d9c50aadb2 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -737,9 +737,27 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo { result = gtNewHelperCallNode(helper, type, gtNewIconNode(typeIndex)); } + else if (helper == CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR || + helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR || + helper == CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE || + helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE) + { + result = gtNewHelperCallNode(helper, type, gtNewIconNode((size_t)info.compCompHnd->getClassThreadStaticDynamicInfo(cls), TYP_I_IMPL)); + } + else if (helper == CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE || + helper == CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE || + helper == CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE_NOCTOR || + helper == CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE_NOCTOR || + helper == CORINFO_HELP_GETPINNED_GCSTATIC_BASE || + helper == CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE || + helper == CORINFO_HELP_GETPINNED_GCSTATIC_BASE_NOCTOR || + helper == CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE_NOCTOR) + { + result = gtNewHelperCallNode(helper, type, gtNewIconNode(info.compCompHnd->getClassStaticDynamicInfo(cls), TYP_I_IMPL)); + } else { - result = gtNewHelperCallNode(helper, type, gtNewIconEmbClsHndNode(cls)); // TODO, handle the PINNED/DYNAMIC cases + result = gtNewHelperCallNode(helper, type, gtNewIconEmbClsHndNode(cls)); } if (IsStaticHelperEligibleForExpansion(result)) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 4fcaa8653ba0a..a9b22fe6a343f 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2114,6 +2114,11 @@ private void LongLifetimeFree(void* obj) Marshal.FreeCoTaskMem((IntPtr)obj); } + private UIntPtr getClassStaticDynamicInfo(CORINFO_CLASS_STRUCT_* cls) + { throw new NotImplementedException("getClassStaticDynamicInfo"); } + private UIntPtr getClassThreadStaticDynamicInfo(CORINFO_CLASS_STRUCT_* cls) + { throw new NotImplementedException("getClassThreadStaticDynamicInfo"); } + private uint getClassSize(CORINFO_CLASS_STRUCT_* cls) { TypeDesc type = HandleToObject(cls); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index f2372b3025e3d..c3ecdfe6708d1 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -702,6 +702,36 @@ private static byte _getIsClassInitedFlagAddress(IntPtr thisHandle, IntPtr* ppEx } } + [UnmanagedCallersOnly] + private static UIntPtr _getClassThreadStaticDynamicInfo(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* clr) + { + var _this = GetThis(thisHandle); + try + { + return _this.getClassThreadStaticDynamicInfo(clr); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + return default; + } + } + + [UnmanagedCallersOnly] + private static UIntPtr _getClassStaticDynamicInfo(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* clr) + { + var _this = GetThis(thisHandle); + try + { + return _this.getClassStaticDynamicInfo(clr); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + return default; + } + } + [UnmanagedCallersOnly] private static byte _getStaticBaseAddress(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* cls, byte isGc, CORINFO_CONST_LOOKUP* addr) { @@ -2535,7 +2565,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_ private static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 171); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 173); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_notifyMethodInfoUsage; @@ -2584,130 +2614,132 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[44] = (delegate* unmanaged)&_LongLifetimeMalloc; callbacks[45] = (delegate* unmanaged)&_LongLifetimeFree; callbacks[46] = (delegate* unmanaged)&_getIsClassInitedFlagAddress; - callbacks[47] = (delegate* unmanaged)&_getStaticBaseAddress; - callbacks[48] = (delegate* unmanaged)&_getClassSize; - callbacks[49] = (delegate* unmanaged)&_getHeapClassSize; - callbacks[50] = (delegate* unmanaged)&_canAllocateOnStack; - callbacks[51] = (delegate* unmanaged)&_getClassAlignmentRequirement; - callbacks[52] = (delegate* unmanaged)&_getClassGClayout; - callbacks[53] = (delegate* unmanaged)&_getClassNumInstanceFields; - callbacks[54] = (delegate* unmanaged)&_getFieldInClass; - callbacks[55] = (delegate* unmanaged)&_getTypeLayout; - callbacks[56] = (delegate* unmanaged)&_checkMethodModifier; - callbacks[57] = (delegate* unmanaged)&_getNewHelper; - callbacks[58] = (delegate* unmanaged)&_getNewArrHelper; - callbacks[59] = (delegate* unmanaged)&_getCastingHelper; - callbacks[60] = (delegate* unmanaged)&_getSharedCCtorHelper; - callbacks[61] = (delegate* unmanaged)&_getTypeForBox; - callbacks[62] = (delegate* unmanaged)&_getBoxHelper; - callbacks[63] = (delegate* unmanaged)&_getUnBoxHelper; - callbacks[64] = (delegate* unmanaged)&_getRuntimeTypePointer; - callbacks[65] = (delegate* unmanaged)&_isObjectImmutable; - callbacks[66] = (delegate* unmanaged)&_getStringChar; - callbacks[67] = (delegate* unmanaged)&_getObjectType; - callbacks[68] = (delegate* unmanaged)&_getReadyToRunHelper; - callbacks[69] = (delegate* unmanaged)&_getReadyToRunDelegateCtorHelper; - callbacks[70] = (delegate* unmanaged)&_initClass; - callbacks[71] = (delegate* unmanaged)&_classMustBeLoadedBeforeCodeIsRun; - callbacks[72] = (delegate* unmanaged)&_getBuiltinClass; - callbacks[73] = (delegate* unmanaged)&_getTypeForPrimitiveValueClass; - callbacks[74] = (delegate* unmanaged)&_getTypeForPrimitiveNumericClass; - callbacks[75] = (delegate* unmanaged)&_canCast; - callbacks[76] = (delegate* unmanaged)&_compareTypesForCast; - callbacks[77] = (delegate* unmanaged)&_compareTypesForEquality; - callbacks[78] = (delegate* unmanaged)&_isMoreSpecificType; - callbacks[79] = (delegate* unmanaged)&_isExactType; - callbacks[80] = (delegate* unmanaged)&_isEnum; - callbacks[81] = (delegate* unmanaged)&_getParentType; - callbacks[82] = (delegate* unmanaged)&_getChildType; - callbacks[83] = (delegate* unmanaged)&_isSDArray; - callbacks[84] = (delegate* unmanaged)&_getArrayRank; - callbacks[85] = (delegate* unmanaged)&_getArrayIntrinsicID; - callbacks[86] = (delegate* unmanaged)&_getArrayInitializationData; - callbacks[87] = (delegate* unmanaged)&_canAccessClass; - callbacks[88] = (delegate* unmanaged)&_printFieldName; - callbacks[89] = (delegate* unmanaged)&_getFieldClass; - callbacks[90] = (delegate* unmanaged)&_getFieldType; - callbacks[91] = (delegate* unmanaged)&_getFieldOffset; - callbacks[92] = (delegate* unmanaged)&_getFieldInfo; - callbacks[93] = (delegate* unmanaged)&_getThreadLocalFieldInfo; - callbacks[94] = (delegate* unmanaged)&_getThreadLocalStaticBlocksInfo; - callbacks[95] = (delegate* unmanaged)&_getThreadLocalStaticInfo_NativeAOT; - callbacks[96] = (delegate* unmanaged)&_isFieldStatic; - callbacks[97] = (delegate* unmanaged)&_getArrayOrStringLength; - callbacks[98] = (delegate* unmanaged)&_getBoundaries; - callbacks[99] = (delegate* unmanaged)&_setBoundaries; - callbacks[100] = (delegate* unmanaged)&_getVars; - callbacks[101] = (delegate* unmanaged)&_setVars; - callbacks[102] = (delegate* unmanaged)&_reportRichMappings; - callbacks[103] = (delegate* unmanaged)&_reportMetadata; - callbacks[104] = (delegate* unmanaged)&_allocateArray; - callbacks[105] = (delegate* unmanaged)&_freeArray; - callbacks[106] = (delegate* unmanaged)&_getArgNext; - callbacks[107] = (delegate* unmanaged)&_getArgType; - callbacks[108] = (delegate* unmanaged)&_getExactClasses; - callbacks[109] = (delegate* unmanaged)&_getArgClass; - callbacks[110] = (delegate* unmanaged)&_getHFAType; - callbacks[111] = (delegate* unmanaged)&_runWithErrorTrap; - callbacks[112] = (delegate* unmanaged)&_runWithSPMIErrorTrap; - callbacks[113] = (delegate* unmanaged)&_getEEInfo; - callbacks[114] = (delegate* unmanaged)&_getJitTimeLogFilename; - callbacks[115] = (delegate* unmanaged)&_getMethodDefFromMethod; - callbacks[116] = (delegate* unmanaged)&_printMethodName; - callbacks[117] = (delegate* unmanaged)&_getMethodNameFromMetadata; - callbacks[118] = (delegate* unmanaged)&_getMethodHash; - callbacks[119] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; - callbacks[120] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; - callbacks[121] = (delegate* unmanaged)&_getRISCV64PassStructInRegisterFlags; - callbacks[122] = (delegate* unmanaged)&_getThreadTLSIndex; - callbacks[123] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; - callbacks[124] = (delegate* unmanaged)&_getHelperFtn; - callbacks[125] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[126] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; - callbacks[127] = (delegate* unmanaged)&_getMethodSync; - callbacks[128] = (delegate* unmanaged)&_getLazyStringLiteralHelper; - callbacks[129] = (delegate* unmanaged)&_embedModuleHandle; - callbacks[130] = (delegate* unmanaged)&_embedClassHandle; - callbacks[131] = (delegate* unmanaged)&_embedMethodHandle; - callbacks[132] = (delegate* unmanaged)&_embedFieldHandle; - callbacks[133] = (delegate* unmanaged)&_embedGenericHandle; - callbacks[134] = (delegate* unmanaged)&_getLocationOfThisType; - callbacks[135] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; - callbacks[136] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[137] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; - callbacks[138] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[139] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[140] = (delegate* unmanaged)&_getCallInfo; - callbacks[141] = (delegate* unmanaged)&_getStaticFieldContent; - callbacks[142] = (delegate* unmanaged)&_getObjectContent; - callbacks[143] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[144] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[145] = (delegate* unmanaged)&_canGetVarArgsHandle; - callbacks[146] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[147] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[148] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[149] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[150] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[151] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[152] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[153] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[154] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[155] = (delegate* unmanaged)&_allocMem; - callbacks[156] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[157] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[158] = (delegate* unmanaged)&_allocGCInfo; - callbacks[159] = (delegate* unmanaged)&_setEHcount; - callbacks[160] = (delegate* unmanaged)&_setEHinfo; - callbacks[161] = (delegate* unmanaged)&_logMsg; - callbacks[162] = (delegate* unmanaged)&_doAssert; - callbacks[163] = (delegate* unmanaged)&_reportFatalError; - callbacks[164] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[165] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[166] = (delegate* unmanaged)&_recordCallSite; - callbacks[167] = (delegate* unmanaged)&_recordRelocation; - callbacks[168] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[169] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[170] = (delegate* unmanaged)&_getJitFlags; + callbacks[47] = (delegate* unmanaged)&_getClassThreadStaticDynamicInfo; + callbacks[48] = (delegate* unmanaged)&_getClassStaticDynamicInfo; + callbacks[49] = (delegate* unmanaged)&_getStaticBaseAddress; + callbacks[50] = (delegate* unmanaged)&_getClassSize; + callbacks[51] = (delegate* unmanaged)&_getHeapClassSize; + callbacks[52] = (delegate* unmanaged)&_canAllocateOnStack; + callbacks[53] = (delegate* unmanaged)&_getClassAlignmentRequirement; + callbacks[54] = (delegate* unmanaged)&_getClassGClayout; + callbacks[55] = (delegate* unmanaged)&_getClassNumInstanceFields; + callbacks[56] = (delegate* unmanaged)&_getFieldInClass; + callbacks[57] = (delegate* unmanaged)&_getTypeLayout; + callbacks[58] = (delegate* unmanaged)&_checkMethodModifier; + callbacks[59] = (delegate* unmanaged)&_getNewHelper; + callbacks[60] = (delegate* unmanaged)&_getNewArrHelper; + callbacks[61] = (delegate* unmanaged)&_getCastingHelper; + callbacks[62] = (delegate* unmanaged)&_getSharedCCtorHelper; + callbacks[63] = (delegate* unmanaged)&_getTypeForBox; + callbacks[64] = (delegate* unmanaged)&_getBoxHelper; + callbacks[65] = (delegate* unmanaged)&_getUnBoxHelper; + callbacks[66] = (delegate* unmanaged)&_getRuntimeTypePointer; + callbacks[67] = (delegate* unmanaged)&_isObjectImmutable; + callbacks[68] = (delegate* unmanaged)&_getStringChar; + callbacks[69] = (delegate* unmanaged)&_getObjectType; + callbacks[70] = (delegate* unmanaged)&_getReadyToRunHelper; + callbacks[71] = (delegate* unmanaged)&_getReadyToRunDelegateCtorHelper; + callbacks[72] = (delegate* unmanaged)&_initClass; + callbacks[73] = (delegate* unmanaged)&_classMustBeLoadedBeforeCodeIsRun; + callbacks[74] = (delegate* unmanaged)&_getBuiltinClass; + callbacks[75] = (delegate* unmanaged)&_getTypeForPrimitiveValueClass; + callbacks[76] = (delegate* unmanaged)&_getTypeForPrimitiveNumericClass; + callbacks[77] = (delegate* unmanaged)&_canCast; + callbacks[78] = (delegate* unmanaged)&_compareTypesForCast; + callbacks[79] = (delegate* unmanaged)&_compareTypesForEquality; + callbacks[80] = (delegate* unmanaged)&_isMoreSpecificType; + callbacks[81] = (delegate* unmanaged)&_isExactType; + callbacks[82] = (delegate* unmanaged)&_isEnum; + callbacks[83] = (delegate* unmanaged)&_getParentType; + callbacks[84] = (delegate* unmanaged)&_getChildType; + callbacks[85] = (delegate* unmanaged)&_isSDArray; + callbacks[86] = (delegate* unmanaged)&_getArrayRank; + callbacks[87] = (delegate* unmanaged)&_getArrayIntrinsicID; + callbacks[88] = (delegate* unmanaged)&_getArrayInitializationData; + callbacks[89] = (delegate* unmanaged)&_canAccessClass; + callbacks[90] = (delegate* unmanaged)&_printFieldName; + callbacks[91] = (delegate* unmanaged)&_getFieldClass; + callbacks[92] = (delegate* unmanaged)&_getFieldType; + callbacks[93] = (delegate* unmanaged)&_getFieldOffset; + callbacks[94] = (delegate* unmanaged)&_getFieldInfo; + callbacks[95] = (delegate* unmanaged)&_getThreadLocalFieldInfo; + callbacks[96] = (delegate* unmanaged)&_getThreadLocalStaticBlocksInfo; + callbacks[97] = (delegate* unmanaged)&_getThreadLocalStaticInfo_NativeAOT; + callbacks[98] = (delegate* unmanaged)&_isFieldStatic; + callbacks[99] = (delegate* unmanaged)&_getArrayOrStringLength; + callbacks[100] = (delegate* unmanaged)&_getBoundaries; + callbacks[101] = (delegate* unmanaged)&_setBoundaries; + callbacks[102] = (delegate* unmanaged)&_getVars; + callbacks[103] = (delegate* unmanaged)&_setVars; + callbacks[104] = (delegate* unmanaged)&_reportRichMappings; + callbacks[105] = (delegate* unmanaged)&_reportMetadata; + callbacks[106] = (delegate* unmanaged)&_allocateArray; + callbacks[107] = (delegate* unmanaged)&_freeArray; + callbacks[108] = (delegate* unmanaged)&_getArgNext; + callbacks[109] = (delegate* unmanaged)&_getArgType; + callbacks[110] = (delegate* unmanaged)&_getExactClasses; + callbacks[111] = (delegate* unmanaged)&_getArgClass; + callbacks[112] = (delegate* unmanaged)&_getHFAType; + callbacks[113] = (delegate* unmanaged)&_runWithErrorTrap; + callbacks[114] = (delegate* unmanaged)&_runWithSPMIErrorTrap; + callbacks[115] = (delegate* unmanaged)&_getEEInfo; + callbacks[116] = (delegate* unmanaged)&_getJitTimeLogFilename; + callbacks[117] = (delegate* unmanaged)&_getMethodDefFromMethod; + callbacks[118] = (delegate* unmanaged)&_printMethodName; + callbacks[119] = (delegate* unmanaged)&_getMethodNameFromMetadata; + callbacks[120] = (delegate* unmanaged)&_getMethodHash; + callbacks[121] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; + callbacks[122] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; + callbacks[123] = (delegate* unmanaged)&_getRISCV64PassStructInRegisterFlags; + callbacks[124] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[125] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[126] = (delegate* unmanaged)&_getHelperFtn; + callbacks[127] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[128] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[129] = (delegate* unmanaged)&_getMethodSync; + callbacks[130] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[131] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[132] = (delegate* unmanaged)&_embedClassHandle; + callbacks[133] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[134] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[135] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[136] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[137] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[138] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[139] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[140] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[141] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[142] = (delegate* unmanaged)&_getCallInfo; + callbacks[143] = (delegate* unmanaged)&_getStaticFieldContent; + callbacks[144] = (delegate* unmanaged)&_getObjectContent; + callbacks[145] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[146] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[147] = (delegate* unmanaged)&_canGetVarArgsHandle; + callbacks[148] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[149] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[150] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[151] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[152] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[153] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[154] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[155] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[156] = (delegate* unmanaged)&_updateEntryPointForTailCall; + callbacks[157] = (delegate* unmanaged)&_allocMem; + callbacks[158] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[159] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[160] = (delegate* unmanaged)&_allocGCInfo; + callbacks[161] = (delegate* unmanaged)&_setEHcount; + callbacks[162] = (delegate* unmanaged)&_setEHinfo; + callbacks[163] = (delegate* unmanaged)&_logMsg; + callbacks[164] = (delegate* unmanaged)&_doAssert; + callbacks[165] = (delegate* unmanaged)&_reportFatalError; + callbacks[166] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[167] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[168] = (delegate* unmanaged)&_recordCallSite; + callbacks[169] = (delegate* unmanaged)&_recordRelocation; + callbacks[170] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[171] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[172] = (delegate* unmanaged)&_getJitFlags; return (IntPtr)callbacks; } diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index 50f2b4fe1d342..47718fb0eff65 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -209,6 +209,8 @@ FUNCTIONS void* LongLifetimeMalloc(size_t sz) void LongLifetimeFree(void* obj) bool getIsClassInitedFlagAddress(CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, int* offset) + size_t getClassThreadStaticDynamicInfo(CORINFO_CLASS_HANDLE clr) + size_t getClassStaticDynamicInfo(CORINFO_CLASS_HANDLE clr) bool getStaticBaseAddress(CORINFO_CLASS_HANDLE cls, bool isGc, CORINFO_CONST_LOOKUP* addr) unsigned getClassSize(CORINFO_CLASS_HANDLE cls) unsigned getHeapClassSize(CORINFO_CLASS_HANDLE cls) diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index 8e945a9eb3bd6..b3630ebdabaad 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -58,6 +58,8 @@ struct JitInterfaceCallbacks void* (* LongLifetimeMalloc)(void * thisHandle, CorInfoExceptionClass** ppException, size_t sz); void (* LongLifetimeFree)(void * thisHandle, CorInfoExceptionClass** ppException, void* obj); bool (* getIsClassInitedFlagAddress)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, int* offset); + size_t (* getClassThreadStaticDynamicInfo)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE clr); + size_t (* getClassStaticDynamicInfo)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE clr); bool (* getStaticBaseAddress)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls, bool isGc, CORINFO_CONST_LOOKUP* addr); unsigned (* getClassSize)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls); unsigned (* getHeapClassSize)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls); @@ -655,6 +657,24 @@ class JitInterfaceWrapper : public ICorJitInfo return temp; } + virtual size_t getClassThreadStaticDynamicInfo( + CORINFO_CLASS_HANDLE clr) +{ + CorInfoExceptionClass* pException = nullptr; + size_t temp = _callbacks->getClassThreadStaticDynamicInfo(_thisHandle, &pException, clr); + if (pException != nullptr) throw pException; + return temp; +} + + virtual size_t getClassStaticDynamicInfo( + CORINFO_CLASS_HANDLE clr) +{ + CorInfoExceptionClass* pException = nullptr; + size_t temp = _callbacks->getClassStaticDynamicInfo(_thisHandle, &pException, clr); + if (pException != nullptr) throw pException; + return temp; +} + virtual bool getStaticBaseAddress( CORINFO_CLASS_HANDLE cls, bool isGc, diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index b8822018b18ca..6138dd856fc66 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -60,6 +60,8 @@ LWM(GetCastingHelper, Agnostic_GetCastingHelper, DWORD) LWM(GetChildType, DWORDLONG, DLD) LWM(GetClassAlignmentRequirement, DLD, DWORD) LWM(GetClassAttribs, DWORDLONG, DWORD) +LWM(GetClassStaticDynamicInfo, DWORDLONG, DLD) +LWM(GetClassThreadStaticDynamicInfo, DWORDLONG, DLD) LWM(GetClassGClayout, DWORDLONG, Agnostic_GetClassGClayout) LWM(GetClassModuleIdForStatics, DWORDLONG, Agnostic_GetClassModuleIdForStatics) LWM(GetIsClassInitedFlagAddress, DWORDLONG, Agnostic_GetIsClassInitedFlagAddress) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index 80f94ccbab59d..63449a0a234ab 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -4654,6 +4654,62 @@ int32_t* MethodContext::repGetAddrOfCaptureThreadGlobal(void** ppIndirection) return (int32_t*)value.B; } +void MethodContext::recGetClassStaticDynamicInfo(CORINFO_CLASS_HANDLE cls, size_t result) +{ + if (GetClassStaticDynamicInfo == nullptr) + GetClassStaticDynamicInfo = new LightWeightMap(); + + DLD value; + + value.A = result; + value.B = 0; + + DWORDLONG key = CastHandle(cls); + GetClassStaticDynamicInfo->Add(key, value); + DEBUG_REC(dmpGetClassStaticDynamicInfo(key, value)); +} +void MethodContext::dmpGetClassStaticDynamicInfo(DWORDLONG key, DLD value) +{ + printf("GetClassStaticDynamicInfo key cls-%016" PRIX64 ", value pp-%016" PRIX64 " res-%u", key, value.A, value.B); +} +size_t MethodContext::repGetClassStaticDynamicInfo(CORINFO_CLASS_HANDLE cls) +{ + DWORDLONG key = CastHandle(cls); + DLD value = LookupByKeyOrMiss(GetClassStaticDynamicInfo, key, ": key %016" PRIX64 "", key); + + DEBUG_REP(dmpGetClassStaticDynamicInfo(key, value)); + + return (size_t)value.A; +} + +void MethodContext::recGetClassThreadStaticDynamicInfo(CORINFO_CLASS_HANDLE cls, size_t result) +{ + if (GetClassThreadStaticDynamicInfo == nullptr) + GetClassThreadStaticDynamicInfo = new LightWeightMap(); + + DLD value; + + value.A = result; + value.B = 0; + + DWORDLONG key = CastHandle(cls); + GetClassThreadStaticDynamicInfo->Add(key, value); + DEBUG_REC(dmpGetClassThreadStaticDynamicInfo(key, value)); +} +void MethodContext::dmpGetClassThreadStaticDynamicInfo(DWORDLONG key, DLD value) +{ + printf("GetClassThreadStaticDynamicInfo key cls-%016" PRIX64 ", value pp-%016" PRIX64 " res-%u", key, value.A, value.B); +} +size_t MethodContext::repGetClassThreadStaticDynamicInfo(CORINFO_CLASS_HANDLE cls) +{ + DWORDLONG key = CastHandle(cls); + DLD value = LookupByKeyOrMiss(GetClassThreadStaticDynamicInfo, key, ": key %016" PRIX64 "", key); + + DEBUG_REP(dmpGetClassThreadStaticDynamicInfo(key, value)); + + return (size_t)value.A; +} + void MethodContext::recGetLocationOfThisType(CORINFO_METHOD_HANDLE context, CORINFO_LOOKUP_KIND* result) { if (GetLocationOfThisType == nullptr) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index 625d99325bae9..228a9b5c8cac1 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -586,6 +586,14 @@ class MethodContext void dmpGetAddrOfCaptureThreadGlobal(DWORD key, DLDL value); int32_t* repGetAddrOfCaptureThreadGlobal(void** ppIndirection); + void recGetClassStaticDynamicInfo(CORINFO_CLASS_HANDLE cls, size_t result); + void dmpGetClassStaticDynamicInfo(DWORDLONG key, DLD value); + size_t repGetClassStaticDynamicInfo(CORINFO_CLASS_HANDLE cls); + + void recGetClassThreadStaticDynamicInfo(CORINFO_CLASS_HANDLE cls, size_t result); + void dmpGetClassThreadStaticDynamicInfo(DWORDLONG key, DLD value); + size_t repGetClassThreadStaticDynamicInfo(CORINFO_CLASS_HANDLE cls); + void recGetLocationOfThisType(CORINFO_METHOD_HANDLE context, CORINFO_LOOKUP_KIND* result); void dmpGetLocationOfThisType(DWORDLONG key, const Agnostic_CORINFO_LOOKUP_KIND& value); void repGetLocationOfThisType(CORINFO_METHOD_HANDLE context, CORINFO_LOOKUP_KIND* pLookupKind); @@ -1150,6 +1158,8 @@ enum mcPackets Packet_HaveSameMethodDefinition = 213, Packet_NotifyMethodInfoUsage = 214, Packet_IsExactType = 215, + Packet_GetClassStaticDynamicInfo = 216, + Packet_GetClassThreadStaticDynamicInfo = 217, }; void SetDebugDumpVariables(); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 1ddea4c9b6c00..0476f21cebd6b 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -529,6 +529,7 @@ void interceptor_ICJI::LongLifetimeFree(void* obj) original_ICorJitInfo->LongLifetimeFree(obj); } + bool interceptor_ICJI::getIsClassInitedFlagAddress(CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, int* offset) @@ -1610,6 +1611,22 @@ void interceptor_ICJI::getCallInfo( }); } +size_t interceptor_ICJI::getClassStaticDynamicInfo(CORINFO_CLASS_HANDLE cls) +{ + mc->cr->AddCall("getClassStaticDynamicInfo"); + size_t temp = original_ICorJitInfo->getClassStaticDynamicInfo(cls); + mc->recGetClassStaticDynamicInfo(cls, temp); + return temp; +} + +size_t interceptor_ICJI::getClassThreadStaticDynamicInfo(CORINFO_CLASS_HANDLE cls) +{ + mc->cr->AddCall("getClassThreadStaticDynamicInfo"); + size_t temp = original_ICorJitInfo->getClassThreadStaticDynamicInfo(cls); + mc->recGetClassThreadStaticDynamicInfo(cls, temp); + return temp; +} + bool interceptor_ICJI::getStaticFieldContent(CORINFO_FIELD_HANDLE field, uint8_t* buffer, int bufferSize, int valueOffset, bool ignoreMovableObjects) { mc->cr->AddCall("getStaticFieldContent"); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp index 9690e427a9227..00ff521f66f3f 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -391,6 +391,20 @@ bool interceptor_ICJI::getIsClassInitedFlagAddress( return original_ICorJitInfo->getIsClassInitedFlagAddress(cls, addr, offset); } +size_t interceptor_ICJI::getClassThreadStaticDynamicInfo( + CORINFO_CLASS_HANDLE clr) +{ + mcs->AddCall("getClassThreadStaticDynamicInfo"); + return original_ICorJitInfo->getClassThreadStaticDynamicInfo(clr); +} + +size_t interceptor_ICJI::getClassStaticDynamicInfo( + CORINFO_CLASS_HANDLE clr) +{ + mcs->AddCall("getClassStaticDynamicInfo"); + return original_ICorJitInfo->getClassStaticDynamicInfo(clr); +} + bool interceptor_ICJI::getStaticBaseAddress( CORINFO_CLASS_HANDLE cls, bool isGc, diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp index 2f2e6d8ddabac..256c89e57815d 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -344,6 +344,18 @@ bool interceptor_ICJI::getIsClassInitedFlagAddress( return original_ICorJitInfo->getIsClassInitedFlagAddress(cls, addr, offset); } +size_t interceptor_ICJI::getClassThreadStaticDynamicInfo( + CORINFO_CLASS_HANDLE clr) +{ + return original_ICorJitInfo->getClassThreadStaticDynamicInfo(clr); +} + +size_t interceptor_ICJI::getClassStaticDynamicInfo( + CORINFO_CLASS_HANDLE clr) +{ + return original_ICorJitInfo->getClassStaticDynamicInfo(clr); +} + bool interceptor_ICJI::getStaticBaseAddress( CORINFO_CLASS_HANDLE cls, bool isGc, diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index e0d626e11e77c..025ba26928083 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -455,6 +455,18 @@ void MyICJI::LongLifetimeFree(void* obj) DebugBreakorAV(33); } +size_t MyICJI::getClassThreadStaticDynamicInfo(CORINFO_CLASS_HANDLE cls) +{ + jitInstance->mc->cr->AddCall("getClassThreadStaticDynamicInfo"); + return jitInstance->mc->repGetClassThreadStaticDynamicInfo(cls); +} + +size_t MyICJI::getClassStaticDynamicInfo(CORINFO_CLASS_HANDLE cls) +{ + jitInstance->mc->cr->AddCall("getClassStaticDynamicInfo"); + return jitInstance->mc->repGetClassStaticDynamicInfo(cls); +} + bool MyICJI::getIsClassInitedFlagAddress(CORINFO_CLASS_HANDLE cls, CORINFO_CONST_LOOKUP* addr, int* offset) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index f64623b232c70..5025660b2cefb 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -1173,14 +1173,60 @@ static CorInfoHelpFunc getGenericStaticsHelper(FieldDesc * pField) return (CorInfoHelpFunc)helper; } +size_t CEEInfo::getClassThreadStaticDynamicInfo(CORINFO_CLASS_HANDLE cls) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + size_t result; + + JIT_TO_EE_TRANSITION_LEAF(); + + TypeHandle clsTypeHandle(cls); + PTR_MethodTable pMT = clsTypeHandle.AsMethodTable(); + result = (size_t)pMT->GetThreadStaticsInfo(); + + EE_TO_JIT_TRANSITION_LEAF(); + + return result; +} + +size_t CEEInfo::getClassStaticDynamicInfo(CORINFO_CLASS_HANDLE cls) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + size_t result; + + JIT_TO_EE_TRANSITION_LEAF(); + + TypeHandle clsTypeHandle(cls); + PTR_MethodTable pMT = clsTypeHandle.AsMethodTable(); + result = (size_t)pMT->GetDynamicStaticsInfo(); + + EE_TO_JIT_TRANSITION_LEAF(); + + return result; +} + CorInfoHelpFunc CEEInfo::getSharedStaticsHelper(FieldDesc * pField, MethodTable * pFieldMT) { STANDARD_VM_CONTRACT; + pFieldMT->AttemptToPreinit(); bool GCStatic = (pField->GetFieldType() == ELEMENT_TYPE_CLASS || pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE); - bool noCtor = ((!pFieldMT->HasClassConstructor() && !pFieldMT->HasBoxedRegularStatics()) || pFieldMT->IsClassInited()); + bool noCtor = pFieldMT->IsClassInited(); bool threadStatic = pField->IsThreadStatic(); + bool isInexactMT = pFieldMT->IsSharedByGenericInstantiations(); + bool isCollectible = pFieldMT->Collectible(); + _ASSERTE(!isInexactMT); CorInfoHelpFunc helper; if (threadStatic) @@ -1188,16 +1234,16 @@ CorInfoHelpFunc CEEInfo::getSharedStaticsHelper(FieldDesc * pField, MethodTable if (GCStatic) { if (noCtor) - helper = CORINFO_HELP_GET_GCTHREADSTATIC_BASE_NOCTOR; + helper = CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR; else - helper = CORINFO_HELP_GET_GCTHREADSTATIC_BASE; + helper = CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE; } else { if (noCtor) - helper = CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE_NOCTOR; + helper = CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR; else - helper = CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE; + helper = CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE; } } else @@ -1205,16 +1251,36 @@ CorInfoHelpFunc CEEInfo::getSharedStaticsHelper(FieldDesc * pField, MethodTable if (GCStatic) { if (noCtor) - helper = CORINFO_HELP_GET_GCSTATIC_BASE_NOCTOR; + { + if (isCollectible) + helper = CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE_NOCTOR; + else + helper = CORINFO_HELP_GETPINNED_GCSTATIC_BASE_NOCTOR; + } else - helper = CORINFO_HELP_GET_GCSTATIC_BASE; + { + if (isCollectible) + helper = CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE; + else + helper = CORINFO_HELP_GETPINNED_GCSTATIC_BASE; + } } else { if (noCtor) - helper = CORINFO_HELP_GET_NONGCSTATIC_BASE_NOCTOR; + { + if (isCollectible) + helper = CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE_NOCTOR; + else + helper = CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE_NOCTOR; + } else - helper = CORINFO_HELP_GET_NONGCSTATIC_BASE; + { + if (isCollectible) + helper = CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE; + else + helper = CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE; + } } } diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 1cdad8b5e5b46..d3b0b3fe7c50d 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -3230,8 +3230,9 @@ static PCODE getHelperForStaticBase(Module * pModule, CORCOMPILE_FIXUP_BLOB_KIND { STANDARD_VM_CONTRACT; + pMT->AttemptToPreinit(); bool GCStatic = (kind == ENCODE_STATIC_BASE_GC_HELPER || kind == ENCODE_THREAD_STATIC_BASE_GC_HELPER); - bool noCtor = (!pMT->HasClassConstructor() && !pMT->HasBoxedRegularStatics()); + bool noCtor = pMT->IsClassInited(); bool threadStatic = (kind == ENCODE_THREAD_STATIC_BASE_NONGC_HELPER || kind == ENCODE_THREAD_STATIC_BASE_GC_HELPER); CorInfoHelpFunc helper; From c375996d5ff5fb047c186bedb7215ecb20517a03 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 1 Mar 2024 15:53:12 -0800 Subject: [PATCH 06/82] Re-enable the helper expansions for statics --- src/coreclr/jit/compiler.hpp | 2 ++ src/coreclr/jit/helperexpansion.cpp | 34 ++++-------------- src/coreclr/jit/importer.cpp | 5 +++ src/coreclr/utilcode/collections.cpp | 5 +++ src/coreclr/vm/jitinterface.cpp | 53 +++++++++++++++++++++------- src/coreclr/vm/threads.cpp | 4 ++- src/coreclr/vm/threadstatics.cpp | 2 ++ 7 files changed, 63 insertions(+), 42 deletions(-) diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 9b67dcd5988aa..b0be1a5032eda 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -3738,12 +3738,14 @@ inline bool Compiler::IsStaticHelperEligibleForExpansion(GenTree* tree, bool* is { case CORINFO_HELP_READYTORUN_GCSTATIC_BASE: case CORINFO_HELP_GET_GCSTATIC_BASE: + case CORINFO_HELP_GETPINNED_GCSTATIC_BASE: result = true; gc = true; retVal = SHRV_STATIC_BASE_PTR; break; case CORINFO_HELP_READYTORUN_NONGCSTATIC_BASE: case CORINFO_HELP_GET_NONGCSTATIC_BASE: + case CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE: result = true; gc = false; retVal = SHRV_STATIC_BASE_PTR; diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index f90119914bc08..11c2c44fdeb53 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -799,10 +799,6 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* { return false; } - - // TODO ... actually implement this stuff. - return false; - /* assert(!opts.IsReadyToRun()); if (TargetOS::IsUnix) @@ -828,24 +824,19 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* DISPTREE(call); JITDUMP("\n"); - bool isGCThreadStatic = - eeGetHelperNum(call->gtCallMethHnd) == CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED; - CORINFO_THREAD_STATIC_BLOCKS_INFO threadStaticBlocksInfo; memset(&threadStaticBlocksInfo, 0, sizeof(CORINFO_THREAD_STATIC_BLOCKS_INFO)); - info.compCompHnd->getThreadLocalStaticBlocksInfo(&threadStaticBlocksInfo, isGCThreadStatic); + info.compCompHnd->getThreadLocalStaticBlocksInfo(&threadStaticBlocksInfo); - JITDUMP("getThreadLocalStaticBlocksInfo (%s)\n:", isGCThreadStatic ? "GC" : "Non-GC"); + JITDUMP("getThreadLocalStaticBlocksInfo\n:"); JITDUMP("tlsIndex= %p\n", dspPtr(threadStaticBlocksInfo.tlsIndex.addr)); JITDUMP("tlsGetAddrFtnPtr= %p\n", dspPtr(threadStaticBlocksInfo.tlsGetAddrFtnPtr)); JITDUMP("tlsIndexObject= %p\n", dspPtr(threadStaticBlocksInfo.tlsIndexObject)); JITDUMP("threadVarsSection= %p\n", dspPtr(threadStaticBlocksInfo.threadVarsSection)); JITDUMP("offsetOfThreadLocalStoragePointer= %u\n", dspOffset(threadStaticBlocksInfo.offsetOfThreadLocalStoragePointer)); - JITDUMP("offsetOfMaxThreadStaticBlocks= %u\n", dspOffset(threadStaticBlocksInfo.offsetOfMaxThreadStaticBlocks)); JITDUMP("offsetOfThreadStaticBlocks= %u\n", dspOffset(threadStaticBlocksInfo.offsetOfThreadStaticBlocks)); - JITDUMP("offsetOfGCDataPointer= %u\n", dspOffset(threadStaticBlocksInfo.offsetOfGCDataPointer)); assert(call->gtArgs.CountArgs() == 1); @@ -996,8 +987,8 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* tlsValueDef = gtNewStoreLclVarNode(tlsLclNum, tlsValue); GenTree* tlsLclValueUse = gtNewLclVarNode(tlsLclNum); - size_t offsetOfThreadStaticBlocksVal = threadStaticBlocksInfo.offsetOfThreadStaticBlocks; - size_t offsetOfMaxThreadStaticBlocksVal = threadStaticBlocksInfo.offsetOfMaxThreadStaticBlocks; + size_t offsetOfThreadStaticBlocksVal = threadStaticBlocksInfo.offsetOfThreadStaticBlocks + TARGET_POINTER_SIZE; + size_t offsetOfMaxThreadStaticBlocksVal = threadStaticBlocksInfo.offsetOfThreadStaticBlocks; // Create tree for "maxThreadStaticBlocks = tls[offsetOfMaxThreadStaticBlocks]" GenTree* offsetOfMaxThreadStaticBlocks = gtNewIconNode(offsetOfMaxThreadStaticBlocksVal, TYP_I_IMPL); @@ -1016,10 +1007,8 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* maxThreadStaticBlocksCond = gtNewOperNode(GT_JTRUE, TYP_VOID, maxThreadStaticBlocksCond); // Create tree to "threadStaticBlockValue = threadStaticBlockBase[typeIndex]" - typeThreadStaticBlockIndexValue = gtNewOperNode(GT_MUL, TYP_INT, gtCloneExpr(typeThreadStaticBlockIndexValue), - gtNewIconNode(TARGET_POINTER_SIZE, TYP_INT)); GenTree* typeThreadStaticBlockRef = - gtNewOperNode(GT_ADD, TYP_I_IMPL, threadStaticBlocksValue, typeThreadStaticBlockIndexValue); + gtNewOperNode(GT_ADD, TYP_I_IMPL, threadStaticBlocksValue, gtCloneExpr(typeThreadStaticBlockIndexValue)); GenTree* typeThreadStaticBlockValue = gtNewIndir(TYP_I_IMPL, typeThreadStaticBlockRef, GTF_IND_NONFAULTING); // Cache the threadStaticBlock value @@ -1076,17 +1065,6 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* BasicBlock* fallbackBb = fgNewBBFromTreeAfter(BBJ_ALWAYS, threadStaticBlockNullCondBB, fallbackValueDef, debugInfo, true); - // fastPathBb - if (isGCThreadStatic) - { - // Need to add extra indirection to access the data pointer. - - threadStaticBlockBaseLclValueUse = gtNewIndir(callType, threadStaticBlockBaseLclValueUse, GTF_IND_NONFAULTING); - threadStaticBlockBaseLclValueUse = - gtNewOperNode(GT_ADD, callType, threadStaticBlockBaseLclValueUse, - gtNewIconNode(threadStaticBlocksInfo.offsetOfGCDataPointer, TYP_I_IMPL)); - } - GenTree* fastPathValueDef = gtNewStoreLclVarNode(threadStaticBlockLclNum, gtCloneExpr(threadStaticBlockBaseLclValueUse)); BasicBlock* fastPathBb = fgNewBBFromTreeAfter(BBJ_ALWAYS, fallbackBb, fastPathValueDef, debugInfo, true); @@ -1141,7 +1119,7 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* assert(BasicBlock::sameEHRegion(prevBb, threadStaticBlockNullCondBB)); assert(BasicBlock::sameEHRegion(prevBb, fastPathBb)); - return true;*/ + return true; } //------------------------------------------------------------------------------ diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 2150a65fa8a66..86a5916d39e4d 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -3815,6 +3815,11 @@ GenTree* Compiler::impImportStaticFieldAddress(CORINFO_RESOLVED_TOKEN* pResolved isHoistable = !s_helperCallProperties.MayRunCctor(pFieldInfo->helper) || (info.compCompHnd->getClassAttribs(pResolvedToken->hClass) & CORINFO_FLG_BEFOREFIELDINIT); op1 = gtNewHelperCallNode(pFieldInfo->helper, type, op1); + if (IsStaticHelperEligibleForExpansion(op1)) + { + // Mark the helper call with the initClsHnd so that rewriting it for expansion can reliably fail + op1->AsCall()->gtInitClsHnd = pResolvedToken->hClass; + } op1 = gtNewOperNode(GT_ADD, type, op1, gtNewIconNode(pFieldInfo->offset, innerFldSeq)); } break; diff --git a/src/coreclr/utilcode/collections.cpp b/src/coreclr/utilcode/collections.cpp index ed5271fde77f8..a0c699e426524 100644 --- a/src/coreclr/utilcode/collections.cpp +++ b/src/coreclr/utilcode/collections.cpp @@ -268,6 +268,11 @@ BYTE *CHashTable::FindNextEntry( // The next entry, or0 for end of list. if (psSrch->iNext != UINT32_MAX) { psEntry = EntryPtr(psSrch->iNext); +#if DACCESS_COMPILE + // If we have an infinite loop. Stop. + if (psEntry->iNext == psSrch->iNext) + return (0); +#endif psSrch->iNext = psEntry->iNext; return ((BYTE *) psEntry); } diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 5025660b2cefb..bca0725a3637a 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -1394,14 +1394,14 @@ uint32_t CEEInfo::getThreadLocalFieldInfo (CORINFO_FIELD_HANDLE field, bool isG void CEEInfo::getThreadLocalStaticBlocksInfo (CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) { CONTRACTL { - NOTHROW; - GC_NOTRIGGER; + THROWS; + GC_TRIGGERS; MODE_PREEMPTIVE; } CONTRACTL_END; - JIT_TO_EE_TRANSITION_LEAF(); + JIT_TO_EE_TRANSITION(); GetThreadLocalStaticBlocksInfo(pInfo); - EE_TO_JIT_TRANSITION_LEAF(); + EE_TO_JIT_TRANSITION(); } /*********************************************************************/ @@ -3649,18 +3649,28 @@ bool CEEInfo::getIsClassInitedFlagAddress(CORINFO_CLASS_HANDLE cls, CORINFO_CONS } CONTRACTL_END; _ASSERTE(addr); + bool result; JIT_TO_EE_TRANSITION_LEAF(); TypeHandle clsTypeHandle(cls); PTR_MethodTable pMT = clsTypeHandle.AsMethodTable(); - addr->addr = (UINT8*)pMT->getIsClassInitedFlagAddress(); - addr->accessType = IAT_VALUE; - *offset = 0; + if (pMT->IsSharedByGenericInstantiations()) + { + // If the MT is shared by generic instantiations, then we don't have an exact flag to check + result = false; + } + else + { + addr->addr = (UINT8*)pMT->getIsClassInitedFlagAddress(); + addr->accessType = IAT_VALUE; + *offset = 0; + result = true; + } EE_TO_JIT_TRANSITION_LEAF(); - return true; + return result; } /*********************************************************************/ @@ -3672,18 +3682,30 @@ bool CEEInfo::getStaticBaseAddress(CORINFO_CLASS_HANDLE cls, bool isGc, CORINFO_ MODE_PREEMPTIVE; } CONTRACTL_END; + bool result; + JIT_TO_EE_TRANSITION_LEAF(); TypeHandle clsTypeHandle(cls); PTR_MethodTable pMT = clsTypeHandle.AsMethodTable(); - GCX_COOP(); - addr->addr = isGc ? pMT->GetGCStaticsBasePointer() : pMT->GetNonGCStaticsBasePointer(); - addr->accessType = IAT_VALUE; + if (pMT->IsSharedByGenericInstantiations()) + { + // If the MT is shared by generic instantiations, then we don't have an exact flag to check + result = false; + } + else + { + GCX_COOP(); + pMT->EnsureStaticDataAllocated(); + addr->addr = isGc ? pMT->GetGCStaticsBasePointer() : pMT->GetNonGCStaticsBasePointer(); + addr->accessType = IAT_VALUE; + result = true; + } EE_TO_JIT_TRANSITION_LEAF(); - return true; + return result; } /*********************************************************************/ @@ -5988,7 +6010,12 @@ CorInfoHelpFunc CEEInfo::getSharedCCtorHelper(CORINFO_CLASS_HANDLE clsHnd) CorInfoHelpFunc result; if (VMClsHnd.GetMethodTable()->IsDynamicStatics()) - result = CORINFO_HELP_GET_NONGCSTATIC_BASE; + { + if (VMClsHnd.GetMethodTable()->GetClass()->GetNonGCRegularStaticFieldBytes() > 0) + result = CORINFO_HELP_GET_NONGCSTATIC_BASE; + else + result = CORINFO_HELP_GET_GCSTATIC_BASE; + } else result = CORINFO_HELP_INITCLASS; diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index b0d6cb05150f8..aa5bd315cdc5e 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -7733,6 +7733,7 @@ void Thread::DeleteThreadStaticData(bool forCurrentThread) CONTRACTL { NOTHROW; GC_NOTRIGGER; + MODE_COOPERATIVE; } CONTRACTL_END; @@ -8310,7 +8311,8 @@ Thread::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) m_ExceptionState.EnumChainMemoryRegions(flags); - EnumThreadMemoryRegions(GetThreadLocalDataPtr(), flags); + if (GetThreadLocalDataPtr() != NULL) + EnumThreadMemoryRegions(GetThreadLocalDataPtr(), flags); if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE) { diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index a87ff5d183a31..d1fc1feea8898 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -92,6 +92,8 @@ PTR_MethodTable LookupMethodTableAndFlagForThreadStatic(TLSIndex index, bool *pI void ScanThreadStaticRoots(PTR_ThreadLocalData pThreadLocalData, promote_func* fn, ScanContext* sc) { + if (pThreadLocalData == NULL) + return; PTR_InFlightTLSData pInFlightData = pThreadLocalData->pInFlightData; while (pInFlightData != NULL) { From 5ec61b29007b22137bfd560d5221bab9e30624ff Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 1 Mar 2024 16:01:19 -0800 Subject: [PATCH 07/82] Attempt to implement for X64 unix. We'll see if I guessed right --- .../vm/amd64/jithelpers_singleappdomain.S | 29 +++++++------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/coreclr/vm/amd64/jithelpers_singleappdomain.S b/src/coreclr/vm/amd64/jithelpers_singleappdomain.S index 9ab88a4702aba..2b7a9ade2e149 100644 --- a/src/coreclr/vm/amd64/jithelpers_singleappdomain.S +++ b/src/coreclr/vm/amd64/jithelpers_singleappdomain.S @@ -10,39 +10,30 @@ // appdomain flag // -LEAF_ENTRY JIT_GetSharedNonGCStaticBase_SingleAppDomain, _TEXT +LEAF_ENTRY JIT_GetDynamicNonGCStaticBase_SingleAppDomain, _TEXT // If class is not initialized, bail to C++ helper - test byte ptr [rdi + OFFSETOF__DomainLocalModule__m_pDataBlob + rsi], 1 + test byte ptr [rdi + OFFSETOF__DynamicStaticsInfo__m_AuxData__m_dwFlags], 1 jz CallHelper - mov rax, rdi + mov rax, [rdi + OFFSETOF__DynamicStaticsInfo__m_pNonGCStatics] rep ret .balign 16 CallHelper: // Tail call JIT_GetSharedNonGCStaticBase_Helper - jmp C_FUNC(JIT_GetSharedNonGCStaticBase_Helper) -LEAF_END_MARKED JIT_GetSharedNonGCStaticBase_SingleAppDomain, _TEXT + jmp C_FUNC(JIT_GetDynamicNonGCStaticBase_Portable) +LEAF_END_MARKED JIT_GetDynamicNonGCStaticBase_SingleAppDomain, _TEXT -LEAF_ENTRY JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT - mov rax, rdi - ret -LEAF_END JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT - -LEAF_ENTRY JIT_GetSharedGCStaticBase_SingleAppDomain, _TEXT +LEAF_ENTRY JIT_GetDynamicGCStaticBase_SingleAppDomain, _TEXT // If class is not initialized, bail to C++ helper - test byte ptr [rdi + OFFSETOF__DomainLocalModule__m_pDataBlob + rsi], 1 + test byte ptr [rdi + OFFSETOF__DynamicStaticsInfo__m_AuxData__m_dwFlags], 1 jz CallHelper1 - mov rax, [rdi + OFFSETOF__DomainLocalModule__m_pGCStatics] + mov rax, [rdi + OFFSETOF__DynamicStaticsInfo__m_pGCStatics] rep ret .balign 16 CallHelper1: // Tail call Jit_GetSharedGCStaticBase_Helper - jmp C_FUNC(JIT_GetSharedGCStaticBase_Helper) -LEAF_END JIT_GetSharedGCStaticBase_SingleAppDomain, _TEXT + jmp C_FUNC(JIT_GetDynamicGCStaticBase_Portable) +LEAF_END JIT_GetDynamicGCStaticBase_SingleAppDomain, _TEXT -LEAF_ENTRY JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT - mov rax, [rdi + OFFSETOF__DomainLocalModule__m_pGCStatics] - ret -LEAF_END JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT From e88cda8098467bb5e7b1df51bb22b6bbd2716cdb Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 5 Mar 2024 11:17:14 -0800 Subject: [PATCH 08/82] Windows Arm64 working, and further tweaks --- .../vm/amd64/JitHelpers_SingleAppDomain.asm | 10 +-- src/coreclr/vm/amd64/asmconstants.h | 4 - src/coreclr/vm/appdomain.cpp | 12 +-- src/coreclr/vm/appdomain.hpp | 8 +- src/coreclr/vm/arm64/asmconstants.h | 10 ++- src/coreclr/vm/arm64/asmhelpers.asm | 58 +++++--------- src/coreclr/vm/arm64/cgencpu.h | 6 +- src/coreclr/vm/jithelpers.cpp | 58 ++++++++++---- src/coreclr/vm/jitinterface.cpp | 20 +---- src/coreclr/vm/jitinterface.h | 2 +- src/coreclr/vm/loaderallocator.cpp | 8 +- src/coreclr/vm/loaderallocator.hpp | 4 +- src/coreclr/vm/methodtable.cpp | 10 +-- src/coreclr/vm/methodtable.h | 75 ++++++++++++++++++- src/coreclr/vm/methodtable.inl | 4 +- src/coreclr/vm/threadstatics.cpp | 74 +++++++----------- src/coreclr/vm/threadstatics.h | 32 +++++++- 17 files changed, 230 insertions(+), 165 deletions(-) diff --git a/src/coreclr/vm/amd64/JitHelpers_SingleAppDomain.asm b/src/coreclr/vm/amd64/JitHelpers_SingleAppDomain.asm index 5f9b58dc2b17a..280bdcd12217e 100644 --- a/src/coreclr/vm/amd64/JitHelpers_SingleAppDomain.asm +++ b/src/coreclr/vm/amd64/JitHelpers_SingleAppDomain.asm @@ -16,9 +16,9 @@ extern JIT_GetDynamicGCStaticBase_Portable:proc LEAF_ENTRY JIT_GetDynamicNonGCStaticBase_SingleAppDomain, _TEXT ; If class is not initialized, bail to C++ helper - test byte ptr [rcx + OFFSETOF__DynamicStaticsInfo__m_AuxData__m_dwFlags], 1 - jz CallHelper mov rax, [rcx + OFFSETOF__DynamicStaticsInfo__m_pNonGCStatics] + test rax, 1 + jnz CallHelper REPRET align 16 @@ -29,9 +29,9 @@ LEAF_END JIT_GetDynamicNonGCStaticBase_SingleAppDomain, _TEXT LEAF_ENTRY JIT_GetDynamicGCStaticBase_SingleAppDomain, _TEXT ; If class is not initialized, bail to C++ helper - test byte ptr [rcx + OFFSETOF__DynamicStaticsInfo__m_AuxData__m_dwFlags], 1 - jz CallHelper - mov rax, [rcx + OFFSETOF__DynamicStaticsInfo__m_pGCStatics] + mov rax, [rcx + OFFSETOF__DynamicStaticsInfo__m_pGCStatics] + test rax, 1 + jnz CallHelper REPRET align 16 diff --git a/src/coreclr/vm/amd64/asmconstants.h b/src/coreclr/vm/amd64/asmconstants.h index 6dc007a6a638e..27d8996d0ec13 100644 --- a/src/coreclr/vm/amd64/asmconstants.h +++ b/src/coreclr/vm/amd64/asmconstants.h @@ -202,10 +202,6 @@ ASMCONSTANTS_C_ASSERT(OFFSETOF__InterfaceInfo_t__m_pMethodTable ASMCONSTANTS_C_ASSERT(SIZEOF__InterfaceInfo_t == sizeof(InterfaceInfo_t)); -#define OFFSETOF__DynamicStaticsInfo__m_AuxData__m_dwFlags 0x18 -ASMCONSTANTS_C_ASSERT(OFFSETOF__DynamicStaticsInfo__m_AuxData__m_dwFlags - == offsetof(DynamicStaticsInfo, m_AuxData) + offsetof(MethodTableAuxiliaryData, m_dwFlags)); - ASMCONSTANTS_C_ASSERT(MethodTableAuxiliaryData::enum_flag_Initialized == 0x1); #define OFFSETOF__DynamicStaticsInfo__m_pNonGCStatics 0x8 diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index 16fadc93cfc5b..0c8930743b8ed 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -785,7 +785,7 @@ void AppDomain::SetNativeDllSearchDirectories(LPCWSTR wszNativeDllSearchDirector } } -OBJECTREF* BaseDomain::AllocateObjRefPtrsInLargeTable(int nRequested, OBJECTREF** ppLazyAllocate, MethodTable *pMTToFillWithStaticBoxes) +OBJECTREF* BaseDomain::AllocateObjRefPtrsInLargeTable(int nRequested, DynamicStaticsInfo* pStaticsInfo, MethodTable *pMTToFillWithStaticBoxes) { CONTRACTL { @@ -797,10 +797,10 @@ OBJECTREF* BaseDomain::AllocateObjRefPtrsInLargeTable(int nRequested, OBJECTREF* } CONTRACTL_END; - if (ppLazyAllocate && *ppLazyAllocate) + if (pStaticsInfo && pStaticsInfo->GetGCStaticsPointer() != NULL) { // Allocation already happened - return *ppLazyAllocate; + return pStaticsInfo->GetGCStaticsPointer(); } GCX_COOP(); @@ -817,15 +817,15 @@ OBJECTREF* BaseDomain::AllocateObjRefPtrsInLargeTable(int nRequested, OBJECTREF* pMTToFillWithStaticBoxes->AllocateRegularStaticBoxes(&result); GCPROTECT_END(); } - if (ppLazyAllocate) + if (pStaticsInfo) { // race with other threads that might be doing the same concurrent allocation - if (InterlockedCompareExchangeT(ppLazyAllocate, result, NULL) != NULL) + if (!pStaticsInfo->InterlockedUpdateStaticsPointer(/*isGCPointer*/ true, (TADDR)result)) { // we lost the race, release our handles and use the handles from the // winning thread m_pPinnedHeapHandleTable->ReleaseHandles(result, nRequested); - result = *ppLazyAllocate; + result = pStaticsInfo->GetGCStaticsPointer(); } } diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp index 71a9c95b73455..1221779053dfd 100644 --- a/src/coreclr/vm/appdomain.hpp +++ b/src/coreclr/vm/appdomain.hpp @@ -582,9 +582,9 @@ class BaseDomain // Returns an array of OBJECTREF* that can be used to store domain specific data. // Statics and reflection info (Types, MemberInfo,..) are stored this way - // If ppLazyAllocate != 0, allocation will only take place if *ppLazyAllocate != 0 (and the allocation + // If pStaticsInfo != 0, allocation will only take place if GC statics in the DynamicStaticsInfo are NULL (and the allocation // will be properly serialized) - OBJECTREF *AllocateObjRefPtrsInLargeTable(int nRequested, OBJECTREF** ppLazyAllocate = NULL, MethodTable *pMTToFillWithStaticBoxes = NULL); + OBJECTREF *AllocateObjRefPtrsInLargeTable(int nRequested, DynamicStaticsInfo* pStaticsInfo = NULL, MethodTable *pMTToFillWithStaticBoxes = NULL); //**************************************************************************************** // Handles @@ -1505,11 +1505,11 @@ class AppDomain : public BaseDomain #endif // DEBUGGING_SUPPORTED #ifndef DACCESS_COMPILE - OBJECTREF* AllocateStaticFieldObjRefPtrs(int nRequested, OBJECTREF** ppLazyAllocate = NULL) + OBJECTREF* AllocateStaticFieldObjRefPtrs(int nRequested) { WRAPPER_NO_CONTRACT; - return AllocateObjRefPtrsInLargeTable(nRequested, ppLazyAllocate); + return AllocateObjRefPtrsInLargeTable(nRequested); } #endif // DACCESS_COMPILE diff --git a/src/coreclr/vm/arm64/asmconstants.h b/src/coreclr/vm/arm64/asmconstants.h index cc19728d9e29e..3a66449914d80 100644 --- a/src/coreclr/vm/arm64/asmconstants.h +++ b/src/coreclr/vm/arm64/asmconstants.h @@ -188,11 +188,13 @@ ASMCONSTANTS_C_ASSERT(MethodDesc_ALIGNMENT_SHIFT == MethodDesc::ALIGNMENT_SHIFT) ASMCONSTANTS_C_ASSERT(ResolveCacheElem__target == offsetof(ResolveCacheElem, target)); ASMCONSTANTS_C_ASSERT(ResolveCacheElem__pNext == offsetof(ResolveCacheElem, pNext)); -#define DomainLocalModule__m_pDataBlob 0x30 -#define DomainLocalModule__m_pGCStatics 0x20 -ASMCONSTANTS_C_ASSERT(DomainLocalModule__m_pDataBlob == offsetof(DomainLocalModule, m_pDataBlob)); -ASMCONSTANTS_C_ASSERT(DomainLocalModule__m_pGCStatics == offsetof(DomainLocalModule, m_pGCStatics)); +#define OFFSETOF__DynamicStaticsInfo__m_pNonGCStatics 0x8 +ASMCONSTANTS_C_ASSERT(OFFSETOF__DynamicStaticsInfo__m_pNonGCStatics + == offsetof(DynamicStaticsInfo, m_pNonGCStatics)); +#define OFFSETOF__DynamicStaticsInfo__m_pGCStatics 0 +ASMCONSTANTS_C_ASSERT(OFFSETOF__DynamicStaticsInfo__m_pGCStatics + == offsetof(DynamicStaticsInfo, m_pGCStatics)); // For JIT_PInvokeBegin and JIT_PInvokeEnd helpers #define Frame__m_Next 0x08 diff --git a/src/coreclr/vm/arm64/asmhelpers.asm b/src/coreclr/vm/arm64/asmhelpers.asm index bc88d15ee330f..bcbbe76383859 100644 --- a/src/coreclr/vm/arm64/asmhelpers.asm +++ b/src/coreclr/vm/arm64/asmhelpers.asm @@ -46,8 +46,8 @@ IMPORT $g_GCShadowEnd #endif // WRITE_BARRIER_CHECK - IMPORT JIT_GetSharedNonGCStaticBase_Helper - IMPORT JIT_GetSharedGCStaticBase_Helper + IMPORT JIT_GetDynamicNonGCStaticBase_Portable + IMPORT JIT_GetDynamicGCStaticBase_Portable #ifdef FEATURE_COMINTEROP IMPORT CLRToCOMWorker @@ -1018,59 +1018,35 @@ Fail ; ; ------------------------------------------------------------------ -; void* JIT_GetSharedNonGCStaticBase(SIZE_T moduleDomainID, DWORD dwClassDomainID) - LEAF_ENTRY JIT_GetSharedNonGCStaticBase_SingleAppDomain - ; If class is not initialized, bail to C++ helper - add x2, x0, #DomainLocalModule__m_pDataBlob - ldrb w2, [x2, w1] - tst w2, #1 - beq CallHelper1 +; void* JIT_GetDynamicNonGCStaticBase(DynamicStaticsInfo *dynamicInfo) + LEAF_ENTRY JIT_GetDynamicNonGCStaticBase_SingleAppDomain + ; If class is not initialized, bail to C++ helper + ldr x1, [x0, #OFFSETOF__DynamicStaticsInfo__m_pNonGCStatics] + tbnz x1, #0, CallHelper1 + mov x0, x1 ret lr CallHelper1 - ; Tail call JIT_GetSharedNonGCStaticBase_Helper - b JIT_GetSharedNonGCStaticBase_Helper + ; Tail call JIT_GetDynamicNonGCStaticBase_Portable + b JIT_GetDynamicNonGCStaticBase_Portable LEAF_END +; void* JIT_GetDynamicGCStaticBase(DynamicStaticsInfo *dynamicInfo) -; ------------------------------------------------------------------ -; void* JIT_GetSharedNonGCStaticBaseNoCtor(SIZE_T moduleDomainID, DWORD dwClassDomainID) - - LEAF_ENTRY JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain - ret lr - LEAF_END - - -; ------------------------------------------------------------------ -; void* JIT_GetSharedGCStaticBase(SIZE_T moduleDomainID, DWORD dwClassDomainID) - - LEAF_ENTRY JIT_GetSharedGCStaticBase_SingleAppDomain + LEAF_ENTRY JIT_GetDynamicGCStaticBase_SingleAppDomain ; If class is not initialized, bail to C++ helper - add x2, x0, #DomainLocalModule__m_pDataBlob - ldrb w2, [x2, w1] - tst w2, #1 - beq CallHelper2 - - ldr x0, [x0, #DomainLocalModule__m_pGCStatics] + ldr x1, [x0, #OFFSETOF__DynamicStaticsInfo__m_pGCStatics] + tbnz x1, #0, CallHelper2 + mov x0, x1 ret lr CallHelper2 - ; Tail call Jit_GetSharedGCStaticBase_Helper - b JIT_GetSharedGCStaticBase_Helper + ; Tail call JIT_GetDynamicGCStaticBase_Portable + b JIT_GetDynamicGCStaticBase_Portable LEAF_END - -; ------------------------------------------------------------------ -; void* JIT_GetSharedGCStaticBaseNoCtor(SIZE_T moduleDomainID, DWORD dwClassDomainID) - - LEAF_ENTRY JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain - ldr x0, [x0, #DomainLocalModule__m_pGCStatics] - ret lr - LEAF_END - - ; ------------------------------------------------------------------ ; __declspec(naked) void F_CALL_CONV JIT_WriteBarrier_Callable(Object **dst, Object* val) LEAF_ENTRY JIT_WriteBarrier_Callable diff --git a/src/coreclr/vm/arm64/cgencpu.h b/src/coreclr/vm/arm64/cgencpu.h index 3ec3d6ea3b1a1..47e20db986704 100644 --- a/src/coreclr/vm/arm64/cgencpu.h +++ b/src/coreclr/vm/arm64/cgencpu.h @@ -109,10 +109,8 @@ inline unsigned StackElemSize(unsigned parmSize, bool isValueType, bool isFloatH // // Create alias for optimized implementations of helpers provided on this platform // -#define JIT_GetSharedGCStaticBase JIT_GetSharedGCStaticBase_SingleAppDomain -#define JIT_GetSharedNonGCStaticBase JIT_GetSharedNonGCStaticBase_SingleAppDomain -#define JIT_GetSharedGCStaticBaseNoCtor JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain -#define JIT_GetSharedNonGCStaticBaseNoCtor JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain +#define JIT_GetDynamicGCStaticBase JIT_GetDynamicGCStaticBase_SingleAppDomain +#define JIT_GetDynamicNonGCStaticBase JIT_GetDynamicNonGCStaticBase_SingleAppDomain //********************************************************************** // Frames diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 8d99b90d51959..d694dcd684329 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1378,9 +1378,10 @@ HCIMPL1(void*, JIT_GetNonGCStaticBase_Portable, MethodTable* pMT) { FCALL_CONTRACT; - if (pMT->IsClassInited()) + PTR_BYTE pBase; + if (pMT->GetDynamicStaticsInfo()->GetIsInitedAndNonGCStaticsPointer(&pBase)) { - return pMT->GetDynamicStaticsInfo()->m_pNonGCStatics; + return pBase; } // Tailcall to the slow helper @@ -1394,9 +1395,10 @@ HCIMPL1(void*, JIT_GetDynamicNonGCStaticBase_Portable, DynamicStaticsInfo* pStat { FCALL_CONTRACT; - if (pStaticsInfo->IsClassInited()) + PTR_BYTE pBase; + if (pStaticsInfo->GetIsInitedAndNonGCStaticsPointer(&pBase)) { - return pStaticsInfo->m_pNonGCStatics; + return pBase; } // Tailcall to the slow helper @@ -1410,7 +1412,7 @@ HCIMPL1(void*, JIT_GetNonGCStaticBaseNoCtor_Portable, MethodTable* pMT) { FCALL_CONTRACT; - return pMT->GetDynamicStaticsInfo()->m_pNonGCStatics; + return pMT->GetDynamicStaticsInfo()->GetNonGCStaticsPointerAssumeIsInited(); } HCIMPLEND @@ -1420,7 +1422,7 @@ HCIMPL1(void*, JIT_GetDynamicNonGCStaticBaseNoCtor_Portable, DynamicStaticsInfo* { FCALL_CONTRACT; - return pDynamicStaticsInfo->m_pNonGCStatics; + return pDynamicStaticsInfo->GetNonGCStaticsPointerAssumeIsInited(); } HCIMPLEND @@ -1428,9 +1430,10 @@ HCIMPL1(void*, JIT_GetGCStaticBase_Portable, MethodTable* pMT) { FCALL_CONTRACT; - if (pMT->IsClassInited()) + PTR_OBJECTREF pBase; + if (pMT->GetDynamicStaticsInfo()->GetIsInitedAndGCStaticsPointer(&pBase)) { - return pMT->GetDynamicStaticsInfo()->m_pGCStatics; + return pBase; } // Tailcall to the slow helper @@ -1443,9 +1446,10 @@ HCIMPL1(void*, JIT_GetDynamicGCStaticBase_Portable, DynamicStaticsInfo* pStatics { FCALL_CONTRACT; - if (pStaticsInfo->IsClassInited()) + PTR_OBJECTREF pBase; + if (pStaticsInfo->GetIsInitedAndGCStaticsPointer(&pBase)) { - return pStaticsInfo->m_pGCStatics; + return pBase; } // Tailcall to the slow helper @@ -1460,7 +1464,7 @@ HCIMPL1(void*, JIT_GetGCStaticBaseNoCtor_Portable, MethodTable* pMT) { FCALL_CONTRACT; - return pMT->GetDynamicStaticsInfo()->m_pGCStatics; + return pMT->GetDynamicStaticsInfo()->GetGCStaticsPointerAssumeIsInited(); } HCIMPLEND @@ -1470,7 +1474,7 @@ HCIMPL1(void*, JIT_GetDynamicGCStaticBaseNoCtor_Portable, DynamicStaticsInfo* pD { FCALL_CONTRACT; - return pDynamicStaticsInfo->m_pGCStatics; + return pDynamicStaticsInfo->GetGCStaticsPointerAssumeIsInited(); } HCIMPLEND @@ -1489,7 +1493,7 @@ HCIMPL1(void*, JIT_GetNonGCStaticBase_Helper, MethodTable* pMT) pMT->CheckRunClassInitThrowing(); HELPER_METHOD_FRAME_END(); - return (void*)pMT->GetDynamicStaticsInfo()->m_pNonGCStatics; + return (void*)pMT->GetDynamicStaticsInfo()->GetNonGCStaticsPointer(); } HCIMPLEND @@ -1503,7 +1507,7 @@ HCIMPL1(void*, JIT_GetGCStaticBase_Helper, MethodTable* pMT) pMT->CheckRunClassInitThrowing(); HELPER_METHOD_FRAME_END(); - return (void*)pMT->GetDynamicStaticsInfo()->m_pGCStatics; + return (void*)pMT->GetDynamicStaticsInfo()->GetGCStaticsPointer(); } HCIMPLEND @@ -1513,6 +1517,32 @@ HCIMPLEND // //======================================================================== +// Define the t_ThreadStatics variable here, so that these helpers can use +// the most optimal TLS access pattern for the platform when inlining the +// GetThreadLocalStaticBaseIfExistsAndInitialized function +#ifdef _MSC_VER +__declspec(thread) ThreadLocalData t_ThreadStatics; +#else +__thread ThreadLocalData t_ThreadStatics; +#endif // _MSC_VER + +// This is the routine used by the JIT helpers for the fast path. It is not used by the JIT for the slow path, or by the EE for any path. +// This is inlined in the header to improve code gen quality +FORCEINLINE void* GetThreadLocalStaticBaseIfExistsAndInitialized(TLSIndex index) +{ + LIMITED_METHOD_CONTRACT; + TADDR pTLSBaseAddress = NULL; + + int32_t cTLSData = t_ThreadStatics.cTLSData; + if (cTLSData < index.GetByteIndex()) + { + return NULL; + } + + TADDR pTLSArrayData = t_ThreadStatics.pTLSArrayData; + pTLSBaseAddress = *reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetByteIndex()); + return reinterpret_cast(pTLSBaseAddress); +} // *** These framed helpers get called if allocation needs to occur or // if the class constructor needs to run diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index bca0725a3637a..a1f094229c30d 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -1508,24 +1508,7 @@ void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken, fieldAccessor = CORINFO_FIELD_STATIC_SHARED_STATIC_HELPER; pResult->helper = getSharedStaticsHelper(pField, pFieldMT); -#if defined(TARGET_ARM) - // Optimization is disabled for linux/windows arm -#elif !defined(TARGET_WINDOWS) && defined(TARGET_X86) - // Optimization is disabled for linux/x86 -#elif defined(TARGET_LINUX_MUSL) && defined(TARGET_ARM64) - // Optimization is disabled for linux musl arm64 -#elif defined(TARGET_FREEBSD) && defined(TARGET_ARM64) - // Optimization is disabled for FreeBSD/arm64 -#else - bool optimizeThreadStaticAccess = true; -#if !defined(TARGET_OSX) && defined(TARGET_UNIX) && defined(TARGET_AMD64) - // For linux/x64, check if compiled coreclr as .so file and not single file. - // For single file, the `tls_index` might not be accurate. - // Do not perform this optimization in such case. - optimizeThreadStaticAccess = GetTlsIndexObjectAddress() != nullptr; -#endif // !TARGET_OSX && TARGET_UNIX && TARGET_AMD64 - - if (optimizeThreadStaticAccess) + if (CanJITOptimizeTLSAccess()) { // For windows x64/x86/arm64, linux x64/arm64/loongarch64/riscv64: // We convert the TLS access to the optimized helper where we will store @@ -1547,7 +1530,6 @@ void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken, pResult->helper = CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED; } } -#endif // TARGET_ARM } else { diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index 437ded0921236..699b4034fc507 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -186,7 +186,7 @@ EXTERN_C FCDECL1(void*, JIT_GetDynamicGCStaticBase, DynamicStaticsInfo* pStatics EXTERN_C FCDECL1(void*, JIT_GetDynamicGCStaticBase_Portable, DynamicStaticsInfo* pStaticsInfo); #ifndef JIT_GetDynamicNonGCStaticBase -#define JIT_GetDynamicNonGCStaticBase JIT_GeDynamictNonGCStaticBase_Portable +#define JIT_GetDynamicNonGCStaticBase JIT_GetDynamictNonGCStaticBase_Portable #endif EXTERN_C FCDECL1(void*, JIT_GetDynamicNonGCStaticBase, DynamicStaticsInfo* pStaticsInfo); EXTERN_C FCDECL1(void*, JIT_GetDynamicNonGCStaticBase_Portable, DynamicStaticsInfo* pStaticsInfo); diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index 13e28152d45c3..f3f659df776d5 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -2199,7 +2199,7 @@ PTR_OnStackReplacementManager LoaderAllocator::GetOnStackReplacementManager() #endif // FEATURE_ON_STACK_REPLACEMENT #ifndef DACCESS_COMPILE -void LoaderAllocator::AllocateBytesForStaticVariables(uint8_t** ppbMem, uint32_t cbMem) +void LoaderAllocator::AllocateBytesForStaticVariables(DynamicStaticsInfo* pStaticsInfo, uint32_t cbMem) { CONTRACTL { @@ -2212,11 +2212,11 @@ void LoaderAllocator::AllocateBytesForStaticVariables(uint8_t** ppbMem, uint32_t if (cbMem > 0) { uint8_t* pbMem = (uint8_t*)(void*)GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbMem)); - InterlockedCompareExchangeT(ppbMem, pbMem, NULL); + pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */false, (TADDR)pbMem); } } -void LoaderAllocator::AllocateGCHandlesBytesForStaticVariables(PTR_OBJECTREF* ppbObjectMem, uint32_t cSlots, MethodTable* pMTWithStaticBoxes) +void LoaderAllocator::AllocateGCHandlesBytesForStaticVariables(DynamicStaticsInfo* pStaticsInfo, uint32_t cSlots, MethodTable* pMTWithStaticBoxes) { CONTRACTL { @@ -2228,7 +2228,7 @@ void LoaderAllocator::AllocateGCHandlesBytesForStaticVariables(PTR_OBJECTREF* pp if (cSlots > 0) { - GetDomain()->AllocateObjRefPtrsInLargeTable(cSlots, (OBJECTREF**)ppbObjectMem, pMTWithStaticBoxes); + GetDomain()->AllocateObjRefPtrsInLargeTable(cSlots, pStaticsInfo, pMTWithStaticBoxes); } } #endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/loaderallocator.hpp b/src/coreclr/vm/loaderallocator.hpp index 630476935ffc1..a9eb485f7cf48 100644 --- a/src/coreclr/vm/loaderallocator.hpp +++ b/src/coreclr/vm/loaderallocator.hpp @@ -730,8 +730,8 @@ class LoaderAllocator LIMITED_METHOD_CONTRACT; return m_nGCCount; } - void AllocateBytesForStaticVariables(uint8_t** ppbMem, uint32_t cbMem); - void AllocateGCHandlesBytesForStaticVariables(PTR_OBJECTREF* ppbObjectMem, uint32_t cSlots, MethodTable* pMTWithStaticBoxes); + void AllocateBytesForStaticVariables(DynamicStaticsInfo* pStaticsInfo, uint32_t cbMem); + void AllocateGCHandlesBytesForStaticVariables(DynamicStaticsInfo* pStaticsInfo, uint32_t cSlots, MethodTable* pMTWithStaticBoxes); static BOOL Destroy(QCall::LoaderAllocatorHandle pLoaderAllocator); diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 30c16c5058d5d..1313c75a77191 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -667,7 +667,7 @@ void MethodTable::AllocateAuxiliaryData(LoaderAllocator *pAllocator, Module *pLo if (HasFlag(staticsFlags, MethodTableStaticsFlags::Present)) { - MethodTableAuxiliaryData::GetDynamicStaticsInfo(pMTAuxiliaryData)->m_pMethodTable = this; + MethodTableAuxiliaryData::GetDynamicStaticsInfo(pMTAuxiliaryData)->Init(this); } } @@ -4387,11 +4387,11 @@ void MethodTable::EnsureStaticDataAllocated() { DynamicStaticsInfo *pDynamicStaticsInfo = GetDynamicStaticsInfo(); // Allocate space for normal statics if we might have them - if (pDynamicStaticsInfo->m_pNonGCStatics == NULL) - GetLoaderAllocator()->AllocateBytesForStaticVariables(&pDynamicStaticsInfo->m_pNonGCStatics, GetClass()->GetNonGCRegularStaticFieldBytes()); + if (pDynamicStaticsInfo->GetNonGCStaticsPointer() == NULL) + GetLoaderAllocator()->AllocateBytesForStaticVariables(pDynamicStaticsInfo, GetClass()->GetNonGCRegularStaticFieldBytes()); - if (pDynamicStaticsInfo->m_pGCStatics == NULL) - GetLoaderAllocator()->AllocateGCHandlesBytesForStaticVariables(&pDynamicStaticsInfo->m_pGCStatics, GetClass()->GetNumHandleRegularStatics(), this); + if (pDynamicStaticsInfo->GetGCStaticsPointer() == NULL) + GetLoaderAllocator()->AllocateGCHandlesBytesForStaticVariables(pDynamicStaticsInfo, GetClass()->GetNumHandleRegularStatics(), this); } pAuxiliaryData->SetIsStaticDataAllocated(); } diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 42cee3fb4dca6..3a4796cb52721 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -530,11 +530,76 @@ struct MethodTableAuxiliaryData // when looking for statics pointers struct DynamicStaticsInfo { - PTR_OBJECTREF m_pGCStatics; - PTR_BYTE m_pNonGCStatics; +private: + // The detail of whether or not the class has been initialized is stored in the statics pointers as well as in + // its normal flag location. This is done so that when getting the statics base for a class, we can get the statics + // base address and check to see if it is initialized without needing a barrier between reading the flag and reading + // the static field address. + static constexpr TADDR ISCLASSNOTINITED = 1; + static constexpr TADDR ISCLASSNOTINITEDMASK = ISCLASSNOTINITED; + static constexpr TADDR STATICSPOINTERMASK = ~ISCLASSNOTINITEDMASK; + + void InterlockedSetClassInited(bool isGC) + { + TADDR oldVal; + TADDR oldValFromInterlockedOp; + TADDR *pAddr = isGC ? &m_pGCStatics : &m_pNonGCStatics; + do + { + oldVal = VolatileLoadWithoutBarrier(pAddr); + // Mask off the ISCLASSNOTINITED bit + oldValFromInterlockedOp = InterlockedCompareExchangeT(pAddr, oldVal & STATICSPOINTERMASK, oldVal); + } while(oldValFromInterlockedOp != oldVal); // We can loop if we happened to allocate the statics pointer in the middle of this operation + } + +public: + TADDR m_pGCStatics; // Always access through helper methods to properly handle the ISCLASSNOTINITED bit + TADDR m_pNonGCStatics; // Always access through helper methods to properly handle the ISCLASSNOTINITED bit PTR_MethodTable m_pMethodTable; MethodTableAuxiliaryData m_AuxData; - bool IsClassInited() const { return m_AuxData.IsClassInited(); } + PTR_OBJECTREF GetGCStaticsPointer() { TADDR staticsVal = VolatileLoadWithoutBarrier(&m_pGCStatics); return dac_cast(staticsVal & STATICSPOINTERMASK); } + PTR_BYTE GetNonGCStaticsPointer() { TADDR staticsVal = VolatileLoadWithoutBarrier(&m_pNonGCStatics); return dac_cast(staticsVal & STATICSPOINTERMASK); } + PTR_OBJECTREF GetGCStaticsPointerAssumeIsInited() { TADDR staticsVal = m_pGCStatics; _ASSERTE(staticsVal != 0); _ASSERTE(staticsVal & ISCLASSNOTINITEDMASK == 0); return dac_cast(staticsVal); } + PTR_BYTE GetNonGCStaticsPointerAssumeIsInited() { TADDR staticsVal = m_pNonGCStatics; _ASSERTE(staticsVal != 0); _ASSERTE(staticsVal & ISCLASSNOTINITEDMASK == 0); return dac_cast(staticsVal); } + bool GetIsInitedAndGCStaticsPointer(PTR_OBJECTREF *ptrResult) { TADDR staticsVal = VolatileLoadWithoutBarrier(&m_pGCStatics); *ptrResult = dac_cast(ISCLASSNOTINITEDMASK); return !(staticsVal & ISCLASSNOTINITED); } + bool GetIsInitedAndNonGCStaticsPointer(PTR_BYTE *ptrResult) { TADDR staticsVal = VolatileLoadWithoutBarrier(&m_pNonGCStatics); *ptrResult = dac_cast(ISCLASSNOTINITEDMASK); return !(staticsVal & ISCLASSNOTINITED); } + + // This function sets the pointer portion of a statics pointer. It returns false if the statics value was already set. + bool InterlockedUpdateStaticsPointer(bool isGC, TADDR newVal) + { + TADDR oldVal; + TADDR oldValFromInterlockedOp; + TADDR *pAddr = isGC ? &m_pGCStatics : &m_pNonGCStatics; + do + { + oldVal = VolatileLoad(pAddr); + + // Check to see if statics value has already been set + if ((oldVal & STATICSPOINTERMASK) != 0) + { + // If it has, then we don't need to do anything + return false; + } + + oldValFromInterlockedOp = InterlockedCompareExchangeT(pAddr, newVal | oldVal, oldVal); + } while(oldValFromInterlockedOp != oldVal); + return true; + } + void SetClassInited() + { + InterlockedSetClassInited(true); + InterlockedSetClassInited(false); + } + +#ifndef DACCESS_COMPILE + void Init(MethodTable* pMT) + { + m_pGCStatics = ISCLASSNOTINITED; + m_pNonGCStatics = ISCLASSNOTINITED; + m_pMethodTable = pMT; + } +#endif + PTR_MethodTable GetMethodTable() const { return m_pMethodTable; } }; @@ -966,6 +1031,10 @@ class MethodTable void SetClassInited() { GetAuxiliaryDataForWrite()->SetClassInited(); + if (IsDynamicStatics()) + { + GetDynamicStaticsInfo()->SetClassInited(); + } } void AttemptToPreinit(); diff --git a/src/coreclr/vm/methodtable.inl b/src/coreclr/vm/methodtable.inl index 894ee6a3c1883..b8b894676e9d6 100644 --- a/src/coreclr/vm/methodtable.inl +++ b/src/coreclr/vm/methodtable.inl @@ -1089,7 +1089,7 @@ inline PTR_BYTE MethodTable::GetNonGCStaticsBasePointer() } else { - return GetDynamicStaticsInfo()->m_pNonGCStatics; + return GetDynamicStaticsInfo()->GetNonGCStaticsPointer(); } } @@ -1103,7 +1103,7 @@ inline PTR_BYTE MethodTable::GetGCStaticsBasePointer() } else { - return (PTR_BYTE)GetDynamicStaticsInfo()->m_pGCStatics; + return (PTR_BYTE)GetDynamicStaticsInfo()->GetGCStaticsPointer(); } } diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index d1fc1feea8898..a1d2ecc7e8f74 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -3,16 +3,6 @@ #include "common.h" #include "threadstatics.h" -struct TLSArray -{ - int32_t cTLSData; // Size in bytes of offset into the TLS array which is valid - TADDR pTLSArrayData; // Points at the Thread local array data. -}; -typedef DPTR(TLSArray) PTR_TLSArray; - -// Used to store access to TLS data for a single index when the TLS is accessed while the class constructor is running -struct InFlightTLSData; -typedef DPTR(InFlightTLSData) PTR_InFlightTLSData; struct InFlightTLSData { #ifndef DACCESS_COMPILE @@ -31,13 +21,6 @@ struct ThreadLocalLoaderAllocator }; typedef DPTR(ThreadLocalLoaderAllocator) PTR_ThreadLocalLoaderAllocator; -struct ThreadLocalData -{ - TLSArray tlsArray; // TLS data - Thread *pThread; - PTR_InFlightTLSData pInFlightData; // Points at the in-flight TLS data (TLS data that exists before the class constructor finishes running) -}; - // This can be used for out of thread access to TLS data. Since that isn't safe in general, we only support it for DAC. PTR_VOID GetThreadLocalStaticBaseNoCreate(PTR_ThreadLocalData pThreadLocalData, TLSIndex index) { @@ -100,8 +83,8 @@ void ScanThreadStaticRoots(PTR_ThreadLocalData pThreadLocalData, promote_func* f fn(dac_cast(pInFlightData->pTLSData), sc, 0 /* could be GC_CALL_INTERIOR or GC_CALL_PINNED */); pInFlightData = pInFlightData->pNext; } - PTR_BYTE pTLSArrayData = dac_cast(pThreadLocalData->tlsArray.pTLSArrayData); - int32_t cTLSData = pThreadLocalData->tlsArray.cTLSData; + PTR_BYTE pTLSArrayData = dac_cast(pThreadLocalData->pTLSArrayData); + int32_t cTLSData = pThreadLocalData->cTLSData; for (int32_t i = 0; i < cTLSData; i += sizeof(TADDR)) { TLSIndex index(i); @@ -120,28 +103,6 @@ void ScanThreadStaticRoots(PTR_ThreadLocalData pThreadLocalData, promote_func* f } #ifndef DACCESS_COMPILE -#ifdef _MSC_VER -__declspec(thread) ThreadLocalData t_ThreadStatics; -#else -__thread ThreadLocalData t_ThreadStatics; -#endif // _MSC_VER - -void* GetThreadLocalStaticBaseIfExistsAndInitialized(TLSIndex index) -{ - LIMITED_METHOD_CONTRACT; - TADDR pTLSBaseAddress = NULL; - TLSArray* pTLSArray = reinterpret_cast((uint8_t*)&t_ThreadStatics + index.GetTLSArrayOffset()); - - int32_t cTLSData = pTLSArray->cTLSData; - if (cTLSData < index.GetByteIndex()) - { - return NULL; - } - - TADDR pTLSArrayData = pTLSArray->pTLSArrayData; - pTLSBaseAddress = *reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetByteIndex()); - return reinterpret_cast(pTLSBaseAddress); -} uint32_t g_NextTLSSlot = (uint32_t)sizeof(TADDR); CrstStatic g_TLSCrst; @@ -199,9 +160,9 @@ void AllocateThreadStaticBoxes(MethodTable *pMT, PTRARRAYREF *ppRef) void FreeCurrentThreadStaticData() { - delete[] (uint8_t*)t_ThreadStatics.tlsArray.pTLSArrayData; + delete[] (uint8_t*)t_ThreadStatics.pTLSArrayData; - t_ThreadStatics.tlsArray.pTLSArrayData = 0; + t_ThreadStatics.pTLSArrayData = 0; while (t_ThreadStatics.pInFlightData != NULL) { @@ -282,7 +243,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) GCPROTECT_BEGIN(gc); if (isGCStatic) { - gc.ptrRef = AllocateObjectArray(pMT->GetClass()->GetNumHandleThreadStatics(), g_pObjectClass); + gc.ptrRef = (PTRARRAYREF)AllocateObjectArray(pMT->GetClass()->GetNumHandleThreadStatics(), g_pObjectClass); if (pMT->HasBoxedThreadStatics()) { AllocateThreadStaticBoxes(pMT, &gc.ptrRef); @@ -332,6 +293,29 @@ void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pInde *pIndex = TLSIndex(tlsRawIndex); } +bool CanJITOptimizeTLSAccess() +{ + bool optimizeThreadStaticAccess = false; +#if defined(TARGET_ARM) + // Optimization is disabled for linux/windows arm +#elif !defined(TARGET_WINDOWS) && defined(TARGET_X86) + // Optimization is disabled for linux/x86 +#elif defined(TARGET_LINUX_MUSL) && defined(TARGET_ARM64) + // Optimization is disabled for linux musl arm64 +#elif defined(TARGET_FREEBSD) && defined(TARGET_ARM64) + // Optimization is disabled for FreeBSD/arm64 +#else + optimizeThreadStaticAccess = true; +#if !defined(TARGET_OSX) && defined(TARGET_UNIX) && defined(TARGET_AMD64) + // For linux/x64, check if compiled coreclr as .so file and not single file. + // For single file, the `tls_index` might not be accurate. + // Do not perform this optimization in such case. + optimizeThreadStaticAccess = GetTlsIndexObjectAddress() != nullptr; +#endif // !TARGET_OSX && TARGET_UNIX && TARGET_AMD64 +#endif + return optimizeThreadStaticAccess; +} + #if defined(TARGET_WINDOWS) EXTERN_C uint32_t _tls_index; /*********************************************************************/ @@ -459,7 +443,7 @@ void EnumThreadMemoryRegions(PTR_ThreadLocalData pThreadLocalData, CLRDataEnumMe { SUPPORTS_DAC; DacEnumMemoryRegion(dac_cast(pThreadLocalData), sizeof(ThreadLocalData), flags); - DacEnumMemoryRegion(dac_cast(pThreadLocalData->tlsArray.pTLSArrayData), pThreadLocalData->tlsArray.cTLSData, flags); + DacEnumMemoryRegion(dac_cast(pThreadLocalData->pTLSArrayData), pThreadLocalData->cTLSData, flags); PTR_InFlightTLSData pInFlightData = pThreadLocalData->pInFlightData; while (pInFlightData != NULL) { diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index 0d462a3372907..d53d227daf175 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -88,6 +88,8 @@ #ifndef __THREADLOCALSTORAGE_H__ #define __THREADLOCALSTORAGE_H__ +class Thread; + struct TLSIndex { TLSIndex(uint32_t rawIndex) : TLSIndexRawIndex(rawIndex) { } @@ -99,9 +101,35 @@ struct TLSIndex bool operator != (TLSIndex index) { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex != index.TLSIndexRawIndex; } }; -struct ThreadLocalData; +struct TLSArray +{ + int32_t cTLSData; // Size in bytes of offset into the TLS array which is valid + TADDR pTLSArrayData; // Points at the Thread local array data. +}; +typedef DPTR(TLSArray) PTR_TLSArray; + +// Used to store access to TLS data for a single index when the TLS is accessed while the class constructor is running +struct InFlightTLSData; +typedef DPTR(InFlightTLSData) PTR_InFlightTLSData; + +struct ThreadLocalData +{ + int32_t cTLSData; // Size in bytes of offset into the TLS array which is valid + TADDR pTLSArrayData; // Points at the Thread local array data. + Thread *pThread; + PTR_InFlightTLSData pInFlightData; // Points at the in-flight TLS data (TLS data that exists before the class constructor finishes running) +}; + typedef DPTR(ThreadLocalData) PTR_ThreadLocalData; +#ifndef DACCESS_COMPILE +#ifdef _MSC_VER +extern __declspec(thread) ThreadLocalData t_ThreadStatics; +#else +extern __thread ThreadLocalData t_ThreadStatics; +#endif // _MSC_VER +#endif // DACCESS_COMPILE + template struct LookupMap; @@ -120,8 +148,8 @@ void FreeCurrentThreadStaticData(); void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pIndex); void FreeTLSIndexForThreadStatic(TLSIndex index); void* GetThreadLocalStaticBase(TLSIndex index); -void* GetThreadLocalStaticBaseIfExistsAndInitialized(TLSIndex index); void GetThreadLocalStaticBlocksInfo (CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo); +bool CanJITOptimizeTLSAccess(); #else void EnumThreadMemoryRegions(PTR_ThreadLocalData pThreadLocalData, CLRDataEnumMemoryFlags flags); #endif From 2eb6eb8026dfe6eb83a682ed6eedb2d3d3343063 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 5 Mar 2024 12:20:54 -0800 Subject: [PATCH 09/82] Enable X86 --- src/coreclr/inc/jithelpers.h | 31 +++---- src/coreclr/vm/i386/jitinterfacex86.cpp | 117 ------------------------ src/coreclr/vm/jithelpers.cpp | 8 +- src/coreclr/vm/jitinterface.h | 2 +- src/coreclr/vm/methodtable.h | 4 +- 5 files changed, 19 insertions(+), 143 deletions(-) diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h index c159965a850c8..f479c61663b02 100644 --- a/src/coreclr/inc/jithelpers.h +++ b/src/coreclr/inc/jithelpers.h @@ -178,25 +178,18 @@ JITHELPER(CORINFO_HELP_GETSTATICFIELDADDR, JIT_GetStaticFieldAddr,CORINFO_HELP_SIG_REG_ONLY) JITHELPER(CORINFO_HELP_GETSTATICFIELDADDR_TLS, NULL, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) -#ifdef TARGET_X86 - DYNAMICJITHELPER(CORINFO_HELP_GETSHARED_GCSTATIC_BASE, NULL, CORINFO_HELP_SIG_REG_ONLY) - DYNAMICJITHELPER(CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE, NULL, CORINFO_HELP_SIG_REG_ONLY) - DYNAMICJITHELPER(CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR, NULL, CORINFO_HELP_SIG_REG_ONLY) - DYNAMICJITHELPER(CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR,NULL, CORINFO_HELP_SIG_REG_ONLY) -#else - DYNAMICJITHELPER(CORINFO_HELP_GET_GCSTATIC_BASE, JIT_GetGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) - DYNAMICJITHELPER(CORINFO_HELP_GET_NONGCSTATIC_BASE, JIT_GetNonGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) - DYNAMICJITHELPER(CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE, JIT_GetDynamicGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) - DYNAMICJITHELPER(CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE, JIT_GetDynamicNonGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) - DYNAMICJITHELPER(CORINFO_HELP_GETPINNED_GCSTATIC_BASE, JIT_GetDynamicGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) - DYNAMICJITHELPER(CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE, JIT_GetDynamicNonGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) - DYNAMICJITHELPER(CORINFO_HELP_GET_GCSTATIC_BASE_NOCTOR, JIT_GetGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) - DYNAMICJITHELPER(CORINFO_HELP_GET_NONGCSTATIC_BASE_NOCTOR, JIT_GetNonGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) - DYNAMICJITHELPER(CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE_NOCTOR, JIT_GetDynamicGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) - DYNAMICJITHELPER(CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE_NOCTOR,JIT_GetDynamicNonGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) - DYNAMICJITHELPER(CORINFO_HELP_GETPINNED_GCSTATIC_BASE_NOCTOR, JIT_GetDynamicGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) - DYNAMICJITHELPER(CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE_NOCTOR,JIT_GetDynamicNonGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) -#endif + JITHELPER(CORINFO_HELP_GET_GCSTATIC_BASE, JIT_GetGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + JITHELPER(CORINFO_HELP_GET_NONGCSTATIC_BASE, JIT_GetNonGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + JITHELPER(CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE, JIT_GetDynamicGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + JITHELPER(CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE, JIT_GetDynamicNonGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + JITHELPER(CORINFO_HELP_GETPINNED_GCSTATIC_BASE, JIT_GetDynamicGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + JITHELPER(CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE, JIT_GetDynamicNonGCStaticBase, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + JITHELPER(CORINFO_HELP_GET_GCSTATIC_BASE_NOCTOR, JIT_GetGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + JITHELPER(CORINFO_HELP_GET_NONGCSTATIC_BASE_NOCTOR, JIT_GetNonGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + JITHELPER(CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE_NOCTOR, JIT_GetDynamicGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + JITHELPER(CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE_NOCTOR,JIT_GetDynamicNonGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + JITHELPER(CORINFO_HELP_GETPINNED_GCSTATIC_BASE_NOCTOR, JIT_GetDynamicGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) + JITHELPER(CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE_NOCTOR,JIT_GetDynamicNonGCStaticBaseNoCtor, CORINFO_HELP_SIG_CANNOT_USE_ALIGN_STUB) // Thread statics JITHELPER(CORINFO_HELP_GET_GCTHREADSTATIC_BASE, JIT_GetGCThreadStaticBase,CORINFO_HELP_SIG_REG_ONLY) diff --git a/src/coreclr/vm/i386/jitinterfacex86.cpp b/src/coreclr/vm/i386/jitinterfacex86.cpp index 08360e9ff0c06..20dae63a3f8a8 100644 --- a/src/coreclr/vm/i386/jitinterfacex86.cpp +++ b/src/coreclr/vm/i386/jitinterfacex86.cpp @@ -790,113 +790,6 @@ void *JIT_TrialAlloc::GenAllocString(Flags flags) return (void *)pStub->GetEntryPoint(); } -// For this helper, -// If bCCtorCheck == true -// ECX contains the domain neutral module ID -// EDX contains the class domain ID, and the -// else -// ECX contains the domain neutral module ID -// EDX is junk -// shared static base is returned in EAX. - -// "init" should be the address of a routine which takes an argument of -// the module domain ID, the class domain ID, and returns the static base pointer -void EmitFastGetSharedStaticBase(CPUSTUBLINKER *psl, CodeLabel *init, bool bCCtorCheck, bool bGCStatic) -{ - STANDARD_VM_CONTRACT; - - CodeLabel *DoInit = 0; - if (bCCtorCheck) - { - DoInit = psl->NewCodeLabel(); - } - - // mov eax, ecx - psl->Emit8(0x89); - psl->Emit8(0xc8); - - if (bCCtorCheck) - { - // test [eax + edx + offsetof(DomainLocalModule, m_pDataBlob], ClassInitFlags::INITIALIZED_FLAG // Is class inited - _ASSERTE(FitsInI1(ClassInitFlags::INITIALIZED_FLAG)); - _ASSERTE(FitsInI1(DomainLocalModule::GetOffsetOfDataBlob())); - - BYTE testClassInit[] = { 0xF6, 0x44, 0x10, - (BYTE) DomainLocalModule::GetOffsetOfDataBlob(), (BYTE)ClassInitFlags::INITIALIZED_FLAG }; - - psl->EmitBytes(testClassInit, sizeof(testClassInit)); - - // jz init // no, init it - psl->X86EmitCondJump(DoInit, X86CondCode::kJZ); - } - - if (bGCStatic) - { - // Indirect to get the pointer to the first GC Static - psl->X86EmitIndexRegLoad(kEAX, kEAX, (__int32) DomainLocalModule::GetOffsetOfGCStaticPointer()); - } - - // ret - psl->X86EmitReturn(0); - - if (bCCtorCheck) - { - // DoInit: - psl->EmitLabel(DoInit); - - psl->X86EmitPushEBPframe(); - -#ifdef UNIX_X86_ABI -#define STACK_ALIGN_PADDING 4 - // sub esp, STACK_ALIGN_PADDING; to align the stack - psl->X86EmitSubEsp(STACK_ALIGN_PADDING); -#endif // UNIX_X86_ABI - - // push edx (must be preserved) - psl->X86EmitPushReg(kEDX); - - // call init - psl->X86EmitCall(init, 0); - - // pop edx - psl->X86EmitPopReg(kEDX); - -#ifdef UNIX_X86_ABI - // add esp, STACK_ALIGN_PADDING - psl->X86EmitAddEsp(STACK_ALIGN_PADDING); -#undef STACK_ALIGN_PADDING -#endif // UNIX_X86_ABI - - psl->X86EmitPopReg(kEBP); - - // ret - psl->X86EmitReturn(0); - } - -} - -void *GenFastGetSharedStaticBase(bool bCheckCCtor, bool bGCStatic) -{ - STANDARD_VM_CONTRACT; - - CPUSTUBLINKER sl; - - CodeLabel *init; - if (bGCStatic) - { - init = sl.NewExternalCodeLabel((LPVOID)JIT_GetSharedGCStaticBase); - } - else - { - init = sl.NewExternalCodeLabel((LPVOID)JIT_GetSharedNonGCStaticBase); - } - - EmitFastGetSharedStaticBase(&sl, init, bCheckCCtor, bGCStatic); - - Stub *pStub = sl.Link(SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()); - - return (void*) pStub->GetEntryPoint(); -} #define NUM_WRITE_BARRIERS 6 @@ -1015,16 +908,6 @@ void InitJITHelpers1() ECall::DynamicallyAssignFCallImpl((PCODE) JIT_TrialAlloc::GenAllocString(flags), ECall::FastAllocateString); } - // Replace static helpers with faster assembly versions - pMethodAddresses[6] = GenFastGetSharedStaticBase(true, true); - SetJitHelperFunction(CORINFO_HELP_GETSHARED_GCSTATIC_BASE, pMethodAddresses[6]); - pMethodAddresses[7] = GenFastGetSharedStaticBase(true, false); - SetJitHelperFunction(CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE, pMethodAddresses[7]); - pMethodAddresses[8] = GenFastGetSharedStaticBase(false, true); - SetJitHelperFunction(CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR, pMethodAddresses[8]); - pMethodAddresses[9] = GenFastGetSharedStaticBase(false, false); - SetJitHelperFunction(CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR, pMethodAddresses[9]); - ETW::MethodLog::StubsInitialized(pMethodAddresses, (PVOID *)pHelperNames, ETW_NUM_JIT_HELPERS); // All write barrier helpers should fit into one page. diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index d694dcd684329..1b3346f06956f 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1379,7 +1379,7 @@ HCIMPL1(void*, JIT_GetNonGCStaticBase_Portable, MethodTable* pMT) FCALL_CONTRACT; PTR_BYTE pBase; - if (pMT->GetDynamicStaticsInfo()->GetIsInitedAndNonGCStaticsPointer(&pBase)) + if (pMT->GetDynamicStaticsInfo()->GetIsInitedAndNonGCStaticsPointerIfInited(&pBase)) { return pBase; } @@ -1396,7 +1396,7 @@ HCIMPL1(void*, JIT_GetDynamicNonGCStaticBase_Portable, DynamicStaticsInfo* pStat FCALL_CONTRACT; PTR_BYTE pBase; - if (pStaticsInfo->GetIsInitedAndNonGCStaticsPointer(&pBase)) + if (pStaticsInfo->GetIsInitedAndNonGCStaticsPointerIfInited(&pBase)) { return pBase; } @@ -1431,7 +1431,7 @@ HCIMPL1(void*, JIT_GetGCStaticBase_Portable, MethodTable* pMT) FCALL_CONTRACT; PTR_OBJECTREF pBase; - if (pMT->GetDynamicStaticsInfo()->GetIsInitedAndGCStaticsPointer(&pBase)) + if (pMT->GetDynamicStaticsInfo()->GetIsInitedAndGCStaticsPointerIfInited(&pBase)) { return pBase; } @@ -1447,7 +1447,7 @@ HCIMPL1(void*, JIT_GetDynamicGCStaticBase_Portable, DynamicStaticsInfo* pStatics FCALL_CONTRACT; PTR_OBJECTREF pBase; - if (pStaticsInfo->GetIsInitedAndGCStaticsPointer(&pBase)) + if (pStaticsInfo->GetIsInitedAndGCStaticsPointerIfInited(&pBase)) { return pBase; } diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index 699b4034fc507..d8da390166be3 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -186,7 +186,7 @@ EXTERN_C FCDECL1(void*, JIT_GetDynamicGCStaticBase, DynamicStaticsInfo* pStatics EXTERN_C FCDECL1(void*, JIT_GetDynamicGCStaticBase_Portable, DynamicStaticsInfo* pStaticsInfo); #ifndef JIT_GetDynamicNonGCStaticBase -#define JIT_GetDynamicNonGCStaticBase JIT_GetDynamictNonGCStaticBase_Portable +#define JIT_GetDynamicNonGCStaticBase JIT_GetDynamicNonGCStaticBase_Portable #endif EXTERN_C FCDECL1(void*, JIT_GetDynamicNonGCStaticBase, DynamicStaticsInfo* pStaticsInfo); EXTERN_C FCDECL1(void*, JIT_GetDynamicNonGCStaticBase_Portable, DynamicStaticsInfo* pStaticsInfo); diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 3a4796cb52721..431d8ed352dda 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -561,8 +561,8 @@ struct DynamicStaticsInfo PTR_BYTE GetNonGCStaticsPointer() { TADDR staticsVal = VolatileLoadWithoutBarrier(&m_pNonGCStatics); return dac_cast(staticsVal & STATICSPOINTERMASK); } PTR_OBJECTREF GetGCStaticsPointerAssumeIsInited() { TADDR staticsVal = m_pGCStatics; _ASSERTE(staticsVal != 0); _ASSERTE(staticsVal & ISCLASSNOTINITEDMASK == 0); return dac_cast(staticsVal); } PTR_BYTE GetNonGCStaticsPointerAssumeIsInited() { TADDR staticsVal = m_pNonGCStatics; _ASSERTE(staticsVal != 0); _ASSERTE(staticsVal & ISCLASSNOTINITEDMASK == 0); return dac_cast(staticsVal); } - bool GetIsInitedAndGCStaticsPointer(PTR_OBJECTREF *ptrResult) { TADDR staticsVal = VolatileLoadWithoutBarrier(&m_pGCStatics); *ptrResult = dac_cast(ISCLASSNOTINITEDMASK); return !(staticsVal & ISCLASSNOTINITED); } - bool GetIsInitedAndNonGCStaticsPointer(PTR_BYTE *ptrResult) { TADDR staticsVal = VolatileLoadWithoutBarrier(&m_pNonGCStatics); *ptrResult = dac_cast(ISCLASSNOTINITEDMASK); return !(staticsVal & ISCLASSNOTINITED); } + bool GetIsInitedAndGCStaticsPointerIfInited(PTR_OBJECTREF *ptrResult) { TADDR staticsVal = VolatileLoadWithoutBarrier(&m_pGCStatics); *ptrResult = dac_cast(staticsVal); return !(staticsVal & ISCLASSNOTINITED); } + bool GetIsInitedAndNonGCStaticsPointerIfInited(PTR_BYTE *ptrResult) { TADDR staticsVal = VolatileLoadWithoutBarrier(&m_pNonGCStatics); *ptrResult = dac_cast(staticsVal); return !(staticsVal & ISCLASSNOTINITED); } // This function sets the pointer portion of a statics pointer. It returns false if the statics value was already set. bool InterlockedUpdateStaticsPointer(bool isGC, TADDR newVal) From aa741bc7fb34d18a0dc8d0cb38ebf1e006d6ab4b Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 5 Mar 2024 12:29:15 -0800 Subject: [PATCH 10/82] Update X64 and Arm64 Unix helpers to new sequence --- .../vm/amd64/jithelpers_singleappdomain.S | 9 ++- src/coreclr/vm/arm64/asmhelpers.S | 62 ++++++------------- 2 files changed, 23 insertions(+), 48 deletions(-) diff --git a/src/coreclr/vm/amd64/jithelpers_singleappdomain.S b/src/coreclr/vm/amd64/jithelpers_singleappdomain.S index 2b7a9ade2e149..080f80c29eb14 100644 --- a/src/coreclr/vm/amd64/jithelpers_singleappdomain.S +++ b/src/coreclr/vm/amd64/jithelpers_singleappdomain.S @@ -12,9 +12,9 @@ LEAF_ENTRY JIT_GetDynamicNonGCStaticBase_SingleAppDomain, _TEXT // If class is not initialized, bail to C++ helper - test byte ptr [rdi + OFFSETOF__DynamicStaticsInfo__m_AuxData__m_dwFlags], 1 - jz CallHelper mov rax, [rdi + OFFSETOF__DynamicStaticsInfo__m_pNonGCStatics] + test rax, 1 + jnz CallHelper rep ret .balign 16 @@ -25,10 +25,9 @@ LEAF_END_MARKED JIT_GetDynamicNonGCStaticBase_SingleAppDomain, _TEXT LEAF_ENTRY JIT_GetDynamicGCStaticBase_SingleAppDomain, _TEXT // If class is not initialized, bail to C++ helper - test byte ptr [rdi + OFFSETOF__DynamicStaticsInfo__m_AuxData__m_dwFlags], 1 - jz CallHelper1 - mov rax, [rdi + OFFSETOF__DynamicStaticsInfo__m_pGCStatics] + test rax, 1 + jnz CallHelper1 rep ret .balign 16 diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S index 0edbb3fdf92fc..fcb7d2d5308e3 100644 --- a/src/coreclr/vm/arm64/asmhelpers.S +++ b/src/coreclr/vm/arm64/asmhelpers.S @@ -652,58 +652,34 @@ DynamicHelper DynamicHelperFrameFlags_ObjectArg | DynamicHelperFrameFlags_Object // // ------------------------------------------------------------------ -// void* JIT_GetSharedNonGCStaticBase(SIZE_T moduleDomainID, DWORD dwClassDomainID) +// void* JIT_GetDynamicNonGCStaticBase(DynamicStaticsInfo* pStaticsInfo) -LEAF_ENTRY JIT_GetSharedNonGCStaticBase_SingleAppDomain, _TEXT - // If class is not initialized, bail to C++ helper - add x2, x0, #DomainLocalModule__m_pDataBlob - ldrb w2, [x2, w1, UXTW] - tst w2, #1 - beq LOCAL_LABEL(JIT_GetSharedNonGCStaticBase_SingleAppDomain_CallHelper) - - ret lr - -LOCAL_LABEL(JIT_GetSharedNonGCStaticBase_SingleAppDomain_CallHelper): - // Tail call JIT_GetSharedNonGCStaticBase_Helper - b C_FUNC(JIT_GetSharedNonGCStaticBase_Helper) -LEAF_END JIT_GetSharedNonGCStaticBase_SingleAppDomain, _TEXT - - -// ------------------------------------------------------------------ -// void* JIT_GetSharedNonGCStaticBaseNoCtor(SIZE_T moduleDomainID, DWORD dwClassDomainID) - -LEAF_ENTRY JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT +LEAF_ENTRY JIT_GetDynamicNonGCStaticBase_SingleAppDomain, _TEXT + ; If class is not initialized, bail to C++ helper + ldr x1, [x0, #OFFSETOF__DynamicStaticsInfo__m_pNonGCStatics] + tbnz x1, #0, LOCAL_LABEL(JIT_GetDynamicNonGCStaticBase_SingleAppDomain_CallHelper) + mov x0, x1 ret lr -LEAF_END JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT +LOCAL_LABEL(JIT_GetDynamicNonGCStaticBase_SingleAppDomain_CallHelper): + // Tail call JIT_GetDynamicNonGCStaticBase_Portable + b C_FUNC(JIT_GetDynamicNonGCStaticBase_Portable) +LEAF_END JIT_GetDynamicNonGCStaticBase_SingleAppDomain, _TEXT // ------------------------------------------------------------------ -// void* JIT_GetSharedGCStaticBase(SIZE_T moduleDomainID, DWORD dwClassDomainID) +// void* JIT_GetDynamicGCStaticBase(DynamicStaticsInfo* pStaticsInfo) -LEAF_ENTRY JIT_GetSharedGCStaticBase_SingleAppDomain, _TEXT +LEAF_ENTRY JIT_GetDynamicGCStaticBase_SingleAppDomain, _TEXT // If class is not initialized, bail to C++ helper - add x2, x0, #DomainLocalModule__m_pDataBlob - ldrb w2, [x2, w1, UXTW] - tst w2, #1 - beq LOCAL_LABEL(JIT_GetSharedGCStaticBase_SingleAppDomain_CallHelper) - - ldr x0, [x0, #DomainLocalModule__m_pGCStatics] - ret lr - -LOCAL_LABEL(JIT_GetSharedGCStaticBase_SingleAppDomain_CallHelper): - // Tail call Jit_GetSharedGCStaticBase_Helper - b C_FUNC(JIT_GetSharedGCStaticBase_Helper) -LEAF_END JIT_GetSharedGCStaticBase_SingleAppDomain, _TEXT - - -// ------------------------------------------------------------------ -// void* JIT_GetSharedGCStaticBaseNoCtor(SIZE_T moduleDomainID, DWORD dwClassDomainID) - -LEAF_ENTRY JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT - ldr x0, [x0, #DomainLocalModule__m_pGCStatics] + ldr x1, [x0, #OFFSETOF__DynamicStaticsInfo__m_pGCStatics] + tbnz x1, #0, LOCAL_LABEL(JIT_GetDynamicGCStaticBase_SingleAppDomain_CallHelper) + mov x0, x1 ret lr -LEAF_END JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT +LOCAL_LABEL(JIT_GetDynamicGCStaticBase_SingleAppDomain_CallHelper): + // Tail call JIT_GetDynamicGCStaticBase_Portable + b C_FUNC(JIT_GetDynamicGCStaticBase_Portable) +LEAF_END JIT_GetDynamicGCStaticBase_SingleAppDomain, _TEXT #ifdef PROFILING_SUPPORTED From f805eda12e91658fbb1f35e0b7a2723752b4d7d7 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 5 Mar 2024 14:50:02 -0800 Subject: [PATCH 11/82] Enable new model for Arm32 by removing all special cases for Arm32 --- src/coreclr/vm/arm/asmconstants.h | 7 ---- src/coreclr/vm/arm/asmhelpers.S | 65 ------------------------------- src/coreclr/vm/arm/cgencpu.h | 5 --- 3 files changed, 77 deletions(-) diff --git a/src/coreclr/vm/arm/asmconstants.h b/src/coreclr/vm/arm/asmconstants.h index 16931168e3ce0..ce7f22a2aeae9 100644 --- a/src/coreclr/vm/arm/asmconstants.h +++ b/src/coreclr/vm/arm/asmconstants.h @@ -149,13 +149,6 @@ ASMCONSTANTS_C_ASSERT(Thread__m_fPreemptiveGCDisabled == offsetof(Thread, m_fPre ASMCONSTANTS_C_ASSERT(Thread__m_pFrame == offsetof(Thread, m_pFrame)); #define Thread_m_pFrame Thread__m_pFrame -#define DomainLocalModule__m_pDataBlob 0x18 -ASMCONSTANTS_C_ASSERT(DomainLocalModule__m_pDataBlob == offsetof(DomainLocalModule, m_pDataBlob)); - -#define DomainLocalModule__m_pGCStatics 0x10 -ASMCONSTANTS_C_ASSERT(DomainLocalModule__m_pGCStatics == offsetof(DomainLocalModule, m_pGCStatics)); - - #define ASM__VTABLE_SLOTS_PER_CHUNK 8 ASMCONSTANTS_C_ASSERT(ASM__VTABLE_SLOTS_PER_CHUNK == VTABLE_SLOTS_PER_CHUNK) diff --git a/src/coreclr/vm/arm/asmhelpers.S b/src/coreclr/vm/arm/asmhelpers.S index 27a44b62c119b..81d92b7a107f0 100644 --- a/src/coreclr/vm/arm/asmhelpers.S +++ b/src/coreclr/vm/arm/asmhelpers.S @@ -586,71 +586,6 @@ LOCAL_LABEL(stackProbe_loop): NESTED_END JIT_RareDisableHelper, _TEXT -#ifdef FEATURE_CORECLR -// -// JIT Static access helpers for single appdomain case -// - -// ------------------------------------------------------------------ -// void* JIT_GetSharedNonGCStaticBase(SIZE_T moduleDomainID, DWORD dwClassDomainID) - - LEAF_ENTRY JIT_GetSharedNonGCStaticBase_SingleAppDomain, _TEXT - - // If class is not initialized, bail to C++ helper - add r2, r0, #DomainLocalModule__m_pDataBlob - ldrb r2, [r2, r1] - tst r2, #1 - beq LOCAL_LABEL(CallCppHelper1) - - bx lr - -LOCAL_LABEL(CallCppHelper1): - // Tail call JIT_GetSharedNonGCStaticBase_Helper - b C_FUNC(JIT_GetSharedNonGCStaticBase_Helper) - LEAF_END JIT_GetSharedNonGCStaticBase_SingleAppDomain, _TEXT - - -// ------------------------------------------------------------------ -// void* JIT_GetSharedNonGCStaticBaseNoCtor(SIZE_T moduleDomainID, DWORD dwClassDomainID) - - LEAF_ENTRY JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT - - bx lr - LEAF_END JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain, _TEXT - - -// ------------------------------------------------------------------ -// void* JIT_GetSharedGCStaticBase(SIZE_T moduleDomainID, DWORD dwClassDomainID) - - LEAF_ENTRY JIT_GetSharedGCStaticBase_SingleAppDomain, _TEXT - - // If class is not initialized, bail to C++ helper - add r2, r0, #DomainLocalModule__m_pDataBlob - ldrb r2, [r2, r1] - tst r2, #1 - beq LOCAL_LABEL(CallCppHelper3) - - ldr r0, [r0, #DomainLocalModule__m_pGCStatics] - bx lr - -LOCAL_LABEL(CallCppHelper3): - // Tail call Jit_GetSharedGCStaticBase_Helper - b C_FUNC(JIT_GetSharedGCStaticBase_Helper) - LEAF_END JIT_GetSharedGCStaticBase_SingleAppDomain, _TEXT - - -// ------------------------------------------------------------------ -// void* JIT_GetSharedGCStaticBaseNoCtor(SIZE_T moduleDomainID, DWORD dwClassDomainID) - - LEAF_ENTRY JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT - - ldr r0, [r0, #DomainLocalModule__m_pGCStatics] - bx lr - LEAF_END JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT - -#endif - - #define __wbScratch r3 #define pShadow r7 diff --git a/src/coreclr/vm/arm/cgencpu.h b/src/coreclr/vm/arm/cgencpu.h index f60822ccaa87c..317f7785ff2d3 100644 --- a/src/coreclr/vm/arm/cgencpu.h +++ b/src/coreclr/vm/arm/cgencpu.h @@ -1005,11 +1005,6 @@ inline BOOL ClrFlushInstructionCache(LPCVOID pCodeAddr, size_t sizeOfCode, bool // // Create alias for optimized implementations of helpers provided on this platform // -#define JIT_GetSharedGCStaticBase JIT_GetSharedGCStaticBase_SingleAppDomain -#define JIT_GetSharedNonGCStaticBase JIT_GetSharedNonGCStaticBase_SingleAppDomain -#define JIT_GetSharedGCStaticBaseNoCtor JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain -#define JIT_GetSharedNonGCStaticBaseNoCtor JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain - //------------------------------------------------------------------------ // From d463f59f58c7f039d52447723c5f255e837ddef1 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 5 Mar 2024 15:35:12 -0800 Subject: [PATCH 12/82] Remove DomainLocalBlock and StaticBoxInit crst types - GenericDictionaryExpansion re-used the DomainLocalBlck Crst type, so it now has a new one with the same Crst rules as it used to have --- src/coreclr/inc/CrstTypes.def | 14 ++--- src/coreclr/inc/crsttypes_generated.h | 81 +++++++++++++-------------- src/coreclr/vm/appdomain.cpp | 3 +- src/coreclr/vm/appdomain.hpp | 27 +-------- src/coreclr/vm/ceeload.cpp | 1 - src/coreclr/vm/ceeload.h | 3 - src/coreclr/vm/genericdict.cpp | 14 ++--- src/coreclr/vm/jitinterface.cpp | 9 +-- src/coreclr/vm/methodtable.cpp | 26 +++------ src/coreclr/vm/methodtable.h | 4 +- 10 files changed, 66 insertions(+), 116 deletions(-) diff --git a/src/coreclr/inc/CrstTypes.def b/src/coreclr/inc/CrstTypes.def index d3930a8d214d3..276e880ccc9d0 100644 --- a/src/coreclr/inc/CrstTypes.def +++ b/src/coreclr/inc/CrstTypes.def @@ -185,7 +185,7 @@ End Crst DelegateToFPtrHash End -Crst DomainLocalBlock +Crst GenericDictionaryExpansion AcquiredBefore PinnedHeapHandleTable IbcProfile LoaderHeap SystemDomainDelayedUnloadList UniqueStack End @@ -265,7 +265,7 @@ Crst InstMethodHashTable End Crst Interop - AcquiredBefore PinnedHeapHandleTable AvailableParamTypes ClassInit DeadlockDetection DomainLocalBlock + AcquiredBefore PinnedHeapHandleTable AvailableParamTypes ClassInit DeadlockDetection GenericDictionaryExpansion HandleTable InstMethodHashTable InteropData JitGenericHandleCache LoaderHeap SigConvert StubDispatchCache StubUnwindInfoHeapSegments SyncBlockCache TypeIDMap UnresolvedClassLock PendingTypeLoadEntry @@ -315,7 +315,7 @@ End Crst LoaderAllocator AcquiredBefore PinnedHeapHandleTable HandleTable UniqueStack ThreadStore - AcquiredAfter DomainLocalBlock + AcquiredAfter GenericDictionaryExpansion End Crst LoaderAllocatorReferences @@ -364,7 +364,7 @@ End Crst PendingTypeLoadEntry AcquiredBefore AppDomainCache PinnedHeapHandleTable AssemblyLoader AvailableClass AvailableParamTypes BaseDomain ClassInit DeadlockDetection DebuggerController DebuggerJitInfo DebuggerMutex - DomainLocalBlock Exception ExecuteManRangeLock FuncPtrStubs + GenericDictionaryExpansion Exception ExecuteManRangeLock FuncPtrStubs FusionAppCtx GlobalStrLiteralMap HandleTable IbcProfile IJWFixupData IJWHash ISymUnmanagedReader Jit JumpStubCache LoaderHeap Module ModuleLookupTable PEImage SecurityStackwalkCache @@ -548,7 +548,7 @@ End Crst EventPipe AcquiredAfter PendingTypeLoadEntry - AcquiredBefore ThreadIdDispenser ThreadStore DomainLocalBlock InstMethodHashTable + AcquiredBefore ThreadIdDispenser ThreadStore GenericDictionaryExpansion InstMethodHashTable End Crst NotifyGdb @@ -581,10 +581,6 @@ Crst PgoData AcquiredBefore LoaderHeap End -Crst StaticBoxInit - AcquiredBefore LoaderHeap FrozenObjectHeap AssemblyLoader -End - Crst PerfMap AcquiredAfter CodeVersioning AssemblyList End diff --git a/src/coreclr/inc/crsttypes_generated.h b/src/coreclr/inc/crsttypes_generated.h index b212928a89d95..cf0b497000c3b 100644 --- a/src/coreclr/inc/crsttypes_generated.h +++ b/src/coreclr/inc/crsttypes_generated.h @@ -41,20 +41,20 @@ enum CrstType CrstDebuggerJitInfo = 23, CrstDebuggerMutex = 24, CrstDelegateToFPtrHash = 25, - CrstDomainLocalBlock = 26, - CrstDynamicIL = 27, - CrstDynamicMT = 28, - CrstEtwTypeLogHash = 29, - CrstEventPipe = 30, - CrstEventStore = 31, - CrstException = 32, - CrstExecutableAllocatorLock = 33, - CrstExecuteManRangeLock = 34, - CrstFCall = 35, - CrstFrozenObjectHeap = 36, - CrstFuncPtrStubs = 37, - CrstFusionAppCtx = 38, - CrstGCCover = 39, + CrstDynamicIL = 26, + CrstDynamicMT = 27, + CrstEtwTypeLogHash = 28, + CrstEventPipe = 29, + CrstEventStore = 30, + CrstException = 31, + CrstExecutableAllocatorLock = 32, + CrstExecuteManRangeLock = 33, + CrstFCall = 34, + CrstFrozenObjectHeap = 35, + CrstFuncPtrStubs = 36, + CrstFusionAppCtx = 37, + CrstGCCover = 38, + CrstGenericDictionaryExpansion = 39, CrstGlobalStrLiteralMap = 40, CrstHandleTable = 41, CrstIbcProfile = 42, @@ -112,30 +112,29 @@ enum CrstType CrstSingleUseLock = 94, CrstSpecialStatics = 95, CrstStackSampler = 96, - CrstStaticBoxInit = 97, - CrstStressLog = 98, - CrstStubCache = 99, - CrstStubDispatchCache = 100, - CrstStubUnwindInfoHeapSegments = 101, - CrstSyncBlockCache = 102, - CrstSyncHashLock = 103, - CrstSystemBaseDomain = 104, - CrstSystemDomain = 105, - CrstSystemDomainDelayedUnloadList = 106, - CrstThreadIdDispenser = 107, - CrstThreadLocalStorageLock = 108, - CrstThreadStore = 109, - CrstTieredCompilation = 110, - CrstTypeEquivalenceMap = 111, - CrstTypeIDMap = 112, - CrstUMEntryThunkCache = 113, - CrstUMEntryThunkFreeListLock = 114, - CrstUniqueStack = 115, - CrstUnresolvedClassLock = 116, - CrstUnwindInfoTableLock = 117, - CrstVSDIndirectionCellLock = 118, - CrstWrapperTemplate = 119, - kNumberOfCrstTypes = 120 + CrstStressLog = 97, + CrstStubCache = 98, + CrstStubDispatchCache = 99, + CrstStubUnwindInfoHeapSegments = 100, + CrstSyncBlockCache = 101, + CrstSyncHashLock = 102, + CrstSystemBaseDomain = 103, + CrstSystemDomain = 104, + CrstSystemDomainDelayedUnloadList = 105, + CrstThreadIdDispenser = 106, + CrstThreadLocalStorageLock = 107, + CrstThreadStore = 108, + CrstTieredCompilation = 109, + CrstTypeEquivalenceMap = 110, + CrstTypeIDMap = 111, + CrstUMEntryThunkCache = 112, + CrstUMEntryThunkFreeListLock = 113, + CrstUniqueStack = 114, + CrstUnresolvedClassLock = 115, + CrstUnwindInfoTableLock = 116, + CrstVSDIndirectionCellLock = 117, + CrstWrapperTemplate = 118, + kNumberOfCrstTypes = 119 }; #endif // __CRST_TYPES_INCLUDED @@ -172,7 +171,6 @@ int g_rgCrstLevelMap[] = 4, // CrstDebuggerJitInfo 13, // CrstDebuggerMutex 0, // CrstDelegateToFPtrHash - 18, // CrstDomainLocalBlock 0, // CrstDynamicIL 3, // CrstDynamicMT 0, // CrstEtwTypeLogHash @@ -186,6 +184,7 @@ int g_rgCrstLevelMap[] = 7, // CrstFuncPtrStubs 10, // CrstFusionAppCtx 10, // CrstGCCover + 18, // CrstGenericDictionaryExpansion 17, // CrstGlobalStrLiteralMap 1, // CrstHandleTable 0, // CrstIbcProfile @@ -243,7 +242,6 @@ int g_rgCrstLevelMap[] = 5, // CrstSingleUseLock 0, // CrstSpecialStatics 0, // CrstStackSampler - 15, // CrstStaticBoxInit -1, // CrstStressLog 5, // CrstStubCache 0, // CrstStubDispatchCache @@ -297,7 +295,6 @@ LPCSTR g_rgCrstNameMap[] = "CrstDebuggerJitInfo", "CrstDebuggerMutex", "CrstDelegateToFPtrHash", - "CrstDomainLocalBlock", "CrstDynamicIL", "CrstDynamicMT", "CrstEtwTypeLogHash", @@ -311,6 +308,7 @@ LPCSTR g_rgCrstNameMap[] = "CrstFuncPtrStubs", "CrstFusionAppCtx", "CrstGCCover", + "CrstGenericDictionaryExpansion", "CrstGlobalStrLiteralMap", "CrstHandleTable", "CrstIbcProfile", @@ -368,7 +366,6 @@ LPCSTR g_rgCrstNameMap[] = "CrstSingleUseLock", "CrstSpecialStatics", "CrstStackSampler", - "CrstStaticBoxInit", "CrstStressLog", "CrstStubCache", "CrstStubDispatchCache", diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index 0c8930743b8ed..326af45b9f7b3 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -612,7 +612,7 @@ void BaseDomain::Init() m_DomainCrst.Init(CrstBaseDomain); m_DomainCacheCrst.Init(CrstAppDomainCache); - m_DomainLocalBlockCrst.Init(CrstDomainLocalBlock); + m_crstGenericDictionaryExpansionLock.Init(CrstGenericDictionaryExpansion); // NOTE: CRST_UNSAFE_COOPGC prevents a GC mode switch to preemptive when entering this crst. // If you remove this flag, we will switch to preemptive mode when entering @@ -637,7 +637,6 @@ void BaseDomain::Init() m_NativeTypeLoadLock.Init(CrstInteropData, CrstFlags(CRST_REENTRANCY), TRUE); m_crstLoaderAllocatorReferences.Init(CrstLoaderAllocatorReferences); - m_crstStaticBoxInitLock.Init(CrstStaticBoxInit); // Has to switch thread to GC_NOTRIGGER while being held (see code:BaseDomain#AssemblyListLock) m_crstAssemblyList.Init(CrstAssemblyList, CrstFlags( CRST_GC_NOTRIGGER_WHEN_TAKEN | CRST_DEBUGGER_THREAD | CRST_TAKEN_DURING_SHUTDOWN)); diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp index 1221779053dfd..8a5ed5833f3a1 100644 --- a/src/coreclr/vm/appdomain.hpp +++ b/src/coreclr/vm/appdomain.hpp @@ -540,15 +540,6 @@ class BaseDomain return m_pMngStdInterfacesInfo; } #endif // FEATURE_COMINTEROP -#ifdef _DEBUG - BOOL OwnDomainLocalBlockLock() - { - WRAPPER_NO_CONTRACT; - - return m_DomainLocalBlockCrst.OwnedByCurrentThread(); - } -#endif - //**************************************************************************************** // Get the class init lock. The method is limited to friends because inappropriate use // will cause deadlocks in the system @@ -681,10 +672,10 @@ class BaseDomain return &m_crstLoaderAllocatorReferences; } - CrstExplicitInit* GetStaticBoxInitLock() + CrstExplicitInit* GetGenericDictionaryExpansionLock() { LIMITED_METHOD_CONTRACT; - return &m_crstStaticBoxInitLock; + return &m_crstGenericDictionaryExpansionLock; } static CrstStatic* GetMethodTableExposedClassObjectLock() @@ -708,10 +699,9 @@ class BaseDomain PEFileListLock m_FileLoadLock; // Protects the list of assemblies in the domain CrstExplicitInit m_DomainCrst; // General Protection for the Domain CrstExplicitInit m_DomainCacheCrst; // Protects the Assembly and Unmanaged caches - CrstExplicitInit m_DomainLocalBlockCrst; // Used to protect the reference lists in the collectible loader allocators attached to this appdomain CrstExplicitInit m_crstLoaderAllocatorReferences; - CrstExplicitInit m_crstStaticBoxInitLock; + CrstExplicitInit m_crstGenericDictionaryExpansionLock; //#AssemblyListLock // Used to protect the assembly list. Taken also by GC or debugger thread, therefore we have to avoid @@ -780,17 +770,6 @@ class BaseDomain } }; - class DomainLocalBlockLockHolder : public CrstHolder - { - public: - DomainLocalBlockLockHolder(BaseDomain *pD) - : CrstHolder(&pD->m_DomainLocalBlockCrst) - { - WRAPPER_NO_CONTRACT; - } - }; - friend class DomainLocalBlockLockHolder; - class LoadLockHolder : public PEFileListLockHolder { public: diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index ad233fe72d5dd..937960e8595e7 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -421,7 +421,6 @@ void Module::Initialize(AllocMemTracker *pamTracker, LPCWSTR szName) m_FixupCrst.Init(CrstModuleFixup, (CrstFlags)(CRST_HOST_BREAKABLE|CRST_REENTRANCY)); m_InstMethodHashTableCrst.Init(CrstInstMethodHashTable, CRST_REENTRANCY); m_ISymUnmanagedReaderCrst.Init(CrstISymUnmanagedReader, CRST_DEBUGGER_THREAD); - m_DictionaryCrst.Init(CrstDomainLocalBlock); AllocateMaps(); m_dwTransientFlags &= ~((DWORD)CLASSES_FREED); // Set flag indicating LookupMaps are now in a consistent and destructable state diff --git a/src/coreclr/vm/ceeload.h b/src/coreclr/vm/ceeload.h index ef3fad7e0cd11..56c0c4842bb10 100644 --- a/src/coreclr/vm/ceeload.h +++ b/src/coreclr/vm/ceeload.h @@ -1620,9 +1620,6 @@ class Module : public ModuleBase uint32_t GetNativeMetadataAssemblyCount(); #endif // !defined(DACCESS_COMPILE) - - // For protecting dictionary layout slot expansions - CrstExplicitInit m_DictionaryCrst; }; // diff --git a/src/coreclr/vm/genericdict.cpp b/src/coreclr/vm/genericdict.cpp index 09dcda4b11ebc..2dc5a013dcf4f 100644 --- a/src/coreclr/vm/genericdict.cpp +++ b/src/coreclr/vm/genericdict.cpp @@ -233,7 +233,7 @@ BOOL DictionaryLayout::FindTokenWorker(LoaderAllocator* pAllocat } // A lock should be taken by FindToken before being allowed to use an empty slot in the layout - _ASSERT(SystemDomain::SystemModule()->m_DictionaryCrst.OwnedByCurrentThread()); + _ASSERT(GetAppDomain()->GetGenericDictionaryExpansionLock()->OwnedByCurrentThread()); PVOID pResultSignature = pSigBuilder == NULL ? pSig : CreateSignatureWithSlotData(pSigBuilder, pAllocator, slot); pDictLayout->m_slots[iSlot].m_signature = pResultSignature; @@ -270,7 +270,7 @@ DictionaryLayout* DictionaryLayout::ExpandDictionaryLayout(LoaderAllocator* { STANDARD_VM_CHECK; INJECT_FAULT(ThrowOutOfMemory();); - PRECONDITION(SystemDomain::SystemModule()->m_DictionaryCrst.OwnedByCurrentThread()); + PRECONDITION(GetAppDomain()->GetGenericDictionaryExpansionLock()->OwnedByCurrentThread()); PRECONDITION(CheckPointer(pResult) && CheckPointer(pSlotOut)); } CONTRACTL_END @@ -337,7 +337,7 @@ BOOL DictionaryLayout::FindToken(MethodTable* pMT, if (FindTokenWorker(pAllocator, pMT->GetNumGenericArgs(), pMT->GetClass()->GetDictionaryLayout(), pSigBuilder, pSig, cbSig, nFirstOffset, signatureSource, pResult, pSlotOut, 0, FALSE)) return TRUE; - CrstHolder ch(&SystemDomain::SystemModule()->m_DictionaryCrst); + CrstHolder ch(GetAppDomain()->GetGenericDictionaryExpansionLock()); { // Try again under lock in case another thread already expanded the dictionaries or filled an empty slot if (FindTokenWorker(pMT->GetLoaderAllocator(), pMT->GetNumGenericArgs(), pMT->GetClass()->GetDictionaryLayout(), pSigBuilder, pSig, cbSig, nFirstOffset, signatureSource, pResult, pSlotOut, *pSlotOut, TRUE)) @@ -384,7 +384,7 @@ BOOL DictionaryLayout::FindToken(MethodDesc* pMD, if (FindTokenWorker(pAllocator, pMD->GetNumGenericMethodArgs(), pMD->GetDictionaryLayout(), pSigBuilder, pSig, cbSig, nFirstOffset, signatureSource, pResult, pSlotOut, 0, FALSE)) return TRUE; - CrstHolder ch(&SystemDomain::SystemModule()->m_DictionaryCrst); + CrstHolder ch(GetAppDomain()->GetGenericDictionaryExpansionLock()); { // Try again under lock in case another thread already expanded the dictionaries or filled an empty slot if (FindTokenWorker(pAllocator, pMD->GetNumGenericMethodArgs(), pMD->GetDictionaryLayout(), pSigBuilder, pSig, cbSig, nFirstOffset, signatureSource, pResult, pSlotOut, *pSlotOut, TRUE)) @@ -502,7 +502,7 @@ Dictionary* Dictionary::GetMethodDictionaryWithSizeCheck(MethodDesc* pMD, ULONG // Only expand the dictionary if the current slot we're trying to use is beyond the size of the dictionary // Take lock and check for size again, just in case another thread already resized the dictionary - CrstHolder ch(&SystemDomain::SystemModule()->m_DictionaryCrst); + CrstHolder ch(GetAppDomain()->GetGenericDictionaryExpansionLock()); pDictionary = pMD->GetMethodDictionary(); currentDictionarySize = pDictionary->GetDictionarySlotsSize(numGenericArgs); @@ -560,7 +560,7 @@ Dictionary* Dictionary::GetTypeDictionaryWithSizeCheck(MethodTable* pMT, ULONG s // Only expand the dictionary if the current slot we're trying to use is beyond the size of the dictionary // Take lock and check for size again, just in case another thread already resized the dictionary - CrstHolder ch(&SystemDomain::SystemModule()->m_DictionaryCrst); + CrstHolder ch(GetAppDomain()->GetGenericDictionaryExpansionLock()); pDictionary = pMT->GetDictionary(); currentDictionarySize = pDictionary->GetDictionarySlotsSize(numGenericArgs); @@ -783,7 +783,7 @@ Dictionary::PopulateEntry( #if _DEBUG // Lock is needed because dictionary pointers can get updated during dictionary size expansion - CrstHolder ch(&SystemDomain::SystemModule()->m_DictionaryCrst); + CrstHolder ch(GetAppDomain()->GetGenericDictionaryExpansionLock()); // MethodTable is expected to be normalized Dictionary* pDictionary = pMT->GetDictionary(); diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index a1f094229c30d..4c0809f9b1121 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -1538,6 +1538,7 @@ void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken, // Allocate space for the local class if necessary, but don't trigger // class construction. pFieldMT->EnsureStaticDataAllocated(); + pFieldMT->AttemptToPreinit(); // We are not going through a helper. The constructor has to be triggered explicitly. if (!pFieldMT->IsClassInited()) @@ -1552,14 +1553,6 @@ void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken, if (fieldFlags & CORINFO_FLG_FIELD_STATIC_IN_HEAP) { Object* frozenObj = VolatileLoad((Object**)pResult->fieldLookup.addr); - - if (frozenObj == nullptr) - { - // Boxed static is not yet set, allocate it - pFieldMT->AllocateRegularStaticBox(pField, (Object**)pResult->fieldLookup.addr); - frozenObj = VolatileLoad((Object**)pResult->fieldLookup.addr); - } - _ASSERT(frozenObj != nullptr); // ContainsPointers here is unnecessary but it's cheaper than IsInFrozenSegment diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 1313c75a77191..ba2646117e049 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -3961,24 +3961,14 @@ void MethodTable::AllocateRegularStaticBox(FieldDesc* pField, Object** boxedStat // Static fields are not pinned in collectible types so we need to protect the address GCPROTECT_BEGININTERIOR(boxedStaticHandle); - if (VolatileLoad(boxedStaticHandle) == nullptr) - { - // Grab field's type handle before we enter lock - MethodTable* pFieldMT = pField->GetFieldTypeHandleThrowing().GetMethodTable(); - bool hasFixedAddr = HasFixedAddressVTStatics(); - - // Taking a lock since we might come here from multiple threads/places - CrstHolder crst(GetAppDomain()->GetStaticBoxInitLock()); - - // double-checked locking - if (VolatileLoad(boxedStaticHandle) == nullptr) - { - LOG((LF_CLASSLOADER, LL_INFO10000, "\tInstantiating static of type %s\n", pFieldMT->GetDebugClassName())); - const bool canBeFrozen = !pFieldMT->ContainsPointers() && !Collectible(); - OBJECTREF obj = AllocateStaticBox(pFieldMT, hasFixedAddr, canBeFrozen); - SetObjectReference((OBJECTREF*)(boxedStaticHandle), obj); - } - } + _ASSERTE(*boxedStaticHandle == nullptr); + MethodTable* pFieldMT = pField->GetFieldTypeHandleThrowing().GetMethodTable(); + bool hasFixedAddr = HasFixedAddressVTStatics(); + + LOG((LF_CLASSLOADER, LL_INFO10000, "\tInstantiating static of type %s\n", pFieldMT->GetDebugClassName())); + const bool canBeFrozen = !pFieldMT->ContainsPointers() && !Collectible(); + OBJECTREF obj = AllocateStaticBox(pFieldMT, hasFixedAddr, canBeFrozen); + SetObjectReference((OBJECTREF*)(boxedStaticHandle), obj); GCPROTECT_END(); } diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 431d8ed352dda..e9a2bbb79568e 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -559,8 +559,8 @@ struct DynamicStaticsInfo MethodTableAuxiliaryData m_AuxData; PTR_OBJECTREF GetGCStaticsPointer() { TADDR staticsVal = VolatileLoadWithoutBarrier(&m_pGCStatics); return dac_cast(staticsVal & STATICSPOINTERMASK); } PTR_BYTE GetNonGCStaticsPointer() { TADDR staticsVal = VolatileLoadWithoutBarrier(&m_pNonGCStatics); return dac_cast(staticsVal & STATICSPOINTERMASK); } - PTR_OBJECTREF GetGCStaticsPointerAssumeIsInited() { TADDR staticsVal = m_pGCStatics; _ASSERTE(staticsVal != 0); _ASSERTE(staticsVal & ISCLASSNOTINITEDMASK == 0); return dac_cast(staticsVal); } - PTR_BYTE GetNonGCStaticsPointerAssumeIsInited() { TADDR staticsVal = m_pNonGCStatics; _ASSERTE(staticsVal != 0); _ASSERTE(staticsVal & ISCLASSNOTINITEDMASK == 0); return dac_cast(staticsVal); } + PTR_OBJECTREF GetGCStaticsPointerAssumeIsInited() { TADDR staticsVal = m_pGCStatics; _ASSERTE(staticsVal != 0); _ASSERTE((staticsVal & (ISCLASSNOTINITEDMASK)) == 0); return dac_cast(staticsVal); } + PTR_BYTE GetNonGCStaticsPointerAssumeIsInited() { TADDR staticsVal = m_pNonGCStatics; _ASSERTE(staticsVal != 0); _ASSERTE((staticsVal & (ISCLASSNOTINITEDMASK)) == 0); return dac_cast(staticsVal); } bool GetIsInitedAndGCStaticsPointerIfInited(PTR_OBJECTREF *ptrResult) { TADDR staticsVal = VolatileLoadWithoutBarrier(&m_pGCStatics); *ptrResult = dac_cast(staticsVal); return !(staticsVal & ISCLASSNOTINITED); } bool GetIsInitedAndNonGCStaticsPointerIfInited(PTR_BYTE *ptrResult) { TADDR staticsVal = VolatileLoadWithoutBarrier(&m_pNonGCStatics); *ptrResult = dac_cast(staticsVal); return !(staticsVal & ISCLASSNOTINITED); } From 6aed90aa61077f1812a4f5759588b41ec0b41c3e Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 5 Mar 2024 16:07:36 -0800 Subject: [PATCH 13/82] Fix Tls index setting in R2R code --- src/coreclr/vm/prestub.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index d3b0b3fe7c50d..67b6e56f5a138 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -3491,6 +3491,11 @@ PCODE DynamicHelperFixup(TransitionBlock * pTransitionBlock, TADDR * pCell, DWOR Statics: th.AsMethodTable()->EnsureInstanceActive(); th.AsMethodTable()->CheckRunClassInitThrowing(); + if (kind == ENCODE_THREAD_STATIC_BASE_NONGC_HELPER || kind == ENCODE_THREAD_STATIC_BASE_GC_HELPER || + (kind == ENCODE_FIELD_ADDRESS && pFD->IsThreadStatic())) + { + th.AsMethodTable()->EnsureTlsIndexAllocated(); + } fReliable = true; break; From 1bb643a411272501ec332173137fcf5f39df6607 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 5 Mar 2024 16:26:36 -0800 Subject: [PATCH 14/82] Save a little bit of code size on X64 Use <= instead of < for TLS index compare Unallocated TLSIndex is not 0xFFFFFFFF, which will make the existing checks fall back to doing the full work for generic TLS lookups. --- src/coreclr/vm/amd64/JitHelpers_SingleAppDomain.asm | 4 ++-- src/coreclr/vm/amd64/jithelpers_singleappdomain.S | 8 ++++---- src/coreclr/vm/jithelpers.cpp | 2 +- src/coreclr/vm/methodtable.cpp | 4 ++++ src/coreclr/vm/methodtable.h | 5 +++++ src/coreclr/vm/threadstatics.cpp | 4 ++-- src/coreclr/vm/threadstatics.h | 3 ++- 7 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/coreclr/vm/amd64/JitHelpers_SingleAppDomain.asm b/src/coreclr/vm/amd64/JitHelpers_SingleAppDomain.asm index 280bdcd12217e..9d21ed57e8e55 100644 --- a/src/coreclr/vm/amd64/JitHelpers_SingleAppDomain.asm +++ b/src/coreclr/vm/amd64/JitHelpers_SingleAppDomain.asm @@ -17,7 +17,7 @@ extern JIT_GetDynamicGCStaticBase_Portable:proc LEAF_ENTRY JIT_GetDynamicNonGCStaticBase_SingleAppDomain, _TEXT ; If class is not initialized, bail to C++ helper mov rax, [rcx + OFFSETOF__DynamicStaticsInfo__m_pNonGCStatics] - test rax, 1 + test al, 1 jnz CallHelper REPRET @@ -30,7 +30,7 @@ LEAF_END JIT_GetDynamicNonGCStaticBase_SingleAppDomain, _TEXT LEAF_ENTRY JIT_GetDynamicGCStaticBase_SingleAppDomain, _TEXT ; If class is not initialized, bail to C++ helper mov rax, [rcx + OFFSETOF__DynamicStaticsInfo__m_pGCStatics] - test rax, 1 + test al, 1 jnz CallHelper REPRET diff --git a/src/coreclr/vm/amd64/jithelpers_singleappdomain.S b/src/coreclr/vm/amd64/jithelpers_singleappdomain.S index 080f80c29eb14..f833b4e796ece 100644 --- a/src/coreclr/vm/amd64/jithelpers_singleappdomain.S +++ b/src/coreclr/vm/amd64/jithelpers_singleappdomain.S @@ -13,8 +13,8 @@ LEAF_ENTRY JIT_GetDynamicNonGCStaticBase_SingleAppDomain, _TEXT // If class is not initialized, bail to C++ helper mov rax, [rdi + OFFSETOF__DynamicStaticsInfo__m_pNonGCStatics] - test rax, 1 - jnz CallHelper + test al, 1 + jnz CallHelper rep ret .balign 16 @@ -26,8 +26,8 @@ LEAF_END_MARKED JIT_GetDynamicNonGCStaticBase_SingleAppDomain, _TEXT LEAF_ENTRY JIT_GetDynamicGCStaticBase_SingleAppDomain, _TEXT // If class is not initialized, bail to C++ helper mov rax, [rdi + OFFSETOF__DynamicStaticsInfo__m_pGCStatics] - test rax, 1 - jnz CallHelper1 + test al, 1 + jnz CallHelper1 rep ret .balign 16 diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 1b3346f06956f..c2104a0e9d906 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1534,7 +1534,7 @@ FORCEINLINE void* GetThreadLocalStaticBaseIfExistsAndInitialized(TLSIndex index) TADDR pTLSBaseAddress = NULL; int32_t cTLSData = t_ThreadStatics.cTLSData; - if (cTLSData < index.GetByteIndex()) + if (cTLSData <= index.GetByteIndex()) { return NULL; } diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index ba2646117e049..37f53b71fce74 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -669,6 +669,10 @@ void MethodTable::AllocateAuxiliaryData(LoaderAllocator *pAllocator, Module *pLo { MethodTableAuxiliaryData::GetDynamicStaticsInfo(pMTAuxiliaryData)->Init(this); } + if (HasFlag(staticsFlags, MethodTableStaticsFlags::Thread)) + { + MethodTableAuxiliaryData::GetThreadStaticsInfo(pMTAuxiliaryData)->Init(); + } } diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index e9a2bbb79568e..2a2dd088328cf 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -629,6 +629,11 @@ struct ThreadStaticsInfo TLSIndex NonGCTlsIndex; TLSIndex GCTlsIndex; GenericsStaticsInfo m_genericStatics; + void Init() + { + NonGCTlsIndex = TLSIndex::Unallocated(); + GCTlsIndex = TLSIndex::Unallocated(); + } }; /* static */ inline PTR_ThreadStaticsInfo MethodTableAuxiliaryData::GetThreadStaticsInfo(PTR_Const_MethodTableAuxiliaryData pAuxiliaryData) diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index a1d2ecc7e8f74..abf2ce8a546ab 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -29,7 +29,7 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(PTR_ThreadLocalData pThreadLocalData, PTR_TLSArray pTLSArray = dac_cast(dac_cast(pThreadLocalData) + index.GetTLSArrayOffset()); int32_t cTLSData = pTLSArray->cTLSData; - if (cTLSData < index.GetByteIndex()) + if (cTLSData <= index.GetByteIndex()) { return NULL; } @@ -187,7 +187,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) TLSArray* pTLSArray = reinterpret_cast((uint8_t*)&t_ThreadStatics + index.GetTLSArrayOffset()); int32_t cTLSData = pTLSArray->cTLSData; - if (cTLSData < index.GetByteIndex()) + if (cTLSData <= index.GetByteIndex()) { // Grow the underlying TLS array CrstHolder ch(&g_TLSCrst); diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index d53d227daf175..2ea1d07fab073 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -96,7 +96,8 @@ struct TLSIndex uint32_t TLSIndexRawIndex; int32_t GetByteIndex() { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex & 0xFFFFFF; } int8_t GetTLSArrayOffset() { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex >> 24; } - bool IsAllocated() { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex != 0;} + bool IsAllocated() { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex != 0xFFFFFFFF;} + static TLSIndex Unallocated() { LIMITED_METHOD_DAC_CONTRACT; return TLSIndex(0xFFFFFFFF); } bool operator == (TLSIndex index) { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex == index.TLSIndexRawIndex; } bool operator != (TLSIndex index) { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex != index.TLSIndexRawIndex; } }; From 02ee7a1eff7e8f3610199c0b2c4c7e3e8a2ee237 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 5 Mar 2024 16:29:37 -0800 Subject: [PATCH 15/82] Fix build breaks on unix --- src/coreclr/vm/arm64/asmhelpers.S | 2 +- src/coreclr/vm/threadstatics.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S index fcb7d2d5308e3..8b24746c2b89a 100644 --- a/src/coreclr/vm/arm64/asmhelpers.S +++ b/src/coreclr/vm/arm64/asmhelpers.S @@ -655,7 +655,7 @@ DynamicHelper DynamicHelperFrameFlags_ObjectArg | DynamicHelperFrameFlags_Object // void* JIT_GetDynamicNonGCStaticBase(DynamicStaticsInfo* pStaticsInfo) LEAF_ENTRY JIT_GetDynamicNonGCStaticBase_SingleAppDomain, _TEXT - ; If class is not initialized, bail to C++ helper + // If class is not initialized, bail to C++ helper ldr x1, [x0, #OFFSETOF__DynamicStaticsInfo__m_pNonGCStatics] tbnz x1, #0, LOCAL_LABEL(JIT_GetDynamicNonGCStaticBase_SingleAppDomain_CallHelper) mov x0, x1 diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index abf2ce8a546ab..28df73b01801b 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -293,6 +293,8 @@ void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pInde *pIndex = TLSIndex(tlsRawIndex); } +static void* GetTlsIndexObjectAddress(); + bool CanJITOptimizeTLSAccess() { bool optimizeThreadStaticAccess = false; From e3e8cd662d648cfe3e98c7f48edd370b6db3de44 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 6 Mar 2024 13:56:25 -0800 Subject: [PATCH 16/82] Fix Unix X64 build break --- src/coreclr/vm/jitinterface.cpp | 8 -------- src/coreclr/vm/threadstatics.cpp | 4 ++++ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 4c0809f9b1121..ac5f60666eb40 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -65,14 +65,6 @@ #include "tailcallhelp.h" -#ifdef TARGET_WINDOWS -EXTERN_C uint32_t _tls_index; -#endif - -#ifndef _MSC_VER -extern "C" void* __tls_get_addr(void* ti); -#endif // !_MSC_VER - // The Stack Overflow probe takes place in the COOPERATIVE_TRANSITION_BEGIN() macro // diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 28df73b01801b..1230c54f9adfd 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -318,6 +318,10 @@ bool CanJITOptimizeTLSAccess() return optimizeThreadStaticAccess; } +#ifndef _MSC_VER +extern "C" void* __tls_get_addr(void* ti); +#endif // !_MSC_VER + #if defined(TARGET_WINDOWS) EXTERN_C uint32_t _tls_index; /*********************************************************************/ From 92153b0706cdb8c6ea444b5efb59d3a9e57af6f7 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 6 Mar 2024 14:29:59 -0800 Subject: [PATCH 17/82] Merge with change introduced by #97590 --- .../System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 733e3a664bcc5..71121113b10d4 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 @@ -653,7 +653,7 @@ internal unsafe struct MethodTableAuxiliaryData [FieldOffset(0)] private uint Flags; - private const uint enum_flag_CanCompareBitsOrUseFastGetHashCode = 0x0001; // Is any field type or sub field type overrode Equals or GetHashCode + private const uint enum_flag_CanCompareBitsOrUseFastGetHashCode = 0x0400; // Is any field type or sub field type overrode Equals or GetHashCode private const uint enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode = 0x0002; // Whether we have checked the overridden Equals or GetHashCode public bool HasCheckedCanCompareBitsOrUseFastGetHashCode => (Flags & enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode) != 0; From a8f9b73550b072bcf8c1cfb8e66f228360d5ca94 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 6 Mar 2024 15:15:08 -0800 Subject: [PATCH 18/82] Fix allocation of thread static boxes --- src/coreclr/vm/threadstatics.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 1230c54f9adfd..573b5db117d6f 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -151,7 +151,8 @@ void AllocateThreadStaticBoxes(MethodTable *pMT, PTRARRAYREF *ppRef) MethodTable* pFieldMT = th.GetMethodTable(); OBJECTREF obj = MethodTable::AllocateStaticBox(pFieldMT, pMT->HasFixedAddressVTStatics()); - (*ppRef)->SetAt(pField->GetOffset(), obj); + uint8_t *pBase = (uint8_t*)OBJECTREFToObject(*ppRef); + SetObjectReference((OBJECTREF*)(pBase + pField->GetOffset()), obj); } pField++; From d37f6120357c73697720144d19fdc3fd2c0bde87 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 6 Mar 2024 15:20:47 -0800 Subject: [PATCH 19/82] Remove custom assembly helpers for statics access - While I didn't do this for most of the Microsoft maintained architectures, there isn't much evidence at the moment that the hand coded assembly actually provides any value --- src/coreclr/vm/loongarch64/asmconstants.h | 5 -- src/coreclr/vm/loongarch64/asmhelpers.S | 36 -------------- src/coreclr/vm/loongarch64/cgencpu.h | 5 -- src/coreclr/vm/riscv64/asmconstants.h | 6 --- src/coreclr/vm/riscv64/asmhelpers.S | 59 ----------------------- src/coreclr/vm/riscv64/cgencpu.h | 4 -- 6 files changed, 115 deletions(-) diff --git a/src/coreclr/vm/loongarch64/asmconstants.h b/src/coreclr/vm/loongarch64/asmconstants.h index e12d0040a74d0..3b9aa746f33ef 100644 --- a/src/coreclr/vm/loongarch64/asmconstants.h +++ b/src/coreclr/vm/loongarch64/asmconstants.h @@ -206,11 +206,6 @@ ASMCONSTANTS_C_ASSERT((1< Date: Thu, 7 Mar 2024 14:11:49 -0800 Subject: [PATCH 20/82] Expose the ability to look at simplified statics through a new ISOSDacInterface14 - This is to compensate for the existing GetDomainLocalModule* api no longer working --- src/coreclr/debug/daccess/daccess.cpp | 4 + src/coreclr/debug/daccess/dacimpl.h | 8 +- src/coreclr/debug/daccess/request.cpp | 110 ++++++++++++++++++++ src/coreclr/inc/sospriv.idl | 14 +++ src/coreclr/pal/prebuilt/idl/sospriv_i.cpp | 9 +- src/coreclr/pal/prebuilt/inc/sospriv.h | 114 +++++++++++++++++++++ 6 files changed, 254 insertions(+), 5 deletions(-) diff --git a/src/coreclr/debug/daccess/daccess.cpp b/src/coreclr/debug/daccess/daccess.cpp index e79dab808def3..eb5127cdfa4f3 100644 --- a/src/coreclr/debug/daccess/daccess.cpp +++ b/src/coreclr/debug/daccess/daccess.cpp @@ -3231,6 +3231,10 @@ ClrDataAccess::QueryInterface(THIS_ { ifaceRet = static_cast(this); } + else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface14))) + { + ifaceRet = static_cast(this); + } else { *iface = NULL; diff --git a/src/coreclr/debug/daccess/dacimpl.h b/src/coreclr/debug/daccess/dacimpl.h index 03756c6716574..90e358f203409 100644 --- a/src/coreclr/debug/daccess/dacimpl.h +++ b/src/coreclr/debug/daccess/dacimpl.h @@ -816,7 +816,8 @@ class ClrDataAccess public ISOSDacInterface10, public ISOSDacInterface11, public ISOSDacInterface12, - public ISOSDacInterface13 + public ISOSDacInterface13, + public ISOSDacInterface14 { public: ClrDataAccess(ICorDebugDataTarget * pTarget, ICLRDataTarget * pLegacyTarget=0); @@ -1216,6 +1217,11 @@ class ClrDataAccess virtual HRESULT STDMETHODCALLTYPE GetGCFreeRegions(ISOSMemoryEnum **ppEnum); virtual HRESULT STDMETHODCALLTYPE LockedFlush(); + // ISOSDacInterface14 + virtual HRESULT STDMETHODCALLTYPE GetStaticBaseAddress(CLRDATA_ADDRESS methodTable, BOOL isGCStaticBase, CLRDATA_ADDRESS *address); + virtual HRESULT STDMETHODCALLTYPE GetThreadStaticBaseAddress(CLRDATA_ADDRESS methodTable, CLRDATA_ADDRESS thread, BOOL isGCStaticBase, CLRDATA_ADDRESS *address); + virtual HRESULT STDMETHODCALLTYPE GetMethodTableInitializationFlags(CLRDATA_ADDRESS methodTable, MethodTableInitializationFlags *initializationStatus); + // // ClrDataAccess. // diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index fbbcbe715736c..b6d5c31a8b5e1 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -5347,3 +5347,113 @@ HRESULT ClrDataAccess::LockedFlush() SOSDacLeave(); return hr; } + +HRESULT STDMETHODCALLTYPE ClrDataAccess::GetStaticBaseAddress(CLRDATA_ADDRESS methodTable, BOOL isGCStaticBase, CLRDATA_ADDRESS *address) +{ + if (!address) + return E_POINTER; + + if (!methodTable) + return E_INVALIDARG; + + SOSDacEnter(); + + PTR_MethodTable mTable = PTR_MethodTable(TO_TADDR(methodTable)); + + BOOL bIsFree = FALSE; + if (!DacValidateMethodTable(mTable, bIsFree)) + { + hr = E_INVALIDARG; + } + else + { + if (!mTable->IsDynamicStatics()) + { + *address = 0; + } + if (isGCStaticBase) + { + *address = PTR_CDADDR(mTable->GetGCStaticsBasePointer()); + } + else + { + *address = PTR_CDADDR(mTable->GetNonGCStaticsBasePointer()); + } + } + + SOSDacLeave(); + return hr; +} + + +HRESULT STDMETHODCALLTYPE ClrDataAccess::GetThreadStaticBaseAddress(CLRDATA_ADDRESS methodTable, CLRDATA_ADDRESS threadPtr, BOOL isGCStaticBase, CLRDATA_ADDRESS *address) +{ + if (!address) + return E_POINTER; + + if (!methodTable) + return E_INVALIDARG; + + if (!threadPtr) + return E_INVALIDARG; + + SOSDacEnter(); + + PTR_MethodTable mTable = PTR_MethodTable(TO_TADDR(methodTable)); + PTR_Thread thread = PTR_Thread(TO_TADDR(threadPtr)); + + + BOOL bIsFree = FALSE; + if (!DacValidateMethodTable(mTable, bIsFree)) + { + hr = E_INVALIDARG; + } + else + { + if (mTable->GetClass()->GetNumThreadStaticFields() == 0) + { + *address = 0; + } + if (isGCStaticBase) + { + *address = PTR_CDADDR(mTable->GetGCThreadStaticsBasePointer(thread)); + } + else + { + *address = PTR_CDADDR(mTable->GetNonGCThreadStaticsBasePointer(thread)); + } + } + + SOSDacLeave(); + return hr; +} + +HRESULT STDMETHODCALLTYPE ClrDataAccess::GetMethodTableInitializationFlags(CLRDATA_ADDRESS methodTable, MethodTableInitializationFlags *initializationStatus) +{ + if (!methodTable) + return E_INVALIDARG; + + if (!initializationStatus) + return E_POINTER; + + SOSDacEnter(); + + *initializationStatus = (MethodTableInitializationFlags)0; + PTR_MethodTable mTable = PTR_MethodTable(TO_TADDR(methodTable)); + BOOL bIsFree = FALSE; + if (!DacValidateMethodTable(mTable, bIsFree)) + { + hr = E_INVALIDARG; + } + else + { + *initializationStatus = mTable->IsClassInited() ? MethodTableInitialized : (MethodTableInitializationFlags)0; + if (mTable->GetAuxiliaryData()->IsInitError()) + { + *initializationStatus = (MethodTableInitializationFlags)(*initializationStatus | MethodTableInitializationFailed); + } + } + + SOSDacLeave(); + return hr; +} diff --git a/src/coreclr/inc/sospriv.idl b/src/coreclr/inc/sospriv.idl index a13760f7732a4..d669e06c375e1 100644 --- a/src/coreclr/inc/sospriv.idl +++ b/src/coreclr/inc/sospriv.idl @@ -43,12 +43,14 @@ typedef unsigned int size_t; typedef int ModuleMapType; typedef int VCSHeapType; typedef int LoaderHeapKind; +typedef int MethodTableInitializationFlags; cpp_quote("#endif") cpp_quote("typedef enum { TYPEDEFTOMETHODTABLE, TYPEREFTOMETHODTABLE } ModuleMapType;") cpp_quote("typedef enum {IndcellHeap, LookupHeap, ResolveHeap, DispatchHeap, CacheEntryHeap, VtableHeap} VCSHeapType;") cpp_quote("typedef enum {LoaderHeapKindNormal = 0, LoaderHeapKindExplicitControl = 1} LoaderHeapKind;") +cpp_quote("typedef enum {MethodTableInitialized = 1, MethodTableInitializationFailed = 2} MethodTableInitializationFlags;") cpp_quote("typedef enum {FreeUnknownRegion = 0, FreeGlobalHugeRegion = 1, FreeGlobalRegion = 2, FreeRegion = 3, FreeSohSegment = 4, FreeUohSegment = 5 } FreeRegionKind;") typedef void (*MODULEMAPTRAVERSE)(UINT index, CLRDATA_ADDRESS methodTable,LPVOID token); @@ -505,3 +507,15 @@ interface ISOSDacInterface13 : IUnknown HRESULT GetGCFreeRegions(ISOSMemoryEnum **ppEnum); HRESULT LockedFlush(); } + +[ + object, + local, + uuid(9aa22aca-6dc6-4a0c-b4e0-70d2416b9837) +] +interface ISOSDacInterface14 : IUnknown +{ + HRESULT GetStaticBaseAddress(CLRDATA_ADDRESS methodTable, BOOL isGCStaticBase, CLRDATA_ADDRESS *address); + HRESULT GetThreadStaticBaseAddress(CLRDATA_ADDRESS methodTable, CLRDATA_ADDRESS thread, BOOL isGCStaticBase, CLRDATA_ADDRESS *address); + HRESULT GetMethodTableInitializationFlags(CLRDATA_ADDRESS methodTable, MethodTableInitializationFlags *initializationStatus); +} diff --git a/src/coreclr/pal/prebuilt/idl/sospriv_i.cpp b/src/coreclr/pal/prebuilt/idl/sospriv_i.cpp index 141ec62612e48..f070ae5816a8a 100644 --- a/src/coreclr/pal/prebuilt/idl/sospriv_i.cpp +++ b/src/coreclr/pal/prebuilt/idl/sospriv_i.cpp @@ -5,11 +5,9 @@ /* link this file in with the server and any clients */ - /* File created by MIDL compiler version 8.01.0622 */ -/* at Mon Jan 18 19:14:07 2038 - */ + /* File created by MIDL compiler version 8.01.0628 */ /* Compiler settings for sospriv.idl: - Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 + Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0628 protocol : dce , ms_ext, c_ext, robust error checks: allocation ref bounds_check enum stub_data VC __declspec() decoration level: @@ -120,6 +118,9 @@ MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface12,0x1b93bacc,0x8ca4,0x432d,0x94,0x3a, MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface13,0x3176a8ed,0x597b,0x4f54,0xa7,0x1f,0x83,0x69,0x5c,0x6a,0x8c,0x5e); + +MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface14,0x9aa22aca,0x6dc6,0x4a0c,0xb4,0xe0,0x70,0xd2,0x41,0x6b,0x98,0x37); + #undef MIDL_DEFINE_GUID #ifdef __cplusplus diff --git a/src/coreclr/pal/prebuilt/inc/sospriv.h b/src/coreclr/pal/prebuilt/inc/sospriv.h index 4c86b39cb6f76..3f41aa56f12af 100644 --- a/src/coreclr/pal/prebuilt/inc/sospriv.h +++ b/src/coreclr/pal/prebuilt/inc/sospriv.h @@ -205,6 +205,7 @@ typedef int VCSHeapType; typedef enum { TYPEDEFTOMETHODTABLE, TYPEREFTOMETHODTABLE } ModuleMapType; typedef enum {IndcellHeap, LookupHeap, ResolveHeap, DispatchHeap, CacheEntryHeap, VtableHeap} VCSHeapType; typedef enum {LoaderHeapKindNormal = 0, LoaderHeapKindExplicitControl = 1} LoaderHeapKind; +typedef enum {MethodTableInitialized = 1, MethodTableInitializationFailed = 2} MethodTableInitializationFlags; typedef enum {FreeUnknownRegion = 0, FreeGlobalHugeRegion = 1, FreeGlobalRegion = 2, FreeRegion = 3, FreeSohSegment = 4, FreeUohSegment = 5 } FreeRegionKind; typedef void ( *MODULEMAPTRAVERSE )( UINT index, @@ -3343,6 +3344,118 @@ EXTERN_C const IID IID_ISOSDacInterface13; #endif /* __ISOSDacInterface13_INTERFACE_DEFINED__ */ +#ifndef __ISOSDacInterface14_INTERFACE_DEFINED__ +#define __ISOSDacInterface14_INTERFACE_DEFINED__ + +/* interface ISOSDacInterface14 */ +/* [uuid][local][object] */ + + +EXTERN_C const IID IID_ISOSDacInterface14; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("9aa22aca-6dc6-4a0c-b4e0-70d2416b9837") + ISOSDacInterface14 : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetStaticBaseAddress( + CLRDATA_ADDRESS methodTable, + BOOL isGCStaticBase, + CLRDATA_ADDRESS *address) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetThreadStaticBaseAddress( + CLRDATA_ADDRESS methodTable, + CLRDATA_ADDRESS thread, + BOOL isGCStaticBase, + CLRDATA_ADDRESS *address) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetMethodTableInitializationFlags( + CLRDATA_ADDRESS methodTable, + MethodTableInitializationFlags *initializationStatus) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ISOSDacInterface14Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ISOSDacInterface14 * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ISOSDacInterface14 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ISOSDacInterface14 * This); + + HRESULT ( STDMETHODCALLTYPE *GetStaticBaseAddress )( + ISOSDacInterface14 * This, + CLRDATA_ADDRESS methodTable, + BOOL isGCStaticBase, + CLRDATA_ADDRESS *address); + + HRESULT ( STDMETHODCALLTYPE *GetThreadStaticBaseAddress )( + ISOSDacInterface14 * This, + CLRDATA_ADDRESS methodTable, + CLRDATA_ADDRESS thread, + BOOL isGCStaticBase, + CLRDATA_ADDRESS *address); + + HRESULT ( STDMETHODCALLTYPE *GetMethodTableInitializationFlags )( + ISOSDacInterface14 * This, + CLRDATA_ADDRESS methodTable, + MethodTableInitializationFlags *initializationStatus); + + END_INTERFACE + } ISOSDacInterface14Vtbl; + + interface ISOSDacInterface14 + { + CONST_VTBL struct ISOSDacInterface14Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ISOSDacInterface14_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ISOSDacInterface14_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ISOSDacInterface14_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ISOSDacInterface14_GetStaticBaseAddress(This,methodTable,isGCStaticBase,address) \ + ( (This)->lpVtbl -> GetStaticBaseAddress(This,methodTable,isGCStaticBase,address) ) + +#define ISOSDacInterface14_GetThreadStaticBaseAddress(This,methodTable,thread,isGCStaticBase,address) \ + ( (This)->lpVtbl -> GetThreadStaticBaseAddress(This,methodTable,thread,isGCStaticBase,address) ) + +#define ISOSDacInterface14_GetMethodTableInitializationFlags(This,methodTable,initializationStatus) \ + ( (This)->lpVtbl -> GetMethodTableInitializationFlags(This,methodTable,initializationStatus) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ISOSDacInterface14_INTERFACE_DEFINED__ */ + + /* Additional Prototypes for ALL interfaces */ /* end of Additional Prototypes */ @@ -3353,3 +3466,4 @@ EXTERN_C const IID IID_ISOSDacInterface13; #endif + From abe8a973677f3223b74aa400617395436971de08 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 7 Mar 2024 14:35:18 -0800 Subject: [PATCH 21/82] Fix missing helper, used by collectible scenario --- src/coreclr/jit/flowgraph.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 8417c6462991c..549fb193ee314 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -685,6 +685,8 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo case CORINFO_HELP_GET_NONGCSTATIC_BASE_NOCTOR: case CORINFO_HELP_GET_GCTHREADSTATIC_BASE_NOCTOR: case CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE_NOCTOR: case CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR: case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR: case CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED: From 3380e00bf4b732bc904d9709bc943805f376323b Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 7 Mar 2024 15:47:40 -0800 Subject: [PATCH 22/82] Delete ThreadStaticHandleTable --- src/coreclr/vm/appdomain.cpp | 112 ----------------------------------- src/coreclr/vm/appdomain.hpp | 54 ----------------- src/coreclr/vm/threads.h | 1 - 3 files changed, 167 deletions(-) diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index 326af45b9f7b3..c2ba1ec3c641c 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -439,118 +439,6 @@ void PinnedHeapHandleTable::EnumStaticGCRefs(promote_func* fn, ScanContext* sc) } } -// Constructor for the ThreadStaticHandleBucket class. -ThreadStaticHandleBucket::ThreadStaticHandleBucket(ThreadStaticHandleBucket *pNext, DWORD Size, BaseDomain *pDomain) -: m_pNext(pNext) -, m_ArraySize(Size) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(pDomain)); - INJECT_FAULT(COMPlusThrowOM();); - } - CONTRACTL_END; - - PTRARRAYREF HandleArrayObj; - - // Allocate the array on the GC heap. - OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED); - HandleArrayObj = (PTRARRAYREF)AllocateObjectArray(Size, g_pObjectClass); - - // Store the array in a strong handle to keep it alive. - m_hndHandleArray = pDomain->CreateStrongHandle((OBJECTREF)HandleArrayObj); -} - -// Destructor for the ThreadStaticHandleBucket class. -ThreadStaticHandleBucket::~ThreadStaticHandleBucket() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - if (m_hndHandleArray) - { - DestroyStrongHandle(m_hndHandleArray); - m_hndHandleArray = NULL; - } -} - -// Allocate handles from the bucket. -OBJECTHANDLE ThreadStaticHandleBucket::GetHandles() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - return m_hndHandleArray; -} - -// Constructor for the ThreadStaticHandleTable class. -ThreadStaticHandleTable::ThreadStaticHandleTable(BaseDomain *pDomain) -: m_pHead(NULL) -, m_pDomain(pDomain) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(CheckPointer(pDomain)); - } - CONTRACTL_END; -} - -// Destructor for the ThreadStaticHandleTable class. -ThreadStaticHandleTable::~ThreadStaticHandleTable() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - } - CONTRACTL_END; - - // Delete the buckets. - while (m_pHead) - { - ThreadStaticHandleBucket *pOld = m_pHead; - m_pHead = pOld->GetNext(); - delete pOld; - } -} - -// Allocate handles from the large heap handle table. -OBJECTHANDLE ThreadStaticHandleTable::AllocateHandles(DWORD nRequested) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(nRequested > 0); - INJECT_FAULT(COMPlusThrowOM();); - } - CONTRACTL_END; - - // create a new bucket for this allocation - m_pHead = new ThreadStaticHandleBucket(m_pHead, nRequested, m_pDomain); - - return m_pHead->GetHandles(); -} - - - //***************************************************************************** // BaseDomain //***************************************************************************** diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp index 8a5ed5833f3a1..a4cfed39d465f 100644 --- a/src/coreclr/vm/appdomain.hpp +++ b/src/coreclr/vm/appdomain.hpp @@ -205,60 +205,6 @@ FORCEINLINE void PinnedHeapHandleBlockHolder__StaticFree(PinnedHeapHandleBlockH pHolder->FreeData(); }; - - - - -// The large heap handle bucket class is used to contain handles allocated -// from an array contained in the large heap. -class ThreadStaticHandleBucket -{ -public: - // Constructor and desctructor. - ThreadStaticHandleBucket(ThreadStaticHandleBucket *pNext, DWORD Size, BaseDomain *pDomain); - ~ThreadStaticHandleBucket(); - - // This returns the next bucket. - ThreadStaticHandleBucket *GetNext() - { - LIMITED_METHOD_CONTRACT; - - return m_pNext; - } - - // Allocate handles from the bucket. - OBJECTHANDLE GetHandles(); - -private: - ThreadStaticHandleBucket *m_pNext; - int m_ArraySize; - OBJECTHANDLE m_hndHandleArray; -}; - - -// The large heap handle table is used to allocate handles that are pointers -// to objects stored in an array in the large object heap. -class ThreadStaticHandleTable -{ -public: - // Constructor and desctructor. - ThreadStaticHandleTable(BaseDomain *pDomain); - ~ThreadStaticHandleTable(); - - // Allocate handles from the large heap handle table. - OBJECTHANDLE AllocateHandles(DWORD nRequested); - -private: - // The buckets of object handles. - ThreadStaticHandleBucket *m_pHead; - - // We need to know the containing domain so we know where to allocate handles - BaseDomain *m_pDomain; -}; - - - - //-------------------------------------------------------------------------------------- // Base class for domains. It provides an abstract way of finding the first assembly and // for creating assemblies in the domain. The system domain only has one assembly, it diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 309423aa31010..3e60afbdaef6b 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -161,7 +161,6 @@ typedef void(*ADCallBackFcnType)(LPVOID); #include "eventpipeadaptertypes.h" #endif // FEATURE_PERFTRACING -class ThreadStaticHandleTable; class Module; // TailCallArgBuffer states From 785f32f91f3fc5c2ff2aa4c2823f57a8697ecef3 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 11 Mar 2024 11:15:52 -0700 Subject: [PATCH 23/82] Fixup collectible assembly static handling to work correctly again --- src/coreclr/vm/ceemain.cpp | 1 + src/coreclr/vm/gcenv.ee.cpp | 9 ++ src/coreclr/vm/loaderallocator.cpp | 198 ++++++++++++++++++++++++++++- src/coreclr/vm/loaderallocator.hpp | 30 +++++ 4 files changed, 234 insertions(+), 4 deletions(-) diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 4b9a34fb74605..73f8dc2c09cdb 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -630,6 +630,7 @@ void EEStartupHelper() Thread::StaticInitialize(); InitializeThreadStaticData(); + InitMoveableGCPointerTracker(); JITInlineTrackingMap::StaticInitialize(); MethodDescBackpatchInfoTracker::StaticInitialize(); diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index 3b1a7fa3d28b7..ee357a7791f41 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -311,6 +311,12 @@ void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen, } } + // Scan the moveable GC Pointer table, which is used to ensure that + // pointers into managed object which are kept alive by collectible + // LoaderAllocators will be updated if their objects are moved. + // We use this for static variable bases and for + g_pMoveableGCPointerTracker->ScanTable(fn, sc); + // In server GC, we should be competing for marking the statics // It's better to do this *after* stack scanning, because this way // we can make up for imbalances in stack scanning @@ -1658,6 +1664,9 @@ void GCToEEInterface::AnalyzeSurvivorsFinished(size_t gcIndex, int condemnedGene { LIMITED_METHOD_CONTRACT; + // This callback is called at least once per run of the GC, so we can reset the work stealing infra in the MoveableGCPointerTracker here + g_pMoveableGCPointerTracker->ResetForNextSetOfTableScans(); + uint64_t elapsed = 0; if (GenAwareMatchingGeneration(condemnedGeneration) && gcGenAnalysisTime > 0) { diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index f3f659df776d5..fffc9a0bcfa21 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -1887,6 +1887,12 @@ void AssemblyLoaderAllocator::CleanupHandles() _ASSERTE(GetDomain()->IsAppDomain()); + if (m_hLoaderAllocatorObjectHandle != NULL) + { + GCX_COOP(); + g_pMoveableGCPointerTracker->RemoveEntriesAssociatedWithGCHandle(m_hLoaderAllocatorObjectHandle); + } + // This method doesn't take a lock around RemoveHead because it's supposed to // be called only from Terminate while (!m_handleCleanupList.IsEmpty()) @@ -2211,12 +2217,24 @@ void LoaderAllocator::AllocateBytesForStaticVariables(DynamicStaticsInfo* pStati if (cbMem > 0) { - uint8_t* pbMem = (uint8_t*)(void*)GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbMem)); - pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */false, (TADDR)pbMem); + if (IsCollectible()) + { + GCX_COOP(); + uint32_t doubleSlots = AlignUp(cbMem, sizeof(double)) / sizeof(double); + BASEARRAYREF ptrArray = (BASEARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_R8, doubleSlots); + CrstHolder crst(g_pMoveableGCPointerTracker->GetCrst()); + pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */false, (TADDR)ptrArray->GetDataPtr()); + g_pMoveableGCPointerTracker->AddPointer(m_hLoaderAllocatorObjectHandle, OBJECTREFToObject(ptrArray), &pStaticsInfo->m_pNonGCStatics); + } + else + { + uint8_t* pbMem = (uint8_t*)(void*)GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbMem)); + pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */false, (TADDR)pbMem); + } } } -void LoaderAllocator::AllocateGCHandlesBytesForStaticVariables(DynamicStaticsInfo* pStaticsInfo, uint32_t cSlots, MethodTable* pMTWithStaticBoxes) +void LoaderAllocator::AllocateGCHandlesBytesForStaticVariables(DynamicStaticsInfo* pStaticsInfo, uint32_t cSlots, MethodTable* pMTToFillWithStaticBoxes) { CONTRACTL { @@ -2228,7 +2246,179 @@ void LoaderAllocator::AllocateGCHandlesBytesForStaticVariables(DynamicStaticsInf if (cSlots > 0) { - GetDomain()->AllocateObjRefPtrsInLargeTable(cSlots, pStaticsInfo, pMTWithStaticBoxes); + if (IsCollectible()) + { + GCX_COOP(); + BASEARRAYREF ptrArray = (BASEARRAYREF)AllocateObjectArray(cSlots, g_pObjectClass, /* bAllocateInPinnedHeap = */FALSE); + GCPROTECT_BEGIN(ptrArray); + if (pMTToFillWithStaticBoxes != NULL) + { + OBJECTREF* pArray = (OBJECTREF*)ptrArray->GetDataPtr(); + GCPROTECT_BEGININTERIOR(pArray); + pMTToFillWithStaticBoxes->AllocateRegularStaticBoxes(&pArray); + GCPROTECT_END(); + } + + { + CrstHolder crst(g_pMoveableGCPointerTracker->GetCrst()); + pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */true, (TADDR)ptrArray->GetDataPtr()); + g_pMoveableGCPointerTracker->AddPointer(m_hLoaderAllocatorObjectHandle, OBJECTREFToObject(ptrArray), &pStaticsInfo->m_pGCStatics); + } + GCPROTECT_END(); + } + else + { + GetDomain()->AllocateObjRefPtrsInLargeTable(cSlots, pStaticsInfo, pMTToFillWithStaticBoxes); + } + } +} +#endif // !DACCESS_COMPILE +#ifndef DACCESS_COMPILE +CollectibleMoveableGCPointerTracker *g_pMoveableGCPointerTracker; +void InitMoveableGCPointerTracker() +{ + STANDARD_VM_CONTRACT; + g_pMoveableGCPointerTracker = new CollectibleMoveableGCPointerTracker(); +} + +CollectibleMoveableGCPointerTracker::CollectibleMoveableGCPointerTracker() : + m_pointers(nullptr), + m_numPointers(0), + m_maxPointers(0), + m_nextTableScanChunk(-1), + m_Crst(CrstLeafLock, (CrstFlags)(CRST_UNSAFE_COOPGC | CRST_REENTRANCY)) +{ +} + +void CollectibleMoveableGCPointerTracker::AddPointer(OBJECTHANDLE gcHandleDescribingLifetime, Object *pObject, uintptr_t *pTrackedInteriorPointer) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_COOPERATIVE; // Since the GC reads the array without taking locks, we need to be in cooperative mode + INJECT_FAULT(COMPlusThrowOM()); + } + CONTRACTL_END; + + CrstHolder ch(&m_Crst); + MoveableGCPointer structToAdd; + structToAdd.LifetimeTrackingHandle = gcHandleDescribingLifetime; + structToAdd.PointerValue = pObject; + structToAdd.TrackedInteriorPointer = pTrackedInteriorPointer; + if (m_numPointers >= m_maxPointers) + { + uintptr_t newMaxPointers = max(16, m_maxPointers * 2); + MoveableGCPointer *newPointers = new MoveableGCPointer[newMaxPointers]; + if (m_pointers != nullptr) + { + memcpy(newPointers, m_pointers, m_maxPointers * sizeof(MoveableGCPointer)); + delete[] m_pointers; + } + m_pointers = newPointers; + m_maxPointers = newMaxPointers; + } + + m_pointers[m_numPointers] = structToAdd; + m_numPointers++; +} + +void CollectibleMoveableGCPointerTracker::ScanTable(promote_func* fn, ScanContext* sc) +{ + // This executes during the GC, so we don't need to take a lock, as the table cannot be modified, as all modifications involve cooperative mode + WRAPPER_NO_CONTRACT; + + + if (sc->promotion) + { + // We must not promote anything, as the lifetime of these pointers is driven by the lifetime of the LifetimeTrackingHandle + return; + } + + if (m_numPointers == 0) + { + // There isn't any work to do. + return; + } + + // On the server heap, where multiple threads may be scanning the table, we can to divide the work up. Use a simple work-stealing approach + // + // Notably, in GCToEEInterface::AnalyzeSurvivorsFinished which is called once for each time that there is a set of relocations, we reset the current + // chunk index to -1. Then in this function, we operate on chunks from 0 to maxChunkCount. We use InterlockedIncrement to get the next chunk index, and + // when a chunk is done, we increment the next chunk index. This is done is a loop, so if 1 (or more) threads of the GC is running ahead of the others, + // it will do more chunks than another thread which is running behind, thus balancing the work. + uintptr_t maxChunkCount = GCHeapUtilities::IsServerHeap() ? sc->thread_count : 1; + LONG chunkIndex = InterlockedIncrement(&m_nextTableScanChunk); + while ((uintptr_t)chunkIndex < maxChunkCount) + { + uintptr_t pointerRangeStart = 0; + uintptr_t pointerRangeEnd = m_numPointers; + + uintptr_t pointersPerThread = (m_numPointers / maxChunkCount) + 1; + pointerRangeStart = pointersPerThread * chunkIndex; + pointerRangeEnd = pointerRangeStart + pointersPerThread; + if (pointerRangeEnd > m_numPointers) + { + pointerRangeEnd = m_numPointers; + } + + for (uintptr_t i = pointerRangeStart; i < pointerRangeEnd; ++i) + { + if (!ObjectHandleIsNull(m_pointers[i].LifetimeTrackingHandle)) + { + Object *pObjectOld = m_pointers[i].PointerValue; + fn(&m_pointers[i].PointerValue, sc, 0); + Object *pObject = m_pointers[i].PointerValue; + + if (pObject != pObjectOld) + { + uintptr_t delta = (uintptr_t)pObject - (uintptr_t)pObjectOld; + // Use VolatileLost/Store to handle the case where the GC is running on one thread and another thread updates the lowest bit + // on the pointer. This can happen when the GC is running on one thread, and another thread is running the JIT, and happens to trigger + // the pre-init logic for a collectible type. The jit will update the value using InterlockedCompareExchange, and this code will do the update + // using VolatileStoreWithoutBarrier. This may result in the flag bit update from SetClassInited to be lost, but that's ok, as the flag bit will + // being lost won't actually change program behavior, and the lost bit is an extremely rare occurence. + uintptr_t *trackedInteriorPointer = m_pointers[i].TrackedInteriorPointer; + uintptr_t currentInteriorPointerVale = VolatileLoadWithoutBarrier(trackedInteriorPointer); + uintptr_t newInteriorPointerValue = currentInteriorPointerVale + delta; + VolatileStoreWithoutBarrier(trackedInteriorPointer, newInteriorPointerValue); + } + } + } + + chunkIndex = InterlockedIncrement(&m_nextTableScanChunk); + } +} + +void CollectibleMoveableGCPointerTracker::RemoveEntriesAssociatedWithGCHandle(OBJECTHANDLE gcHandleDescribingLifetime) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; // Since the GC reads the array without taking locks, we need to be in cooperative mode + } + CONTRACTL_END; + + CrstHolder ch(&m_Crst); + if (m_numPointers == 0) + return; + + uintptr_t index = m_numPointers - 1; + + while (true) + { + MoveableGCPointer ¤tElement = m_pointers[index]; + if (currentElement.LifetimeTrackingHandle == gcHandleDescribingLifetime) + { + currentElement = m_pointers[m_numPointers - 1]; + --m_numPointers; + } + + if (index == 0) + break; + index--; } } + #endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/loaderallocator.hpp b/src/coreclr/vm/loaderallocator.hpp index a9eb485f7cf48..b8903df7ed822 100644 --- a/src/coreclr/vm/loaderallocator.hpp +++ b/src/coreclr/vm/loaderallocator.hpp @@ -943,6 +943,36 @@ class AssemblyLoaderAllocator : public LoaderAllocator typedef VPTR(AssemblyLoaderAllocator) PTR_AssemblyLoaderAllocator; +#ifndef DACCESS_COMPILE +class CollectibleMoveableGCPointerTracker +{ +private: + struct MoveableGCPointer + { + OBJECTHANDLE LifetimeTrackingHandle; + Object* PointerValue; + uintptr_t *TrackedInteriorPointer; + }; + + MoveableGCPointer *m_pointers; + uintptr_t m_numPointers; // Number of elements in use in m_pointers + uintptr_t m_maxPointers; // Allocated size of m_pointers; + LONG m_nextTableScanChunk; + Crst m_Crst; + +public: + CollectibleMoveableGCPointerTracker(); + CrstBase* GetCrst() { LIMITED_METHOD_CONTRACT; return &m_Crst; } + void AddPointer(OBJECTHANDLE gcHandleDescribingLifetime, Object *pObject, uintptr_t *pTrackedInteriorPointer); + void ScanTable(promote_func* fn, ScanContext* sc); + void RemoveEntriesAssociatedWithGCHandle(OBJECTHANDLE gcHandleDescribingLifetime); + void ResetForNextSetOfTableScans() { LIMITED_METHOD_CONTRACT; VolatileStore(&m_nextTableScanChunk, (LONG)-1); } +}; + +extern CollectibleMoveableGCPointerTracker *g_pMoveableGCPointerTracker; +void InitMoveableGCPointerTracker(); +#endif + #include "loaderallocator.inl" #endif // __LoaderAllocator_h__ From 504d93e5b89517937e9ab494eb37723cc6997400 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 11 Mar 2024 11:22:34 -0700 Subject: [PATCH 24/82] Missed allocation of LoaderAllocatorHandle for the collectible static fields --- src/coreclr/vm/loaderallocator.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index fffc9a0bcfa21..57c2c0ce695ea 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -2222,9 +2222,15 @@ void LoaderAllocator::AllocateBytesForStaticVariables(DynamicStaticsInfo* pStati GCX_COOP(); uint32_t doubleSlots = AlignUp(cbMem, sizeof(double)) / sizeof(double); BASEARRAYREF ptrArray = (BASEARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_R8, doubleSlots); - CrstHolder crst(g_pMoveableGCPointerTracker->GetCrst()); - pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */false, (TADDR)ptrArray->GetDataPtr()); - g_pMoveableGCPointerTracker->AddPointer(m_hLoaderAllocatorObjectHandle, OBJECTREFToObject(ptrArray), &pStaticsInfo->m_pNonGCStatics); + GCPROTECT_BEGIN(ptrArray); + // Keep this allocation alive till the LoaderAllocator is collected + AllocateHandle(ptrArray); + { + CrstHolder crst(g_pMoveableGCPointerTracker->GetCrst()); + pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */false, (TADDR)ptrArray->GetDataPtr()); + g_pMoveableGCPointerTracker->AddPointer(m_hLoaderAllocatorObjectHandle, OBJECTREFToObject(ptrArray), &pStaticsInfo->m_pNonGCStatics); + } + GCPROTECT_END(); } else { @@ -2258,6 +2264,8 @@ void LoaderAllocator::AllocateGCHandlesBytesForStaticVariables(DynamicStaticsInf pMTToFillWithStaticBoxes->AllocateRegularStaticBoxes(&pArray); GCPROTECT_END(); } + // Keep this allocation alive till the LoaderAllocator is collected + AllocateHandle(ptrArray); { CrstHolder crst(g_pMoveableGCPointerTracker->GetCrst()); From 8afc63e684dfbdf02f95c05dfc7b7d5d2dc03f6e Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 13 Mar 2024 16:23:46 -0700 Subject: [PATCH 25/82] Implement collectible thread statics --- src/coreclr/vm/ceemain.cpp | 9 - src/coreclr/vm/ceemain.h | 2 - src/coreclr/vm/gcenv.ee.cpp | 10 +- src/coreclr/vm/jithelpers.cpp | 4 +- src/coreclr/vm/loaderallocator.cpp | 1 + src/coreclr/vm/loaderallocator.hpp | 9 + src/coreclr/vm/loaderallocator.inl | 8 + src/coreclr/vm/methodtable.inl | 4 +- src/coreclr/vm/spinlock.h | 3 +- src/coreclr/vm/threads.cpp | 13 +- src/coreclr/vm/threads.h | 9 +- src/coreclr/vm/threadstatics.cpp | 338 +++++++++++++++++++++++++---- src/coreclr/vm/threadstatics.h | 236 ++++++++++++++++++-- src/coreclr/vm/vars.hpp | 1 + 14 files changed, 559 insertions(+), 88 deletions(-) diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 73f8dc2c09cdb..46468e096a2c9 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -1757,7 +1757,6 @@ struct TlsDestructionMonitor thread->DetachThread(TRUE); } - DeleteThreadLocalMemory(); ThreadDetaching(); } } @@ -1772,14 +1771,6 @@ void EnsureTlsDestructionMonitor() tls_destructionMonitor.Activate(); } -// Delete the thread local memory only if we the current thread -// is the one executing this code. If we do not guard it, it will -// end up deleting the thread local memory of the calling thread. -void DeleteThreadLocalMemory() -{ - FreeCurrentThreadStaticData(); -} - #ifdef DEBUGGING_SUPPORTED // // InitializeDebugger initialized the Runtime-side COM+ Debugging Services diff --git a/src/coreclr/vm/ceemain.h b/src/coreclr/vm/ceemain.h index 85efc8d0a9c78..1404a5a04237f 100644 --- a/src/coreclr/vm/ceemain.h +++ b/src/coreclr/vm/ceemain.h @@ -47,8 +47,6 @@ void ThreadDetaching(); void EnsureTlsDestructionMonitor(); -void DeleteThreadLocalMemory(); - void SetLatchedExitCode (INT32 code); INT32 GetLatchedExitCode (void); diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index ee357a7791f41..c025e834d991e 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -302,7 +302,7 @@ void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen, #endif // FEATURE_EVENT_TRACE ScanStackRoots(pThread, fn, sc); ScanTailCallArgBufferRoots(pThread, fn, sc); - ScanThreadStaticRoots(pThread->GetThreadLocalDataPtr(), fn, sc); + ScanThreadStaticRoots(pThread->GetThreadLocalDataPtr(), /*forGC*/ true, fn, sc); #ifdef FEATURE_EVENT_TRACE sc->dwEtwRootKind = kEtwGCRootKindOther; #endif // FEATURE_EVENT_TRACE @@ -317,6 +317,12 @@ void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen, // We use this for static variable bases and for g_pMoveableGCPointerTracker->ScanTable(fn, sc); + if (sc->thread_number == 0 || !GCHeapUtilities::IsServerHeap()) + { + // This function must be called once per run of calls to ScanThreadStaticRoots + NotifyThreadStaticGCHappened(); + } + // In server GC, we should be competing for marking the statics // It's better to do this *after* stack scanning, because this way // we can make up for imbalances in stack scanning @@ -627,7 +633,7 @@ void GcScanRootsForProfilerAndETW(promote_func* fn, int condemned, int max_gen, #endif // FEATURE_EVENT_TRACE ScanStackRoots(pThread, fn, sc); ScanTailCallArgBufferRoots(pThread, fn, sc); - ScanThreadStaticRoots(pThread->GetThreadLocalDataPtr(), fn, sc); + ScanThreadStaticRoots(pThread->GetThreadLocalDataPtr(), /*forGC*/ false, fn, sc); #ifdef FEATURE_EVENT_TRACE sc->dwEtwRootKind = kEtwGCRootKindOther; #endif // FEATURE_EVENT_TRACE diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index c2104a0e9d906..f247e6e080953 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1645,7 +1645,7 @@ HCIMPL1(void*, JIT_GetNonGCThreadStaticBaseOptimized, UINT32 staticBlockIndex) HELPER_METHOD_FRAME_BEGIN_RET_0(); // Set up a frame TLSIndex tlsIndex(staticBlockIndex); // Check if the class constructor needs to be run - MethodTable *pMT = LookupMethodTableForThreadStatic(tlsIndex); + MethodTable *pMT = LookupMethodTableForThreadStaticKnownToBeAllocated(tlsIndex); pMT->CheckRunClassInitThrowing(); // Lookup the non-GC statics base pointer @@ -1711,7 +1711,7 @@ HCIMPL1(void*, JIT_GetGCThreadStaticBaseOptimized, UINT32 staticBlockIndex) TLSIndex tlsIndex(staticBlockIndex); // Check if the class constructor needs to be run - MethodTable *pMT = LookupMethodTableForThreadStatic(tlsIndex); + MethodTable *pMT = LookupMethodTableForThreadStaticKnownToBeAllocated(tlsIndex); pMT->CheckRunClassInitThrowing(); // Lookup the non-GC statics base pointer diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index 57c2c0ce695ea..b405562db01eb 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -1891,6 +1891,7 @@ void AssemblyLoaderAllocator::CleanupHandles() { GCX_COOP(); g_pMoveableGCPointerTracker->RemoveEntriesAssociatedWithGCHandle(m_hLoaderAllocatorObjectHandle); + FreeTLSIndicesForLoaderAllocator(this); } // This method doesn't take a lock around RemoveHead because it's supposed to diff --git a/src/coreclr/vm/loaderallocator.hpp b/src/coreclr/vm/loaderallocator.hpp index b8903df7ed822..c1b5190cc9a9a 100644 --- a/src/coreclr/vm/loaderallocator.hpp +++ b/src/coreclr/vm/loaderallocator.hpp @@ -349,6 +349,8 @@ class LoaderAllocator Volatile m_pgoManager; #endif // FEATURE_PGO + SArray m_tlsIndices; + public: BYTE *GetVSDHeapInitialBlock(DWORD *pSize); BYTE *GetCodeHeapInitialBlock(const BYTE * loAddr, const BYTE * hiAddr, DWORD minimumSize, DWORD *pSize); @@ -389,6 +391,12 @@ class LoaderAllocator PTR_VirtualCallStubManager m_pVirtualCallStubManager; +public: + SArray& GetTLSIndexList() + { + return m_tlsIndices; + } + private: LoaderAllocatorSet m_LoaderAllocatorReferences; Volatile m_cReferences; @@ -631,6 +639,7 @@ class LoaderAllocator } LOADERALLOCATORREF GetExposedObject(); + bool IsExposedObjectLive(); #ifndef DACCESS_COMPILE LOADERHANDLE AllocateHandle(OBJECTREF value); diff --git a/src/coreclr/vm/loaderallocator.inl b/src/coreclr/vm/loaderallocator.inl index 993732d4010f8..5c2544132a3b2 100644 --- a/src/coreclr/vm/loaderallocator.inl +++ b/src/coreclr/vm/loaderallocator.inl @@ -16,6 +16,14 @@ inline LOADERALLOCATORREF LoaderAllocator::GetExposedObject() } #endif +inline bool LoaderAllocator::IsExposedObjectLive() +{ + LIMITED_METHOD_CONTRACT; + if (m_hLoaderAllocatorObjectHandle == NULL) + return false; + return !ObjectHandleIsNull(m_hLoaderAllocatorObjectHandle); +} + inline void GlobalLoaderAllocator::Init(BaseDomain *pDomain) { LoaderAllocator::Init(pDomain, m_ExecutableHeapInstance); diff --git a/src/coreclr/vm/methodtable.inl b/src/coreclr/vm/methodtable.inl index b8b894676e9d6..465448c6168fa 100644 --- a/src/coreclr/vm/methodtable.inl +++ b/src/coreclr/vm/methodtable.inl @@ -1151,7 +1151,7 @@ inline PTR_BYTE MethodTable::GetNonGCThreadStaticsBasePointer(PTR_Thread pThread if (!tlsIndex.IsAllocated()) return NULL; - PTR_ThreadLocalData pThreadLocalData = pThread->GetThreadLocalDataPtr(); + ThreadLocalData* pThreadLocalData = pThread->GetThreadLocalDataPtr(); if (pThreadLocalData == NULL) return NULL; @@ -1167,7 +1167,7 @@ inline PTR_BYTE MethodTable::GetGCThreadStaticsBasePointer(PTR_Thread pThread) if (!tlsIndex.IsAllocated()) return NULL; - PTR_ThreadLocalData pThreadLocalData = pThread->GetThreadLocalDataPtr(); + ThreadLocalData* pThreadLocalData = pThread->GetThreadLocalDataPtr(); if (pThreadLocalData == NULL) return NULL; diff --git a/src/coreclr/vm/spinlock.h b/src/coreclr/vm/spinlock.h index 7601d2341d3b7..6ac0eff726d12 100644 --- a/src/coreclr/vm/spinlock.h +++ b/src/coreclr/vm/spinlock.h @@ -134,7 +134,8 @@ enum LOCK_TYPE #endif LOCK_REFLECTCACHE = 5, LOCK_CORMAP = 7, - LOCK_TYPE_DEFAULT = 8 + LOCK_TLSDATA = 8, + LOCK_TYPE_DEFAULT = 9 }; //---------------------------------------------------------------------------- diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 60438278fad8a..edddb77ff0034 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -1379,8 +1379,6 @@ Thread::Thread() m_fPreemptiveGCDisabled = 0; - m_pThreadLocalData = NULL; - #ifdef _DEBUG m_ulForbidTypeLoad = 0; m_GCOnTransitionsOK = TRUE; @@ -2939,7 +2937,7 @@ void Thread::OnThreadTerminate(BOOL holdingLock) SafeSetThrowables(NULL); // Free all structures related to thread statics for this thread - DeleteThreadStaticData(this == GetThreadNULLOk()); + DeleteThreadStaticData(); } @@ -7728,7 +7726,7 @@ Frame * Thread::NotifyFrameChainOfExceptionUnwind(Frame* pStartFrame, LPVOID pvL // //+---------------------------------------------------------------------------- -void Thread::DeleteThreadStaticData(bool forCurrentThread) +void Thread::DeleteThreadStaticData() { CONTRACTL { NOTHROW; @@ -7737,12 +7735,7 @@ void Thread::DeleteThreadStaticData(bool forCurrentThread) } CONTRACTL_END; - if (m_pThreadLocalData != NULL) - { - if (forCurrentThread) - FreeCurrentThreadStaticData(); - m_pThreadLocalData = NULL; - } + FreeThreadStaticData(&m_ThreadLocalDataThreadObjectCopy); } OBJECTREF Thread::GetCulture(BOOL bUICulture) diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 3e60afbdaef6b..5a0aefaec9215 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -161,6 +161,8 @@ typedef void(*ADCallBackFcnType)(LPVOID); #include "eventpipeadaptertypes.h" #endif // FEATURE_PERFTRACING +#include "threadstatics.h" + class Module; // TailCallArgBuffer states @@ -3777,14 +3779,15 @@ class Thread } #endif //DACCESS_COMPILE - PTR_ThreadLocalData m_pThreadLocalData; - PTR_ThreadLocalData GetThreadLocalDataPtr() { LIMITED_METHOD_DAC_CONTRACT; return m_pThreadLocalData; } + ThreadLocalData m_ThreadLocalDataThreadObjectCopy; + SpinLock m_TlsSpinLock; + ThreadLocalData* GetThreadLocalDataPtr() { LIMITED_METHOD_DAC_CONTRACT; return &m_ThreadLocalDataThreadObjectCopy; } private: // Called during Thread death to clean up all structures // associated with thread statics - void DeleteThreadStaticData(bool forCurrentThread); + void DeleteThreadStaticData(); private: TailCallTls m_tailCallTls; diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 573b5db117d6f..0e250b637e4aa 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -21,10 +21,14 @@ struct ThreadLocalLoaderAllocator }; typedef DPTR(ThreadLocalLoaderAllocator) PTR_ThreadLocalLoaderAllocator; -// This can be used for out of thread access to TLS data. Since that isn't safe in general, we only support it for DAC. -PTR_VOID GetThreadLocalStaticBaseNoCreate(PTR_ThreadLocalData pThreadLocalData, TLSIndex index) +// This can be used for out of thread access to TLS data. +PTR_VOID GetThreadLocalStaticBaseNoCreate(ThreadLocalData* pThreadLocalData, TLSIndex index) { LIMITED_METHOD_CONTRACT; +#ifndef DACCESS_COMPILE + // Since this api can be used from a different thread, we need a lock to keep it all safe + SpinLockHolder spinLock(&pThreadLocalData->pThread->m_TlsSpinLock); +#endif TADDR pTLSBaseAddress = NULL; PTR_TLSArray pTLSArray = dac_cast(dac_cast(pThreadLocalData) + index.GetTLSArrayOffset()); @@ -54,33 +58,123 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(PTR_ThreadLocalData pThreadLocalData, } GPTR_IMPL(TLSIndexToMethodTableMap, g_pThreadStaticTypeIndices); - -PTR_MethodTable LookupMethodTableForThreadStatic(TLSIndex index) + +PTR_MethodTable LookupMethodTableForThreadStaticKnownToBeAllocated(TLSIndex index) { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + // TODO, if and when we get array indices, we should be pickier. - TADDR flagsUnused; - return g_pThreadStaticTypeIndices->GetElement(index.TLSIndexRawIndex, &flagsUnused); + return g_pThreadStaticTypeIndices->LookupTlsIndexKnownToBeAllocated(index); } TADDR isGCFlag = 0x1; -PTR_MethodTable LookupMethodTableAndFlagForThreadStatic(TLSIndex index, bool *pIsGCStatic) +PTR_MethodTable LookupMethodTableAndFlagForThreadStatic(TLSIndex index, bool *pIsGCStatic, bool *pIsCollectible) { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; // TODO, if and when we get array indices, we should be pickier. - TADDR flags; - PTR_MethodTable retVal = g_pThreadStaticTypeIndices->GetElement(index.TLSIndexRawIndex, &flags); - *pIsGCStatic = flags == isGCFlag; + PTR_MethodTable retVal = g_pThreadStaticTypeIndices->Lookup(index, pIsGCStatic, pIsCollectible); return retVal; } -void ScanThreadStaticRoots(PTR_ThreadLocalData pThreadLocalData, promote_func* fn, ScanContext* sc) + +// Report a TLS index to the GC, but if it is no longer in use and should be cleared out, return false +bool ReportTLSIndexCarefully(TLSIndex index, int32_t cLoaderHandles, PTR_LOADERHANDLE pLoaderHandles, PTR_PTR_Object ppTLSBaseAddress, promote_func* fn, ScanContext* sc) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + bool isGCStatic; + bool isCollectible; + PTR_MethodTable pMT = LookupMethodTableAndFlagForThreadStatic(index, &isGCStatic, &isCollectible); + if (pMT == NULL) + { + // The TLS index is not in use. This either means that the TLS index was never used, or that it was + // used for a collectible assembly, and that assembly has been freed. In the latter case, we may need to + // clean this entry up + if (cLoaderHandles > index.GetByteIndex()) + { + pLoaderHandles[index.GetByteIndex()] = NULL; + *ppTLSBaseAddress = NULL; + } + return false; + } + + if (isCollectible) + { + // Check to see if the associated loaderallocator is still live + if (!pMT->GetLoaderAllocator()->IsExposedObjectLive()) + { + if (cLoaderHandles > index.GetByteIndex()) + { + uintptr_t indexIntoLoaderHandleTable = index.GetByteIndex(); + pLoaderHandles[indexIntoLoaderHandleTable] = NULL; + *ppTLSBaseAddress = NULL; + } + return false; + } + } + fn(ppTLSBaseAddress, sc, 0 /* could be GC_CALL_INTERIOR or GC_CALL_PINNED */); + return true; +} + +// We use a scheme where the TLS data on each thread will be cleaned up within a GC promotion round or two. +#ifndef DACCESS_COMPILE +static Volatile s_GCsWhichDoRelocateAndCanEmptyOutTheTLSIndices = 0; + +void NotifyThreadStaticGCHappened() +{ + LIMITED_METHOD_CONTRACT; + s_GCsWhichDoRelocateAndCanEmptyOutTheTLSIndices += 1; +} +#endif + +void ScanThreadStaticRoots(ThreadLocalData *pThreadLocalData, bool forGC, promote_func* fn, ScanContext* sc) { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + if (pThreadLocalData == NULL) return; + + int32_t cLoaderHandles = forGC ? pThreadLocalData->cLoaderHandles : 0; // We can only need to pass this to ReportTLSIndexCarefully if we are permitted to to clean out this array PTR_InFlightTLSData pInFlightData = pThreadLocalData->pInFlightData; while (pInFlightData != NULL) { - fn(dac_cast(pInFlightData->pTLSData), sc, 0 /* could be GC_CALL_INTERIOR or GC_CALL_PINNED */); + if (!ReportTLSIndexCarefully(pInFlightData->tlsIndex, pThreadLocalData->cLoaderHandles, pThreadLocalData->pLoaderHandles, dac_cast(&pInFlightData->pTLSData), fn, sc)) + { + // TLS index is now dead. We should delete it, as the ReportTLSIndexCarefully function will have already deleted any assocated LOADERHANDLE +#ifndef DACCESS_COMPILE + if (forGC) + { + PTR_InFlightTLSData pNext = pInFlightData->pNext; + delete pInFlightData; + pInFlightData = pNext; + continue; + } +#endif + } pInFlightData = pInFlightData->pNext; } PTR_BYTE pTLSArrayData = dac_cast(pThreadLocalData->pTLSArrayData); @@ -88,21 +182,97 @@ void ScanThreadStaticRoots(PTR_ThreadLocalData pThreadLocalData, promote_func* f for (int32_t i = 0; i < cTLSData; i += sizeof(TADDR)) { TLSIndex index(i); - bool isGCStatic; - MethodTable *pMT = LookupMethodTableAndFlagForThreadStatic(index, &isGCStatic); - if (pMT == NULL) + TADDR *pTLSBaseAddress = dac_cast(pTLSArrayData + i); + ReportTLSIndexCarefully(index, pThreadLocalData->cLoaderHandles, pThreadLocalData->pLoaderHandles, dac_cast(pTLSBaseAddress), fn, sc); + } +} + +#ifndef DACCESS_COMPILE + +void TLSIndexToMethodTableMap::Set(TLSIndex index, PTR_MethodTable pMT, bool isGCStatic) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + if (index.TLSIndexRawIndex >= m_maxIndex) + { + uint32_t newSize = max(m_maxIndex, 16); + while (index.TLSIndexRawIndex >= newSize) { - continue; + newSize *= 2; } - TADDR *pTLSBaseAddress = dac_cast(pTLSArrayData + i); - if (pTLSBaseAddress != NULL) + TADDR *newMap = new TADDR[newSize]; + memset(newMap, 0, sizeof(TADDR) * newSize); + if (pMap != NULL) { - fn(dac_cast(pTLSBaseAddress), sc, 0 /* could be GC_CALL_INTERIOR or GC_CALL_PINNED */); + memcpy(newMap, pMap, m_maxIndex * sizeof(TADDR)); + // Don't delete the old map in case some other thread is reading from it, this won't waste significant amounts of memory, since this map cannot grow indefinitely } + pMap = newMap; + m_maxIndex = newSize; } + + TADDR rawValue = dac_cast(pMT); + if (isGCStatic) + { + rawValue |= IsGCFlag(); + } + if (pMT->Collectible()) + { + rawValue |= IsCollectibleFlag(); + m_collectibleEntries++; + } + _ASSERTE(pMap[index.TLSIndexRawIndex] == 0 || IsClearedValue(pMap[index.TLSIndexRawIndex])); + pMap[index.TLSIndexRawIndex] = rawValue; } -#ifndef DACCESS_COMPILE +void TLSIndexToMethodTableMap::Clear(TLSIndex index, uint16_t whenCleared) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + _ASSERTE(index.TLSIndexRawIndex < m_maxIndex); + TADDR rawValue = pMap[index.TLSIndexRawIndex]; + _ASSERTE(rawValue & IsCollectibleFlag()); + if (rawValue & IsCollectibleFlag()) + { + m_collectibleEntries--; + } + pMap[index.TLSIndexRawIndex] = (whenCleared << 2) | 0x3; + _ASSERTE(GetClearedMarker(pMap[index.TLSIndexRawIndex]) == whenCleared); +} + +bool TLSIndexToMethodTableMap::FindClearedIndex(uint16_t whenClearedMarkerToAvoid, TLSIndex* pIndex) +{ + for (const auto& entry : *this) + { + if (entry.IsClearedValue) + { + uint16_t whenClearedMarker = entry.ClearedMarker; + if ((whenClearedMarker == whenClearedMarkerToAvoid) || + (whenClearedMarker == (whenClearedMarkerToAvoid - 1)) || + (whenClearedMarker == (whenClearedMarkerToAvoid - 2))) + { + // Make sure we are not within 2 of the marker we are trying to avoid + // Use multiple compares instead of trying to fuss around with the overflow style comparisons + continue; + } + *pIndex = entry.TlsIndex; + return true; + } + } + return false; +} uint32_t g_NextTLSSlot = (uint32_t)sizeof(TADDR); CrstStatic g_TLSCrst; @@ -110,14 +280,14 @@ CrstStatic g_TLSCrst; void InitializeThreadStaticData() { g_pThreadStaticTypeIndices = new TLSIndexToMethodTableMap(); - g_pThreadStaticTypeIndices->supportedFlags = isGCFlag; g_TLSCrst.Init(CrstThreadLocalStorageLock, CRST_UNSAFE_ANYMODE); } void InitializeCurrentThreadsStaticData(Thread* pThread) { - pThread->m_pThreadLocalData = &t_ThreadStatics; t_ThreadStatics.pThread = pThread; + t_ThreadStatics.pThread->m_ThreadLocalDataThreadObjectCopy = t_ThreadStatics; + t_ThreadStatics.pThread->m_TlsSpinLock.Init(LOCK_TLSDATA, TRUE); } void AllocateThreadStaticBoxes(MethodTable *pMT, PTRARRAYREF *ppRef) @@ -159,20 +329,29 @@ void AllocateThreadStaticBoxes(MethodTable *pMT, PTRARRAYREF *ppRef) } } -void FreeCurrentThreadStaticData() +void FreeThreadStaticData(ThreadLocalData *pThreadLocalData) { - delete[] (uint8_t*)t_ThreadStatics.pTLSArrayData; + if (pThreadLocalData->cLoaderHandles > 0) + { + CrstHolder ch(&g_TLSCrst); + for (const auto& entry : g_pThreadStaticTypeIndices->CollectibleEntries()) + { + pThreadLocalData->pLoaderHandles[entry.TlsIndex.GetByteIndex()] = NULL; + } + } + + delete[] (uint8_t*)pThreadLocalData->pTLSArrayData; - t_ThreadStatics.pTLSArrayData = 0; + pThreadLocalData->pTLSArrayData = 0; - while (t_ThreadStatics.pInFlightData != NULL) + while (pThreadLocalData->pInFlightData != NULL) { - InFlightTLSData* pInFlightData = t_ThreadStatics.pInFlightData; - t_ThreadStatics.pInFlightData = pInFlightData->pNext; + InFlightTLSData* pInFlightData = pThreadLocalData->pInFlightData; + pThreadLocalData->pInFlightData = pInFlightData->pNext; delete pInFlightData; } - t_ThreadStatics.pThread = NULL; + pThreadLocalData->pThread = NULL; } void* GetThreadLocalStaticBase(TLSIndex index) @@ -186,12 +365,15 @@ void* GetThreadLocalStaticBase(TLSIndex index) CONTRACTL_END; TLSArray* pTLSArray = reinterpret_cast((uint8_t*)&t_ThreadStatics + index.GetTLSArrayOffset()); + bool isGCStatic; + bool isCollectible; + MethodTable *pMT = LookupMethodTableAndFlagForThreadStatic(index, &isGCStatic, &isCollectible); int32_t cTLSData = pTLSArray->cTLSData; if (cTLSData <= index.GetByteIndex()) { // Grow the underlying TLS array - CrstHolder ch(&g_TLSCrst); + SpinLockHolder spinLock(&t_ThreadStatics.pThread->m_TlsSpinLock); int32_t newcTLSData = index.GetByteIndex() + sizeof(TADDR) * 8; // Leave a bit of margin uint8_t* pNewTLSArrayData = new uint8_t[newcTLSData]; memset(pNewTLSArrayData, 0, newcTLSData); @@ -202,6 +384,25 @@ void* GetThreadLocalStaticBase(TLSIndex index) cTLSData = newcTLSData - 1; pTLSArray->cTLSData = cTLSData; delete[] pOldArray; + t_ThreadStatics.pThread->m_ThreadLocalDataThreadObjectCopy = t_ThreadStatics; + } + + if (isCollectible && t_ThreadStatics.cLoaderHandles <= index.GetByteIndex()) + { + // Grow the underlying TLS array + SpinLockHolder spinLock(&t_ThreadStatics.pThread->m_TlsSpinLock); + int32_t cNewTLSLoaderHandles = index.GetByteIndex() + sizeof(TADDR) * 8; // Leave a bit of margin + size_t cbNewTLSLoaderHandles = sizeof(LOADERHANDLE) * cNewTLSLoaderHandles; + LOADERHANDLE* pNewTLSLoaderHandles = new LOADERHANDLE[cNewTLSLoaderHandles]; + memset(pNewTLSLoaderHandles, 0, cbNewTLSLoaderHandles); + if (cTLSData > 0) + memcpy(pNewTLSLoaderHandles, (void*)t_ThreadStatics.pLoaderHandles, t_ThreadStatics.cLoaderHandles * sizeof(LOADERHANDLE)); + + LOADERHANDLE* pOldArray = t_ThreadStatics.pLoaderHandles; + t_ThreadStatics.pLoaderHandles = pNewTLSLoaderHandles; + t_ThreadStatics.cLoaderHandles = cNewTLSLoaderHandles; + delete[] pOldArray; + t_ThreadStatics.pThread->m_ThreadLocalDataThreadObjectCopy = t_ThreadStatics; } TADDR pTLSArrayData = pTLSArray->pTLSArrayData; @@ -218,12 +419,14 @@ void* GetThreadLocalStaticBase(TLSIndex index) if (pInFlightData->tlsIndex == index) { pTLSBaseAddress = pInFlightData->pTLSData; - MethodTable *pMT = LookupMethodTableForThreadStatic(index); + if (pMT->IsClassInited()) { + SpinLockHolder spinLock(&t_ThreadStatics.pThread->m_TlsSpinLock); *ppTLSBaseAddress = pTLSBaseAddress; *ppOldNextPtr = pInFlightData->pNext; delete pInFlightData; + t_ThreadStatics.pThread->m_ThreadLocalDataThreadObjectCopy = t_ThreadStatics; } break; } @@ -233,8 +436,6 @@ void* GetThreadLocalStaticBase(TLSIndex index) if (pTLSBaseAddress == NULL) { // Now we need to actually allocate the TLS data block - bool isGCStatic; - MethodTable *pMT = LookupMethodTableAndFlagForThreadStatic(index, &isGCStatic); struct { PTRARRAYREF ptrRef; @@ -256,18 +457,36 @@ void* GetThreadLocalStaticBase(TLSIndex index) gc.tlsEntry = AllocatePrimitiveArray(ELEMENT_TYPE_I1, static_cast(pMT->GetClass()->GetNonGCThreadStaticFieldBytes())); } + NewHolder pInFlightData = NULL; + if (!pMT->IsClassInited()) + { + pInFlightData = new InFlightTLSData(index, pTLSBaseAddress); + } + + if (isCollectible) + { + LOADERHANDLE *pLoaderHandle = reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetByteIndex()); + // Note, that this can fail, but if it succeeds we don't have a holder in place to clean it up if future operations fail + // Add such a holder if we ever add a possibly failing operation after this + *pLoaderHandle = pMT->GetLoaderAllocator()->AllocateHandle(gc.tlsEntry); + } + + // After this, we cannot fail + pInFlightData.SuppressRelease(); + { GCX_FORBID(); pTLSBaseAddress = (TADDR)OBJECTREFToObject(gc.tlsEntry); - if (pMT->IsClassInited()) + if (pInFlightData == NULL) { *ppTLSBaseAddress = pTLSBaseAddress; } else { - InFlightTLSData* pInFlightData = new InFlightTLSData(index, pTLSBaseAddress); + SpinLockHolder spinLock(&t_ThreadStatics.pThread->m_TlsSpinLock); pInFlightData->pNext = t_ThreadStatics.pInFlightData; t_ThreadStatics.pInFlightData = pInFlightData; + t_ThreadStatics.pThread->m_ThreadLocalDataThreadObjectCopy = t_ThreadStatics; } } GCPROTECT_END(); @@ -280,18 +499,54 @@ void* GetThreadLocalStaticBase(TLSIndex index) void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pIndex) { WRAPPER_NO_CONTRACT; + + GCX_COOP(); CrstHolder ch(&g_TLSCrst); if (pIndex->IsAllocated()) { return; } - uint32_t tlsRawIndex = g_NextTLSSlot; - g_NextTLSSlot += (uint32_t)sizeof(TADDR); - g_pThreadStaticTypeIndices->AddElement(g_pObjectClass->GetModule(), tlsRawIndex, pMT, (gcStatic ? isGCFlag : 0)); + TLSIndex newTLSIndex = TLSIndex::Unallocated(); + if (!g_pThreadStaticTypeIndices->FindClearedIndex(s_GCsWhichDoRelocateAndCanEmptyOutTheTLSIndices, &newTLSIndex)) + { + uint32_t tlsRawIndex = g_NextTLSSlot; + newTLSIndex = TLSIndex(tlsRawIndex); + g_NextTLSSlot += (uint32_t)sizeof(TADDR); + } - // TODO Handle collectible cases - *pIndex = TLSIndex(tlsRawIndex); + if (pMT->Collectible()) + { + pMT->GetLoaderAllocator()->GetTLSIndexList().Append(newTLSIndex); + } + + g_pThreadStaticTypeIndices->Set(newTLSIndex, pMT, (gcStatic ? isGCFlag : 0)); + + *pIndex = newTLSIndex; +} + +void FreeTLSIndicesForLoaderAllocator(LoaderAllocator *pLoaderAllocator) +{ + CONTRACTL + { + GC_TRIGGERS; + NOTHROW; + MODE_COOPERATIVE; + CAN_TAKE_LOCK; + } + CONTRACTL_END; + + CrstHolder ch(&g_TLSCrst); + + const auto& tlsIndicesToCleanup = pLoaderAllocator->GetTLSIndexList(); + COUNT_T current = 0; + COUNT_T end = tlsIndicesToCleanup.GetCount(); + + while (current != end) + { + g_pThreadStaticTypeIndices->Clear(tlsIndicesToCleanup[current], s_GCsWhichDoRelocateAndCanEmptyOutTheTLSIndices); + ++current; + } } static void* GetTlsIndexObjectAddress(); @@ -446,10 +701,9 @@ void GetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) #endif // !DACCESS_COMPILE #ifdef DACCESS_COMPILE -void EnumThreadMemoryRegions(PTR_ThreadLocalData pThreadLocalData, CLRDataEnumMemoryFlags flags) +void EnumThreadMemoryRegions(ThreadLocalData *pThreadLocalData, CLRDataEnumMemoryFlags flags) { SUPPORTS_DAC; - DacEnumMemoryRegion(dac_cast(pThreadLocalData), sizeof(ThreadLocalData), flags); DacEnumMemoryRegion(dac_cast(pThreadLocalData->pTLSArrayData), pThreadLocalData->cTLSData, flags); PTR_InFlightTLSData pInFlightData = pThreadLocalData->pInFlightData; while (pInFlightData != NULL) diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index 2ea1d07fab073..e40742bc642c7 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -92,14 +92,15 @@ class Thread; struct TLSIndex { + TLSIndex() : TLSIndexRawIndex(0xFFFFFFFF) { } TLSIndex(uint32_t rawIndex) : TLSIndexRawIndex(rawIndex) { } uint32_t TLSIndexRawIndex; - int32_t GetByteIndex() { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex & 0xFFFFFF; } - int8_t GetTLSArrayOffset() { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex >> 24; } - bool IsAllocated() { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex != 0xFFFFFFFF;} + int32_t GetByteIndex() const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex & 0xFFFFFF; } + int8_t GetTLSArrayOffset() const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex >> 24; } + bool IsAllocated() const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex != 0xFFFFFFFF;} static TLSIndex Unallocated() { LIMITED_METHOD_DAC_CONTRACT; return TLSIndex(0xFFFFFFFF); } - bool operator == (TLSIndex index) { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex == index.TLSIndexRawIndex; } - bool operator != (TLSIndex index) { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex != index.TLSIndexRawIndex; } + bool operator == (TLSIndex index) const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex == index.TLSIndexRawIndex; } + bool operator != (TLSIndex index) const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex != index.TLSIndexRawIndex; } }; struct TLSArray @@ -116,9 +117,11 @@ typedef DPTR(InFlightTLSData) PTR_InFlightTLSData; struct ThreadLocalData { int32_t cTLSData; // Size in bytes of offset into the TLS array which is valid + int32_t cLoaderHandles; TADDR pTLSArrayData; // Points at the Thread local array data. Thread *pThread; PTR_InFlightTLSData pInFlightData; // Points at the in-flight TLS data (TLS data that exists before the class constructor finishes running) + PTR_LOADERHANDLE pLoaderHandles; }; typedef DPTR(ThreadLocalData) PTR_ThreadLocalData; @@ -131,28 +134,231 @@ extern __thread ThreadLocalData t_ThreadStatics; #endif // _MSC_VER #endif // DACCESS_COMPILE -template -struct LookupMap; +class TLSIndexToMethodTableMap +{ + PTR_TADDR pMap; + uint32_t m_maxIndex; + uint32_t m_collectibleEntries; + + TADDR IsGCFlag() const { return (TADDR)0x1; } + TADDR IsCollectibleFlag() const { return (TADDR)0x2; } + TADDR UnwrapValue(TADDR input) const { return input & ~3; } +public: + TLSIndexToMethodTableMap() : pMap(dac_cast(dac_cast(0))), m_maxIndex(0), m_collectibleEntries(0) { } + + PTR_MethodTable Lookup(TLSIndex index, bool *isGCStatic, bool *isCollectible) const + { + LIMITED_METHOD_CONTRACT; + *isGCStatic = false; + *isCollectible = false; + if (index.TLSIndexRawIndex < VolatileLoad(&m_maxIndex)) + { + TADDR rawValue = VolatileLoadWithoutBarrier(&VolatileLoad(&pMap)[index.TLSIndexRawIndex]); + if (IsClearedValue(rawValue)) + { + return NULL; + } + *isGCStatic = (rawValue & IsGCFlag()) != 0; + *isCollectible = (rawValue & IsCollectibleFlag()) != 0; + return (PTR_MethodTable)UnwrapValue(rawValue); + } + return NULL; + } + + PTR_MethodTable LookupTlsIndexKnownToBeAllocated(TLSIndex index) const + { + LIMITED_METHOD_CONTRACT; + if (index.TLSIndexRawIndex < VolatileLoad(&m_maxIndex)) + { + TADDR rawValue = VolatileLoadWithoutBarrier(&VolatileLoad(&pMap)[index.TLSIndexRawIndex]); + return (PTR_MethodTable)UnwrapValue(rawValue); + } + return NULL; + } + + + struct entry + { + entry(TLSIndex tlsIndex) : pMT(dac_cast(dac_cast(0))), IsCollectible(false), IsGCStatic(false), IsClearedValue(false), ClearedMarker(0), TlsIndex(tlsIndex) { } + + PTR_MethodTable pMT; + bool IsCollectible; + bool IsGCStatic; + bool IsClearedValue; + uint16_t ClearedMarker; + TLSIndex TlsIndex; + }; + + entry Lookup(TLSIndex index) const + { + LIMITED_METHOD_CONTRACT; + entry e(index); + if (index.TLSIndexRawIndex < VolatileLoad(&m_maxIndex)) + { + TADDR rawValue = VolatileLoadWithoutBarrier(&VolatileLoad(&pMap)[index.TLSIndexRawIndex]); + if (!IsClearedValue(rawValue)) + { + e.pMT = (PTR_MethodTable)UnwrapValue(rawValue); + e.IsCollectible = (rawValue & IsCollectibleFlag()) != 0; + e.IsGCStatic = (rawValue & IsGCFlag()) != 0; + } + else + { + e.IsClearedValue = true; + e.ClearedMarker = GetClearedMarker(rawValue); + } + } + else + { + e.TlsIndex = TLSIndex(m_maxIndex); + } + return e; + } + + class iterator + { + friend class TLSIndexToMethodTableMap; + const TLSIndexToMethodTableMap& m_pMap; + entry m_entry; + iterator(const TLSIndexToMethodTableMap& pMap, uint32_t currentIndex) : m_pMap(pMap), m_entry(pMap.Lookup(TLSIndex(currentIndex))) {} + public: + const entry& operator*() const { return m_entry; } + const entry* operator->() const { return &m_entry; } + + bool operator==(const iterator& other) const { return (m_entry.TlsIndex == other.m_entry.TlsIndex); } + bool operator!=(const iterator& other) const { return (m_entry.TlsIndex != other.m_entry.TlsIndex); } + + iterator& operator++() + { + m_entry = m_pMap.Lookup(TLSIndex(m_entry.TlsIndex.TLSIndexRawIndex + 1)); + return *this; + } + iterator operator++(int) { iterator tmp = *this; ++(*this); return tmp; } + }; + + iterator begin() const + { + iterator it(*this, 0); + return it; + } + + iterator end() const + { + return iterator(*this, m_maxIndex); + } + + class CollectibleEntriesCollection + { + friend class TLSIndexToMethodTableMap; + const TLSIndexToMethodTableMap& m_pMap; + + CollectibleEntriesCollection(const TLSIndexToMethodTableMap& pMap) : m_pMap(pMap) {} + + public: + + class iterator + { + friend class CollectibleEntriesCollection; + TLSIndexToMethodTableMap::iterator m_current; + iterator(const TLSIndexToMethodTableMap::iterator& current) : m_current(current) {} + public: + const entry& operator*() const { return *m_current; } + const entry* operator->() const { return m_current.operator->(); } + + bool operator==(const iterator& other) const { return (m_current == other.m_current); } + bool operator!=(const iterator& other) const { return (m_current != other.m_current); } + + iterator& operator++() + { + TLSIndex oldIndex = m_current->TlsIndex; + while (++m_current, m_current->TlsIndex != oldIndex) + { + if (m_current->IsCollectible) + break; + oldIndex = m_current->TlsIndex; + } + return *this; + } + iterator operator++(int) { iterator tmp = *this; ++(*this); return tmp; } + }; + + iterator begin() const + { + iterator it(m_pMap.begin()); + if (!(it->IsCollectible)) + { + ++it; + } + return it; + } + + iterator end() const + { + return iterator(m_pMap.end()); + } + }; + + bool HasCollectibleEntries() const + { + LIMITED_METHOD_CONTRACT; + return VolatileLoadWithoutBarrier(&m_collectibleEntries) > 0; + } + + CollectibleEntriesCollection CollectibleEntries() const + { + return CollectibleEntriesCollection(*this); + } + + static bool IsClearedValue(TADDR value) + { + LIMITED_METHOD_CONTRACT; + return (value & 0x3FF) == value && value != 0; + } + + static uint16_t GetClearedMarker(TADDR value) + { + LIMITED_METHOD_CONTRACT; + return (uint16_t)((value & 0x3FFFF) >> 2); + } + +#ifndef DACCESS_COMPILE + void Set(TLSIndex index, PTR_MethodTable pMT, bool isGCStatic); + bool FindClearedIndex(uint16_t whenClearedMarkerToAvoid, TLSIndex* pIndex); + void Clear(TLSIndex index, uint16_t whenCleared); +#endif // !DACCESS_COMPILE + +#ifdef DACCESS_COMPILE + void EnumMemoryRegions(CLRDataEnumMemoryFlags flags) + { + SUPPORTS_DAC; + DAC_ENUM_DTHIS(); + if (pMap != NULL) + { + DacEnumMemoryRegion(dac_cast(pMap), m_maxIndex * sizeof(TADDR)); + } + } +#endif +}; -typedef LookupMap TLSIndexToMethodTableMap; typedef DPTR(TLSIndexToMethodTableMap) PTR_TLSIndexToMethodTableMap; GPTR_DECL(TLSIndexToMethodTableMap, g_pThreadStaticTypeIndices); -PTR_MethodTable LookupMethodTableForThreadStatic(TLSIndex index); -PTR_VOID GetThreadLocalStaticBaseNoCreate(PTR_ThreadLocalData pThreadLocalData, TLSIndex index); -void ScanThreadStaticRoots(PTR_ThreadLocalData pThreadLocalData, promote_func* fn, ScanContext* sc); +PTR_VOID GetThreadLocalStaticBaseNoCreate(ThreadLocalData *pThreadLocalData, TLSIndex index); +void ScanThreadStaticRoots(ThreadLocalData *pThreadLocalData, bool forGC, promote_func* fn, ScanContext* sc); #ifndef DACCESS_COMPILE +PTR_MethodTable LookupMethodTableForThreadStaticKnownToBeAllocated(TLSIndex index); void InitializeThreadStaticData(); void InitializeCurrentThreadsStaticData(Thread* pThread); -void FreeCurrentThreadStaticData(); +void FreeThreadStaticData(ThreadLocalData *pThreadLocalData); void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pIndex); -void FreeTLSIndexForThreadStatic(TLSIndex index); +void FreeTLSIndicesForLoaderAllocator(LoaderAllocator *pLoaderAllocator); void* GetThreadLocalStaticBase(TLSIndex index); void GetThreadLocalStaticBlocksInfo (CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo); bool CanJITOptimizeTLSAccess(); +void NotifyThreadStaticGCHappened(); #else -void EnumThreadMemoryRegions(PTR_ThreadLocalData pThreadLocalData, CLRDataEnumMemoryFlags flags); +void EnumThreadMemoryRegions(ThreadLocalData* pThreadLocalData, CLRDataEnumMemoryFlags flags); #endif -#endif // __THREADLOCALSTORAGE_H__ \ No newline at end of file +#endif // __THREADLOCALSTORAGE_H__ diff --git a/src/coreclr/vm/vars.hpp b/src/coreclr/vm/vars.hpp index d02de1bef0dd2..f5a40cb91dde7 100644 --- a/src/coreclr/vm/vars.hpp +++ b/src/coreclr/vm/vars.hpp @@ -91,6 +91,7 @@ class RCWCleanupList; typedef TADDR LOADERHANDLE; typedef TADDR RUNTIMETYPEHANDLE; +typedef DPTR(LOADERHANDLE) PTR_LOADERHANDLE; #ifdef DACCESS_COMPILE void OBJECTHANDLE_EnumMemoryRegions(OBJECTHANDLE handle); From b8ef938d8188d4fab850c8965bdcff9b29d91c3a Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 13 Mar 2024 16:34:34 -0700 Subject: [PATCH 26/82] Handle scenario with a RVA statc field and a class constructor, but no other types of fields --- src/coreclr/vm/jitinterface.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index ac5f60666eb40..68e112a5a93e3 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -5980,8 +5980,10 @@ CorInfoHelpFunc CEEInfo::getSharedCCtorHelper(CORINFO_CLASS_HANDLE clsHnd) { if (VMClsHnd.GetMethodTable()->GetClass()->GetNonGCRegularStaticFieldBytes() > 0) result = CORINFO_HELP_GET_NONGCSTATIC_BASE; - else + else if (VMClsHnd.GetMethodTable()->GetClass()->GetNumHandleRegularStatics() > 0) result = CORINFO_HELP_GET_GCSTATIC_BASE; + else + result = CORINFO_HELP_INITCLASS; } else result = CORINFO_HELP_INITCLASS; From fce7f9f77232a3321c1baf14b360a2b4f7ff2821 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 14 Mar 2024 11:42:53 -0700 Subject: [PATCH 27/82] Fix static field reading for R2R --- src/coreclr/vm/prestub.cpp | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 67b6e56f5a138..612b8b85bde67 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -3215,7 +3215,31 @@ static PCODE getHelperForSharedStatic(Module * pModule, CORCOMPILE_FIXUP_BLOB_KI AllocMem(S_SIZE_T(sizeof(StaticFieldAddressArgs)))); pArgs->staticBaseHelper = (FnStaticBaseHelper)CEEJitInfo::getHelperFtnStatic((CorInfoHelpFunc)helpFunc); - pArgs->arg0 = (TADDR)pMT; + + switch(helpFunc) + { + case CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE: + case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE: + pArgs->arg0 = (TADDR)pMT->GetThreadStaticsInfo(); + break; + + case CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETPINNED_GCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE: + case CORINFO_HELP_GETPINNED_GCSTATIC_BASE: + case CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE: + case CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE: + pArgs->arg0 = (TADDR)pMT->GetDynamicStaticsInfo(); + break; + default: + _ASSERTE(!"Unexpected shared statics helper CORINFO_HELP_FUNC"); + pArgs->arg0 = 0; + break; + } pArgs->offset = pFD->GetOffset(); PCODE pHelper = DynamicHelpers::CreateHelper(pModule->GetLoaderAllocator(), (TADDR)pArgs, From 8bd309c5040b857a30c514990ca710420858b355 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 14 Mar 2024 15:35:59 -0700 Subject: [PATCH 28/82] Attempt to fixup arm32 behavior - 8 byte align static data Also fix up a variety of oopses noted while debugging --- src/coreclr/vm/loaderallocator.cpp | 21 +++++++++++++++++++++ src/coreclr/vm/methodtable.cpp | 2 +- src/coreclr/vm/methodtable.h | 1 - src/coreclr/vm/threadstatics.cpp | 25 ++++++++++++++----------- src/coreclr/vm/threadstatics.h | 8 -------- 5 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index b405562db01eb..47f4a075bf7a7 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -2235,7 +2235,28 @@ void LoaderAllocator::AllocateBytesForStaticVariables(DynamicStaticsInfo* pStati } else { + uint32_t initialcbMem = cbMem; + if (initialcbMem >= 8) + { + cbMem = ALIGN_UP(cbMem, sizeof(double)); +#ifndef TARGET_64BIT + cbMem += 4; // We need to align the memory to 8 bytes so that static doubles, and static int64's work + // and this allocator doesn't guarantee 8 byte alignment, so allocate 4 bytes extra, and alignup. + // Technically this isn't necessary on X86, but it's simpler to do it unconditionally. +#endif + } + else + { + // Always allocate in multiples of pointer size + cbMem = sizeof(TADDR); + } uint8_t* pbMem = (uint8_t*)(void*)GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbMem)); + if (initialcbMem >= 8) + { +#ifdef TARGET_64BIT + pbMem = (uint8_t*)ALIGN_UP(pbMem, 8); +#endif + } pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */false, (TADDR)pbMem); } } diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 37f53b71fce74..7447f40b7feeb 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -4385,7 +4385,7 @@ void MethodTable::EnsureStaticDataAllocated() GetLoaderAllocator()->AllocateBytesForStaticVariables(pDynamicStaticsInfo, GetClass()->GetNonGCRegularStaticFieldBytes()); if (pDynamicStaticsInfo->GetGCStaticsPointer() == NULL) - GetLoaderAllocator()->AllocateGCHandlesBytesForStaticVariables(pDynamicStaticsInfo, GetClass()->GetNumHandleRegularStatics(), this); + GetLoaderAllocator()->AllocateGCHandlesBytesForStaticVariables(pDynamicStaticsInfo, GetClass()->GetNumHandleRegularStatics(), this->HasBoxedRegularStatics() ? this : NULL); } pAuxiliaryData->SetIsStaticDataAllocated(); } diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 2a2dd088328cf..118dd9f8f5a92 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -556,7 +556,6 @@ struct DynamicStaticsInfo TADDR m_pGCStatics; // Always access through helper methods to properly handle the ISCLASSNOTINITED bit TADDR m_pNonGCStatics; // Always access through helper methods to properly handle the ISCLASSNOTINITED bit PTR_MethodTable m_pMethodTable; - MethodTableAuxiliaryData m_AuxData; PTR_OBJECTREF GetGCStaticsPointer() { TADDR staticsVal = VolatileLoadWithoutBarrier(&m_pGCStatics); return dac_cast(staticsVal & STATICSPOINTERMASK); } PTR_BYTE GetNonGCStaticsPointer() { TADDR staticsVal = VolatileLoadWithoutBarrier(&m_pNonGCStatics); return dac_cast(staticsVal & STATICSPOINTERMASK); } PTR_OBJECTREF GetGCStaticsPointerAssumeIsInited() { TADDR staticsVal = m_pGCStatics; _ASSERTE(staticsVal != 0); _ASSERTE((staticsVal & (ISCLASSNOTINITEDMASK)) == 0); return dac_cast(staticsVal); } diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 0e250b637e4aa..04a9873153161 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -30,15 +30,13 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(ThreadLocalData* pThreadLocalData, TLS SpinLockHolder spinLock(&pThreadLocalData->pThread->m_TlsSpinLock); #endif TADDR pTLSBaseAddress = NULL; - PTR_TLSArray pTLSArray = dac_cast(dac_cast(pThreadLocalData) + index.GetTLSArrayOffset()); - - int32_t cTLSData = pTLSArray->cTLSData; + int32_t cTLSData = pThreadLocalData->cTLSData; if (cTLSData <= index.GetByteIndex()) { return NULL; } - TADDR pTLSArrayData = pTLSArray->pTLSArrayData; + TADDR pTLSArrayData = pThreadLocalData->pTLSArrayData; pTLSBaseAddress = *dac_cast(dac_cast(pTLSArrayData) + index.GetByteIndex()); if (pTLSBaseAddress == NULL) { @@ -364,12 +362,11 @@ void* GetThreadLocalStaticBase(TLSIndex index) } CONTRACTL_END; - TLSArray* pTLSArray = reinterpret_cast((uint8_t*)&t_ThreadStatics + index.GetTLSArrayOffset()); bool isGCStatic; bool isCollectible; MethodTable *pMT = LookupMethodTableAndFlagForThreadStatic(index, &isGCStatic, &isCollectible); - int32_t cTLSData = pTLSArray->cTLSData; + int32_t cTLSData = t_ThreadStatics.cTLSData; if (cTLSData <= index.GetByteIndex()) { // Grow the underlying TLS array @@ -378,11 +375,11 @@ void* GetThreadLocalStaticBase(TLSIndex index) uint8_t* pNewTLSArrayData = new uint8_t[newcTLSData]; memset(pNewTLSArrayData, 0, newcTLSData); if (cTLSData > 0) - memcpy(pNewTLSArrayData, (void*)pTLSArray->pTLSArrayData, cTLSData + 1); - uint8_t* pOldArray = (uint8_t*)pTLSArray->pTLSArrayData; - pTLSArray->pTLSArrayData = (TADDR)pNewTLSArrayData; + memcpy(pNewTLSArrayData, (void*)t_ThreadStatics.pTLSArrayData, cTLSData + 1); + uint8_t* pOldArray = (uint8_t*)t_ThreadStatics.pTLSArrayData; + t_ThreadStatics.pTLSArrayData = (TADDR)pNewTLSArrayData; cTLSData = newcTLSData - 1; - pTLSArray->cTLSData = cTLSData; + t_ThreadStatics.cTLSData = cTLSData; delete[] pOldArray; t_ThreadStatics.pThread->m_ThreadLocalDataThreadObjectCopy = t_ThreadStatics; } @@ -405,7 +402,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) t_ThreadStatics.pThread->m_ThreadLocalDataThreadObjectCopy = t_ThreadStatics; } - TADDR pTLSArrayData = pTLSArray->pTLSArrayData; + TADDR pTLSArrayData = t_ThreadStatics.pTLSArrayData; TADDR *ppTLSBaseAddress = reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetByteIndex()); TADDR pTLSBaseAddress = *ppTLSBaseAddress; @@ -454,7 +451,13 @@ void* GetThreadLocalStaticBase(TLSIndex index) } else { +#ifndef TARGET_64BIT + // On non 64 bit platforms, the static data may need to be 8 byte aligned to allow for good performance + // for doubles and 64bit ints, come as close as possible, by simply allocating the data as a double array + gc.tlsEntry = AllocatePrimiteveArray(ELEMENT_TYPE_R8, static_cast(AlignUp(pMT->GetClass()->GetNonGCThreadStaticFieldBytes(), 8)/8)); +#else gc.tlsEntry = AllocatePrimitiveArray(ELEMENT_TYPE_I1, static_cast(pMT->GetClass()->GetNonGCThreadStaticFieldBytes())); +#endif } NewHolder pInFlightData = NULL; diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index e40742bc642c7..e2be621e28610 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -96,20 +96,12 @@ struct TLSIndex TLSIndex(uint32_t rawIndex) : TLSIndexRawIndex(rawIndex) { } uint32_t TLSIndexRawIndex; int32_t GetByteIndex() const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex & 0xFFFFFF; } - int8_t GetTLSArrayOffset() const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex >> 24; } bool IsAllocated() const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex != 0xFFFFFFFF;} static TLSIndex Unallocated() { LIMITED_METHOD_DAC_CONTRACT; return TLSIndex(0xFFFFFFFF); } bool operator == (TLSIndex index) const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex == index.TLSIndexRawIndex; } bool operator != (TLSIndex index) const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex != index.TLSIndexRawIndex; } }; -struct TLSArray -{ - int32_t cTLSData; // Size in bytes of offset into the TLS array which is valid - TADDR pTLSArrayData; // Points at the Thread local array data. -}; -typedef DPTR(TLSArray) PTR_TLSArray; - // Used to store access to TLS data for a single index when the TLS is accessed while the class constructor is running struct InFlightTLSData; typedef DPTR(InFlightTLSData) PTR_InFlightTLSData; From 7db100e8332da1a99927fc0c6c4901a042dbd22d Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 14 Mar 2024 15:48:22 -0700 Subject: [PATCH 29/82] Merge branch 'main' of github.com:dotnet/runtime into make_normal_statics_simpler --- src/coreclr/inc/jiteeversionguid.h | 10 +++++----- .../tools/superpmi/superpmi-shared/methodcontext.h | 7 ++----- src/coreclr/vm/threadstatics.cpp | 2 +- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index dfeb6c303a010..2a067e78ea2db 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 17928f97-1b60-42c1-b4f2-63a7fb4cbe0b */ - 0x17928f97, - 0x1b60, - 0x42c1, - {0xb4, 0xf2, 0x63, 0xa7, 0xfb, 0x4c, 0xbe, 0x0b} +constexpr GUID JITEEVersionIdentifier = { /* 724558e6-1501-40eb-908f-aea11b38339a */ + 0x724558e6, + 0x1501, + 0x40eb, + {0x90, 0x8f, 0xae, 0xa1, 0x1b, 0x38, 0x33, 0x9a} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index dd2f869b37e4c..c208014376659 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -1168,12 +1168,9 @@ enum mcPackets Packet_HaveSameMethodDefinition = 213, Packet_NotifyMethodInfoUsage = 214, Packet_IsExactType = 215, -<<<<<<< HEAD - Packet_GetClassStaticDynamicInfo = 216, - Packet_GetClassThreadStaticDynamicInfo = 217, -======= Packet_GetSwiftLowering = 216, ->>>>>>> 9b40636087d24c8e9107d4a112589b053317591d + Packet_GetClassStaticDynamicInfo = 217, + Packet_GetClassThreadStaticDynamicInfo = 218, }; void SetDebugDumpVariables(); diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 04a9873153161..806961a691fde 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -454,7 +454,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) #ifndef TARGET_64BIT // On non 64 bit platforms, the static data may need to be 8 byte aligned to allow for good performance // for doubles and 64bit ints, come as close as possible, by simply allocating the data as a double array - gc.tlsEntry = AllocatePrimiteveArray(ELEMENT_TYPE_R8, static_cast(AlignUp(pMT->GetClass()->GetNonGCThreadStaticFieldBytes(), 8)/8)); + gc.tlsEntry = AllocatePrimitiveArray(ELEMENT_TYPE_R8, static_cast(AlignUp(pMT->GetClass()->GetNonGCThreadStaticFieldBytes(), 8)/8)); #else gc.tlsEntry = AllocatePrimitiveArray(ELEMENT_TYPE_I1, static_cast(pMT->GetClass()->GetNonGCThreadStaticFieldBytes())); #endif From 1595cf0c29c11faa0969be3d0af946f2b0ab880d Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 14 Mar 2024 21:13:47 -0700 Subject: [PATCH 30/82] Continued fallout from my possibly ill considered decision to make the layout of `ThreadLocalData` slightly more optimal - Turns out there was more code which depended on the offset and pointer for thread static data being next to each other. I believe this is now fixed --- src/coreclr/inc/corinfo.h | 1 + src/coreclr/jit/helperexpansion.cpp | 5 +++-- src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs | 1 + src/coreclr/tools/superpmi/superpmi-shared/agnostic.h | 1 + .../tools/superpmi/superpmi-shared/methodcontext.cpp | 7 +++++-- src/coreclr/vm/threadstatics.cpp | 3 ++- 6 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 8b91b3d6f7829..20c988500c555 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -1728,6 +1728,7 @@ struct CORINFO_THREAD_STATIC_BLOCKS_INFO void* tlsIndexObject; // linux/x64 specific - address of tls_index object void* threadVarsSection; // osx x64/arm64 specific - address of __thread_vars section of `t_ThreadStatics` uint32_t offsetOfThreadLocalStoragePointer; // windows specific + uint32_t offsetOfMaxThreadStaticBlocks; uint32_t offsetOfThreadStaticBlocks; }; diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index 0842de19a1035..efffcdc22d16e 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -837,6 +837,7 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* JITDUMP("threadVarsSection= %p\n", dspPtr(threadStaticBlocksInfo.threadVarsSection)); JITDUMP("offsetOfThreadLocalStoragePointer= %u\n", dspOffset(threadStaticBlocksInfo.offsetOfThreadLocalStoragePointer)); + JITDUMP("offsetOfMaxThreadStaticBlocks= %u\n", dspOffset(threadStaticBlocksInfo.offsetOfMaxThreadStaticBlocks)); JITDUMP("offsetOfThreadStaticBlocks= %u\n", dspOffset(threadStaticBlocksInfo.offsetOfThreadStaticBlocks)); assert(call->gtArgs.CountArgs() == 1); @@ -988,8 +989,8 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* tlsValueDef = gtNewStoreLclVarNode(tlsLclNum, tlsValue); GenTree* tlsLclValueUse = gtNewLclVarNode(tlsLclNum); - size_t offsetOfThreadStaticBlocksVal = threadStaticBlocksInfo.offsetOfThreadStaticBlocks + TARGET_POINTER_SIZE; - size_t offsetOfMaxThreadStaticBlocksVal = threadStaticBlocksInfo.offsetOfThreadStaticBlocks; + size_t offsetOfThreadStaticBlocksVal = threadStaticBlocksInfo.offsetOfThreadStaticBlocks; + size_t offsetOfMaxThreadStaticBlocksVal = threadStaticBlocksInfo.offsetOfMaxThreadStaticBlocks; // Create tree for "maxThreadStaticBlocks = tls[offsetOfMaxThreadStaticBlocks]" GenTree* offsetOfMaxThreadStaticBlocks = gtNewIconNode(offsetOfMaxThreadStaticBlocksVal, TYP_I_IMPL); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 1c2fa821be88e..1bd4c0c1ae1cf 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1156,6 +1156,7 @@ public unsafe struct CORINFO_THREAD_STATIC_BLOCKS_INFO public nuint tlsIndexObject; public nuint threadVarsSection; public uint offsetOfThreadLocalStoragePointer; + public uint offsetOfMaxThreadStaticBlocks; public uint offsetOfThreadStaticBlocks; }; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h index b5fd9ed62cae2..b5bb5a7e42ec7 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h @@ -535,6 +535,7 @@ struct Agnostic_GetThreadLocalStaticBlocksInfo DWORDLONG tlsIndexObject; DWORDLONG threadVarsSection; DWORD offsetOfThreadLocalStoragePointer; + DWORD offsetOfMaxThreadStaticBlocks; DWORD offsetOfThreadStaticBlocks; }; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index d01f1195405d7..50e57ec2e8646 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -3633,6 +3633,7 @@ void MethodContext::recGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOC value.tlsIndexObject = CastPointer(pInfo->tlsIndexObject); value.threadVarsSection = CastPointer(pInfo->threadVarsSection); value.offsetOfThreadLocalStoragePointer = pInfo->offsetOfThreadLocalStoragePointer; + value.offsetOfMaxThreadStaticBlocks = pInfo->offsetOfMaxThreadStaticBlocks; value.offsetOfThreadStaticBlocks = pInfo->offsetOfThreadStaticBlocks; // This data is same for entire process, so just add it against key '0'. @@ -3647,10 +3648,11 @@ void MethodContext::dmpGetThreadLocalStaticBlocksInfo(DWORD key, const Agnostic_ ", tlsGetAddrFtnPtr-%016" PRIX64 ", tlsIndexObject - %016" PRIX64 ", threadVarsSection - %016" PRIX64 ", offsetOfThreadLocalStoragePointer-%u" - ", offsetOfThreadStaticBlocks-%u", + ", offsetOfMaxThreadStaticBlocks-%u" + ", offsetOfThreadStaticBlocks-%u", key, SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(value.tlsIndex).c_str(), value.tlsGetAddrFtnPtr, value.tlsIndexObject, value.threadVarsSection, value.offsetOfThreadLocalStoragePointer, - value.offsetOfThreadStaticBlocks); + value.offsetOfMaxThreadStaticBlocks, value.offsetOfThreadStaticBlocks); } void MethodContext::repGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) @@ -3665,6 +3667,7 @@ void MethodContext::repGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOC pInfo->tlsIndexObject = (void*)value.tlsIndexObject; pInfo->threadVarsSection = (void*)value.threadVarsSection; pInfo->offsetOfThreadLocalStoragePointer = value.offsetOfThreadLocalStoragePointer; + pInfo->offsetOfMaxThreadStaticBlocks = value.offsetOfMaxThreadStaticBlocks; pInfo->offsetOfThreadStaticBlocks = value.offsetOfThreadStaticBlocks; } diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 806961a691fde..42a23a9249c14 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -699,7 +699,8 @@ void GetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) _ASSERTE_MSG(false, "Unsupported scenario of optimizing TLS access on Linux Arm32/x86"); #endif // TARGET_WINDOWS - pInfo->offsetOfThreadStaticBlocks = (uint32_t)threadStaticBaseOffset; + pInfo->offsetOfMaxThreadStaticBlocks = (uint32_t)(threadStaticBaseOffset + offsetof(ThreadLocalData, cTLSData)); + pInfo->offsetOfThreadStaticBlocks = (uint32_t)(threadStaticBaseOffset + offsetof(ThreadLocalData, pTLSArrayData)); } #endif // !DACCESS_COMPILE From 961744dc4a315c0bb358b7602a9f157478b6fcbf Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 15 Mar 2024 10:27:55 -0700 Subject: [PATCH 31/82] Fix InFlightTLSData so that values are properly kept for thread statics in cctors --- src/coreclr/vm/threadstatics.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 42a23a9249c14..08c0283ca269c 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -6,7 +6,7 @@ struct InFlightTLSData { #ifndef DACCESS_COMPILE - InFlightTLSData(TLSIndex index, TADDR pTLSData) : pNext(NULL), tlsIndex(index), pTLSData(pTLSData) { } + InFlightTLSData(TLSIndex index) : pNext(NULL), tlsIndex(index), pTLSData(dac_cast(NULL)) { } #endif // !DACCESS_COMPILE PTR_InFlightTLSData pNext; // Points at the next in-flight TLS data TLSIndex tlsIndex; // The TLS index for the static @@ -463,7 +463,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) NewHolder pInFlightData = NULL; if (!pMT->IsClassInited()) { - pInFlightData = new InFlightTLSData(index, pTLSBaseAddress); + pInFlightData = new InFlightTLSData(index); } if (isCollectible) @@ -488,6 +488,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) { SpinLockHolder spinLock(&t_ThreadStatics.pThread->m_TlsSpinLock); pInFlightData->pNext = t_ThreadStatics.pInFlightData; + pInFlightData->pTLSData = pTLSBaseAddress; t_ThreadStatics.pInFlightData = pInFlightData; t_ThreadStatics.pThread->m_ThreadLocalDataThreadObjectCopy = t_ThreadStatics; } From 69de672745fc238cfe97b95c3c2cd131ba9a8257 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 18 Mar 2024 16:06:40 -0700 Subject: [PATCH 32/82] Fix oops in statics allocation --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 16 +++++++++++++--- src/coreclr/vm/loaderallocator.cpp | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index bc07064795582..5d08e5641b2f5 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -5619,10 +5619,13 @@ void DacDbiInterfaceImpl::LookupEnCVersions(Module* pModule, DebuggerJitInfo * pDJI = NULL; EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY { - pDMI = g_pDebugger->GetOrCreateMethodInfo(pModule, mdMethod); - if (pDMI != NULL) + if (g_pDebugger != NULL) { - pDJI = pDMI->FindJitInfo(pMD, CORDB_ADDRESS_TO_TADDR(pNativeStartAddress)); + pDMI = g_pDebugger->GetOrCreateMethodInfo(pModule, mdMethod); + if (pDMI != NULL) + { + pDJI = pDMI->FindJitInfo(pMD, CORDB_ADDRESS_TO_TADDR(pNativeStartAddress)); + } } } EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY; @@ -7471,6 +7474,10 @@ HRESULT DacDbiInterfaceImpl::GetDefinesBitField(ULONG32 *pDefines) DD_ENTER_MAY_THROW; if (pDefines == NULL) return E_INVALIDARG; + + if (g_pDebugger == NULL) + return CORDBG_E_NOTREADY; + *pDefines = g_pDebugger->m_defines; return S_OK; } @@ -7480,6 +7487,9 @@ HRESULT DacDbiInterfaceImpl::GetMDStructuresVersion(ULONG32* pMDStructuresVersio DD_ENTER_MAY_THROW; if (pMDStructuresVersion == NULL) return E_INVALIDARG; + if (g_pDebugger == NULL) + return CORDBG_E_NOTREADY; + *pMDStructuresVersion = g_pDebugger->m_mdDataStructureVersion; return S_OK; } diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index 47f4a075bf7a7..ffbcbd401265d 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -2248,7 +2248,7 @@ void LoaderAllocator::AllocateBytesForStaticVariables(DynamicStaticsInfo* pStati else { // Always allocate in multiples of pointer size - cbMem = sizeof(TADDR); + cbMem = ALIGN_UP(cbMem, sizeof(TADDR)); } uint8_t* pbMem = (uint8_t*)(void*)GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbMem)); if (initialcbMem >= 8) From 94b58ed08686539a8a1668c230012b84b0add3f4 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 19 Mar 2024 10:21:57 -0700 Subject: [PATCH 33/82] Zero out cTLSData when cleaning up thread statics --- src/coreclr/vm/threadstatics.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 08c0283ca269c..622ee394ca709 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -329,6 +329,13 @@ void AllocateThreadStaticBoxes(MethodTable *pMT, PTRARRAYREF *ppRef) void FreeThreadStaticData(ThreadLocalData *pThreadLocalData) { + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + if (pThreadLocalData->cLoaderHandles > 0) { CrstHolder ch(&g_TLSCrst); @@ -341,6 +348,7 @@ void FreeThreadStaticData(ThreadLocalData *pThreadLocalData) delete[] (uint8_t*)pThreadLocalData->pTLSArrayData; pThreadLocalData->pTLSArrayData = 0; + pThreadLocalData->cTLSData = 0; while (pThreadLocalData->pInFlightData != NULL) { From 170f74cf535ba49f6d5f8c3aa41172a91f43274f Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 19 Mar 2024 14:15:26 -0700 Subject: [PATCH 34/82] Fix static field alignment on 32bit platforms --- src/coreclr/vm/loaderallocator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index ffbcbd401265d..29f1f30c84037 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -2251,12 +2251,12 @@ void LoaderAllocator::AllocateBytesForStaticVariables(DynamicStaticsInfo* pStati cbMem = ALIGN_UP(cbMem, sizeof(TADDR)); } uint8_t* pbMem = (uint8_t*)(void*)GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbMem)); +#ifndef TARGET_64BIT // Second part of alignment work if (initialcbMem >= 8) { -#ifdef TARGET_64BIT pbMem = (uint8_t*)ALIGN_UP(pbMem, 8); -#endif } +#endif pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */false, (TADDR)pbMem); } } From 87ae678d0f579d4f6bba7434bfa25e1a0939364a Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 19 Mar 2024 20:40:03 -0700 Subject: [PATCH 35/82] Fix handling of cleared markers for thread statics --- src/coreclr/vm/threadstatics.cpp | 9 +++++---- src/coreclr/vm/threadstatics.h | 10 +++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 622ee394ca709..e38c0d82a3795 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -134,7 +134,7 @@ bool ReportTLSIndexCarefully(TLSIndex index, int32_t cLoaderHandles, PTR_LOADERH // We use a scheme where the TLS data on each thread will be cleaned up within a GC promotion round or two. #ifndef DACCESS_COMPILE -static Volatile s_GCsWhichDoRelocateAndCanEmptyOutTheTLSIndices = 0; +static Volatile s_GCsWhichDoRelocateAndCanEmptyOutTheTLSIndices = 0; void NotifyThreadStaticGCHappened() { @@ -229,7 +229,7 @@ void TLSIndexToMethodTableMap::Set(TLSIndex index, PTR_MethodTable pMT, bool isG pMap[index.TLSIndexRawIndex] = rawValue; } -void TLSIndexToMethodTableMap::Clear(TLSIndex index, uint16_t whenCleared) +void TLSIndexToMethodTableMap::Clear(TLSIndex index, uint8_t whenCleared) { CONTRACTL { @@ -248,15 +248,16 @@ void TLSIndexToMethodTableMap::Clear(TLSIndex index, uint16_t whenCleared) } pMap[index.TLSIndexRawIndex] = (whenCleared << 2) | 0x3; _ASSERTE(GetClearedMarker(pMap[index.TLSIndexRawIndex]) == whenCleared); + _ASSERTE(IsClearedValue(pMap[index.TLSIndexRawIndex])); } -bool TLSIndexToMethodTableMap::FindClearedIndex(uint16_t whenClearedMarkerToAvoid, TLSIndex* pIndex) +bool TLSIndexToMethodTableMap::FindClearedIndex(uint8_t whenClearedMarkerToAvoid, TLSIndex* pIndex) { for (const auto& entry : *this) { if (entry.IsClearedValue) { - uint16_t whenClearedMarker = entry.ClearedMarker; + uint8_t whenClearedMarker = entry.ClearedMarker; if ((whenClearedMarker == whenClearedMarkerToAvoid) || (whenClearedMarker == (whenClearedMarkerToAvoid - 1)) || (whenClearedMarker == (whenClearedMarkerToAvoid - 2))) diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index e2be621e28610..2b176a613cb05 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -177,7 +177,7 @@ class TLSIndexToMethodTableMap bool IsCollectible; bool IsGCStatic; bool IsClearedValue; - uint16_t ClearedMarker; + uint8_t ClearedMarker; TLSIndex TlsIndex; }; @@ -307,16 +307,16 @@ class TLSIndexToMethodTableMap return (value & 0x3FF) == value && value != 0; } - static uint16_t GetClearedMarker(TADDR value) + static uint8_t GetClearedMarker(TADDR value) { LIMITED_METHOD_CONTRACT; - return (uint16_t)((value & 0x3FFFF) >> 2); + return (uint8_t)((value & 0x3FF) >> 2); } #ifndef DACCESS_COMPILE void Set(TLSIndex index, PTR_MethodTable pMT, bool isGCStatic); - bool FindClearedIndex(uint16_t whenClearedMarkerToAvoid, TLSIndex* pIndex); - void Clear(TLSIndex index, uint16_t whenCleared); + bool FindClearedIndex(uint8_t whenClearedMarkerToAvoid, TLSIndex* pIndex); + void Clear(TLSIndex index, uint8_t whenCleared); #endif // !DACCESS_COMPILE #ifdef DACCESS_COMPILE From 4543b03ee87b60c28fa5dd1931cf03613214cee9 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 21 Mar 2024 15:18:38 -0700 Subject: [PATCH 36/82] Update comment --- src/coreclr/vm/gcenv.ee.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index c025e834d991e..6f04ba0b9dc18 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -314,7 +314,11 @@ void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen, // Scan the moveable GC Pointer table, which is used to ensure that // pointers into managed object which are kept alive by collectible // LoaderAllocators will be updated if their objects are moved. - // We use this for static variable bases and for + // We use this for collectible static variable bases. In the future we + // may also choose to use this for RuntimeType instances for collectible + // types. The intent is to be able to use this capability to allow the + // data structures for collectible assemblies to be implemented in the + // same way as the data structures for non-collectible assemblies. g_pMoveableGCPointerTracker->ScanTable(fn, sc); if (sc->thread_number == 0 || !GCHeapUtilities::IsServerHeap()) From 53a68a0c44dd6d99bcdf24e997d050318536a9bb Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 21 Mar 2024 15:38:25 -0700 Subject: [PATCH 37/82] Fix TLS indices to be pointer indexed offsets --- src/coreclr/jit/helperexpansion.cpp | 8 +++++--- src/coreclr/vm/jithelpers.cpp | 4 ++-- src/coreclr/vm/threadstatics.cpp | 30 ++++++++++++++--------------- src/coreclr/vm/threadstatics.h | 8 ++++---- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index ef1c84e99484a..a460eb3595f60 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -1004,12 +1004,14 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* // Create tree for "if (maxThreadStaticBlocks < typeIndex)" GenTree* typeThreadStaticBlockIndexValue = call->gtArgs.GetArgByIndex(0)->GetNode(); GenTree* maxThreadStaticBlocksCond = - gtNewOperNode(GT_LT, TYP_INT, maxThreadStaticBlocksValue, gtCloneExpr(typeThreadStaticBlockIndexValue)); + gtNewOperNode(GT_LE, TYP_INT, maxThreadStaticBlocksValue, gtCloneExpr(typeThreadStaticBlockIndexValue)); maxThreadStaticBlocksCond = gtNewOperNode(GT_JTRUE, TYP_VOID, maxThreadStaticBlocksCond); // Create tree to "threadStaticBlockValue = threadStaticBlockBase[typeIndex]" + typeThreadStaticBlockIndexValue = gtNewOperNode(GT_MUL, TYP_INT, gtCloneExpr(typeThreadStaticBlockIndexValue), + gtNewIconNode(TARGET_POINTER_SIZE, TYP_INT)); GenTree* typeThreadStaticBlockRef = - gtNewOperNode(GT_ADD, TYP_I_IMPL, threadStaticBlocksValue, gtCloneExpr(typeThreadStaticBlockIndexValue)); + gtNewOperNode(GT_ADD, TYP_I_IMPL, threadStaticBlocksValue, typeThreadStaticBlockIndexValue); GenTree* typeThreadStaticBlockValue = gtNewIndir(TYP_I_IMPL, typeThreadStaticBlockRef, GTF_IND_NONFAULTING); // Cache the threadStaticBlock value @@ -1028,7 +1030,7 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* // // maxThreadStaticBlocksCondBB (BBJ_COND): [weight: 1.0] // tlsValue = tls_access_code - // if (maxThreadStaticBlocks < typeIndex) + // if (maxThreadStaticBlocks <= typeIndex) // goto fallbackBb; // // threadStaticBlockNullCondBB (BBJ_COND): [weight: 1.0] diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index f247e6e080953..5748717b9ca6d 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1534,13 +1534,13 @@ FORCEINLINE void* GetThreadLocalStaticBaseIfExistsAndInitialized(TLSIndex index) TADDR pTLSBaseAddress = NULL; int32_t cTLSData = t_ThreadStatics.cTLSData; - if (cTLSData <= index.GetByteIndex()) + if (cTLSData <= index.GetIndexOffset()) { return NULL; } TADDR pTLSArrayData = t_ThreadStatics.pTLSArrayData; - pTLSBaseAddress = *reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetByteIndex()); + pTLSBaseAddress = *reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetIndexOffset()); return reinterpret_cast(pTLSBaseAddress); } diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index e38c0d82a3795..925ea03a5d20d 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -31,13 +31,13 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(ThreadLocalData* pThreadLocalData, TLS #endif TADDR pTLSBaseAddress = NULL; int32_t cTLSData = pThreadLocalData->cTLSData; - if (cTLSData <= index.GetByteIndex()) + if (cTLSData <= index.GetIndexOffset()) { return NULL; } TADDR pTLSArrayData = pThreadLocalData->pTLSArrayData; - pTLSBaseAddress = *dac_cast(dac_cast(pTLSArrayData) + index.GetByteIndex()); + pTLSBaseAddress = *(dac_cast(pTLSArrayData) + index.GetIndexOffset()); if (pTLSBaseAddress == NULL) { // Maybe it is in the InFlightData @@ -106,9 +106,9 @@ bool ReportTLSIndexCarefully(TLSIndex index, int32_t cLoaderHandles, PTR_LOADERH // The TLS index is not in use. This either means that the TLS index was never used, or that it was // used for a collectible assembly, and that assembly has been freed. In the latter case, we may need to // clean this entry up - if (cLoaderHandles > index.GetByteIndex()) + if (cLoaderHandles > index.GetIndexOffset()) { - pLoaderHandles[index.GetByteIndex()] = NULL; + pLoaderHandles[index.GetIndexOffset()] = NULL; *ppTLSBaseAddress = NULL; } return false; @@ -119,9 +119,9 @@ bool ReportTLSIndexCarefully(TLSIndex index, int32_t cLoaderHandles, PTR_LOADERH // Check to see if the associated loaderallocator is still live if (!pMT->GetLoaderAllocator()->IsExposedObjectLive()) { - if (cLoaderHandles > index.GetByteIndex()) + if (cLoaderHandles > index.GetIndexOffset()) { - uintptr_t indexIntoLoaderHandleTable = index.GetByteIndex(); + uintptr_t indexIntoLoaderHandleTable = index.GetIndexOffset(); pLoaderHandles[indexIntoLoaderHandleTable] = NULL; *ppTLSBaseAddress = NULL; } @@ -342,7 +342,7 @@ void FreeThreadStaticData(ThreadLocalData *pThreadLocalData) CrstHolder ch(&g_TLSCrst); for (const auto& entry : g_pThreadStaticTypeIndices->CollectibleEntries()) { - pThreadLocalData->pLoaderHandles[entry.TlsIndex.GetByteIndex()] = NULL; + pThreadLocalData->pLoaderHandles[entry.TlsIndex.GetIndexOffset()] = NULL; } } @@ -376,16 +376,16 @@ void* GetThreadLocalStaticBase(TLSIndex index) MethodTable *pMT = LookupMethodTableAndFlagForThreadStatic(index, &isGCStatic, &isCollectible); int32_t cTLSData = t_ThreadStatics.cTLSData; - if (cTLSData <= index.GetByteIndex()) + if (cTLSData <= index.GetIndexOffset()) { // Grow the underlying TLS array SpinLockHolder spinLock(&t_ThreadStatics.pThread->m_TlsSpinLock); - int32_t newcTLSData = index.GetByteIndex() + sizeof(TADDR) * 8; // Leave a bit of margin - uint8_t* pNewTLSArrayData = new uint8_t[newcTLSData]; + int32_t newcTLSData = index.GetIndexOffset() + 8; // Leave a bit of margin + uintptr_t* pNewTLSArrayData = new uintptr_t[newcTLSData]; memset(pNewTLSArrayData, 0, newcTLSData); if (cTLSData > 0) memcpy(pNewTLSArrayData, (void*)t_ThreadStatics.pTLSArrayData, cTLSData + 1); - uint8_t* pOldArray = (uint8_t*)t_ThreadStatics.pTLSArrayData; + uintptr_t* pOldArray = (uintptr_t*)t_ThreadStatics.pTLSArrayData; t_ThreadStatics.pTLSArrayData = (TADDR)pNewTLSArrayData; cTLSData = newcTLSData - 1; t_ThreadStatics.cTLSData = cTLSData; @@ -393,11 +393,11 @@ void* GetThreadLocalStaticBase(TLSIndex index) t_ThreadStatics.pThread->m_ThreadLocalDataThreadObjectCopy = t_ThreadStatics; } - if (isCollectible && t_ThreadStatics.cLoaderHandles <= index.GetByteIndex()) + if (isCollectible && t_ThreadStatics.cLoaderHandles <= index.GetIndexOffset()) { // Grow the underlying TLS array SpinLockHolder spinLock(&t_ThreadStatics.pThread->m_TlsSpinLock); - int32_t cNewTLSLoaderHandles = index.GetByteIndex() + sizeof(TADDR) * 8; // Leave a bit of margin + int32_t cNewTLSLoaderHandles = index.GetIndexOffset() + 8; // Leave a bit of margin size_t cbNewTLSLoaderHandles = sizeof(LOADERHANDLE) * cNewTLSLoaderHandles; LOADERHANDLE* pNewTLSLoaderHandles = new LOADERHANDLE[cNewTLSLoaderHandles]; memset(pNewTLSLoaderHandles, 0, cbNewTLSLoaderHandles); @@ -412,7 +412,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) } TADDR pTLSArrayData = t_ThreadStatics.pTLSArrayData; - TADDR *ppTLSBaseAddress = reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetByteIndex()); + TADDR *ppTLSBaseAddress = reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetIndexOffset()); TADDR pTLSBaseAddress = *ppTLSBaseAddress; if (pTLSBaseAddress == NULL) @@ -477,7 +477,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) if (isCollectible) { - LOADERHANDLE *pLoaderHandle = reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetByteIndex()); + LOADERHANDLE *pLoaderHandle = reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetIndexOffset()); // Note, that this can fail, but if it succeeds we don't have a holder in place to clean it up if future operations fail // Add such a holder if we ever add a possibly failing operation after this *pLoaderHandle = pMT->GetLoaderAllocator()->AllocateHandle(gc.tlsEntry); diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index 2b176a613cb05..271b969dfbf12 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -36,7 +36,7 @@ // struct TLSIndex // { // int32_t TLSIndexRawIndex; -// int32_t GetByteIndex() { return TLSIndexRawIndex & 0xFFFFFF; } +// int32_t GetIndexOffset() { return TLSIndexRawIndex & 0xFFFFFF; } // int8_t GetTLSArrayOffset() { return TLSIndexRawIndex >> 24; } // }; // @@ -51,10 +51,10 @@ // 1. Get TLS pointer to OS managed TLS block for the current thread ie. pThreadLocalData = &t_ThreadStatics // 2. Get the TLSArray for the TLS index (pTLSArray = ((uint8_t*)pThreadLocalData) + index.GetTLSArrayOffset()) // 3. Read 1 integer value (cTLSData=pThreadLocalData->cTLSData) -// 4. Compare cTLSData against the index we're looking up (if (cTLSData < index.GetByteIndex())) +// 4. Compare cTLSData against the index we're looking up (if (cTLSData < index.GetIndexOffset())) // 5. If the index is not within range, jump to step 10. // 6. Read 1 pointer value from TLS block (pTLSArrayData=pThreadLocalData->pTLSArrayData) -// 7. Read 1 pointer from within the TLS Array. (pTLSBaseAddress = *(intptr_t*)(((uint8_t*)pTLSArrayData) + index.GetByteIndex()); +// 7. Read 1 pointer from within the TLS Array. (pTLSBaseAddress = *(intptr_t*)(((uint8_t*)pTLSArrayData) + index.GetIndexOffset()); // 8. If pointer is NULL jump to step 10 (if pTLSBaseAddress == NULL) // 9. Return pTLSBaseAddress // 10. Tail-call a helper (return GetThreadLocalStaticBase(index)) @@ -95,7 +95,7 @@ struct TLSIndex TLSIndex() : TLSIndexRawIndex(0xFFFFFFFF) { } TLSIndex(uint32_t rawIndex) : TLSIndexRawIndex(rawIndex) { } uint32_t TLSIndexRawIndex; - int32_t GetByteIndex() const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex & 0xFFFFFF; } + int32_t GetIndexOffset() const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex & 0xFFFFFF; } bool IsAllocated() const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex != 0xFFFFFFFF;} static TLSIndex Unallocated() { LIMITED_METHOD_DAC_CONTRACT; return TLSIndex(0xFFFFFFFF); } bool operator == (TLSIndex index) const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex == index.TLSIndexRawIndex; } From 378ecc10ee4f311d7f8d518332aee1deab6ee130 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 22 Mar 2024 12:03:01 -0700 Subject: [PATCH 38/82] Finish updates that I meant to make in previous commit --- src/coreclr/vm/jithelpers.cpp | 2 +- src/coreclr/vm/threadstatics.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 5748717b9ca6d..78f927b645f67 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1540,7 +1540,7 @@ FORCEINLINE void* GetThreadLocalStaticBaseIfExistsAndInitialized(TLSIndex index) } TADDR pTLSArrayData = t_ThreadStatics.pTLSArrayData; - pTLSBaseAddress = *reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetIndexOffset()); + pTLSBaseAddress = *reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetIndexOffset()); return reinterpret_cast(pTLSBaseAddress); } diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 925ea03a5d20d..0a6f9b35de12e 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -273,7 +273,7 @@ bool TLSIndexToMethodTableMap::FindClearedIndex(uint8_t whenClearedMarkerToAvoid return false; } -uint32_t g_NextTLSSlot = (uint32_t)sizeof(TADDR); +uint32_t g_NextTLSSlot = 1; CrstStatic g_TLSCrst; void InitializeThreadStaticData() @@ -412,7 +412,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) } TADDR pTLSArrayData = t_ThreadStatics.pTLSArrayData; - TADDR *ppTLSBaseAddress = reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetIndexOffset()); + TADDR *ppTLSBaseAddress = reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetIndexOffset()); TADDR pTLSBaseAddress = *ppTLSBaseAddress; if (pTLSBaseAddress == NULL) @@ -477,7 +477,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) if (isCollectible) { - LOADERHANDLE *pLoaderHandle = reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetIndexOffset()); + LOADERHANDLE *pLoaderHandle = reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetIndexOffset()); // Note, that this can fail, but if it succeeds we don't have a holder in place to clean it up if future operations fail // Add such a holder if we ever add a possibly failing operation after this *pLoaderHandle = pMT->GetLoaderAllocator()->AllocateHandle(gc.tlsEntry); @@ -525,7 +525,7 @@ void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pInde { uint32_t tlsRawIndex = g_NextTLSSlot; newTLSIndex = TLSIndex(tlsRawIndex); - g_NextTLSSlot += (uint32_t)sizeof(TADDR); + g_NextTLSSlot += 1; } if (pMT->Collectible()) From f38ae83d09862cfdfe75e19cff5aefee8191e2e5 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 22 Mar 2024 14:40:26 -0700 Subject: [PATCH 39/82] Copy and zero memory correctly --- src/coreclr/vm/threadstatics.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 0a6f9b35de12e..e04b9e8e39923 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -380,14 +380,14 @@ void* GetThreadLocalStaticBase(TLSIndex index) { // Grow the underlying TLS array SpinLockHolder spinLock(&t_ThreadStatics.pThread->m_TlsSpinLock); - int32_t newcTLSData = index.GetIndexOffset() + 8; // Leave a bit of margin + int32_t newcTLSData = index.GetIndexOffset() + 8; // Leave a bit of margin uintptr_t* pNewTLSArrayData = new uintptr_t[newcTLSData]; - memset(pNewTLSArrayData, 0, newcTLSData); + memset(pNewTLSArrayData, 0, newcTLSData * sizeof(uintptr_t)); if (cTLSData > 0) - memcpy(pNewTLSArrayData, (void*)t_ThreadStatics.pTLSArrayData, cTLSData + 1); + memcpy(pNewTLSArrayData, (void*)t_ThreadStatics.pTLSArrayData, cTLSData * sizeof(uintptr_t)); uintptr_t* pOldArray = (uintptr_t*)t_ThreadStatics.pTLSArrayData; t_ThreadStatics.pTLSArrayData = (TADDR)pNewTLSArrayData; - cTLSData = newcTLSData - 1; + cTLSData = newcTLSData; t_ThreadStatics.cTLSData = cTLSData; delete[] pOldArray; t_ThreadStatics.pThread->m_ThreadLocalDataThreadObjectCopy = t_ThreadStatics; From 66cd564d4bb2bdba83830ba69cbb51f5e7a3abbd Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 25 Mar 2024 12:17:52 -0700 Subject: [PATCH 40/82] Fix thread static reporting when indices are per pointer offset value --- src/coreclr/vm/threadstatics.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index e04b9e8e39923..34dfeafed1ec9 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -175,12 +175,12 @@ void ScanThreadStaticRoots(ThreadLocalData *pThreadLocalData, bool forGC, promot } pInFlightData = pInFlightData->pNext; } - PTR_BYTE pTLSArrayData = dac_cast(pThreadLocalData->pTLSArrayData); + PTR_TADDR pTLSArrayData = dac_cast(pThreadLocalData->pTLSArrayData); int32_t cTLSData = pThreadLocalData->cTLSData; - for (int32_t i = 0; i < cTLSData; i += sizeof(TADDR)) + for (int32_t i = 0; i < cTLSData; ++i) { TLSIndex index(i); - TADDR *pTLSBaseAddress = dac_cast(pTLSArrayData + i); + TADDR *pTLSBaseAddress = pTLSArrayData + i; ReportTLSIndexCarefully(index, pThreadLocalData->cLoaderHandles, pThreadLocalData->pLoaderHandles, dac_cast(pTLSBaseAddress), fn, sc); } } From 0e7c1d43332982fc44180186b168a791c8b079cd Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 25 Mar 2024 15:47:17 -0700 Subject: [PATCH 41/82] Update R2R version and update comment on thread statics --- src/coreclr/inc/corinfo.h | 2 - src/coreclr/inc/jithelpers.h | 2 - src/coreclr/inc/readytorun.h | 14 +- src/coreclr/inc/readytorunhelpers.h | 2 - src/coreclr/jit/utils.cpp | 2 - .../Common/Internal/Runtime/ModuleHeaders.cs | 4 +- .../Internal/Runtime/ReadyToRunConstants.cs | 2 - .../Common/JitInterface/CorInfoHelpFunc.cs | 2 - .../JitInterface/CorInfoImpl.ReadyToRun.cs | 6 - .../ReadyToRunSignature.cs | 658 +++++++++--------- .../JitInterface/CorInfoImpl.RyuJit.cs | 6 - src/coreclr/vm/threadstatics.cpp | 2 - src/coreclr/vm/threadstatics.h | 119 ++-- 13 files changed, 385 insertions(+), 436 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index b39a2adab1836..20aa3f1ff8007 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -397,8 +397,6 @@ enum CorInfoHelpFunc CORINFO_HELP_DBL2ULNG_OVF, CORINFO_HELP_FLTREM, CORINFO_HELP_DBLREM, - CORINFO_HELP_FLTROUND, - CORINFO_HELP_DBLROUND, /* Allocating a new object. Always use ICorClassInfo::getNewHelper() to decide which is the right helper to use to allocate an object of a given type. */ diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h index f479c61663b02..eefdc9bdb4137 100644 --- a/src/coreclr/inc/jithelpers.h +++ b/src/coreclr/inc/jithelpers.h @@ -65,8 +65,6 @@ JITHELPER(CORINFO_HELP_DBL2ULNG_OVF, JIT_Dbl2ULngOvf, CORINFO_HELP_SIG_8_STACK) JITHELPER(CORINFO_HELP_FLTREM, JIT_FltRem, CORINFO_HELP_SIG_8_STACK) JITHELPER(CORINFO_HELP_DBLREM, JIT_DblRem, CORINFO_HELP_SIG_16_STACK) - JITHELPER(CORINFO_HELP_FLTROUND, JIT_FloatRound, CORINFO_HELP_SIG_8_STACK) - JITHELPER(CORINFO_HELP_DBLROUND, JIT_DoubleRound, CORINFO_HELP_SIG_16_STACK) // Allocating a new object JITHELPER(CORINFO_HELP_NEWFAST, JIT_New, CORINFO_HELP_SIG_REG_ONLY) diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index 41a4aa251fa74..e12abe6e8a6da 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -19,10 +19,10 @@ // src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h // If you update this, ensure you run `git grep MINIMUM_READYTORUN_MAJOR_VERSION` // and handle pending work. -#define READYTORUN_MAJOR_VERSION 0x0009 -#define READYTORUN_MINOR_VERSION 0x0002 +#define READYTORUN_MAJOR_VERSION 0x000A +#define READYTORUN_MINOR_VERSION 0x0000 -#define MINIMUM_READYTORUN_MAJOR_VERSION 0x009 +#define MINIMUM_READYTORUN_MAJOR_VERSION 0x00A // R2R Version 2.1 adds the InliningInfo section // R2R Version 2.2 adds the ProfileDataInfo section @@ -34,6 +34,7 @@ // R2R Version 9.0 adds support for the Vector512 type // R2R Version 9.1 adds new helpers to allocate objects on frozen segments // R2R Version 9.2 adds MemZero and NativeMemSet helpers +// R2R Version 10.0 changes the algorithm for statics layout struct READYTORUN_CORE_HEADER @@ -404,11 +405,8 @@ enum ReadyToRunHelper READYTORUN_HELPER_DblRem = 0xE0, READYTORUN_HELPER_FltRem = 0xE1, - // These two helpers can be removed once MINIMUM_READYTORUN_MAJOR_VERSION is 10+ - // alongside the CORINFO_HELP_FLTROUND/CORINFO_HELP_DBLROUND - // counterparts and all related code. - READYTORUN_HELPER_DblRound = 0xE2, - READYTORUN_HELPER_FltRound = 0xE3, + // Formerly READYTORUN_HELPER_DblRound = 0xE2, + // Formerly READYTORUN_HELPER_FltRound = 0xE3, #ifdef FEATURE_EH_FUNCLETS // Personality routines diff --git a/src/coreclr/inc/readytorunhelpers.h b/src/coreclr/inc/readytorunhelpers.h index 3bf63aca41209..e2eb23ec61cc0 100644 --- a/src/coreclr/inc/readytorunhelpers.h +++ b/src/coreclr/inc/readytorunhelpers.h @@ -89,8 +89,6 @@ HELPER(READYTORUN_HELPER_Dbl2ULngOvf, CORINFO_HELP_DBL2ULNG_OVF, HELPER(READYTORUN_HELPER_FltRem, CORINFO_HELP_FLTREM, ) HELPER(READYTORUN_HELPER_DblRem, CORINFO_HELP_DBLREM, ) -HELPER(READYTORUN_HELPER_FltRound, CORINFO_HELP_FLTROUND, ) -HELPER(READYTORUN_HELPER_DblRound, CORINFO_HELP_DBLROUND, ) #ifndef TARGET_X86 HELPER(READYTORUN_HELPER_PersonalityRoutine, CORINFO_HELP_EE_PERSONALITY_ROUTINE, OPTIMIZEFORSIZE) diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index 2b4ce1a08c6fa..1c8139a46785f 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -1542,8 +1542,6 @@ void HelperCallProperties::init() case CORINFO_HELP_DBL2ULNG: case CORINFO_HELP_FLTREM: case CORINFO_HELP_DBLREM: - case CORINFO_HELP_FLTROUND: - case CORINFO_HELP_DBLROUND: isPure = true; noThrow = true; diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index 6fc5d9542e160..8aa1080913076 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -15,8 +15,8 @@ internal struct ReadyToRunHeaderConstants { public const uint Signature = 0x00525452; // 'RTR' - public const ushort CurrentMajorVersion = 9; - public const ushort CurrentMinorVersion = 2; + public const ushort CurrentMajorVersion = 10; + public const ushort CurrentMinorVersion = 0; } #if READYTORUN #pragma warning disable 0169 diff --git a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs index a37945534865b..15b8d57af6af8 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs @@ -315,8 +315,6 @@ public enum ReadyToRunHelper // Floating point ops DblRem = 0xE0, FltRem = 0xE1, - DblRound = 0xE2, - FltRound = 0xE3, // Personality routines PersonalityRoutine = 0xF0, diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs index 4b474fce1c181..2915cd7b31fe3 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs @@ -40,8 +40,6 @@ public enum CorInfoHelpFunc CORINFO_HELP_DBL2ULNG_OVF, CORINFO_HELP_FLTREM, CORINFO_HELP_DBLREM, - CORINFO_HELP_FLTROUND, - CORINFO_HELP_DBLROUND, /* Allocating a new object. Always use ICorClassInfo::getNewHelper() to decide which is the right helper to use to allocate an object of a given type. */ diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 79f785e319446..674eeb9e7957f 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -1167,12 +1167,6 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum) case CorInfoHelpFunc.CORINFO_HELP_DBLREM: id = ReadyToRunHelper.DblRem; break; - case CorInfoHelpFunc.CORINFO_HELP_FLTROUND: - id = ReadyToRunHelper.FltRound; - break; - case CorInfoHelpFunc.CORINFO_HELP_DBLROUND: - id = ReadyToRunHelper.DblRound; - break; case CorInfoHelpFunc.CORINFO_HELP_CHKCASTANY: id = ReadyToRunHelper.CheckCastAny; diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs index 0eae2f10cb8f0..8c66cd8d59d4d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs @@ -1586,416 +1586,428 @@ private void ParseHelper(StringBuilder builder) { uint helperType = ReadUIntAndEmitInlineSignatureBinary(builder); - switch ((ReadyToRunHelper)helperType) + bool retiredHelperFound = false; + if (r2rReader.MajorVersion <= 9) { - case ReadyToRunHelper.Invalid: - builder.Append("INVALID"); - break; + if (helperType == 0xE2) + { + retiredHelperFound = true; + builder.Append("DBL_ROUND"); + } + else if (helperType == 0xE3) + { + retiredHelperFound = true; + builder.Append("FLT_ROUND"); + } + } - // Not a real helper - handle to current module passed to delay load helpers. - case ReadyToRunHelper.Module: - builder.Append("MODULE"); - break; + if (!retiredHelperFound) + { + switch ((ReadyToRunHelper)helperType) + { + case ReadyToRunHelper.Invalid: + builder.Append("INVALID"); + break; - case ReadyToRunHelper.GSCookie: - builder.Append("GC_COOKIE"); - break; + // Not a real helper - handle to current module passed to delay load helpers. + case ReadyToRunHelper.Module: + builder.Append("MODULE"); + break; - case ReadyToRunHelper.IndirectTrapThreads: - builder.Append("INDIRECT_TRAP_THREADS"); - break; + case ReadyToRunHelper.GSCookie: + builder.Append("GC_COOKIE"); + break; - // - // Delay load helpers - // + case ReadyToRunHelper.IndirectTrapThreads: + builder.Append("INDIRECT_TRAP_THREADS"); + break; - // All delay load helpers use custom calling convention: - // - scratch register - address of indirection cell. 0 = address is inferred from callsite. - // - stack - section index, module handle - case ReadyToRunHelper.DelayLoad_MethodCall: - builder.Append("DELAYLOAD_METHODCALL"); - break; + // + // Delay load helpers + // - case ReadyToRunHelper.DelayLoad_Helper: - builder.Append("DELAYLOAD_HELPER"); - break; + // All delay load helpers use custom calling convention: + // - scratch register - address of indirection cell. 0 = address is inferred from callsite. + // - stack - section index, module handle + case ReadyToRunHelper.DelayLoad_MethodCall: + builder.Append("DELAYLOAD_METHODCALL"); + break; - case ReadyToRunHelper.DelayLoad_Helper_Obj: - builder.Append("DELAYLOAD_HELPER_OBJ"); - break; + case ReadyToRunHelper.DelayLoad_Helper: + builder.Append("DELAYLOAD_HELPER"); + break; - case ReadyToRunHelper.DelayLoad_Helper_ObjObj: - builder.Append("DELAYLOAD_HELPER_OBJ_OBJ"); - break; + case ReadyToRunHelper.DelayLoad_Helper_Obj: + builder.Append("DELAYLOAD_HELPER_OBJ"); + break; - // JIT helpers + case ReadyToRunHelper.DelayLoad_Helper_ObjObj: + builder.Append("DELAYLOAD_HELPER_OBJ_OBJ"); + break; - // Exception handling helpers - case ReadyToRunHelper.Throw: - builder.Append("THROW"); - break; + // JIT helpers - case ReadyToRunHelper.Rethrow: - builder.Append("RETHROW"); - break; + // Exception handling helpers + case ReadyToRunHelper.Throw: + builder.Append("THROW"); + break; - case ReadyToRunHelper.Overflow: - builder.Append("OVERFLOW"); - break; + case ReadyToRunHelper.Rethrow: + builder.Append("RETHROW"); + break; - case ReadyToRunHelper.RngChkFail: - builder.Append("RNG_CHK_FAIL"); - break; + case ReadyToRunHelper.Overflow: + builder.Append("OVERFLOW"); + break; - case ReadyToRunHelper.FailFast: - builder.Append("FAIL_FAST"); - break; + case ReadyToRunHelper.RngChkFail: + builder.Append("RNG_CHK_FAIL"); + break; - case ReadyToRunHelper.ThrowNullRef: - builder.Append("THROW_NULL_REF"); - break; + case ReadyToRunHelper.FailFast: + builder.Append("FAIL_FAST"); + break; - case ReadyToRunHelper.ThrowDivZero: - builder.Append("THROW_DIV_ZERO"); - break; + case ReadyToRunHelper.ThrowNullRef: + builder.Append("THROW_NULL_REF"); + break; - // Write barriers - case ReadyToRunHelper.WriteBarrier: - builder.Append("WRITE_BARRIER"); - break; + case ReadyToRunHelper.ThrowDivZero: + builder.Append("THROW_DIV_ZERO"); + break; - case ReadyToRunHelper.CheckedWriteBarrier: - builder.Append("CHECKED_WRITE_BARRIER"); - break; + // Write barriers + case ReadyToRunHelper.WriteBarrier: + builder.Append("WRITE_BARRIER"); + break; - case ReadyToRunHelper.ByRefWriteBarrier: - builder.Append("BYREF_WRITE_BARRIER"); - break; + case ReadyToRunHelper.CheckedWriteBarrier: + builder.Append("CHECKED_WRITE_BARRIER"); + break; - // Array helpers - case ReadyToRunHelper.Stelem_Ref: - builder.Append("STELEM_REF"); - break; + case ReadyToRunHelper.ByRefWriteBarrier: + builder.Append("BYREF_WRITE_BARRIER"); + break; - case ReadyToRunHelper.Ldelema_Ref: - builder.Append("LDELEMA_REF"); - break; + // Array helpers + case ReadyToRunHelper.Stelem_Ref: + builder.Append("STELEM_REF"); + break; - case ReadyToRunHelper.MemSet: - builder.Append("MEM_SET"); - break; + case ReadyToRunHelper.Ldelema_Ref: + builder.Append("LDELEMA_REF"); + break; - case ReadyToRunHelper.MemZero: - builder.Append("MEM_ZERO"); - break; + case ReadyToRunHelper.MemSet: + builder.Append("MEM_SET"); + break; - case ReadyToRunHelper.MemCpy: - builder.Append("MEM_CPY"); - break; + case ReadyToRunHelper.MemZero: + builder.Append("MEM_ZERO"); + break; - case ReadyToRunHelper.NativeMemSet: - builder.Append("NATIVE_MEM_SET"); - break; + case ReadyToRunHelper.MemCpy: + builder.Append("MEM_CPY"); + break; - // PInvoke helpers - case ReadyToRunHelper.PInvokeBegin: - builder.Append("PINVOKE_BEGIN"); - break; + case ReadyToRunHelper.NativeMemSet: + builder.Append("NATIVE_MEM_SET"); + break; - case ReadyToRunHelper.PInvokeEnd: - builder.Append("PINVOKE_END"); - break; + // PInvoke helpers + case ReadyToRunHelper.PInvokeBegin: + builder.Append("PINVOKE_BEGIN"); + break; - case ReadyToRunHelper.GCPoll: - builder.Append("GCPOLL"); - break; + case ReadyToRunHelper.PInvokeEnd: + builder.Append("PINVOKE_END"); + break; - case ReadyToRunHelper.GetCurrentManagedThreadId: - builder.Append("GET_CURRENT_MANAGED_THREAD_ID"); - break; + case ReadyToRunHelper.GCPoll: + builder.Append("GCPOLL"); + break; - case ReadyToRunHelper.ReversePInvokeEnter: - builder.Append("REVERSE_PINVOKE_ENTER"); - break; + case ReadyToRunHelper.GetCurrentManagedThreadId: + builder.Append("GET_CURRENT_MANAGED_THREAD_ID"); + break; - case ReadyToRunHelper.ReversePInvokeExit: - builder.Append("REVERSE_PINVOKE_EXIT"); - break; + case ReadyToRunHelper.ReversePInvokeEnter: + builder.Append("REVERSE_PINVOKE_ENTER"); + break; - // Get string handle lazily - case ReadyToRunHelper.GetString: - builder.Append("GET_STRING"); - break; + case ReadyToRunHelper.ReversePInvokeExit: + builder.Append("REVERSE_PINVOKE_EXIT"); + break; - // Used by /Tuning for Profile optimizations - case ReadyToRunHelper.LogMethodEnter: - builder.Append("LOG_METHOD_ENTER"); - break; + // Get string handle lazily + case ReadyToRunHelper.GetString: + builder.Append("GET_STRING"); + break; - // Reflection helpers - case ReadyToRunHelper.GetRuntimeTypeHandle: - builder.Append("GET_RUNTIME_TYPE_HANDLE"); - break; + // Used by /Tuning for Profile optimizations + case ReadyToRunHelper.LogMethodEnter: + builder.Append("LOG_METHOD_ENTER"); + break; - case ReadyToRunHelper.GetRuntimeMethodHandle: - builder.Append("GET_RUNTIME_METHOD_HANDLE"); - break; + // Reflection helpers + case ReadyToRunHelper.GetRuntimeTypeHandle: + builder.Append("GET_RUNTIME_TYPE_HANDLE"); + break; - case ReadyToRunHelper.GetRuntimeFieldHandle: - builder.Append("GET_RUNTIME_FIELD_HANDLE"); - break; + case ReadyToRunHelper.GetRuntimeMethodHandle: + builder.Append("GET_RUNTIME_METHOD_HANDLE"); + break; - case ReadyToRunHelper.Box: - builder.Append("BOX"); - break; + case ReadyToRunHelper.GetRuntimeFieldHandle: + builder.Append("GET_RUNTIME_FIELD_HANDLE"); + break; - case ReadyToRunHelper.Box_Nullable: - builder.Append("BOX_NULLABLE"); - break; + case ReadyToRunHelper.Box: + builder.Append("BOX"); + break; - case ReadyToRunHelper.Unbox: - builder.Append("UNBOX"); - break; + case ReadyToRunHelper.Box_Nullable: + builder.Append("BOX_NULLABLE"); + break; - case ReadyToRunHelper.Unbox_Nullable: - builder.Append("UNBOX_NULLABLE"); - break; + case ReadyToRunHelper.Unbox: + builder.Append("UNBOX"); + break; - case ReadyToRunHelper.NewMultiDimArr: - builder.Append("NEW_MULTI_DIM_ARR"); - break; + case ReadyToRunHelper.Unbox_Nullable: + builder.Append("UNBOX_NULLABLE"); + break; - case ReadyToRunHelper.MonitorEnter: - builder.Append("MONITOR_ENTER"); - break; + case ReadyToRunHelper.NewMultiDimArr: + builder.Append("NEW_MULTI_DIM_ARR"); + break; - case ReadyToRunHelper.MonitorExit: - builder.Append("MONITOR_EXIT"); - break; + case ReadyToRunHelper.MonitorEnter: + builder.Append("MONITOR_ENTER"); + break; - // Helpers used with generic handle lookup cases - case ReadyToRunHelper.NewObject: - builder.Append("NEW_OBJECT"); - break; + case ReadyToRunHelper.MonitorExit: + builder.Append("MONITOR_EXIT"); + break; - case ReadyToRunHelper.NewArray: - builder.Append("NEW_ARRAY"); - break; + // Helpers used with generic handle lookup cases + case ReadyToRunHelper.NewObject: + builder.Append("NEW_OBJECT"); + break; - case ReadyToRunHelper.NewMaybeFrozenArray: - builder.Append("NEW_MAYBEFROZEN_ARRAY"); - break; + case ReadyToRunHelper.NewArray: + builder.Append("NEW_ARRAY"); + break; - case ReadyToRunHelper.NewMaybeFrozenObject: - builder.Append("NEW_MAYBEFROZEN_OBJECT"); - break; + case ReadyToRunHelper.NewMaybeFrozenArray: + builder.Append("NEW_MAYBEFROZEN_ARRAY"); + break; - case ReadyToRunHelper.CheckCastAny: - builder.Append("CHECK_CAST_ANY"); - break; + case ReadyToRunHelper.NewMaybeFrozenObject: + builder.Append("NEW_MAYBEFROZEN_OBJECT"); + break; - case ReadyToRunHelper.CheckInstanceAny: - builder.Append("CHECK_INSTANCE_ANY"); - break; + case ReadyToRunHelper.CheckCastAny: + builder.Append("CHECK_CAST_ANY"); + break; - case ReadyToRunHelper.IsInstanceOfException: - builder.Append("SIMPLE_ISINSTANCE_OF"); - break; + case ReadyToRunHelper.CheckInstanceAny: + builder.Append("CHECK_INSTANCE_ANY"); + break; - case ReadyToRunHelper.GenericGcStaticBase: - builder.Append("GENERIC_GC_STATIC_BASE"); - break; + case ReadyToRunHelper.IsInstanceOfException: + builder.Append("SIMPLE_ISINSTANCE_OF"); + break; - case ReadyToRunHelper.GenericNonGcStaticBase: - builder.Append("GENERIC_NON_GC_STATIC_BASE"); - break; + case ReadyToRunHelper.GenericGcStaticBase: + builder.Append("GENERIC_GC_STATIC_BASE"); + break; - case ReadyToRunHelper.GenericGcTlsBase: - builder.Append("GENERIC_GC_TLS_BASE"); - break; + case ReadyToRunHelper.GenericNonGcStaticBase: + builder.Append("GENERIC_NON_GC_STATIC_BASE"); + break; - case ReadyToRunHelper.GenericNonGcTlsBase: - builder.Append("GENERIC_NON_GC_TLS_BASE"); - break; + case ReadyToRunHelper.GenericGcTlsBase: + builder.Append("GENERIC_GC_TLS_BASE"); + break; - case ReadyToRunHelper.VirtualFuncPtr: - builder.Append("VIRTUAL_FUNC_PTR"); - break; + case ReadyToRunHelper.GenericNonGcTlsBase: + builder.Append("GENERIC_NON_GC_TLS_BASE"); + break; - // Long mul/div/shift ops - case ReadyToRunHelper.LMul: - builder.Append("LMUL"); - break; + case ReadyToRunHelper.VirtualFuncPtr: + builder.Append("VIRTUAL_FUNC_PTR"); + break; - case ReadyToRunHelper.LMulOfv: - builder.Append("LMUL_OFV"); - break; + // Long mul/div/shift ops + case ReadyToRunHelper.LMul: + builder.Append("LMUL"); + break; - case ReadyToRunHelper.ULMulOvf: - builder.Append("ULMUL_OVF"); - break; + case ReadyToRunHelper.LMulOfv: + builder.Append("LMUL_OFV"); + break; - case ReadyToRunHelper.LDiv: - builder.Append("LDIV"); - break; + case ReadyToRunHelper.ULMulOvf: + builder.Append("ULMUL_OVF"); + break; - case ReadyToRunHelper.LMod: - builder.Append("LMOD"); - break; + case ReadyToRunHelper.LDiv: + builder.Append("LDIV"); + break; - case ReadyToRunHelper.ULDiv: - builder.Append("ULDIV"); - break; + case ReadyToRunHelper.LMod: + builder.Append("LMOD"); + break; - case ReadyToRunHelper.ULMod: - builder.Append("ULMOD"); - break; + case ReadyToRunHelper.ULDiv: + builder.Append("ULDIV"); + break; - case ReadyToRunHelper.LLsh: - builder.Append("LLSH"); - break; + case ReadyToRunHelper.ULMod: + builder.Append("ULMOD"); + break; - case ReadyToRunHelper.LRsh: - builder.Append("LRSH"); - break; + case ReadyToRunHelper.LLsh: + builder.Append("LLSH"); + break; - case ReadyToRunHelper.LRsz: - builder.Append("LRSZ"); - break; + case ReadyToRunHelper.LRsh: + builder.Append("LRSH"); + break; - case ReadyToRunHelper.Lng2Dbl: - builder.Append("LNG2DBL"); - break; + case ReadyToRunHelper.LRsz: + builder.Append("LRSZ"); + break; - case ReadyToRunHelper.ULng2Dbl: - builder.Append("ULNG2DBL"); - break; + case ReadyToRunHelper.Lng2Dbl: + builder.Append("LNG2DBL"); + break; - // 32-bit division helpers - case ReadyToRunHelper.Div: - builder.Append("DIV"); - break; + case ReadyToRunHelper.ULng2Dbl: + builder.Append("ULNG2DBL"); + break; - case ReadyToRunHelper.Mod: - builder.Append("MOD"); - break; + // 32-bit division helpers + case ReadyToRunHelper.Div: + builder.Append("DIV"); + break; - case ReadyToRunHelper.UDiv: - builder.Append("UDIV"); - break; + case ReadyToRunHelper.Mod: + builder.Append("MOD"); + break; - case ReadyToRunHelper.UMod: - builder.Append("UMOD"); - break; + case ReadyToRunHelper.UDiv: + builder.Append("UDIV"); + break; - // Floating point conversions - case ReadyToRunHelper.Dbl2Int: - builder.Append("DBL2INT"); - break; + case ReadyToRunHelper.UMod: + builder.Append("UMOD"); + break; - case ReadyToRunHelper.Dbl2IntOvf: - builder.Append("DBL2INTOVF"); - break; + // Floating point conversions + case ReadyToRunHelper.Dbl2Int: + builder.Append("DBL2INT"); + break; - case ReadyToRunHelper.Dbl2Lng: - builder.Append("DBL2LNG"); - break; + case ReadyToRunHelper.Dbl2IntOvf: + builder.Append("DBL2INTOVF"); + break; - case ReadyToRunHelper.Dbl2LngOvf: - builder.Append("DBL2LNGOVF"); - break; + case ReadyToRunHelper.Dbl2Lng: + builder.Append("DBL2LNG"); + break; - case ReadyToRunHelper.Dbl2UInt: - builder.Append("DBL2UINT"); - break; + case ReadyToRunHelper.Dbl2LngOvf: + builder.Append("DBL2LNGOVF"); + break; - case ReadyToRunHelper.Dbl2UIntOvf: - builder.Append("DBL2UINTOVF"); - break; + case ReadyToRunHelper.Dbl2UInt: + builder.Append("DBL2UINT"); + break; - case ReadyToRunHelper.Dbl2ULng: - builder.Append("DBL2ULNG"); - break; + case ReadyToRunHelper.Dbl2UIntOvf: + builder.Append("DBL2UINTOVF"); + break; - case ReadyToRunHelper.Dbl2ULngOvf: - builder.Append("DBL2ULNGOVF"); - break; + case ReadyToRunHelper.Dbl2ULng: + builder.Append("DBL2ULNG"); + break; - // Floating point ops - case ReadyToRunHelper.DblRem: - builder.Append("DBL_REM"); - break; - case ReadyToRunHelper.FltRem: - builder.Append("FLT_REM"); - break; - case ReadyToRunHelper.DblRound: - builder.Append("DBL_ROUND"); - break; - case ReadyToRunHelper.FltRound: - builder.Append("FLT_ROUND"); - break; + case ReadyToRunHelper.Dbl2ULngOvf: + builder.Append("DBL2ULNGOVF"); + break; - // Personality routines - case ReadyToRunHelper.PersonalityRoutine: - builder.Append("PERSONALITY_ROUTINE"); - break; - case ReadyToRunHelper.PersonalityRoutineFilterFunclet: - builder.Append("PERSONALITY_ROUTINE_FILTER_FUNCLET"); - break; + // Floating point ops + case ReadyToRunHelper.DblRem: + builder.Append("DBL_REM"); + break; + case ReadyToRunHelper.FltRem: + builder.Append("FLT_REM"); + break; - // - // Deprecated/legacy - // + // Personality routines + case ReadyToRunHelper.PersonalityRoutine: + builder.Append("PERSONALITY_ROUTINE"); + break; + case ReadyToRunHelper.PersonalityRoutineFilterFunclet: + builder.Append("PERSONALITY_ROUTINE_FILTER_FUNCLET"); + break; - // JIT32 x86-specific write barriers - case ReadyToRunHelper.WriteBarrier_EAX: - builder.Append("WRITE_BARRIER_EAX"); - break; - case ReadyToRunHelper.WriteBarrier_EBX: - builder.Append("WRITE_BARRIER_EBX"); - break; - case ReadyToRunHelper.WriteBarrier_ECX: - builder.Append("WRITE_BARRIER_ECX"); - break; - case ReadyToRunHelper.WriteBarrier_ESI: - builder.Append("WRITE_BARRIER_ESI"); - break; - case ReadyToRunHelper.WriteBarrier_EDI: - builder.Append("WRITE_BARRIER_EDI"); - break; - case ReadyToRunHelper.WriteBarrier_EBP: - builder.Append("WRITE_BARRIER_EBP"); - break; - case ReadyToRunHelper.CheckedWriteBarrier_EAX: - builder.Append("CHECKED_WRITE_BARRIER_EAX"); - break; - case ReadyToRunHelper.CheckedWriteBarrier_EBX: - builder.Append("CHECKED_WRITE_BARRIER_EBX"); - break; - case ReadyToRunHelper.CheckedWriteBarrier_ECX: - builder.Append("CHECKED_WRITE_BARRIER_ECX"); - break; - case ReadyToRunHelper.CheckedWriteBarrier_ESI: - builder.Append("CHECKED_WRITE_BARRIER_ESI"); - break; - case ReadyToRunHelper.CheckedWriteBarrier_EDI: - builder.Append("CHECKED_WRITE_BARRIER_EDI"); - break; - case ReadyToRunHelper.CheckedWriteBarrier_EBP: - builder.Append("CHECKED_WRITE_BARRIER_EBP"); - break; + // + // Deprecated/legacy + // - // JIT32 x86-specific exception handling - case ReadyToRunHelper.EndCatch: - builder.Append("END_CATCH"); - break; + // JIT32 x86-specific write barriers + case ReadyToRunHelper.WriteBarrier_EAX: + builder.Append("WRITE_BARRIER_EAX"); + break; + case ReadyToRunHelper.WriteBarrier_EBX: + builder.Append("WRITE_BARRIER_EBX"); + break; + case ReadyToRunHelper.WriteBarrier_ECX: + builder.Append("WRITE_BARRIER_ECX"); + break; + case ReadyToRunHelper.WriteBarrier_ESI: + builder.Append("WRITE_BARRIER_ESI"); + break; + case ReadyToRunHelper.WriteBarrier_EDI: + builder.Append("WRITE_BARRIER_EDI"); + break; + case ReadyToRunHelper.WriteBarrier_EBP: + builder.Append("WRITE_BARRIER_EBP"); + break; + case ReadyToRunHelper.CheckedWriteBarrier_EAX: + builder.Append("CHECKED_WRITE_BARRIER_EAX"); + break; + case ReadyToRunHelper.CheckedWriteBarrier_EBX: + builder.Append("CHECKED_WRITE_BARRIER_EBX"); + break; + case ReadyToRunHelper.CheckedWriteBarrier_ECX: + builder.Append("CHECKED_WRITE_BARRIER_ECX"); + break; + case ReadyToRunHelper.CheckedWriteBarrier_ESI: + builder.Append("CHECKED_WRITE_BARRIER_ESI"); + break; + case ReadyToRunHelper.CheckedWriteBarrier_EDI: + builder.Append("CHECKED_WRITE_BARRIER_EDI"); + break; + case ReadyToRunHelper.CheckedWriteBarrier_EBP: + builder.Append("CHECKED_WRITE_BARRIER_EBP"); + break; - case ReadyToRunHelper.StackProbe: - builder.Append("STACK_PROBE"); - break; + // JIT32 x86-specific exception handling + case ReadyToRunHelper.EndCatch: + builder.Append("END_CATCH"); + break; - default: - throw new BadImageFormatException(); + case ReadyToRunHelper.StackProbe: + builder.Append("STACK_PROBE"); + break; + + default: + throw new BadImageFormatException(); + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index f98d7b0fc0650..91b594d5fe564 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -693,12 +693,6 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum) case CorInfoHelpFunc.CORINFO_HELP_DBLREM: id = ReadyToRunHelper.DblRem; break; - case CorInfoHelpFunc.CORINFO_HELP_FLTROUND: - id = ReadyToRunHelper.FltRound; - break; - case CorInfoHelpFunc.CORINFO_HELP_DBLROUND: - id = ReadyToRunHelper.DblRound; - break; case CorInfoHelpFunc.CORINFO_HELP_JIT_PINVOKE_BEGIN: id = ReadyToRunHelper.PInvokeBegin; diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 34dfeafed1ec9..43dbd2ce8264b 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -67,7 +67,6 @@ PTR_MethodTable LookupMethodTableForThreadStaticKnownToBeAllocated(TLSIndex inde } CONTRACTL_END; - // TODO, if and when we get array indices, we should be pickier. return g_pThreadStaticTypeIndices->LookupTlsIndexKnownToBeAllocated(index); } @@ -82,7 +81,6 @@ PTR_MethodTable LookupMethodTableAndFlagForThreadStatic(TLSIndex index, bool *pI MODE_COOPERATIVE; } CONTRACTL_END; - // TODO, if and when we get array indices, we should be pickier. PTR_MethodTable retVal = g_pThreadStaticTypeIndices->Lookup(index, pIsGCStatic, pIsCollectible); return retVal; } diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index 271b969dfbf12..0f3f6334e940e 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -4,98 +4,63 @@ // Thread local storage is designed to be as efficient as possible. // This leads to several different access patterns. // -// There shall be a global TLS data structure used for all threads. This is initialized before any managed code is permitted to run on a thread -// struct TLSArray -// { -// int32_t cTLSData; // Size in bytes of offset into the TLS array which is valid -// void* pTLSArrayData; // Points at the Thread local array data. -// }; -// -// Used to store access to TLS data for a single index when the TLS is accessed while the class constructor is running -// struct InFlightTLSData -// { -// InFlightTLSData* pNext; // Points at the next in-flight TLS data -// TLSIndex tlsIndex; // The TLS index for the static -// void* pTLSData; // The TLS data for the static -// }; -// -// struct ThreadLocalLoaderAllocator -// { -// ThreadLocalLoaderAllocator* pNext; // Points at the next thread local loader allocator -// LoaderAllocator* pLoaderAllocator; // The loader allocator that has a TLS used in this thread -// bool ReportToGC(PromoteFunction* fn, ScanContext* sc, int flags); // Reports the thread local loader allocator state to the GC, returns true if the ThreadLocalLoaderAllocator structure should be removed from the linked list. This is what allows the GC statics for collectible types to actually exist on the nonGC thread local storage array -// }; -// -// struct ThreadLocalData -// { -// TLSArray nongcArray; // Array for nonGC data, as well as collectible GC static. cTLSData is initialized to PRE_ALLOCATED_TLS_NONGC_SLOT_COUNT * sizeof(void*) - 1, and pTLSArrayData points at memory of size PRE_ALLOCATED_TLS_NONGC_SLOT_COUNT * sizeof(void*) at thread startup -// TLSArray gcArray; // Array for non-collectible GC pointers. cTLSData is initialized to PRE_ALLOCATED_TLS_GC_SLOT_COUNT * sizeof(OBJECTREF) + sizeof(void*) * 2 - 1, and pTLSArrayData points at a managed object[], initialized to an object array of size PRE_ALLOCATED_TLS_GC_SLOT_COUNT at thread startup -// InFlightTLSData* pNext; // Points at the next in-flight TLS data -// }; -// -// struct TLSIndex -// { -// int32_t TLSIndexRawIndex; -// int32_t GetIndexOffset() { return TLSIndexRawIndex & 0xFFFFFF; } -// int8_t GetTLSArrayOffset() { return TLSIndexRawIndex >> 24; } -// }; -// -// thread_local ThreadLocalData t_ThreadStatics; -// SArray* g_pNonGCTLSIndexToMethodTable; -// int g_maxNonGCTlsSize; -// SArray* g_pGCTLSIndexToMethodTable; -// int g_maxGCTlsSlots; -// // Access pattern for a TLS static // 0. Get the TLS index somehow -// 1. Get TLS pointer to OS managed TLS block for the current thread ie. pThreadLocalData = &t_ThreadStatics -// 2. Get the TLSArray for the TLS index (pTLSArray = ((uint8_t*)pThreadLocalData) + index.GetTLSArrayOffset()) -// 3. Read 1 integer value (cTLSData=pThreadLocalData->cTLSData) -// 4. Compare cTLSData against the index we're looking up (if (cTLSData < index.GetIndexOffset())) -// 5. If the index is not within range, jump to step 10. -// 6. Read 1 pointer value from TLS block (pTLSArrayData=pThreadLocalData->pTLSArrayData) -// 7. Read 1 pointer from within the TLS Array. (pTLSBaseAddress = *(intptr_t*)(((uint8_t*)pTLSArrayData) + index.GetIndexOffset()); -// 8. If pointer is NULL jump to step 10 (if pTLSBaseAddress == NULL) -// 9. Return pTLSBaseAddress -// 10. Tail-call a helper (return GetThreadLocalStaticBase(index)) -// -// The Runtime shall define a couple of well known TLS indices. These are used for the most common -// TLS statics, and are used to avoid the overhead of checking for the index being in range, and -// the class constructor for having run, so that we can skip steps 3, 4, 5, and 8. It shall do this -// by allocating the associated memory before permitting any code to run on the thread. +// 1. Get TLS pointer to OS managed TLS block for the current thread ie. pThreadStatics = &t_ThreadStatics +// 2. Determine the TLSIndexType of the TLS index. Currently the only TLS access type in use is TLSIndexType::Standard, but over the .NET 9 period we expect to add a couple more +// If TLSIndexType == TLSIndexType::Standard +// - Compare pThreadStatics->cTLSData against the index offset of the TLSIndex. +// - If in range, multiply the index offset by the size of a a pointer and add it to pThreadStatics->pTLSArrayData, then dereference +// - If not found +// - Slow path look in the pThreadStatics->pInFlightData, if found there, return +// - If not found there, trigger allocation behavior to grow TLS data, etc. // -// Psuedocode for -// ref byte GetThreadLocalStaticBase(uint index) -// { -// Do the access pattern above, but if the TLS array is too small, allocate a new one, and if the base pointer is NULL, call the class constructor for the static. -// if After all that the base pointer is still NULL, walk the InFlightTLSData chain to see if it exists in there. -// If the InFlightTLSData chain has a value -// check to see if the class constructor has run. If it has completed, update the base pointer in the TLS array, and delete the InFlightTLSData entry. -// return the found value -// ELSE -// allocate a new InFlightTLSData entry, and return the address of the pTLSData field. -// } +// Normal thread-local statics lifetime management +// ------------------------------------- +// - Each entry in the TLS table which is not collectible shall be reported to the GC during promotion and +// relocation. There are no GCHandle or other structures which keep TLS data alive // -// Rationale for basic decisions here -// 1. We want access to TLS statics to be as fast as possible, especially for extremely common -// thread statics like the ones used for async, and memory allocation. -// 2. We want access to TLS statics for shared generic types to be nearly fully inlineable. This -// is why the variation between collectible and non-collectible gc statics access is handled by -// a single byte in the index itself. The intent is that access to statics shall be as simple as -// reading the index from a MethodTable, and then using a very straightforward pattern from there. - - +// Collectible thread-local statics lifetime management +// ------------------------------------- +// Lifetime management is substantially complicated due the issue that it is possible for either a thread or a +// collectible type to be collected first. Thus the collection algorithm is as follows. +// - The system shall maintain a global mapping of TLS indices to MethodTable structures +// - When a native LoaderAllocator is being cleaned up, before the WeakTrackResurrection GCHandle that +// points at the the managed LoaderAllocator object is destroyed, the mapping from TLS indices to +// collectible LoaderAllocator structures shall be cleared of all relevant entries (and the current +// GC index shall be stored in the TLS to MethodTable mapping) +// - When a GC promotion or relocation scan occurs, for every TLS index which was freed to point at a GC +// index the relevant entry in the TLS table shall be set to NULL in preparation for that entry in the +// table being reused in the future. In addition, if the TLS index refers to a MethodTable which is in +// a collectible assembly, and the associated LoaderAllocator has been freed, then set the relevant +// entry to NULL. +// - When allocating new entries from the TLS mapping table for new collectible thread local structures, +// do not re-use an entry in the table until at least 2 GCs have occurred. This is to allow every +// thread to have NULL'd out the relevant entry in its thread local table. +// - When allocating new TLS entries for collectible TLS statics on a per-thread basis allocate a +// LOADERHANDLE for each object allocated, and associate it with the TLS index on that thread. +// - When cleaning up a thread, for each collectible thread static which is still allocated, we will have +// a LOADERHANDLE. If the collectible type still has a live managed LoaderAllocator free the +// LOADERHANDLE. +// - In each relocation scan, report all live collectible entries to the GC. +// #ifndef __THREADLOCALSTORAGE_H__ #define __THREADLOCALSTORAGE_H__ class Thread; +enum class TLSIndexType +{ + Standard, // IndexOffset for this form of TLSIndex is scaled by sizeof(void*) and then added to ThreadLocalData::pTLSArrayData to get the final address +} + struct TLSIndex { TLSIndex() : TLSIndexRawIndex(0xFFFFFFFF) { } TLSIndex(uint32_t rawIndex) : TLSIndexRawIndex(rawIndex) { } uint32_t TLSIndexRawIndex; int32_t GetIndexOffset() const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex & 0xFFFFFF; } + TLSIndexType GetIndexType() const { LIMITED_METHOD_DAC_CONTRACT; return (TLSIndexType)(TLSIndexRawIndex >> 24); } bool IsAllocated() const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex != 0xFFFFFFFF;} static TLSIndex Unallocated() { LIMITED_METHOD_DAC_CONTRACT; return TLSIndex(0xFFFFFFFF); } bool operator == (TLSIndex index) const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex == index.TLSIndexRawIndex; } From 446a60e13312dcef04ed400f3b1b7a21cf3538fe Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 25 Mar 2024 15:57:27 -0700 Subject: [PATCH 42/82] Add missing semicolon --- src/coreclr/vm/threadstatics.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index 0f3f6334e940e..a4071657482aa 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -52,7 +52,7 @@ class Thread; enum class TLSIndexType { Standard, // IndexOffset for this form of TLSIndex is scaled by sizeof(void*) and then added to ThreadLocalData::pTLSArrayData to get the final address -} +}; struct TLSIndex { From b2850ea29aaf2ef3604e81297005283b6ae9acbe Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 25 Mar 2024 16:07:49 -0700 Subject: [PATCH 43/82] Jit formatting round 1 --- src/coreclr/jit/compiler.hpp | 5 +++-- src/coreclr/jit/flowgraph.cpp | 15 ++++++++------- src/coreclr/jit/utils.cpp | 3 ++- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 7249bedf6c4e1..9ad103c8f4706 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -3802,8 +3802,9 @@ inline bool Compiler::IsSharedStaticHelper(GenTree* tree) // helpers being added to IsSharedStaticHelper helper == CORINFO_HELP_GETSTATICFIELDADDR_TLS || - - (helper >= CORINFO_HELP_GET_GCSTATIC_BASE && helper <= CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) + + (helper >= CORINFO_HELP_GET_GCSTATIC_BASE && + helper <= CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) #ifdef FEATURE_READYTORUN || helper == CORINFO_HELP_READYTORUN_GENERIC_STATIC_BASE || helper == CORINFO_HELP_READYTORUN_GCSTATIC_BASE || helper == CORINFO_HELP_READYTORUN_NONGCSTATIC_BASE || helper == CORINFO_HELP_READYTORUN_THREADSTATIC_BASE || diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 1de13747be59e..8623bb9466e67 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -746,7 +746,7 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo GenTreeCall* result; if ((helper == CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) || - (helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED)) + (helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED)) { result = gtNewHelperCallNode(helper, type, gtNewIconNode(typeIndex)); } @@ -755,18 +755,19 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo helper == CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE || helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE) { - result = gtNewHelperCallNode(helper, type, gtNewIconNode((size_t)info.compCompHnd->getClassThreadStaticDynamicInfo(cls), TYP_I_IMPL)); + result = gtNewHelperCallNode(helper, type, + gtNewIconNode((size_t)info.compCompHnd->getClassThreadStaticDynamicInfo(cls), + TYP_I_IMPL)); } - else if (helper == CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE || - helper == CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE || + else if (helper == CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE || helper == CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE || helper == CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE_NOCTOR || helper == CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE_NOCTOR || - helper == CORINFO_HELP_GETPINNED_GCSTATIC_BASE || - helper == CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE || + helper == CORINFO_HELP_GETPINNED_GCSTATIC_BASE || helper == CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE || helper == CORINFO_HELP_GETPINNED_GCSTATIC_BASE_NOCTOR || helper == CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE_NOCTOR) { - result = gtNewHelperCallNode(helper, type, gtNewIconNode(info.compCompHnd->getClassStaticDynamicInfo(cls), TYP_I_IMPL)); + result = gtNewHelperCallNode(helper, type, + gtNewIconNode(info.compCompHnd->getClassStaticDynamicInfo(cls), TYP_I_IMPL)); } else { diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index 1c8139a46785f..54b7adb779510 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -1696,7 +1696,8 @@ void HelperCallProperties::init() // Helpers that load the base address for static variables. // We divide these between those that may and may not invoke // static class constructors. - // TODO, logic previously had the dynamic class init helper but not the non-dynamic ones, now the dynamic helper is gone, what to do? + // TODO, logic previously had the dynamic class init helper but not the non-dynamic ones, now the dynamic + // helper is gone, what to do? case CORINFO_HELP_GET_GCSTATIC_BASE: case CORINFO_HELP_GET_NONGCSTATIC_BASE: case CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE: From d526181c00ff3d04c51f2c86eb99c161b589d828 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 25 Mar 2024 16:20:25 -0700 Subject: [PATCH 44/82] Update InitClass helper data --- src/coreclr/jit/utils.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index 54b7adb779510..f3556e51b3214 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -1696,8 +1696,6 @@ void HelperCallProperties::init() // Helpers that load the base address for static variables. // We divide these between those that may and may not invoke // static class constructors. - // TODO, logic previously had the dynamic class init helper but not the non-dynamic ones, now the dynamic - // helper is gone, what to do? case CORINFO_HELP_GET_GCSTATIC_BASE: case CORINFO_HELP_GET_NONGCSTATIC_BASE: case CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE: @@ -1723,6 +1721,12 @@ void HelperCallProperties::init() mayRunCctor = true; break; + case CORINFO_HELP_INITCLASS: + case CORINFO_HELP_INITINSTCLASS: + isPure = true; + mayRunCctor = true; + break; + case CORINFO_HELP_GET_GCSTATIC_BASE_NOCTOR: case CORINFO_HELP_GET_NONGCSTATIC_BASE_NOCTOR: case CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE_NOCTOR: From e2aa42fe209ab1b042db15fe3e059190495311ad Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 26 Mar 2024 12:17:19 -0700 Subject: [PATCH 45/82] Revert "Update R2R version and update comment on thread statics" This reverts commit 0e7c1d43332982fc44180186b168a791c8b079cd. --- src/coreclr/inc/corinfo.h | 2 + src/coreclr/inc/jithelpers.h | 2 + src/coreclr/inc/readytorun.h | 14 +- src/coreclr/inc/readytorunhelpers.h | 2 + src/coreclr/jit/utils.cpp | 2 + .../Common/Internal/Runtime/ModuleHeaders.cs | 4 +- .../Internal/Runtime/ReadyToRunConstants.cs | 2 + .../Common/JitInterface/CorInfoHelpFunc.cs | 2 + .../JitInterface/CorInfoImpl.ReadyToRun.cs | 6 + .../ReadyToRunSignature.cs | 658 +++++++++--------- .../JitInterface/CorInfoImpl.RyuJit.cs | 6 + 11 files changed, 357 insertions(+), 343 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 20aa3f1ff8007..b39a2adab1836 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -397,6 +397,8 @@ enum CorInfoHelpFunc CORINFO_HELP_DBL2ULNG_OVF, CORINFO_HELP_FLTREM, CORINFO_HELP_DBLREM, + CORINFO_HELP_FLTROUND, + CORINFO_HELP_DBLROUND, /* Allocating a new object. Always use ICorClassInfo::getNewHelper() to decide which is the right helper to use to allocate an object of a given type. */ diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h index eefdc9bdb4137..f479c61663b02 100644 --- a/src/coreclr/inc/jithelpers.h +++ b/src/coreclr/inc/jithelpers.h @@ -65,6 +65,8 @@ JITHELPER(CORINFO_HELP_DBL2ULNG_OVF, JIT_Dbl2ULngOvf, CORINFO_HELP_SIG_8_STACK) JITHELPER(CORINFO_HELP_FLTREM, JIT_FltRem, CORINFO_HELP_SIG_8_STACK) JITHELPER(CORINFO_HELP_DBLREM, JIT_DblRem, CORINFO_HELP_SIG_16_STACK) + JITHELPER(CORINFO_HELP_FLTROUND, JIT_FloatRound, CORINFO_HELP_SIG_8_STACK) + JITHELPER(CORINFO_HELP_DBLROUND, JIT_DoubleRound, CORINFO_HELP_SIG_16_STACK) // Allocating a new object JITHELPER(CORINFO_HELP_NEWFAST, JIT_New, CORINFO_HELP_SIG_REG_ONLY) diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index e12abe6e8a6da..41a4aa251fa74 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -19,10 +19,10 @@ // src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h // If you update this, ensure you run `git grep MINIMUM_READYTORUN_MAJOR_VERSION` // and handle pending work. -#define READYTORUN_MAJOR_VERSION 0x000A -#define READYTORUN_MINOR_VERSION 0x0000 +#define READYTORUN_MAJOR_VERSION 0x0009 +#define READYTORUN_MINOR_VERSION 0x0002 -#define MINIMUM_READYTORUN_MAJOR_VERSION 0x00A +#define MINIMUM_READYTORUN_MAJOR_VERSION 0x009 // R2R Version 2.1 adds the InliningInfo section // R2R Version 2.2 adds the ProfileDataInfo section @@ -34,7 +34,6 @@ // R2R Version 9.0 adds support for the Vector512 type // R2R Version 9.1 adds new helpers to allocate objects on frozen segments // R2R Version 9.2 adds MemZero and NativeMemSet helpers -// R2R Version 10.0 changes the algorithm for statics layout struct READYTORUN_CORE_HEADER @@ -405,8 +404,11 @@ enum ReadyToRunHelper READYTORUN_HELPER_DblRem = 0xE0, READYTORUN_HELPER_FltRem = 0xE1, - // Formerly READYTORUN_HELPER_DblRound = 0xE2, - // Formerly READYTORUN_HELPER_FltRound = 0xE3, + // These two helpers can be removed once MINIMUM_READYTORUN_MAJOR_VERSION is 10+ + // alongside the CORINFO_HELP_FLTROUND/CORINFO_HELP_DBLROUND + // counterparts and all related code. + READYTORUN_HELPER_DblRound = 0xE2, + READYTORUN_HELPER_FltRound = 0xE3, #ifdef FEATURE_EH_FUNCLETS // Personality routines diff --git a/src/coreclr/inc/readytorunhelpers.h b/src/coreclr/inc/readytorunhelpers.h index e2eb23ec61cc0..3bf63aca41209 100644 --- a/src/coreclr/inc/readytorunhelpers.h +++ b/src/coreclr/inc/readytorunhelpers.h @@ -89,6 +89,8 @@ HELPER(READYTORUN_HELPER_Dbl2ULngOvf, CORINFO_HELP_DBL2ULNG_OVF, HELPER(READYTORUN_HELPER_FltRem, CORINFO_HELP_FLTREM, ) HELPER(READYTORUN_HELPER_DblRem, CORINFO_HELP_DBLREM, ) +HELPER(READYTORUN_HELPER_FltRound, CORINFO_HELP_FLTROUND, ) +HELPER(READYTORUN_HELPER_DblRound, CORINFO_HELP_DBLROUND, ) #ifndef TARGET_X86 HELPER(READYTORUN_HELPER_PersonalityRoutine, CORINFO_HELP_EE_PERSONALITY_ROUTINE, OPTIMIZEFORSIZE) diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index f3556e51b3214..b154b263616f3 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -1542,6 +1542,8 @@ void HelperCallProperties::init() case CORINFO_HELP_DBL2ULNG: case CORINFO_HELP_FLTREM: case CORINFO_HELP_DBLREM: + case CORINFO_HELP_FLTROUND: + case CORINFO_HELP_DBLROUND: isPure = true; noThrow = true; diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index 8aa1080913076..6fc5d9542e160 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -15,8 +15,8 @@ internal struct ReadyToRunHeaderConstants { public const uint Signature = 0x00525452; // 'RTR' - public const ushort CurrentMajorVersion = 10; - public const ushort CurrentMinorVersion = 0; + public const ushort CurrentMajorVersion = 9; + public const ushort CurrentMinorVersion = 2; } #if READYTORUN #pragma warning disable 0169 diff --git a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs index 15b8d57af6af8..a37945534865b 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs @@ -315,6 +315,8 @@ public enum ReadyToRunHelper // Floating point ops DblRem = 0xE0, FltRem = 0xE1, + DblRound = 0xE2, + FltRound = 0xE3, // Personality routines PersonalityRoutine = 0xF0, diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs index 2915cd7b31fe3..4b474fce1c181 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs @@ -40,6 +40,8 @@ public enum CorInfoHelpFunc CORINFO_HELP_DBL2ULNG_OVF, CORINFO_HELP_FLTREM, CORINFO_HELP_DBLREM, + CORINFO_HELP_FLTROUND, + CORINFO_HELP_DBLROUND, /* Allocating a new object. Always use ICorClassInfo::getNewHelper() to decide which is the right helper to use to allocate an object of a given type. */ diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 674eeb9e7957f..79f785e319446 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -1167,6 +1167,12 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum) case CorInfoHelpFunc.CORINFO_HELP_DBLREM: id = ReadyToRunHelper.DblRem; break; + case CorInfoHelpFunc.CORINFO_HELP_FLTROUND: + id = ReadyToRunHelper.FltRound; + break; + case CorInfoHelpFunc.CORINFO_HELP_DBLROUND: + id = ReadyToRunHelper.DblRound; + break; case CorInfoHelpFunc.CORINFO_HELP_CHKCASTANY: id = ReadyToRunHelper.CheckCastAny; diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs index 8c66cd8d59d4d..0eae2f10cb8f0 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs @@ -1586,428 +1586,416 @@ private void ParseHelper(StringBuilder builder) { uint helperType = ReadUIntAndEmitInlineSignatureBinary(builder); - bool retiredHelperFound = false; - if (r2rReader.MajorVersion <= 9) + switch ((ReadyToRunHelper)helperType) { - if (helperType == 0xE2) - { - retiredHelperFound = true; - builder.Append("DBL_ROUND"); - } - else if (helperType == 0xE3) - { - retiredHelperFound = true; - builder.Append("FLT_ROUND"); - } - } - - if (!retiredHelperFound) - { - switch ((ReadyToRunHelper)helperType) - { - case ReadyToRunHelper.Invalid: - builder.Append("INVALID"); - break; + case ReadyToRunHelper.Invalid: + builder.Append("INVALID"); + break; - // Not a real helper - handle to current module passed to delay load helpers. - case ReadyToRunHelper.Module: - builder.Append("MODULE"); - break; + // Not a real helper - handle to current module passed to delay load helpers. + case ReadyToRunHelper.Module: + builder.Append("MODULE"); + break; - case ReadyToRunHelper.GSCookie: - builder.Append("GC_COOKIE"); - break; + case ReadyToRunHelper.GSCookie: + builder.Append("GC_COOKIE"); + break; - case ReadyToRunHelper.IndirectTrapThreads: - builder.Append("INDIRECT_TRAP_THREADS"); - break; + case ReadyToRunHelper.IndirectTrapThreads: + builder.Append("INDIRECT_TRAP_THREADS"); + break; - // - // Delay load helpers - // + // + // Delay load helpers + // - // All delay load helpers use custom calling convention: - // - scratch register - address of indirection cell. 0 = address is inferred from callsite. - // - stack - section index, module handle - case ReadyToRunHelper.DelayLoad_MethodCall: - builder.Append("DELAYLOAD_METHODCALL"); - break; + // All delay load helpers use custom calling convention: + // - scratch register - address of indirection cell. 0 = address is inferred from callsite. + // - stack - section index, module handle + case ReadyToRunHelper.DelayLoad_MethodCall: + builder.Append("DELAYLOAD_METHODCALL"); + break; - case ReadyToRunHelper.DelayLoad_Helper: - builder.Append("DELAYLOAD_HELPER"); - break; + case ReadyToRunHelper.DelayLoad_Helper: + builder.Append("DELAYLOAD_HELPER"); + break; - case ReadyToRunHelper.DelayLoad_Helper_Obj: - builder.Append("DELAYLOAD_HELPER_OBJ"); - break; + case ReadyToRunHelper.DelayLoad_Helper_Obj: + builder.Append("DELAYLOAD_HELPER_OBJ"); + break; - case ReadyToRunHelper.DelayLoad_Helper_ObjObj: - builder.Append("DELAYLOAD_HELPER_OBJ_OBJ"); - break; + case ReadyToRunHelper.DelayLoad_Helper_ObjObj: + builder.Append("DELAYLOAD_HELPER_OBJ_OBJ"); + break; - // JIT helpers + // JIT helpers - // Exception handling helpers - case ReadyToRunHelper.Throw: - builder.Append("THROW"); - break; + // Exception handling helpers + case ReadyToRunHelper.Throw: + builder.Append("THROW"); + break; - case ReadyToRunHelper.Rethrow: - builder.Append("RETHROW"); - break; + case ReadyToRunHelper.Rethrow: + builder.Append("RETHROW"); + break; - case ReadyToRunHelper.Overflow: - builder.Append("OVERFLOW"); - break; + case ReadyToRunHelper.Overflow: + builder.Append("OVERFLOW"); + break; - case ReadyToRunHelper.RngChkFail: - builder.Append("RNG_CHK_FAIL"); - break; + case ReadyToRunHelper.RngChkFail: + builder.Append("RNG_CHK_FAIL"); + break; - case ReadyToRunHelper.FailFast: - builder.Append("FAIL_FAST"); - break; + case ReadyToRunHelper.FailFast: + builder.Append("FAIL_FAST"); + break; - case ReadyToRunHelper.ThrowNullRef: - builder.Append("THROW_NULL_REF"); - break; + case ReadyToRunHelper.ThrowNullRef: + builder.Append("THROW_NULL_REF"); + break; - case ReadyToRunHelper.ThrowDivZero: - builder.Append("THROW_DIV_ZERO"); - break; + case ReadyToRunHelper.ThrowDivZero: + builder.Append("THROW_DIV_ZERO"); + break; - // Write barriers - case ReadyToRunHelper.WriteBarrier: - builder.Append("WRITE_BARRIER"); - break; + // Write barriers + case ReadyToRunHelper.WriteBarrier: + builder.Append("WRITE_BARRIER"); + break; - case ReadyToRunHelper.CheckedWriteBarrier: - builder.Append("CHECKED_WRITE_BARRIER"); - break; + case ReadyToRunHelper.CheckedWriteBarrier: + builder.Append("CHECKED_WRITE_BARRIER"); + break; - case ReadyToRunHelper.ByRefWriteBarrier: - builder.Append("BYREF_WRITE_BARRIER"); - break; + case ReadyToRunHelper.ByRefWriteBarrier: + builder.Append("BYREF_WRITE_BARRIER"); + break; - // Array helpers - case ReadyToRunHelper.Stelem_Ref: - builder.Append("STELEM_REF"); - break; + // Array helpers + case ReadyToRunHelper.Stelem_Ref: + builder.Append("STELEM_REF"); + break; - case ReadyToRunHelper.Ldelema_Ref: - builder.Append("LDELEMA_REF"); - break; + case ReadyToRunHelper.Ldelema_Ref: + builder.Append("LDELEMA_REF"); + break; - case ReadyToRunHelper.MemSet: - builder.Append("MEM_SET"); - break; + case ReadyToRunHelper.MemSet: + builder.Append("MEM_SET"); + break; - case ReadyToRunHelper.MemZero: - builder.Append("MEM_ZERO"); - break; + case ReadyToRunHelper.MemZero: + builder.Append("MEM_ZERO"); + break; - case ReadyToRunHelper.MemCpy: - builder.Append("MEM_CPY"); - break; + case ReadyToRunHelper.MemCpy: + builder.Append("MEM_CPY"); + break; - case ReadyToRunHelper.NativeMemSet: - builder.Append("NATIVE_MEM_SET"); - break; + case ReadyToRunHelper.NativeMemSet: + builder.Append("NATIVE_MEM_SET"); + break; - // PInvoke helpers - case ReadyToRunHelper.PInvokeBegin: - builder.Append("PINVOKE_BEGIN"); - break; + // PInvoke helpers + case ReadyToRunHelper.PInvokeBegin: + builder.Append("PINVOKE_BEGIN"); + break; - case ReadyToRunHelper.PInvokeEnd: - builder.Append("PINVOKE_END"); - break; + case ReadyToRunHelper.PInvokeEnd: + builder.Append("PINVOKE_END"); + break; - case ReadyToRunHelper.GCPoll: - builder.Append("GCPOLL"); - break; + case ReadyToRunHelper.GCPoll: + builder.Append("GCPOLL"); + break; - case ReadyToRunHelper.GetCurrentManagedThreadId: - builder.Append("GET_CURRENT_MANAGED_THREAD_ID"); - break; + case ReadyToRunHelper.GetCurrentManagedThreadId: + builder.Append("GET_CURRENT_MANAGED_THREAD_ID"); + break; - case ReadyToRunHelper.ReversePInvokeEnter: - builder.Append("REVERSE_PINVOKE_ENTER"); - break; + case ReadyToRunHelper.ReversePInvokeEnter: + builder.Append("REVERSE_PINVOKE_ENTER"); + break; - case ReadyToRunHelper.ReversePInvokeExit: - builder.Append("REVERSE_PINVOKE_EXIT"); - break; + case ReadyToRunHelper.ReversePInvokeExit: + builder.Append("REVERSE_PINVOKE_EXIT"); + break; - // Get string handle lazily - case ReadyToRunHelper.GetString: - builder.Append("GET_STRING"); - break; + // Get string handle lazily + case ReadyToRunHelper.GetString: + builder.Append("GET_STRING"); + break; - // Used by /Tuning for Profile optimizations - case ReadyToRunHelper.LogMethodEnter: - builder.Append("LOG_METHOD_ENTER"); - break; + // Used by /Tuning for Profile optimizations + case ReadyToRunHelper.LogMethodEnter: + builder.Append("LOG_METHOD_ENTER"); + break; - // Reflection helpers - case ReadyToRunHelper.GetRuntimeTypeHandle: - builder.Append("GET_RUNTIME_TYPE_HANDLE"); - break; + // Reflection helpers + case ReadyToRunHelper.GetRuntimeTypeHandle: + builder.Append("GET_RUNTIME_TYPE_HANDLE"); + break; - case ReadyToRunHelper.GetRuntimeMethodHandle: - builder.Append("GET_RUNTIME_METHOD_HANDLE"); - break; + case ReadyToRunHelper.GetRuntimeMethodHandle: + builder.Append("GET_RUNTIME_METHOD_HANDLE"); + break; - case ReadyToRunHelper.GetRuntimeFieldHandle: - builder.Append("GET_RUNTIME_FIELD_HANDLE"); - break; + case ReadyToRunHelper.GetRuntimeFieldHandle: + builder.Append("GET_RUNTIME_FIELD_HANDLE"); + break; - case ReadyToRunHelper.Box: - builder.Append("BOX"); - break; + case ReadyToRunHelper.Box: + builder.Append("BOX"); + break; - case ReadyToRunHelper.Box_Nullable: - builder.Append("BOX_NULLABLE"); - break; + case ReadyToRunHelper.Box_Nullable: + builder.Append("BOX_NULLABLE"); + break; - case ReadyToRunHelper.Unbox: - builder.Append("UNBOX"); - break; + case ReadyToRunHelper.Unbox: + builder.Append("UNBOX"); + break; - case ReadyToRunHelper.Unbox_Nullable: - builder.Append("UNBOX_NULLABLE"); - break; + case ReadyToRunHelper.Unbox_Nullable: + builder.Append("UNBOX_NULLABLE"); + break; - case ReadyToRunHelper.NewMultiDimArr: - builder.Append("NEW_MULTI_DIM_ARR"); - break; + case ReadyToRunHelper.NewMultiDimArr: + builder.Append("NEW_MULTI_DIM_ARR"); + break; - case ReadyToRunHelper.MonitorEnter: - builder.Append("MONITOR_ENTER"); - break; + case ReadyToRunHelper.MonitorEnter: + builder.Append("MONITOR_ENTER"); + break; - case ReadyToRunHelper.MonitorExit: - builder.Append("MONITOR_EXIT"); - break; + case ReadyToRunHelper.MonitorExit: + builder.Append("MONITOR_EXIT"); + break; - // Helpers used with generic handle lookup cases - case ReadyToRunHelper.NewObject: - builder.Append("NEW_OBJECT"); - break; + // Helpers used with generic handle lookup cases + case ReadyToRunHelper.NewObject: + builder.Append("NEW_OBJECT"); + break; - case ReadyToRunHelper.NewArray: - builder.Append("NEW_ARRAY"); - break; + case ReadyToRunHelper.NewArray: + builder.Append("NEW_ARRAY"); + break; - case ReadyToRunHelper.NewMaybeFrozenArray: - builder.Append("NEW_MAYBEFROZEN_ARRAY"); - break; + case ReadyToRunHelper.NewMaybeFrozenArray: + builder.Append("NEW_MAYBEFROZEN_ARRAY"); + break; - case ReadyToRunHelper.NewMaybeFrozenObject: - builder.Append("NEW_MAYBEFROZEN_OBJECT"); - break; + case ReadyToRunHelper.NewMaybeFrozenObject: + builder.Append("NEW_MAYBEFROZEN_OBJECT"); + break; - case ReadyToRunHelper.CheckCastAny: - builder.Append("CHECK_CAST_ANY"); - break; + case ReadyToRunHelper.CheckCastAny: + builder.Append("CHECK_CAST_ANY"); + break; - case ReadyToRunHelper.CheckInstanceAny: - builder.Append("CHECK_INSTANCE_ANY"); - break; + case ReadyToRunHelper.CheckInstanceAny: + builder.Append("CHECK_INSTANCE_ANY"); + break; - case ReadyToRunHelper.IsInstanceOfException: - builder.Append("SIMPLE_ISINSTANCE_OF"); - break; + case ReadyToRunHelper.IsInstanceOfException: + builder.Append("SIMPLE_ISINSTANCE_OF"); + break; - case ReadyToRunHelper.GenericGcStaticBase: - builder.Append("GENERIC_GC_STATIC_BASE"); - break; + case ReadyToRunHelper.GenericGcStaticBase: + builder.Append("GENERIC_GC_STATIC_BASE"); + break; - case ReadyToRunHelper.GenericNonGcStaticBase: - builder.Append("GENERIC_NON_GC_STATIC_BASE"); - break; + case ReadyToRunHelper.GenericNonGcStaticBase: + builder.Append("GENERIC_NON_GC_STATIC_BASE"); + break; - case ReadyToRunHelper.GenericGcTlsBase: - builder.Append("GENERIC_GC_TLS_BASE"); - break; + case ReadyToRunHelper.GenericGcTlsBase: + builder.Append("GENERIC_GC_TLS_BASE"); + break; - case ReadyToRunHelper.GenericNonGcTlsBase: - builder.Append("GENERIC_NON_GC_TLS_BASE"); - break; + case ReadyToRunHelper.GenericNonGcTlsBase: + builder.Append("GENERIC_NON_GC_TLS_BASE"); + break; - case ReadyToRunHelper.VirtualFuncPtr: - builder.Append("VIRTUAL_FUNC_PTR"); - break; + case ReadyToRunHelper.VirtualFuncPtr: + builder.Append("VIRTUAL_FUNC_PTR"); + break; - // Long mul/div/shift ops - case ReadyToRunHelper.LMul: - builder.Append("LMUL"); - break; + // Long mul/div/shift ops + case ReadyToRunHelper.LMul: + builder.Append("LMUL"); + break; - case ReadyToRunHelper.LMulOfv: - builder.Append("LMUL_OFV"); - break; + case ReadyToRunHelper.LMulOfv: + builder.Append("LMUL_OFV"); + break; - case ReadyToRunHelper.ULMulOvf: - builder.Append("ULMUL_OVF"); - break; + case ReadyToRunHelper.ULMulOvf: + builder.Append("ULMUL_OVF"); + break; - case ReadyToRunHelper.LDiv: - builder.Append("LDIV"); - break; + case ReadyToRunHelper.LDiv: + builder.Append("LDIV"); + break; - case ReadyToRunHelper.LMod: - builder.Append("LMOD"); - break; + case ReadyToRunHelper.LMod: + builder.Append("LMOD"); + break; - case ReadyToRunHelper.ULDiv: - builder.Append("ULDIV"); - break; + case ReadyToRunHelper.ULDiv: + builder.Append("ULDIV"); + break; - case ReadyToRunHelper.ULMod: - builder.Append("ULMOD"); - break; + case ReadyToRunHelper.ULMod: + builder.Append("ULMOD"); + break; - case ReadyToRunHelper.LLsh: - builder.Append("LLSH"); - break; + case ReadyToRunHelper.LLsh: + builder.Append("LLSH"); + break; - case ReadyToRunHelper.LRsh: - builder.Append("LRSH"); - break; + case ReadyToRunHelper.LRsh: + builder.Append("LRSH"); + break; - case ReadyToRunHelper.LRsz: - builder.Append("LRSZ"); - break; + case ReadyToRunHelper.LRsz: + builder.Append("LRSZ"); + break; - case ReadyToRunHelper.Lng2Dbl: - builder.Append("LNG2DBL"); - break; + case ReadyToRunHelper.Lng2Dbl: + builder.Append("LNG2DBL"); + break; - case ReadyToRunHelper.ULng2Dbl: - builder.Append("ULNG2DBL"); - break; + case ReadyToRunHelper.ULng2Dbl: + builder.Append("ULNG2DBL"); + break; - // 32-bit division helpers - case ReadyToRunHelper.Div: - builder.Append("DIV"); - break; + // 32-bit division helpers + case ReadyToRunHelper.Div: + builder.Append("DIV"); + break; - case ReadyToRunHelper.Mod: - builder.Append("MOD"); - break; + case ReadyToRunHelper.Mod: + builder.Append("MOD"); + break; - case ReadyToRunHelper.UDiv: - builder.Append("UDIV"); - break; + case ReadyToRunHelper.UDiv: + builder.Append("UDIV"); + break; - case ReadyToRunHelper.UMod: - builder.Append("UMOD"); - break; + case ReadyToRunHelper.UMod: + builder.Append("UMOD"); + break; - // Floating point conversions - case ReadyToRunHelper.Dbl2Int: - builder.Append("DBL2INT"); - break; + // Floating point conversions + case ReadyToRunHelper.Dbl2Int: + builder.Append("DBL2INT"); + break; - case ReadyToRunHelper.Dbl2IntOvf: - builder.Append("DBL2INTOVF"); - break; + case ReadyToRunHelper.Dbl2IntOvf: + builder.Append("DBL2INTOVF"); + break; - case ReadyToRunHelper.Dbl2Lng: - builder.Append("DBL2LNG"); - break; + case ReadyToRunHelper.Dbl2Lng: + builder.Append("DBL2LNG"); + break; - case ReadyToRunHelper.Dbl2LngOvf: - builder.Append("DBL2LNGOVF"); - break; + case ReadyToRunHelper.Dbl2LngOvf: + builder.Append("DBL2LNGOVF"); + break; - case ReadyToRunHelper.Dbl2UInt: - builder.Append("DBL2UINT"); - break; + case ReadyToRunHelper.Dbl2UInt: + builder.Append("DBL2UINT"); + break; - case ReadyToRunHelper.Dbl2UIntOvf: - builder.Append("DBL2UINTOVF"); - break; + case ReadyToRunHelper.Dbl2UIntOvf: + builder.Append("DBL2UINTOVF"); + break; - case ReadyToRunHelper.Dbl2ULng: - builder.Append("DBL2ULNG"); - break; + case ReadyToRunHelper.Dbl2ULng: + builder.Append("DBL2ULNG"); + break; - case ReadyToRunHelper.Dbl2ULngOvf: - builder.Append("DBL2ULNGOVF"); - break; + case ReadyToRunHelper.Dbl2ULngOvf: + builder.Append("DBL2ULNGOVF"); + break; - // Floating point ops - case ReadyToRunHelper.DblRem: - builder.Append("DBL_REM"); - break; - case ReadyToRunHelper.FltRem: - builder.Append("FLT_REM"); - break; + // Floating point ops + case ReadyToRunHelper.DblRem: + builder.Append("DBL_REM"); + break; + case ReadyToRunHelper.FltRem: + builder.Append("FLT_REM"); + break; + case ReadyToRunHelper.DblRound: + builder.Append("DBL_ROUND"); + break; + case ReadyToRunHelper.FltRound: + builder.Append("FLT_ROUND"); + break; - // Personality routines - case ReadyToRunHelper.PersonalityRoutine: - builder.Append("PERSONALITY_ROUTINE"); - break; - case ReadyToRunHelper.PersonalityRoutineFilterFunclet: - builder.Append("PERSONALITY_ROUTINE_FILTER_FUNCLET"); - break; + // Personality routines + case ReadyToRunHelper.PersonalityRoutine: + builder.Append("PERSONALITY_ROUTINE"); + break; + case ReadyToRunHelper.PersonalityRoutineFilterFunclet: + builder.Append("PERSONALITY_ROUTINE_FILTER_FUNCLET"); + break; - // - // Deprecated/legacy - // + // + // Deprecated/legacy + // - // JIT32 x86-specific write barriers - case ReadyToRunHelper.WriteBarrier_EAX: - builder.Append("WRITE_BARRIER_EAX"); - break; - case ReadyToRunHelper.WriteBarrier_EBX: - builder.Append("WRITE_BARRIER_EBX"); - break; - case ReadyToRunHelper.WriteBarrier_ECX: - builder.Append("WRITE_BARRIER_ECX"); - break; - case ReadyToRunHelper.WriteBarrier_ESI: - builder.Append("WRITE_BARRIER_ESI"); - break; - case ReadyToRunHelper.WriteBarrier_EDI: - builder.Append("WRITE_BARRIER_EDI"); - break; - case ReadyToRunHelper.WriteBarrier_EBP: - builder.Append("WRITE_BARRIER_EBP"); - break; - case ReadyToRunHelper.CheckedWriteBarrier_EAX: - builder.Append("CHECKED_WRITE_BARRIER_EAX"); - break; - case ReadyToRunHelper.CheckedWriteBarrier_EBX: - builder.Append("CHECKED_WRITE_BARRIER_EBX"); - break; - case ReadyToRunHelper.CheckedWriteBarrier_ECX: - builder.Append("CHECKED_WRITE_BARRIER_ECX"); - break; - case ReadyToRunHelper.CheckedWriteBarrier_ESI: - builder.Append("CHECKED_WRITE_BARRIER_ESI"); - break; - case ReadyToRunHelper.CheckedWriteBarrier_EDI: - builder.Append("CHECKED_WRITE_BARRIER_EDI"); - break; - case ReadyToRunHelper.CheckedWriteBarrier_EBP: - builder.Append("CHECKED_WRITE_BARRIER_EBP"); - break; + // JIT32 x86-specific write barriers + case ReadyToRunHelper.WriteBarrier_EAX: + builder.Append("WRITE_BARRIER_EAX"); + break; + case ReadyToRunHelper.WriteBarrier_EBX: + builder.Append("WRITE_BARRIER_EBX"); + break; + case ReadyToRunHelper.WriteBarrier_ECX: + builder.Append("WRITE_BARRIER_ECX"); + break; + case ReadyToRunHelper.WriteBarrier_ESI: + builder.Append("WRITE_BARRIER_ESI"); + break; + case ReadyToRunHelper.WriteBarrier_EDI: + builder.Append("WRITE_BARRIER_EDI"); + break; + case ReadyToRunHelper.WriteBarrier_EBP: + builder.Append("WRITE_BARRIER_EBP"); + break; + case ReadyToRunHelper.CheckedWriteBarrier_EAX: + builder.Append("CHECKED_WRITE_BARRIER_EAX"); + break; + case ReadyToRunHelper.CheckedWriteBarrier_EBX: + builder.Append("CHECKED_WRITE_BARRIER_EBX"); + break; + case ReadyToRunHelper.CheckedWriteBarrier_ECX: + builder.Append("CHECKED_WRITE_BARRIER_ECX"); + break; + case ReadyToRunHelper.CheckedWriteBarrier_ESI: + builder.Append("CHECKED_WRITE_BARRIER_ESI"); + break; + case ReadyToRunHelper.CheckedWriteBarrier_EDI: + builder.Append("CHECKED_WRITE_BARRIER_EDI"); + break; + case ReadyToRunHelper.CheckedWriteBarrier_EBP: + builder.Append("CHECKED_WRITE_BARRIER_EBP"); + break; - // JIT32 x86-specific exception handling - case ReadyToRunHelper.EndCatch: - builder.Append("END_CATCH"); - break; + // JIT32 x86-specific exception handling + case ReadyToRunHelper.EndCatch: + builder.Append("END_CATCH"); + break; - case ReadyToRunHelper.StackProbe: - builder.Append("STACK_PROBE"); - break; + case ReadyToRunHelper.StackProbe: + builder.Append("STACK_PROBE"); + break; - default: - throw new BadImageFormatException(); - } + default: + throw new BadImageFormatException(); } } diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 91b594d5fe564..f98d7b0fc0650 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -693,6 +693,12 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum) case CorInfoHelpFunc.CORINFO_HELP_DBLREM: id = ReadyToRunHelper.DblRem; break; + case CorInfoHelpFunc.CORINFO_HELP_FLTROUND: + id = ReadyToRunHelper.FltRound; + break; + case CorInfoHelpFunc.CORINFO_HELP_DBLROUND: + id = ReadyToRunHelper.DblRound; + break; case CorInfoHelpFunc.CORINFO_HELP_JIT_PINVOKE_BEGIN: id = ReadyToRunHelper.PInvokeBegin; From 24801db8d9dbe3c02d727874c32f030b009b164e Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 26 Mar 2024 12:17:38 -0700 Subject: [PATCH 46/82] Update thread statics comments --- src/coreclr/vm/threadstatics.cpp | 2 + src/coreclr/vm/threadstatics.h | 114 +++++++++++++++++++++---------- 2 files changed, 79 insertions(+), 37 deletions(-) diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 43dbd2ce8264b..34dfeafed1ec9 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -67,6 +67,7 @@ PTR_MethodTable LookupMethodTableForThreadStaticKnownToBeAllocated(TLSIndex inde } CONTRACTL_END; + // TODO, if and when we get array indices, we should be pickier. return g_pThreadStaticTypeIndices->LookupTlsIndexKnownToBeAllocated(index); } @@ -81,6 +82,7 @@ PTR_MethodTable LookupMethodTableAndFlagForThreadStatic(TLSIndex index, bool *pI MODE_COOPERATIVE; } CONTRACTL_END; + // TODO, if and when we get array indices, we should be pickier. PTR_MethodTable retVal = g_pThreadStaticTypeIndices->Lookup(index, pIsGCStatic, pIsCollectible); return retVal; } diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index a4071657482aa..206da905d9e9e 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -4,46 +4,87 @@ // Thread local storage is designed to be as efficient as possible. // This leads to several different access patterns. // +// There shall be a global TLS data structure used for all threads. This is initialized before any managed code is permitted to run on a thread +// struct TLSArray +// { +// int32_t cTLSData; // Size in bytes of offset into the TLS array which is valid +// void* pTLSArrayData; // Points at the Thread local array data. +// }; +// +// Used to store access to TLS data for a single index when the TLS is accessed while the class constructor is running +// struct InFlightTLSData +// { +// InFlightTLSData* pNext; // Points at the next in-flight TLS data +// TLSIndex tlsIndex; // The TLS index for the static +// void* pTLSData; // The TLS data for the static +// }; +// +// struct ThreadLocalLoaderAllocator +// { +// ThreadLocalLoaderAllocator* pNext; // Points at the next thread local loader allocator +// LoaderAllocator* pLoaderAllocator; // The loader allocator that has a TLS used in this thread +// bool ReportToGC(PromoteFunction* fn, ScanContext* sc, int flags); // Reports the thread local loader allocator state to the GC, returns true if the ThreadLocalLoaderAllocator structure should be removed from the linked list. This is what allows the GC statics for collectible types to actually exist on the nonGC thread local storage array +// }; +// +// struct ThreadLocalData +// { +// TLSArray nongcArray; // Array for nonGC data, as well as collectible GC static. cTLSData is initialized to PRE_ALLOCATED_TLS_NONGC_SLOT_COUNT * sizeof(void*) - 1, and pTLSArrayData points at memory of size PRE_ALLOCATED_TLS_NONGC_SLOT_COUNT * sizeof(void*) at thread startup +// TLSArray gcArray; // Array for non-collectible GC pointers. cTLSData is initialized to PRE_ALLOCATED_TLS_GC_SLOT_COUNT * sizeof(OBJECTREF) + sizeof(void*) * 2 - 1, and pTLSArrayData points at a managed object[], initialized to an object array of size PRE_ALLOCATED_TLS_GC_SLOT_COUNT at thread startup +// InFlightTLSData* pNext; // Points at the next in-flight TLS data +// }; +// +// struct TLSIndex +// { +// int32_t TLSIndexRawIndex; +// int32_t GetIndexOffset() { return TLSIndexRawIndex & 0xFFFFFF; } +// int8_t GetTLSArrayOffset() { return TLSIndexRawIndex >> 24; } +// }; +// +// thread_local ThreadLocalData t_ThreadStatics; +// SArray* g_pNonGCTLSIndexToMethodTable; +// int g_maxNonGCTlsSize; +// SArray* g_pGCTLSIndexToMethodTable; +// int g_maxGCTlsSlots; +// // Access pattern for a TLS static // 0. Get the TLS index somehow -// 1. Get TLS pointer to OS managed TLS block for the current thread ie. pThreadStatics = &t_ThreadStatics -// 2. Determine the TLSIndexType of the TLS index. Currently the only TLS access type in use is TLSIndexType::Standard, but over the .NET 9 period we expect to add a couple more -// If TLSIndexType == TLSIndexType::Standard -// - Compare pThreadStatics->cTLSData against the index offset of the TLSIndex. -// - If in range, multiply the index offset by the size of a a pointer and add it to pThreadStatics->pTLSArrayData, then dereference -// - If not found -// - Slow path look in the pThreadStatics->pInFlightData, if found there, return -// - If not found there, trigger allocation behavior to grow TLS data, etc. +// 1. Get TLS pointer to OS managed TLS block for the current thread ie. pThreadLocalData = &t_ThreadStatics +// 2. Get the TLSArray for the TLS index (pTLSArray = ((uint8_t*)pThreadLocalData) + index.GetTLSArrayOffset()) +// 3. Read 1 integer value (cTLSData=pThreadLocalData->cTLSData) +// 4. Compare cTLSData against the index we're looking up (if (cTLSData < index.GetIndexOffset())) +// 5. If the index is not within range, jump to step 10. +// 6. Read 1 pointer value from TLS block (pTLSArrayData=pThreadLocalData->pTLSArrayData) +// 7. Read 1 pointer from within the TLS Array. (pTLSBaseAddress = *(intptr_t*)(((uint8_t*)pTLSArrayData) + index.GetIndexOffset()); +// 8. If pointer is NULL jump to step 10 (if pTLSBaseAddress == NULL) +// 9. Return pTLSBaseAddress +// 10. Tail-call a helper (return GetThreadLocalStaticBase(index)) +// +// The Runtime shall define a couple of well known TLS indices. These are used for the most common +// TLS statics, and are used to avoid the overhead of checking for the index being in range, and +// the class constructor for having run, so that we can skip steps 3, 4, 5, and 8. It shall do this +// by allocating the associated memory before permitting any code to run on the thread. // -// Normal thread-local statics lifetime management -// ------------------------------------- -// - Each entry in the TLS table which is not collectible shall be reported to the GC during promotion and -// relocation. There are no GCHandle or other structures which keep TLS data alive +// Psuedocode for +// ref byte GetThreadLocalStaticBase(uint index) +// { +// Do the access pattern above, but if the TLS array is too small, allocate a new one, and if the base pointer is NULL, call the class constructor for the static. +// if After all that the base pointer is still NULL, walk the InFlightTLSData chain to see if it exists in there. +// If the InFlightTLSData chain has a value +// check to see if the class constructor has run. If it has completed, update the base pointer in the TLS array, and delete the InFlightTLSData entry. +// return the found value +// ELSE +// allocate a new InFlightTLSData entry, and return the address of the pTLSData field. +// } // -// Collectible thread-local statics lifetime management -// ------------------------------------- -// Lifetime management is substantially complicated due the issue that it is possible for either a thread or a -// collectible type to be collected first. Thus the collection algorithm is as follows. -// - The system shall maintain a global mapping of TLS indices to MethodTable structures -// - When a native LoaderAllocator is being cleaned up, before the WeakTrackResurrection GCHandle that -// points at the the managed LoaderAllocator object is destroyed, the mapping from TLS indices to -// collectible LoaderAllocator structures shall be cleared of all relevant entries (and the current -// GC index shall be stored in the TLS to MethodTable mapping) -// - When a GC promotion or relocation scan occurs, for every TLS index which was freed to point at a GC -// index the relevant entry in the TLS table shall be set to NULL in preparation for that entry in the -// table being reused in the future. In addition, if the TLS index refers to a MethodTable which is in -// a collectible assembly, and the associated LoaderAllocator has been freed, then set the relevant -// entry to NULL. -// - When allocating new entries from the TLS mapping table for new collectible thread local structures, -// do not re-use an entry in the table until at least 2 GCs have occurred. This is to allow every -// thread to have NULL'd out the relevant entry in its thread local table. -// - When allocating new TLS entries for collectible TLS statics on a per-thread basis allocate a -// LOADERHANDLE for each object allocated, and associate it with the TLS index on that thread. -// - When cleaning up a thread, for each collectible thread static which is still allocated, we will have -// a LOADERHANDLE. If the collectible type still has a live managed LoaderAllocator free the -// LOADERHANDLE. -// - In each relocation scan, report all live collectible entries to the GC. -// +// Rationale for basic decisions here +// 1. We want access to TLS statics to be as fast as possible, especially for extremely common +// thread statics like the ones used for async, and memory allocation. +// 2. We want access to TLS statics for shared generic types to be nearly fully inlineable. This +// is why the variation between collectible and non-collectible gc statics access is handled by +// a single byte in the index itself. The intent is that access to statics shall be as simple as +// reading the index from a MethodTable, and then using a very straightforward pattern from there. + + #ifndef __THREADLOCALSTORAGE_H__ #define __THREADLOCALSTORAGE_H__ @@ -60,7 +101,6 @@ struct TLSIndex TLSIndex(uint32_t rawIndex) : TLSIndexRawIndex(rawIndex) { } uint32_t TLSIndexRawIndex; int32_t GetIndexOffset() const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex & 0xFFFFFF; } - TLSIndexType GetIndexType() const { LIMITED_METHOD_DAC_CONTRACT; return (TLSIndexType)(TLSIndexRawIndex >> 24); } bool IsAllocated() const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex != 0xFFFFFFFF;} static TLSIndex Unallocated() { LIMITED_METHOD_DAC_CONTRACT; return TLSIndex(0xFFFFFFFF); } bool operator == (TLSIndex index) const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex == index.TLSIndexRawIndex; } From 662579ba8ad6ba5b87bf847eb438cb28ac520e3d Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 26 Mar 2024 12:35:02 -0700 Subject: [PATCH 47/82] Just bump R2R headers --- src/coreclr/inc/readytorun.h | 6 +++--- src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index 41a4aa251fa74..d591a2f11f3f1 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -19,10 +19,10 @@ // src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h // If you update this, ensure you run `git grep MINIMUM_READYTORUN_MAJOR_VERSION` // and handle pending work. -#define READYTORUN_MAJOR_VERSION 0x0009 -#define READYTORUN_MINOR_VERSION 0x0002 +#define READYTORUN_MAJOR_VERSION 0x000A +#define READYTORUN_MINOR_VERSION 0x0000 -#define MINIMUM_READYTORUN_MAJOR_VERSION 0x009 +#define MINIMUM_READYTORUN_MAJOR_VERSION 0x00A // R2R Version 2.1 adds the InliningInfo section // R2R Version 2.2 adds the ProfileDataInfo section diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index 6fc5d9542e160..8aa1080913076 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -15,8 +15,8 @@ internal struct ReadyToRunHeaderConstants { public const uint Signature = 0x00525452; // 'RTR' - public const ushort CurrentMajorVersion = 9; - public const ushort CurrentMinorVersion = 2; + public const ushort CurrentMajorVersion = 10; + public const ushort CurrentMinorVersion = 0; } #if READYTORUN #pragma warning disable 0169 From 43e4e5229a4eea7f299a45aa71649b69b6e7d016 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 28 Mar 2024 09:54:00 -0700 Subject: [PATCH 48/82] Update SOS_BREAKING_CHANGE_VERSION --- src/coreclr/inc/sospriv.idl | 2 +- src/coreclr/pal/prebuilt/inc/sospriv.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/inc/sospriv.idl b/src/coreclr/inc/sospriv.idl index d669e06c375e1..0ecd77733ecc3 100644 --- a/src/coreclr/inc/sospriv.idl +++ b/src/coreclr/inc/sospriv.idl @@ -444,7 +444,7 @@ interface ISOSDacInterface8 : IUnknown // Increment anytime there is a change in the data structures that SOS depends on like // stress log structs (StressMsg, StressLogChunck, ThreadStressLog, etc), exception // stack traces (StackTraceElement), the PredefinedTlsSlots enums, etc. -cpp_quote("#define SOS_BREAKING_CHANGE_VERSION 4") +cpp_quote("#define SOS_BREAKING_CHANGE_VERSION 5") [ object, diff --git a/src/coreclr/pal/prebuilt/inc/sospriv.h b/src/coreclr/pal/prebuilt/inc/sospriv.h index 3f41aa56f12af..9106ceb0a86ed 100644 --- a/src/coreclr/pal/prebuilt/inc/sospriv.h +++ b/src/coreclr/pal/prebuilt/inc/sospriv.h @@ -2802,7 +2802,7 @@ EXTERN_C const IID IID_ISOSDacInterface8; /* interface __MIDL_itf_sospriv_0000_0012 */ /* [local] */ -#define SOS_BREAKING_CHANGE_VERSION 4 +#define SOS_BREAKING_CHANGE_VERSION 5 extern RPC_IF_HANDLE __MIDL_itf_sospriv_0000_0012_v0_0_c_ifspec; From 37ee3e915867b4b78b5234a72054a453a073b7a5 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 28 Mar 2024 11:06:18 -0700 Subject: [PATCH 49/82] Move fast get hashcode flags near to each other. --- src/coreclr/vm/methodtable.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 29d9efa7d244a..9e07b076ebfea 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -329,8 +329,8 @@ struct MethodTableAuxiliaryData enum_flag_Initialized = 0x0001, enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode = 0x0002, // Whether we have checked the overridden Equals or GetHashCode + enum_flag_CanCompareBitsOrUseFastGetHashCode = 0x0004, // Is any field type or sub field type overrode Equals or GetHashCode - // enum_unused = 0x0004, enum_flag_HasApproxParent = 0x0010, // enum_unused = 0x0020, enum_flag_IsNotFullyLoaded = 0x0040, @@ -338,7 +338,7 @@ struct MethodTableAuxiliaryData enum_flag_IsInitError = 0x0100, enum_flag_IsStaticDataAllocated = 0x0200, - enum_flag_CanCompareBitsOrUseFastGetHashCode = 0x0400, // Is any field type or sub field type overrode Equals or GetHashCode + // unum_unused = 0x0400, // Is any field type or sub field type overrode Equals or GetHashCode enum_flag_IsTlsIndexAllocated = 0x0800, enum_flag_MayHaveOpenInterfaceInInterfaceMap = 0x1000, // enum_unused = 0x2000, From 07f7d14aaad7d024b07e3fb34921b12c97835219 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 28 Mar 2024 14:22:33 -0700 Subject: [PATCH 50/82] Use a new HandleType for managing interior pointers instead of the previous approach --- src/coreclr/debug/daccess/daccess.cpp | 2 + src/coreclr/debug/daccess/request.cpp | 2 +- src/coreclr/gc/gchandletable.cpp | 2 +- src/coreclr/gc/gcinterface.h | 12 +- src/coreclr/gc/gcscan.cpp | 1 + src/coreclr/gc/objecthandle.cpp | 82 ++++++++++ src/coreclr/gc/objecthandle.h | 1 + src/coreclr/vm/appdomain.hpp | 6 + src/coreclr/vm/ceemain.cpp | 1 - src/coreclr/vm/gcenv.ee.cpp | 13 -- src/coreclr/vm/gchandleutilities.h | 18 +++ src/coreclr/vm/loaderallocator.cpp | 221 ++++++++------------------ src/coreclr/vm/loaderallocator.hpp | 32 +--- 13 files changed, 188 insertions(+), 205 deletions(-) diff --git a/src/coreclr/debug/daccess/daccess.cpp b/src/coreclr/debug/daccess/daccess.cpp index eb5127cdfa4f3..7d27b3b81001a 100644 --- a/src/coreclr/debug/daccess/daccess.cpp +++ b/src/coreclr/debug/daccess/daccess.cpp @@ -7746,6 +7746,8 @@ void CALLBACK DacHandleWalker::EnumCallback(PTR_UNCHECKED_OBJECTREF handle, uint data.Type = param->Type; if (param->Type == HNDTYPE_DEPENDENT) data.Secondary = GetDependentHandleSecondary(handle.GetAddr()).GetAddr(); + else if (param->Type == HNDTYPE_WEAK_INTERIOR_POINTER) + data.Secondary = TO_CDADDR(HndGetHandleExtraInfo(handle.GetAddr())); else data.Secondary = 0; data.AppDomain = param->AppDomain; diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index b6d5c31a8b5e1..7e31f0f4ca01d 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -3201,7 +3201,7 @@ ClrDataAccess::GetThreadLocalModuleData(CLRDATA_ADDRESS thread, unsigned int ind HRESULT ClrDataAccess::GetHandleEnum(ISOSHandleEnum **ppHandleEnum) { unsigned int types[] = {HNDTYPE_WEAK_SHORT, HNDTYPE_WEAK_LONG, HNDTYPE_STRONG, HNDTYPE_PINNED, HNDTYPE_DEPENDENT, - HNDTYPE_SIZEDREF, + HNDTYPE_SIZEDREF, HNDTYPE_WEAK_INTERIOR_POINTER, #if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL) HNDTYPE_REFCOUNTED, #endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL diff --git a/src/coreclr/gc/gchandletable.cpp b/src/coreclr/gc/gchandletable.cpp index a4b3ab8629a0d..ef0bbf8c93aef 100644 --- a/src/coreclr/gc/gchandletable.cpp +++ b/src/coreclr/gc/gchandletable.cpp @@ -213,7 +213,7 @@ Object* GCHandleManager::InterlockedCompareExchangeObjectInHandle(OBJECTHANDLE h HandleType GCHandleManager::HandleFetchType(OBJECTHANDLE handle) { uint32_t type = ::HandleFetchType(handle); - assert(type >= HNDTYPE_WEAK_SHORT && type <= HNDTYPE_WEAK_NATIVE_COM); + assert(type >= HNDTYPE_WEAK_SHORT && type <= HNDTYPE_WEAK_INTERIOR_POINTER); return static_cast(type); } diff --git a/src/coreclr/gc/gcinterface.h b/src/coreclr/gc/gcinterface.h index 75b84684bb9ed..f15ee691ebef0 100644 --- a/src/coreclr/gc/gcinterface.h +++ b/src/coreclr/gc/gcinterface.h @@ -502,7 +502,17 @@ typedef enum * but we are keeping it here for backward compatibility purposes" * */ - HNDTYPE_WEAK_NATIVE_COM = 9 + HNDTYPE_WEAK_NATIVE_COM = 9, + + /* + * INTERIOR POINTER HANDLES + * + * Interior pointer handles allow the vm to request that the GC keep an interior pointer to + * a given object updated to keep pointing at the same location within an object. These handles + * have an extra pointer which points at an interior pointer into the first object. + * + */ + HNDTYPE_WEAK_INTERIOR_POINTER = 10 } HandleType; typedef enum diff --git a/src/coreclr/gc/gcscan.cpp b/src/coreclr/gc/gcscan.cpp index 92ed7f3a86870..6f200810fed59 100644 --- a/src/coreclr/gc/gcscan.cpp +++ b/src/coreclr/gc/gcscan.cpp @@ -171,6 +171,7 @@ void GCScan::GcScanHandles (promote_func* fn, int condemned, int max_gen, Ref_UpdatePointers(condemned, max_gen, sc, fn); Ref_UpdatePinnedPointers(condemned, max_gen, sc, fn); Ref_ScanDependentHandlesForRelocation(condemned, max_gen, sc, fn); + Ref_ScanWeakInteriorPointersForRelocation(condemned, max_gen, sc, fn); } } diff --git a/src/coreclr/gc/objecthandle.cpp b/src/coreclr/gc/objecthandle.cpp index 1c404c6c23b15..5374229b5b067 100644 --- a/src/coreclr/gc/objecthandle.cpp +++ b/src/coreclr/gc/objecthandle.cpp @@ -145,6 +145,47 @@ void CALLBACK TraceDependentHandle(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pEx } } +void CALLBACK UpdateWeakInteriorHandle(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE(pExtraInfo); + + Object **pPrimaryRef = (Object **)pObjRef; + uintptr_t **ppInteriorPtrRef = (uintptr_t **)pExtraInfo; + + LOG((LF_GC, LL_INFO10000, LOG_HANDLE_OBJECT("Querying for new location of ", + pPrimaryRef, "to ", *pPrimaryRef))); + + Object *pOldPrimary = *pPrimaryRef; + + _ASSERTE(lp2); + promote_func* callback = (promote_func*) lp2; + callback(pPrimaryRef, (ScanContext *)lp1, 0); + + Object *pNewPrimary = *pPrimaryRef; + if (pNewPrimary != NULL) + { + uintptr_t pOldInterior = **ppInteriorPtrRef; + uintptr_t delta = ((uintptr_t)pNewPrimary) - ((uintptr_t)pOldPrimary); + uintptr_t pNewInterior = pOldInterior + delta; + **ppInteriorPtrRef = pNewInterior; +#ifdef _DEBUG + if (pOldPrimary != *pPrimaryRef) + LOG((LF_GC, LL_INFO10000, "Updating " FMT_HANDLE "from" FMT_ADDR "to " FMT_OBJECT "\n", + DBG_ADDR(pPrimaryRef), DBG_ADDR(pOldPrimary), DBG_ADDR(*pPrimaryRef))); + else + LOG((LF_GC, LL_INFO10000, "Updating " FMT_HANDLE "- " FMT_OBJECT "did not move\n", + DBG_ADDR(pPrimaryRef), DBG_ADDR(*pPrimaryRef))); + if (pOldInterior != pNewInterior) + LOG((LF_GC, LL_INFO10000, "Updating " FMT_HANDLE "from" FMT_ADDR "to " FMT_OBJECT "\n", + DBG_ADDR(*ppInteriorPtrRef), DBG_ADDR(pOldInterior), DBG_ADDR(pNewInterior))); + else + LOG((LF_GC, LL_INFO10000, "Updating " FMT_HANDLE "- " FMT_OBJECT "did not move\n", + DBG_ADDR(*ppInteriorPtrRef), DBG_ADDR(pOldInterior))); +#endif + } +} + void CALLBACK UpdateDependentHandle(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2) { LIMITED_METHOD_CONTRACT; @@ -427,6 +468,7 @@ void CALLBACK ScanPointerForProfilerAndETW(_UNCHECKED_OBJECTREF *pObjRef, uintpt break; case HNDTYPE_WEAK_SHORT: case HNDTYPE_WEAK_LONG: + case HNDTYPE_WEAK_INTERIOR_POINTER: #ifdef FEATURE_WEAK_NATIVE_COM_HANDLES case HNDTYPE_WEAK_NATIVE_COM: #endif // FEATURE_WEAK_NATIVE_COM_HANDLES @@ -527,6 +569,7 @@ static const uint32_t s_rgTypeFlags[] = HNDF_NORMAL, // HNDTYPE_ASYNCPINNED HNDF_EXTRAINFO, // HNDTYPE_SIZEDREF HNDF_EXTRAINFO, // HNDTYPE_WEAK_NATIVE_COM + HNDF_EXTRAINFO, // HNDTYPE_WEAK_INTERIOR_POINTER }; int getNumberOfSlots() @@ -1170,6 +1213,7 @@ void Ref_CheckReachable(uint32_t condemned, uint32_t maxgen, ScanContext *sc) #ifdef FEATURE_REFCOUNTED_HANDLES HNDTYPE_REFCOUNTED, #endif + HNDTYPE_WEAK_INTERIOR_POINTER }; // check objects pointed to by short weak handles @@ -1339,6 +1383,40 @@ void Ref_ScanDependentHandlesForClearing(uint32_t condemned, uint32_t maxgen, Sc } } +// Perform a scan of weak interior pointers for the purpose of updating handles to track relocated objects. +void Ref_ScanWeakInteriorPointersForRelocation(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn) +{ + LOG((LF_GC, LL_INFO10000, "Relocating moved dependent handles in generation %u\n", condemned)); + uint32_t type = HNDTYPE_WEAK_INTERIOR_POINTER; + uint32_t flags = (sc->concurrent) ? HNDGCF_ASYNC : HNDGCF_NORMAL; + flags |= HNDGCF_EXTRAINFO; + + HandleTableMap *walk = &g_HandleTableMap; + while (walk) + { + for (uint32_t i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; i ++) + { + if (walk->pBuckets[i] != NULL) + { + int uCPUindex = getSlotNumber(sc); + int uCPUlimit = getNumberOfSlots(); + assert(uCPUlimit > 0); + int uCPUstep = getThreadCount(sc); + HHANDLETABLE* pTable = walk->pBuckets[i]->pTable; + for ( ; uCPUindex < uCPUlimit; uCPUindex += uCPUstep) + { + HHANDLETABLE hTable = pTable[uCPUindex]; + if (hTable) + { + HndScanHandlesForGC(hTable, UpdateWeakInteriorHandle, uintptr_t(sc), uintptr_t(fn), &type, 1, condemned, maxgen, flags ); + } + } + } + } + walk = walk->pNext; + } +} + // Perform a scan of dependent handles for the purpose of updating handles to track relocated objects. void Ref_ScanDependentHandlesForRelocation(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn) { @@ -1590,6 +1668,7 @@ void Ref_ScanHandlesForProfilerAndETW(uint32_t maxgen, uintptr_t lp1, handle_sca HNDTYPE_ASYNCPINNED, #endif HNDTYPE_SIZEDREF, + HNDTYPE_WEAK_INTERIOR_POINTER }; uint32_t flags = HNDGCF_NORMAL; @@ -1712,6 +1791,7 @@ void Ref_AgeHandles(uint32_t condemned, uint32_t maxgen, ScanContext* sc) HNDTYPE_ASYNCPINNED, #endif HNDTYPE_SIZEDREF, + HNDTYPE_WEAK_INTERIOR_POINTER }; // perform a multi-type scan that ages the handles @@ -1766,6 +1846,7 @@ void Ref_RejuvenateHandles(uint32_t condemned, uint32_t maxgen, ScanContext* sc) HNDTYPE_ASYNCPINNED, #endif HNDTYPE_SIZEDREF, + HNDTYPE_WEAK_INTERIOR_POINTER }; // reset the ages of these handles @@ -1819,6 +1900,7 @@ void Ref_VerifyHandleTable(uint32_t condemned, uint32_t maxgen, ScanContext* sc) #endif HNDTYPE_SIZEDREF, HNDTYPE_DEPENDENT, + HNDTYPE_WEAK_INTERIOR_POINTER }; // verify these handles diff --git a/src/coreclr/gc/objecthandle.h b/src/coreclr/gc/objecthandle.h index d579e8631d6ca..634510758c322 100644 --- a/src/coreclr/gc/objecthandle.h +++ b/src/coreclr/gc/objecthandle.h @@ -102,6 +102,7 @@ DhContext *Ref_GetDependentHandleContext(ScanContext* sc); bool Ref_ScanDependentHandlesForPromotion(DhContext *pDhContext); void Ref_ScanDependentHandlesForClearing(uint32_t condemned, uint32_t maxgen, ScanContext* sc); void Ref_ScanDependentHandlesForRelocation(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn); +void Ref_ScanWeakInteriorPointersForRelocation(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn); void Ref_ScanSizedRefHandles(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_promote_func* fn); void Ref_CheckReachable (uint32_t uCondemnedGeneration, uint32_t uMaxGeneration, ScanContext* sc); diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp index a4cfed39d465f..d6e17b211dd1d 100644 --- a/src/coreclr/vm/appdomain.hpp +++ b/src/coreclr/vm/appdomain.hpp @@ -577,6 +577,12 @@ class BaseDomain return ::CreatePinningHandle(m_handleStore, object); } + OBJECTHANDLE CreateWeakInteriorHandle(OBJECTREF object, void* pInteriorPointerLocation) + { + WRAPPER_NO_CONTRACT; + return ::CreateWeakInteriorHandle(m_handleStore, object, pInteriorPointerLocation); + } + OBJECTHANDLE CreateSizedRefHandle(OBJECTREF object) { WRAPPER_NO_CONTRACT; diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 46468e096a2c9..6af3377a111ed 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -630,7 +630,6 @@ void EEStartupHelper() Thread::StaticInitialize(); InitializeThreadStaticData(); - InitMoveableGCPointerTracker(); JITInlineTrackingMap::StaticInitialize(); MethodDescBackpatchInfoTracker::StaticInitialize(); diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index 6f04ba0b9dc18..1582b1be1f27d 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -311,16 +311,6 @@ void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen, } } - // Scan the moveable GC Pointer table, which is used to ensure that - // pointers into managed object which are kept alive by collectible - // LoaderAllocators will be updated if their objects are moved. - // We use this for collectible static variable bases. In the future we - // may also choose to use this for RuntimeType instances for collectible - // types. The intent is to be able to use this capability to allow the - // data structures for collectible assemblies to be implemented in the - // same way as the data structures for non-collectible assemblies. - g_pMoveableGCPointerTracker->ScanTable(fn, sc); - if (sc->thread_number == 0 || !GCHeapUtilities::IsServerHeap()) { // This function must be called once per run of calls to ScanThreadStaticRoots @@ -1674,9 +1664,6 @@ void GCToEEInterface::AnalyzeSurvivorsFinished(size_t gcIndex, int condemnedGene { LIMITED_METHOD_CONTRACT; - // This callback is called at least once per run of the GC, so we can reset the work stealing infra in the MoveableGCPointerTracker here - g_pMoveableGCPointerTracker->ResetForNextSetOfTableScans(); - uint64_t elapsed = 0; if (GenAwareMatchingGeneration(condemnedGeneration) && gcGenAnalysisTime > 0) { diff --git a/src/coreclr/vm/gchandleutilities.h b/src/coreclr/vm/gchandleutilities.h index 1f0e7013f22b6..97fa60910236e 100644 --- a/src/coreclr/vm/gchandleutilities.h +++ b/src/coreclr/vm/gchandleutilities.h @@ -147,6 +147,18 @@ inline OBJECTHANDLE CreateDependentHandle(IGCHandleStore* store, OBJECTREF prima return hnd; } +inline OBJECTHANDLE CreateWeakInteriorHandle(IGCHandleStore* store, OBJECTREF primary, void* interiorPointerLocation) +{ + OBJECTHANDLE hnd = store->CreateHandleWithExtraInfo(OBJECTREFToObject(primary), HNDTYPE_WEAK_INTERIOR_POINTER, interiorPointerLocation); + if (!hnd) + { + COMPlusThrowOM(); + } + + DiagHandleCreated(hnd, primary); + return hnd; +} + // Global handle creation convenience functions inline OBJECTHANDLE CreateGlobalHandleCommon(OBJECTREF object, HandleType type) { @@ -321,6 +333,11 @@ inline void DestroyGlobalRefcountedHandle(OBJECTHANDLE handle) DestroyHandleCommon(handle, HNDTYPE_REFCOUNTED); } +inline void DestroyWeakInteriorHandle(OBJECTHANDLE handle) +{ + DestroyHandleCommon(handle, HNDTYPE_WEAK_INTERIOR_POINTER); +} + inline void DestroyTypedHandle(OBJECTHANDLE handle) { DiagHandleDestroyed(handle); @@ -338,6 +355,7 @@ typedef Wrapper, DestroyRefcountedHandle> typedef Holder, DestroyLongWeakHandle> LongWeakHandleHolder; typedef Holder, DestroyGlobalStrongHandle> GlobalStrongHandleHolder; typedef Holder, DestroyGlobalShortWeakHandle> GlobalShortWeakHandleHolder; +typedef Holder, DestroyWeakInteriorHandle> WeakInteriorHandleHolder; typedef Holder, ResetOBJECTHANDLE> ObjectInHandleHolder; class RCOBJECTHANDLEHolder : public RefCountedOHWrapper diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index 29f1f30c84037..1fac07f6a018c 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -1833,9 +1833,9 @@ void AssemblyLoaderAllocator::RegisterHandleForCleanup(OBJECTHANDLE objHandle) { CONTRACTL { - GC_TRIGGERS; + GC_NOTRIGGER; THROWS; - MODE_ANY; + MODE_COOPERATIVE; CAN_TAKE_LOCK; PRECONDITION(CheckPointer(objHandle)); INJECT_FAULT(COMPlusThrowOM();); @@ -1849,6 +1849,25 @@ void AssemblyLoaderAllocator::RegisterHandleForCleanup(OBJECTHANDLE objHandle) m_handleCleanupList.InsertTail(new (pItem) HandleCleanupListItem(objHandle)); } +void AssemblyLoaderAllocator::RegisterHandleForCleanupLocked(OBJECTHANDLE objHandle) +{ + CONTRACTL + { + GC_NOTRIGGER; + THROWS; + MODE_COOPERATIVE; + CAN_TAKE_LOCK; + PRECONDITION(CheckPointer(objHandle)); + INJECT_FAULT(COMPlusThrowOM();); + } + CONTRACTL_END; + + void * pItem = GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(HandleCleanupListItem))); + // InsertTail must be protected by a lock. Just use the loader allocator lock + _ASSERTE(m_crstLoaderAllocator.OwnedByCurrentThread()); + m_handleCleanupList.InsertTail(new (pItem) HandleCleanupListItem(objHandle)); +} + void AssemblyLoaderAllocator::UnregisterHandleFromCleanup(OBJECTHANDLE objHandle) { CONTRACTL @@ -1890,7 +1909,6 @@ void AssemblyLoaderAllocator::CleanupHandles() if (m_hLoaderAllocatorObjectHandle != NULL) { GCX_COOP(); - g_pMoveableGCPointerTracker->RemoveEntriesAssociatedWithGCHandle(m_hLoaderAllocatorObjectHandle); FreeTLSIndicesForLoaderAllocator(this); } @@ -2226,10 +2244,28 @@ void LoaderAllocator::AllocateBytesForStaticVariables(DynamicStaticsInfo* pStati GCPROTECT_BEGIN(ptrArray); // Keep this allocation alive till the LoaderAllocator is collected AllocateHandle(ptrArray); + CrstHolder cs(&m_crstLoaderAllocator); { - CrstHolder crst(g_pMoveableGCPointerTracker->GetCrst()); - pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */false, (TADDR)ptrArray->GetDataPtr()); - g_pMoveableGCPointerTracker->AddPointer(m_hLoaderAllocatorObjectHandle, OBJECTREFToObject(ptrArray), &pStaticsInfo->m_pNonGCStatics); + if (pStaticsInfo->GetNonGCStaticsPointer() == NULL) + { + GCX_FORBID(); + // Allocating a weak interior handle is a tricky thing. + // 1. If there are multiple weak interior handles that point at a given interior pointer location, there will be heap corruption + // 2. If the interior handle is created, but not registered for cleanup, it is a memory leak + // 3. Since the weak interior handle doesn't actually keep the object alive, it needs to be kept alive by some other means + // + // We work around these details by the following means + // 1. We use a LOADERHANDLE to keep the object alive until the LoaderAllocator is freed. + // 2. We hold the crstLoaderAllocatorLock, and double check to wnsure that the data is ready to be filled in + // 3. We create the weak interior handle, and register it for cleanup (which can fail with an OOM) before updating the statics data to have the pointer + // 4. Registration for cleanup cannot trigger a GC + // 5. We then unconditionally set the statics pointer. + WeakInteriorHandleHolder weakHandleHolder = GetAppDomain()->CreateWeakInteriorHandle(ptrArray, &pStaticsInfo->m_pNonGCStatics); + RegisterHandleForCleanupLocked(weakHandleHolder.GetValue()); + weakHandleHolder.SuppressRelease(); + bool didUpdateStaticsPointer = pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */false, (TADDR)ptrArray->GetDataPtr()); + _ASSERTE(didUpdateStaticsPointer); + } } GCPROTECT_END(); } @@ -2288,11 +2324,28 @@ void LoaderAllocator::AllocateGCHandlesBytesForStaticVariables(DynamicStaticsInf } // Keep this allocation alive till the LoaderAllocator is collected AllocateHandle(ptrArray); - + CrstHolder cs(&m_crstLoaderAllocator); { - CrstHolder crst(g_pMoveableGCPointerTracker->GetCrst()); - pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */true, (TADDR)ptrArray->GetDataPtr()); - g_pMoveableGCPointerTracker->AddPointer(m_hLoaderAllocatorObjectHandle, OBJECTREFToObject(ptrArray), &pStaticsInfo->m_pGCStatics); + if (pStaticsInfo->GetGCStaticsPointer() == NULL) + { + GCX_FORBID(); + // Allocating a weak interior handle is a tricky thing. + // 1. If there are multiple weak interior handles that point at a given interior pointer location, there will be heap corruption + // 2. If the interior handle is created, but not registered for cleanup, it is a memory leak + // 3. Since the weak interior handle doesn't actually keep the object alive, it needs to be kept alive by some other means + // + // We work around these details by the following means + // 1. We use a LOADERHANDLE to keep the object alive until the LoaderAllocator is freed. + // 2. We hold the crstLoaderAllocatorLock, and double check to wnsure that the data is ready to be filled in + // 3. We create the weak interior handle, and register it for cleanup (which can fail with an OOM) before updating the statics data to have the pointer + // 4. Registration for cleanup cannot trigger a GC + // 5. We then unconditionally set the statics pointer. + WeakInteriorHandleHolder weakHandleHolder = GetAppDomain()->CreateWeakInteriorHandle(ptrArray, &pStaticsInfo->m_pGCStatics); + RegisterHandleForCleanupLocked(weakHandleHolder.GetValue()); + weakHandleHolder.SuppressRelease(); + bool didUpdateStaticsPointer = pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */true, (TADDR)ptrArray->GetDataPtr()); + _ASSERTE(didUpdateStaticsPointer); + } } GCPROTECT_END(); } @@ -2303,152 +2356,4 @@ void LoaderAllocator::AllocateGCHandlesBytesForStaticVariables(DynamicStaticsInf } } #endif // !DACCESS_COMPILE -#ifndef DACCESS_COMPILE -CollectibleMoveableGCPointerTracker *g_pMoveableGCPointerTracker; -void InitMoveableGCPointerTracker() -{ - STANDARD_VM_CONTRACT; - g_pMoveableGCPointerTracker = new CollectibleMoveableGCPointerTracker(); -} - -CollectibleMoveableGCPointerTracker::CollectibleMoveableGCPointerTracker() : - m_pointers(nullptr), - m_numPointers(0), - m_maxPointers(0), - m_nextTableScanChunk(-1), - m_Crst(CrstLeafLock, (CrstFlags)(CRST_UNSAFE_COOPGC | CRST_REENTRANCY)) -{ -} - -void CollectibleMoveableGCPointerTracker::AddPointer(OBJECTHANDLE gcHandleDescribingLifetime, Object *pObject, uintptr_t *pTrackedInteriorPointer) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_COOPERATIVE; // Since the GC reads the array without taking locks, we need to be in cooperative mode - INJECT_FAULT(COMPlusThrowOM()); - } - CONTRACTL_END; - - CrstHolder ch(&m_Crst); - MoveableGCPointer structToAdd; - structToAdd.LifetimeTrackingHandle = gcHandleDescribingLifetime; - structToAdd.PointerValue = pObject; - structToAdd.TrackedInteriorPointer = pTrackedInteriorPointer; - if (m_numPointers >= m_maxPointers) - { - uintptr_t newMaxPointers = max(16, m_maxPointers * 2); - MoveableGCPointer *newPointers = new MoveableGCPointer[newMaxPointers]; - if (m_pointers != nullptr) - { - memcpy(newPointers, m_pointers, m_maxPointers * sizeof(MoveableGCPointer)); - delete[] m_pointers; - } - m_pointers = newPointers; - m_maxPointers = newMaxPointers; - } - - m_pointers[m_numPointers] = structToAdd; - m_numPointers++; -} - -void CollectibleMoveableGCPointerTracker::ScanTable(promote_func* fn, ScanContext* sc) -{ - // This executes during the GC, so we don't need to take a lock, as the table cannot be modified, as all modifications involve cooperative mode - WRAPPER_NO_CONTRACT; - - - if (sc->promotion) - { - // We must not promote anything, as the lifetime of these pointers is driven by the lifetime of the LifetimeTrackingHandle - return; - } - - if (m_numPointers == 0) - { - // There isn't any work to do. - return; - } - - // On the server heap, where multiple threads may be scanning the table, we can to divide the work up. Use a simple work-stealing approach - // - // Notably, in GCToEEInterface::AnalyzeSurvivorsFinished which is called once for each time that there is a set of relocations, we reset the current - // chunk index to -1. Then in this function, we operate on chunks from 0 to maxChunkCount. We use InterlockedIncrement to get the next chunk index, and - // when a chunk is done, we increment the next chunk index. This is done is a loop, so if 1 (or more) threads of the GC is running ahead of the others, - // it will do more chunks than another thread which is running behind, thus balancing the work. - uintptr_t maxChunkCount = GCHeapUtilities::IsServerHeap() ? sc->thread_count : 1; - LONG chunkIndex = InterlockedIncrement(&m_nextTableScanChunk); - while ((uintptr_t)chunkIndex < maxChunkCount) - { - uintptr_t pointerRangeStart = 0; - uintptr_t pointerRangeEnd = m_numPointers; - - uintptr_t pointersPerThread = (m_numPointers / maxChunkCount) + 1; - pointerRangeStart = pointersPerThread * chunkIndex; - pointerRangeEnd = pointerRangeStart + pointersPerThread; - if (pointerRangeEnd > m_numPointers) - { - pointerRangeEnd = m_numPointers; - } - - for (uintptr_t i = pointerRangeStart; i < pointerRangeEnd; ++i) - { - if (!ObjectHandleIsNull(m_pointers[i].LifetimeTrackingHandle)) - { - Object *pObjectOld = m_pointers[i].PointerValue; - fn(&m_pointers[i].PointerValue, sc, 0); - Object *pObject = m_pointers[i].PointerValue; - if (pObject != pObjectOld) - { - uintptr_t delta = (uintptr_t)pObject - (uintptr_t)pObjectOld; - // Use VolatileLost/Store to handle the case where the GC is running on one thread and another thread updates the lowest bit - // on the pointer. This can happen when the GC is running on one thread, and another thread is running the JIT, and happens to trigger - // the pre-init logic for a collectible type. The jit will update the value using InterlockedCompareExchange, and this code will do the update - // using VolatileStoreWithoutBarrier. This may result in the flag bit update from SetClassInited to be lost, but that's ok, as the flag bit will - // being lost won't actually change program behavior, and the lost bit is an extremely rare occurence. - uintptr_t *trackedInteriorPointer = m_pointers[i].TrackedInteriorPointer; - uintptr_t currentInteriorPointerVale = VolatileLoadWithoutBarrier(trackedInteriorPointer); - uintptr_t newInteriorPointerValue = currentInteriorPointerVale + delta; - VolatileStoreWithoutBarrier(trackedInteriorPointer, newInteriorPointerValue); - } - } - } - - chunkIndex = InterlockedIncrement(&m_nextTableScanChunk); - } -} - -void CollectibleMoveableGCPointerTracker::RemoveEntriesAssociatedWithGCHandle(OBJECTHANDLE gcHandleDescribingLifetime) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; // Since the GC reads the array without taking locks, we need to be in cooperative mode - } - CONTRACTL_END; - - CrstHolder ch(&m_Crst); - if (m_numPointers == 0) - return; - - uintptr_t index = m_numPointers - 1; - - while (true) - { - MoveableGCPointer ¤tElement = m_pointers[index]; - if (currentElement.LifetimeTrackingHandle == gcHandleDescribingLifetime) - { - currentElement = m_pointers[m_numPointers - 1]; - --m_numPointers; - } - - if (index == 0) - break; - index--; - } -} - -#endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/loaderallocator.hpp b/src/coreclr/vm/loaderallocator.hpp index c1b5190cc9a9a..857f32071e8d8 100644 --- a/src/coreclr/vm/loaderallocator.hpp +++ b/src/coreclr/vm/loaderallocator.hpp @@ -650,6 +650,7 @@ class LoaderAllocator // The default implementation is a no-op. Only collectible loader allocators implement this method. virtual void RegisterHandleForCleanup(OBJECTHANDLE /* objHandle */) { } + virtual void RegisterHandleForCleanupLocked(OBJECTHANDLE /* objHandle */) { } virtual void UnregisterHandleFromCleanup(OBJECTHANDLE /* objHandle */) { } virtual void CleanupHandles() { } @@ -907,6 +908,7 @@ class AssemblyLoaderAllocator : public LoaderAllocator #if !defined(DACCESS_COMPILE) virtual void RegisterHandleForCleanup(OBJECTHANDLE objHandle); + virtual void RegisterHandleForCleanupLocked(OBJECTHANDLE objHandle); virtual void UnregisterHandleFromCleanup(OBJECTHANDLE objHandle); virtual void CleanupHandles(); CustomAssemblyBinder* GetBinder() @@ -952,36 +954,6 @@ class AssemblyLoaderAllocator : public LoaderAllocator typedef VPTR(AssemblyLoaderAllocator) PTR_AssemblyLoaderAllocator; -#ifndef DACCESS_COMPILE -class CollectibleMoveableGCPointerTracker -{ -private: - struct MoveableGCPointer - { - OBJECTHANDLE LifetimeTrackingHandle; - Object* PointerValue; - uintptr_t *TrackedInteriorPointer; - }; - - MoveableGCPointer *m_pointers; - uintptr_t m_numPointers; // Number of elements in use in m_pointers - uintptr_t m_maxPointers; // Allocated size of m_pointers; - LONG m_nextTableScanChunk; - Crst m_Crst; - -public: - CollectibleMoveableGCPointerTracker(); - CrstBase* GetCrst() { LIMITED_METHOD_CONTRACT; return &m_Crst; } - void AddPointer(OBJECTHANDLE gcHandleDescribingLifetime, Object *pObject, uintptr_t *pTrackedInteriorPointer); - void ScanTable(promote_func* fn, ScanContext* sc); - void RemoveEntriesAssociatedWithGCHandle(OBJECTHANDLE gcHandleDescribingLifetime); - void ResetForNextSetOfTableScans() { LIMITED_METHOD_CONTRACT; VolatileStore(&m_nextTableScanChunk, (LONG)-1); } -}; - -extern CollectibleMoveableGCPointerTracker *g_pMoveableGCPointerTracker; -void InitMoveableGCPointerTracker(); -#endif - #include "loaderallocator.inl" #endif // __LoaderAllocator_h__ From e8273e6cc2b615713fac8581c5882c5811aad4a8 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 29 Mar 2024 07:34:28 -0700 Subject: [PATCH 51/82] Fix build break --- src/coreclr/vm/methodtable.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 42422d92557b0..e9da695986ce0 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -411,8 +411,6 @@ void MethodTable::SetIsTrackedReferenceWithFinalizer() SetFlag(enum_flag_IsTrackedReferenceWithFinalizer); } -#endif // !DACCESS_COMPILE - BOOL MethodTable::IsTrackedReferenceWithFinalizer() { LIMITED_METHOD_DAC_CONTRACT; From 4b27a9579bb72eedfefd618f8a385e5d3274a241 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 17 Apr 2024 17:01:00 -0700 Subject: [PATCH 52/82] Rework Thread static changes to make non-collectible tls data have a very cheap ScanThreadStatics path (Just a single object array marked) --- src/coreclr/inc/dacvars.h | 2 - src/coreclr/vm/ceemain.cpp | 4 +- src/coreclr/vm/gcenv.ee.cpp | 4 +- src/coreclr/vm/jithelpers.cpp | 24 +- src/coreclr/vm/jitinterface.cpp | 4 +- src/coreclr/vm/methodtable.inl | 12 +- src/coreclr/vm/spinlock.cpp | 2 + src/coreclr/vm/spinlock.h | 6 +- src/coreclr/vm/threads.cpp | 5 +- src/coreclr/vm/threads.h | 8 +- src/coreclr/vm/threadstatics.cpp | 385 +++++++++++++++++++++---------- src/coreclr/vm/threadstatics.h | 46 ++-- 12 files changed, 334 insertions(+), 168 deletions(-) diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h index 0511a65f872a5..b68e7a9cfe76c 100644 --- a/src/coreclr/inc/dacvars.h +++ b/src/coreclr/inc/dacvars.h @@ -102,8 +102,6 @@ DEFINE_DACVAR(PTR_ThreadStore, ThreadStore__s_pThreadStore, ThreadStore::s_pThre DEFINE_DACVAR(PTR_Thread, dac__g_pFinalizerThread, ::g_pFinalizerThread) DEFINE_DACVAR(PTR_Thread, dac__g_pSuspensionThread, ::g_pSuspensionThread) -DEFINE_DACVAR(DPTR(LookupMap), dac__g_pThreadStaticTypeIndices, ::g_pThreadStaticTypeIndices) - DEFINE_DACVAR(DWORD, dac__g_heap_type, g_heap_type) DEFINE_DACVAR(PTR_GcDacVars, dac__g_gcDacGlobals, g_gcDacGlobals) diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 2384686ca2ccc..c6c44bc828aad 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -629,7 +629,6 @@ void EEStartupHelper() IfFailGo(ExecutableAllocator::StaticInitialize(FatalErrorHandler)); Thread::StaticInitialize(); - InitializeThreadStaticData(); JITInlineTrackingMap::StaticInitialize(); MethodDescBackpatchInfoTracker::StaticInitialize(); @@ -930,6 +929,8 @@ void EEStartupHelper() SystemDomain::System()->DefaultDomain()->SetupSharedStatics(); + InitializeThreadStaticData(); + #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS // retrieve configured max size for the mini-metadata buffer (defaults to 64KB) g_MiniMetaDataBuffMaxSize = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MiniMdBufferCapacity); @@ -1720,6 +1721,7 @@ struct TlsDestructionMonitor thread->m_pFrame = FRAME_TOP; GCX_COOP_NO_DTOR_END(); } + FreeThreadStaticData(&t_ThreadStatics, thread); thread->DetachThread(TRUE); } diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index 1582b1be1f27d..130126a710568 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -302,7 +302,7 @@ void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen, #endif // FEATURE_EVENT_TRACE ScanStackRoots(pThread, fn, sc); ScanTailCallArgBufferRoots(pThread, fn, sc); - ScanThreadStaticRoots(pThread->GetThreadLocalDataPtr(), /*forGC*/ true, fn, sc); + ScanThreadStaticRoots(pThread, /*forGC*/ true, fn, sc); #ifdef FEATURE_EVENT_TRACE sc->dwEtwRootKind = kEtwGCRootKindOther; #endif // FEATURE_EVENT_TRACE @@ -627,7 +627,7 @@ void GcScanRootsForProfilerAndETW(promote_func* fn, int condemned, int max_gen, #endif // FEATURE_EVENT_TRACE ScanStackRoots(pThread, fn, sc); ScanTailCallArgBufferRoots(pThread, fn, sc); - ScanThreadStaticRoots(pThread->GetThreadLocalDataPtr(), /*forGC*/ false, fn, sc); + ScanThreadStaticRoots(pThread, /*forGC*/ false, fn, sc); #ifdef FEATURE_EVENT_TRACE sc->dwEtwRootKind = kEtwGCRootKindOther; #endif // FEATURE_EVENT_TRACE diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 4f27b4d092bf5..1c64bed2ab0e1 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1398,7 +1398,7 @@ HCIMPLEND // the most optimal TLS access pattern for the platform when inlining the // GetThreadLocalStaticBaseIfExistsAndInitialized function #ifdef _MSC_VER -__declspec(thread) ThreadLocalData t_ThreadStatics; +__declspec(selectany) __declspec(thread) ThreadLocalData t_ThreadStatics; #else __thread ThreadLocalData t_ThreadStatics; #endif // _MSC_VER @@ -1410,14 +1410,26 @@ FORCEINLINE void* GetThreadLocalStaticBaseIfExistsAndInitialized(TLSIndex index) LIMITED_METHOD_CONTRACT; TADDR pTLSBaseAddress = NULL; - int32_t cTLSData = t_ThreadStatics.cTLSData; - if (cTLSData <= index.GetIndexOffset()) + if (index.GetTLSIndexType() == TLSIndexType::NonCollectible) { - return NULL; + PTRARRAYREF tlsArray = (PTRARRAYREF)UNCHECKED_OBJECTREF_TO_OBJECTREF(t_ThreadStatics.pNonCollectibleTlsReferenceData); + if (t_ThreadStatics.cNonCollectibleTlsData <= index.GetIndexOffset()) + { + return NULL; + } + pTLSBaseAddress = (TADDR)OBJECTREFToObject(tlsArray->GetAt(index.GetIndexOffset() - NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY)); } + else + { + int32_t cTLSData = t_ThreadStatics.cTLSData; + if (cTLSData <= index.GetIndexOffset()) + { + return NULL; + } - TADDR pTLSArrayData = t_ThreadStatics.pTLSArrayData; - pTLSBaseAddress = *reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetIndexOffset()); + TADDR pTLSArrayData = t_ThreadStatics.pTLSArrayData; + pTLSBaseAddress = *reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetIndexOffset()); + } return reinterpret_cast(pTLSBaseAddress); } diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 586dce17d3328..9394e1e4ca283 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -1369,11 +1369,11 @@ uint32_t CEEInfo::getThreadLocalFieldInfo (CORINFO_FIELD_HANDLE field, bool isG if (isGCType) { - typeIndex = MethodTableAuxiliaryData::GetThreadStaticsInfo(pMT->GetAuxiliaryData())->GCTlsIndex.TLSIndexRawIndex; + typeIndex = MethodTableAuxiliaryData::GetThreadStaticsInfo(pMT->GetAuxiliaryData())->GCTlsIndex.GetIndexOffset(); } else { - typeIndex = MethodTableAuxiliaryData::GetThreadStaticsInfo(pMT->GetAuxiliaryData())->NonGCTlsIndex.TLSIndexRawIndex; + typeIndex = MethodTableAuxiliaryData::GetThreadStaticsInfo(pMT->GetAuxiliaryData())->NonGCTlsIndex.GetIndexOffset(); } assert(typeIndex != TypeIDProvider::INVALID_TYPE_ID); diff --git a/src/coreclr/vm/methodtable.inl b/src/coreclr/vm/methodtable.inl index 8f551be341eb2..81e7ac1805d13 100644 --- a/src/coreclr/vm/methodtable.inl +++ b/src/coreclr/vm/methodtable.inl @@ -1150,11 +1150,7 @@ inline PTR_BYTE MethodTable::GetNonGCThreadStaticsBasePointer(PTR_Thread pThread if (!tlsIndex.IsAllocated()) return NULL; - ThreadLocalData* pThreadLocalData = pThread->GetThreadLocalDataPtr(); - if (pThreadLocalData == NULL) - return NULL; - - return (PTR_BYTE)GetThreadLocalStaticBaseNoCreate(pThreadLocalData, tlsIndex); + return (PTR_BYTE)GetThreadLocalStaticBaseNoCreate(pThread, tlsIndex); } //========================================================================================== @@ -1166,11 +1162,7 @@ inline PTR_BYTE MethodTable::GetGCThreadStaticsBasePointer(PTR_Thread pThread) if (!tlsIndex.IsAllocated()) return NULL; - ThreadLocalData* pThreadLocalData = pThread->GetThreadLocalDataPtr(); - if (pThreadLocalData == NULL) - return NULL; - - return (PTR_BYTE)GetThreadLocalStaticBaseNoCreate(pThreadLocalData, tlsIndex); + return (PTR_BYTE)GetThreadLocalStaticBaseNoCreate(pThread, tlsIndex); } //========================================================================================== diff --git a/src/coreclr/vm/spinlock.cpp b/src/coreclr/vm/spinlock.cpp index e750ebab5d527..40b00ed5818f1 100644 --- a/src/coreclr/vm/spinlock.cpp +++ b/src/coreclr/vm/spinlock.cpp @@ -85,6 +85,7 @@ void SpinLock::Init(LOCK_TYPE type, bool RequireCoopGC) #endif } +#ifndef DACCESS_COMPILE #ifdef _DEBUG BOOL SpinLock::OwnedByCurrentThread() { @@ -398,5 +399,6 @@ void SpinLockProfiler::DumpStatics() } #endif // _DEBUG +#endif // !DACCESS_COMPILE // End of file: spinlock.cpp diff --git a/src/coreclr/vm/spinlock.h b/src/coreclr/vm/spinlock.h index 0ee41c7b7ef74..b38624d70f3fe 100644 --- a/src/coreclr/vm/spinlock.h +++ b/src/coreclr/vm/spinlock.h @@ -205,6 +205,7 @@ class SpinLock static void AcquireLock(SpinLock *s); static void ReleaseLock(SpinLock *s); +#ifndef DACCESS_COMPILE class Holder { SpinLock * m_pSpinLock; @@ -225,9 +226,10 @@ class SpinLock m_pSpinLock->FreeLock(); } }; +#endif // !DACCESS_COMPILE }; - +#ifndef DACCESS_COMPILE typedef SpinLock::Holder SpinLockHolder; #define TAKE_SPINLOCK_AND_DONOT_TRIGGER_GC(lock) \ SpinLockHolder __spinLockHolder(lock);\ @@ -245,6 +247,8 @@ typedef SpinLock::Holder SpinLockHolder; SpinLock::ReleaseLock(lock); \ } \ +#endif // !DACCESS_COMPILE + __inline BOOL IsOwnerOfSpinLock (LPVOID lock) { WRAPPER_NO_CONTRACT; diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 1b36d229827bb..5cb4a600da79c 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -1611,6 +1611,8 @@ Thread::Thread() m_isInForbidSuspendForDebuggerRegion = false; m_hasPendingActivation = false; + m_ThreadLocalDataPtr = NULL; + #ifdef _DEBUG memset(dangerousObjRefs, 0, sizeof(dangerousObjRefs)); #endif // _DEBUG @@ -7718,7 +7720,8 @@ void Thread::DeleteThreadStaticData() } CONTRACTL_END; - FreeThreadStaticData(&m_ThreadLocalDataThreadObjectCopy); + FreeLoaderAllocatorHandlesForTLSData(this); + FreeThreadStaticData(m_ThreadLocalDataPtr, this); } OBJECTREF Thread::GetCulture(BOOL bUICulture) diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 2432edb018a1a..389ab0130af64 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -3360,11 +3360,13 @@ class Thread } #endif //DACCESS_COMPILE - ThreadLocalData m_ThreadLocalDataThreadObjectCopy; + PTR_ThreadLocalData m_ThreadLocalDataPtr; + int32_t cLoaderHandles; + PTR_LOADERHANDLE pLoaderHandles; SpinLock m_TlsSpinLock; - ThreadLocalData* GetThreadLocalDataPtr() { LIMITED_METHOD_DAC_CONTRACT; return &m_ThreadLocalDataThreadObjectCopy; } + PTR_ThreadLocalData GetThreadLocalDataPtr() { LIMITED_METHOD_DAC_CONTRACT; return m_ThreadLocalDataPtr; } -private: +public: // Called during Thread death to clean up all structures // associated with thread statics diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 34dfeafed1ec9..ddd4ad5e7e364 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -22,22 +22,39 @@ struct ThreadLocalLoaderAllocator typedef DPTR(ThreadLocalLoaderAllocator) PTR_ThreadLocalLoaderAllocator; // This can be used for out of thread access to TLS data. -PTR_VOID GetThreadLocalStaticBaseNoCreate(ThreadLocalData* pThreadLocalData, TLSIndex index) +PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread* pThread, TLSIndex index) { LIMITED_METHOD_CONTRACT; #ifndef DACCESS_COMPILE // Since this api can be used from a different thread, we need a lock to keep it all safe - SpinLockHolder spinLock(&pThreadLocalData->pThread->m_TlsSpinLock); + SpinLockHolder spinLock(&pThread->m_TlsSpinLock); #endif + + ThreadLocalData* pThreadLocalData = pThread->GetThreadLocalDataPtr(); + if (pThreadLocalData == NULL) + return NULL; + TADDR pTLSBaseAddress = NULL; - int32_t cTLSData = pThreadLocalData->cTLSData; - if (cTLSData <= index.GetIndexOffset()) + if (index.GetTLSIndexType() == TLSIndexType::NonCollectible) { - return NULL; + PTRARRAYREF tlsArray = (PTRARRAYREF)UNCHECKED_OBJECTREF_TO_OBJECTREF(pThreadLocalData->pNonCollectibleTlsReferenceData); + if (pThreadLocalData->cNonCollectibleTlsData <= index.GetIndexOffset()) + { + return NULL; + } + pTLSBaseAddress = dac_cast(OBJECTREFToObject(tlsArray->GetAt(index.GetIndexOffset() - NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY))); } + else + { + int32_t cTLSData = pThreadLocalData->cTLSData; + if (cTLSData <= index.GetIndexOffset()) + { + return NULL; + } - TADDR pTLSArrayData = pThreadLocalData->pTLSArrayData; - pTLSBaseAddress = *(dac_cast(pTLSArrayData) + index.GetIndexOffset()); + TADDR pTLSArrayData = pThreadLocalData->pTLSArrayData; + pTLSBaseAddress = *(dac_cast(pTLSArrayData) + index.GetIndexOffset()); + } if (pTLSBaseAddress == NULL) { // Maybe it is in the InFlightData @@ -55,7 +72,8 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(ThreadLocalData* pThreadLocalData, TLS return dac_cast(pTLSBaseAddress); } -GPTR_IMPL(TLSIndexToMethodTableMap, g_pThreadStaticTypeIndices); +TLSIndexToMethodTableMap *g_pThreadStaticCollectibleTypeIndices; +TLSIndexToMethodTableMap *g_pThreadStaticNonCollectibleTypeIndices; PTR_MethodTable LookupMethodTableForThreadStaticKnownToBeAllocated(TLSIndex index) { @@ -67,8 +85,14 @@ PTR_MethodTable LookupMethodTableForThreadStaticKnownToBeAllocated(TLSIndex inde } CONTRACTL_END; - // TODO, if and when we get array indices, we should be pickier. - return g_pThreadStaticTypeIndices->LookupTlsIndexKnownToBeAllocated(index); + if (index.GetTLSIndexType() == TLSIndexType::NonCollectible) + { + return g_pThreadStaticCollectibleTypeIndices->LookupTlsIndexKnownToBeAllocated(index); + } + else + { + return g_pThreadStaticNonCollectibleTypeIndices->LookupTlsIndexKnownToBeAllocated(index); + } } TADDR isGCFlag = 0x1; @@ -82,8 +106,16 @@ PTR_MethodTable LookupMethodTableAndFlagForThreadStatic(TLSIndex index, bool *pI MODE_COOPERATIVE; } CONTRACTL_END; - // TODO, if and when we get array indices, we should be pickier. - PTR_MethodTable retVal = g_pThreadStaticTypeIndices->Lookup(index, pIsGCStatic, pIsCollectible); + + PTR_MethodTable retVal; + if (index.GetTLSIndexType() == TLSIndexType::NonCollectible) + { + retVal = g_pThreadStaticNonCollectibleTypeIndices->Lookup(index, pIsGCStatic, pIsCollectible); + } + else + { + retVal = g_pThreadStaticCollectibleTypeIndices->Lookup(index, pIsGCStatic, pIsCollectible); + } return retVal; } @@ -101,33 +133,16 @@ bool ReportTLSIndexCarefully(TLSIndex index, int32_t cLoaderHandles, PTR_LOADERH bool isGCStatic; bool isCollectible; PTR_MethodTable pMT = LookupMethodTableAndFlagForThreadStatic(index, &isGCStatic, &isCollectible); - if (pMT == NULL) + _ASSERTE(((pMT == NULL) || isCollectible) && index.GetTLSIndexType() == TLSIndexType::Collectible); + if (index.GetTLSIndexType() == TLSIndexType::Collectible && pLoaderHandles[index.GetIndexOffset()] == NULL) { // The TLS index is not in use. This either means that the TLS index was never used, or that it was // used for a collectible assembly, and that assembly has been freed. In the latter case, we may need to // clean this entry up - if (cLoaderHandles > index.GetIndexOffset()) - { - pLoaderHandles[index.GetIndexOffset()] = NULL; - *ppTLSBaseAddress = NULL; - } + *ppTLSBaseAddress = NULL; return false; } - if (isCollectible) - { - // Check to see if the associated loaderallocator is still live - if (!pMT->GetLoaderAllocator()->IsExposedObjectLive()) - { - if (cLoaderHandles > index.GetIndexOffset()) - { - uintptr_t indexIntoLoaderHandleTable = index.GetIndexOffset(); - pLoaderHandles[indexIntoLoaderHandleTable] = NULL; - *ppTLSBaseAddress = NULL; - } - return false; - } - } fn(ppTLSBaseAddress, sc, 0 /* could be GC_CALL_INTERIOR or GC_CALL_PINNED */); return true; } @@ -143,7 +158,7 @@ void NotifyThreadStaticGCHappened() } #endif -void ScanThreadStaticRoots(ThreadLocalData *pThreadLocalData, bool forGC, promote_func* fn, ScanContext* sc) +void ScanThreadStaticRoots(Thread* pThread, bool forGC, promote_func* fn, ScanContext* sc) { CONTRACTL { @@ -153,14 +168,42 @@ void ScanThreadStaticRoots(ThreadLocalData *pThreadLocalData, bool forGC, promot } CONTRACTL_END; - if (pThreadLocalData == NULL) +#ifndef DACCESS_COMPILE + SpinLockHolder spinLock(&pThread->m_TlsSpinLock); +#endif // !DACCESS_COMPILE + + if (forGC) + { + // Clean out LoaderHandles for dead collectible assemblies + for (int32_t i = 0; i < pThread->cLoaderHandles; ++i) + { + TLSIndex index(TLSIndexType::Collectible, i); + PTR_LOADERHANDLE pLoaderHandle = pThread->pLoaderHandles + i; + if (*pLoaderHandle != NULL) + { + bool isGCStatic; + bool isCollectible; + PTR_MethodTable pMT = LookupMethodTableAndFlagForThreadStatic(index, &isGCStatic, &isCollectible); + _ASSERTE(isCollectible || pMT == NULL); + + if (pMT == NULL || !pMT->GetLoaderAllocator()->IsExposedObjectLive()) + { + *pLoaderHandle = NULL; + } + } + } + } + + if (pThread->m_ThreadLocalDataPtr == NULL) return; - int32_t cLoaderHandles = forGC ? pThreadLocalData->cLoaderHandles : 0; // We can only need to pass this to ReportTLSIndexCarefully if we are permitted to to clean out this array + ThreadLocalData *pThreadLocalData = pThread->m_ThreadLocalDataPtr; + + int32_t cLoaderHandles = pThread->cLoaderHandles; PTR_InFlightTLSData pInFlightData = pThreadLocalData->pInFlightData; while (pInFlightData != NULL) { - if (!ReportTLSIndexCarefully(pInFlightData->tlsIndex, pThreadLocalData->cLoaderHandles, pThreadLocalData->pLoaderHandles, dac_cast(&pInFlightData->pTLSData), fn, sc)) + if (!ReportTLSIndexCarefully(pInFlightData->tlsIndex, cLoaderHandles, pThread->pLoaderHandles, dac_cast(&pInFlightData->pTLSData), fn, sc)) { // TLS index is now dead. We should delete it, as the ReportTLSIndexCarefully function will have already deleted any assocated LOADERHANDLE #ifndef DACCESS_COMPILE @@ -175,14 +218,22 @@ void ScanThreadStaticRoots(ThreadLocalData *pThreadLocalData, bool forGC, promot } pInFlightData = pInFlightData->pNext; } + PTR_TADDR pTLSArrayData = dac_cast(pThreadLocalData->pTLSArrayData); int32_t cTLSData = pThreadLocalData->cTLSData; for (int32_t i = 0; i < cTLSData; ++i) { - TLSIndex index(i); + TLSIndex index(TLSIndexType::Collectible, i); TADDR *pTLSBaseAddress = pTLSArrayData + i; - ReportTLSIndexCarefully(index, pThreadLocalData->cLoaderHandles, pThreadLocalData->pLoaderHandles, dac_cast(pTLSBaseAddress), fn, sc); + ReportTLSIndexCarefully(index, cLoaderHandles, pThread->pLoaderHandles, dac_cast(pTLSBaseAddress), fn, sc); } + + // Report non-collectible object array +#ifndef DACCESS_COMPILE + fn(&pThreadLocalData->pNonCollectibleTlsReferenceData, sc, 0 /* could be GC_CALL_INTERIOR or GC_CALL_PINNED */); +#else + fn(dac_cast(&pThreadLocalData->pNonCollectibleTlsReferenceData), sc, 0 /* could be GC_CALL_INTERIOR or GC_CALL_PINNED */); +#endif } #ifndef DACCESS_COMPILE @@ -197,10 +248,10 @@ void TLSIndexToMethodTableMap::Set(TLSIndex index, PTR_MethodTable pMT, bool isG } CONTRACTL_END; - if (index.TLSIndexRawIndex >= m_maxIndex) + if (index.GetIndexOffset() >= m_maxIndex) { - uint32_t newSize = max(m_maxIndex, 16); - while (index.TLSIndexRawIndex >= newSize) + int32_t newSize = max(m_maxIndex, 16); + while (index.GetIndexOffset() >= newSize) { newSize *= 2; } @@ -225,8 +276,8 @@ void TLSIndexToMethodTableMap::Set(TLSIndex index, PTR_MethodTable pMT, bool isG rawValue |= IsCollectibleFlag(); m_collectibleEntries++; } - _ASSERTE(pMap[index.TLSIndexRawIndex] == 0 || IsClearedValue(pMap[index.TLSIndexRawIndex])); - pMap[index.TLSIndexRawIndex] = rawValue; + _ASSERTE(pMap[index.GetIndexOffset()] == 0 || IsClearedValue(pMap[index.GetIndexOffset()])); + pMap[index.GetIndexOffset()] = rawValue; } void TLSIndexToMethodTableMap::Clear(TLSIndex index, uint8_t whenCleared) @@ -239,16 +290,16 @@ void TLSIndexToMethodTableMap::Clear(TLSIndex index, uint8_t whenCleared) } CONTRACTL_END; - _ASSERTE(index.TLSIndexRawIndex < m_maxIndex); - TADDR rawValue = pMap[index.TLSIndexRawIndex]; + _ASSERTE(index.GetIndexOffset() < m_maxIndex); + TADDR rawValue = pMap[index.GetIndexOffset()]; _ASSERTE(rawValue & IsCollectibleFlag()); if (rawValue & IsCollectibleFlag()) { m_collectibleEntries--; } - pMap[index.TLSIndexRawIndex] = (whenCleared << 2) | 0x3; - _ASSERTE(GetClearedMarker(pMap[index.TLSIndexRawIndex]) == whenCleared); - _ASSERTE(IsClearedValue(pMap[index.TLSIndexRawIndex])); + pMap[index.GetIndexOffset()] = (whenCleared << 2) | 0x3; + _ASSERTE(GetClearedMarker(pMap[index.GetIndexOffset()]) == whenCleared); + _ASSERTE(IsClearedValue(pMap[index.GetIndexOffset()])); } bool TLSIndexToMethodTableMap::FindClearedIndex(uint8_t whenClearedMarkerToAvoid, TLSIndex* pIndex) @@ -274,19 +325,31 @@ bool TLSIndexToMethodTableMap::FindClearedIndex(uint8_t whenClearedMarkerToAvoid } uint32_t g_NextTLSSlot = 1; +uint32_t g_NextNonCollectibleTLSSlot = NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY; CrstStatic g_TLSCrst; void InitializeThreadStaticData() { - g_pThreadStaticTypeIndices = new TLSIndexToMethodTableMap(); + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_PREEMPTIVE; + } + CONTRACTL_END; + + g_pThreadStaticCollectibleTypeIndices = new TLSIndexToMethodTableMap(TLSIndexType::NonCollectible); + g_pThreadStaticNonCollectibleTypeIndices = new TLSIndexToMethodTableMap(TLSIndexType::NonCollectible); g_TLSCrst.Init(CrstThreadLocalStorageLock, CRST_UNSAFE_ANYMODE); } void InitializeCurrentThreadsStaticData(Thread* pThread) { + LIMITED_METHOD_CONTRACT; + t_ThreadStatics.pThread = pThread; - t_ThreadStatics.pThread->m_ThreadLocalDataThreadObjectCopy = t_ThreadStatics; - t_ThreadStatics.pThread->m_TlsSpinLock.Init(LOCK_TLSDATA, TRUE); + t_ThreadStatics.pThread->m_ThreadLocalDataPtr = &t_ThreadStatics; + t_ThreadStatics.pThread->m_TlsSpinLock.Init(LOCK_TLSDATA, FALSE); } void AllocateThreadStaticBoxes(MethodTable *pMT, PTRARRAYREF *ppRef) @@ -328,7 +391,7 @@ void AllocateThreadStaticBoxes(MethodTable *pMT, PTRARRAYREF *ppRef) } } -void FreeThreadStaticData(ThreadLocalData *pThreadLocalData) +void FreeLoaderAllocatorHandlesForTLSData(Thread *pThread) { CONTRACTL { NOTHROW; @@ -337,19 +400,56 @@ void FreeThreadStaticData(ThreadLocalData *pThreadLocalData) } CONTRACTL_END; - if (pThreadLocalData->cLoaderHandles > 0) + if (pThread->cLoaderHandles > 0) { CrstHolder ch(&g_TLSCrst); - for (const auto& entry : g_pThreadStaticTypeIndices->CollectibleEntries()) +#ifdef _DEBUG + bool allRemainingIndicesAreNotValid = false; +#endif + for (const auto& entry : g_pThreadStaticCollectibleTypeIndices->CollectibleEntries()) { - pThreadLocalData->pLoaderHandles[entry.TlsIndex.GetIndexOffset()] = NULL; + _ASSERTE((entry.TlsIndex.GetIndexOffset() < pThread->cLoaderHandles) || allRemainingIndicesAreNotValid); + if (entry.TlsIndex.GetIndexOffset() >= pThread->cLoaderHandles) + { +#ifndef _DEBUG + break; +#else + allRemainingIndicesAreNotValid = true; +#endif + } + else + { + if (pThread->pLoaderHandles[entry.TlsIndex.GetIndexOffset()] != NULL) + { + entry.pMT->GetLoaderAllocator()->FreeHandle(pThread->pLoaderHandles[entry.TlsIndex.GetIndexOffset()]); + pThread->pLoaderHandles[entry.TlsIndex.GetIndexOffset()] = NULL; + } + } } } +} + +void FreeThreadStaticData(ThreadLocalData *pThreadLocalData, Thread* pThread) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + SpinLockHolder spinLock(&pThread->m_TlsSpinLock); + + if (pThreadLocalData->pThread == NULL) + { + return; + } delete[] (uint8_t*)pThreadLocalData->pTLSArrayData; pThreadLocalData->pTLSArrayData = 0; pThreadLocalData->cTLSData = 0; + pThreadLocalData->pNonCollectibleTlsReferenceData = 0; + pThreadLocalData->cNonCollectibleTlsData = 0; while (pThreadLocalData->pInFlightData != NULL) { @@ -357,10 +457,21 @@ void FreeThreadStaticData(ThreadLocalData *pThreadLocalData) pThreadLocalData->pInFlightData = pInFlightData->pNext; delete pInFlightData; } - - pThreadLocalData->pThread = NULL; + pThreadLocalData->pThread->m_ThreadLocalDataPtr = NULL; + VolatileStoreWithoutBarrier(&pThreadLocalData->pThread, (Thread*)NULL); } +void SetTLSBaseValue(TADDR *ppTLSBaseAddress, TADDR pTLSBaseAddress, bool useGCBarrier) +{ + if (useGCBarrier) + { + SetObjectReference((OBJECTREF *)ppTLSBaseAddress, (OBJECTREF)ObjectToOBJECTREF((Object*)pTLSBaseAddress)); + } + else + { + *ppTLSBaseAddress = pTLSBaseAddress; + } +} void* GetThreadLocalStaticBase(TLSIndex index) { CONTRACTL @@ -373,49 +484,80 @@ void* GetThreadLocalStaticBase(TLSIndex index) bool isGCStatic; bool isCollectible; + bool useWriteBarrierToWriteTLSBase = false; MethodTable *pMT = LookupMethodTableAndFlagForThreadStatic(index, &isGCStatic, &isCollectible); - int32_t cTLSData = t_ThreadStatics.cTLSData; - if (cTLSData <= index.GetIndexOffset()) - { - // Grow the underlying TLS array - SpinLockHolder spinLock(&t_ThreadStatics.pThread->m_TlsSpinLock); - int32_t newcTLSData = index.GetIndexOffset() + 8; // Leave a bit of margin - uintptr_t* pNewTLSArrayData = new uintptr_t[newcTLSData]; - memset(pNewTLSArrayData, 0, newcTLSData * sizeof(uintptr_t)); - if (cTLSData > 0) - memcpy(pNewTLSArrayData, (void*)t_ThreadStatics.pTLSArrayData, cTLSData * sizeof(uintptr_t)); - uintptr_t* pOldArray = (uintptr_t*)t_ThreadStatics.pTLSArrayData; - t_ThreadStatics.pTLSArrayData = (TADDR)pNewTLSArrayData; - cTLSData = newcTLSData; - t_ThreadStatics.cTLSData = cTLSData; - delete[] pOldArray; - t_ThreadStatics.pThread->m_ThreadLocalDataThreadObjectCopy = t_ThreadStatics; - } - - if (isCollectible && t_ThreadStatics.cLoaderHandles <= index.GetIndexOffset()) - { - // Grow the underlying TLS array - SpinLockHolder spinLock(&t_ThreadStatics.pThread->m_TlsSpinLock); - int32_t cNewTLSLoaderHandles = index.GetIndexOffset() + 8; // Leave a bit of margin - size_t cbNewTLSLoaderHandles = sizeof(LOADERHANDLE) * cNewTLSLoaderHandles; - LOADERHANDLE* pNewTLSLoaderHandles = new LOADERHANDLE[cNewTLSLoaderHandles]; - memset(pNewTLSLoaderHandles, 0, cbNewTLSLoaderHandles); - if (cTLSData > 0) - memcpy(pNewTLSLoaderHandles, (void*)t_ThreadStatics.pLoaderHandles, t_ThreadStatics.cLoaderHandles * sizeof(LOADERHANDLE)); - - LOADERHANDLE* pOldArray = t_ThreadStatics.pLoaderHandles; - t_ThreadStatics.pLoaderHandles = pNewTLSLoaderHandles; - t_ThreadStatics.cLoaderHandles = cNewTLSLoaderHandles; - delete[] pOldArray; - t_ThreadStatics.pThread->m_ThreadLocalDataThreadObjectCopy = t_ThreadStatics; - } - - TADDR pTLSArrayData = t_ThreadStatics.pTLSArrayData; - TADDR *ppTLSBaseAddress = reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetIndexOffset()); - TADDR pTLSBaseAddress = *ppTLSBaseAddress; + struct + { + TADDR *ppTLSBaseAddress = NULL; + TADDR pTLSBaseAddress = NULL; + } gcBaseAddresses; + GCPROTECT_BEGININTERIOR(gcBaseAddresses); - if (pTLSBaseAddress == NULL) + if (index.GetTLSIndexType() == TLSIndexType::NonCollectible) + { + PTRARRAYREF tlsArray = (PTRARRAYREF)UNCHECKED_OBJECTREF_TO_OBJECTREF(t_ThreadStatics.pNonCollectibleTlsReferenceData); + if (t_ThreadStatics.cNonCollectibleTlsData <= index.GetIndexOffset()) + { + GCPROTECT_BEGIN(tlsArray); + PTRARRAYREF tlsArrayNew = (PTRARRAYREF)AllocateObjectArray(index.GetIndexOffset() + 8, g_pObjectClass); + if (tlsArray != NULL) + { + for (DWORD i = 0; i < tlsArray->GetNumComponents(); i++) + { + tlsArrayNew->SetAt(i, tlsArray->GetAt(i)); + } + } + t_ThreadStatics.pNonCollectibleTlsReferenceData = OBJECTREF_TO_UNCHECKED_OBJECTREF(tlsArrayNew); + tlsArray = tlsArrayNew; + t_ThreadStatics.cNonCollectibleTlsData = tlsArrayNew->GetNumComponents() + NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY; + GCPROTECT_END(); + } + gcBaseAddresses.ppTLSBaseAddress = (TADDR*)(tlsArray->GetDataPtr() + (index.GetIndexOffset() - NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY)) ; + useWriteBarrierToWriteTLSBase = true; + } + else + { + int32_t cTLSData = t_ThreadStatics.cTLSData; + if (cTLSData <= index.GetIndexOffset()) + { + // Grow the underlying TLS array + SpinLockHolder spinLock(&t_ThreadStatics.pThread->m_TlsSpinLock); + int32_t newcTLSData = index.GetIndexOffset() + 8; // Leave a bit of margin + uintptr_t* pNewTLSArrayData = new uintptr_t[newcTLSData]; + memset(pNewTLSArrayData, 0, newcTLSData * sizeof(uintptr_t)); + if (cTLSData > 0) + memcpy(pNewTLSArrayData, (void*)t_ThreadStatics.pTLSArrayData, cTLSData * sizeof(uintptr_t)); + uintptr_t* pOldArray = (uintptr_t*)t_ThreadStatics.pTLSArrayData; + t_ThreadStatics.pTLSArrayData = (TADDR)pNewTLSArrayData; + cTLSData = newcTLSData; + t_ThreadStatics.cTLSData = cTLSData; + delete[] pOldArray; + } + + if (isCollectible && t_ThreadStatics.pThread->cLoaderHandles <= index.GetIndexOffset()) + { + // Grow the underlying TLS array + SpinLockHolder spinLock(&t_ThreadStatics.pThread->m_TlsSpinLock); + int32_t cNewTLSLoaderHandles = index.GetIndexOffset() + 8; // Leave a bit of margin + size_t cbNewTLSLoaderHandles = sizeof(LOADERHANDLE) * cNewTLSLoaderHandles; + LOADERHANDLE* pNewTLSLoaderHandles = new LOADERHANDLE[cNewTLSLoaderHandles]; + memset(pNewTLSLoaderHandles, 0, cbNewTLSLoaderHandles); + if (cTLSData > 0) + memcpy(pNewTLSLoaderHandles, (void*)t_ThreadStatics.pThread->pLoaderHandles, t_ThreadStatics.pThread->cLoaderHandles * sizeof(LOADERHANDLE)); + + LOADERHANDLE* pOldArray = t_ThreadStatics.pThread->pLoaderHandles; + t_ThreadStatics.pThread->pLoaderHandles = pNewTLSLoaderHandles; + t_ThreadStatics.pThread->cLoaderHandles = cNewTLSLoaderHandles; + delete[] pOldArray; + } + + TADDR pTLSArrayData = t_ThreadStatics.pTLSArrayData; + gcBaseAddresses.ppTLSBaseAddress = reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetIndexOffset()); + } + gcBaseAddresses.pTLSBaseAddress = *gcBaseAddresses.ppTLSBaseAddress; + + if (gcBaseAddresses.pTLSBaseAddress == NULL) { // Maybe it is in the InFlightData InFlightTLSData* pInFlightData = t_ThreadStatics.pInFlightData; @@ -424,22 +566,21 @@ void* GetThreadLocalStaticBase(TLSIndex index) { if (pInFlightData->tlsIndex == index) { - pTLSBaseAddress = pInFlightData->pTLSData; + gcBaseAddresses.pTLSBaseAddress = pInFlightData->pTLSData; if (pMT->IsClassInited()) { SpinLockHolder spinLock(&t_ThreadStatics.pThread->m_TlsSpinLock); - *ppTLSBaseAddress = pTLSBaseAddress; + SetTLSBaseValue(gcBaseAddresses.ppTLSBaseAddress, gcBaseAddresses.pTLSBaseAddress, useWriteBarrierToWriteTLSBase); *ppOldNextPtr = pInFlightData->pNext; delete pInFlightData; - t_ThreadStatics.pThread->m_ThreadLocalDataThreadObjectCopy = t_ThreadStatics; } break; } ppOldNextPtr = &pInFlightData->pNext; pInFlightData = pInFlightData->pNext; } - if (pTLSBaseAddress == NULL) + if (gcBaseAddresses.pTLSBaseAddress == NULL) { // Now we need to actually allocate the TLS data block struct @@ -477,7 +618,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) if (isCollectible) { - LOADERHANDLE *pLoaderHandle = reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetIndexOffset()); + LOADERHANDLE *pLoaderHandle = t_ThreadStatics.pThread->pLoaderHandles + index.GetIndexOffset(); // Note, that this can fail, but if it succeeds we don't have a holder in place to clean it up if future operations fail // Add such a holder if we ever add a possibly failing operation after this *pLoaderHandle = pMT->GetLoaderAllocator()->AllocateHandle(gc.tlsEntry); @@ -488,25 +629,25 @@ void* GetThreadLocalStaticBase(TLSIndex index) { GCX_FORBID(); - pTLSBaseAddress = (TADDR)OBJECTREFToObject(gc.tlsEntry); + gcBaseAddresses.pTLSBaseAddress = (TADDR)OBJECTREFToObject(gc.tlsEntry); if (pInFlightData == NULL) { - *ppTLSBaseAddress = pTLSBaseAddress; + SetTLSBaseValue(gcBaseAddresses.ppTLSBaseAddress, gcBaseAddresses.pTLSBaseAddress, useWriteBarrierToWriteTLSBase); } else { SpinLockHolder spinLock(&t_ThreadStatics.pThread->m_TlsSpinLock); pInFlightData->pNext = t_ThreadStatics.pInFlightData; - pInFlightData->pTLSData = pTLSBaseAddress; + pInFlightData->pTLSData = gcBaseAddresses.pTLSBaseAddress; t_ThreadStatics.pInFlightData = pInFlightData; - t_ThreadStatics.pThread->m_ThreadLocalDataThreadObjectCopy = t_ThreadStatics; } } GCPROTECT_END(); } } - _ASSERTE(pTLSBaseAddress != NULL); - return reinterpret_cast(pTLSBaseAddress); + GCPROTECT_END(); + _ASSERTE(gcBaseAddresses.pTLSBaseAddress != NULL); + return reinterpret_cast(gcBaseAddresses.pTLSBaseAddress); } void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pIndex) @@ -521,20 +662,26 @@ void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pInde } TLSIndex newTLSIndex = TLSIndex::Unallocated(); - if (!g_pThreadStaticTypeIndices->FindClearedIndex(s_GCsWhichDoRelocateAndCanEmptyOutTheTLSIndices, &newTLSIndex)) + + if (!pMT->Collectible()) { - uint32_t tlsRawIndex = g_NextTLSSlot; - newTLSIndex = TLSIndex(tlsRawIndex); - g_NextTLSSlot += 1; + uint32_t tlsRawIndex = g_NextNonCollectibleTLSSlot++; + newTLSIndex = TLSIndex(TLSIndexType::NonCollectible, tlsRawIndex); + g_pThreadStaticNonCollectibleTypeIndices->Set(newTLSIndex, pMT, isGCFlag); } - - if (pMT->Collectible()) + else { + if (!g_pThreadStaticCollectibleTypeIndices->FindClearedIndex(s_GCsWhichDoRelocateAndCanEmptyOutTheTLSIndices, &newTLSIndex)) + { + uint32_t tlsRawIndex = g_NextTLSSlot; + newTLSIndex = TLSIndex(TLSIndexType::Collectible, tlsRawIndex); + g_NextTLSSlot += 1; + } + + g_pThreadStaticCollectibleTypeIndices->Set(newTLSIndex, pMT, (gcStatic ? isGCFlag : 0)); pMT->GetLoaderAllocator()->GetTLSIndexList().Append(newTLSIndex); } - g_pThreadStaticTypeIndices->Set(newTLSIndex, pMT, (gcStatic ? isGCFlag : 0)); - *pIndex = newTLSIndex; } @@ -557,7 +704,7 @@ void FreeTLSIndicesForLoaderAllocator(LoaderAllocator *pLoaderAllocator) while (current != end) { - g_pThreadStaticTypeIndices->Clear(tlsIndicesToCleanup[current], s_GCsWhichDoRelocateAndCanEmptyOutTheTLSIndices); + g_pThreadStaticCollectibleTypeIndices->Clear(tlsIndicesToCleanup[current], s_GCsWhichDoRelocateAndCanEmptyOutTheTLSIndices); ++current; } } @@ -709,8 +856,8 @@ void GetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) _ASSERTE_MSG(false, "Unsupported scenario of optimizing TLS access on Linux Arm32/x86"); #endif // TARGET_WINDOWS - pInfo->offsetOfMaxThreadStaticBlocks = (uint32_t)(threadStaticBaseOffset + offsetof(ThreadLocalData, cTLSData)); - pInfo->offsetOfThreadStaticBlocks = (uint32_t)(threadStaticBaseOffset + offsetof(ThreadLocalData, pTLSArrayData)); + pInfo->offsetOfMaxThreadStaticBlocks = (uint32_t)(threadStaticBaseOffset + offsetof(ThreadLocalData, cNonCollectibleTlsData)); + pInfo->offsetOfThreadStaticBlocks = (uint32_t)(threadStaticBaseOffset + offsetof(ThreadLocalData, pNonCollectibleTlsReferenceData)); } #endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index 206da905d9e9e..701d37d0ba315 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -92,15 +92,18 @@ class Thread; enum class TLSIndexType { - Standard, // IndexOffset for this form of TLSIndex is scaled by sizeof(void*) and then added to ThreadLocalData::pTLSArrayData to get the final address + NonCollectible, // IndexOffset for this form of TLSIndex is scaled by sizeof(OBJECTREF) and used as an index into the array at ThreadLocalData::pNonCollectibleTlsReferenceData to get the final address + Collectible, // IndexOffset for this form of TLSIndex is scaled by sizeof(void*) and then added to ThreadLocalData::pTLSArrayData to get the final address }; struct TLSIndex { TLSIndex() : TLSIndexRawIndex(0xFFFFFFFF) { } TLSIndex(uint32_t rawIndex) : TLSIndexRawIndex(rawIndex) { } + TLSIndex(TLSIndexType indexType, int32_t indexOffset) : TLSIndexRawIndex((((uint32_t)indexType) << 24) | (uint32_t)indexOffset) { } uint32_t TLSIndexRawIndex; int32_t GetIndexOffset() const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex & 0xFFFFFF; } + TLSIndexType GetTLSIndexType() const { LIMITED_METHOD_DAC_CONTRACT; return (TLSIndexType)(TLSIndexRawIndex >> 24); } bool IsAllocated() const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex != 0xFFFFFFFF;} static TLSIndex Unallocated() { LIMITED_METHOD_DAC_CONTRACT; return TLSIndex(0xFFFFFFFF); } bool operator == (TLSIndex index) const { LIMITED_METHOD_DAC_CONTRACT; return TLSIndexRawIndex == index.TLSIndexRawIndex; } @@ -113,44 +116,47 @@ typedef DPTR(InFlightTLSData) PTR_InFlightTLSData; struct ThreadLocalData { - int32_t cTLSData; // Size in bytes of offset into the TLS array which is valid - int32_t cLoaderHandles; + int32_t cNonCollectibleTlsData; // Size of offset into the non-collectible TLS array which is valid, NOTE: this is relative to the start of the pNonCollectibleTlsReferenceData object, not the start of the data in the array + int32_t cTLSData; // Size of offset into the TLS array which is valid + PTR_Object pNonCollectibleTlsReferenceData; TADDR pTLSArrayData; // Points at the Thread local array data. Thread *pThread; PTR_InFlightTLSData pInFlightData; // Points at the in-flight TLS data (TLS data that exists before the class constructor finishes running) - PTR_LOADERHANDLE pLoaderHandles; }; typedef DPTR(ThreadLocalData) PTR_ThreadLocalData; #ifndef DACCESS_COMPILE #ifdef _MSC_VER -extern __declspec(thread) ThreadLocalData t_ThreadStatics; +extern __declspec(selectany) __declspec(thread) ThreadLocalData t_ThreadStatics; #else extern __thread ThreadLocalData t_ThreadStatics; #endif // _MSC_VER #endif // DACCESS_COMPILE +#define NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY 2 + class TLSIndexToMethodTableMap { PTR_TADDR pMap; - uint32_t m_maxIndex; + int32_t m_maxIndex; uint32_t m_collectibleEntries; + TLSIndexType m_indexType; TADDR IsGCFlag() const { return (TADDR)0x1; } TADDR IsCollectibleFlag() const { return (TADDR)0x2; } TADDR UnwrapValue(TADDR input) const { return input & ~3; } public: - TLSIndexToMethodTableMap() : pMap(dac_cast(dac_cast(0))), m_maxIndex(0), m_collectibleEntries(0) { } + TLSIndexToMethodTableMap(TLSIndexType indexType) : pMap(dac_cast(dac_cast(0))), m_maxIndex(0), m_collectibleEntries(0), m_indexType(indexType) { } PTR_MethodTable Lookup(TLSIndex index, bool *isGCStatic, bool *isCollectible) const { LIMITED_METHOD_CONTRACT; *isGCStatic = false; *isCollectible = false; - if (index.TLSIndexRawIndex < VolatileLoad(&m_maxIndex)) + if (index.GetIndexOffset() < VolatileLoad(&m_maxIndex)) { - TADDR rawValue = VolatileLoadWithoutBarrier(&VolatileLoad(&pMap)[index.TLSIndexRawIndex]); + TADDR rawValue = VolatileLoadWithoutBarrier(&VolatileLoad(&pMap)[index.GetIndexOffset()]); if (IsClearedValue(rawValue)) { return NULL; @@ -165,9 +171,9 @@ class TLSIndexToMethodTableMap PTR_MethodTable LookupTlsIndexKnownToBeAllocated(TLSIndex index) const { LIMITED_METHOD_CONTRACT; - if (index.TLSIndexRawIndex < VolatileLoad(&m_maxIndex)) + if (index.GetIndexOffset() < VolatileLoad(&m_maxIndex)) { - TADDR rawValue = VolatileLoadWithoutBarrier(&VolatileLoad(&pMap)[index.TLSIndexRawIndex]); + TADDR rawValue = VolatileLoadWithoutBarrier(&VolatileLoad(&pMap)[index.GetIndexOffset()]); return (PTR_MethodTable)UnwrapValue(rawValue); } return NULL; @@ -190,9 +196,9 @@ class TLSIndexToMethodTableMap { LIMITED_METHOD_CONTRACT; entry e(index); - if (index.TLSIndexRawIndex < VolatileLoad(&m_maxIndex)) + if (index.GetIndexOffset() < VolatileLoad(&m_maxIndex)) { - TADDR rawValue = VolatileLoadWithoutBarrier(&VolatileLoad(&pMap)[index.TLSIndexRawIndex]); + TADDR rawValue = VolatileLoadWithoutBarrier(&VolatileLoad(&pMap)[index.GetIndexOffset()]); if (!IsClearedValue(rawValue)) { e.pMT = (PTR_MethodTable)UnwrapValue(rawValue); @@ -207,7 +213,7 @@ class TLSIndexToMethodTableMap } else { - e.TlsIndex = TLSIndex(m_maxIndex); + e.TlsIndex = TLSIndex(m_indexType, m_maxIndex); } return e; } @@ -217,7 +223,7 @@ class TLSIndexToMethodTableMap friend class TLSIndexToMethodTableMap; const TLSIndexToMethodTableMap& m_pMap; entry m_entry; - iterator(const TLSIndexToMethodTableMap& pMap, uint32_t currentIndex) : m_pMap(pMap), m_entry(pMap.Lookup(TLSIndex(currentIndex))) {} + iterator(const TLSIndexToMethodTableMap& pMap, uint32_t currentIndex) : m_pMap(pMap), m_entry(pMap.Lookup(TLSIndex(pMap.m_indexType, currentIndex))) {} public: const entry& operator*() const { return m_entry; } const entry* operator->() const { return &m_entry; } @@ -337,17 +343,15 @@ class TLSIndexToMethodTableMap #endif }; -typedef DPTR(TLSIndexToMethodTableMap) PTR_TLSIndexToMethodTableMap; -GPTR_DECL(TLSIndexToMethodTableMap, g_pThreadStaticTypeIndices); - -PTR_VOID GetThreadLocalStaticBaseNoCreate(ThreadLocalData *pThreadLocalData, TLSIndex index); -void ScanThreadStaticRoots(ThreadLocalData *pThreadLocalData, bool forGC, promote_func* fn, ScanContext* sc); +PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread *pThreadLocalData, TLSIndex index); +void ScanThreadStaticRoots(Thread* pThread, bool forGC, promote_func* fn, ScanContext* sc); #ifndef DACCESS_COMPILE PTR_MethodTable LookupMethodTableForThreadStaticKnownToBeAllocated(TLSIndex index); void InitializeThreadStaticData(); void InitializeCurrentThreadsStaticData(Thread* pThread); -void FreeThreadStaticData(ThreadLocalData *pThreadLocalData); +void FreeLoaderAllocatorHandlesForTLSData(Thread* pThread); +void FreeThreadStaticData(ThreadLocalData *pThreadLocalData, Thread* pThread); void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pIndex); void FreeTLSIndicesForLoaderAllocator(LoaderAllocator *pLoaderAllocator); void* GetThreadLocalStaticBase(TLSIndex index); From f6aaa6943d65e86edd57a1c1847a0ddb8c245b81 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 18 Apr 2024 13:16:59 -0700 Subject: [PATCH 53/82] Update JIT interface guid again --- src/coreclr/inc/jiteeversionguid.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 2a067e78ea2db..6bcb13b5a9265 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 724558e6-1501-40eb-908f-aea11b38339a */ - 0x724558e6, - 0x1501, - 0x40eb, - {0x90, 0x8f, 0xae, 0xa1, 0x1b, 0x38, 0x33, 0x9a} +constexpr GUID JITEEVersionIdentifier = { /* de0cd36d-3094-4110-af7d-31eb36234e46 */ + 0xde0cd36d, + 0x3094, + 0x4110, + {0xaf, 0x7d, 0x31, 0xeb, 0x36, 0x23, 0x4e, 0x46} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// From 61823a4a294301423f2c48246114ea98f8b956f3 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 18 Apr 2024 13:51:44 -0700 Subject: [PATCH 54/82] Fixed build breaks caused by merge, and code review comments --- .../RuntimeHelpers.CoreCLR.cs | 2 +- src/coreclr/inc/readytorun.h | 6 +- src/coreclr/jit/valuenum.cpp | 65 ++++++++++--------- src/coreclr/jit/valuenumfuncs.h | 5 +- src/coreclr/vm/methodtable.h | 2 +- 5 files changed, 42 insertions(+), 38 deletions(-) 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 a4238c2843c4e..a609805af4ce4 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 @@ -724,8 +724,8 @@ internal unsafe struct MethodTableAuxiliaryData [FieldOffset(0)] private uint Flags; - private const uint enum_flag_CanCompareBitsOrUseFastGetHashCode = 0x0400; // Is any field type or sub field type overrode Equals or GetHashCode private const uint enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode = 0x0002; // Whether we have checked the overridden Equals or GetHashCode + private const uint enum_flag_CanCompareBitsOrUseFastGetHashCode = 0x0004; // Is any field type or sub field type overrode Equals or GetHashCode public bool HasCheckedCanCompareBitsOrUseFastGetHashCode => (Flags & enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode) != 0; diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index a6190db620e16..d7ac1df99e82d 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -19,10 +19,10 @@ // src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h // If you update this, ensure you run `git grep MINIMUM_READYTORUN_MAJOR_VERSION` // and handle pending work. -#define READYTORUN_MAJOR_VERSION 0x000A +#define READYTORUN_MAJOR_VERSION 10 #define READYTORUN_MINOR_VERSION 0x0000 -#define MINIMUM_READYTORUN_MAJOR_VERSION 0x00A +#define MINIMUM_READYTORUN_MAJOR_VERSION 10 // R2R Version 2.1 adds the InliningInfo section // R2R Version 2.2 adds the ProfileDataInfo section @@ -34,7 +34,7 @@ // R2R Version 9.0 adds support for the Vector512 type // R2R Version 9.1 adds new helpers to allocate objects on frozen segments // R2R Version 9.2 adds MemZero and NativeMemSet helpers - +// R2R Version 10.0 adds support for the statics being allocated on a per type basis instead of on a per module basis struct READYTORUN_CORE_HEADER { diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index a80da23e5f327..b001f56f6e612 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -13185,33 +13185,6 @@ bool Compiler::fgValueNumberHelperCall(GenTreeCall* call) break; } - case CORINFO_HELP_GETSHARED_GCSTATIC_BASE: - case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE: - case CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS: - case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_DYNAMICCLASS: - case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE: - case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE: - case CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS: - case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS: - case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS: - // These all take (Module*, class ID) as parameters. - // - // Strictly speaking the exact possible exception thrown by the - // static constructor depends on heap state too, but given that - // the constructor is only invoked once we can model that for - // the same class the same exceptions are thrown. Downstream - // code like CSE/copy prop that makes use VNs innately need to - // establish some form of dominance around the individual trees - // that makes this ok. - // - vnpExc = vnStore->VNPExcSetSingleton( - vnStore->VNPairForFunc(TYP_REF, VNF_ClassInitExc, - vnStore->VNPNormalPair( - call->gtArgs.GetUserArgByIndex(0)->GetNode()->gtVNPair), - vnStore->VNPNormalPair( - call->gtArgs.GetUserArgByIndex(1)->GetNode()->gtVNPair))); - break; - #ifdef FEATURE_READYTORUN case CORINFO_HELP_READYTORUN_GCSTATIC_BASE: case CORINFO_HELP_READYTORUN_NONGCSTATIC_BASE: @@ -13228,10 +13201,14 @@ bool Compiler::fgValueNumberHelperCall(GenTreeCall* call) } #endif - case CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE: - case CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE: - case CORINFO_HELP_GETGENERICS_GCSTATIC_BASE: - case CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE: + case CORINFO_HELP_GET_GCSTATIC_BASE: + case CORINFO_HELP_GET_NONGCSTATIC_BASE: + case CORINFO_HELP_GET_GCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GET_NONGCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GET_GCTHREADSTATIC_BASE: + case CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE: + case CORINFO_HELP_GET_GCTHREADSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GET_NONGCTHREADSTATIC_BASE_NOCTOR: // These take class handles as parameters. vnpExc = vnStore->VNPExcSetSingleton( vnStore->VNPairForFunc(TYP_REF, VNF_ClassInitGenericExc, @@ -13239,6 +13216,32 @@ bool Compiler::fgValueNumberHelperCall(GenTreeCall* call) call->gtArgs.GetUserArgByIndex(0)->GetNode()->gtVNPair))); break; + case CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE: + case CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE: + case CORINFO_HELP_GETPINNED_GCSTATIC_BASE: + case CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE: + case CORINFO_HELP_GETDYNAMIC_GCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETDYNAMIC_NONGCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETPINNED_GCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE_NOCTOR: + // These take DynamicClassInfo handles as parameters. + vnpExc = vnStore->VNPExcSetSingleton( + vnStore->VNPairForFunc(TYP_REF, VNF_DynamicClassInitExc, + vnStore->VNPNormalPair( + call->gtArgs.GetUserArgByIndex(0)->GetNode()->gtVNPair))); + break; + + case CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE: + case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE: + case CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR: + // These take ThreadStaticInfo as parameters. + vnpExc = vnStore->VNPExcSetSingleton( + vnStore->VNPairForFunc(TYP_REF, VNF_ThreadClassInitExc, + vnStore->VNPNormalPair( + call->gtArgs.GetUserArgByIndex(0)->GetNode()->gtVNPair))); + break; + case CORINFO_HELP_DIV: case CORINFO_HELP_LDIV: vnpExc = fgValueNumberDivisionExceptions(GT_DIV, call->gtArgs.GetUserArgByIndex(0)->GetNode(), diff --git a/src/coreclr/jit/valuenumfuncs.h b/src/coreclr/jit/valuenumfuncs.h index 3576cef75b33d..3942e1197e180 100644 --- a/src/coreclr/jit/valuenumfuncs.h +++ b/src/coreclr/jit/valuenumfuncs.h @@ -68,9 +68,10 @@ ValueNumFuncDef(IndexOutOfRangeExc, 2, false, false, false, false) // Array boun ValueNumFuncDef(InvalidCastExc, 2, false, false, false, false) // CastClass check, Args: 0: ref value being cast; 1: handle of type being cast to ValueNumFuncDef(R2RInvalidCastExc, 2, false, false, false, false) // CastClass check, Args: 0: ref value being cast; 1: entry point of R2R cast helper ValueNumFuncDef(NewArrOverflowExc, 1, false, false, false, false) // Raises Integer overflow when Arg 0 is negative -ValueNumFuncDef(ClassInitExc, 2, false, false, false, false) // Represents exceptions thrown by static constructor for class. Args: 0: VN of module, 1: VN of class ID +ValueNumFuncDef(DynamicClassInitExc, 1, false, false, false, false) // Represents exceptions thrown by static constructor for class. Args: 0: VN of DynamicStaticsInfo +ValueNumFuncDef(ThreadClassInitExc, 1, false, false, false, false) // Represents exceptions thrown by static constructor for class. Args: 0: VN of ThreadStaticsInfo ValueNumFuncDef(R2RClassInitExc, 1, false, false, false, false) // Represents exceptions thrown by static constructor for class. Args: 0: VN of R2R entry point -ValueNumFuncDef(ClassInitGenericExc, 2, false, false, false, false)// Represents exceptions thrown by static constructor for generic class. Args: 0: VN of class handle +ValueNumFuncDef(ClassInitGenericExc, 2, false, false, false, false)// Represents exceptions thrown by static constructor for class. Args: 0: VN of class handle ValueNumFuncDef(HelperOpaqueExc, 1, false, false, false, false) // Represents opaque exceptions could be thrown by a JIT helper. // Args: 0: Input to helper that uniquely determines exceptions thrown. diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 134cdb1486344..c13b8191ea152 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -338,7 +338,7 @@ struct MethodTableAuxiliaryData enum_flag_IsInitError = 0x0100, enum_flag_IsStaticDataAllocated = 0x0200, - // unum_unused = 0x0400, // Is any field type or sub field type overrode Equals or GetHashCode + // unum_unused = 0x0400, enum_flag_IsTlsIndexAllocated = 0x0800, enum_flag_MayHaveOpenInterfaceInInterfaceMap = 0x1000, // enum_unused = 0x2000, From 5914558ba7c711208fc9856b291282e011337eab Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 18 Apr 2024 15:26:50 -0700 Subject: [PATCH 55/82] Fix comment and version number --- src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h | 4 ++-- src/coreclr/vm/methodtablebuilder.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h index 6a3b24a394487..f3f60f6900924 100644 --- a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h +++ b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h @@ -11,8 +11,8 @@ struct ReadyToRunHeaderConstants { static const uint32_t Signature = 0x00525452; // 'RTR' - static const uint32_t CurrentMajorVersion = 9; - static const uint32_t CurrentMinorVersion = 2; + static const uint32_t CurrentMajorVersion = 10; + static const uint32_t CurrentMinorVersion = 0; }; struct ReadyToRunHeader diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 4bfd61bbde571..36e48a7dd4368 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -1676,7 +1676,7 @@ MethodTableBuilder::BuildMethodTableThrowing( // the offsets of our fields will depend on this. if (bmtEnumFields->dwNumStaticFields != 0) { - // We will need statics + // We will need static variables bmtProp->fDynamicStatics = true; if (bmtGenerics->HasInstantiation()) From 4563623b4bf2b9df9576c71b101e49ef1d23538a Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 19 Apr 2024 13:51:18 -0700 Subject: [PATCH 56/82] Fix issues noted in testing --- src/coreclr/vm/threads.h | 4 ++-- src/coreclr/vm/threadstatics.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 83b7c3d6a903f..2738f36fbf4ca 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -3361,8 +3361,8 @@ class Thread #endif //DACCESS_COMPILE PTR_ThreadLocalData m_ThreadLocalDataPtr; - int32_t cLoaderHandles; - PTR_LOADERHANDLE pLoaderHandles; + int32_t cLoaderHandles = 0; + PTR_LOADERHANDLE pLoaderHandles = 0; SpinLock m_TlsSpinLock; PTR_ThreadLocalData GetThreadLocalDataPtr() { LIMITED_METHOD_DAC_CONTRACT; return m_ThreadLocalDataPtr; } diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index ddd4ad5e7e364..5ef547089779c 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -667,7 +667,7 @@ void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pInde { uint32_t tlsRawIndex = g_NextNonCollectibleTLSSlot++; newTLSIndex = TLSIndex(TLSIndexType::NonCollectible, tlsRawIndex); - g_pThreadStaticNonCollectibleTypeIndices->Set(newTLSIndex, pMT, isGCFlag); + g_pThreadStaticNonCollectibleTypeIndices->Set(newTLSIndex, pMT, gcStatic); } else { @@ -678,7 +678,7 @@ void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pInde g_NextTLSSlot += 1; } - g_pThreadStaticCollectibleTypeIndices->Set(newTLSIndex, pMT, (gcStatic ? isGCFlag : 0)); + g_pThreadStaticCollectibleTypeIndices->Set(newTLSIndex, pMT, gcStatic); pMT->GetLoaderAllocator()->GetTLSIndexList().Append(newTLSIndex); } From 11c671f09b727fd62090ff2f8e7ce98b38c72f20 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 19 Apr 2024 16:06:32 -0700 Subject: [PATCH 57/82] Fix another oops --- src/coreclr/vm/threadstatics.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 5ef547089779c..e9ea8c8c79df9 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -87,11 +87,11 @@ PTR_MethodTable LookupMethodTableForThreadStaticKnownToBeAllocated(TLSIndex inde if (index.GetTLSIndexType() == TLSIndexType::NonCollectible) { - return g_pThreadStaticCollectibleTypeIndices->LookupTlsIndexKnownToBeAllocated(index); + return g_pThreadStaticNonCollectibleTypeIndices->LookupTlsIndexKnownToBeAllocated(index); } else { - return g_pThreadStaticNonCollectibleTypeIndices->LookupTlsIndexKnownToBeAllocated(index); + return g_pThreadStaticCollectibleTypeIndices->LookupTlsIndexKnownToBeAllocated(index); } } From ec88375ad3c352a2f52c87c1d5d5be1b27328bd3 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 22 Apr 2024 09:01:27 -0700 Subject: [PATCH 58/82] Extra checks for thread cleanup paths operating in parallel --- src/coreclr/vm/threadstatics.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index e9ea8c8c79df9..2ec6f45ba8d50 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -437,6 +437,9 @@ void FreeThreadStaticData(ThreadLocalData *pThreadLocalData, Thread* pThread) } CONTRACTL_END; + if (pThreadLocalData == NULL) + return; + SpinLockHolder spinLock(&pThread->m_TlsSpinLock); if (pThreadLocalData->pThread == NULL) @@ -444,6 +447,11 @@ void FreeThreadStaticData(ThreadLocalData *pThreadLocalData, Thread* pThread) return; } + pThreadLocalData = pThread->m_ThreadLocalDataPtr; + + if (pThreadLocalData == NULL) + return; + delete[] (uint8_t*)pThreadLocalData->pTLSArrayData; pThreadLocalData->pTLSArrayData = 0; From 0ef9bb1b4b13accd4a536a7d8e36d62de5a768c0 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 22 Apr 2024 16:27:32 -0700 Subject: [PATCH 59/82] - Fix incorrect assert that fires in the presence of non-collectible thread statics used within a class constructor - Fix GC hole caused by not reporting the intermediate steps of accessing non-collectible TLS variables - Remove spin lock usage kept around to ensure that FreeThreadStaticData didn't happen in parallel with ScanThreadStaticRoots. Instead make FreeThreadStaticData always happen in COOPERATIVE gc mode. (note, we still have a use of the spin lock in FreeThreadStaticData as it is used to ensure that it doesn't conflict with out of thread reads of thread static data. --- src/coreclr/jit/helperexpansion.cpp | 8 ++++---- src/coreclr/vm/ceemain.cpp | 14 ++++++++++++++ src/coreclr/vm/threadstatics.cpp | 17 ++++++++++++----- src/coreclr/vm/threadstatics.h | 1 + 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index db32f08fa2af1..b24c9c9a17411 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -999,7 +999,7 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* GenTree* threadStaticBlocksRef = gtNewOperNode(GT_ADD, TYP_I_IMPL, gtCloneExpr(tlsLclValueUse), gtNewIconNode(offsetOfThreadStaticBlocksVal, TYP_I_IMPL)); - threadStaticBlocksValue = gtNewIndir(TYP_I_IMPL, threadStaticBlocksRef, GTF_IND_NONFAULTING | GTF_IND_INVARIANT); + threadStaticBlocksValue = gtNewIndir(TYP_REF, threadStaticBlocksRef, GTF_IND_NONFAULTING | GTF_IND_INVARIANT); // Create tree for "if (maxThreadStaticBlocks < typeIndex)" GenTree* typeThreadStaticBlockIndexValue = call->gtArgs.GetArgByIndex(0)->GetNode(); @@ -1011,12 +1011,12 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* typeThreadStaticBlockIndexValue = gtNewOperNode(GT_MUL, TYP_INT, gtCloneExpr(typeThreadStaticBlockIndexValue), gtNewIconNode(TARGET_POINTER_SIZE, TYP_INT)); GenTree* typeThreadStaticBlockRef = - gtNewOperNode(GT_ADD, TYP_I_IMPL, threadStaticBlocksValue, typeThreadStaticBlockIndexValue); - GenTree* typeThreadStaticBlockValue = gtNewIndir(TYP_I_IMPL, typeThreadStaticBlockRef, GTF_IND_NONFAULTING); + gtNewOperNode(GT_ADD, TYP_BYREF, threadStaticBlocksValue, typeThreadStaticBlockIndexValue); + GenTree* typeThreadStaticBlockValue = gtNewIndir(TYP_BYREF, typeThreadStaticBlockRef, GTF_IND_NONFAULTING); // Cache the threadStaticBlock value unsigned threadStaticBlockBaseLclNum = lvaGrabTemp(true DEBUGARG("ThreadStaticBlockBase access")); - lvaTable[threadStaticBlockBaseLclNum].lvType = TYP_I_IMPL; + lvaTable[threadStaticBlockBaseLclNum].lvType = TYP_BYREF; GenTree* threadStaticBlockBaseDef = gtNewStoreLclVarNode(threadStaticBlockBaseLclNum, typeThreadStaticBlockValue); GenTree* threadStaticBlockBaseLclValueUse = gtNewLclVarNode(threadStaticBlockBaseLclNum); diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 07c6e97fe424a..f7a81faaeff56 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -1725,9 +1725,23 @@ struct TlsDestructionMonitor thread->m_pFrame = FRAME_TOP; GCX_COOP_NO_DTOR_END(); } +#ifdef _DEBUG + BOOL oldGCOnTransitionsOK = thread->m_GCOnTransitionsOK; + thread->m_GCOnTransitionsOK = FALSE; +#endif + GCX_COOP_NO_DTOR(); FreeThreadStaticData(&t_ThreadStatics, thread); + GCX_COOP_NO_DTOR_END(); +#ifdef _DEBUG + thread->m_GCOnTransitionsOK = oldGCOnTransitionsOK; +#endif thread->DetachThread(TRUE); } + else + { + // Since we don't actually cleanup the TLS data along this path, verify that it is already cleaned up + AssertThreadStaticDataFreed(&t_ThreadStatics); + } ThreadDetaching(); } diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 2ec6f45ba8d50..9d0bce990dd0f 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -133,7 +133,7 @@ bool ReportTLSIndexCarefully(TLSIndex index, int32_t cLoaderHandles, PTR_LOADERH bool isGCStatic; bool isCollectible; PTR_MethodTable pMT = LookupMethodTableAndFlagForThreadStatic(index, &isGCStatic, &isCollectible); - _ASSERTE(((pMT == NULL) || isCollectible) && index.GetTLSIndexType() == TLSIndexType::Collectible); + _ASSERTE(index.GetTLSIndexType() == TLSIndexType::NonCollectible || (((pMT == NULL) || isCollectible) && index.GetTLSIndexType() == TLSIndexType::Collectible)); if (index.GetTLSIndexType() == TLSIndexType::Collectible && pLoaderHandles[index.GetIndexOffset()] == NULL) { // The TLS index is not in use. This either means that the TLS index was never used, or that it was @@ -168,10 +168,6 @@ void ScanThreadStaticRoots(Thread* pThread, bool forGC, promote_func* fn, ScanCo } CONTRACTL_END; -#ifndef DACCESS_COMPILE - SpinLockHolder spinLock(&pThread->m_TlsSpinLock); -#endif // !DACCESS_COMPILE - if (forGC) { // Clean out LoaderHandles for dead collectible assemblies @@ -429,11 +425,22 @@ void FreeLoaderAllocatorHandlesForTLSData(Thread *pThread) } } +void AssertThreadStaticDataFreed(ThreadLocalData *pThreadLocalData) +{ + _ASSERTE(pThreadLocalData->pThread == NULL); + _ASSERTE(pThreadLocalData->pTLSArrayData == NULL); + _ASSERTE(pThreadLocalData->cTLSData == 0); + _ASSERTE(pThreadLocalData->pNonCollectibleTlsReferenceData == NULL); + _ASSERTE(pThreadLocalData->cNonCollectibleTlsData == 0); + _ASSERTE(pThreadLocalData->pInFlightData == NULL); +} + void FreeThreadStaticData(ThreadLocalData *pThreadLocalData, Thread* pThread) { CONTRACTL { NOTHROW; GC_NOTRIGGER; + MODE_COOPERATIVE; } CONTRACTL_END; diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index 701d37d0ba315..f51cd7fd53ab4 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -352,6 +352,7 @@ void InitializeThreadStaticData(); void InitializeCurrentThreadsStaticData(Thread* pThread); void FreeLoaderAllocatorHandlesForTLSData(Thread* pThread); void FreeThreadStaticData(ThreadLocalData *pThreadLocalData, Thread* pThread); +void AssertThreadStaticDataFreed(ThreadLocalData *pThreadLocalData); void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pIndex); void FreeTLSIndicesForLoaderAllocator(LoaderAllocator *pLoaderAllocator); void* GetThreadLocalStaticBase(TLSIndex index); From 53cd1f6a01b9a366d209d3ee3d2e72ffb06fc541 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 23 Apr 2024 11:48:03 -0700 Subject: [PATCH 60/82] Add some checks to try to avoid deadlocks on shutdown --- src/coreclr/vm/ceemain.cpp | 9 +++-- src/coreclr/vm/threads.cpp | 5 ++- src/coreclr/vm/threadstatics.cpp | 58 ++++++++++++++++++-------------- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index f7a81faaeff56..1da2c2ef5614f 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -1729,9 +1729,12 @@ struct TlsDestructionMonitor BOOL oldGCOnTransitionsOK = thread->m_GCOnTransitionsOK; thread->m_GCOnTransitionsOK = FALSE; #endif - GCX_COOP_NO_DTOR(); - FreeThreadStaticData(&t_ThreadStatics, thread); - GCX_COOP_NO_DTOR_END(); + if (!IsAtProcessExit() && !g_fEEShutDown) + { + GCX_COOP_NO_DTOR(); + FreeThreadStaticData(&t_ThreadStatics, thread); + GCX_COOP_NO_DTOR_END(); + } #ifdef _DEBUG thread->m_GCOnTransitionsOK = oldGCOnTransitionsOK; #endif diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index abaa3e17e6e0d..09af838ef7830 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -7721,7 +7721,10 @@ void Thread::DeleteThreadStaticData() CONTRACTL_END; FreeLoaderAllocatorHandlesForTLSData(this); - FreeThreadStaticData(m_ThreadLocalDataPtr, this); + if (!IsAtProcessExit() && !g_fEEShutDown) + { + FreeThreadStaticData(m_ThreadLocalDataPtr, this); + } } OBJECTREF Thread::GetCulture(BOOL bUICulture) diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 9d0bce990dd0f..8236fc71c34c3 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -427,12 +427,15 @@ void FreeLoaderAllocatorHandlesForTLSData(Thread *pThread) void AssertThreadStaticDataFreed(ThreadLocalData *pThreadLocalData) { - _ASSERTE(pThreadLocalData->pThread == NULL); - _ASSERTE(pThreadLocalData->pTLSArrayData == NULL); - _ASSERTE(pThreadLocalData->cTLSData == 0); - _ASSERTE(pThreadLocalData->pNonCollectibleTlsReferenceData == NULL); - _ASSERTE(pThreadLocalData->cNonCollectibleTlsData == 0); - _ASSERTE(pThreadLocalData->pInFlightData == NULL); + if (!IsAtProcessExit() && !g_fEEShutDown) + { + _ASSERTE(pThreadLocalData->pThread == NULL); + _ASSERTE(pThreadLocalData->pTLSArrayData == NULL); + _ASSERTE(pThreadLocalData->cTLSData == 0); + _ASSERTE(pThreadLocalData->pNonCollectibleTlsReferenceData == NULL); + _ASSERTE(pThreadLocalData->cNonCollectibleTlsData == 0); + _ASSERTE(pThreadLocalData->pInFlightData == NULL); + } } void FreeThreadStaticData(ThreadLocalData *pThreadLocalData, Thread* pThread) @@ -447,33 +450,36 @@ void FreeThreadStaticData(ThreadLocalData *pThreadLocalData, Thread* pThread) if (pThreadLocalData == NULL) return; - SpinLockHolder spinLock(&pThread->m_TlsSpinLock); - - if (pThreadLocalData->pThread == NULL) + if (!IsAtProcessExit() && !g_fEEShutDown) { - return; - } + SpinLockHolder spinLock(&pThread->m_TlsSpinLock); + + if (pThreadLocalData->pThread == NULL) + { + return; + } - pThreadLocalData = pThread->m_ThreadLocalDataPtr; + pThreadLocalData = pThread->m_ThreadLocalDataPtr; - if (pThreadLocalData == NULL) - return; + if (pThreadLocalData == NULL) + return; - delete[] (uint8_t*)pThreadLocalData->pTLSArrayData; + delete[] (uint8_t*)pThreadLocalData->pTLSArrayData; - pThreadLocalData->pTLSArrayData = 0; - pThreadLocalData->cTLSData = 0; - pThreadLocalData->pNonCollectibleTlsReferenceData = 0; - pThreadLocalData->cNonCollectibleTlsData = 0; + pThreadLocalData->pTLSArrayData = 0; + pThreadLocalData->cTLSData = 0; + pThreadLocalData->pNonCollectibleTlsReferenceData = 0; + pThreadLocalData->cNonCollectibleTlsData = 0; - while (pThreadLocalData->pInFlightData != NULL) - { - InFlightTLSData* pInFlightData = pThreadLocalData->pInFlightData; - pThreadLocalData->pInFlightData = pInFlightData->pNext; - delete pInFlightData; + while (pThreadLocalData->pInFlightData != NULL) + { + InFlightTLSData* pInFlightData = pThreadLocalData->pInFlightData; + pThreadLocalData->pInFlightData = pInFlightData->pNext; + delete pInFlightData; + } + pThreadLocalData->pThread->m_ThreadLocalDataPtr = NULL; + VolatileStoreWithoutBarrier(&pThreadLocalData->pThread, (Thread*)NULL); } - pThreadLocalData->pThread->m_ThreadLocalDataPtr = NULL; - VolatileStoreWithoutBarrier(&pThreadLocalData->pThread, (Thread*)NULL); } void SetTLSBaseValue(TADDR *ppTLSBaseAddress, TADDR pTLSBaseAddress, bool useGCBarrier) From cacb466e91c629f52b2b124a183061bac6cf005a Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 29 Apr 2024 16:59:35 -0700 Subject: [PATCH 61/82] Implement direct access to TLS data on the ThreadLocalData structure - Should fix ThreadBlockingInfo issue - Use for the Async perf improvement scenario not implemented. - Not tested yet. --- src/coreclr/inc/corinfo.h | 1 + src/coreclr/inc/jithelpers.h | 1 + src/coreclr/jit/compiler.hpp | 2 +- src/coreclr/jit/flowgraph.cpp | 9 +- src/coreclr/jit/helperexpansion.cpp | 286 ++++++++++++++++------------ src/coreclr/jit/importer.cpp | 2 +- src/coreclr/jit/utils.cpp | 1 + src/coreclr/jit/valuenum.cpp | 3 + src/coreclr/jit/valuenumfuncs.h | 1 + src/coreclr/vm/jithelpers.cpp | 28 +++ src/coreclr/vm/jitinterface.cpp | 9 + src/coreclr/vm/threadstatics.cpp | 59 +++++- src/coreclr/vm/threadstatics.h | 2 + 13 files changed, 274 insertions(+), 130 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index dca9981506343..760374fff312d 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -552,6 +552,7 @@ enum CorInfoHelpFunc CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR, CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, + CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2, /* Debugger */ diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h index a586d2f8edcec..b1c008c449d7c 100644 --- a/src/coreclr/inc/jithelpers.h +++ b/src/coreclr/inc/jithelpers.h @@ -202,6 +202,7 @@ JITHELPER(CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR,JIT_GetDynamicNonGCThreadStaticBase, CORINFO_HELP_SIG_REG_ONLY) JITHELPER(CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED,JIT_GetGCThreadStaticBaseOptimized, CORINFO_HELP_SIG_REG_ONLY) JITHELPER(CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED,JIT_GetNonGCThreadStaticBaseOptimized, CORINFO_HELP_SIG_REG_ONLY) + JITHELPER(CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2,JIT_GetNonGCThreadStaticBaseOptimized2, CORINFO_HELP_SIG_REG_ONLY) // Debugger JITHELPER(CORINFO_HELP_DBG_IS_JUST_MY_CODE, JIT_DbgIsJustMyCode,CORINFO_HELP_SIG_REG_ONLY) diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index e6af6680025be..2f6723f25e323 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -3772,7 +3772,7 @@ inline bool Compiler::IsSharedStaticHelper(GenTree* tree) helper == CORINFO_HELP_GETSTATICFIELDADDR_TLS || (helper >= CORINFO_HELP_GET_GCSTATIC_BASE && - helper <= CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) + helper <= CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2) #ifdef FEATURE_READYTORUN || helper == CORINFO_HELP_READYTORUN_GENERIC_STATIC_BASE || helper == CORINFO_HELP_READYTORUN_GCSTATIC_BASE || helper == CORINFO_HELP_READYTORUN_NONGCSTATIC_BASE || helper == CORINFO_HELP_READYTORUN_THREADSTATIC_BASE || diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 847c6aa07e3a4..a6a179b1b3430 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -679,8 +679,9 @@ bool Compiler::fgIsCommaThrow(GenTree* tree, bool forFolding /* = false */) // cls - The class handle // helper - The helper function // typeIndex - The static block type index. Used only for -// CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED or -// CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED to cache +// CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, +// CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, or +// CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2 to cache // the static block in an array at index typeIndex. // // Return Value: @@ -722,6 +723,7 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo case CORINFO_HELP_GETPINNED_GCSTATIC_BASE_NOCTOR: case CORINFO_HELP_GETPINNED_NONGCSTATIC_BASE_NOCTOR: + case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2: callFlags |= GTF_CALL_HOISTABLE; FALLTHROUGH; @@ -750,7 +752,8 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo GenTreeCall* result; if ((helper == CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) || - (helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED)) + (helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) || + (helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2)) { result = gtNewHelperCallNode(helper, type, gtNewIconNode(typeIndex)); } diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index b24c9c9a17411..d59e6a24b46c6 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -455,8 +455,9 @@ bool Compiler::fgExpandRuntimeLookupsForCall(BasicBlock** pBlock, Statement* stm } //------------------------------------------------------------------------------ -// fgExpandThreadLocalAccess: Inline the CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED -// or CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED helper. See +// fgExpandThreadLocalAccess: Inline the CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, +// CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, or +// CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZEDhelper. See // fgExpandThreadLocalAccessForCall for details. // // Returns: @@ -764,8 +765,8 @@ bool Compiler::fgExpandThreadLocalAccessForCallNativeAOT(BasicBlock** pBlock, St } //------------------------------------------------------------------------------ -// fgExpandThreadLocalAccessForCall : Expand the CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED -// or CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, that access fields marked with [ThreadLocal]. +// fgExpandThreadLocalAccessForCall : Expand the CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED +// or CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, that access fields marked with [ThreadLocal]. // // Arguments: // pBlock - Block containing the helper call to expand. If expansion is performed, @@ -795,7 +796,8 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* CorInfoHelpFunc helper = call->GetHelperNum(); if ((helper != CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) && - (helper != CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED)) + (helper != CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) && + (helper != CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2)) { return false; } @@ -987,139 +989,183 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* // Cache the tls value tlsValueDef = gtNewStoreLclVarNode(tlsLclNum, tlsValue); GenTree* tlsLclValueUse = gtNewLclVarNode(tlsLclNum); + GenTree* typeThreadStaticBlockIndexValue = call->gtArgs.GetArgByIndex(0)->GetNode(); - size_t offsetOfThreadStaticBlocksVal = threadStaticBlocksInfo.offsetOfThreadStaticBlocks; - size_t offsetOfMaxThreadStaticBlocksVal = threadStaticBlocksInfo.offsetOfMaxThreadStaticBlocks; + if (helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2) + { + // In this case we can entirely remove the block which uses the helper call, and just get the TLS pointer directly - // Create tree for "maxThreadStaticBlocks = tls[offsetOfMaxThreadStaticBlocks]" - GenTree* offsetOfMaxThreadStaticBlocks = gtNewIconNode(offsetOfMaxThreadStaticBlocksVal, TYP_I_IMPL); - GenTree* maxThreadStaticBlocksRef = - gtNewOperNode(GT_ADD, TYP_I_IMPL, gtCloneExpr(tlsLclValueUse), offsetOfMaxThreadStaticBlocks); - maxThreadStaticBlocksValue = gtNewIndir(TYP_INT, maxThreadStaticBlocksRef, GTF_IND_NONFAULTING | GTF_IND_INVARIANT); + // prevBb (BBJ_NONE): [weight: 1.0] + // ... + // + // tlsBaseComputeBB (BBJ_COND): [weight: 1.0] + // tlsRoot = [tlsRootAddress] + // + // block (...): [weight: 1.0] + // use(tlsRoot); + // ... - GenTree* threadStaticBlocksRef = gtNewOperNode(GT_ADD, TYP_I_IMPL, gtCloneExpr(tlsLclValueUse), - gtNewIconNode(offsetOfThreadStaticBlocksVal, TYP_I_IMPL)); - threadStaticBlocksValue = gtNewIndir(TYP_REF, threadStaticBlocksRef, GTF_IND_NONFAULTING | GTF_IND_INVARIANT); + GenTree* typeThreadStaticBlockIndexValue = call->gtArgs.GetArgByIndex(0)->GetNode(); + GenTree* threadStaticBase = gtNewOperNode(GT_ADD, TYP_I_IMPL, gtCloneExpr(tlsLclValueUse), gtCloneExpr(typeThreadStaticBlockIndexValue)); + GenTree* tlsStaticBaseStoreLcl = gtNewStoreLclVarNode(threadStaticBlockLclNum, threadStaticBase); + + BasicBlock* tlsBaseComputeBB = fgNewBBFromTreeAfter(BBJ_ALWAYS, prevBb, tlsValueDef, debugInfo, true); + fgInsertStmtAfter(tlsBaseComputeBB, tlsBaseComputeBB->firstStmt(), fgNewStmtFromTree(tlsStaticBaseStoreLcl)); - // Create tree for "if (maxThreadStaticBlocks < typeIndex)" - GenTree* typeThreadStaticBlockIndexValue = call->gtArgs.GetArgByIndex(0)->GetNode(); - GenTree* maxThreadStaticBlocksCond = - gtNewOperNode(GT_LE, TYP_INT, maxThreadStaticBlocksValue, gtCloneExpr(typeThreadStaticBlockIndexValue)); - maxThreadStaticBlocksCond = gtNewOperNode(GT_JTRUE, TYP_VOID, maxThreadStaticBlocksCond); - - // Create tree to "threadStaticBlockValue = threadStaticBlockBase[typeIndex]" - typeThreadStaticBlockIndexValue = gtNewOperNode(GT_MUL, TYP_INT, gtCloneExpr(typeThreadStaticBlockIndexValue), - gtNewIconNode(TARGET_POINTER_SIZE, TYP_INT)); - GenTree* typeThreadStaticBlockRef = - gtNewOperNode(GT_ADD, TYP_BYREF, threadStaticBlocksValue, typeThreadStaticBlockIndexValue); - GenTree* typeThreadStaticBlockValue = gtNewIndir(TYP_BYREF, typeThreadStaticBlockRef, GTF_IND_NONFAULTING); - - // Cache the threadStaticBlock value - unsigned threadStaticBlockBaseLclNum = lvaGrabTemp(true DEBUGARG("ThreadStaticBlockBase access")); - lvaTable[threadStaticBlockBaseLclNum].lvType = TYP_BYREF; - GenTree* threadStaticBlockBaseDef = gtNewStoreLclVarNode(threadStaticBlockBaseLclNum, typeThreadStaticBlockValue); - GenTree* threadStaticBlockBaseLclValueUse = gtNewLclVarNode(threadStaticBlockBaseLclNum); - - // Create tree for "if (threadStaticBlockValue != nullptr)" - GenTree* threadStaticBlockNullCond = - gtNewOperNode(GT_NE, TYP_INT, threadStaticBlockBaseLclValueUse, gtNewIconNode(0, TYP_I_IMPL)); - threadStaticBlockNullCond = gtNewOperNode(GT_JTRUE, TYP_VOID, threadStaticBlockNullCond); - - // prevBb (BBJ_ALWAYS): [weight: 1.0] - // ... - // - // maxThreadStaticBlocksCondBB (BBJ_COND): [weight: 1.0] - // tlsValue = tls_access_code - // if (maxThreadStaticBlocks <= typeIndex) - // goto fallbackBb; - // - // threadStaticBlockNullCondBB (BBJ_COND): [weight: 1.0] - // fastPathValue = t_threadStaticBlocks[typeIndex] - // if (fastPathValue != nullptr) - // goto fastPathBb; - // - // fallbackBb (BBJ_ALWAYS): [weight: 0] - // threadStaticBlockBase = HelperCall(); - // goto block; - // - // fastPathBb(BBJ_ALWAYS): [weight: 1.0] - // threadStaticBlockBase = fastPathValue; - // - // block (...): [weight: 1.0] - // use(threadStaticBlockBase); + // + // Update preds in all new blocks + // - // maxThreadStaticBlocksCondBB + FlowEdge* const newEdge = fgAddRefPred(block, tlsBaseComputeBB); + tlsBaseComputeBB->SetTargetEdge(newEdge); - // maxThreadStaticBlocksCondBB conditionally jumps to fallbackBb, but fallbackBb must be initialized last - // so it can be placed after it. So set the jump target later. - BasicBlock* maxThreadStaticBlocksCondBB = fgNewBBFromTreeAfter(BBJ_COND, prevBb, tlsValueDef, debugInfo); + assert(prevBb->KindIs(BBJ_ALWAYS)); + fgRedirectTargetEdge(prevBb, tlsBaseComputeBB); - fgInsertStmtAfter(maxThreadStaticBlocksCondBB, maxThreadStaticBlocksCondBB->firstStmt(), - fgNewStmtFromTree(maxThreadStaticBlocksCond)); + // Inherit the weights + block->inheritWeight(prevBb); + tlsBaseComputeBB->inheritWeight(prevBb); - // Similarly, set threadStaticBlockNulLCondBB to jump to fastPathBb once the latter exists. - BasicBlock* threadStaticBlockNullCondBB = - fgNewBBFromTreeAfter(BBJ_COND, maxThreadStaticBlocksCondBB, threadStaticBlockBaseDef, debugInfo); - fgInsertStmtAfter(threadStaticBlockNullCondBB, threadStaticBlockNullCondBB->firstStmt(), - fgNewStmtFromTree(threadStaticBlockNullCond)); + // All blocks are expected to be in the same EH region + assert(BasicBlock::sameEHRegion(prevBb, block)); + assert(BasicBlock::sameEHRegion(prevBb, tlsBaseComputeBB)); - // fallbackBb - GenTree* fallbackValueDef = gtNewStoreLclVarNode(threadStaticBlockLclNum, call); - BasicBlock* fallbackBb = - fgNewBBFromTreeAfter(BBJ_ALWAYS, threadStaticBlockNullCondBB, fallbackValueDef, debugInfo, true); + JITDUMP("tlsBaseComputeBB: " FMT_BB "\n", tlsBaseComputeBB->bbNum); + } + else + { + size_t offsetOfThreadStaticBlocksVal = threadStaticBlocksInfo.offsetOfThreadStaticBlocks; + size_t offsetOfMaxThreadStaticBlocksVal = threadStaticBlocksInfo.offsetOfMaxThreadStaticBlocks; + + // Create tree for "maxThreadStaticBlocks = tls[offsetOfMaxThreadStaticBlocks]" + GenTree* offsetOfMaxThreadStaticBlocks = gtNewIconNode(offsetOfMaxThreadStaticBlocksVal, TYP_I_IMPL); + GenTree* maxThreadStaticBlocksRef = + gtNewOperNode(GT_ADD, TYP_I_IMPL, gtCloneExpr(tlsLclValueUse), offsetOfMaxThreadStaticBlocks); + maxThreadStaticBlocksValue = gtNewIndir(TYP_INT, maxThreadStaticBlocksRef, GTF_IND_NONFAULTING | GTF_IND_INVARIANT); + + GenTree* threadStaticBlocksRef = gtNewOperNode(GT_ADD, TYP_I_IMPL, gtCloneExpr(tlsLclValueUse), + gtNewIconNode(offsetOfThreadStaticBlocksVal, TYP_I_IMPL)); + threadStaticBlocksValue = gtNewIndir(TYP_REF, threadStaticBlocksRef, GTF_IND_NONFAULTING | GTF_IND_INVARIANT); + + // Create tree for "if (maxThreadStaticBlocks < typeIndex)" + GenTree* maxThreadStaticBlocksCond = + gtNewOperNode(GT_LE, TYP_INT, maxThreadStaticBlocksValue, gtCloneExpr(typeThreadStaticBlockIndexValue)); + maxThreadStaticBlocksCond = gtNewOperNode(GT_JTRUE, TYP_VOID, maxThreadStaticBlocksCond); + + // Create tree to "threadStaticBlockValue = threadStaticBlockBase[typeIndex]" + typeThreadStaticBlockIndexValue = gtNewOperNode(GT_MUL, TYP_INT, gtCloneExpr(typeThreadStaticBlockIndexValue), + gtNewIconNode(TARGET_POINTER_SIZE, TYP_INT)); + GenTree* typeThreadStaticBlockRef = + gtNewOperNode(GT_ADD, TYP_BYREF, threadStaticBlocksValue, typeThreadStaticBlockIndexValue); + GenTree* typeThreadStaticBlockValue = gtNewIndir(TYP_BYREF, typeThreadStaticBlockRef, GTF_IND_NONFAULTING); + + // Cache the threadStaticBlock value + unsigned threadStaticBlockBaseLclNum = lvaGrabTemp(true DEBUGARG("ThreadStaticBlockBase access")); + lvaTable[threadStaticBlockBaseLclNum].lvType = TYP_BYREF; + GenTree* threadStaticBlockBaseDef = gtNewStoreLclVarNode(threadStaticBlockBaseLclNum, typeThreadStaticBlockValue); + GenTree* threadStaticBlockBaseLclValueUse = gtNewLclVarNode(threadStaticBlockBaseLclNum); + + // Create tree for "if (threadStaticBlockValue != nullptr)" + GenTree* threadStaticBlockNullCond = + gtNewOperNode(GT_NE, TYP_INT, threadStaticBlockBaseLclValueUse, gtNewIconNode(0, TYP_I_IMPL)); + threadStaticBlockNullCond = gtNewOperNode(GT_JTRUE, TYP_VOID, threadStaticBlockNullCond); + + // prevBb (BBJ_ALWAYS): [weight: 1.0] + // ... + // + // maxThreadStaticBlocksCondBB (BBJ_COND): [weight: 1.0] + // tlsValue = tls_access_code + // if (maxThreadStaticBlocks <= typeIndex) + // goto fallbackBb; + // + // threadStaticBlockNullCondBB (BBJ_COND): [weight: 1.0] + // fastPathValue = t_threadStaticBlocks[typeIndex] + // if (fastPathValue != nullptr) + // goto fastPathBb; + // + // fallbackBb (BBJ_ALWAYS): [weight: 0] + // threadStaticBlockBase = HelperCall(); + // goto block; + // + // fastPathBb(BBJ_ALWAYS): [weight: 1.0] + // threadStaticBlockBase = fastPathValue; + // + // block (...): [weight: 1.0] + // use(threadStaticBlockBase); - GenTree* fastPathValueDef = - gtNewStoreLclVarNode(threadStaticBlockLclNum, gtCloneExpr(threadStaticBlockBaseLclValueUse)); - BasicBlock* fastPathBb = fgNewBBFromTreeAfter(BBJ_ALWAYS, fallbackBb, fastPathValueDef, debugInfo, true); + // maxThreadStaticBlocksCondBB - // - // Update preds in all new blocks - // - assert(prevBb->KindIs(BBJ_ALWAYS)); - fgRedirectTargetEdge(prevBb, maxThreadStaticBlocksCondBB); + // maxThreadStaticBlocksCondBB conditionally jumps to fallbackBb, but fallbackBb must be initialized last + // so it can be placed after it. So set the jump target later. + BasicBlock* maxThreadStaticBlocksCondBB = fgNewBBFromTreeAfter(BBJ_COND, prevBb, tlsValueDef, debugInfo); - { - FlowEdge* const trueEdge = fgAddRefPred(fallbackBb, maxThreadStaticBlocksCondBB); - FlowEdge* const falseEdge = fgAddRefPred(threadStaticBlockNullCondBB, maxThreadStaticBlocksCondBB); - maxThreadStaticBlocksCondBB->SetTrueEdge(trueEdge); - maxThreadStaticBlocksCondBB->SetFalseEdge(falseEdge); - trueEdge->setLikelihood(0.0); - falseEdge->setLikelihood(1.0); - } + fgInsertStmtAfter(maxThreadStaticBlocksCondBB, maxThreadStaticBlocksCondBB->firstStmt(), + fgNewStmtFromTree(maxThreadStaticBlocksCond)); - { - FlowEdge* const trueEdge = fgAddRefPred(fastPathBb, threadStaticBlockNullCondBB); - FlowEdge* const falseEdge = fgAddRefPred(fallbackBb, threadStaticBlockNullCondBB); - threadStaticBlockNullCondBB->SetTrueEdge(trueEdge); - threadStaticBlockNullCondBB->SetFalseEdge(falseEdge); - trueEdge->setLikelihood(1.0); - falseEdge->setLikelihood(0.0); - } + // Similarly, set threadStaticBlockNulLCondBB to jump to fastPathBb once the latter exists. + BasicBlock* threadStaticBlockNullCondBB = + fgNewBBFromTreeAfter(BBJ_COND, maxThreadStaticBlocksCondBB, threadStaticBlockBaseDef, debugInfo); + fgInsertStmtAfter(threadStaticBlockNullCondBB, threadStaticBlockNullCondBB->firstStmt(), + fgNewStmtFromTree(threadStaticBlockNullCond)); - { - FlowEdge* const newEdge = fgAddRefPred(block, fastPathBb); - fastPathBb->SetTargetEdge(newEdge); - } + // fallbackBb + GenTree* fallbackValueDef = gtNewStoreLclVarNode(threadStaticBlockLclNum, call); + BasicBlock* fallbackBb = + fgNewBBFromTreeAfter(BBJ_ALWAYS, threadStaticBlockNullCondBB, fallbackValueDef, debugInfo, true); - { - FlowEdge* const newEdge = fgAddRefPred(block, fallbackBb); - fallbackBb->SetTargetEdge(newEdge); - } + GenTree* fastPathValueDef = + gtNewStoreLclVarNode(threadStaticBlockLclNum, gtCloneExpr(threadStaticBlockBaseLclValueUse)); + BasicBlock* fastPathBb = fgNewBBFromTreeAfter(BBJ_ALWAYS, fallbackBb, fastPathValueDef, debugInfo, true); - // Inherit the weights - block->inheritWeight(prevBb); - maxThreadStaticBlocksCondBB->inheritWeight(prevBb); - threadStaticBlockNullCondBB->inheritWeight(prevBb); - fastPathBb->inheritWeight(prevBb); + // + // Update preds in all new blocks + // + assert(prevBb->KindIs(BBJ_ALWAYS)); + fgRedirectTargetEdge(prevBb, maxThreadStaticBlocksCondBB); - // fallback will just execute first time - fallbackBb->bbSetRunRarely(); + { + FlowEdge* const trueEdge = fgAddRefPred(fallbackBb, maxThreadStaticBlocksCondBB); + FlowEdge* const falseEdge = fgAddRefPred(threadStaticBlockNullCondBB, maxThreadStaticBlocksCondBB); + maxThreadStaticBlocksCondBB->SetTrueEdge(trueEdge); + maxThreadStaticBlocksCondBB->SetFalseEdge(falseEdge); + trueEdge->setLikelihood(0.0); + falseEdge->setLikelihood(1.0); + } - // All blocks are expected to be in the same EH region - assert(BasicBlock::sameEHRegion(prevBb, block)); - assert(BasicBlock::sameEHRegion(prevBb, maxThreadStaticBlocksCondBB)); - assert(BasicBlock::sameEHRegion(prevBb, threadStaticBlockNullCondBB)); - assert(BasicBlock::sameEHRegion(prevBb, fastPathBb)); + { + FlowEdge* const trueEdge = fgAddRefPred(fastPathBb, threadStaticBlockNullCondBB); + FlowEdge* const falseEdge = fgAddRefPred(fallbackBb, threadStaticBlockNullCondBB); + threadStaticBlockNullCondBB->SetTrueEdge(trueEdge); + threadStaticBlockNullCondBB->SetFalseEdge(falseEdge); + trueEdge->setLikelihood(1.0); + falseEdge->setLikelihood(0.0); + } + + { + FlowEdge* const newEdge = fgAddRefPred(block, fastPathBb); + fastPathBb->SetTargetEdge(newEdge); + } + + { + FlowEdge* const newEdge = fgAddRefPred(block, fallbackBb); + fallbackBb->SetTargetEdge(newEdge); + } + + // Inherit the weights + block->inheritWeight(prevBb); + maxThreadStaticBlocksCondBB->inheritWeight(prevBb); + threadStaticBlockNullCondBB->inheritWeight(prevBb); + fastPathBb->inheritWeight(prevBb); + + // fallback will just execute first time + fallbackBb->bbSetRunRarely(); + + // All blocks are expected to be in the same EH region + assert(BasicBlock::sameEHRegion(prevBb, block)); + assert(BasicBlock::sameEHRegion(prevBb, maxThreadStaticBlocksCondBB)); + assert(BasicBlock::sameEHRegion(prevBb, threadStaticBlockNullCondBB)); + assert(BasicBlock::sameEHRegion(prevBb, fastPathBb)); + } return true; } diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 685b7e3aa97d8..bd3c284c6a848 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -3826,7 +3826,7 @@ GenTree* Compiler::impImportStaticFieldAddress(CORINFO_RESOLVED_TOKEN* pResolved if (!opts.IsReadyToRun()) #endif // FEATURE_READYTORUN { - if (pFieldInfo->helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) + if ((pFieldInfo->helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) || (pFieldInfo->helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2)) { typeIndex = info.compCompHnd->getThreadLocalFieldInfo(pResolvedToken->hField, false); } diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index 679dfee71b05e..f9fa2530a996d 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -1740,6 +1740,7 @@ void HelperCallProperties::init() case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR: case CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED: case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED: + case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2: case CORINFO_HELP_READYTORUN_THREADSTATIC_BASE_NOCTOR: // These do not invoke static class constructors diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index b001f56f6e612..281dab4706826 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -12999,6 +12999,9 @@ VNFunc Compiler::fgValueNumberJitHelperMethodVNFunc(CorInfoHelpFunc helpFunc) case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED: vnf = VNF_GetdynamicNongcthreadstaticBaseNoctorOptimized; break; + case CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2: + vnf = VNF_GetdynamicNongcthreadstaticBaseNoctorOptimized2; + break; case CORINFO_HELP_GETSTATICFIELDADDR_TLS: vnf = VNF_GetStaticAddrTLS; break; diff --git a/src/coreclr/jit/valuenumfuncs.h b/src/coreclr/jit/valuenumfuncs.h index 3942e1197e180..869cce00c9c3c 100644 --- a/src/coreclr/jit/valuenumfuncs.h +++ b/src/coreclr/jit/valuenumfuncs.h @@ -141,6 +141,7 @@ ValueNumFuncDef(GetdynamicGcthreadstaticBaseNoctor, 1, false, true, true, false) ValueNumFuncDef(GetdynamicGcthreadstaticBaseNoctorOptimized, 1, false, true, true, false) ValueNumFuncDef(GetdynamicNongcthreadstaticBaseNoctor, 1, false, true, true, false) ValueNumFuncDef(GetdynamicNongcthreadstaticBaseNoctorOptimized, 1, false, true, true, false) +ValueNumFuncDef(GetdynamicNongcthreadstaticBaseNoctorOptimized2, 1, false, true, true, false) ValueNumFuncDef(RuntimeHandleMethod, 2, false, true, false, false) ValueNumFuncDef(RuntimeHandleClass, 2, false, true, false, false) diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 1efa17991d82c..4a131c14d26f9 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1418,6 +1418,10 @@ FORCEINLINE void* GetThreadLocalStaticBaseIfExistsAndInitialized(TLSIndex index) } pTLSBaseAddress = (TADDR)OBJECTREFToObject(tlsArray->GetAt(index.GetIndexOffset() - NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY)); } + else if (index.GetTLSIndexType() == TLSIndexType::DirectOnThreadLocalData) + { + return ((BYTE*)&t_ThreadStatics) + index.GetIndexOffset(); + } else { int32_t cTLSData = t_ThreadStatics.cTLSData; @@ -1544,6 +1548,30 @@ HCIMPL1(void*, JIT_GetNonGCThreadStaticBaseOptimized, UINT32 staticBlockIndex) } HCIMPLEND +// *** This helper corresponds CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2. +// Even though we always check if the class constructor has been run, we have a separate +// helper ID for the "no ctor" version because it allows the JIT to do some reordering that +// otherwise wouldn't be possible. +HCIMPL1(void*, JIT_GetNonGCThreadStaticBaseOptimized2, UINT32 staticBlockIndex) +{ + void* staticBlock = nullptr; + + FCALL_CONTRACT; + + HELPER_METHOD_FRAME_BEGIN_RET_0(); // Set up a frame + TLSIndex tlsIndex(TLSIndexType::DirectOnThreadLocalData, staticBlockIndex); + // Check if the class constructor needs to be run + MethodTable *pMT = LookupMethodTableForThreadStaticKnownToBeAllocated(tlsIndex); + pMT->CheckRunClassInitThrowing(); + + // Lookup the non-GC statics base pointer + staticBlock = (void*) pMT->GetNonGCThreadStaticsBasePointer(); + HELPER_METHOD_FRAME_END(); + + return staticBlock; +} +HCIMPLEND + #include // *** This helper corresponds to both CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE and diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 7b070019447e0..519424f15ddfb 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -1511,6 +1511,15 @@ void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken, { fieldAccessor = CORINFO_FIELD_STATIC_TLS_MANAGED; pResult->helper = CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED; + if (pFieldMT->GetModule()->IsSystem()) + { + // Check for highly optimized DirectOnThreadLocalData case + pFieldMT->EnsureTlsIndexAllocated(); + if (pFieldMT->GetThreadStaticsInfo()->NonGCTlsIndex.GetTLSIndexType() == TLSIndexType::DirectOnThreadLocalData) + { + pResult->helper = CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2; + } + } } else if ((pResult->helper == CORINFO_HELP_GET_GCTHREADSTATIC_BASE_NOCTOR) || (pResult->helper == CORINFO_HELP_GET_GCTHREADSTATIC_BASE) || diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 8236fc71c34c3..667c95d215bf3 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -30,7 +30,7 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread* pThread, TLSIndex index) SpinLockHolder spinLock(&pThread->m_TlsSpinLock); #endif - ThreadLocalData* pThreadLocalData = pThread->GetThreadLocalDataPtr(); + PTR_ThreadLocalData pThreadLocalData = pThread->GetThreadLocalDataPtr(); if (pThreadLocalData == NULL) return NULL; @@ -44,6 +44,10 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread* pThread, TLSIndex index) } pTLSBaseAddress = dac_cast(OBJECTREFToObject(tlsArray->GetAt(index.GetIndexOffset() - NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY))); } + else if (index.GetTLSIndexType() == TLSIndexType::DirectOnThreadLocalData) + { + return dac_cast((dac_cast(pThreadLocalData)) + index.GetIndexOffset()); + } else { int32_t cTLSData = pThreadLocalData->cTLSData; @@ -89,6 +93,17 @@ PTR_MethodTable LookupMethodTableForThreadStaticKnownToBeAllocated(TLSIndex inde { return g_pThreadStaticNonCollectibleTypeIndices->LookupTlsIndexKnownToBeAllocated(index); } + else if (index.GetTLSIndexType() == TLSIndexType::DirectOnThreadLocalData) + { + if (index.GetIndexOffset() == offsetof(ThreadLocalData, ThreadBlockingInfo_First)) + { + return CoreLibBinder::GetClass(CLASS__THREAD_BLOCKING_INFO); + } + else + { + return NULL; + } + } else { return g_pThreadStaticCollectibleTypeIndices->LookupTlsIndexKnownToBeAllocated(index); @@ -112,6 +127,19 @@ PTR_MethodTable LookupMethodTableAndFlagForThreadStatic(TLSIndex index, bool *pI { retVal = g_pThreadStaticNonCollectibleTypeIndices->Lookup(index, pIsGCStatic, pIsCollectible); } + else if (index.GetTLSIndexType() == TLSIndexType::DirectOnThreadLocalData) + { + *pIsGCStatic = false; + *pIsCollectible = false; + if (index.GetIndexOffset() == offsetof(ThreadLocalData, ThreadBlockingInfo_First)) + { + retVal = CoreLibBinder::GetClass(CLASS__THREAD_BLOCKING_INFO); + } + else + { + retVal = NULL; + } + } else { retVal = g_pThreadStaticCollectibleTypeIndices->Lookup(index, pIsGCStatic, pIsCollectible); @@ -132,6 +160,10 @@ bool ReportTLSIndexCarefully(TLSIndex index, int32_t cLoaderHandles, PTR_LOADERH CONTRACTL_END; bool isGCStatic; bool isCollectible; + + if (index.GetTLSIndexType() == TLSIndexType::DirectOnThreadLocalData) + return true; + PTR_MethodTable pMT = LookupMethodTableAndFlagForThreadStatic(index, &isGCStatic, &isCollectible); _ASSERTE(index.GetTLSIndexType() == TLSIndexType::NonCollectible || (((pMT == NULL) || isCollectible) && index.GetTLSIndexType() == TLSIndexType::Collectible)); if (index.GetTLSIndexType() == TLSIndexType::Collectible && pLoaderHandles[index.GetIndexOffset()] == NULL) @@ -536,6 +568,15 @@ void* GetThreadLocalStaticBase(TLSIndex index) } gcBaseAddresses.ppTLSBaseAddress = (TADDR*)(tlsArray->GetDataPtr() + (index.GetIndexOffset() - NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY)) ; useWriteBarrierToWriteTLSBase = true; + gcBaseAddresses.pTLSBaseAddress = *gcBaseAddresses.ppTLSBaseAddress; + } + else if (index.GetTLSIndexType() == TLSIndexType::DirectOnThreadLocalData) + { + // All of the current cases only require a single pointer sized field + _ASSERTE(pMT->GetClass()->GetNonGCThreadStaticFieldBytes() == sizeof(TADDR)); + _ASSERTE(!isGCStatic); + _ASSERTE(!isCollectible); + gcBaseAddresses.pTLSBaseAddress = ((TADDR)&t_ThreadStatics) + index.GetIndexOffset(); } else { @@ -575,8 +616,8 @@ void* GetThreadLocalStaticBase(TLSIndex index) TADDR pTLSArrayData = t_ThreadStatics.pTLSArrayData; gcBaseAddresses.ppTLSBaseAddress = reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetIndexOffset()); + gcBaseAddresses.pTLSBaseAddress = *gcBaseAddresses.ppTLSBaseAddress; } - gcBaseAddresses.pTLSBaseAddress = *gcBaseAddresses.ppTLSBaseAddress; if (gcBaseAddresses.pTLSBaseAddress == NULL) { @@ -686,9 +727,17 @@ void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pInde if (!pMT->Collectible()) { - uint32_t tlsRawIndex = g_NextNonCollectibleTLSSlot++; - newTLSIndex = TLSIndex(TLSIndexType::NonCollectible, tlsRawIndex); - g_pThreadStaticNonCollectibleTypeIndices->Set(newTLSIndex, pMT, gcStatic); + if (!gcStatic && pMT == CoreLibBinder::GetClassIfExist(CLASS__THREAD_BLOCKING_INFO)) + { + _ASSERTE(!pMT->HasClassConstructor()); // The DirectOnThreadLocalData scenario is not supported for types with class constructors + newTLSIndex = TLSIndex(TLSIndexType::DirectOnThreadLocalData, offsetof(ThreadLocalData, ThreadBlockingInfo_First)); + } + else + { + uint32_t tlsRawIndex = g_NextNonCollectibleTLSSlot++; + newTLSIndex = TLSIndex(TLSIndexType::NonCollectible, tlsRawIndex); + g_pThreadStaticNonCollectibleTypeIndices->Set(newTLSIndex, pMT, gcStatic); + } } else { diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index f51cd7fd53ab4..53959b9c204c6 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -94,6 +94,7 @@ enum class TLSIndexType { NonCollectible, // IndexOffset for this form of TLSIndex is scaled by sizeof(OBJECTREF) and used as an index into the array at ThreadLocalData::pNonCollectibleTlsReferenceData to get the final address Collectible, // IndexOffset for this form of TLSIndex is scaled by sizeof(void*) and then added to ThreadLocalData::pTLSArrayData to get the final address + DirectOnThreadLocalData, // IndexOffset for this form of TLS index is an offset into the ThreadLocalData structure itself. This is used for very high performance scenarios, and scenario where the runtime native code needs to hold a TLS pointer to a managed TLS slot. Each one of these is hand-opted into this model. }; struct TLSIndex @@ -122,6 +123,7 @@ struct ThreadLocalData TADDR pTLSArrayData; // Points at the Thread local array data. Thread *pThread; PTR_InFlightTLSData pInFlightData; // Points at the in-flight TLS data (TLS data that exists before the class constructor finishes running) + TADDR ThreadBlockingInfo_First; // System.Threading.ThreadBlockingInfo.First }; typedef DPTR(ThreadLocalData) PTR_ThreadLocalData; From 10531658e5bf1694eb1bbb056050463857b54576 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 30 Apr 2024 10:58:49 -0700 Subject: [PATCH 62/82] Fix missing enum value --- src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs index 4b474fce1c181..e184994c20d0a 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs @@ -194,6 +194,7 @@ which is the right helper to use to allocate an object of a given type. */ CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR, CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, + CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2, /* Debugger */ CORINFO_HELP_DBG_IS_JUST_MY_CODE, // Check if this is "JustMyCode" and needs to be stepped through. From 43abbf31b2bafeb6e32a81587a208f45643fa7f6 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 30 Apr 2024 14:56:01 -0700 Subject: [PATCH 63/82] DynamicallyAllocatedThreadLocalData_data --- src/coreclr/vm/jitinterface.cpp | 12 ++--- src/coreclr/vm/methodtable.cpp | 21 +++++++-- src/coreclr/vm/threaddebugblockinginfo.cpp | 34 ++++++-------- src/coreclr/vm/threadstatics.cpp | 54 +++++++++++++--------- src/coreclr/vm/threadstatics.h | 15 +++++- 5 files changed, 82 insertions(+), 54 deletions(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 519424f15ddfb..480784e1fbc1a 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -1511,14 +1511,12 @@ void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken, { fieldAccessor = CORINFO_FIELD_STATIC_TLS_MANAGED; pResult->helper = CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED; - if (pFieldMT->GetModule()->IsSystem()) + + // Check for highly optimized DirectOnThreadLocalData case + pFieldMT->EnsureTlsIndexAllocated(); + if (pFieldMT->GetThreadStaticsInfo()->NonGCTlsIndex.GetTLSIndexType() == TLSIndexType::DirectOnThreadLocalData) { - // Check for highly optimized DirectOnThreadLocalData case - pFieldMT->EnsureTlsIndexAllocated(); - if (pFieldMT->GetThreadStaticsInfo()->NonGCTlsIndex.GetTLSIndexType() == TLSIndexType::DirectOnThreadLocalData) - { - pResult->helper = CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2; - } + pResult->helper = CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2; } } else if ((pResult->helper == CORINFO_HELP_GET_GCTHREADSTATIC_BASE_NOCTOR) || diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 7d3dc874eb266..a48ed9fb85544 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -4396,11 +4396,24 @@ void MethodTable::EnsureTlsIndexAllocated() { ThreadStaticsInfo *pThreadStaticsInfo = MethodTableAuxiliaryData::GetThreadStaticsInfo(GetAuxiliaryDataForWrite()); // Allocate space for normal statics if we might have them - if (!pThreadStaticsInfo->NonGCTlsIndex.IsAllocated() && GetClass()->GetNonGCThreadStaticFieldBytes() > 0) - GetTLSIndexForThreadStatic(this, false, &pThreadStaticsInfo->NonGCTlsIndex); + if (!pThreadStaticsInfo->NonGCTlsIndex.IsAllocated()) + { + DWORD bytesNeeded = GetClass()->GetNonGCThreadStaticFieldBytes(); + if (bytesNeeded > 0) + { + GetTLSIndexForThreadStatic(this, false, &pThreadStaticsInfo->NonGCTlsIndex, bytesNeeded); + } + } - if (!pThreadStaticsInfo->GCTlsIndex.IsAllocated() && GetClass()->GetNumHandleThreadStatics() > 0) - GetTLSIndexForThreadStatic(this, true, &pThreadStaticsInfo->GCTlsIndex); + if (!pThreadStaticsInfo->GCTlsIndex.IsAllocated()) + { + DWORD bytesNeeded = GetClass()->GetNumHandleThreadStatics() * sizeof(OBJECTREF); + if (bytesNeeded > 0) + { + GetTLSIndexForThreadStatic(this, true, &pThreadStaticsInfo->GCTlsIndex, bytesNeeded); + } + } + } pAuxiliaryData->SetIsTlsIndexAllocated(); } diff --git a/src/coreclr/vm/threaddebugblockinginfo.cpp b/src/coreclr/vm/threaddebugblockinginfo.cpp index 8f4f3831b3518..85948e48921fd 100644 --- a/src/coreclr/vm/threaddebugblockinginfo.cpp +++ b/src/coreclr/vm/threaddebugblockinginfo.cpp @@ -82,26 +82,26 @@ DebugBlockingItemHolder::DebugBlockingItemHolder(Thread *pThread, DebugBlockingI } CONTRACTL_END; - // Try to get the address of the thread-local slot for the managed ThreadBlockingInfo.t_first + m_ppFirstBlockingInfo = (ThreadBlockingInfo**)&t_ThreadStatics.ThreadBlockingInfo_First; +#ifdef _DEBUG + // Try to verify the address of the thread-local slot for the managed ThreadBlockingInfo.t_first matches the address of the native thread static EX_TRY { FieldDesc *pFD = CoreLibBinder::GetField(FIELD__THREAD_BLOCKING_INFO__FIRST); - m_ppFirstBlockingInfo = (ThreadBlockingInfo **)Thread::GetStaticFieldAddress(pFD); + _ASSERTE(m_ppFirstBlockingInfo == (ThreadBlockingInfo **)Thread::GetStaticFieldAddress(pFD)); } EX_CATCH { } EX_END_CATCH(RethrowTerminalExceptions); +#endif - if (m_ppFirstBlockingInfo != nullptr) - { - // Push info for the managed ThreadBlockingInfo - m_blockingInfo.objectPtr = pItem->pMonitor; - m_blockingInfo.objectKind = (ThreadBlockingInfo::ObjectKind)pItem->type; - m_blockingInfo.timeoutMs = (INT32)pItem->dwTimeout; - m_blockingInfo.next = *m_ppFirstBlockingInfo; - *m_ppFirstBlockingInfo = &m_blockingInfo; - } + // Push info for the managed ThreadBlockingInfo + m_blockingInfo.objectPtr = pItem->pMonitor; + m_blockingInfo.objectKind = (ThreadBlockingInfo::ObjectKind)pItem->type; + m_blockingInfo.timeoutMs = (INT32)pItem->dwTimeout; + m_blockingInfo.next = *m_ppFirstBlockingInfo; + *m_ppFirstBlockingInfo = &m_blockingInfo; pThread->DebugBlockingInfo.PushBlockingItem(pItem); } @@ -115,14 +115,8 @@ DebugBlockingItemHolder::~DebugBlockingItemHolder() m_pThread->DebugBlockingInfo.PopBlockingItem(); - if (m_ppFirstBlockingInfo != nullptr) - { - // Pop info for the managed ThreadBlockingInfo - _ASSERTE( - m_ppFirstBlockingInfo == - (void *)m_pThread->GetStaticFieldAddrNoCreate(CoreLibBinder::GetField(FIELD__THREAD_BLOCKING_INFO__FIRST))); - _ASSERTE(*m_ppFirstBlockingInfo == &m_blockingInfo); - *m_ppFirstBlockingInfo = m_blockingInfo.next; - } + // Pop info for the managed ThreadBlockingInfo + _ASSERTE(*m_ppFirstBlockingInfo == &m_blockingInfo); + *m_ppFirstBlockingInfo = m_blockingInfo.next; } #endif //DACCESS_COMPILE diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 667c95d215bf3..99e25763d1ecb 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -78,6 +78,19 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread* pThread, TLSIndex index) TLSIndexToMethodTableMap *g_pThreadStaticCollectibleTypeIndices; TLSIndexToMethodTableMap *g_pThreadStaticNonCollectibleTypeIndices; +PTR_MethodTable g_pMethodTablesForDirectThreadLocalData[MAX_DIRECT_THREAD_LOCAL_COUNT]; + +int32_t IndexOffsetToDirectThreadLocalIndex(int32_t indexOffset) +{ + LIMITED_METHOD_CONTRACT; + int32_t adjustedIndexOffset = indexOffset + OFFSETOF__CORINFO_Array__data; + _ASSERTE(adjustedIndexOffset >= offsetof(ThreadLocalData, ThreadBlockingInfo_First)); + _ASSERTE((adjustedIndexOffset & (sizeof(DIRECT_THREAD_LOCAL_CHUNK_TYPE) - 1)) == 0); + _ASSERTE(offsetof(ThreadLocalData, ExtendedDirectThreadLocalTLSData) - offsetof(ThreadLocalData, ThreadBlockingInfo_First) == sizeof(DIRECT_THREAD_LOCAL_CHUNK_TYPE) * HARDCODED_DIRECT_THREAD_LOCAL_TLS_INDICES_USED); + int32_t directThreadLocalIndex = (adjustedIndexOffset - offsetof(ThreadLocalData, ThreadBlockingInfo_First)) / sizeof(DIRECT_THREAD_LOCAL_CHUNK_TYPE); + _ASSERTE(directThreadLocalIndex < MAX_DIRECT_THREAD_LOCAL_COUNT); + return directThreadLocalIndex; +} PTR_MethodTable LookupMethodTableForThreadStaticKnownToBeAllocated(TLSIndex index) { @@ -95,14 +108,7 @@ PTR_MethodTable LookupMethodTableForThreadStaticKnownToBeAllocated(TLSIndex inde } else if (index.GetTLSIndexType() == TLSIndexType::DirectOnThreadLocalData) { - if (index.GetIndexOffset() == offsetof(ThreadLocalData, ThreadBlockingInfo_First)) - { - return CoreLibBinder::GetClass(CLASS__THREAD_BLOCKING_INFO); - } - else - { - return NULL; - } + return VolatileLoadWithoutBarrier(&g_pMethodTablesForDirectThreadLocalData[IndexOffsetToDirectThreadLocalIndex(index.GetIndexOffset())]); } else { @@ -131,14 +137,7 @@ PTR_MethodTable LookupMethodTableAndFlagForThreadStatic(TLSIndex index, bool *pI { *pIsGCStatic = false; *pIsCollectible = false; - if (index.GetIndexOffset() == offsetof(ThreadLocalData, ThreadBlockingInfo_First)) - { - retVal = CoreLibBinder::GetClass(CLASS__THREAD_BLOCKING_INFO); - } - else - { - retVal = NULL; - } + retVal = g_pMethodTablesForDirectThreadLocalData[IndexOffsetToDirectThreadLocalIndex(index.GetIndexOffset())]; } else { @@ -572,8 +571,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) } else if (index.GetTLSIndexType() == TLSIndexType::DirectOnThreadLocalData) { - // All of the current cases only require a single pointer sized field - _ASSERTE(pMT->GetClass()->GetNonGCThreadStaticFieldBytes() == sizeof(TADDR)); + // All of the current cases are non GC static, non-collectible _ASSERTE(!isGCStatic); _ASSERTE(!isCollectible); gcBaseAddresses.pTLSBaseAddress = ((TADDR)&t_ThreadStatics) + index.GetIndexOffset(); @@ -712,7 +710,9 @@ void* GetThreadLocalStaticBase(TLSIndex index) return reinterpret_cast(gcBaseAddresses.pTLSBaseAddress); } -void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pIndex) +static uint32_t g_directThreadLocalTLSBytesAvailable = (MAX_DIRECT_THREAD_LOCAL_COUNT - HARDCODED_DIRECT_THREAD_LOCAL_TLS_INDICES_USED) * sizeof(TADDR); + +void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pIndex, uint32_t bytesNeeded) { WRAPPER_NO_CONTRACT; @@ -727,10 +727,20 @@ void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pInde if (!pMT->Collectible()) { - if (!gcStatic && pMT == CoreLibBinder::GetClassIfExist(CLASS__THREAD_BLOCKING_INFO)) + if (!gcStatic && ((pMT == CoreLibBinder::GetClassIfExist(CLASS__THREAD_BLOCKING_INFO)) || ((g_directThreadLocalTLSBytesAvailable >= bytesNeeded) && (!pMT->HasClassConstructor() || pMT->IsClassInited())))) { - _ASSERTE(!pMT->HasClassConstructor()); // The DirectOnThreadLocalData scenario is not supported for types with class constructors - newTLSIndex = TLSIndex(TLSIndexType::DirectOnThreadLocalData, offsetof(ThreadLocalData, ThreadBlockingInfo_First)); + if (pMT == CoreLibBinder::GetClassIfExist(CLASS__THREAD_BLOCKING_INFO)) + { + newTLSIndex = TLSIndex(TLSIndexType::DirectOnThreadLocalData, offsetof(ThreadLocalData, ThreadBlockingInfo_First) - OFFSETOF__CORINFO_Array__data); + } + else + { + g_directThreadLocalTLSBytesAvailable -= bytesNeeded; + g_directThreadLocalTLSBytesAvailable = AlignDown((ULONG)g_directThreadLocalTLSBytesAvailable, sizeof(DIRECT_THREAD_LOCAL_CHUNK_TYPE)); + int32_t indexOffset = offsetof(ThreadLocalData, ThreadBlockingInfo_First) - OFFSETOF__CORINFO_Array__data + g_directThreadLocalTLSBytesAvailable; + newTLSIndex = TLSIndex(TLSIndexType::DirectOnThreadLocalData, indexOffset); + } + VolatileStoreWithoutBarrier(&g_pMethodTablesForDirectThreadLocalData[IndexOffsetToDirectThreadLocalIndex(newTLSIndex.GetIndexOffset())], pMT); } else { diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index 53959b9c204c6..ef7ffd52cf0cb 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -115,6 +115,15 @@ struct TLSIndex struct InFlightTLSData; typedef DPTR(InFlightTLSData) PTR_InFlightTLSData; +#define HARDCODED_DIRECT_THREAD_LOCAL_TLS_INDICES_USED 1 // The only one currently is ThreadBlockingInfo_First +#define MAX_DIRECT_THREAD_LOCAL_COUNT 16 + +#ifdef TARGET_ARM +typedef double DIRECT_THREAD_LOCAL_CHUNK_TYPE; +#else +typedef TADDR DIRECT_THREAD_LOCAL_CHUNK_TYPE; +#endif + struct ThreadLocalData { int32_t cNonCollectibleTlsData; // Size of offset into the non-collectible TLS array which is valid, NOTE: this is relative to the start of the pNonCollectibleTlsReferenceData object, not the start of the data in the array @@ -124,6 +133,10 @@ struct ThreadLocalData Thread *pThread; PTR_InFlightTLSData pInFlightData; // Points at the in-flight TLS data (TLS data that exists before the class constructor finishes running) TADDR ThreadBlockingInfo_First; // System.Threading.ThreadBlockingInfo.First +#ifdef TARGET_ARM + TADDR Padding; +#endif + DIRECT_THREAD_LOCAL_CHUNK_TYPE ExtendedDirectThreadLocalTLSData[MAX_DIRECT_THREAD_LOCAL_COUNT - HARDCODED_DIRECT_THREAD_LOCAL_TLS_INDICES_USED]; // The first one is always ThreadBlockInfo_First which is directly used by the runtime }; typedef DPTR(ThreadLocalData) PTR_ThreadLocalData; @@ -355,7 +368,7 @@ void InitializeCurrentThreadsStaticData(Thread* pThread); void FreeLoaderAllocatorHandlesForTLSData(Thread* pThread); void FreeThreadStaticData(ThreadLocalData *pThreadLocalData, Thread* pThread); void AssertThreadStaticDataFreed(ThreadLocalData *pThreadLocalData); -void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pIndex); +void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pIndex, uint32_t bytesNeeded); void FreeTLSIndicesForLoaderAllocator(LoaderAllocator *pLoaderAllocator); void* GetThreadLocalStaticBase(TLSIndex index); void GetThreadLocalStaticBlocksInfo (CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo); From 42fffaa413e1d28b8ab849bd0795902cdb568c1c Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 30 Apr 2024 16:49:39 -0700 Subject: [PATCH 64/82] Now it allocates at the byte level to be a bit more efficient, and reflection works right, etc --- src/coreclr/inc/corinfo.h | 1 + src/coreclr/jit/helperexpansion.cpp | 3 +- .../tools/Common/JitInterface/CorInfoTypes.cs | 1 + .../tools/superpmi/superpmi-shared/agnostic.h | 1 + .../superpmi-shared/methodcontext.cpp | 7 ++- src/coreclr/vm/jithelpers.cpp | 17 +------ src/coreclr/vm/threadstatics.cpp | 46 ++++++++++++++----- src/coreclr/vm/threadstatics.h | 17 ++----- 8 files changed, 49 insertions(+), 44 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 760374fff312d..168d3f6c9f47e 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -1701,6 +1701,7 @@ struct CORINFO_THREAD_STATIC_BLOCKS_INFO uint32_t offsetOfThreadLocalStoragePointer; // windows specific uint32_t offsetOfMaxThreadStaticBlocks; uint32_t offsetOfThreadStaticBlocks; + uint32_t offsetOfBaseOfThreadLocalData; }; //---------------------------------------------------------------------------- diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index d59e6a24b46c6..3b3d535e75ab6 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -840,6 +840,7 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* dspOffset(threadStaticBlocksInfo.offsetOfThreadLocalStoragePointer)); JITDUMP("offsetOfMaxThreadStaticBlocks= %u\n", dspOffset(threadStaticBlocksInfo.offsetOfMaxThreadStaticBlocks)); JITDUMP("offsetOfThreadStaticBlocks= %u\n", dspOffset(threadStaticBlocksInfo.offsetOfThreadStaticBlocks)); + JITDUMP("offsetOfBaseOfThreadLocalData= %u\n", dspOffset(threadStaticBlocksInfo.offsetOfBaseOfThreadLocalData)); assert(call->gtArgs.CountArgs() == 1); @@ -1006,7 +1007,7 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* // ... GenTree* typeThreadStaticBlockIndexValue = call->gtArgs.GetArgByIndex(0)->GetNode(); - GenTree* threadStaticBase = gtNewOperNode(GT_ADD, TYP_I_IMPL, gtCloneExpr(tlsLclValueUse), gtCloneExpr(typeThreadStaticBlockIndexValue)); + GenTree* threadStaticBase = gtNewOperNode(GT_ADD, TYP_I_IMPL, gtNewOperNode(GT_ADD, TYP_I_IMPL, gtCloneExpr(tlsLclValueUse), gtCloneExpr(typeThreadStaticBlockIndexValue)), gtNewIconNode(threadStaticBlocksInfo.offsetOfBaseOfThreadLocalData, TYP_I_IMPL)); GenTree* tlsStaticBaseStoreLcl = gtNewStoreLclVarNode(threadStaticBlockLclNum, threadStaticBase); BasicBlock* tlsBaseComputeBB = fgNewBBFromTreeAfter(BBJ_ALWAYS, prevBb, tlsValueDef, debugInfo, true); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 7a51fb6a00906..2d1ec2176f6fc 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1158,6 +1158,7 @@ public unsafe struct CORINFO_THREAD_STATIC_BLOCKS_INFO public uint offsetOfThreadLocalStoragePointer; public uint offsetOfMaxThreadStaticBlocks; public uint offsetOfThreadStaticBlocks; + public uint offsetOfBaseOfThreadLocalData; }; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h index 386bcb17b3332..db91679a16370 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h @@ -538,6 +538,7 @@ struct Agnostic_GetThreadLocalStaticBlocksInfo DWORD offsetOfThreadLocalStoragePointer; DWORD offsetOfMaxThreadStaticBlocks; DWORD offsetOfThreadStaticBlocks; + DWORD offsetOfBaseOfThreadLocalData; }; struct Agnostic_GetThreadStaticInfo_NativeAOT diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index 3f7778d29c953..8df0a10e15c3d 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -3635,6 +3635,7 @@ void MethodContext::recGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOC value.offsetOfThreadLocalStoragePointer = pInfo->offsetOfThreadLocalStoragePointer; value.offsetOfMaxThreadStaticBlocks = pInfo->offsetOfMaxThreadStaticBlocks; value.offsetOfThreadStaticBlocks = pInfo->offsetOfThreadStaticBlocks; + value.offsetOfBaseOfThreadLocalData = pInfo->offsetOfBaseOfThreadLocalData; // This data is same for entire process, so just add it against key '0'. DWORD key = 0; @@ -3649,10 +3650,11 @@ void MethodContext::dmpGetThreadLocalStaticBlocksInfo(DWORD key, const Agnostic_ ", threadVarsSection - %016" PRIX64 ", offsetOfThreadLocalStoragePointer-%u" ", offsetOfMaxThreadStaticBlocks-%u" - ", offsetOfThreadStaticBlocks-%u", + ", offsetOfThreadStaticBlocks-%u" + ", offsetOfBaseOfThreadLocalData-%u", key, SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(value.tlsIndex).c_str(), value.tlsGetAddrFtnPtr, value.tlsIndexObject, value.threadVarsSection, value.offsetOfThreadLocalStoragePointer, - value.offsetOfMaxThreadStaticBlocks, value.offsetOfThreadStaticBlocks); + value.offsetOfMaxThreadStaticBlocks, value.offsetOfThreadStaticBlocks, value.offsetOfBaseOfThreadLocalData); } void MethodContext::repGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) @@ -3669,6 +3671,7 @@ void MethodContext::repGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOC pInfo->offsetOfThreadLocalStoragePointer = value.offsetOfThreadLocalStoragePointer; pInfo->offsetOfMaxThreadStaticBlocks = value.offsetOfMaxThreadStaticBlocks; pInfo->offsetOfThreadStaticBlocks = value.offsetOfThreadStaticBlocks; + pInfo->offsetOfBaseOfThreadLocalData = value.offsetOfBaseOfThreadLocalData; } void MethodContext::recGetThreadLocalStaticInfo_NativeAOT(CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo) diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 4a131c14d26f9..80ed9d27fadbd 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1549,26 +1549,11 @@ HCIMPL1(void*, JIT_GetNonGCThreadStaticBaseOptimized, UINT32 staticBlockIndex) HCIMPLEND // *** This helper corresponds CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2. -// Even though we always check if the class constructor has been run, we have a separate -// helper ID for the "no ctor" version because it allows the JIT to do some reordering that -// otherwise wouldn't be possible. HCIMPL1(void*, JIT_GetNonGCThreadStaticBaseOptimized2, UINT32 staticBlockIndex) { - void* staticBlock = nullptr; - FCALL_CONTRACT; - HELPER_METHOD_FRAME_BEGIN_RET_0(); // Set up a frame - TLSIndex tlsIndex(TLSIndexType::DirectOnThreadLocalData, staticBlockIndex); - // Check if the class constructor needs to be run - MethodTable *pMT = LookupMethodTableForThreadStaticKnownToBeAllocated(tlsIndex); - pMT->CheckRunClassInitThrowing(); - - // Lookup the non-GC statics base pointer - staticBlock = (void*) pMT->GetNonGCThreadStaticsBasePointer(); - HELPER_METHOD_FRAME_END(); - - return staticBlock; + return ((BYTE*)&t_ThreadStatics) + staticBlockIndex; } HCIMPLEND diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 99e25763d1ecb..7f642ee1d44fd 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -78,17 +78,16 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread* pThread, TLSIndex index) TLSIndexToMethodTableMap *g_pThreadStaticCollectibleTypeIndices; TLSIndexToMethodTableMap *g_pThreadStaticNonCollectibleTypeIndices; -PTR_MethodTable g_pMethodTablesForDirectThreadLocalData[MAX_DIRECT_THREAD_LOCAL_COUNT]; +PTR_MethodTable g_pMethodTablesForDirectThreadLocalData[offsetof(ThreadLocalData, ExtendedDirectThreadLocalTLSData) - offsetof(ThreadLocalData, ThreadBlockingInfo_First) + EXTENDED_DIRECT_THREAD_LOCAL_SIZE]; int32_t IndexOffsetToDirectThreadLocalIndex(int32_t indexOffset) { LIMITED_METHOD_CONTRACT; int32_t adjustedIndexOffset = indexOffset + OFFSETOF__CORINFO_Array__data; _ASSERTE(adjustedIndexOffset >= offsetof(ThreadLocalData, ThreadBlockingInfo_First)); - _ASSERTE((adjustedIndexOffset & (sizeof(DIRECT_THREAD_LOCAL_CHUNK_TYPE) - 1)) == 0); - _ASSERTE(offsetof(ThreadLocalData, ExtendedDirectThreadLocalTLSData) - offsetof(ThreadLocalData, ThreadBlockingInfo_First) == sizeof(DIRECT_THREAD_LOCAL_CHUNK_TYPE) * HARDCODED_DIRECT_THREAD_LOCAL_TLS_INDICES_USED); - int32_t directThreadLocalIndex = (adjustedIndexOffset - offsetof(ThreadLocalData, ThreadBlockingInfo_First)) / sizeof(DIRECT_THREAD_LOCAL_CHUNK_TYPE); - _ASSERTE(directThreadLocalIndex < MAX_DIRECT_THREAD_LOCAL_COUNT); + int32_t directThreadLocalIndex = adjustedIndexOffset - offsetof(ThreadLocalData, ThreadBlockingInfo_First); + _ASSERTE(directThreadLocalIndex < (sizeof(g_pMethodTablesForDirectThreadLocalData) / sizeof(g_pMethodTablesForDirectThreadLocalData[0]))); + _ASSERTE(directThreadLocalIndex >= 0); return directThreadLocalIndex; } @@ -710,7 +709,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) return reinterpret_cast(gcBaseAddresses.pTLSBaseAddress); } -static uint32_t g_directThreadLocalTLSBytesAvailable = (MAX_DIRECT_THREAD_LOCAL_COUNT - HARDCODED_DIRECT_THREAD_LOCAL_TLS_INDICES_USED) * sizeof(TADDR); +static uint32_t g_directThreadLocalTLSBytesAvailable = EXTENDED_DIRECT_THREAD_LOCAL_SIZE; void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pIndex, uint32_t bytesNeeded) { @@ -727,22 +726,44 @@ void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pInde if (!pMT->Collectible()) { + bool usedDirectOnThreadLocalDataPath = false; + if (!gcStatic && ((pMT == CoreLibBinder::GetClassIfExist(CLASS__THREAD_BLOCKING_INFO)) || ((g_directThreadLocalTLSBytesAvailable >= bytesNeeded) && (!pMT->HasClassConstructor() || pMT->IsClassInited())))) { if (pMT == CoreLibBinder::GetClassIfExist(CLASS__THREAD_BLOCKING_INFO)) { newTLSIndex = TLSIndex(TLSIndexType::DirectOnThreadLocalData, offsetof(ThreadLocalData, ThreadBlockingInfo_First) - OFFSETOF__CORINFO_Array__data); + usedDirectOnThreadLocalDataPath = true; } else { - g_directThreadLocalTLSBytesAvailable -= bytesNeeded; - g_directThreadLocalTLSBytesAvailable = AlignDown((ULONG)g_directThreadLocalTLSBytesAvailable, sizeof(DIRECT_THREAD_LOCAL_CHUNK_TYPE)); - int32_t indexOffset = offsetof(ThreadLocalData, ThreadBlockingInfo_First) - OFFSETOF__CORINFO_Array__data + g_directThreadLocalTLSBytesAvailable; - newTLSIndex = TLSIndex(TLSIndexType::DirectOnThreadLocalData, indexOffset); + // This is a top down bump allocator that aligns data at the largest alignment that might be needed + uint32_t newBytesAvailable = g_directThreadLocalTLSBytesAvailable - bytesNeeded; + uint32_t indexOffsetWithoutAlignment = offsetof(ThreadLocalData, ExtendedDirectThreadLocalTLSData) - OFFSETOF__CORINFO_Array__data + newBytesAvailable; + uint32_t alignment; + if (bytesNeeded >= 8) + alignment = 8; + if (bytesNeeded >= 4) + alignment = 4; + else if (bytesNeeded >= 2) + alignment = 2; + else + alignment = 1; + + uint32_t actualIndexOffset = AlignDown(indexOffsetWithoutAlignment, alignment); + uint32_t alignmentAdjust = indexOffsetWithoutAlignment - actualIndexOffset; + if (alignmentAdjust <= newBytesAvailable) + { + g_directThreadLocalTLSBytesAvailable = newBytesAvailable - alignmentAdjust; + newTLSIndex = TLSIndex(TLSIndexType::DirectOnThreadLocalData, actualIndexOffset); + } + usedDirectOnThreadLocalDataPath = true; } - VolatileStoreWithoutBarrier(&g_pMethodTablesForDirectThreadLocalData[IndexOffsetToDirectThreadLocalIndex(newTLSIndex.GetIndexOffset())], pMT); + if (usedDirectOnThreadLocalDataPath) + VolatileStoreWithoutBarrier(&g_pMethodTablesForDirectThreadLocalData[IndexOffsetToDirectThreadLocalIndex(newTLSIndex.GetIndexOffset())], pMT); } - else + + if (!usedDirectOnThreadLocalDataPath) { uint32_t tlsRawIndex = g_NextNonCollectibleTLSSlot++; newTLSIndex = TLSIndex(TLSIndexType::NonCollectible, tlsRawIndex); @@ -938,6 +959,7 @@ void GetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) pInfo->offsetOfMaxThreadStaticBlocks = (uint32_t)(threadStaticBaseOffset + offsetof(ThreadLocalData, cNonCollectibleTlsData)); pInfo->offsetOfThreadStaticBlocks = (uint32_t)(threadStaticBaseOffset + offsetof(ThreadLocalData, pNonCollectibleTlsReferenceData)); + pInfo->offsetOfBaseOfThreadLocalData = (uint32_t)threadStaticBaseOffset; } #endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index ef7ffd52cf0cb..159f425e8f1ce 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -115,28 +115,19 @@ struct TLSIndex struct InFlightTLSData; typedef DPTR(InFlightTLSData) PTR_InFlightTLSData; -#define HARDCODED_DIRECT_THREAD_LOCAL_TLS_INDICES_USED 1 // The only one currently is ThreadBlockingInfo_First -#define MAX_DIRECT_THREAD_LOCAL_COUNT 16 - -#ifdef TARGET_ARM -typedef double DIRECT_THREAD_LOCAL_CHUNK_TYPE; -#else -typedef TADDR DIRECT_THREAD_LOCAL_CHUNK_TYPE; -#endif +#define EXTENDED_DIRECT_THREAD_LOCAL_SIZE 48 struct ThreadLocalData { + alignas(8) // This is to ensure that the ExtendedDirectThreadLocalTLSData is aligned to be able to hold a double on arm legally int32_t cNonCollectibleTlsData; // Size of offset into the non-collectible TLS array which is valid, NOTE: this is relative to the start of the pNonCollectibleTlsReferenceData object, not the start of the data in the array int32_t cTLSData; // Size of offset into the TLS array which is valid PTR_Object pNonCollectibleTlsReferenceData; TADDR pTLSArrayData; // Points at the Thread local array data. Thread *pThread; PTR_InFlightTLSData pInFlightData; // Points at the in-flight TLS data (TLS data that exists before the class constructor finishes running) - TADDR ThreadBlockingInfo_First; // System.Threading.ThreadBlockingInfo.First -#ifdef TARGET_ARM - TADDR Padding; -#endif - DIRECT_THREAD_LOCAL_CHUNK_TYPE ExtendedDirectThreadLocalTLSData[MAX_DIRECT_THREAD_LOCAL_COUNT - HARDCODED_DIRECT_THREAD_LOCAL_TLS_INDICES_USED]; // The first one is always ThreadBlockInfo_First which is directly used by the runtime + TADDR ThreadBlockingInfo_First; // System.Threading.ThreadBlockingInfo.First, This starts the region of ThreadLocalData which is referenceable by TLSIndexType::DirectOnThreadLocalData + BYTE ExtendedDirectThreadLocalTLSData[EXTENDED_DIRECT_THREAD_LOCAL_SIZE]; }; typedef DPTR(ThreadLocalData) PTR_ThreadLocalData; From 35c935e4c702b65103012c239c8bb5b99d1d275d Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 1 May 2024 10:34:07 -0700 Subject: [PATCH 65/82] Jit format and GCC build changes --- src/coreclr/jit/helperexpansion.cpp | 28 +++++++++++++++++----------- src/coreclr/jit/importer.cpp | 3 ++- src/coreclr/vm/threadstatics.cpp | 4 ++-- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index 3b3d535e75ab6..8db4698ebdc29 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -988,13 +988,14 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* } // Cache the tls value - tlsValueDef = gtNewStoreLclVarNode(tlsLclNum, tlsValue); - GenTree* tlsLclValueUse = gtNewLclVarNode(tlsLclNum); + tlsValueDef = gtNewStoreLclVarNode(tlsLclNum, tlsValue); + GenTree* tlsLclValueUse = gtNewLclVarNode(tlsLclNum); GenTree* typeThreadStaticBlockIndexValue = call->gtArgs.GetArgByIndex(0)->GetNode(); if (helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2) { - // In this case we can entirely remove the block which uses the helper call, and just get the TLS pointer directly + // In this case we can entirely remove the block which uses the helper call, and just get the TLS pointer + // directly // prevBb (BBJ_NONE): [weight: 1.0] // ... @@ -1007,9 +1008,12 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* // ... GenTree* typeThreadStaticBlockIndexValue = call->gtArgs.GetArgByIndex(0)->GetNode(); - GenTree* threadStaticBase = gtNewOperNode(GT_ADD, TYP_I_IMPL, gtNewOperNode(GT_ADD, TYP_I_IMPL, gtCloneExpr(tlsLclValueUse), gtCloneExpr(typeThreadStaticBlockIndexValue)), gtNewIconNode(threadStaticBlocksInfo.offsetOfBaseOfThreadLocalData, TYP_I_IMPL)); - GenTree* tlsStaticBaseStoreLcl = gtNewStoreLclVarNode(threadStaticBlockLclNum, threadStaticBase); - + GenTree* threadStaticBase = + gtNewOperNode(GT_ADD, TYP_I_IMPL, + gtNewOperNode(GT_ADD, TYP_I_IMPL, gtCloneExpr(tlsLclValueUse), + gtCloneExpr(typeThreadStaticBlockIndexValue)), + gtNewIconNode(threadStaticBlocksInfo.offsetOfBaseOfThreadLocalData, TYP_I_IMPL)); GenTree* tlsStaticBaseStoreLcl = gtNewStoreLclVarNode(threadStaticBlockLclNum, threadStaticBase); + BasicBlock* tlsBaseComputeBB = fgNewBBFromTreeAfter(BBJ_ALWAYS, prevBb, tlsValueDef, debugInfo, true); fgInsertStmtAfter(tlsBaseComputeBB, tlsBaseComputeBB->firstStmt(), fgNewStmtFromTree(tlsStaticBaseStoreLcl)); @@ -1042,10 +1046,11 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* GenTree* offsetOfMaxThreadStaticBlocks = gtNewIconNode(offsetOfMaxThreadStaticBlocksVal, TYP_I_IMPL); GenTree* maxThreadStaticBlocksRef = gtNewOperNode(GT_ADD, TYP_I_IMPL, gtCloneExpr(tlsLclValueUse), offsetOfMaxThreadStaticBlocks); - maxThreadStaticBlocksValue = gtNewIndir(TYP_INT, maxThreadStaticBlocksRef, GTF_IND_NONFAULTING | GTF_IND_INVARIANT); + maxThreadStaticBlocksValue = + gtNewIndir(TYP_INT, maxThreadStaticBlocksRef, GTF_IND_NONFAULTING | GTF_IND_INVARIANT); GenTree* threadStaticBlocksRef = gtNewOperNode(GT_ADD, TYP_I_IMPL, gtCloneExpr(tlsLclValueUse), - gtNewIconNode(offsetOfThreadStaticBlocksVal, TYP_I_IMPL)); + gtNewIconNode(offsetOfThreadStaticBlocksVal, TYP_I_IMPL)); threadStaticBlocksValue = gtNewIndir(TYP_REF, threadStaticBlocksRef, GTF_IND_NONFAULTING | GTF_IND_INVARIANT); // Create tree for "if (maxThreadStaticBlocks < typeIndex)" @@ -1063,7 +1068,8 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* // Cache the threadStaticBlock value unsigned threadStaticBlockBaseLclNum = lvaGrabTemp(true DEBUGARG("ThreadStaticBlockBase access")); lvaTable[threadStaticBlockBaseLclNum].lvType = TYP_BYREF; - GenTree* threadStaticBlockBaseDef = gtNewStoreLclVarNode(threadStaticBlockBaseLclNum, typeThreadStaticBlockValue); + GenTree* threadStaticBlockBaseDef = + gtNewStoreLclVarNode(threadStaticBlockBaseLclNum, typeThreadStaticBlockValue); GenTree* threadStaticBlockBaseLclValueUse = gtNewLclVarNode(threadStaticBlockBaseLclNum); // Create tree for "if (threadStaticBlockValue != nullptr)" @@ -1101,13 +1107,13 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* BasicBlock* maxThreadStaticBlocksCondBB = fgNewBBFromTreeAfter(BBJ_COND, prevBb, tlsValueDef, debugInfo); fgInsertStmtAfter(maxThreadStaticBlocksCondBB, maxThreadStaticBlocksCondBB->firstStmt(), - fgNewStmtFromTree(maxThreadStaticBlocksCond)); + fgNewStmtFromTree(maxThreadStaticBlocksCond)); // Similarly, set threadStaticBlockNulLCondBB to jump to fastPathBb once the latter exists. BasicBlock* threadStaticBlockNullCondBB = fgNewBBFromTreeAfter(BBJ_COND, maxThreadStaticBlocksCondBB, threadStaticBlockBaseDef, debugInfo); fgInsertStmtAfter(threadStaticBlockNullCondBB, threadStaticBlockNullCondBB->firstStmt(), - fgNewStmtFromTree(threadStaticBlockNullCond)); + fgNewStmtFromTree(threadStaticBlockNullCond)); // fallbackBb GenTree* fallbackValueDef = gtNewStoreLclVarNode(threadStaticBlockLclNum, call); diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index bd3c284c6a848..dde686b0f291d 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -3826,7 +3826,8 @@ GenTree* Compiler::impImportStaticFieldAddress(CORINFO_RESOLVED_TOKEN* pResolved if (!opts.IsReadyToRun()) #endif // FEATURE_READYTORUN { - if ((pFieldInfo->helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) || (pFieldInfo->helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2)) + if ((pFieldInfo->helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) || + (pFieldInfo->helper == CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2)) { typeIndex = info.compCompHnd->getThreadLocalFieldInfo(pResolvedToken->hField, false); } diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 7f642ee1d44fd..cf0163029b0df 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -84,9 +84,9 @@ int32_t IndexOffsetToDirectThreadLocalIndex(int32_t indexOffset) { LIMITED_METHOD_CONTRACT; int32_t adjustedIndexOffset = indexOffset + OFFSETOF__CORINFO_Array__data; - _ASSERTE(adjustedIndexOffset >= offsetof(ThreadLocalData, ThreadBlockingInfo_First)); + _ASSERTE(((uint32_t)adjustedIndexOffset) >= offsetof(ThreadLocalData, ThreadBlockingInfo_First)); int32_t directThreadLocalIndex = adjustedIndexOffset - offsetof(ThreadLocalData, ThreadBlockingInfo_First); - _ASSERTE(directThreadLocalIndex < (sizeof(g_pMethodTablesForDirectThreadLocalData) / sizeof(g_pMethodTablesForDirectThreadLocalData[0]))); + _ASSERTE(((uint32_t)directThreadLocalIndex) < (sizeof(g_pMethodTablesForDirectThreadLocalData) / sizeof(g_pMethodTablesForDirectThreadLocalData[0]))); _ASSERTE(directThreadLocalIndex >= 0); return directThreadLocalIndex; } From d48ef557699fc9ea46cd734dfb38c83f556a5dc2 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 1 May 2024 11:02:13 -0700 Subject: [PATCH 66/82] Try to fix the musl validation leg --- src/coreclr/vm/jithelpers.cpp | 2 +- src/coreclr/vm/loaderallocator.inl | 2 +- src/coreclr/vm/threadstatics.cpp | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 80ed9d27fadbd..4e1efc4b84527 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1407,7 +1407,7 @@ __thread ThreadLocalData t_ThreadStatics; FORCEINLINE void* GetThreadLocalStaticBaseIfExistsAndInitialized(TLSIndex index) { LIMITED_METHOD_CONTRACT; - TADDR pTLSBaseAddress = NULL; + TADDR pTLSBaseAddress = (TADDR)NULL; if (index.GetTLSIndexType() == TLSIndexType::NonCollectible) { diff --git a/src/coreclr/vm/loaderallocator.inl b/src/coreclr/vm/loaderallocator.inl index 5c2544132a3b2..d9f6da55df5b9 100644 --- a/src/coreclr/vm/loaderallocator.inl +++ b/src/coreclr/vm/loaderallocator.inl @@ -19,7 +19,7 @@ inline LOADERALLOCATORREF LoaderAllocator::GetExposedObject() inline bool LoaderAllocator::IsExposedObjectLive() { LIMITED_METHOD_CONTRACT; - if (m_hLoaderAllocatorObjectHandle == NULL) + if (m_hLoaderAllocatorObjectHandle == 0) return false; return !ObjectHandleIsNull(m_hLoaderAllocatorObjectHandle); } diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index cf0163029b0df..affaf850abbdb 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -34,7 +34,7 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread* pThread, TLSIndex index) if (pThreadLocalData == NULL) return NULL; - TADDR pTLSBaseAddress = NULL; + TADDR pTLSBaseAddress = (TADDR)NULL; if (index.GetTLSIndexType() == TLSIndexType::NonCollectible) { PTRARRAYREF tlsArray = (PTRARRAYREF)UNCHECKED_OBJECTREF_TO_OBJECTREF(pThreadLocalData->pNonCollectibleTlsReferenceData); @@ -164,7 +164,7 @@ bool ReportTLSIndexCarefully(TLSIndex index, int32_t cLoaderHandles, PTR_LOADERH PTR_MethodTable pMT = LookupMethodTableAndFlagForThreadStatic(index, &isGCStatic, &isCollectible); _ASSERTE(index.GetTLSIndexType() == TLSIndexType::NonCollectible || (((pMT == NULL) || isCollectible) && index.GetTLSIndexType() == TLSIndexType::Collectible)); - if (index.GetTLSIndexType() == TLSIndexType::Collectible && pLoaderHandles[index.GetIndexOffset()] == NULL) + if (index.GetTLSIndexType() == TLSIndexType::Collectible && pLoaderHandles[index.GetIndexOffset()] == (LOADERHANDLE)NULL) { // The TLS index is not in use. This either means that the TLS index was never used, or that it was // used for a collectible assembly, and that assembly has been freed. In the latter case, we may need to @@ -445,10 +445,10 @@ void FreeLoaderAllocatorHandlesForTLSData(Thread *pThread) } else { - if (pThread->pLoaderHandles[entry.TlsIndex.GetIndexOffset()] != NULL) + if (pThread->pLoaderHandles[entry.TlsIndex.GetIndexOffset()] != (LOADERHANDLE)NULL) { entry.pMT->GetLoaderAllocator()->FreeHandle(pThread->pLoaderHandles[entry.TlsIndex.GetIndexOffset()]); - pThread->pLoaderHandles[entry.TlsIndex.GetIndexOffset()] = NULL; + pThread->pLoaderHandles[entry.TlsIndex.GetIndexOffset()] = (LOADERHANDLE)NULL; } } } @@ -541,7 +541,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) struct { TADDR *ppTLSBaseAddress = NULL; - TADDR pTLSBaseAddress = NULL; + TADDR pTLSBaseAddress = (TADDR)NULL; } gcBaseAddresses; GCPROTECT_BEGININTERIOR(gcBaseAddresses); From 638ea504a21bb73da11c4dd8b85a4242f3bb5446 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 1 May 2024 11:57:10 -0700 Subject: [PATCH 67/82] Attempt to fix even more build nits --- src/coreclr/jit/helperexpansion.cpp | 3 ++- src/coreclr/vm/threadstatics.cpp | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index 8db4698ebdc29..5f790fafc7a47 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -1012,7 +1012,8 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* gtNewOperNode(GT_ADD, TYP_I_IMPL, gtNewOperNode(GT_ADD, TYP_I_IMPL, gtCloneExpr(tlsLclValueUse), gtCloneExpr(typeThreadStaticBlockIndexValue)), - gtNewIconNode(threadStaticBlocksInfo.offsetOfBaseOfThreadLocalData, TYP_I_IMPL)); GenTree* tlsStaticBaseStoreLcl = gtNewStoreLclVarNode(threadStaticBlockLclNum, threadStaticBase); + gtNewIconNode(threadStaticBlocksInfo.offsetOfBaseOfThreadLocalData, TYP_I_IMPL)); + GenTree* tlsStaticBaseStoreLcl = gtNewStoreLclVarNode(threadStaticBlockLclNum, threadStaticBase); BasicBlock* tlsBaseComputeBB = fgNewBBFromTreeAfter(BBJ_ALWAYS, prevBb, tlsValueDef, debugInfo, true); fgInsertStmtAfter(tlsBaseComputeBB, tlsBaseComputeBB->firstStmt(), fgNewStmtFromTree(tlsStaticBaseStoreLcl)); diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index affaf850abbdb..d0ce36c9401fb 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -59,7 +59,7 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread* pThread, TLSIndex index) TADDR pTLSArrayData = pThreadLocalData->pTLSArrayData; pTLSBaseAddress = *(dac_cast(pTLSArrayData) + index.GetIndexOffset()); } - if (pTLSBaseAddress == NULL) + if (pTLSBaseAddress == (TADDR)NULL) { // Maybe it is in the InFlightData PTR_InFlightTLSData pInFlightData = pThreadLocalData->pInFlightData; @@ -205,7 +205,7 @@ void ScanThreadStaticRoots(Thread* pThread, bool forGC, promote_func* fn, ScanCo { TLSIndex index(TLSIndexType::Collectible, i); PTR_LOADERHANDLE pLoaderHandle = pThread->pLoaderHandles + i; - if (*pLoaderHandle != NULL) + if (*pLoaderHandle != (LOADERHANDLE)NULL) { bool isGCStatic; bool isCollectible; @@ -214,7 +214,7 @@ void ScanThreadStaticRoots(Thread* pThread, bool forGC, promote_func* fn, ScanCo if (pMT == NULL || !pMT->GetLoaderAllocator()->IsExposedObjectLive()) { - *pLoaderHandle = NULL; + *pLoaderHandle = (LOADERHANDLE)NULL; } } } From fc67ecdc926996e140568e8a978d18b63bbaa9fe Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 1 May 2024 14:22:22 -0700 Subject: [PATCH 68/82] Fix next set of musl validation errors --- src/coreclr/vm/threadstatics.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index d0ce36c9401fb..461dc5bc42ad7 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -616,7 +616,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) gcBaseAddresses.pTLSBaseAddress = *gcBaseAddresses.ppTLSBaseAddress; } - if (gcBaseAddresses.pTLSBaseAddress == NULL) + if (gcBaseAddresses.pTLSBaseAddress == (TADDR)NULL) { // Maybe it is in the InFlightData InFlightTLSData* pInFlightData = t_ThreadStatics.pInFlightData; @@ -639,7 +639,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) ppOldNextPtr = &pInFlightData->pNext; pInFlightData = pInFlightData->pNext; } - if (gcBaseAddresses.pTLSBaseAddress == NULL) + if (gcBaseAddresses.pTLSBaseAddress == (TADDR)NULL) { // Now we need to actually allocate the TLS data block struct From 4e2e0c2febd239d0575a920b4a9a5e85747613c3 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 3 May 2024 10:31:47 -0700 Subject: [PATCH 69/82] Add statics heap specifically for non-collectible non-gc statics - Seems to reduce variability in benchmark results by a small factor --- src/coreclr/debug/daccess/request.cpp | 2 ++ src/coreclr/vm/appdomain.hpp | 5 ++++- src/coreclr/vm/loaderallocator.cpp | 31 ++++++++++++++++++++++++++- src/coreclr/vm/loaderallocator.hpp | 8 +++++++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 787d09e5d7e58..5d956147dddf1 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -3562,6 +3562,7 @@ static const char *LoaderAllocatorLoaderHeapNames[] = { "LowFrequencyHeap", "HighFrequencyHeap", + "StaticsHeap", "StubHeap", "ExecutableHeap", "FixupPrecodeHeap", @@ -3596,6 +3597,7 @@ HRESULT ClrDataAccess::GetLoaderAllocatorHeaps(CLRDATA_ADDRESS loaderAllocatorAd int i = 0; pLoaderHeaps[i++] = HOST_CDADDR(pLoaderAllocator->GetLowFrequencyHeap()); pLoaderHeaps[i++] = HOST_CDADDR(pLoaderAllocator->GetHighFrequencyHeap()); + pLoaderHeaps[i++] = HOST_CDADDR(pLoaderAllocator->GetStaticsHeap()); pLoaderHeaps[i++] = HOST_CDADDR(pLoaderAllocator->GetStubHeap()); pLoaderHeaps[i++] = HOST_CDADDR(pLoaderAllocator->GetExecutableHeap()); pLoaderHeaps[i++] = HOST_CDADDR(pLoaderAllocator->GetFixupPrecodeHeap()); diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp index 320cda33baf18..c0a22f8369aad 100644 --- a/src/coreclr/vm/appdomain.hpp +++ b/src/coreclr/vm/appdomain.hpp @@ -221,12 +221,15 @@ FORCEINLINE void PinnedHeapHandleBlockHolder__StaticFree(PinnedHeapHandleBlockH #define LOW_FREQUENCY_HEAP_RESERVE_SIZE (3 * GetOsPageSize()) #define LOW_FREQUENCY_HEAP_COMMIT_SIZE (1 * GetOsPageSize()) -#define HIGH_FREQUENCY_HEAP_RESERVE_SIZE (10 * GetOsPageSize()) +#define HIGH_FREQUENCY_HEAP_RESERVE_SIZE (8 * GetOsPageSize()) #define HIGH_FREQUENCY_HEAP_COMMIT_SIZE (1 * GetOsPageSize()) #define STUB_HEAP_RESERVE_SIZE (3 * GetOsPageSize()) #define STUB_HEAP_COMMIT_SIZE (1 * GetOsPageSize()) +#define STATIC_FIELD_HEAP_RESERVE_SIZE (2 * GetOsPageSize()) +#define STATIC_FIELD_HEAP_COMMIT_SIZE (1 * GetOsPageSize()) + // -------------------------------------------------------------------------------- // PE File List lock - for creating list locks on PE files // -------------------------------------------------------------------------------- diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index e30f584ac74e0..a267ae62795f7 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -1078,6 +1078,7 @@ void LoaderAllocator::Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory) DWORD dwLowFrequencyHeapReserveSize; DWORD dwHighFrequencyHeapReserveSize; + DWORD dwStaticsHeapReserveSize; DWORD dwStubHeapReserveSize; DWORD dwExecutableHeapReserveSize; DWORD dwCodeHeapReserveSize; @@ -1092,12 +1093,14 @@ void LoaderAllocator::Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory) dwStubHeapReserveSize = COLLECTIBLE_STUB_HEAP_SIZE; dwCodeHeapReserveSize = COLLECTIBLE_CODEHEAP_SIZE; dwVSDHeapReserveSize = COLLECTIBLE_VIRTUALSTUBDISPATCH_HEAP_SPACE; + dwStaticsHeapReserveSize = 0; } else { dwLowFrequencyHeapReserveSize = LOW_FREQUENCY_HEAP_RESERVE_SIZE; dwHighFrequencyHeapReserveSize = HIGH_FREQUENCY_HEAP_RESERVE_SIZE; dwStubHeapReserveSize = STUB_HEAP_RESERVE_SIZE; + dwStaticsHeapReserveSize = STATIC_FIELD_HEAP_RESERVE_SIZE; // Non-collectible assemblies do not reserve space for these heaps. dwCodeHeapReserveSize = 0; @@ -1116,6 +1119,7 @@ void LoaderAllocator::Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory) DWORD dwTotalReserveMemSize = dwLowFrequencyHeapReserveSize + dwHighFrequencyHeapReserveSize + + dwStaticsHeapReserveSize + dwStubHeapReserveSize + dwCodeHeapReserveSize + dwVSDHeapReserveSize @@ -1181,6 +1185,19 @@ void LoaderAllocator::Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory) m_pHighFrequencyHeap->m_fPermitStubsWithUnwindInfo = TRUE; #endif + if (dwStaticsHeapReserveSize != 0) + { + m_pStaticsHeap = new (&m_StaticsHeapInstance) LoaderHeap(STATIC_FIELD_HEAP_RESERVE_SIZE, + STATIC_FIELD_HEAP_COMMIT_SIZE, + initReservedMem, + dwStaticsHeapReserveSize); + initReservedMem += dwStaticsHeapReserveSize; + } + else + { + m_pStaticsHeap = m_pHighFrequencyHeap; + } + m_pStubHeap = new (&m_StubHeapInstance) LoaderHeap(STUB_HEAP_RESERVE_SIZE, STUB_HEAP_COMMIT_SIZE, initReservedMem, @@ -1378,6 +1395,12 @@ void LoaderAllocator::Terminate() m_pHighFrequencyHeap = NULL; } + if ((m_pStaticsHeap != NULL) && m_pStaticsHeap != m_pHighFrequencyHeap) + { + m_pStaticsHeap->~LoaderHeap(); + m_pStaticsHeap = NULL; + } + if (m_pStubHeap != NULL) { #ifdef STUBLINKER_GENERATES_UNWIND_INFO @@ -1462,6 +1485,10 @@ void LoaderAllocator::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) { m_pHighFrequencyHeap->EnumMemoryRegions(flags); } + if (m_pStaticsHeap.IsValid()) + { + m_pStaticsHeap->EnumMemoryRegions(flags); + } if (m_pStubHeap.IsValid()) { m_pStubHeap->EnumMemoryRegions(flags); @@ -1501,6 +1528,8 @@ SIZE_T LoaderAllocator::EstimateSize() SIZE_T retval=0; if(m_pHighFrequencyHeap) retval+=m_pHighFrequencyHeap->GetSize(); + if(m_pStaticsHeap) + retval+=m_pStaticsHeap->GetSize(); if(m_pLowFrequencyHeap) retval+=m_pLowFrequencyHeap->GetSize(); if(m_pStubHeap) @@ -2286,7 +2315,7 @@ void LoaderAllocator::AllocateBytesForStaticVariables(DynamicStaticsInfo* pStati // Always allocate in multiples of pointer size cbMem = ALIGN_UP(cbMem, sizeof(TADDR)); } - uint8_t* pbMem = (uint8_t*)(void*)GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbMem)); + uint8_t* pbMem = (uint8_t*)(void*)GetStaticsHeap()->AllocMem(S_SIZE_T(cbMem)); #ifndef TARGET_64BIT // Second part of alignment work if (initialcbMem >= 8) { diff --git a/src/coreclr/vm/loaderallocator.hpp b/src/coreclr/vm/loaderallocator.hpp index dc125a5058836..8d3fdb3fcde89 100644 --- a/src/coreclr/vm/loaderallocator.hpp +++ b/src/coreclr/vm/loaderallocator.hpp @@ -306,8 +306,10 @@ class LoaderAllocator BYTE m_PrecodeHeapInstance[sizeof(CodeFragmentHeap)]; BYTE m_FixupPrecodeHeapInstance[sizeof(LoaderHeap)]; BYTE m_NewStubPrecodeHeapInstance[sizeof(LoaderHeap)]; + BYTE m_StaticsHeapInstance[sizeof(LoaderHeap)]; PTR_LoaderHeap m_pLowFrequencyHeap; PTR_LoaderHeap m_pHighFrequencyHeap; + PTR_LoaderHeap m_pStaticsHeap; PTR_LoaderHeap m_pStubHeap; // stubs for PInvoke, remoting, etc PTR_CodeFragmentHeap m_pPrecodeHeap; PTR_LoaderHeap m_pExecutableHeap; @@ -590,6 +592,12 @@ class LoaderAllocator return m_pHighFrequencyHeap; } + PTR_LoaderHeap GetStaticsHeap() + { + LIMITED_METHOD_CONTRACT; + return m_pStaticsHeap; + } + PTR_LoaderHeap GetStubHeap() { LIMITED_METHOD_CONTRACT; From 4e78250db9f21c81efb8bd2f6dec2e023b5f0c94 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 3 May 2024 10:56:44 -0700 Subject: [PATCH 70/82] Fix naming for thread local static field names to be something which makes sense --- src/coreclr/vm/jithelpers.cpp | 10 ++-- src/coreclr/vm/threadstatics.cpp | 76 ++++++++++++------------- src/coreclr/vm/threadstatics.h | 95 ++++++++------------------------ 3 files changed, 66 insertions(+), 115 deletions(-) diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 4e1efc4b84527..af2b1ca5d643c 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1411,7 +1411,7 @@ FORCEINLINE void* GetThreadLocalStaticBaseIfExistsAndInitialized(TLSIndex index) if (index.GetTLSIndexType() == TLSIndexType::NonCollectible) { - PTRARRAYREF tlsArray = (PTRARRAYREF)UNCHECKED_OBJECTREF_TO_OBJECTREF(t_ThreadStatics.pNonCollectibleTlsReferenceData); + PTRARRAYREF tlsArray = (PTRARRAYREF)UNCHECKED_OBJECTREF_TO_OBJECTREF(t_ThreadStatics.pNonCollectibleTlsArrayData); if (t_ThreadStatics.cNonCollectibleTlsData <= index.GetIndexOffset()) { return NULL; @@ -1424,14 +1424,14 @@ FORCEINLINE void* GetThreadLocalStaticBaseIfExistsAndInitialized(TLSIndex index) } else { - int32_t cTLSData = t_ThreadStatics.cTLSData; - if (cTLSData <= index.GetIndexOffset()) + int32_t cCollectibleTlsData = t_ThreadStatics.cCollectibleTlsData; + if (cCollectibleTlsData <= index.GetIndexOffset()) { return NULL; } - TADDR pTLSArrayData = t_ThreadStatics.pTLSArrayData; - pTLSBaseAddress = *reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetIndexOffset()); + TADDR pCollectibleTlsArrayData = t_ThreadStatics.pCollectibleTlsArrayData; + pTLSBaseAddress = *reinterpret_cast(reinterpret_cast(pCollectibleTlsArrayData) + index.GetIndexOffset()); } return reinterpret_cast(pTLSBaseAddress); } diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 461dc5bc42ad7..65a3870822cd8 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -37,7 +37,7 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread* pThread, TLSIndex index) TADDR pTLSBaseAddress = (TADDR)NULL; if (index.GetTLSIndexType() == TLSIndexType::NonCollectible) { - PTRARRAYREF tlsArray = (PTRARRAYREF)UNCHECKED_OBJECTREF_TO_OBJECTREF(pThreadLocalData->pNonCollectibleTlsReferenceData); + PTRARRAYREF tlsArray = (PTRARRAYREF)UNCHECKED_OBJECTREF_TO_OBJECTREF(pThreadLocalData->pNonCollectibleTlsArrayData); if (pThreadLocalData->cNonCollectibleTlsData <= index.GetIndexOffset()) { return NULL; @@ -50,14 +50,14 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread* pThread, TLSIndex index) } else { - int32_t cTLSData = pThreadLocalData->cTLSData; - if (cTLSData <= index.GetIndexOffset()) + int32_t cCollectibleTlsData = pThreadLocalData->cCollectibleTlsData; + if (cCollectibleTlsData <= index.GetIndexOffset()) { return NULL; } - TADDR pTLSArrayData = pThreadLocalData->pTLSArrayData; - pTLSBaseAddress = *(dac_cast(pTLSArrayData) + index.GetIndexOffset()); + TADDR pCollectibleTlsArrayData = pThreadLocalData->pCollectibleTlsArrayData; + pTLSBaseAddress = *(dac_cast(pCollectibleTlsArrayData) + index.GetIndexOffset()); } if (pTLSBaseAddress == (TADDR)NULL) { @@ -245,20 +245,20 @@ void ScanThreadStaticRoots(Thread* pThread, bool forGC, promote_func* fn, ScanCo pInFlightData = pInFlightData->pNext; } - PTR_TADDR pTLSArrayData = dac_cast(pThreadLocalData->pTLSArrayData); - int32_t cTLSData = pThreadLocalData->cTLSData; - for (int32_t i = 0; i < cTLSData; ++i) + PTR_TADDR pCollectibleTlsArrayData = dac_cast(pThreadLocalData->pCollectibleTlsArrayData); + int32_t cCollectibleTlsData = pThreadLocalData->cCollectibleTlsData; + for (int32_t i = 0; i < cCollectibleTlsData; ++i) { TLSIndex index(TLSIndexType::Collectible, i); - TADDR *pTLSBaseAddress = pTLSArrayData + i; + TADDR *pTLSBaseAddress = pCollectibleTlsArrayData + i; ReportTLSIndexCarefully(index, cLoaderHandles, pThread->pLoaderHandles, dac_cast(pTLSBaseAddress), fn, sc); } // Report non-collectible object array #ifndef DACCESS_COMPILE - fn(&pThreadLocalData->pNonCollectibleTlsReferenceData, sc, 0 /* could be GC_CALL_INTERIOR or GC_CALL_PINNED */); + fn(&pThreadLocalData->pNonCollectibleTlsArrayData, sc, 0 /* could be GC_CALL_INTERIOR or GC_CALL_PINNED */); #else - fn(dac_cast(&pThreadLocalData->pNonCollectibleTlsReferenceData), sc, 0 /* could be GC_CALL_INTERIOR or GC_CALL_PINNED */); + fn(dac_cast(&pThreadLocalData->pNonCollectibleTlsArrayData), sc, 0 /* could be GC_CALL_INTERIOR or GC_CALL_PINNED */); #endif } @@ -351,7 +351,7 @@ bool TLSIndexToMethodTableMap::FindClearedIndex(uint8_t whenClearedMarkerToAvoid } uint32_t g_NextTLSSlot = 1; -uint32_t g_NextNonCollectibleTLSSlot = NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY; +uint32_t g_NextNonCollectibleTlsSlot = NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY; CrstStatic g_TLSCrst; void InitializeThreadStaticData() @@ -460,9 +460,9 @@ void AssertThreadStaticDataFreed(ThreadLocalData *pThreadLocalData) if (!IsAtProcessExit() && !g_fEEShutDown) { _ASSERTE(pThreadLocalData->pThread == NULL); - _ASSERTE(pThreadLocalData->pTLSArrayData == NULL); - _ASSERTE(pThreadLocalData->cTLSData == 0); - _ASSERTE(pThreadLocalData->pNonCollectibleTlsReferenceData == NULL); + _ASSERTE(pThreadLocalData->pCollectibleTlsArrayData == NULL); + _ASSERTE(pThreadLocalData->cCollectibleTlsData == 0); + _ASSERTE(pThreadLocalData->pNonCollectibleTlsArrayData == NULL); _ASSERTE(pThreadLocalData->cNonCollectibleTlsData == 0); _ASSERTE(pThreadLocalData->pInFlightData == NULL); } @@ -494,11 +494,11 @@ void FreeThreadStaticData(ThreadLocalData *pThreadLocalData, Thread* pThread) if (pThreadLocalData == NULL) return; - delete[] (uint8_t*)pThreadLocalData->pTLSArrayData; + delete[] (uint8_t*)pThreadLocalData->pCollectibleTlsArrayData; - pThreadLocalData->pTLSArrayData = 0; - pThreadLocalData->cTLSData = 0; - pThreadLocalData->pNonCollectibleTlsReferenceData = 0; + pThreadLocalData->pCollectibleTlsArrayData = 0; + pThreadLocalData->cCollectibleTlsData = 0; + pThreadLocalData->pNonCollectibleTlsArrayData = 0; pThreadLocalData->cNonCollectibleTlsData = 0; while (pThreadLocalData->pInFlightData != NULL) @@ -547,7 +547,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) if (index.GetTLSIndexType() == TLSIndexType::NonCollectible) { - PTRARRAYREF tlsArray = (PTRARRAYREF)UNCHECKED_OBJECTREF_TO_OBJECTREF(t_ThreadStatics.pNonCollectibleTlsReferenceData); + PTRARRAYREF tlsArray = (PTRARRAYREF)UNCHECKED_OBJECTREF_TO_OBJECTREF(t_ThreadStatics.pNonCollectibleTlsArrayData); if (t_ThreadStatics.cNonCollectibleTlsData <= index.GetIndexOffset()) { GCPROTECT_BEGIN(tlsArray); @@ -559,7 +559,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) tlsArrayNew->SetAt(i, tlsArray->GetAt(i)); } } - t_ThreadStatics.pNonCollectibleTlsReferenceData = OBJECTREF_TO_UNCHECKED_OBJECTREF(tlsArrayNew); + t_ThreadStatics.pNonCollectibleTlsArrayData = OBJECTREF_TO_UNCHECKED_OBJECTREF(tlsArrayNew); tlsArray = tlsArrayNew; t_ThreadStatics.cNonCollectibleTlsData = tlsArrayNew->GetNumComponents() + NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY; GCPROTECT_END(); @@ -577,20 +577,20 @@ void* GetThreadLocalStaticBase(TLSIndex index) } else { - int32_t cTLSData = t_ThreadStatics.cTLSData; - if (cTLSData <= index.GetIndexOffset()) + int32_t cCollectibleTlsData = t_ThreadStatics.cCollectibleTlsData; + if (cCollectibleTlsData <= index.GetIndexOffset()) { // Grow the underlying TLS array SpinLockHolder spinLock(&t_ThreadStatics.pThread->m_TlsSpinLock); - int32_t newcTLSData = index.GetIndexOffset() + 8; // Leave a bit of margin - uintptr_t* pNewTLSArrayData = new uintptr_t[newcTLSData]; - memset(pNewTLSArrayData, 0, newcTLSData * sizeof(uintptr_t)); - if (cTLSData > 0) - memcpy(pNewTLSArrayData, (void*)t_ThreadStatics.pTLSArrayData, cTLSData * sizeof(uintptr_t)); - uintptr_t* pOldArray = (uintptr_t*)t_ThreadStatics.pTLSArrayData; - t_ThreadStatics.pTLSArrayData = (TADDR)pNewTLSArrayData; - cTLSData = newcTLSData; - t_ThreadStatics.cTLSData = cTLSData; + int32_t newcCollectibleTlsData = index.GetIndexOffset() + 8; // Leave a bit of margin + uintptr_t* pNewTLSArrayData = new uintptr_t[newcCollectibleTlsData]; + memset(pNewTLSArrayData, 0, newcCollectibleTlsData * sizeof(uintptr_t)); + if (cCollectibleTlsData > 0) + memcpy(pNewTLSArrayData, (void*)t_ThreadStatics.pCollectibleTlsArrayData, cCollectibleTlsData * sizeof(uintptr_t)); + uintptr_t* pOldArray = (uintptr_t*)t_ThreadStatics.pCollectibleTlsArrayData; + t_ThreadStatics.pCollectibleTlsArrayData = (TADDR)pNewTLSArrayData; + cCollectibleTlsData = newcCollectibleTlsData; + t_ThreadStatics.cCollectibleTlsData = cCollectibleTlsData; delete[] pOldArray; } @@ -602,7 +602,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) size_t cbNewTLSLoaderHandles = sizeof(LOADERHANDLE) * cNewTLSLoaderHandles; LOADERHANDLE* pNewTLSLoaderHandles = new LOADERHANDLE[cNewTLSLoaderHandles]; memset(pNewTLSLoaderHandles, 0, cbNewTLSLoaderHandles); - if (cTLSData > 0) + if (cCollectibleTlsData > 0) memcpy(pNewTLSLoaderHandles, (void*)t_ThreadStatics.pThread->pLoaderHandles, t_ThreadStatics.pThread->cLoaderHandles * sizeof(LOADERHANDLE)); LOADERHANDLE* pOldArray = t_ThreadStatics.pThread->pLoaderHandles; @@ -611,8 +611,8 @@ void* GetThreadLocalStaticBase(TLSIndex index) delete[] pOldArray; } - TADDR pTLSArrayData = t_ThreadStatics.pTLSArrayData; - gcBaseAddresses.ppTLSBaseAddress = reinterpret_cast(reinterpret_cast(pTLSArrayData) + index.GetIndexOffset()); + TADDR pCollectibleTlsArrayData = t_ThreadStatics.pCollectibleTlsArrayData; + gcBaseAddresses.ppTLSBaseAddress = reinterpret_cast(reinterpret_cast(pCollectibleTlsArrayData) + index.GetIndexOffset()); gcBaseAddresses.pTLSBaseAddress = *gcBaseAddresses.ppTLSBaseAddress; } @@ -765,7 +765,7 @@ void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pInde if (!usedDirectOnThreadLocalDataPath) { - uint32_t tlsRawIndex = g_NextNonCollectibleTLSSlot++; + uint32_t tlsRawIndex = g_NextNonCollectibleTlsSlot++; newTLSIndex = TLSIndex(TLSIndexType::NonCollectible, tlsRawIndex); g_pThreadStaticNonCollectibleTypeIndices->Set(newTLSIndex, pMT, gcStatic); } @@ -958,7 +958,7 @@ void GetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) #endif // TARGET_WINDOWS pInfo->offsetOfMaxThreadStaticBlocks = (uint32_t)(threadStaticBaseOffset + offsetof(ThreadLocalData, cNonCollectibleTlsData)); - pInfo->offsetOfThreadStaticBlocks = (uint32_t)(threadStaticBaseOffset + offsetof(ThreadLocalData, pNonCollectibleTlsReferenceData)); + pInfo->offsetOfThreadStaticBlocks = (uint32_t)(threadStaticBaseOffset + offsetof(ThreadLocalData, pNonCollectibleTlsArrayData)); pInfo->offsetOfBaseOfThreadLocalData = (uint32_t)threadStaticBaseOffset; } #endif // !DACCESS_COMPILE @@ -967,7 +967,7 @@ void GetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo) void EnumThreadMemoryRegions(ThreadLocalData *pThreadLocalData, CLRDataEnumMemoryFlags flags) { SUPPORTS_DAC; - DacEnumMemoryRegion(dac_cast(pThreadLocalData->pTLSArrayData), pThreadLocalData->cTLSData, flags); + DacEnumMemoryRegion(dac_cast(pThreadLocalData->pCollectibleTlsArrayData), pThreadLocalData->cCollectibleTlsData, flags); PTR_InFlightTLSData pInFlightData = pThreadLocalData->pInFlightData; while (pInFlightData != NULL) { diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index 159f425e8f1ce..4f934e3429781 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -4,77 +4,28 @@ // Thread local storage is designed to be as efficient as possible. // This leads to several different access patterns. // -// There shall be a global TLS data structure used for all threads. This is initialized before any managed code is permitted to run on a thread -// struct TLSArray -// { -// int32_t cTLSData; // Size in bytes of offset into the TLS array which is valid -// void* pTLSArrayData; // Points at the Thread local array data. -// }; // -// Used to store access to TLS data for a single index when the TLS is accessed while the class constructor is running -// struct InFlightTLSData -// { -// InFlightTLSData* pNext; // Points at the next in-flight TLS data -// TLSIndex tlsIndex; // The TLS index for the static -// void* pTLSData; // The TLS data for the static -// }; -// -// struct ThreadLocalLoaderAllocator -// { -// ThreadLocalLoaderAllocator* pNext; // Points at the next thread local loader allocator -// LoaderAllocator* pLoaderAllocator; // The loader allocator that has a TLS used in this thread -// bool ReportToGC(PromoteFunction* fn, ScanContext* sc, int flags); // Reports the thread local loader allocator state to the GC, returns true if the ThreadLocalLoaderAllocator structure should be removed from the linked list. This is what allows the GC statics for collectible types to actually exist on the nonGC thread local storage array -// }; -// -// struct ThreadLocalData -// { -// TLSArray nongcArray; // Array for nonGC data, as well as collectible GC static. cTLSData is initialized to PRE_ALLOCATED_TLS_NONGC_SLOT_COUNT * sizeof(void*) - 1, and pTLSArrayData points at memory of size PRE_ALLOCATED_TLS_NONGC_SLOT_COUNT * sizeof(void*) at thread startup -// TLSArray gcArray; // Array for non-collectible GC pointers. cTLSData is initialized to PRE_ALLOCATED_TLS_GC_SLOT_COUNT * sizeof(OBJECTREF) + sizeof(void*) * 2 - 1, and pTLSArrayData points at a managed object[], initialized to an object array of size PRE_ALLOCATED_TLS_GC_SLOT_COUNT at thread startup -// InFlightTLSData* pNext; // Points at the next in-flight TLS data -// }; -// -// struct TLSIndex -// { -// int32_t TLSIndexRawIndex; -// int32_t GetIndexOffset() { return TLSIndexRawIndex & 0xFFFFFF; } -// int8_t GetTLSArrayOffset() { return TLSIndexRawIndex >> 24; } -// }; +// Access pattern for a TLS static that is on a dynamically growing array +// 0. Get the TLS index somehow +// 1. Get TLS pointer to OS managed TLS block for the current thread ie. pThreadLocalData = &t_ThreadStatics +// 2. Read 1 integer value (pThreadLocalData->cCollectibleTlsData OR pThreadLocalData->cNonCollectibleTlsData) +// 3. Compare cTlsData against the index we're looking up (if (cTlsData < index.GetIndexOffset())) +// 4. If the index is not within range, jump to step 9. +// 5. Read 1 pointer value from TLS block (pThreadLocalData->pCollectibleTlsArrayData OR pThreadLocalData->pNonCollectibleTlsArrayData) +// 6. Read 1 pointer from within the TLS Array. (pTLSBaseAddress = *(intptr_t*)(((uint8_t*)pTlsArrayData) + index.GetIndexOffset()); +// 7. If pointer is NULL jump to step 9 (if pTLSBaseAddress == NULL) +// 8. Return pTLSBaseAddress +// 9. Tail-call a helper (return GetThreadLocalStaticBase(index)) // -// thread_local ThreadLocalData t_ThreadStatics; -// SArray* g_pNonGCTLSIndexToMethodTable; -// int g_maxNonGCTlsSize; -// SArray* g_pGCTLSIndexToMethodTable; -// int g_maxGCTlsSlots; +// In addition, we support accessing a TLS static that is directly on the ThreadLocalData structure. This is used for scenarios where the +// runtime native code needs to share a TLS variable between native and managed code, and for the first few TLS slots that are used by non-collectible, non-GC statics. +// We may also choose to use it for improved performance in the future, as it generates the most efficient code. // -// Access pattern for a TLS static +// Access pattern for a TLS static that is directly on the ThreadLocalData structure // 0. Get the TLS index somehow // 1. Get TLS pointer to OS managed TLS block for the current thread ie. pThreadLocalData = &t_ThreadStatics -// 2. Get the TLSArray for the TLS index (pTLSArray = ((uint8_t*)pThreadLocalData) + index.GetTLSArrayOffset()) -// 3. Read 1 integer value (cTLSData=pThreadLocalData->cTLSData) -// 4. Compare cTLSData against the index we're looking up (if (cTLSData < index.GetIndexOffset())) -// 5. If the index is not within range, jump to step 10. -// 6. Read 1 pointer value from TLS block (pTLSArrayData=pThreadLocalData->pTLSArrayData) -// 7. Read 1 pointer from within the TLS Array. (pTLSBaseAddress = *(intptr_t*)(((uint8_t*)pTLSArrayData) + index.GetIndexOffset()); -// 8. If pointer is NULL jump to step 10 (if pTLSBaseAddress == NULL) -// 9. Return pTLSBaseAddress -// 10. Tail-call a helper (return GetThreadLocalStaticBase(index)) -// -// The Runtime shall define a couple of well known TLS indices. These are used for the most common -// TLS statics, and are used to avoid the overhead of checking for the index being in range, and -// the class constructor for having run, so that we can skip steps 3, 4, 5, and 8. It shall do this -// by allocating the associated memory before permitting any code to run on the thread. +// 2. Add the index offset to the start of the ThreadLocalData structure (pTLSBaseAddress = ((uint8_t*)pThreadLocalData) + index.GetIndexOffset()) // -// Psuedocode for -// ref byte GetThreadLocalStaticBase(uint index) -// { -// Do the access pattern above, but if the TLS array is too small, allocate a new one, and if the base pointer is NULL, call the class constructor for the static. -// if After all that the base pointer is still NULL, walk the InFlightTLSData chain to see if it exists in there. -// If the InFlightTLSData chain has a value -// check to see if the class constructor has run. If it has completed, update the base pointer in the TLS array, and delete the InFlightTLSData entry. -// return the found value -// ELSE -// allocate a new InFlightTLSData entry, and return the address of the pTLSData field. -// } // // Rationale for basic decisions here // 1. We want access to TLS statics to be as fast as possible, especially for extremely common @@ -92,8 +43,8 @@ class Thread; enum class TLSIndexType { - NonCollectible, // IndexOffset for this form of TLSIndex is scaled by sizeof(OBJECTREF) and used as an index into the array at ThreadLocalData::pNonCollectibleTlsReferenceData to get the final address - Collectible, // IndexOffset for this form of TLSIndex is scaled by sizeof(void*) and then added to ThreadLocalData::pTLSArrayData to get the final address + NonCollectible, // IndexOffset for this form of TLSIndex is scaled by sizeof(OBJECTREF) and used as an index into the array at ThreadLocalData::pNonCollectibleTlsArrayData to get the final address + Collectible, // IndexOffset for this form of TLSIndex is scaled by sizeof(void*) and then added to ThreadLocalData::pCollectibleTlsArrayData to get the final address DirectOnThreadLocalData, // IndexOffset for this form of TLS index is an offset into the ThreadLocalData structure itself. This is used for very high performance scenarios, and scenario where the runtime native code needs to hold a TLS pointer to a managed TLS slot. Each one of these is hand-opted into this model. }; @@ -119,11 +70,11 @@ typedef DPTR(InFlightTLSData) PTR_InFlightTLSData; struct ThreadLocalData { - alignas(8) // This is to ensure that the ExtendedDirectThreadLocalTLSData is aligned to be able to hold a double on arm legally - int32_t cNonCollectibleTlsData; // Size of offset into the non-collectible TLS array which is valid, NOTE: this is relative to the start of the pNonCollectibleTlsReferenceData object, not the start of the data in the array - int32_t cTLSData; // Size of offset into the TLS array which is valid - PTR_Object pNonCollectibleTlsReferenceData; - TADDR pTLSArrayData; // Points at the Thread local array data. + DAC_ALIGNAS(UINT64) // This is to ensure that the ExtendedDirectThreadLocalTLSData is aligned to be able to hold a double on arm legally + int32_t cNonCollectibleTlsData; // Size of offset into the non-collectible TLS array which is valid, NOTE: this is relative to the start of the pNonCollectibleTlsArrayData object, not the start of the data in the array + int32_t cCollectibleTlsData; // Size of offset into the TLS array which is valid + PTR_Object pNonCollectibleTlsArrayData; + TADDR pCollectibleTlsArrayData; // Points at the Thread local array data. Thread *pThread; PTR_InFlightTLSData pInFlightData; // Points at the in-flight TLS data (TLS data that exists before the class constructor finishes running) TADDR ThreadBlockingInfo_First; // System.Threading.ThreadBlockingInfo.First, This starts the region of ThreadLocalData which is referenceable by TLSIndexType::DirectOnThreadLocalData From 100c2adb1817baece3d0605025e35e9c1e361bf0 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 3 May 2024 15:19:43 -0700 Subject: [PATCH 71/82] Delete Statics heap at correct time --- src/coreclr/vm/loaderallocator.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index a267ae62795f7..ec5bf82c597bf 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -1385,6 +1385,12 @@ void LoaderAllocator::Terminate() m_pLowFrequencyHeap = NULL; } + if ((m_pStaticsHeap != NULL) && (m_pStaticsHeap != m_pHighFrequencyHeap)) + { + m_pStaticsHeap->~LoaderHeap(); + m_pStaticsHeap = NULL; + } + if (m_pHighFrequencyHeap != NULL) { #ifdef STUBLINKER_GENERATES_UNWIND_INFO @@ -1395,12 +1401,6 @@ void LoaderAllocator::Terminate() m_pHighFrequencyHeap = NULL; } - if ((m_pStaticsHeap != NULL) && m_pStaticsHeap != m_pHighFrequencyHeap) - { - m_pStaticsHeap->~LoaderHeap(); - m_pStaticsHeap = NULL; - } - if (m_pStubHeap != NULL) { #ifdef STUBLINKER_GENERATES_UNWIND_INFO From 270e99157b235a9074583f47f969762b583b06ff Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 7 May 2024 14:58:47 -0700 Subject: [PATCH 72/82] Code review updates - Adjust comment to use overriden instead of overrode - Add scheme to easily look at statics structures without needing to do math in the debugger - Adjust comment on DynamicStaticsInfo and GenericsStaticsInfo to be correct - Elaborate on comment on non-thread local implementation of GetNonGCThreadStaticsBasePointer and GetGCThreadStaticsBasePointer - Fixup contracts in the threadstatics work and elsewhere, as well as be more intentional about DAC vs non-DAC code. --- .../RuntimeHelpers.CoreCLR.cs | 2 +- src/coreclr/vm/loaderallocator.cpp | 181 +++++++++--------- src/coreclr/vm/methodtable.cpp | 42 +++- src/coreclr/vm/methodtable.h | 23 ++- src/coreclr/vm/threadstatics.cpp | 94 ++++++--- src/coreclr/vm/threadstatics.h | 47 +++-- 6 files changed, 253 insertions(+), 136 deletions(-) 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 a609805af4ce4..b358e7989fdac 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 @@ -725,7 +725,7 @@ internal unsafe struct MethodTableAuxiliaryData private uint Flags; private const uint enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode = 0x0002; // Whether we have checked the overridden Equals or GetHashCode - private const uint enum_flag_CanCompareBitsOrUseFastGetHashCode = 0x0004; // Is any field type or sub field type overrode Equals or GetHashCode + private const uint enum_flag_CanCompareBitsOrUseFastGetHashCode = 0x0004; // Is any field type or sub field type overridden Equals or GetHashCode public bool HasCheckedCanCompareBitsOrUseFastGetHashCode => (Flags & enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode) != 0; diff --git a/src/coreclr/vm/loaderallocator.cpp b/src/coreclr/vm/loaderallocator.cpp index ec5bf82c597bf..c35d13d6afc77 100644 --- a/src/coreclr/vm/loaderallocator.cpp +++ b/src/coreclr/vm/loaderallocator.cpp @@ -1195,6 +1195,7 @@ void LoaderAllocator::Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory) } else { + _ASSERTE(m_pHighFrequencyHeap != NULL); m_pStaticsHeap = m_pHighFrequencyHeap; } @@ -2263,67 +2264,67 @@ void LoaderAllocator::AllocateBytesForStaticVariables(DynamicStaticsInfo* pStati } CONTRACTL_END; - if (cbMem > 0) + if (cbMem == 0) + return; + + if (IsCollectible()) { - if (IsCollectible()) + GCX_COOP(); + uint32_t doubleSlots = AlignUp(cbMem, sizeof(double)) / sizeof(double); + BASEARRAYREF ptrArray = (BASEARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_R8, doubleSlots); + GCPROTECT_BEGIN(ptrArray); + // Keep this allocation alive till the LoaderAllocator is collected + AllocateHandle(ptrArray); + CrstHolder cs(&m_crstLoaderAllocator); { - GCX_COOP(); - uint32_t doubleSlots = AlignUp(cbMem, sizeof(double)) / sizeof(double); - BASEARRAYREF ptrArray = (BASEARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_R8, doubleSlots); - GCPROTECT_BEGIN(ptrArray); - // Keep this allocation alive till the LoaderAllocator is collected - AllocateHandle(ptrArray); - CrstHolder cs(&m_crstLoaderAllocator); + if (pStaticsInfo->GetNonGCStaticsPointer() == NULL) { - if (pStaticsInfo->GetNonGCStaticsPointer() == NULL) - { - GCX_FORBID(); - // Allocating a weak interior handle is a tricky thing. - // 1. If there are multiple weak interior handles that point at a given interior pointer location, there will be heap corruption - // 2. If the interior handle is created, but not registered for cleanup, it is a memory leak - // 3. Since the weak interior handle doesn't actually keep the object alive, it needs to be kept alive by some other means - // - // We work around these details by the following means - // 1. We use a LOADERHANDLE to keep the object alive until the LoaderAllocator is freed. - // 2. We hold the crstLoaderAllocatorLock, and double check to wnsure that the data is ready to be filled in - // 3. We create the weak interior handle, and register it for cleanup (which can fail with an OOM) before updating the statics data to have the pointer - // 4. Registration for cleanup cannot trigger a GC - // 5. We then unconditionally set the statics pointer. - WeakInteriorHandleHolder weakHandleHolder = GetAppDomain()->CreateWeakInteriorHandle(ptrArray, &pStaticsInfo->m_pNonGCStatics); - RegisterHandleForCleanupLocked(weakHandleHolder.GetValue()); - weakHandleHolder.SuppressRelease(); - bool didUpdateStaticsPointer = pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */false, (TADDR)ptrArray->GetDataPtr()); - _ASSERTE(didUpdateStaticsPointer); - } + GCX_FORBID(); + // Allocating a weak interior handle is a tricky thing. + // 1. If there are multiple weak interior handles that point at a given interior pointer location, there will be heap corruption + // 2. If the interior handle is created, but not registered for cleanup, it is a memory leak + // 3. Since the weak interior handle doesn't actually keep the object alive, it needs to be kept alive by some other means + // + // We work around these details by the following means + // 1. We use a LOADERHANDLE to keep the object alive until the LoaderAllocator is freed. + // 2. We hold the crstLoaderAllocatorLock, and double check to wnsure that the data is ready to be filled in + // 3. We create the weak interior handle, and register it for cleanup (which can fail with an OOM) before updating the statics data to have the pointer + // 4. Registration for cleanup cannot trigger a GC + // 5. We then unconditionally set the statics pointer. + WeakInteriorHandleHolder weakHandleHolder = GetAppDomain()->CreateWeakInteriorHandle(ptrArray, &pStaticsInfo->m_pNonGCStatics); + RegisterHandleForCleanupLocked(weakHandleHolder.GetValue()); + weakHandleHolder.SuppressRelease(); + bool didUpdateStaticsPointer = pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */false, (TADDR)ptrArray->GetDataPtr()); + _ASSERTE(didUpdateStaticsPointer); } - GCPROTECT_END(); } - else + GCPROTECT_END(); + } + else + { + uint32_t initialcbMem = cbMem; + if (initialcbMem >= 8) { - uint32_t initialcbMem = cbMem; - if (initialcbMem >= 8) - { - cbMem = ALIGN_UP(cbMem, sizeof(double)); + cbMem = ALIGN_UP(cbMem, sizeof(double)); #ifndef TARGET_64BIT - cbMem += 4; // We need to align the memory to 8 bytes so that static doubles, and static int64's work - // and this allocator doesn't guarantee 8 byte alignment, so allocate 4 bytes extra, and alignup. - // Technically this isn't necessary on X86, but it's simpler to do it unconditionally. + cbMem += 4; // We need to align the memory to 8 bytes so that static doubles, and static int64's work + // and this allocator doesn't guarantee 8 byte alignment, so allocate 4 bytes extra, and alignup. + // Technically this isn't necessary on X86, but it's simpler to do it unconditionally. #endif - } - else - { - // Always allocate in multiples of pointer size - cbMem = ALIGN_UP(cbMem, sizeof(TADDR)); - } - uint8_t* pbMem = (uint8_t*)(void*)GetStaticsHeap()->AllocMem(S_SIZE_T(cbMem)); + } + else + { + // Always allocate in multiples of pointer size + cbMem = ALIGN_UP(cbMem, sizeof(TADDR)); + } + uint8_t* pbMem = (uint8_t*)(void*)GetStaticsHeap()->AllocMem(S_SIZE_T(cbMem)); #ifndef TARGET_64BIT // Second part of alignment work - if (initialcbMem >= 8) - { - pbMem = (uint8_t*)ALIGN_UP(pbMem, 8); - } -#endif - pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */false, (TADDR)pbMem); + if (initialcbMem >= 8) + { + pbMem = (uint8_t*)ALIGN_UP(pbMem, 8); } +#endif + pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */false, (TADDR)pbMem); } } @@ -2337,51 +2338,51 @@ void LoaderAllocator::AllocateGCHandlesBytesForStaticVariables(DynamicStaticsInf } CONTRACTL_END; - if (cSlots > 0) + if (cSlots == 0) + return; + + if (IsCollectible()) { - if (IsCollectible()) + GCX_COOP(); + BASEARRAYREF ptrArray = (BASEARRAYREF)AllocateObjectArray(cSlots, g_pObjectClass, /* bAllocateInPinnedHeap = */FALSE); + GCPROTECT_BEGIN(ptrArray); + if (pMTToFillWithStaticBoxes != NULL) { - GCX_COOP(); - BASEARRAYREF ptrArray = (BASEARRAYREF)AllocateObjectArray(cSlots, g_pObjectClass, /* bAllocateInPinnedHeap = */FALSE); - GCPROTECT_BEGIN(ptrArray); - if (pMTToFillWithStaticBoxes != NULL) - { - OBJECTREF* pArray = (OBJECTREF*)ptrArray->GetDataPtr(); - GCPROTECT_BEGININTERIOR(pArray); - pMTToFillWithStaticBoxes->AllocateRegularStaticBoxes(&pArray); - GCPROTECT_END(); - } - // Keep this allocation alive till the LoaderAllocator is collected - AllocateHandle(ptrArray); - CrstHolder cs(&m_crstLoaderAllocator); - { - if (pStaticsInfo->GetGCStaticsPointer() == NULL) - { - GCX_FORBID(); - // Allocating a weak interior handle is a tricky thing. - // 1. If there are multiple weak interior handles that point at a given interior pointer location, there will be heap corruption - // 2. If the interior handle is created, but not registered for cleanup, it is a memory leak - // 3. Since the weak interior handle doesn't actually keep the object alive, it needs to be kept alive by some other means - // - // We work around these details by the following means - // 1. We use a LOADERHANDLE to keep the object alive until the LoaderAllocator is freed. - // 2. We hold the crstLoaderAllocatorLock, and double check to wnsure that the data is ready to be filled in - // 3. We create the weak interior handle, and register it for cleanup (which can fail with an OOM) before updating the statics data to have the pointer - // 4. Registration for cleanup cannot trigger a GC - // 5. We then unconditionally set the statics pointer. - WeakInteriorHandleHolder weakHandleHolder = GetAppDomain()->CreateWeakInteriorHandle(ptrArray, &pStaticsInfo->m_pGCStatics); - RegisterHandleForCleanupLocked(weakHandleHolder.GetValue()); - weakHandleHolder.SuppressRelease(); - bool didUpdateStaticsPointer = pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */true, (TADDR)ptrArray->GetDataPtr()); - _ASSERTE(didUpdateStaticsPointer); - } - } + OBJECTREF* pArray = (OBJECTREF*)ptrArray->GetDataPtr(); + GCPROTECT_BEGININTERIOR(pArray); + pMTToFillWithStaticBoxes->AllocateRegularStaticBoxes(&pArray); GCPROTECT_END(); } - else + // Keep this allocation alive till the LoaderAllocator is collected + AllocateHandle(ptrArray); + CrstHolder cs(&m_crstLoaderAllocator); { - GetDomain()->AllocateObjRefPtrsInLargeTable(cSlots, pStaticsInfo, pMTToFillWithStaticBoxes); + if (pStaticsInfo->GetGCStaticsPointer() == NULL) + { + GCX_FORBID(); + // Allocating a weak interior handle is a tricky thing. + // 1. If there are multiple weak interior handles that point at a given interior pointer location, there will be heap corruption + // 2. If the interior handle is created, but not registered for cleanup, it is a memory leak + // 3. Since the weak interior handle doesn't actually keep the object alive, it needs to be kept alive by some other means + // + // We work around these details by the following means + // 1. We use a LOADERHANDLE to keep the object alive until the LoaderAllocator is freed. + // 2. We hold the crstLoaderAllocatorLock, and double check to wnsure that the data is ready to be filled in + // 3. We create the weak interior handle, and register it for cleanup (which can fail with an OOM) before updating the statics data to have the pointer + // 4. Registration for cleanup cannot trigger a GC + // 5. We then unconditionally set the statics pointer. + WeakInteriorHandleHolder weakHandleHolder = GetAppDomain()->CreateWeakInteriorHandle(ptrArray, &pStaticsInfo->m_pGCStatics); + RegisterHandleForCleanupLocked(weakHandleHolder.GetValue()); + weakHandleHolder.SuppressRelease(); + bool didUpdateStaticsPointer = pStaticsInfo->InterlockedUpdateStaticsPointer(/* isGCPointer */true, (TADDR)ptrArray->GetDataPtr()); + _ASSERTE(didUpdateStaticsPointer); + } } + GCPROTECT_END(); + } + else + { + GetDomain()->AllocateObjRefPtrsInLargeTable(cSlots, pStaticsInfo, pMTToFillWithStaticBoxes); } } #endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index a48ed9fb85544..6e9ae7e5a665f 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -670,16 +670,31 @@ void MethodTable::AllocateAuxiliaryData(LoaderAllocator *pAllocator, Module *pLo pMTAuxiliaryData = (MethodTableAuxiliaryData *)(pAuxiliaryDataRegion + prependedAllocationSpace); pMTAuxiliaryData->SetLoaderModule(pLoaderModule); + + // NOTE: There is a - sign here making it so that the offset points to BEFORE the MethodTableAuxiliaryData pMTAuxiliaryData->SetOffsetToNonVirtualSlots(-sizeofStaticsStructure); + m_pAuxiliaryData = pMTAuxiliaryData; if (HasFlag(staticsFlags, MethodTableStaticsFlags::Present)) { MethodTableAuxiliaryData::GetDynamicStaticsInfo(pMTAuxiliaryData)->Init(this); +#ifdef _DEBUG + pMTAuxiliaryData->m_debugOnlyDynamicStatics = MethodTableAuxiliaryData::GetDynamicStaticsInfo(pMTAuxiliaryData); +#endif } +#ifdef _DEBUG + if (HasFlag(staticsFlags, MethodTableStaticsFlags::Generic)) + { + pMTAuxiliaryData->m_debugOnlyGenericStatics = MethodTableAuxiliaryData::GetGenericStaticsInfo(pMTAuxiliaryData); + } +#endif if (HasFlag(staticsFlags, MethodTableStaticsFlags::Thread)) { MethodTableAuxiliaryData::GetThreadStaticsInfo(pMTAuxiliaryData)->Init(); +#ifdef _DEBUG + pMTAuxiliaryData->m_debugOnlyThreadStatics = MethodTableAuxiliaryData::GetThreadStaticsInfo(pMTAuxiliaryData); +#endif } } @@ -4341,7 +4356,14 @@ void MethodTable::CheckRunClassInitThrowing() void MethodTable::EnsureStaticDataAllocated() { - WRAPPER_NO_CONTRACT; + CONTRACTL + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM()); + } + CONTRACTL_END; + PTR_MethodTableAuxiliaryData pAuxiliaryData = GetAuxiliaryDataForWrite(); if (!pAuxiliaryData->IsStaticDataAllocated() && IsDynamicStatics()) { @@ -4358,6 +4380,14 @@ void MethodTable::EnsureStaticDataAllocated() void MethodTable::AttemptToPreinit() { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + INJECT_FAULT(COMPlusThrowOM()); + } + CONTRACTL_END; + if (IsClassInited()) return; @@ -4390,7 +4420,14 @@ void MethodTable::AttemptToPreinit() void MethodTable::EnsureTlsIndexAllocated() { - WRAPPER_NO_CONTRACT; + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + PTR_MethodTableAuxiliaryData pAuxiliaryData = GetAuxiliaryDataForWrite(); if (!pAuxiliaryData->IsTlsIndexAllocated() && GetNumThreadStaticFields() > 0) { @@ -4413,7 +4450,6 @@ void MethodTable::EnsureTlsIndexAllocated() GetTLSIndexForThreadStatic(this, true, &pThreadStaticsInfo->GCTlsIndex, bytesNeeded); } } - } pAuxiliaryData->SetIsTlsIndexAllocated(); } diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index c13b8191ea152..ba69a320a0c70 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -329,7 +329,7 @@ struct MethodTableAuxiliaryData enum_flag_Initialized = 0x0001, enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode = 0x0002, // Whether we have checked the overridden Equals or GetHashCode - enum_flag_CanCompareBitsOrUseFastGetHashCode = 0x0004, // Is any field type or sub field type overrode Equals or GetHashCode + enum_flag_CanCompareBitsOrUseFastGetHashCode = 0x0004, // Is any field type or sub field type overridden Equals or GetHashCode enum_flag_HasApproxParent = 0x0010, // enum_unused = 0x0020, @@ -376,6 +376,10 @@ struct MethodTableAuxiliaryData DWORD m_dwPadding; // Just to keep the size a multiple of 8 #endif + // These pointers make it easier to examine the various statics structures in the debugger + PTR_DynamicStaticsInfo m_debugOnlyDynamicStatics; + PTR_GenericsStaticsInfo m_debugOnlyGenericStatics; + PTR_ThreadStaticsInfo m_debugOnlyThreadStatics; #endif public: @@ -525,9 +529,8 @@ struct MethodTableAuxiliaryData } }; // struct MethodTableAuxiliaryData -// Any Generic MethodTable which has static variables has this structure. Note that it ends -// with a DynamicStatics structure so that lookups for just DynamicStatics will find that structure -// when looking for statics pointers +// All MethodTables which have static variables will have one of these. It contains the pointers necessary to +// find the normal (non-thread) static variables of the type. struct DynamicStaticsInfo { private: @@ -607,6 +610,11 @@ struct DynamicStaticsInfo return dac_cast(dac_cast(pAuxiliaryData) - sizeof(DynamicStaticsInfo)); } +// Any Generic MethodTable which has static variables has this structure. Note that it ends +// with a DynamicStatics structure so that lookups for just DynamicStatics will find that structure +// when looking for statics pointers +// In addition, for simplicity in access, all MethodTables which have a ThreadStaticsInfo have this structure +// but it is unitialized and should not be used if the type is not generic struct GenericsStaticsInfo { // Pointer to field descs for statics @@ -2432,7 +2440,12 @@ class MethodTable inline PTR_BYTE GetGCThreadStaticsBasePointer(); #endif //!DACCESS_COMPILE - // Do not use except in DAC and profiler scenarios + // Do not use except in DAC and profiler scenarios. + // These apis are difficult to use correctly. Users must + // 1. Be aware that a GC may make the address returned invalid + // 2. Be aware that a thread shutdown may make the address returned invalid + // 3. Be aware that a collectible assembly could be collected, thus rendering the address returned invalid + // This is particularly relevant as a problem for profiler developers, but they are given the tools (such as GC events) to be notified of situations where these invariants may not hold inline PTR_BYTE GetNonGCThreadStaticsBasePointer(PTR_Thread pThread); inline PTR_BYTE GetGCThreadStaticsBasePointer(PTR_Thread pThread); diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 65a3870822cd8..3b763e7e143b7 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -21,10 +21,30 @@ struct ThreadLocalLoaderAllocator }; typedef DPTR(ThreadLocalLoaderAllocator) PTR_ThreadLocalLoaderAllocator; +#ifndef DACCESS_COMPILE +static TLSIndexToMethodTableMap *g_pThreadStaticCollectibleTypeIndices; +static TLSIndexToMethodTableMap *g_pThreadStaticNonCollectibleTypeIndices; +static PTR_MethodTable g_pMethodTablesForDirectThreadLocalData[offsetof(ThreadLocalData, ExtendedDirectThreadLocalTLSData) - offsetof(ThreadLocalData, ThreadBlockingInfo_First) + EXTENDED_DIRECT_THREAD_LOCAL_SIZE]; + +static Volatile s_GCsWhichDoRelocateAndCanEmptyOutTheTLSIndices = 0; +static uint32_t g_NextTLSSlot = 1; +static uint32_t g_NextNonCollectibleTlsSlot = NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY; +static uint32_t g_directThreadLocalTLSBytesAvailable = EXTENDED_DIRECT_THREAD_LOCAL_SIZE; + +static CrstStatic g_TLSCrst; +#endif + // This can be used for out of thread access to TLS data. PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread* pThread, TLSIndex index) { - LIMITED_METHOD_CONTRACT; + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + #ifndef DACCESS_COMPILE // Since this api can be used from a different thread, we need a lock to keep it all safe SpinLockHolder spinLock(&pThread->m_TlsSpinLock); @@ -76,13 +96,11 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread* pThread, TLSIndex index) return dac_cast(pTLSBaseAddress); } -TLSIndexToMethodTableMap *g_pThreadStaticCollectibleTypeIndices; -TLSIndexToMethodTableMap *g_pThreadStaticNonCollectibleTypeIndices; -PTR_MethodTable g_pMethodTablesForDirectThreadLocalData[offsetof(ThreadLocalData, ExtendedDirectThreadLocalTLSData) - offsetof(ThreadLocalData, ThreadBlockingInfo_First) + EXTENDED_DIRECT_THREAD_LOCAL_SIZE]; - +#ifndef DACCESS_COMPILE int32_t IndexOffsetToDirectThreadLocalIndex(int32_t indexOffset) { LIMITED_METHOD_CONTRACT; + int32_t adjustedIndexOffset = indexOffset + OFFSETOF__CORINFO_Array__data; _ASSERTE(((uint32_t)adjustedIndexOffset) >= offsetof(ThreadLocalData, ThreadBlockingInfo_First)); int32_t directThreadLocalIndex = adjustedIndexOffset - offsetof(ThreadLocalData, ThreadBlockingInfo_First); @@ -90,7 +108,9 @@ int32_t IndexOffsetToDirectThreadLocalIndex(int32_t indexOffset) _ASSERTE(directThreadLocalIndex >= 0); return directThreadLocalIndex; } +#endif // DACCESS_COMPILE +#ifndef DACCESS_COMPILE PTR_MethodTable LookupMethodTableForThreadStaticKnownToBeAllocated(TLSIndex index) { CONTRACTL @@ -114,9 +134,9 @@ PTR_MethodTable LookupMethodTableForThreadStaticKnownToBeAllocated(TLSIndex inde return g_pThreadStaticCollectibleTypeIndices->LookupTlsIndexKnownToBeAllocated(index); } } +#endif // DACCESS_COMPILE -TADDR isGCFlag = 0x1; - +#ifndef DACCESS_COMPILE PTR_MethodTable LookupMethodTableAndFlagForThreadStatic(TLSIndex index, bool *pIsGCStatic, bool *pIsCollectible) { CONTRACTL @@ -144,8 +164,9 @@ PTR_MethodTable LookupMethodTableAndFlagForThreadStatic(TLSIndex index, bool *pI } return retVal; } +#endif // DACCESS_COMPILE - +#ifndef DACCESS_COMPILE // Report a TLS index to the GC, but if it is no longer in use and should be cleared out, return false bool ReportTLSIndexCarefully(TLSIndex index, int32_t cLoaderHandles, PTR_LOADERHANDLE pLoaderHandles, PTR_PTR_Object ppTLSBaseAddress, promote_func* fn, ScanContext* sc) { @@ -156,6 +177,7 @@ bool ReportTLSIndexCarefully(TLSIndex index, int32_t cLoaderHandles, PTR_LOADERH MODE_COOPERATIVE; } CONTRACTL_END; + bool isGCStatic; bool isCollectible; @@ -176,11 +198,10 @@ bool ReportTLSIndexCarefully(TLSIndex index, int32_t cLoaderHandles, PTR_LOADERH fn(ppTLSBaseAddress, sc, 0 /* could be GC_CALL_INTERIOR or GC_CALL_PINNED */); return true; } +#endif // DACCESS_COMPILE // We use a scheme where the TLS data on each thread will be cleaned up within a GC promotion round or two. #ifndef DACCESS_COMPILE -static Volatile s_GCsWhichDoRelocateAndCanEmptyOutTheTLSIndices = 0; - void NotifyThreadStaticGCHappened() { LIMITED_METHOD_CONTRACT; @@ -188,6 +209,7 @@ void NotifyThreadStaticGCHappened() } #endif +#ifndef DACCESS_COMPILE void ScanThreadStaticRoots(Thread* pThread, bool forGC, promote_func* fn, ScanContext* sc) { CONTRACTL @@ -232,7 +254,6 @@ void ScanThreadStaticRoots(Thread* pThread, bool forGC, promote_func* fn, ScanCo if (!ReportTLSIndexCarefully(pInFlightData->tlsIndex, cLoaderHandles, pThread->pLoaderHandles, dac_cast(&pInFlightData->pTLSData), fn, sc)) { // TLS index is now dead. We should delete it, as the ReportTLSIndexCarefully function will have already deleted any assocated LOADERHANDLE -#ifndef DACCESS_COMPILE if (forGC) { PTR_InFlightTLSData pNext = pInFlightData->pNext; @@ -240,7 +261,6 @@ void ScanThreadStaticRoots(Thread* pThread, bool forGC, promote_func* fn, ScanCo pInFlightData = pNext; continue; } -#endif } pInFlightData = pInFlightData->pNext; } @@ -255,12 +275,9 @@ void ScanThreadStaticRoots(Thread* pThread, bool forGC, promote_func* fn, ScanCo } // Report non-collectible object array -#ifndef DACCESS_COMPILE fn(&pThreadLocalData->pNonCollectibleTlsArrayData, sc, 0 /* could be GC_CALL_INTERIOR or GC_CALL_PINNED */); -#else - fn(dac_cast(&pThreadLocalData->pNonCollectibleTlsArrayData), sc, 0 /* could be GC_CALL_INTERIOR or GC_CALL_PINNED */); -#endif } +#endif // DACCESS_COMPILE #ifndef DACCESS_COMPILE @@ -330,6 +347,14 @@ void TLSIndexToMethodTableMap::Clear(TLSIndex index, uint8_t whenCleared) bool TLSIndexToMethodTableMap::FindClearedIndex(uint8_t whenClearedMarkerToAvoid, TLSIndex* pIndex) { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + for (const auto& entry : *this) { if (entry.IsClearedValue) @@ -350,10 +375,6 @@ bool TLSIndexToMethodTableMap::FindClearedIndex(uint8_t whenClearedMarkerToAvoid return false; } -uint32_t g_NextTLSSlot = 1; -uint32_t g_NextNonCollectibleTlsSlot = NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY; -CrstStatic g_TLSCrst; - void InitializeThreadStaticData() { CONTRACTL @@ -457,6 +478,8 @@ void FreeLoaderAllocatorHandlesForTLSData(Thread *pThread) void AssertThreadStaticDataFreed(ThreadLocalData *pThreadLocalData) { + LIMITED_METHOD_CONTRACT; + if (!IsAtProcessExit() && !g_fEEShutDown) { _ASSERTE(pThreadLocalData->pThread == NULL); @@ -514,6 +537,14 @@ void FreeThreadStaticData(ThreadLocalData *pThreadLocalData, Thread* pThread) void SetTLSBaseValue(TADDR *ppTLSBaseAddress, TADDR pTLSBaseAddress, bool useGCBarrier) { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + if (useGCBarrier) { SetObjectReference((OBJECTREF *)ppTLSBaseAddress, (OBJECTREF)ObjectToOBJECTREF((Object*)pTLSBaseAddress)); @@ -523,6 +554,7 @@ void SetTLSBaseValue(TADDR *ppTLSBaseAddress, TADDR pTLSBaseAddress, bool useGCB *ppTLSBaseAddress = pTLSBaseAddress; } } + void* GetThreadLocalStaticBase(TLSIndex index) { CONTRACTL @@ -709,11 +741,15 @@ void* GetThreadLocalStaticBase(TLSIndex index) return reinterpret_cast(gcBaseAddresses.pTLSBaseAddress); } -static uint32_t g_directThreadLocalTLSBytesAvailable = EXTENDED_DIRECT_THREAD_LOCAL_SIZE; - void GetTLSIndexForThreadStatic(MethodTable* pMT, bool gcStatic, TLSIndex* pIndex, uint32_t bytesNeeded) { - WRAPPER_NO_CONTRACT; + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; GCX_COOP(); CrstHolder ch(&g_TLSCrst); @@ -814,6 +850,8 @@ static void* GetTlsIndexObjectAddress(); bool CanJITOptimizeTLSAccess() { + LIMITED_METHOD_CONTRACT; + bool optimizeThreadStaticAccess = false; #if defined(TARGET_ARM) // Optimization is disabled for linux/windows arm @@ -844,6 +882,8 @@ EXTERN_C uint32_t _tls_index; /*********************************************************************/ static uint32_t ThreadLocalOffset(void* p) { + LIMITED_METHOD_CONTRACT; + PTEB Teb = NtCurrentTeb(); uint8_t** pTls = (uint8_t**)Teb->ThreadLocalStoragePointer; uint8_t* pOurTls = pTls[_tls_index]; @@ -854,6 +894,8 @@ extern "C" void* GetThreadVarsAddress(); static void* GetThreadVarsSectionAddressFromDesc(uint8_t* p) { + LIMITED_METHOD_CONTRACT; + _ASSERT(p[0] == 0x48 && p[1] == 0x8d && p[2] == 0x3d); // At this point, `p` contains the instruction pointer and is pointing to the above opcodes. @@ -869,6 +911,8 @@ static void* GetThreadVarsSectionAddressFromDesc(uint8_t* p) static void* GetThreadVarsSectionAddress() { + LIMITED_METHOD_CONTRACT; + #ifdef TARGET_AMD64 // On x64, the address is related to rip, so, disassemble the function, // read the offset, and then relative to the IP, find the final address of @@ -890,6 +934,8 @@ extern "C" void* GetTlsIndexObjectDescOffset(); static void* GetThreadStaticDescriptor(uint8_t* p) { + LIMITED_METHOD_CONTRACT; + if (!(p[0] == 0x66 && p[1] == 0x48 && p[2] == 0x8d && p[3] == 0x3d)) { // The optimization is disabled if coreclr is not compiled in .so format. @@ -912,6 +958,8 @@ static void* GetThreadStaticDescriptor(uint8_t* p) static void* GetTlsIndexObjectAddress() { + LIMITED_METHOD_CONTRACT; + uint8_t* p = reinterpret_cast(&GetTlsIndexObjectDescOffset); return GetThreadStaticDescriptor(p); } diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index 4f934e3429781..2773c909d9c81 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -100,9 +100,9 @@ class TLSIndexToMethodTableMap uint32_t m_collectibleEntries; TLSIndexType m_indexType; - TADDR IsGCFlag() const { return (TADDR)0x1; } - TADDR IsCollectibleFlag() const { return (TADDR)0x2; } - TADDR UnwrapValue(TADDR input) const { return input & ~3; } + TADDR IsGCFlag() const { LIMITED_METHOD_CONTRACT; return (TADDR)0x1; } + TADDR IsCollectibleFlag() const { LIMITED_METHOD_CONTRACT; return (TADDR)0x2; } + TADDR UnwrapValue(TADDR input) const { LIMITED_METHOD_CONTRACT; return input & ~3; } public: TLSIndexToMethodTableMap(TLSIndexType indexType) : pMap(dac_cast(dac_cast(0))), m_maxIndex(0), m_collectibleEntries(0), m_indexType(indexType) { } @@ -182,28 +182,37 @@ class TLSIndexToMethodTableMap entry m_entry; iterator(const TLSIndexToMethodTableMap& pMap, uint32_t currentIndex) : m_pMap(pMap), m_entry(pMap.Lookup(TLSIndex(pMap.m_indexType, currentIndex))) {} public: - const entry& operator*() const { return m_entry; } - const entry* operator->() const { return &m_entry; } + const entry& operator*() const { LIMITED_METHOD_CONTRACT; return m_entry; } + const entry* operator->() const { LIMITED_METHOD_CONTRACT; return &m_entry; } - bool operator==(const iterator& other) const { return (m_entry.TlsIndex == other.m_entry.TlsIndex); } - bool operator!=(const iterator& other) const { return (m_entry.TlsIndex != other.m_entry.TlsIndex); } + bool operator==(const iterator& other) const { LIMITED_METHOD_CONTRACT; return (m_entry.TlsIndex == other.m_entry.TlsIndex); } + bool operator!=(const iterator& other) const { LIMITED_METHOD_CONTRACT; return (m_entry.TlsIndex != other.m_entry.TlsIndex); } iterator& operator++() { + LIMITED_METHOD_CONTRACT; m_entry = m_pMap.Lookup(TLSIndex(m_entry.TlsIndex.TLSIndexRawIndex + 1)); return *this; } - iterator operator++(int) { iterator tmp = *this; ++(*this); return tmp; } + iterator operator++(int) + { + LIMITED_METHOD_CONTRACT; + iterator tmp = *this; + ++(*this); + return tmp; + } }; iterator begin() const { + LIMITED_METHOD_CONTRACT; iterator it(*this, 0); return it; } iterator end() const { + LIMITED_METHOD_CONTRACT; return iterator(*this, m_maxIndex); } @@ -222,14 +231,15 @@ class TLSIndexToMethodTableMap TLSIndexToMethodTableMap::iterator m_current; iterator(const TLSIndexToMethodTableMap::iterator& current) : m_current(current) {} public: - const entry& operator*() const { return *m_current; } - const entry* operator->() const { return m_current.operator->(); } + const entry& operator*() const { LIMITED_METHOD_CONTRACT; return *m_current; } + const entry* operator->() const { LIMITED_METHOD_CONTRACT; return m_current.operator->(); } - bool operator==(const iterator& other) const { return (m_current == other.m_current); } - bool operator!=(const iterator& other) const { return (m_current != other.m_current); } + bool operator==(const iterator& other) const { LIMITED_METHOD_CONTRACT; return (m_current == other.m_current); } + bool operator!=(const iterator& other) const { LIMITED_METHOD_CONTRACT; return (m_current != other.m_current); } iterator& operator++() { + LIMITED_METHOD_CONTRACT; TLSIndex oldIndex = m_current->TlsIndex; while (++m_current, m_current->TlsIndex != oldIndex) { @@ -239,11 +249,18 @@ class TLSIndexToMethodTableMap } return *this; } - iterator operator++(int) { iterator tmp = *this; ++(*this); return tmp; } + iterator operator++(int) + { + LIMITED_METHOD_CONTRACT; + iterator tmp = *this; + ++(*this); + return tmp; + } }; iterator begin() const { + LIMITED_METHOD_CONTRACT; iterator it(m_pMap.begin()); if (!(it->IsCollectible)) { @@ -254,6 +271,7 @@ class TLSIndexToMethodTableMap iterator end() const { + LIMITED_METHOD_CONTRACT; return iterator(m_pMap.end()); } }; @@ -266,6 +284,7 @@ class TLSIndexToMethodTableMap CollectibleEntriesCollection CollectibleEntries() const { + LIMITED_METHOD_CONTRACT; return CollectibleEntriesCollection(*this); } @@ -301,9 +320,9 @@ class TLSIndexToMethodTableMap }; PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread *pThreadLocalData, TLSIndex index); -void ScanThreadStaticRoots(Thread* pThread, bool forGC, promote_func* fn, ScanContext* sc); #ifndef DACCESS_COMPILE +void ScanThreadStaticRoots(Thread* pThread, bool forGC, promote_func* fn, ScanContext* sc); PTR_MethodTable LookupMethodTableForThreadStaticKnownToBeAllocated(TLSIndex index); void InitializeThreadStaticData(); void InitializeCurrentThreadsStaticData(Thread* pThread); From 2db5ffdc121ccedd0fccd9ab30fe30095f9e3fc6 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 7 May 2024 17:17:21 -0700 Subject: [PATCH 73/82] Initial pass at a BoTR chapter update. --- docs/design/coreclr/botr/type-system.md | 142 ++++++++++++++++++++++-- 1 file changed, 130 insertions(+), 12 deletions(-) diff --git a/docs/design/coreclr/botr/type-system.md b/docs/design/coreclr/botr/type-system.md index d111fd79f03f7..f98423de64f1a 100644 --- a/docs/design/coreclr/botr/type-system.md +++ b/docs/design/coreclr/botr/type-system.md @@ -193,19 +193,136 @@ This is enforced via an extensive and complicated set of enforcements within the - **ISSUE:** The signature walks performed are done with the normal signature walking code. This code is designed to load types as it walks the signature, but in this case the type load functionality is used with the assumption that no type load will actually be triggered. - **ISSUE:** Stackwalker requirements require support from not just the type system, but also the assembly loader. The Loader has had a number of issues meeting the needs of the type system here. -Type System and NGEN --------------------- +## Static variables -The type system data structures are a core part of what is saved into NGEN images. Unfortunately, these data structures logically have pointers within them that point to other NGEN images. In order to handle this situation, the type system data structures implement a concept known as restoration. - -In restoration, when a type system data structure is first needed, the data structure is fixed up with correct pointers. This is tied into the type loading levels described in the [Type Loader](type-loader.md) Book of the Runtime chapter. - -There also exists the concept of pre-restored data structures. This means that the data structure is sufficiently correct at NGEN image load time (after intra-module pointer fixups and eager load type fixups), that the data structure may be used as is. This optimization requires that the NGEN image be "hard bound" to its dependent assemblies. See NGEN documentation for further details. - -Type System and Domain Neutral Loading --------------------------------------- - -The type system is a core part of the implementation of domain neutral loading. This is exposed to customers through the LoaderOptimization options available at AppDomain creation. Mscorlib is always loaded as domain neutral. The core requirement of this feature is that the type system data structures must not require pointers to domain specific state. Primarily this manifests itself in requirements around static fields and class constructors. In particular, whether or not a class constructor has been run is not a part of the core MethodTable data structure for this reason, and there is a mechanism for storing static data attached to the DomainFile data structure instead of the MethodTable data structure. +Static variables in CoreCLR are handled by a combination of getting the "static base", and then adjusting it by an offset to get a pointer to the actual value. +We define the statics base as either non-gc or gc for each field. +Currently non-gc statics are any statics which are represented by primitive types (byte, sbyte, char, int, uint, long, ulong, float, double, pointers of various forms), and enums. +GC statics are any statics which are represented by classes or by user defined structures. +For user defined structure statics, the static variable is actually a pointer to a boxed instance of the structure. + +### Per type static variable information +As of .NET 9, the static variable bases are now all associated with their particular type. +As you can see from this diagram, the data for statics can be acquired by starting at a `MethodTable` and then getting either the `DynamicStaticsInfo` to get a statics pointer, or by getting a `ThreadStaticsInfo` to get a TLSIndex, which then can be used with the thread static variable system to get the actual thread static base. + +```mermaid +classDiagram +MethodTable : MethodTableAuxiliaryData* m_pAuxData +MethodTable --> MethodTableAuxiliaryData +MethodTableAuxiliaryData --> DynamicStaticsInfo : If has static variables +MethodTableAuxiliaryData --> GenericStaticsInfo : If is generic and has static variables +MethodTableAuxiliaryData --> ThreadStaticsInfo : If has thread local static variables + +DynamicStaticsInfo : StaticsPointer m_pGCStatics +DynamicStaticsInfo : StaticsPointer m_pNonGCStatics + +GenericStaticsInfo : FieldDesc* m_pFieldDescs + +ThreadStaticsInfo : TLSIndex NonGCTlsIndex +ThreadStaticsInfo : TLSIndex GCTlsIndex +``` + +```mermaid +classDiagram + +note for StaticsPointer "StaticsPointer is a pointer sized integer" +StaticsPointer : void* PointerToStaticBase +StaticsPointer : bool HasClassConstructorBeenRun + +note for TLSIndex "TLSIndex is a 32bit integer" +TLSIndex : TLSIndexType indexType +TLSIndex : 24bit int indexOffset +``` + +In the above diagram, you can see that we have separate fields for non-gc and gc statics, as well as thread and normal statics. +For normal statics, we use a single pointer sized field, which also happens to encode whether or not the class constructor has been run. +This is done to allow a lock free atomic access to both get the static field address as well as determine if the class constructor needs to be triggered. +For TLS statics, handling of detecting whether or not the class constructor has been run is a more complex process described as part of the thread statics infrastructure. +The `DynamicStaticsInfo` and `ThreadStaticsInfo` structures are accessed without any locks, so it is important to ensure that access to fields on these structures can be done with a single memory access, to avoid memory order tearing issues. + +Also, notably, for generic types, each field has a `FieldDesc` which is allocated per type instance, and is not shared by multiple canonical instances. + +#### Lifetime management for collectible statics + +Finally we have a concept of collectible assemblies in the CoreCLR runtime, so we need to handle lifetime management for static variables. +The approach chosen was to build a special GC handle type which will allow the runtime to have a pointer in the runtime data structures to the interior of a managed object on the GC heap. + +The requirement of behavior here is that a static variable cannot keep its own collectible assembly alive, and so collectible statics have the peculiar property that they can exist and be finalized before the collectible assembly is finally collected. +If there is some resurrection scenario, this can lead to very surprising behavior. + +### Thread Statics + +Thread statics are static variables which have a lifetime which is defined to be the shorter of the lifetime of the type containing the static, and the lifetime of the thread on which the static variable is accessed. +They are created by having a static variable on a type which is attributed with `[System.Runtime.CompilerServices.ThreadStaticAttribute]`. +The general scheme of how this works is to assign an "index" to the type which is the same on all threads, and then on each thread hold a data structure which is efficiently accessed by means of this index. +However, we have a few peculiarities in our approach. + +1. We segregate collectible and non-collectible thread statics (`TLSIndexType::NonCollectible` and `TLSIndexType::Collectible`) +2. We provide an ability to share a non-gc thread static between native CoreCLR code and managed code (Subset of `TLSIndexType::DirectOnThreadLocalData`) +3. We provide an extremely efficient means to access a small number of non-gc thread statics. (The rest of the usage of `TLSIndexType::DirectOnThreadLocalData`) + +For the purposes of t + +#### Per-Thread Statics Data structures +```mermaid +classDiagram + +note for ThreadLocalInfo "There is 1 of these per thread, and it is managed by the C++ compiler/OS using standard mechanisms. +It can be found as the t_ThreadStatics variable in a C++ compiler, and is also pointed at by the native Thread class." +ThreadLocalInfo : int cNonCollectibleTlsData +ThreadLocalInfo : void** pNonCollectibleTlsArrayData +ThreadLocalInfo : int cCollectibleTlsData +ThreadLocalInfo : void** pCollectibleTlsArrayData +ThreadLocalInfo : InFlightTLSData *pInFightData +ThreadLocalInfo : Thread* pThread +ThreadLocalInfo : Special Thread Statics Shared Between Native and Managed code +ThreadLocalInfo : byte[N] ExtendedDirectThreadLocalTLSData + +InFlightTLSData : InFlightTLSData* pNext +InFlightTLSData : TLSIndex tlsIndex +InFlightTLSData : void* pTLSData + +ThreadLocalInfo --> InFlightTLSData : For TLS statics which have their memory allocated, but have not been accessed since the class finished running its class constructor +InFlightTLSData --> InFlightTLSData : linked list +``` + +#### Access patterns for getting the thread statics address + +This is the pattern that the JIT will use to access a thread static which is not `DirectOnThreadLocalData`. + +0. Get the TLS index somehow +1. Get TLS pointer to OS managed TLS block for the current thread ie. `pThreadLocalData = &t_ThreadStatics` +2. Read 1 integer value `pThreadLocalData->cCollectibleTlsData OR pThreadLocalData->cNonCollectibleTlsData` +3. Compare cTlsData against the index we're looking up `if (cTlsData < index.GetIndexOffset())` +4. If the index is not within range, jump to step 9. +5. Read 1 pointer value from TLS block `pThreadLocalData->pCollectibleTlsArrayData` OR `pThreadLocalData->pNonCollectibleTlsArrayData` +6. Read 1 pointer from within the TLS Array. `pTLSBaseAddress = *(intptr_t*)(((uint8_t*)pTlsArrayData) + index.GetIndexOffset()` +7. If pointer is NULL jump to step 9 `if pTLSBaseAddress == NULL` +8. Return pTLSBaseAddress +9. Tail-call a helper `return GetThreadLocalStaticBase(index)` + +This is the pattern that the JIT will use to access a thread static which is on `DirectOnThreadLocalData` +0. Get the TLS index somehow +1. Get TLS pointer to OS managed TLS block for the current thread ie. `pThreadLocalData = &t_ThreadStatics` +2. Add the index offset to the start of the ThreadLocalData structure `pTLSBaseAddress = ((uint8_t*)pThreadLocalData) + index.GetIndexOffset()` + +#### Lifetime management for thread static variables +We distinguish between collectible and non-collectible thread static variables for efficiency purposes. + +A non-collectible thread static is a thread static defined on a type which cannot be collected by the runtime. +This describes most thread statics in actual observed practice. +The `DirectOnThreadLocalData` statics are a subset of this category which has a speical optimized form and does not need any GC reporting. +For non-collectible thread statics, the pointer (`pNonCollectibleTlsArrayData`) in the `ThreadLocalData` is a pointer to a managed `object[]` which points at either `object[]`, `byte[]`, or `double[]` arrays. +At GC scan time, the pointer to the initial object[] is the only detail which needs to be reported to the GC. + +A collectible thread static is a thread static which can be collected by the runtime. +This describes the static variables defined on types which can be collected by the runtime. +The pointer (`pCollectibleTlsArrayData`) in the `ThreadLocalData` is a pointer to a chunk of memory allocated via `malloc`, and holds pointers to `object[]`, `byte[]`, or `double[]` arrays. +At GC scan time, each managed object must individually be kept alive only if the type and thread is still alive. This requires properly handling several situations. +1. If a collectible assembly becomes unreferenced, but a thread static variable associated with it has a finalizer, the object must move to the finalization queue. +2. If a thread static variable associated with a collectible assembly refers to the collectible assembly `LoaderAllocator` via a series of object references, it must not provide a reason for the collectible assembly to be considered referenced. +3. If a collectible assembly is collected, then the associated static variables no longer exist, and the TLSIndex values associated with that collectible assembly becomre re-useable. +4. If a thread is no longer executing, then all thread statics associated with that thread are no longer kept alive. Physical Architecture ===================== @@ -221,6 +338,7 @@ Major parts of the type system are found in: - Array – Code for handling the special cases required for array processing - VirtualStubDispatch.cpp/h/inl – Code for virtual stub dispatch - VirtualCallStubCpu.hpp – Processor specific code for virtual stub dispatch. +- threadstatics.cpp/h - Handling for thread static variables. Major entry points are BuildMethodTable, LoadTypeHandleThrowing, CanCastTo\*, GetMethodDescFromMemberDefOrRefOrSpecThrowing, GetFieldDescFromMemberRefThrowing, CompareSigs, and VirtualCallStubManager::ResolveWorkerStatic. From 0be084dc8661e6d0e20b644872b7989a53f57ddd Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 8 May 2024 11:31:16 -0700 Subject: [PATCH 74/82] Get rid of the complicated GC interaction model for collectible assemblies and just use a Weak Long Handle instead --- docs/design/coreclr/botr/type-system.md | 19 ++- src/coreclr/vm/gcenv.ee.cpp | 10 +- src/coreclr/vm/jithelpers.cpp | 8 +- src/coreclr/vm/threadstatics.cpp | 179 ++++++++---------------- src/coreclr/vm/threadstatics.h | 17 +-- 5 files changed, 89 insertions(+), 144 deletions(-) diff --git a/docs/design/coreclr/botr/type-system.md b/docs/design/coreclr/botr/type-system.md index f98423de64f1a..559c03cad80af 100644 --- a/docs/design/coreclr/botr/type-system.md +++ b/docs/design/coreclr/botr/type-system.md @@ -280,7 +280,7 @@ ThreadLocalInfo : byte[N] ExtendedDirectThreadLocalTLSData InFlightTLSData : InFlightTLSData* pNext InFlightTLSData : TLSIndex tlsIndex -InFlightTLSData : void* pTLSData +InFlightTLSData : OBJECTHANDLE hTLSData ThreadLocalInfo --> InFlightTLSData : For TLS statics which have their memory allocated, but have not been accessed since the class finished running its class constructor InFlightTLSData --> InFlightTLSData : linked list @@ -294,12 +294,14 @@ This is the pattern that the JIT will use to access a thread static which is not 1. Get TLS pointer to OS managed TLS block for the current thread ie. `pThreadLocalData = &t_ThreadStatics` 2. Read 1 integer value `pThreadLocalData->cCollectibleTlsData OR pThreadLocalData->cNonCollectibleTlsData` 3. Compare cTlsData against the index we're looking up `if (cTlsData < index.GetIndexOffset())` -4. If the index is not within range, jump to step 9. +4. If the index is not within range, jump to step 11. 5. Read 1 pointer value from TLS block `pThreadLocalData->pCollectibleTlsArrayData` OR `pThreadLocalData->pNonCollectibleTlsArrayData` 6. Read 1 pointer from within the TLS Array. `pTLSBaseAddress = *(intptr_t*)(((uint8_t*)pTlsArrayData) + index.GetIndexOffset()` -7. If pointer is NULL jump to step 9 `if pTLSBaseAddress == NULL` -8. Return pTLSBaseAddress -9. Tail-call a helper `return GetThreadLocalStaticBase(index)` +7. If pointer is NULL jump to step 11 `if pTLSBaseAddress == NULL` +8. If TLS index not a Collectible index, return pTLSBaseAddress +9. if `ObjectFromHandle((OBJECTHANDLE)pTLSBaseAddress)` is NULL, jump to step 11 +10. Return `ObjectFromHandle((OBJECTHANDLE)pTLSBaseAddress)` +11. Tail-call a helper `return GetThreadLocalStaticBase(index)` This is the pattern that the JIT will use to access a thread static which is on `DirectOnThreadLocalData` 0. Get the TLS index somehow @@ -324,6 +326,13 @@ At GC scan time, each managed object must individually be kept alive only if the 3. If a collectible assembly is collected, then the associated static variables no longer exist, and the TLSIndex values associated with that collectible assembly becomre re-useable. 4. If a thread is no longer executing, then all thread statics associated with that thread are no longer kept alive. +The approach chosen is to use a pair of different handle types. +For efficient access, the handle type stored in the dynamically adjusted array is a WeakTrackResurrection GCHandle. +This handle instance is associated with the slot in the TLS data, not with the exact instantiation, so it can be re-used when the if the associated collectible assembly is collected, and then the slot is re-used. +In addition, each slot that is in use will have a `LOADERHANDLE` which will keep the object alive until the `LoaderAllocator` is freed. +This `LOADERHANDLE` will be abandoned if the `LoaderAllocator` is collected, but that's ok, as `LOADERHANDLE` only needs to be cleaned up if the `LoaderAllocator` isn't collected. +On thread destroy, for each collectible slot in the tls array, we will explicitly free the `LOADERHANDLE` on the correct `LoaderAllocator`. + Physical Architecture ===================== diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index 130126a710568..1e083e04260ee 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -302,7 +302,7 @@ void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen, #endif // FEATURE_EVENT_TRACE ScanStackRoots(pThread, fn, sc); ScanTailCallArgBufferRoots(pThread, fn, sc); - ScanThreadStaticRoots(pThread, /*forGC*/ true, fn, sc); + ScanThreadStaticRoots(pThread, fn, sc); #ifdef FEATURE_EVENT_TRACE sc->dwEtwRootKind = kEtwGCRootKindOther; #endif // FEATURE_EVENT_TRACE @@ -311,12 +311,6 @@ void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen, } } - if (sc->thread_number == 0 || !GCHeapUtilities::IsServerHeap()) - { - // This function must be called once per run of calls to ScanThreadStaticRoots - NotifyThreadStaticGCHappened(); - } - // In server GC, we should be competing for marking the statics // It's better to do this *after* stack scanning, because this way // we can make up for imbalances in stack scanning @@ -627,7 +621,7 @@ void GcScanRootsForProfilerAndETW(promote_func* fn, int condemned, int max_gen, #endif // FEATURE_EVENT_TRACE ScanStackRoots(pThread, fn, sc); ScanTailCallArgBufferRoots(pThread, fn, sc); - ScanThreadStaticRoots(pThread, /*forGC*/ false, fn, sc); + ScanThreadStaticRoots(pThread, fn, sc); #ifdef FEATURE_EVENT_TRACE sc->dwEtwRootKind = kEtwGCRootKindOther; #endif // FEATURE_EVENT_TRACE diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index aaa18c0a154a5..0f6f0220c082d 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1430,8 +1430,12 @@ FORCEINLINE void* GetThreadLocalStaticBaseIfExistsAndInitialized(TLSIndex index) return NULL; } - TADDR pCollectibleTlsArrayData = t_ThreadStatics.pCollectibleTlsArrayData; - pTLSBaseAddress = *reinterpret_cast(reinterpret_cast(pCollectibleTlsArrayData) + index.GetIndexOffset()); + OBJECTHANDLE* pCollectibleTlsArrayData = t_ThreadStatics.pCollectibleTlsArrayData; + pCollectibleTlsArrayData += index.GetIndexOffset(); + OBJECTHANDLE objHandle = *pCollectibleTlsArrayData; + if (IsHandleNullUnchecked(objHandle)) + return NULL; + pTLSBaseAddress = dac_cast(OBJECTREFToObject(ObjectFromHandle(objHandle))); } return reinterpret_cast(pTLSBaseAddress); } diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 3b763e7e143b7..6c0bb8d282259 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -6,11 +6,18 @@ struct InFlightTLSData { #ifndef DACCESS_COMPILE - InFlightTLSData(TLSIndex index) : pNext(NULL), tlsIndex(index), pTLSData(dac_cast(NULL)) { } + InFlightTLSData(TLSIndex index) : pNext(NULL), tlsIndex(index), hTLSData(0) { } + ~InFlightTLSData() + { + if (!IsHandleNullUnchecked(hTLSData)) + { + DestroyTypedHandle(hTLSData); + } + } #endif // !DACCESS_COMPILE PTR_InFlightTLSData pNext; // Points at the next in-flight TLS data TLSIndex tlsIndex; // The TLS index for the static - TADDR pTLSData; // The TLS data for the static + OBJECTHANDLE hTLSData; // The TLS data for the static }; @@ -76,8 +83,12 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread* pThread, TLSIndex index) return NULL; } - TADDR pCollectibleTlsArrayData = pThreadLocalData->pCollectibleTlsArrayData; - pTLSBaseAddress = *(dac_cast(pCollectibleTlsArrayData) + index.GetIndexOffset()); + TADDR pCollectibleTlsArrayData = dac_cast(pThreadLocalData->pCollectibleTlsArrayData); + pCollectibleTlsArrayData += index.GetIndexOffset() * sizeof(TADDR); + OBJECTHANDLE objHandle = *dac_cast(pCollectibleTlsArrayData); + if (IsHandleNullUnchecked(objHandle)) + return NULL; + pTLSBaseAddress = dac_cast(OBJECTREFToObject(ObjectFromHandle(objHandle))); } if (pTLSBaseAddress == (TADDR)NULL) { @@ -87,7 +98,7 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread* pThread, TLSIndex index) { if (pInFlightData->tlsIndex == index) { - pTLSBaseAddress = pInFlightData->pTLSData; + pTLSBaseAddress = dac_cast(OBJECTREFToObject(ObjectFromHandle(pInFlightData->hTLSData))); break; } pInFlightData = pInFlightData->pNext; @@ -167,50 +178,7 @@ PTR_MethodTable LookupMethodTableAndFlagForThreadStatic(TLSIndex index, bool *pI #endif // DACCESS_COMPILE #ifndef DACCESS_COMPILE -// Report a TLS index to the GC, but if it is no longer in use and should be cleared out, return false -bool ReportTLSIndexCarefully(TLSIndex index, int32_t cLoaderHandles, PTR_LOADERHANDLE pLoaderHandles, PTR_PTR_Object ppTLSBaseAddress, promote_func* fn, ScanContext* sc) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - bool isGCStatic; - bool isCollectible; - - if (index.GetTLSIndexType() == TLSIndexType::DirectOnThreadLocalData) - return true; - - PTR_MethodTable pMT = LookupMethodTableAndFlagForThreadStatic(index, &isGCStatic, &isCollectible); - _ASSERTE(index.GetTLSIndexType() == TLSIndexType::NonCollectible || (((pMT == NULL) || isCollectible) && index.GetTLSIndexType() == TLSIndexType::Collectible)); - if (index.GetTLSIndexType() == TLSIndexType::Collectible && pLoaderHandles[index.GetIndexOffset()] == (LOADERHANDLE)NULL) - { - // The TLS index is not in use. This either means that the TLS index was never used, or that it was - // used for a collectible assembly, and that assembly has been freed. In the latter case, we may need to - // clean this entry up - *ppTLSBaseAddress = NULL; - return false; - } - - fn(ppTLSBaseAddress, sc, 0 /* could be GC_CALL_INTERIOR or GC_CALL_PINNED */); - return true; -} -#endif // DACCESS_COMPILE - -// We use a scheme where the TLS data on each thread will be cleaned up within a GC promotion round or two. -#ifndef DACCESS_COMPILE -void NotifyThreadStaticGCHappened() -{ - LIMITED_METHOD_CONTRACT; - s_GCsWhichDoRelocateAndCanEmptyOutTheTLSIndices += 1; -} -#endif - -#ifndef DACCESS_COMPILE -void ScanThreadStaticRoots(Thread* pThread, bool forGC, promote_func* fn, ScanContext* sc) +void ScanThreadStaticRoots(Thread* pThread, promote_func* fn, ScanContext* sc) { CONTRACTL { @@ -220,62 +188,13 @@ void ScanThreadStaticRoots(Thread* pThread, bool forGC, promote_func* fn, ScanCo } CONTRACTL_END; - if (forGC) - { - // Clean out LoaderHandles for dead collectible assemblies - for (int32_t i = 0; i < pThread->cLoaderHandles; ++i) - { - TLSIndex index(TLSIndexType::Collectible, i); - PTR_LOADERHANDLE pLoaderHandle = pThread->pLoaderHandles + i; - if (*pLoaderHandle != (LOADERHANDLE)NULL) - { - bool isGCStatic; - bool isCollectible; - PTR_MethodTable pMT = LookupMethodTableAndFlagForThreadStatic(index, &isGCStatic, &isCollectible); - _ASSERTE(isCollectible || pMT == NULL); - - if (pMT == NULL || !pMT->GetLoaderAllocator()->IsExposedObjectLive()) - { - *pLoaderHandle = (LOADERHANDLE)NULL; - } - } - } - } - if (pThread->m_ThreadLocalDataPtr == NULL) return; ThreadLocalData *pThreadLocalData = pThread->m_ThreadLocalDataPtr; - int32_t cLoaderHandles = pThread->cLoaderHandles; - PTR_InFlightTLSData pInFlightData = pThreadLocalData->pInFlightData; - while (pInFlightData != NULL) - { - if (!ReportTLSIndexCarefully(pInFlightData->tlsIndex, cLoaderHandles, pThread->pLoaderHandles, dac_cast(&pInFlightData->pTLSData), fn, sc)) - { - // TLS index is now dead. We should delete it, as the ReportTLSIndexCarefully function will have already deleted any assocated LOADERHANDLE - if (forGC) - { - PTR_InFlightTLSData pNext = pInFlightData->pNext; - delete pInFlightData; - pInFlightData = pNext; - continue; - } - } - pInFlightData = pInFlightData->pNext; - } - - PTR_TADDR pCollectibleTlsArrayData = dac_cast(pThreadLocalData->pCollectibleTlsArrayData); - int32_t cCollectibleTlsData = pThreadLocalData->cCollectibleTlsData; - for (int32_t i = 0; i < cCollectibleTlsData; ++i) - { - TLSIndex index(TLSIndexType::Collectible, i); - TADDR *pTLSBaseAddress = pCollectibleTlsArrayData + i; - ReportTLSIndexCarefully(index, cLoaderHandles, pThread->pLoaderHandles, dac_cast(pTLSBaseAddress), fn, sc); - } - // Report non-collectible object array - fn(&pThreadLocalData->pNonCollectibleTlsArrayData, sc, 0 /* could be GC_CALL_INTERIOR or GC_CALL_PINNED */); + fn(&pThreadLocalData->pNonCollectibleTlsArrayData, sc, 0); } #endif // DACCESS_COMPILE @@ -517,6 +436,14 @@ void FreeThreadStaticData(ThreadLocalData *pThreadLocalData, Thread* pThread) if (pThreadLocalData == NULL) return; + for (int32_t iTlsSlot = 0; iTlsSlot < pThreadLocalData->cCollectibleTlsData; ++iTlsSlot) + { + if (!IsHandleNullUnchecked(pThreadLocalData->pCollectibleTlsArrayData[iTlsSlot])) + { + DestroyLongWeakHandle(pThreadLocalData->pCollectibleTlsArrayData[iTlsSlot]); + } + } + delete[] (uint8_t*)pThreadLocalData->pCollectibleTlsArrayData; pThreadLocalData->pCollectibleTlsArrayData = 0; @@ -535,7 +462,7 @@ void FreeThreadStaticData(ThreadLocalData *pThreadLocalData, Thread* pThread) } } -void SetTLSBaseValue(TADDR *ppTLSBaseAddress, TADDR pTLSBaseAddress, bool useGCBarrier) +void SetTLSBaseValue(TADDR *ppTLSBaseAddress, TADDR pTLSBaseAddress, bool useGCBarrierInsteadOfHandleStore) { CONTRACTL { @@ -545,13 +472,14 @@ void SetTLSBaseValue(TADDR *ppTLSBaseAddress, TADDR pTLSBaseAddress, bool useGCB } CONTRACTL_END; - if (useGCBarrier) + if (useGCBarrierInsteadOfHandleStore) { SetObjectReference((OBJECTREF *)ppTLSBaseAddress, (OBJECTREF)ObjectToOBJECTREF((Object*)pTLSBaseAddress)); } else { - *ppTLSBaseAddress = pTLSBaseAddress; + OBJECTHANDLE objHandle = (OBJECTHANDLE)ppTLSBaseAddress; + StoreObjectInHandle(objHandle, (OBJECTREF)ObjectToOBJECTREF((Object*)pTLSBaseAddress)); } } @@ -567,7 +495,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) bool isGCStatic; bool isCollectible; - bool useWriteBarrierToWriteTLSBase = false; + bool staticIsNonCollectible = false; MethodTable *pMT = LookupMethodTableAndFlagForThreadStatic(index, &isGCStatic, &isCollectible); struct @@ -597,7 +525,7 @@ void* GetThreadLocalStaticBase(TLSIndex index) GCPROTECT_END(); } gcBaseAddresses.ppTLSBaseAddress = (TADDR*)(tlsArray->GetDataPtr() + (index.GetIndexOffset() - NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY)) ; - useWriteBarrierToWriteTLSBase = true; + staticIsNonCollectible = true; gcBaseAddresses.pTLSBaseAddress = *gcBaseAddresses.ppTLSBaseAddress; } else if (index.GetTLSIndexType() == TLSIndexType::DirectOnThreadLocalData) @@ -615,12 +543,12 @@ void* GetThreadLocalStaticBase(TLSIndex index) // Grow the underlying TLS array SpinLockHolder spinLock(&t_ThreadStatics.pThread->m_TlsSpinLock); int32_t newcCollectibleTlsData = index.GetIndexOffset() + 8; // Leave a bit of margin - uintptr_t* pNewTLSArrayData = new uintptr_t[newcCollectibleTlsData]; - memset(pNewTLSArrayData, 0, newcCollectibleTlsData * sizeof(uintptr_t)); + OBJECTHANDLE* pNewTLSArrayData = new OBJECTHANDLE[newcCollectibleTlsData]; + memset(pNewTLSArrayData, 0, newcCollectibleTlsData * sizeof(OBJECTHANDLE)); if (cCollectibleTlsData > 0) - memcpy(pNewTLSArrayData, (void*)t_ThreadStatics.pCollectibleTlsArrayData, cCollectibleTlsData * sizeof(uintptr_t)); - uintptr_t* pOldArray = (uintptr_t*)t_ThreadStatics.pCollectibleTlsArrayData; - t_ThreadStatics.pCollectibleTlsArrayData = (TADDR)pNewTLSArrayData; + memcpy(pNewTLSArrayData, (void*)t_ThreadStatics.pCollectibleTlsArrayData, cCollectibleTlsData * sizeof(OBJECTHANDLE)); + OBJECTHANDLE* pOldArray = (OBJECTHANDLE*)t_ThreadStatics.pCollectibleTlsArrayData; + t_ThreadStatics.pCollectibleTlsArrayData = pNewTLSArrayData; cCollectibleTlsData = newcCollectibleTlsData; t_ThreadStatics.cCollectibleTlsData = cCollectibleTlsData; delete[] pOldArray; @@ -643,9 +571,16 @@ void* GetThreadLocalStaticBase(TLSIndex index) delete[] pOldArray; } - TADDR pCollectibleTlsArrayData = t_ThreadStatics.pCollectibleTlsArrayData; - gcBaseAddresses.ppTLSBaseAddress = reinterpret_cast(reinterpret_cast(pCollectibleTlsArrayData) + index.GetIndexOffset()); - gcBaseAddresses.pTLSBaseAddress = *gcBaseAddresses.ppTLSBaseAddress; + OBJECTHANDLE* pCollectibleTlsArrayData = t_ThreadStatics.pCollectibleTlsArrayData; + pCollectibleTlsArrayData += index.GetIndexOffset(); + OBJECTHANDLE objHandle = *pCollectibleTlsArrayData; + if (IsHandleNullUnchecked(objHandle)) + { + objHandle = GetAppDomain()->CreateLongWeakHandle(NULL); + *pCollectibleTlsArrayData = objHandle; + } + gcBaseAddresses.ppTLSBaseAddress = reinterpret_cast(objHandle); + gcBaseAddresses.pTLSBaseAddress = dac_cast(OBJECTREFToObject(ObjectFromHandle(objHandle))); } if (gcBaseAddresses.pTLSBaseAddress == (TADDR)NULL) @@ -657,12 +592,11 @@ void* GetThreadLocalStaticBase(TLSIndex index) { if (pInFlightData->tlsIndex == index) { - gcBaseAddresses.pTLSBaseAddress = pInFlightData->pTLSData; - + gcBaseAddresses.pTLSBaseAddress = dac_cast(OBJECTREFToObject(ObjectFromHandle(pInFlightData->hTLSData))); if (pMT->IsClassInited()) { SpinLockHolder spinLock(&t_ThreadStatics.pThread->m_TlsSpinLock); - SetTLSBaseValue(gcBaseAddresses.ppTLSBaseAddress, gcBaseAddresses.pTLSBaseAddress, useWriteBarrierToWriteTLSBase); + SetTLSBaseValue(gcBaseAddresses.ppTLSBaseAddress, gcBaseAddresses.pTLSBaseAddress, staticIsNonCollectible); *ppOldNextPtr = pInFlightData->pNext; delete pInFlightData; } @@ -701,10 +635,13 @@ void* GetThreadLocalStaticBase(TLSIndex index) #endif } - NewHolder pInFlightData = NULL; - if (!pMT->IsClassInited()) + NewHolder pNewInFlightData = NULL; + if (!pMT->IsClassInited() && pInFlightData == NULL) { - pInFlightData = new InFlightTLSData(index); + pNewInFlightData = new InFlightTLSData(index); + HandleType handleType = staticIsNonCollectible ? HNDTYPE_STRONG : HNDTYPE_WEAK_LONG; + pNewInFlightData->hTLSData = GetAppDomain()->CreateTypedHandle(gc.tlsEntry, handleType); + pInFlightData = pNewInFlightData; } if (isCollectible) @@ -716,20 +653,20 @@ void* GetThreadLocalStaticBase(TLSIndex index) } // After this, we cannot fail - pInFlightData.SuppressRelease(); + pNewInFlightData.SuppressRelease(); { GCX_FORBID(); gcBaseAddresses.pTLSBaseAddress = (TADDR)OBJECTREFToObject(gc.tlsEntry); if (pInFlightData == NULL) { - SetTLSBaseValue(gcBaseAddresses.ppTLSBaseAddress, gcBaseAddresses.pTLSBaseAddress, useWriteBarrierToWriteTLSBase); + SetTLSBaseValue(gcBaseAddresses.ppTLSBaseAddress, gcBaseAddresses.pTLSBaseAddress, staticIsNonCollectible); } else { SpinLockHolder spinLock(&t_ThreadStatics.pThread->m_TlsSpinLock); pInFlightData->pNext = t_ThreadStatics.pInFlightData; - pInFlightData->pTLSData = gcBaseAddresses.pTLSBaseAddress; + StoreObjectInHandle(pInFlightData->hTLSData, gc.tlsEntry); t_ThreadStatics.pInFlightData = pInFlightData; } } diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index 2773c909d9c81..0422629e05f9c 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -10,12 +10,14 @@ // 1. Get TLS pointer to OS managed TLS block for the current thread ie. pThreadLocalData = &t_ThreadStatics // 2. Read 1 integer value (pThreadLocalData->cCollectibleTlsData OR pThreadLocalData->cNonCollectibleTlsData) // 3. Compare cTlsData against the index we're looking up (if (cTlsData < index.GetIndexOffset())) -// 4. If the index is not within range, jump to step 9. +// 4. If the index is not within range, jump to step 11. // 5. Read 1 pointer value from TLS block (pThreadLocalData->pCollectibleTlsArrayData OR pThreadLocalData->pNonCollectibleTlsArrayData) // 6. Read 1 pointer from within the TLS Array. (pTLSBaseAddress = *(intptr_t*)(((uint8_t*)pTlsArrayData) + index.GetIndexOffset()); -// 7. If pointer is NULL jump to step 9 (if pTLSBaseAddress == NULL) -// 8. Return pTLSBaseAddress -// 9. Tail-call a helper (return GetThreadLocalStaticBase(index)) +// 7. If pointer is NULL jump to step 11 (if pTLSBaseAddress == NULL) +// 8. If TLS index not a Collectible index, return pTLSBaseAddress +// 9. if ObjectFromHandle((OBJECTHANDLE)pTLSBaseAddress) is NULL, jump to step 11 +// 10. Return ObjectFromHandle((OBJECTHANDLE)pTLSBaseAddress) +// 11. Tail-call a helper (return GetThreadLocalStaticBase(index)) // // In addition, we support accessing a TLS static that is directly on the ThreadLocalData structure. This is used for scenarios where the // runtime native code needs to share a TLS variable between native and managed code, and for the first few TLS slots that are used by non-collectible, non-GC statics. @@ -74,8 +76,8 @@ struct ThreadLocalData int32_t cNonCollectibleTlsData; // Size of offset into the non-collectible TLS array which is valid, NOTE: this is relative to the start of the pNonCollectibleTlsArrayData object, not the start of the data in the array int32_t cCollectibleTlsData; // Size of offset into the TLS array which is valid PTR_Object pNonCollectibleTlsArrayData; - TADDR pCollectibleTlsArrayData; // Points at the Thread local array data. - Thread *pThread; + DPTR(OBJECTHANDLE) pCollectibleTlsArrayData; // Points at the Thread local array data. + PTR_Thread pThread; PTR_InFlightTLSData pInFlightData; // Points at the in-flight TLS data (TLS data that exists before the class constructor finishes running) TADDR ThreadBlockingInfo_First; // System.Threading.ThreadBlockingInfo.First, This starts the region of ThreadLocalData which is referenceable by TLSIndexType::DirectOnThreadLocalData BYTE ExtendedDirectThreadLocalTLSData[EXTENDED_DIRECT_THREAD_LOCAL_SIZE]; @@ -322,7 +324,7 @@ class TLSIndexToMethodTableMap PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread *pThreadLocalData, TLSIndex index); #ifndef DACCESS_COMPILE -void ScanThreadStaticRoots(Thread* pThread, bool forGC, promote_func* fn, ScanContext* sc); +void ScanThreadStaticRoots(Thread* pThread, promote_func* fn, ScanContext* sc); PTR_MethodTable LookupMethodTableForThreadStaticKnownToBeAllocated(TLSIndex index); void InitializeThreadStaticData(); void InitializeCurrentThreadsStaticData(Thread* pThread); @@ -334,7 +336,6 @@ void FreeTLSIndicesForLoaderAllocator(LoaderAllocator *pLoaderAllocator); void* GetThreadLocalStaticBase(TLSIndex index); void GetThreadLocalStaticBlocksInfo (CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo); bool CanJITOptimizeTLSAccess(); -void NotifyThreadStaticGCHappened(); #else void EnumThreadMemoryRegions(ThreadLocalData* pThreadLocalData, CLRDataEnumMemoryFlags flags); #endif From 8d7243b21fdfb841b742fe3952d2c03f2c3d0bff Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 8 May 2024 12:17:16 -0700 Subject: [PATCH 75/82] Fix contract violation --- src/coreclr/vm/jitinterface.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 480784e1fbc1a..d3fc34521f15f 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -3675,14 +3675,14 @@ bool CEEInfo::getIsClassInitedFlagAddress(CORINFO_CLASS_HANDLE cls, CORINFO_CONS bool CEEInfo::getStaticBaseAddress(CORINFO_CLASS_HANDLE cls, bool isGc, CORINFO_CONST_LOOKUP* addr) { CONTRACTL { - NOTHROW; - GC_NOTRIGGER; + THROWS; + GC_TRIGGERS; MODE_PREEMPTIVE; } CONTRACTL_END; - bool result; + bool result = false; - JIT_TO_EE_TRANSITION_LEAF(); + JIT_TO_EE_TRANSITION(); TypeHandle clsTypeHandle(cls); PTR_MethodTable pMT = clsTypeHandle.AsMethodTable(); @@ -3701,7 +3701,7 @@ bool CEEInfo::getStaticBaseAddress(CORINFO_CLASS_HANDLE cls, bool isGc, CORINFO_ result = true; } - EE_TO_JIT_TRANSITION_LEAF(); + EE_TO_JIT_TRANSITION(); return result; } From b08d8eb534af968fa2caed2af0d60bb53a6c475e Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 8 May 2024 12:21:11 -0700 Subject: [PATCH 76/82] Fix markdownlint --- docs/design/coreclr/botr/type-system.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/design/coreclr/botr/type-system.md b/docs/design/coreclr/botr/type-system.md index 559c03cad80af..2cd5a0825dc19 100644 --- a/docs/design/coreclr/botr/type-system.md +++ b/docs/design/coreclr/botr/type-system.md @@ -196,7 +196,7 @@ This is enforced via an extensive and complicated set of enforcements within the ## Static variables Static variables in CoreCLR are handled by a combination of getting the "static base", and then adjusting it by an offset to get a pointer to the actual value. -We define the statics base as either non-gc or gc for each field. +We define the statics base as either non-gc or gc for each field. Currently non-gc statics are any statics which are represented by primitive types (byte, sbyte, char, int, uint, long, ulong, float, double, pointers of various forms), and enums. GC statics are any statics which are represented by classes or by user defined structures. For user defined structure statics, the static variable is actually a pointer to a boxed instance of the structure. @@ -309,7 +309,7 @@ This is the pattern that the JIT will use to access a thread static which is on 2. Add the index offset to the start of the ThreadLocalData structure `pTLSBaseAddress = ((uint8_t*)pThreadLocalData) + index.GetIndexOffset()` #### Lifetime management for thread static variables -We distinguish between collectible and non-collectible thread static variables for efficiency purposes. +We distinguish between collectible and non-collectible thread static variables for efficiency purposes. A non-collectible thread static is a thread static defined on a type which cannot be collected by the runtime. This describes most thread statics in actual observed practice. From 3b94fa0cc10dc462abbd48a06fdd8b05f2a5a429 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 14 May 2024 10:39:33 -0700 Subject: [PATCH 77/82] Adjust to code review feedback --- docs/design/coreclr/botr/type-system.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/design/coreclr/botr/type-system.md b/docs/design/coreclr/botr/type-system.md index 2cd5a0825dc19..0fb06e89bb7d7 100644 --- a/docs/design/coreclr/botr/type-system.md +++ b/docs/design/coreclr/botr/type-system.md @@ -198,8 +198,8 @@ This is enforced via an extensive and complicated set of enforcements within the Static variables in CoreCLR are handled by a combination of getting the "static base", and then adjusting it by an offset to get a pointer to the actual value. We define the statics base as either non-gc or gc for each field. Currently non-gc statics are any statics which are represented by primitive types (byte, sbyte, char, int, uint, long, ulong, float, double, pointers of various forms), and enums. -GC statics are any statics which are represented by classes or by user defined structures. -For user defined structure statics, the static variable is actually a pointer to a boxed instance of the structure. +GC statics are any statics which are represented by classes or by non-primitive valuetypes. +For struct statics, the static variable is actually a pointer to a boxed instance of the structure. ### Per type static variable information As of .NET 9, the static variable bases are now all associated with their particular type. @@ -236,7 +236,7 @@ TLSIndex : 24bit int indexOffset In the above diagram, you can see that we have separate fields for non-gc and gc statics, as well as thread and normal statics. For normal statics, we use a single pointer sized field, which also happens to encode whether or not the class constructor has been run. -This is done to allow a lock free atomic access to both get the static field address as well as determine if the class constructor needs to be triggered. +This is done to allow lock free atomic access to both get the static field address as well as determine if the class constructor needs to be triggered. For TLS statics, handling of detecting whether or not the class constructor has been run is a more complex process described as part of the thread statics infrastructure. The `DynamicStaticsInfo` and `ThreadStaticsInfo` structures are accessed without any locks, so it is important to ensure that access to fields on these structures can be done with a single memory access, to avoid memory order tearing issues. @@ -261,8 +261,6 @@ However, we have a few peculiarities in our approach. 2. We provide an ability to share a non-gc thread static between native CoreCLR code and managed code (Subset of `TLSIndexType::DirectOnThreadLocalData`) 3. We provide an extremely efficient means to access a small number of non-gc thread statics. (The rest of the usage of `TLSIndexType::DirectOnThreadLocalData`) -For the purposes of t - #### Per-Thread Statics Data structures ```mermaid classDiagram @@ -323,7 +321,7 @@ The pointer (`pCollectibleTlsArrayData`) in the `ThreadLocalData` is a pointer t At GC scan time, each managed object must individually be kept alive only if the type and thread is still alive. This requires properly handling several situations. 1. If a collectible assembly becomes unreferenced, but a thread static variable associated with it has a finalizer, the object must move to the finalization queue. 2. If a thread static variable associated with a collectible assembly refers to the collectible assembly `LoaderAllocator` via a series of object references, it must not provide a reason for the collectible assembly to be considered referenced. -3. If a collectible assembly is collected, then the associated static variables no longer exist, and the TLSIndex values associated with that collectible assembly becomre re-useable. +3. If a collectible assembly is collected, then the associated static variables no longer exist, and the TLSIndex values associated with that collectible assembly becomes re-useable. 4. If a thread is no longer executing, then all thread statics associated with that thread are no longer kept alive. The approach chosen is to use a pair of different handle types. From ecd26bcf8c48bf65ed7f2bf95b6665a62a4d85b5 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 24 May 2024 12:42:05 -0700 Subject: [PATCH 78/82] Code review comments --- src/coreclr/jit/helperexpansion.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index 04584d59963e0..e62660aaab3af 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -456,7 +456,7 @@ bool Compiler::fgExpandRuntimeLookupsForCall(BasicBlock** pBlock, Statement* stm //------------------------------------------------------------------------------ // fgExpandThreadLocalAccess: Inline the CORINFO_HELP_GETDYNAMIC_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, // CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, or -// CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZEDhelper. See +// CORINFO_HELP_GETDYNAMIC_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED2 helper. See // fgExpandThreadLocalAccessForCall for details. // // Returns: @@ -1034,8 +1034,6 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* // All blocks are expected to be in the same EH region assert(BasicBlock::sameEHRegion(prevBb, block)); assert(BasicBlock::sameEHRegion(prevBb, tlsBaseComputeBB)); - - JITDUMP("tlsBaseComputeBB: " FMT_BB "\n", tlsBaseComputeBB->bbNum); } else { From e6f47d190c951b738824a70b058003f770543458 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 3 Jun 2024 15:22:33 -0700 Subject: [PATCH 79/82] Fix details found while examining diagnostic tests with this change --- src/coreclr/vm/methodtable.cpp | 13 ++++++++++++- src/coreclr/vm/threadstatics.cpp | 10 +++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index f6c4c4f4c1f23..5c62a3edaea10 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -3347,7 +3347,7 @@ void MethodTable::AllocateRegularStaticBox(FieldDesc* pField, Object** boxedStat // Static fields are not pinned in collectible types so we need to protect the address GCPROTECT_BEGININTERIOR(boxedStaticHandle); _ASSERTE(*boxedStaticHandle == nullptr); - MethodTable* pFieldMT = pField->GetFieldTypeHandleThrowing().GetMethodTable(); + MethodTable* pFieldMT = pField->GetFieldTypeHandleThrowing(CLASS_DEPENDENCIES_LOADED).GetMethodTable(); bool hasFixedAddr = HasFixedAddressVTStatics(); LOG((LF_CLASSLOADER, LL_INFO10000, "\tInstantiating static of type %s\n", pFieldMT->GetDebugClassName())); @@ -4406,6 +4406,17 @@ void MethodTable::DoFullyLoad(Generics::RecursionGraph * const pVisited, const ClassLoader::ValidateMethodsWithCovariantReturnTypes(this); } + if ((level == CLASS_LOADED) && CORDisableJITOptimizations(this->GetModule()->GetDebuggerInfoBits()) && !HasInstantiation()) + { + if (g_fEEStarted) + { + // If the module is DEBUG, then allocate static variable memory now, so that the debugger will always be able to examine + // the static variables of the type. Never do this in the EEStartup path, as it can cause incorrect load orders to happen. + // (This only happens in practice in DEBUG builds of coreclr, or when the profiler disables all optimizations.) + EnsureStaticDataAllocated(); + } + } + if (IsArray()) { Generics::RecursionGraph newVisited(pVisited, TypeHandle(this)); diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 6c0bb8d282259..144dbc4d1eee6 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -52,6 +52,12 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread* pThread, TLSIndex index) } CONTRACTL_END; + TADDR pTLSBaseAddress = (TADDR)NULL; + + // The only lock we take here is this spin lock, which is safe to take even when the GC is running. + // The only time it isn't safe is if target thread is suspended by an OS primitive without + // going through the suspend thread routine of the runtime itself. + BEGIN_CONTRACT_VIOLATION(TakesLockViolation); #ifndef DACCESS_COMPILE // Since this api can be used from a different thread, we need a lock to keep it all safe SpinLockHolder spinLock(&pThread->m_TlsSpinLock); @@ -61,7 +67,6 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread* pThread, TLSIndex index) if (pThreadLocalData == NULL) return NULL; - TADDR pTLSBaseAddress = (TADDR)NULL; if (index.GetTLSIndexType() == TLSIndexType::NonCollectible) { PTRARRAYREF tlsArray = (PTRARRAYREF)UNCHECKED_OBJECTREF_TO_OBJECTREF(pThreadLocalData->pNonCollectibleTlsArrayData); @@ -104,6 +109,9 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread* pThread, TLSIndex index) pInFlightData = pInFlightData->pNext; } } + + END_CONTRACT_VIOLATION; + return dac_cast(pTLSBaseAddress); } From 71a4ae9447f540907db4497fef026697274e6f34 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 4 Jun 2024 14:11:04 -0700 Subject: [PATCH 80/82] Get rid of the asserts from the Profiler tls reading path when running on a preemptive thread --- src/coreclr/vm/gchandleutilities.h | 8 ++++ src/coreclr/vm/threadstatics.cpp | 77 ++++++++++++++++-------------- 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/src/coreclr/vm/gchandleutilities.h b/src/coreclr/vm/gchandleutilities.h index a5079f38399a5..3561323566044 100644 --- a/src/coreclr/vm/gchandleutilities.h +++ b/src/coreclr/vm/gchandleutilities.h @@ -48,6 +48,14 @@ inline OBJECTREF ObjectFromHandle(OBJECTHANDLE handle) return UNCHECKED_OBJECTREF_TO_OBJECTREF(*PTR_UNCHECKED_OBJECTREF(handle)); } +// Given a handle, returns an OBJECTREF for the object it refers to. +inline _UNCHECKED_OBJECTREF ObjectFromHandleUnchecked(OBJECTHANDLE handle) +{ + _ASSERTE(handle); + + return (*PTR_UNCHECKED_OBJECTREF(handle)); +} + // Quick inline check for whether a handle is null inline BOOL IsHandleNullUnchecked(OBJECTHANDLE handle) { diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index 144dbc4d1eee6..f76b2f1273970 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -48,7 +48,7 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread* pThread, TLSIndex index) { NOTHROW; GC_NOTRIGGER; - MODE_COOPERATIVE; + MODE_ANY; } CONTRACTL_END; @@ -64,49 +64,54 @@ PTR_VOID GetThreadLocalStaticBaseNoCreate(Thread* pThread, TLSIndex index) #endif PTR_ThreadLocalData pThreadLocalData = pThread->GetThreadLocalDataPtr(); - if (pThreadLocalData == NULL) - return NULL; - - if (index.GetTLSIndexType() == TLSIndexType::NonCollectible) + if (pThreadLocalData != NULL) { - PTRARRAYREF tlsArray = (PTRARRAYREF)UNCHECKED_OBJECTREF_TO_OBJECTREF(pThreadLocalData->pNonCollectibleTlsArrayData); - if (pThreadLocalData->cNonCollectibleTlsData <= index.GetIndexOffset()) + if (index.GetTLSIndexType() == TLSIndexType::NonCollectible) { - return NULL; + PTR_ArrayBase tlsArray = (PTR_ArrayBase)pThreadLocalData->pNonCollectibleTlsArrayData; + if (pThreadLocalData->cNonCollectibleTlsData > index.GetIndexOffset()) + { + size_t arrayIndex = index.GetIndexOffset() - NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY; + TADDR arrayTargetAddress = dac_cast(tlsArray) + offsetof(PtrArray, m_Array); +#ifdef DACCESS_COMPILE + __ArrayDPtr<_UNCHECKED_OBJECTREF> targetArray = dac_cast< __ArrayDPtr<_UNCHECKED_OBJECTREF> >(arrayTargetAddress); +#else + _UNCHECKED_OBJECTREF* targetArray = reinterpret_cast<_UNCHECKED_OBJECTREF*>(arrayTargetAddress); +#endif + pTLSBaseAddress = dac_cast(targetArray[arrayIndex]); + } } - pTLSBaseAddress = dac_cast(OBJECTREFToObject(tlsArray->GetAt(index.GetIndexOffset() - NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY))); - } - else if (index.GetTLSIndexType() == TLSIndexType::DirectOnThreadLocalData) - { - return dac_cast((dac_cast(pThreadLocalData)) + index.GetIndexOffset()); - } - else - { - int32_t cCollectibleTlsData = pThreadLocalData->cCollectibleTlsData; - if (cCollectibleTlsData <= index.GetIndexOffset()) + else if (index.GetTLSIndexType() == TLSIndexType::DirectOnThreadLocalData) { - return NULL; + pTLSBaseAddress = dac_cast((dac_cast(pThreadLocalData)) + index.GetIndexOffset()); } - - TADDR pCollectibleTlsArrayData = dac_cast(pThreadLocalData->pCollectibleTlsArrayData); - pCollectibleTlsArrayData += index.GetIndexOffset() * sizeof(TADDR); - OBJECTHANDLE objHandle = *dac_cast(pCollectibleTlsArrayData); - if (IsHandleNullUnchecked(objHandle)) - return NULL; - pTLSBaseAddress = dac_cast(OBJECTREFToObject(ObjectFromHandle(objHandle))); - } - if (pTLSBaseAddress == (TADDR)NULL) - { - // Maybe it is in the InFlightData - PTR_InFlightTLSData pInFlightData = pThreadLocalData->pInFlightData; - while (pInFlightData != NULL) + else { - if (pInFlightData->tlsIndex == index) + int32_t cCollectibleTlsData = pThreadLocalData->cCollectibleTlsData; + if (cCollectibleTlsData > index.GetIndexOffset()) { - pTLSBaseAddress = dac_cast(OBJECTREFToObject(ObjectFromHandle(pInFlightData->hTLSData))); - break; + TADDR pCollectibleTlsArrayData = dac_cast(pThreadLocalData->pCollectibleTlsArrayData); + pCollectibleTlsArrayData += index.GetIndexOffset() * sizeof(TADDR); + OBJECTHANDLE objHandle = *dac_cast(pCollectibleTlsArrayData); + if (!IsHandleNullUnchecked(objHandle)) + { + pTLSBaseAddress = dac_cast(ObjectFromHandleUnchecked(objHandle)); + } + } + } + if (pTLSBaseAddress == (TADDR)NULL) + { + // Maybe it is in the InFlightData + PTR_InFlightTLSData pInFlightData = pThreadLocalData->pInFlightData; + while (pInFlightData != NULL) + { + if (pInFlightData->tlsIndex == index) + { + pTLSBaseAddress = dac_cast(ObjectFromHandleUnchecked(pInFlightData->hTLSData)); + break; + } + pInFlightData = pInFlightData->pNext; } - pInFlightData = pInFlightData->pNext; } } From 5afc45ac4d78ff5a106dafbd7b6eedbba29ebe4c Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 7 Jun 2024 11:17:59 -0700 Subject: [PATCH 81/82] Code review feedback --- docs/design/coreclr/botr/type-system.md | 2 +- src/coreclr/vm/methodtable.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/design/coreclr/botr/type-system.md b/docs/design/coreclr/botr/type-system.md index 0fb06e89bb7d7..edd11c447bc8a 100644 --- a/docs/design/coreclr/botr/type-system.md +++ b/docs/design/coreclr/botr/type-system.md @@ -199,7 +199,7 @@ Static variables in CoreCLR are handled by a combination of getting the "static We define the statics base as either non-gc or gc for each field. Currently non-gc statics are any statics which are represented by primitive types (byte, sbyte, char, int, uint, long, ulong, float, double, pointers of various forms), and enums. GC statics are any statics which are represented by classes or by non-primitive valuetypes. -For struct statics, the static variable is actually a pointer to a boxed instance of the structure. +For valuetype statics which are GC statics, the static variable is actually a pointer to a boxed instance of the valuetype. ### Per type static variable information As of .NET 9, the static variable bases are now all associated with their particular type. diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 5c62a3edaea10..58a410af1b1aa 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -8666,6 +8666,7 @@ PTR_MethodTable MethodTable::InterfaceMapIterator::GetInterface(MethodTable* pMT void MethodTable::GetStaticsOffsets(StaticsOffsetType offsetType, bool fGenericStatics, uint32_t *dwGCOffset, uint32_t *dwNonGCOffset) { + LIMITED_METHOD_CONTRACT; if (offsetType == StaticsOffsetType::Normal) { *dwNonGCOffset = 0; From 8ca9b7c6ed15e391e2e81369a45b6cfd7de30a6d Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 12 Jun 2024 12:22:56 -0700 Subject: [PATCH 82/82] Fix test to actually be valid --- .../CLR-x86-JIT/V1.2-M02/b138117/b138117.il | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/tests/JIT/Regression/CLR-x86-JIT/V1.2-M02/b138117/b138117.il b/src/tests/JIT/Regression/CLR-x86-JIT/V1.2-M02/b138117/b138117.il index a76ee9fbdfbd2..e971ebd476c5b 100644 --- a/src/tests/JIT/Regression/CLR-x86-JIT/V1.2-M02/b138117/b138117.il +++ b/src/tests/JIT/Regression/CLR-x86-JIT/V1.2-M02/b138117/b138117.il @@ -41,9 +41,6 @@ .maxstack 2 .locals init (valuetype VT V_0, int32 V_1) - IL_0000: ldsflda valuetype VT ldsfldainitonlyvt::vt1 - IL_0005: ldc.i4.s 100 - IL_0007: stfld int32 VT::m IL_000c: ldloca.s V_0 IL_000e: initobj VT IL_0014: ldloca.s V_0 @@ -78,4 +75,12 @@ FAIL: IL_0006: ret } // end of method ldsfldainitonlyvt::.ctor + .method private hidebysig specialname rtspecialname static void .cctor() cil managed + { + .maxstack 2 + IL_0000: ldsflda valuetype VT ldsfldainitonlyvt::vt1 + IL_0005: ldc.i4.s 100 + IL_0007: stfld int32 VT::m + ret + } }