diff --git a/src/coreclr/nativeaot/BuildIntegration/NativeAOT.natvis b/src/coreclr/nativeaot/BuildIntegration/NativeAOT.natvis index bd5ebc5f660d9d..497ceea990c659 100644 --- a/src/coreclr/nativeaot/BuildIntegration/NativeAOT.natvis +++ b/src/coreclr/nativeaot/BuildIntegration/NativeAOT.natvis @@ -61,15 +61,42 @@ - - - - + + + - Null - {*Get()} + + + + + + + + + + + + + + + + + + + + + Null + {*Get()} - Get() + Get() diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index 9eb428c3d0c402..4c924524fd4d73 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -1281,17 +1281,19 @@ InlinedThreadStaticRoot* Thread::GetInlinedThreadStaticList() return m_pInlinedThreadLocalStatics; } -void Thread::RegisterInlinedThreadStaticRoot(InlinedThreadStaticRoot* newRoot) +void Thread::RegisterInlinedThreadStaticRoot(InlinedThreadStaticRoot* newRoot, TypeManager* typeManager) { ASSERT(newRoot->m_next == NULL); newRoot->m_next = m_pInlinedThreadLocalStatics; m_pInlinedThreadLocalStatics = newRoot; + // we do not need typeManager for runtime needs, but it may be useful for debugging purposes. + newRoot->m_typeManager = typeManager; } -COOP_PINVOKE_HELPER(void, RhRegisterInlinedThreadStaticRoot, (Object** root)) +COOP_PINVOKE_HELPER(void, RhRegisterInlinedThreadStaticRoot, (Object** root, TypeManager* typeManager)) { Thread* pCurrentThread = ThreadStore::RawGetCurrentThread(); - pCurrentThread->RegisterInlinedThreadStaticRoot((InlinedThreadStaticRoot*)root); + pCurrentThread->RegisterInlinedThreadStaticRoot((InlinedThreadStaticRoot*)root, typeManager); } // This is function is used to quickly query a value that can uniquely identify a thread diff --git a/src/coreclr/nativeaot/Runtime/thread.h b/src/coreclr/nativeaot/Runtime/thread.h index 1213c3e16ce03b..7aa473eb6763d4 100644 --- a/src/coreclr/nativeaot/Runtime/thread.h +++ b/src/coreclr/nativeaot/Runtime/thread.h @@ -12,6 +12,7 @@ class RuntimeInstance; class ThreadStore; class CLREventStatic; class Thread; +class TypeManager; #ifdef TARGET_UNIX #include "UnixContext.h" @@ -74,8 +75,12 @@ struct GCFrameRegistration struct InlinedThreadStaticRoot { + // The reference to the memory block that stores variables for the current {thread, typeManager} combination Object* m_threadStaticsBase; + // The next root in the list. All roots in the list belong to the same thread, but to different typeManagers. InlinedThreadStaticRoot* m_next; + // m_typeManager is used by NativeAOT.natvis when debugging + TypeManager* m_typeManager; }; struct ThreadBuffer @@ -294,7 +299,7 @@ class Thread : private ThreadBuffer Object** GetThreadStaticStorage(); InlinedThreadStaticRoot* GetInlinedThreadStaticList(); - void RegisterInlinedThreadStaticRoot(InlinedThreadStaticRoot* newRoot); + void RegisterInlinedThreadStaticRoot(InlinedThreadStaticRoot* newRoot, TypeManager* typeManager); NATIVE_CONTEXT* GetInterruptedContext(); 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 7bfcbff517b22f..61061564af7292 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 @@ -40,7 +40,7 @@ internal static unsafe object GetInlinedThreadStaticBaseSlow(ref object? threadS object threadStaticBase = AllocateThreadStaticStorageForType(typeManager, 0); // register the storage location with the thread for GC reporting. - RuntimeImports.RhRegisterInlinedThreadStaticRoot(ref threadStorage); + RuntimeImports.RhRegisterInlinedThreadStaticRoot(ref threadStorage, typeManager); // assign the storage block to the storage variable and return threadStorage = threadStaticBase; 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 9c652dd69bb9aa..0456e6879d6fde 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 @@ -581,7 +581,7 @@ internal static IntPtr RhGetModuleSection(TypeManagerHandle module, ReadyToRunSe [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhRegisterInlinedThreadStaticRoot")] - internal static extern void RhRegisterInlinedThreadStaticRoot(ref object? root); + internal static extern void RhRegisterInlinedThreadStaticRoot(ref object? root, TypeManagerHandle module); [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhCurrentNativeThreadId")] diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs index 23bc01a20065c5..fbb6c08e04006c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -203,22 +203,20 @@ private void CreateNodeCaches() _threadStatics = new NodeCache(CreateThreadStaticsNode); - TypeThreadStaticIndexNode inlinedThreadStatiscIndexNode = null; if (_inlinedThreadStatics.IsComputed()) { _inlinedThreadStatiscNode = new ThreadStaticsNode(_inlinedThreadStatics, this); - inlinedThreadStatiscIndexNode = new TypeThreadStaticIndexNode(_inlinedThreadStatiscNode); } _typeThreadStaticIndices = new NodeCache(type => { - if (inlinedThreadStatiscIndexNode != null && - _inlinedThreadStatics.GetOffsets().ContainsKey(type)) + if (_inlinedThreadStatics.IsComputed() && + _inlinedThreadStatics.GetOffsets().ContainsKey(type)) { - return inlinedThreadStatiscIndexNode; + return new TypeThreadStaticIndexNode(type, _inlinedThreadStatiscNode); } - return new TypeThreadStaticIndexNode(type); + return new TypeThreadStaticIndexNode(type, null); }); _GCStaticEETypes = new NodeCache((GCPointerMap gcMap) => diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs index 83c2b3db3bab22..0fca02c971a4f1 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs @@ -72,7 +72,7 @@ protected override void EmitCode(NodeFactory factory, ref ARM64Emitter encoder, { MetadataType target = (MetadataType)Target; ISortableSymbolNode index = factory.TypeThreadStaticIndex(target); - if (index is TypeThreadStaticIndexNode ti && ti.Type == null) + if (index is TypeThreadStaticIndexNode ti && ti.IsInlined) { if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs index 8fbd5f609b2546..4d6b4b01ba180c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs @@ -72,7 +72,7 @@ protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bo { MetadataType target = (MetadataType)Target; ISortableSymbolNode index = factory.TypeThreadStaticIndex(target); - if (index is TypeThreadStaticIndexNode ti && ti.Type == null) + if (index is TypeThreadStaticIndexNode ti && ti.IsInlined) { if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ThreadStaticsNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ThreadStaticsNode.cs index 6fa2a7a70cc114..8935ce8d3d0561 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ThreadStaticsNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ThreadStaticsNode.cs @@ -73,7 +73,6 @@ public override IEnumerable GetStaticDependencies(NodeFacto if (_type != null) { - if (factory.PreinitializationManager.HasEagerStaticConstructor(_type)) { result.Add(new DependencyListEntry(factory.EagerCctorIndirection(_type.GetStaticConstructor()), "Eager .cctor")); @@ -90,6 +89,9 @@ public override IEnumerable GetStaticDependencies(NodeFacto result.Add(new DependencyListEntry(factory.EagerCctorIndirection(type.GetStaticConstructor()), "Eager .cctor")); } + // inlined threadstatics do not need the index for execution, but may need it for debug visualization. + result.Add(new DependencyListEntry(factory.TypeThreadStaticIndex(type), "ThreadStatic index for debug visualization")); + ModuleUseBasedDependencyAlgorithm.AddDependenciesDueToModuleUse(ref result, factory, type.Module); } } @@ -143,5 +145,7 @@ public override int CompareToImpl(ISortableNode other, CompilerComparer comparer return comparer.Compare(_type, ((ThreadStaticsNode)other)._type); } + + internal int GetTypeStorageOffset(MetadataType type) => _inlined.GetOffsets()[type]; } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TlsRootNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TlsRootNode.cs index 424ba7130f0df7..a26ff9bec28f6c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TlsRootNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TlsRootNode.cs @@ -26,10 +26,15 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) objData.RequireInitialPointerAlignment(); objData.AddSymbol(this); - // root + // storage for InlinedThreadStaticRoot instances + + // m_threadStaticsBase + objData.EmitZeroPointer(); + + // m_next objData.EmitZeroPointer(); - // next + // m_typeManager objData.EmitZeroPointer(); return objData.ToObjectData(); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs index f91853d1834014..95853fe6cf35cf 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs @@ -15,19 +15,17 @@ public class TypeThreadStaticIndexNode : DehydratableObjectNode, ISymbolDefiniti private MetadataType _type; private ThreadStaticsNode _inlinedThreadStatics; - public TypeThreadStaticIndexNode(MetadataType type) + public TypeThreadStaticIndexNode(MetadataType type, ThreadStaticsNode inlinedThreadStatics) { _type = type; - } - - public TypeThreadStaticIndexNode(ThreadStaticsNode inlinedThreadStatics) - { _inlinedThreadStatics = inlinedThreadStatics; } + public bool IsInlined => _inlinedThreadStatics != null; + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { - sb.Append(_type != null ? nameMangler.NodeMangler.ThreadStaticsIndex(_type) : "_inlinedThreadStaticsIndex"); + sb.Append(nameMangler.NodeMangler.ThreadStaticsIndex(_type)); } public int Offset => 0; @@ -46,9 +44,7 @@ protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory) protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { - ISymbolDefinitionNode node = _type != null ? - factory.TypeThreadStaticsSymbol(_type) : - _inlinedThreadStatics; + ISymbolDefinitionNode node = _inlinedThreadStatics ?? factory.TypeThreadStaticsSymbol(_type); return new DependencyList { @@ -66,21 +62,23 @@ protected override ObjectData GetDehydratableData(NodeFactory factory, bool relo int typeTlsIndex = 0; if (!relocsOnly) { - if (_type != null) - { - ISymbolDefinitionNode node = factory.TypeThreadStaticsSymbol(_type); - typeTlsIndex = ((ThreadStaticsNode)node).IndexFromBeginningOfArray; - } - else + if (IsInlined) { - // we use -1 to specify the index of inlined threadstatics, - // which are stored separately from uninlined ones. - typeTlsIndex = -1; + // Inlined threadstatics are stored as a single data block and thus do not need + // an index in the containing storage. + // We use a negative index to indicate that. Any negative value would work. + // For the purpose of natvis we will encode the offset of the type storage within the block. + typeTlsIndex = - (_inlinedThreadStatics.GetTypeStorageOffset(_type) + factory.Target.PointerSize); // the type of the storage block for inlined threadstatics, if present, // is serialized as the item #0 among other storage block types. Debug.Assert(_inlinedThreadStatics.IndexFromBeginningOfArray == 0); } + else + { + ISymbolDefinitionNode node = factory.TypeThreadStaticsSymbol(_type); + typeTlsIndex = ((ThreadStaticsNode)node).IndexFromBeginningOfArray; + } } // needed to construct storage