From 74961633cef5f1ae4313ff9b16d1c22b22e96e6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Mon, 4 Jul 2022 15:59:29 +0900 Subject: [PATCH] Implement type loader support for thread statics (#71524) We do threadstatics differently from .NET Native and this wasn't implemented. Fixes #70878 that was reaching a failfast because we couldn't build the target of GVM dispatch at runtime due to unhandled dictionary cell. I've homed the threadstatic bases under the type manager of the template type. We could potentially make a new type manager, but they currently use this one. Deleted some more .NET Native leftovers. Had to update the DynamicGeneric test RD.XML because the test was assuming .NET Native behavior where the ctors are always kept on types that escape the dataflow analysis. --- .../src/Internal/Runtime/MethodTable.cs | 34 +++++++-- .../nativeaot/Runtime/RuntimeInstance.cpp | 12 ---- src/coreclr/nativeaot/Runtime/thread.cpp | 64 ----------------- src/coreclr/nativeaot/Runtime/thread.h | 10 --- .../Runtime/Augments/RuntimeAugments.cs | 6 ++ .../Runtime/Augments/TypeLoaderCallbacks.cs | 2 +- .../src/Internal/Runtime/ThreadStatics.cs | 13 +++- .../src/System/Runtime/RuntimeImports.cs | 4 -- .../src/System/Runtime/TypeLoaderExports.cs | 16 ----- .../Runtime/TypeLoader/EETypeCreator.cs | 46 +++++++----- .../TypeLoader/GenericDictionaryCell.cs | 28 +++++++- .../Runtime/TypeLoader/TypeBuilder.cs | 5 +- .../Runtime/TypeLoader/TypeBuilderState.cs | 5 -- .../TypeLoaderEnvironment.StaticsLookup.cs | 70 +++++++++---------- .../TypeLoader/TypeLoaderEnvironment.cs | 4 +- .../Internal/NativeFormat/NativeFormat.cs | 1 - .../NativeLayoutVertexNode.cs | 4 +- .../DynamicGenerics/DynamicGenerics.main.cs | 2 +- .../SmokeTests/DynamicGenerics/rd.xml | 12 ++++ 19 files changed, 156 insertions(+), 182 deletions(-) diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs index 348f0878a4bb8..38472633c62cd 100644 --- a/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs @@ -1195,6 +1195,30 @@ internal IntPtr DynamicNonGcStaticsData #endif } + internal IntPtr DynamicThreadStaticsIndex + { + get + { + Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithThreadStatics) != 0); + uint cbOffset = GetFieldOffset(EETypeField.ETF_DynamicThreadStaticOffset); + fixed (MethodTable* pThis = &this) + { + return *(IntPtr*)((byte*)pThis + cbOffset); + } + } +#if TYPE_LOADER_IMPLEMENTATION + set + { + Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithThreadStatics) != 0); + uint cbOffset = GetFieldOffset(EETypeField.ETF_DynamicThreadStaticOffset); + fixed (MethodTable* pThis = &this) + { + *(IntPtr*)((byte*)pThis + cbOffset) = value; + } + } +#endif + } + internal DynamicModule* DynamicModule { get @@ -1243,12 +1267,14 @@ internal IntPtr PointerToTypeManager { get { - uint cbOffset = GetFieldOffset(EETypeField.ETF_TypeManagerIndirection); - // This is always a pointer to a pointer to a type manager - return (IntPtr)(*(TypeManagerHandle**)((byte*)Unsafe.AsPointer(ref this) + cbOffset)); + if (IsDynamicType || !SupportsRelativePointers) + return GetField(EETypeField.ETF_TypeManagerIndirection).Value; + + return GetField(EETypeField.ETF_TypeManagerIndirection).Value; } set { + Debug.Assert(IsDynamicType); uint cbOffset = GetFieldOffset(EETypeField.ETF_TypeManagerIndirection); // This is always a pointer to a pointer to a type manager *(TypeManagerHandle**)((byte*)Unsafe.AsPointer(ref this) + cbOffset) = (TypeManagerHandle*)value; @@ -1497,7 +1523,7 @@ internal static uint GetSizeofEEType( (fHasGenericInfo ? sizeof(IntPtr)*2 : 0) + // pointers to GenericDefinition and GenericComposition (fHasNonGcStatics ? sizeof(IntPtr) : 0) + // pointer to data (fHasGcStatics ? sizeof(IntPtr) : 0) + // pointer to data - (fHasThreadStatics ? sizeof(uint) : 0)); // tls offset + (fHasThreadStatics ? sizeof(IntPtr) : 0)); // threadstatic index cell } #endif } diff --git a/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp b/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp index 26a8909fdbb34..107a9af19f70c 100644 --- a/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp +++ b/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp @@ -367,16 +367,4 @@ COOP_PINVOKE_HELPER(void *, RhNewInterfaceDispatchCell, (MethodTable * pInterfac } #endif // FEATURE_CACHED_INTERFACE_DISPATCH -COOP_PINVOKE_HELPER(PTR_UInt8, RhGetThreadLocalStorageForDynamicType, (uint32_t uOffset, uint32_t tlsStorageSize, uint32_t numTlsCells)) -{ - Thread * pCurrentThread = ThreadStore::GetCurrentThread(); - - PTR_UInt8 pResult = pCurrentThread->GetThreadLocalStorageForDynamicType(uOffset); - if (pResult != NULL || tlsStorageSize == 0 || numTlsCells == 0) - return pResult; - - ASSERT(tlsStorageSize > 0 && numTlsCells > 0); - return pCurrentThread->AllocateThreadLocalStorageForDynamicType(uOffset, tlsStorageSize, numTlsCells); -} - #endif // DACCESS_COMPILE diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index 4592e046f0c16..3500e46f48a4e 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -254,9 +254,6 @@ void Thread::Construct() (offsetof(Thread, m_pTransitionFrame))); #endif // USE_PORTABLE_HELPERS - m_numDynamicTypesTlsCells = 0; - m_pDynamicTypesTlsCells = NULL; - m_pThreadLocalModuleStatics = NULL; m_numThreadLocalModuleStatics = 0; @@ -353,16 +350,6 @@ void Thread::Destroy() if (m_hPalThread != INVALID_HANDLE_VALUE) PalCloseHandle(m_hPalThread); - if (m_pDynamicTypesTlsCells != NULL) - { - for (uint32_t i = 0; i < m_numDynamicTypesTlsCells; i++) - { - if (m_pDynamicTypesTlsCells[i] != NULL) - delete[] m_pDynamicTypesTlsCells[i]; - } - delete[] m_pDynamicTypesTlsCells; - } - if (m_pThreadLocalModuleStatics != NULL) { for (uint32_t i = 0; i < m_numThreadLocalModuleStatics; i++) @@ -1104,58 +1091,7 @@ PTR_UInt8 Thread::GetThreadLocalStorage(uint32_t uTlsIndex, uint32_t uTlsStartOf #endif } -PTR_UInt8 Thread::GetThreadLocalStorageForDynamicType(uint32_t uTlsTypeOffset) -{ - // Note: When called from GC root enumeration, no changes can be made by the AllocateThreadLocalStorageForDynamicType to - // the 2 variables accessed here because AllocateThreadLocalStorageForDynamicType is called in cooperative mode. - - uTlsTypeOffset &= ~DYNAMIC_TYPE_TLS_OFFSET_FLAG; - return dac_cast(uTlsTypeOffset < m_numDynamicTypesTlsCells ? m_pDynamicTypesTlsCells[uTlsTypeOffset] : NULL); -} - #ifndef DACCESS_COMPILE -PTR_UInt8 Thread::AllocateThreadLocalStorageForDynamicType(uint32_t uTlsTypeOffset, uint32_t tlsStorageSize, uint32_t numTlsCells) -{ - uTlsTypeOffset &= ~DYNAMIC_TYPE_TLS_OFFSET_FLAG; - - if (m_pDynamicTypesTlsCells == NULL || m_numDynamicTypesTlsCells <= uTlsTypeOffset) - { - // Keep at least a 2x grow so that we don't have to reallocate everytime a new type with TLS statics is created - if (numTlsCells < 2 * m_numDynamicTypesTlsCells) - numTlsCells = 2 * m_numDynamicTypesTlsCells; - - PTR_UInt8* pTlsCells = new (nothrow) PTR_UInt8[numTlsCells]; - if (pTlsCells == NULL) - return NULL; - - memset(&pTlsCells[m_numDynamicTypesTlsCells], 0, sizeof(PTR_UInt8) * (numTlsCells - m_numDynamicTypesTlsCells)); - - if (m_pDynamicTypesTlsCells != NULL) - { - memcpy(pTlsCells, m_pDynamicTypesTlsCells, sizeof(PTR_UInt8) * m_numDynamicTypesTlsCells); - delete[] m_pDynamicTypesTlsCells; - } - - m_pDynamicTypesTlsCells = pTlsCells; - m_numDynamicTypesTlsCells = numTlsCells; - } - - ASSERT(uTlsTypeOffset < m_numDynamicTypesTlsCells); - - if (m_pDynamicTypesTlsCells[uTlsTypeOffset] == NULL) - { - uint8_t* pTlsStorage = new (nothrow) uint8_t[tlsStorageSize]; - if (pTlsStorage == NULL) - return NULL; - - // Initialize storage to 0's before returning it - memset(pTlsStorage, 0, tlsStorageSize); - - m_pDynamicTypesTlsCells[uTlsTypeOffset] = pTlsStorage; - } - - return m_pDynamicTypesTlsCells[uTlsTypeOffset]; -} #ifndef TARGET_UNIX EXTERN_C NATIVEAOT_API uint32_t __cdecl RhCompatibleReentrantWaitAny(UInt32_BOOL alertable, uint32_t timeout, uint32_t count, HANDLE* pHandles) diff --git a/src/coreclr/nativeaot/Runtime/thread.h b/src/coreclr/nativeaot/Runtime/thread.h index 627e7618b40cd..4aab6d88eaa78 100644 --- a/src/coreclr/nativeaot/Runtime/thread.h +++ b/src/coreclr/nativeaot/Runtime/thread.h @@ -30,9 +30,6 @@ class Thread; #define TOP_OF_STACK_MARKER ((PInvokeTransitionFrame*)(ptrdiff_t)-1) #define REDIRECTED_THREAD_MARKER ((PInvokeTransitionFrame*)(ptrdiff_t)-2) -#define DYNAMIC_TYPE_TLS_OFFSET_FLAG 0x80000000 - - enum SyncRequestResult { TryAgain, @@ -99,10 +96,6 @@ struct ThreadBuffer uint32_t m_uRand; // current per-thread random number #endif // FEATURE_GC_STRESS - // Thread Statics Storage for dynamic types - uint32_t m_numDynamicTypesTlsCells; - PTR_PTR_UInt8 m_pDynamicTypesTlsCells; - }; struct ReversePInvokeFrame @@ -200,9 +193,6 @@ class Thread : private ThreadBuffer void GetStackBounds(PTR_VOID * ppStackLow, PTR_VOID * ppStackHigh); - PTR_UInt8 AllocateThreadLocalStorageForDynamicType(uint32_t uTlsTypeOffset, uint32_t tlsStorageSize, uint32_t numTlsCells); - - PTR_UInt8 GetThreadLocalStorageForDynamicType(uint32_t uTlsTypeOffset); PTR_UInt8 GetThreadLocalStorage(uint32_t uTlsIndex, uint32_t uTlsStartOffset); void PushExInfo(ExInfo * pExInfo); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs index 09ee25f38db8e..010c453c367da 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs @@ -349,6 +349,12 @@ public static unsafe object GetThreadStaticBase(IntPtr cookie) return ThreadStatics.GetThreadStaticBaseForType(*(TypeManagerSlot**)cookie, (int)*((IntPtr*)(cookie) + 1)); } + public static int GetHighestStaticThreadStaticIndex(TypeManagerHandle typeManager) + { + RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.ThreadStaticRegion, out int length); + return length / IntPtr.Size; + } + public static unsafe int ObjectHeaderSize => sizeof(EETypePtr); [DebuggerGuidedStepThroughAttribute] diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs index ed04c9b860e08..c1bb6d28dc5cb 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs @@ -17,7 +17,7 @@ public abstract class TypeLoaderCallbacks { public abstract TypeManagerHandle GetModuleForMetadataReader(MetadataReader reader); public abstract bool TryGetConstructedGenericTypeForComponents(RuntimeTypeHandle genericTypeDefinitionHandle, RuntimeTypeHandle[] genericTypeArgumentHandles, out RuntimeTypeHandle runtimeTypeHandle); - public abstract int GetThreadStaticsSizeForDynamicType(int index, out int numTlsCells); + public abstract IntPtr GetThreadStaticGCDescForDynamicType(TypeManagerHandle handle, int index); public abstract IntPtr GenericLookupFromContextAndSignature(IntPtr context, IntPtr signature, out IntPtr auxResult); public abstract bool GetRuntimeMethodHandleComponents(RuntimeMethodHandle runtimeMethodHandle, out RuntimeTypeHandle declaringTypeHandle, out MethodNameAndSignature nameAndSignature, out RuntimeTypeHandle[] genericMethodArgs); public abstract RuntimeMethodHandle GetRuntimeMethodHandleForComponents(RuntimeTypeHandle declaringTypeHandle, string methodName, RuntimeSignature methodSignature, RuntimeTypeHandle[] genericMethodArgs); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs index fded4506f3521..55280a425db70 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs @@ -96,7 +96,18 @@ private static unsafe object AllocateThreadStaticStorageForType(TypeManagerHandl // Get a pointer to the beginning of the module's Thread Static section. Then get a pointer // to the MethodTable that represents a memory map for thread statics storage. threadStaticRegion = (IntPtr*)RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.ThreadStaticRegion, out length); - return RuntimeImports.RhNewObject(new EETypePtr(threadStaticRegion[typeTlsIndex])); + + IntPtr gcDesc; + if (typeTlsIndex < (length / IntPtr.Size)) + { + gcDesc = threadStaticRegion[typeTlsIndex]; + } + else + { + gcDesc = Internal.Runtime.Augments.RuntimeAugments.TypeLoaderCallbacks.GetThreadStaticGCDescForDynamicType(typeManager, typeTlsIndex); + } + + return RuntimeImports.RhNewObject(new EETypePtr(gcDesc)); } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index 31d613b45002f..f349195a030a6 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -459,10 +459,6 @@ internal static unsafe int RhCompatibleReentrantWaitAny(bool alertable, int time [RuntimeImport(RuntimeLibrary, "RhGetThunkSize")] internal static extern int RhGetThunkSize(); - [MethodImplAttribute(MethodImplOptions.InternalCall)] - [RuntimeImport(RuntimeLibrary, "RhGetThreadLocalStorageForDynamicType")] - internal static extern IntPtr RhGetThreadLocalStorageForDynamicType(int index, int tlsStorageSize, int numTlsCells); - [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhResolveDispatchOnType")] // For my life cannot figure out the ordering of modifiers this is expecting. diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs index 38f890c73a771..b83792fd593ba 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs @@ -13,22 +13,6 @@ namespace System.Runtime [ReflectionBlocked] public static class TypeLoaderExports { - public static IntPtr GetThreadStaticsForDynamicType(int index) - { - IntPtr result = RuntimeImports.RhGetThreadLocalStorageForDynamicType(index, 0, 0); - if (result != IntPtr.Zero) - return result; - - int numTlsCells; - int tlsStorageSize = RuntimeAugments.TypeLoaderCallbacks.GetThreadStaticsSizeForDynamicType(index, out numTlsCells); - result = RuntimeImports.RhGetThreadLocalStorageForDynamicType(index, tlsStorageSize, numTlsCells); - - if (result == IntPtr.Zero) - throw new OutOfMemoryException(); - - return result; - } - public static unsafe void ActivatorCreateInstanceAny(ref object ptrToData, IntPtr pEETypePtr) { EETypePtr pEEType = new EETypePtr(pEETypePtr); diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs index b31abb9f70d49..d70d1bfb68332 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs @@ -40,6 +40,11 @@ public static unsafe int GetNumVtableSlots(this RuntimeTypeHandle rtth) return rtth.ToEETypePtr()->NumVtableSlots; } + public static unsafe TypeManagerHandle GetTypeManager(this RuntimeTypeHandle rtth) + { + return rtth.ToEETypePtr()->TypeManager; + } + public static unsafe IntPtr GetDictionary(this RuntimeTypeHandle rtth) { return EETypeCreator.GetDictionary(rtth.ToEETypePtr()); @@ -148,6 +153,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo IntPtr gcStaticData = IntPtr.Zero; IntPtr nonGcStaticData = IntPtr.Zero; IntPtr genericComposition = IntPtr.Zero; + IntPtr threadStaticIndex = IntPtr.Zero; try { @@ -221,6 +227,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo isByRefLike = false; componentSize = checked((ushort)state.TypeBeingBuilt.Instantiation.Length); baseSize = 0; + typeManager = PermanentAllocatedMemoryBlobs.GetPointerToIntPtr(moduleInfo.Handle.GetIntPtrUNSAFE()); } else { @@ -251,6 +258,8 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo } Debug.Assert(i == state.GenericVarianceFlags.Length); } + + typeManager = PermanentAllocatedMemoryBlobs.GetPointerToIntPtr(moduleInfo.Handle.GetIntPtrUNSAFE()); } flags |= (ushort)EETypeFlags.IsDynamicTypeFlag; @@ -453,7 +462,6 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo pEEType->OptionalFieldsPtr = (byte*)pEEType + cbEEType; optionalFields.WriteToEEType(pEEType, cbOptionalFieldsSize); - pEEType->PointerToTypeManager = PermanentAllocatedMemoryBlobs.GetPointerToIntPtr(moduleInfo.Handle.GetIntPtrUNSAFE()); pEEType->DynamicModule = dynamicModulePtr; // Copy VTable entries from template type @@ -639,22 +647,11 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo // create GC desc if (state.GcDataSize != 0 && state.GcStaticDesc == IntPtr.Zero) { - if (state.GcStaticEEType != IntPtr.Zero) - { - // Statics are allocated on GC heap - object obj = RuntimeAugments.NewObject(((MethodTable*)state.GcStaticEEType)->ToRuntimeTypeHandle()); - gcStaticData = RuntimeAugments.RhHandleAlloc(obj, GCHandleType.Normal); - - pEEType->DynamicGcStaticsData = gcStaticData; - } - else - { - int cbStaticGCDesc; - state.GcStaticDesc = CreateStaticGCDesc(state.StaticGCLayout, out state.AllocatedStaticGCDesc, out cbStaticGCDesc); + int cbStaticGCDesc; + state.GcStaticDesc = CreateStaticGCDesc(state.StaticGCLayout, out state.AllocatedStaticGCDesc, out cbStaticGCDesc); #if GENERICS_FORCE_USG - TestGCDescsForEquality(state.GcStaticDesc, state.NonUniversalStaticGCDesc, cbStaticGCDesc, false); + TestGCDescsForEquality(state.GcStaticDesc, state.NonUniversalStaticGCDesc, cbStaticGCDesc, false); #endif - } } if (state.ThreadDataSize != 0 && state.ThreadStaticDesc == IntPtr.Zero) @@ -686,8 +683,21 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo if (!isGenericEETypeDef && state.ThreadDataSize != 0) { - // TODO: thread statics - throw new NotSupportedException(); + state.ThreadStaticOffset = TypeLoaderEnvironment.Instance.GetNextThreadStaticsOffsetValue(pEEType->TypeManager); + + threadStaticIndex = MemoryHelpers.AllocateMemory(IntPtr.Size * 2); + *(IntPtr*)threadStaticIndex = pEEType->PointerToTypeManager; + *(((IntPtr*)threadStaticIndex) + 1) = (IntPtr)state.ThreadStaticOffset; + pEEType->DynamicThreadStaticsIndex = threadStaticIndex; + } + + if (!isGenericEETypeDef && state.GcDataSize != 0) + { + // Statics are allocated on GC heap + object obj = RuntimeAugments.NewObject(((MethodTable*)state.GcStaticDesc)->ToRuntimeTypeHandle()); + gcStaticData = RuntimeAugments.RhHandleAlloc(obj, GCHandleType.Normal); + + pEEType->DynamicGcStaticsData = gcStaticData; } if (state.Dictionary != null) @@ -723,6 +733,8 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo MemoryHelpers.FreeMemory(nonGcStaticData); if (writableDataPtr != IntPtr.Zero) MemoryHelpers.FreeMemory(writableDataPtr); + if (threadStaticIndex != IntPtr.Zero) + MemoryHelpers.FreeMemory(threadStaticIndex); } } } diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs index 3aa25ab4816ab..5c6deafe47a28 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs @@ -313,6 +313,24 @@ internal override unsafe IntPtr CreateLazyLookupCell(TypeBuilder builder, out In } } + private class ThreadStaticIndexCell : GenericDictionaryCell + { + internal TypeDesc Type; + + internal override void Prepare(TypeBuilder builder) + { + if (Type.IsCanonicalSubtype(CanonicalFormKind.Any)) + Environment.FailFast("Unable to compute static field locations for a canonical type."); + + builder.RegisterForPreparation(Type); + } + + internal override unsafe IntPtr Create(TypeBuilder builder) + { + return TypeLoaderEnvironment.Instance.TryGetThreadStaticFieldData(builder.GetRuntimeTypeHandle(Type)); + } + } + #if SUPPORTS_NATIVE_METADATA_TYPE_LOADING public static GenericDictionaryCell CreateMethodDictionaryCell(InstantiatedMethod method) { @@ -1843,8 +1861,16 @@ internal static GenericDictionaryCell ParseAndCreateCell(NativeLayoutInfoLoadCon break; #endif - case FixupSignatureKind.NotYetSupported: case FixupSignatureKind.ThreadStaticIndex: + { + var type = nativeLayoutInfoLoadContext.GetType(ref parser); + TypeLoaderLogger.WriteLine("ThreadStaticIndex on: " + type.ToString()); + + cell = new ThreadStaticIndexCell { Type = type }; + } + break; + + case FixupSignatureKind.NotYetSupported: case FixupSignatureKind.GenericStaticConstrainedMethod: TypeLoaderLogger.WriteLine("Valid dictionary entry, but not yet supported by the TypeLoader!"); throw new TypeBuilder.MissingTemplateException(); diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs index a0c7bc7324639..272fb6c4ad971 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs @@ -977,13 +977,10 @@ private unsafe void AllocateRuntimeType(TypeDesc type) Debug.Assert(type is DefType || type is ArrayType || type is PointerType || type is ByRefType); - if (state.ThreadDataSize != 0) - state.ThreadStaticOffset = TypeLoaderEnvironment.Instance.GetNextThreadStaticsOffsetValue(); - RuntimeTypeHandle rtt = EETypeCreator.CreateEEType(type, state); if (state.ThreadDataSize != 0) - TypeLoaderEnvironment.Instance.RegisterDynamicThreadStaticsInfo(state.HalfBakedRuntimeTypeHandle, state.ThreadStaticOffset, state.ThreadDataSize); + TypeLoaderEnvironment.Instance.RegisterDynamicThreadStaticsInfo(state.HalfBakedRuntimeTypeHandle, state.ThreadStaticOffset, state.ThreadStaticDesc); TypeLoaderLogger.WriteLine("Allocated new type " + type.ToString() + " with hashcode value = 0x" + type.GetHashCode().LowLevelToString() + " with MethodTable = " + rtt.ToIntPtr().LowLevelToString() + " of size " + rtt.ToEETypePtr()->BaseSize.LowLevelToString()); } diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs index dcc81310178d8..38b5971517fee 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs @@ -523,7 +523,6 @@ public bool HasStaticConstructor public IntPtr? ClassConstructorPointer; public IntPtr GcStaticDesc; - public IntPtr GcStaticEEType; public IntPtr ThreadStaticDesc; public bool AllocatedStaticGCDesc; public bool AllocatedThreadStaticGCDesc; @@ -697,10 +696,6 @@ public void PrepareStaticGCLayout() ThreadStaticDesc = NativeLayoutInfo.LoadContext.GetGCStaticInfo(typeInfoParser.GetUnsigned()); break; - case BagElementKind.GcStaticEEType: - GcStaticEEType = NativeLayoutInfo.LoadContext.GetGCStaticInfo(typeInfoParser.GetUnsigned()); - break; - default: typeInfoParser.SkipInteger(); break; diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.StaticsLookup.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.StaticsLookup.cs index d6810252ab7b8..9f5aae247a364 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.StaticsLookup.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.StaticsLookup.cs @@ -3,13 +3,10 @@ using System; -using System.Runtime; using System.Collections.Generic; using System.Diagnostics; -using System.Runtime.InteropServices; using System.Threading; -using Internal.Runtime; using Internal.Runtime.Augments; using Internal.NativeFormat; @@ -18,15 +15,12 @@ namespace Internal.Runtime.TypeLoader { public sealed partial class TypeLoaderEnvironment { - private const int DynamicTypeTlsOffsetFlag = unchecked((int)0x80000000); - // To keep the synchronization simple, we execute all TLS registration/lookups under a global lock private Lock _threadStaticsLock = new Lock(); // Counter to keep track of generated offsets for TLS cells of dynamic types; - private int _maxTlsCells; - private LowLevelDictionary _dynamicGenericsThreadStatics = new LowLevelDictionary(); - private LowLevelDictionary _dynamicGenericsThreadStaticSizes = new LowLevelDictionary(); + private LowLevelDictionary _maxThreadLocalIndex = new LowLevelDictionary(); + private LowLevelDictionary> _dynamicGenericsThreadStaticDescs = new LowLevelDictionary>(); // Various functions in static access need to create permanent pointers for use by thread static lookup. #region GC/Non-GC Statics @@ -197,62 +191,64 @@ public IntPtr TryGetThreadStaticFieldData(RuntimeTypeHandle runtimeTypeHandle) return index.HasValue ? staticInfoLookup.GetIntPtrFromIndex(index.Value) : IntPtr.Zero; } - // Not found in hashtable... must be a dynamically created type - Debug.Assert(!runtimeTypeHandle.IsDynamicType()); - // Not yet implemented... + // Not found in hashtable... might be a dynamically created type + if (runtimeTypeHandle.IsDynamicType()) + { + unsafe + { + MethodTable* typeAsEEType = runtimeTypeHandle.ToEETypePtr(); + if (typeAsEEType->DynamicThreadStaticsIndex != IntPtr.Zero) + return typeAsEEType->DynamicThreadStaticsIndex; + } + } // Type has no GC statics return IntPtr.Zero; } - public int TryGetThreadStaticsSizeForDynamicType(int index, out int numTlsCells) + public IntPtr GetThreadStaticGCDescForDynamicType(TypeManagerHandle typeManagerHandle, uint index) { - Debug.Assert((index & DynamicTypeTlsOffsetFlag) == DynamicTypeTlsOffsetFlag); - - numTlsCells = _maxTlsCells; - using (LockHolder.Hold(_threadStaticsLock)) { - int storageSize; - if (_dynamicGenericsThreadStaticSizes.TryGetValue((uint)index, out storageSize)) - return storageSize; + return _dynamicGenericsThreadStaticDescs[typeManagerHandle.GetIntPtrUNSAFE()][index]; } - - Debug.Assert(false); - return 0; } - public uint GetNextThreadStaticsOffsetValue() + public uint GetNextThreadStaticsOffsetValue(TypeManagerHandle typeManagerHandle) { - // Highest bit of the TLS offset used as a flag to indicate that it's a special TLS offset of a dynamic type - var result = 0x80000000 | (uint)_maxTlsCells; - // Use checked arithmetics to ensure there aren't any overflows/truncations - _maxTlsCells = checked(_maxTlsCells + 1); + if (!_maxThreadLocalIndex.TryGetValue(typeManagerHandle.GetIntPtrUNSAFE(), out uint result)) + result = (uint)RuntimeAugments.GetHighestStaticThreadStaticIndex(typeManagerHandle); + + _maxThreadLocalIndex[typeManagerHandle.GetIntPtrUNSAFE()] = checked(++result); + return result; } - public void RegisterDynamicThreadStaticsInfo(RuntimeTypeHandle runtimeTypeHandle, uint offsetValue, int storageSize) + public void RegisterDynamicThreadStaticsInfo(RuntimeTypeHandle runtimeTypeHandle, uint offsetValue, IntPtr gcDesc) { bool registered = false; - Debug.Assert(offsetValue != 0 && storageSize > 0 && runtimeTypeHandle.IsDynamicType()); + Debug.Assert(offsetValue != 0 && runtimeTypeHandle.IsDynamicType()); + + IntPtr typeManager = runtimeTypeHandle.GetTypeManager().GetIntPtrUNSAFE(); _threadStaticsLock.Acquire(); try { - // Sanity check to make sure we do not register thread statics for the same type more than once - uint temp; - Debug.Assert(!_dynamicGenericsThreadStatics.TryGetValue(runtimeTypeHandle, out temp) && storageSize > 0); - - _dynamicGenericsThreadStatics.Add(runtimeTypeHandle, offsetValue); - _dynamicGenericsThreadStaticSizes.Add(offsetValue, storageSize); + if (!_dynamicGenericsThreadStaticDescs.TryGetValue(typeManager, out LowLevelDictionary gcDescs)) + { + _dynamicGenericsThreadStaticDescs.Add(typeManager, gcDescs = new LowLevelDictionary()); + } + gcDescs.Add(offsetValue, gcDesc); registered = true; } finally { if (!registered) { - _dynamicGenericsThreadStatics.Remove(runtimeTypeHandle); - _dynamicGenericsThreadStaticSizes.Remove(offsetValue); + if (_dynamicGenericsThreadStaticDescs.TryGetValue(typeManager, out LowLevelDictionary gcDescs)) + { + gcDescs.Remove(offsetValue); + } } _threadStaticsLock.Release(); diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs index 310534e09451c..fb0b2d5105002 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs @@ -35,9 +35,9 @@ public override bool TryGetConstructedGenericTypeForComponents(RuntimeTypeHandle return TypeLoaderEnvironment.Instance.TryGetConstructedGenericTypeForComponents(genericTypeDefinitionHandle, genericTypeArgumentHandles, out runtimeTypeHandle); } - public override int GetThreadStaticsSizeForDynamicType(int index, out int numTlsCells) + public override IntPtr GetThreadStaticGCDescForDynamicType(TypeManagerHandle typeManagerHandle, int index) { - return TypeLoaderEnvironment.Instance.TryGetThreadStaticsSizeForDynamicType(index, out numTlsCells); + return TypeLoaderEnvironment.Instance.GetThreadStaticGCDescForDynamicType(typeManagerHandle, (uint)index); } public override IntPtr GenericLookupFromContextAndSignature(IntPtr context, IntPtr signature, out IntPtr auxResult) diff --git a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormat.cs b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormat.cs index 5fd89c6d66b8e..c41b83df9b57e 100644 --- a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormat.cs +++ b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormat.cs @@ -57,7 +57,6 @@ enum BagElementKind : uint BaseTypeSize = 0x4f, GenericVarianceInfo = 0x50, DelegateInvokeSignature = 0x51, - GcStaticEEType = 0x52, // Add new custom bag elements that don't match to something you'd find in the ECMA metadata here. } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs index c2900ffc1269a..951a12b30fedd 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs @@ -953,7 +953,7 @@ private static TypeDesc GetActualTemplateTypeForType(NodeFactory factory, TypeDe private ISymbolNode GetStaticsNode(NodeFactory context, out BagElementKind staticsBagKind) { ISymbolNode symbol = context.GCStaticEEType(GCPointerMap.FromStaticLayout(_type.GetClosestDefType())); - staticsBagKind = BagElementKind.GcStaticEEType; + staticsBagKind = BagElementKind.GcStaticDesc; return symbol; } @@ -961,7 +961,7 @@ private ISymbolNode GetStaticsNode(NodeFactory context, out BagElementKind stati private ISymbolNode GetThreadStaticsNode(NodeFactory context, out BagElementKind staticsBagKind) { ISymbolNode symbol = context.GCStaticEEType(GCPointerMap.FromThreadStaticLayout(_type.GetClosestDefType())); - staticsBagKind = BagElementKind.End; // GC static EETypes not yet implemented in type loader + staticsBagKind = BagElementKind.ThreadStaticDesc; return symbol; } diff --git a/src/tests/nativeaot/SmokeTests/DynamicGenerics/DynamicGenerics.main.cs b/src/tests/nativeaot/SmokeTests/DynamicGenerics/DynamicGenerics.main.cs index 561642918f228..483cbdf1ac506 100644 --- a/src/tests/nativeaot/SmokeTests/DynamicGenerics/DynamicGenerics.main.cs +++ b/src/tests/nativeaot/SmokeTests/DynamicGenerics/DynamicGenerics.main.cs @@ -72,7 +72,7 @@ public static int Main(string[] args) #endif //new CoreFXTestLibrary.Internal.TestInfo("RdExperienceTests.TestRdExperience", () => global::RdExperienceTests.TestRdExperience(), null), new CoreFXTestLibrary.Internal.TestInfo("StaticsTests.TestStatics", () => global::StaticsTests.TestStatics(), null), -//new CoreFXTestLibrary.Internal.TestInfo("ThreadLocalStatics.TLSTesting.ThreadLocalStatics_Test", () => global::ThreadLocalStatics.TLSTesting.ThreadLocalStatics_Test(), null), +new CoreFXTestLibrary.Internal.TestInfo("ThreadLocalStatics.TLSTesting.ThreadLocalStatics_Test", () => global::ThreadLocalStatics.TLSTesting.ThreadLocalStatics_Test(), null), #if UNIVERSAL_GENERICS new CoreFXTestLibrary.Internal.TestInfo("UnivConstCalls.Test.TestRefTypeCallsOnNonGenClass", () => global::UnivConstCalls.Test.TestRefTypeCallsOnNonGenClass(), null), new CoreFXTestLibrary.Internal.TestInfo("UnivConstCalls.Test.TestUSCCallsOnNonGenStruct", () => global::UnivConstCalls.Test.TestUSCCallsOnNonGenStruct(), null), diff --git a/src/tests/nativeaot/SmokeTests/DynamicGenerics/rd.xml b/src/tests/nativeaot/SmokeTests/DynamicGenerics/rd.xml index 92d116b2a139f..6f473551d1cb8 100644 --- a/src/tests/nativeaot/SmokeTests/DynamicGenerics/rd.xml +++ b/src/tests/nativeaot/SmokeTests/DynamicGenerics/rd.xml @@ -126,6 +126,18 @@ + + + + + + + + + + + +