Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@
<Compile Include="$(IntermediatesDir)\nativeaot\Runtime\Full\AsmOffsets.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\System.Private.CoreLib\src\System\Diagnostics\DataContractAttribute.cs">
<Link>System\Diagnostics\DataContractAttribute.cs</Link>
</Compile>
<Compile Include="$(CompilerCommonPath)\Internal\NativeFormat\NativeFormatReader.Primitives.cs">
<Link>Internal\NativeFormat\NativeFormatReader.Primitives.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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
//
Expand All @@ -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");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ public static void Entrypoint()
new Derived().CallBaseGenericVirtualDirectly<object>();
}
}

class ThreadStaticDataDescriptorTest
{
[ThreadStatic]
public static object Value = null;

[GeneratesDataDescriptorType("Internal.Runtime.CompilerHelpers.TypeManagerSlot")]
[GeneratesDataDescriptorType("Internal.Runtime.CompilerHelpers.TypeThreadStaticIndex")]
public static void Entrypoint()
{
}
}
}

#region Custom attributes that define invariants to check
Expand Down Expand Up @@ -92,5 +104,18 @@ public NoMethodBodyAttribute(Type owningType, string methodName) { }
public Type[] GenericArguments;
public Type[] Signature;
}

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class GeneratesDataDescriptorTypeAttribute : Attribute
{
public GeneratesDataDescriptorTypeAttribute(string name) { }
}
#endregion
}

