diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs
index 7f5372e706fbe5..b39210b9eff8d4 100644
--- a/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs
+++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Diagnostics;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -337,10 +338,25 @@ static void WriteRelPtr32(void* dest, void* value)
}
}
+ [DataContract]
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct TypeManagerSlot
{
+ [DataContract]
public TypeManagerHandle TypeManager;
+
+ [DataContract]
public int ModuleIndex;
}
+
+ [DataContract]
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct TypeThreadStaticIndex
+ {
+ [DataContract]
+ public TypeManagerSlot* TypeManagerSlot;
+
+ [DataContract]
+ public nint ClassIndex;
+ }
}
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 c8cea459512be1..04d585b26de163 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
@@ -338,7 +338,9 @@ public static unsafe object LoadPointerTypeFieldValueFromValueType(TypedReferenc
public static unsafe object GetThreadStaticBase(IntPtr cookie)
{
- return ThreadStatics.GetThreadStaticBaseForType(*(TypeManagerSlot**)cookie, (int)*((IntPtr*)(cookie) + 1));
+ TypeThreadStaticIndex* index = (TypeThreadStaticIndex*)cookie;
+
+ return ThreadStatics.GetThreadStaticBaseForType(index->TypeManagerSlot, (int)index->ClassIndex);
}
public static int GetHighestStaticThreadStaticIndex(TypeManagerHandle typeManager)
diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj
index e96a5deff9b401..10a0661a98c7f5 100644
--- a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj
+++ b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj
@@ -71,6 +71,9 @@
+
+ System\Diagnostics\DataContractAttribute.cs
+
Internal\NativeFormat\NativeFormatReader.Primitives.cs
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs
index 6295e9341f6bf3..3d39bf2e45e599 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs
@@ -4,7 +4,9 @@
using System;
using System.Collections.Generic;
using System.Text;
+using System.Text.Json;
using ILCompiler.Dataflow;
+using ILCompiler.DependencyAnalysis;
using Internal.IL;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
@@ -80,7 +82,11 @@ public void TestDependencyGraphInvariants(EcmaMethod method)
.UseILProvider(ilProvider);
IILScanner scanner = builder.GetILScannerBuilder()
- .UseCompilationRoots(new ICompilationRootProvider[] { new SingleMethodRootProvider(method) })
+ .UseCompilationRoots(new ICompilationRootProvider[]
+ {
+ new SingleMethodRootProvider(method),
+ new ThreadStaticBaseRootProvider(method.OwningType)
+ })
.UseMetadataManager(metadataManager)
.ToILScanner();
@@ -119,6 +125,13 @@ public void TestDependencyGraphInvariants(EcmaMethod method)
Assert.DoesNotContain(methodToCheck.GetCanonMethodTarget(CanonicalFormKind.Specific), results.CompiledMethodBodies);
}
+ foreach (var attr in method.GetDecodedCustomAttributes(assetsNamespace, "GeneratesDataDescriptorTypeAttribute"))
+ {
+ foundSomethingToCheck = true;
+ string typeName = (string)attr.FixedArguments[0].Value;
+ AssertDataDescriptorType(metadataManager, typeName, context.Target.PointerSize);
+ }
+
//
// Make sure we checked something
//
@@ -133,5 +146,39 @@ private static MethodDesc GetMethodFromAttribute(CustomAttributeValue attr)
return ((TypeDesc)attr.FixedArguments[0].Value).GetMethod(Encoding.UTF8.GetBytes((string)attr.FixedArguments[1].Value), null);
}
+
+ private static void AssertDataDescriptorType(UsageBasedMetadataManager metadataManager, string typeName, int pointerSize)
+ {
+ byte[] json = ManagedDataDescriptorNode.BuildJsonDescriptor(metadataManager);
+ using JsonDocument document = JsonDocument.Parse(json);
+ JsonElement jsonType = document.RootElement.GetProperty("types").GetProperty(typeName);
+ Assert.Equal(2 * pointerSize, jsonType.GetProperty("!").GetInt32());
+ if (typeName == "Internal.Runtime.CompilerHelpers.TypeManagerSlot")
+ {
+ Assert.Equal(0, jsonType.GetProperty("TypeManager").GetInt32());
+ Assert.Equal(pointerSize, jsonType.GetProperty("ModuleIndex").GetInt32());
+ }
+ else
+ {
+ Assert.Equal("Internal.Runtime.CompilerHelpers.TypeThreadStaticIndex", typeName);
+ Assert.Equal(0, jsonType.GetProperty("TypeManagerSlot").GetInt32());
+ Assert.Equal(pointerSize, jsonType.GetProperty("ClassIndex").GetInt32());
+ }
+ }
+
+ private sealed class ThreadStaticBaseRootProvider : ICompilationRootProvider
+ {
+ private readonly TypeDesc _type;
+
+ public ThreadStaticBaseRootProvider(TypeDesc type)
+ {
+ _type = type;
+ }
+
+ public void AddCompilationRoots(IRootingServiceProvider rootProvider)
+ {
+ rootProvider.RootThreadStaticBaseForType(_type, "Dependency graph test");
+ }
+ }
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/DependencyGraph.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/DependencyGraph.cs
index ef2fdab5b8b790..e8251f6e3fb018 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/DependencyGraph.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/DependencyGraph.cs
@@ -64,6 +64,18 @@ public static void Entrypoint()
new Derived().CallBaseGenericVirtualDirectly