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