namespace System
{
public sealed class ThreadStaticAttribute : Attribute
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Internal.TypeSystem;

namespace ILCompiler.DependencyAnalysis
{
internal interface IDataDescriptorTypeProvider
{
MetadataType GetDataDescriptorType(CompilerTypeSystemContext context);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ namespace ILCompiler.DependencyAnalysis
/// can consume as a sub-descriptor. ILC knows managed type layouts at compile time,
/// so it can emit field offsets that would otherwise require runtime metadata resolution.
///
/// Types are discovered by scanning MetadataManager.GetTypesWithEETypes() for types
/// annotated with [DataContract], ensuring only types that actually have a MethodTable
/// in the binary are included.
/// Runtime types are discovered by scanning MetadataManager.GetTypesWithEETypes() for
/// [DataContract]-annotated types. ILC object nodes can also register annotated managed
/// layout types for data that does not have a MethodTable.
/// </summary>
public class ManagedDataDescriptorNode : ObjectNode, ISymbolDefinitionNode
{
Expand Down Expand Up @@ -48,7 +48,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
if (relocsOnly)
return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this });

byte[] jsonBytes = BuildJsonDescriptor(factory);
byte[] jsonBytes = BuildJsonDescriptor(factory.MetadataManager);

// Header layout: magic(8) + flags(4) + desc_size(4) + desc_ptr(ptr) + pointer_data_count(4) + pad(4) + pointer_data(ptr)
int headerSize = 8 + 4 + 4 + factory.Target.PointerSize + 4 + 4 + factory.Target.PointerSize;
Expand Down Expand Up @@ -92,7 +92,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
/// ContractDescriptorParser. Types are objects with an optional "!" size sigil and
/// field-name properties mapped to their offsets.
/// </summary>
private static byte[] BuildJsonDescriptor(NodeFactory factory)
internal static byte[] BuildJsonDescriptor(MetadataManager metadataManager)
{
using var stream = new MemoryStream();
using (var writer = new Utf8JsonWriter(stream))
Expand All @@ -102,7 +102,7 @@ private static byte[] BuildJsonDescriptor(NodeFactory factory)
writer.WriteString("baseline", "empty");

writer.WriteStartObject("types");
foreach (TypeDesc type in factory.MetadataManager.GetTypesWithEETypes())
foreach (TypeDesc type in metadataManager.GetTypesWithEETypes())
{
if (type is not EcmaType ecmaType)
continue;
Expand All @@ -112,6 +112,17 @@ private static byte[] BuildJsonDescriptor(NodeFactory factory)

WriteType(writer, ecmaType);
}

foreach (MetadataType type in metadataManager.GetDataDescriptorTypes())
{
if (type is not EcmaType ecmaType ||
!ecmaType.HasCustomAttribute(DataContractAttributeNamespace, DataContractAttributeName))
{
throw new InvalidOperationException($"Registered data descriptor type '{type}' is not annotated with [DataContract].");
}

WriteType(writer, ecmaType);
}
writer.WriteEndObject();

writer.WriteStartObject("globals");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1666,6 +1666,7 @@ public Utf8String GetSymbolAlternateName(ISymbolNode node, out bool isHidden)
public virtual void AttachToDependencyGraph(DependencyAnalyzerBase<NodeFactory> graph)
{
ReadyToRunHeader = new ReadyToRunHeaderNode();
MetadataManager.AttachToDependencyGraph(graph);

graph.AddRoot(ReadyToRunHeader, "ReadyToRunHeader is always generated");
graph.AddRoot(new ModulesSectionNode(), "ModulesSection is always generated");
Expand Down Expand Up @@ -1694,7 +1695,6 @@ public virtual void AttachToDependencyGraph(DependencyAnalyzerBase<NodeFactory>
InteropStubManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode);
TypeMapManager.AddToReadyToRunHeader(ReadyToRunHeader, this, new ExternalReferencesTableIndex(commonFixupsTableNode, this));
MetadataManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode);
MetadataManager.AttachToDependencyGraph(graph);
TypeMapManager.AttachToDependencyGraph(graph);
ReadyToRunHeader.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.CommonFixupsTable), commonFixupsTableNode);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Internal.Text;
using Internal.TypeSystem;

namespace ILCompiler.DependencyAnalysis
{
public class TypeManagerIndirectionNode : ObjectNode, ISymbolDefinitionNode
public class TypeManagerIndirectionNode : ObjectNode, ISymbolDefinitionNode, IDataDescriptorTypeProvider
{
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
Expand All @@ -31,5 +32,8 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
}

public override int ClassCode => -2028598574;

MetadataType IDataDescriptorTypeProvider.GetDataDescriptorType(CompilerTypeSystemContext context)
=> context.SystemModule.GetType("Internal.Runtime.CompilerHelpers"u8, "TypeManagerSlot"u8);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace ILCompiler.DependencyAnalysis
/// <summary>
/// Represents a node containing information necessary at runtime to locate type's thread static base.
/// </summary>
public class TypeThreadStaticIndexNode : DehydratableObjectNode, ISymbolDefinitionNode, ISortableSymbolNode
public class TypeThreadStaticIndexNode : DehydratableObjectNode, ISymbolDefinitionNode, ISortableSymbolNode, IDataDescriptorTypeProvider
{
private MetadataType _type;
private ThreadStaticsNode _inlinedThreadStatics;
Expand Down Expand Up @@ -96,6 +96,9 @@ protected override ObjectData GetDehydratableData(NodeFactory factory, bool relo

public override int ClassCode => -149601250;

MetadataType IDataDescriptorTypeProvider.GetDataDescriptorType(CompilerTypeSystemContext context)
=> context.SystemModule.GetType("Internal.Runtime.CompilerHelpers"u8, "TypeThreadStaticIndex"u8);

public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
{
return comparer.Compare(_type, ((TypeThreadStaticIndexNode)other)._type);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ namespace ILCompiler
{
/// <summary>
/// Compilation root provider that adds the managed cDAC data descriptor node.
/// The node discovers [DataContract]-annotated types from MetadataManager.GetTypesWithEETypes()
/// during object data emission, ensuring only types with MethodTables are included.
/// The node discovers [DataContract]-annotated runtime types and layouts registered by
/// ILC object nodes during object data emission.
/// </summary>
public class ManagedDataDescriptorProvider : ICompilationRootProvider
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public abstract class MetadataManager : ICompilationRootProvider
private readonly SortedSet<MetadataType> _typesWithThreadStaticsGenerated = new SortedSet<MetadataType>(CompilerComparer.Instance);
private readonly SortedSet<TypeDesc> _typesWithEETypesGenerated = new SortedSet<TypeDesc>(TypeSystemComparer.Instance);
private readonly SortedSet<TypeDesc> _typesWithConstructedEETypesGenerated = new SortedSet<TypeDesc>(TypeSystemComparer.Instance);
private readonly SortedSet<MetadataType> _dataDescriptorTypes = new SortedSet<MetadataType>(TypeSystemComparer.Instance);
private readonly SortedSet<MethodDesc> _methodsGenerated = new SortedSet<MethodDesc>(TypeSystemComparer.Instance);
private readonly SortedSet<MethodDesc> _reflectableMethods = new SortedSet<MethodDesc>(TypeSystemComparer.Instance);
private readonly SortedSet<GenericDictionaryNode> _genericDictionariesGenerated = new SortedSet<GenericDictionaryNode>(CompilerComparer.Instance);
Expand Down Expand Up @@ -250,6 +251,11 @@ public virtual void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFacto

protected virtual void Graph_NewMarkedNode(DependencyNodeCore<NodeFactory> obj)
{
if (obj is IDataDescriptorTypeProvider dataDescriptorTypeProvider)
{
_dataDescriptorTypes.Add(dataDescriptorTypeProvider.GetDataDescriptorType(_typeSystemContext));
}

var eetypeNode = obj as EETypeNode;
if (eetypeNode != null)
{
Expand Down Expand Up @@ -1156,6 +1162,11 @@ internal IEnumerable<TypeDesc> GetTypesWithEETypes()
return _typesWithEETypesGenerated;
}

internal IEnumerable<MetadataType> GetDataDescriptorTypes()
{
return _dataDescriptorTypes;
}

internal IEnumerable<TypeDesc> GetTypesWithConstructedEETypes()
{
return _typesWithConstructedEETypesGenerated;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
<NoWarn>$(NoWarn);CA1859</NoWarn>
</PropertyGroup>

<ItemGroup>
<InternalsVisibleTo Include="ILCompiler.Compiler.Tests" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\ILCompiler.DependencyAnalysisFramework\ILCompiler.DependencyAnalysisFramework.csproj" />
<ProjectReference Include="..\ILCompiler.Diagnostics\ILCompiler.Diagnostics.csproj" />
Expand Down Expand Up @@ -641,6 +645,7 @@
<Compile Include="Compiler\DependencyAnalysis\Target_X64\X64JumpStubNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\Target_X64\X64ReadyToRunHelperNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\InterfaceDispatchMapNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\IDataDescriptorTypeProvider.cs" />
<Compile Include="Compiler\DependencyAnalysis\Target_X86\X86UnboxingStubNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\Target_X86\X86JumpStubNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\Target_X86\X86ReadyToRunHelperNode.cs" />
Expand Down
Loading