Skip to content
Draft
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
4 changes: 4 additions & 0 deletions src/coreclr/inc/readytorun.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
// R2R 17 is not backward compatible with 16.x or earlier.
// R2R Version 17.1 adds the READYTORUN_FLAG_PLATFORM_NATIVE_IMAGE flag to specify that the R2R image pointed to by OwnerCompositeExecutable is in the platform native format.
// R2R Version 18 updates fields layout algorithm
// R2R Version 18.1 adds the ExternalTypeMaps, ProxyTypeMaps, TypeMapAssemblyTargets sections

struct READYTORUN_CORE_HEADER
{
Expand Down Expand Up @@ -117,6 +118,9 @@ enum class ReadyToRunSectionType : uint32_t
MethodIsGenericMap = 121, // Added in V9.0
EnclosingTypeMap = 122, // Added in V9.0
TypeGenericInfoMap = 123, // Added in V9.0
ExternalTypeMaps = 124, // Added in V18.1
ProxyTypeMaps = 125, // Added in V18.1
TypeMapAssemblyTargets = 126, // Added in V18.1

// If you add a new section consider whether it is a breaking or non-breaking change.
// Usually it is non-breaking, but if it is preferable to have older runtimes fail
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public static IReadOnlyDictionary<string, Type> CreateExternalTypeMap(RuntimeTyp
RuntimeTypeHandle typeMapGroupHandle = typeMapGroup.TypeHandle;
foreach (TypeManagerHandle module in RuntimeAugments.GetLoadedModules())
{
if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.ExternalTypeMap, out NativeReader externalTypeMapReader))
if (!TryGetNativeReaderForBlob(module, ReadyToRunSectionType.ExternalTypeMaps, out NativeReader externalTypeMapReader))
{
continue;
}
Expand Down Expand Up @@ -66,7 +66,7 @@ public static IReadOnlyDictionary<Type, Type> CreateProxyTypeMap(RuntimeType typ
RuntimeTypeHandle typeMapGroupHandle = typeMapGroup.TypeHandle;
foreach (TypeManagerHandle module in RuntimeAugments.GetLoadedModules())
{
if (!TryGetNativeReaderForBlob(module, ReflectionMapBlob.ProxyTypeMap, out NativeReader externalTypeMapReader))
if (!TryGetNativeReaderForBlob(module, ReadyToRunSectionType.ProxyTypeMaps, out NativeReader externalTypeMapReader))
{
continue;
}
Expand Down Expand Up @@ -104,14 +104,13 @@ public static IReadOnlyDictionary<Type, Type> CreateProxyTypeMap(RuntimeType typ
throw ReflectionCoreExecution.ExecutionEnvironment.CreateMissingMetadataException(typeMapGroup);
}

private static unsafe bool TryGetNativeReaderForBlob(TypeManagerHandle module, ReflectionMapBlob blob, out NativeReader reader)
private static unsafe bool TryGetNativeReaderForBlob(TypeManagerHandle module, ReadyToRunSectionType sectionType, out NativeReader reader)
{
byte* pBlob;
uint cbBlob;
byte* pBlob = (byte*)RuntimeImports.RhGetModuleSection(module, sectionType, out int length);

if (RuntimeImports.RhFindBlob(module, (uint)blob, &pBlob, &cbBlob))
if (pBlob != null)
{
reader = new NativeReader(pBlob, cbBlob);
reader = new NativeReader(pBlob, (uint)length);
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ protected internal enum ObjectNodeOrder
//
Win32ResourcesNode,
CorHeaderNode,
ReadyToRunHeaderNode,
GlobalHeaderNode,
ReadyToRunAssemblyHeaderNode,
ImportSectionsTableNode,
ImportSectionNode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

namespace ILCompiler.DependencyAnalysis
{
internal sealed class ExternalTypeMapObjectNode(ExternalReferencesTableNode externalReferences) : ObjectNode, ISymbolDefinitionNode, INodeWithSize
public sealed class ExternalTypeMapObjectNode(TypeMapManager manager, INativeFormatTypeReferenceProvider externalReferences) : ObjectNode, ISymbolDefinitionNode
{
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
Expand All @@ -26,7 +26,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
Section hashTableSection = writer.NewSection();
hashTableSection.Place(typeMapGroupHashTable);

foreach (IExternalTypeMapNode externalTypeMap in factory.TypeMapManager.GetExternalTypeMaps())
foreach (IExternalTypeMapNode externalTypeMap in manager.GetExternalTypeMaps())
{
typeMapGroupHashTable.Append((uint)externalTypeMap.TypeMapGroup.GetHashCode(), externalTypeMap.CreateTypeMap(factory, writer, hashTableSection, externalReferences));
}
Expand All @@ -45,7 +45,7 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
public int Size { get; private set; }
public int Offset => 0;
public override bool IsShareable => false;
public override ObjectNodeSection GetSection(NodeFactory factory) => externalReferences.GetSection(factory);
public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.DataSection;
protected internal override int Phase => (int)ObjectNodePhase.Ordered;

public override int ClassCode => (int)ObjectNodeOrder.ExternalTypeMapObjectNode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@

namespace ILCompiler.DependencyAnalysis
{
public interface IExternalTypeMapNode : IDependencyNode, ISortableNode
internal interface IExternalTypeMapNode : IDependencyNode, ISortableNode
{
TypeDesc TypeMapGroup { get; }

Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences);

Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider externalReferences);
#if !READYTORUN
IExternalTypeMapNode ToAnalysisBasedNode(NodeFactory factory);
#endif
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Text;
using ILCompiler.DependencyAnalysisFramework;
using Internal.NativeFormat;
using Internal.TypeSystem;

namespace ILCompiler.DependencyAnalysis
{
public interface INativeFormatTypeReferenceProvider
{
internal Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type);
internal Vertex EncodeReferenceToMethod(NativeWriter writer, MethodDesc method);
}
Comment on lines +13 to +17
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

INativeFormatTypeReferenceProvider is declared as a public interface, but its members are marked internal without providing default implementations. This won’t compile (interface members without bodies can’t have non-public accessibility). Make the interface itself internal, or make the members public (or provide default interface implementations if you intended non-public helpers).

Copilot uses AI. Check for mistakes.
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@

namespace ILCompiler.DependencyAnalysis
{
public interface IProxyTypeMapNode : IDependencyNode, ISortableNode
internal interface IProxyTypeMapNode : IDependencyNode, ISortableNode
{
TypeDesc TypeMapGroup { get; }

Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences);

IProxyTypeMapNode ToAnalysisBasedNode(NodeFactory factor);
Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider externalReferences);
#if !READYTORUN
IProxyTypeMapNode ToAnalysisBasedNode(NodeFactory factory);
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

namespace ILCompiler.DependencyAnalysis
{
internal sealed class ProxyTypeMapObjectNode(ExternalReferencesTableNode externalReferences) : ObjectNode, ISymbolDefinitionNode, INodeWithSize
internal sealed class ProxyTypeMapObjectNode(TypeMapManager manager, INativeFormatTypeReferenceProvider externalReferences) : ObjectNode, ISymbolDefinitionNode
{
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
Expand All @@ -27,7 +27,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
Section hashTableSection = writer.NewSection();
hashTableSection.Place(typeMapGroupHashTable);

foreach (IProxyTypeMapNode proxyTypeMap in factory.TypeMapManager.GetProxyTypeMaps())
foreach (IProxyTypeMapNode proxyTypeMap in manager.GetProxyTypeMaps())
{
TypeDesc typeMapGroup = proxyTypeMap.TypeMapGroup;
typeMapGroupHashTable.Append((uint)typeMapGroup.GetHashCode(), proxyTypeMap.CreateTypeMap(factory, writer, hashTableSection, externalReferences));
Expand All @@ -47,7 +47,7 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
public int Size { get; private set; }
public int Offset => 0;
public override bool IsShareable => false;
public override ObjectNodeSection GetSection(NodeFactory factory) => externalReferences.GetSection(factory);
public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.DataSection;
protected internal override int Phase => (int)ObjectNodePhase.Ordered;

public override int ClassCode => (int)ObjectNodeOrder.ProxyTypeMapObjectNode;
Expand Down
49 changes: 49 additions & 0 deletions src/coreclr/tools/Common/Compiler/TypeMapManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Reflection.Metadata;
using ILCompiler.DependencyAnalysis;
#if READYTORUN
using ILCompiler.DependencyAnalysis.ReadyToRun;
#endif
using ILCompiler.DependencyAnalysisFramework;
using Internal.Runtime;
using Internal.TypeSystem;

namespace ILCompiler
{

/// <summary>
/// This class is responsible for managing emitted data for type maps.
/// </summary>
public abstract class TypeMapManager : ICompilationRootProvider
{
public virtual void AttachToDependencyGraph(DependencyAnalyzerBase<NodeFactory> graph)
{
}

internal abstract IEnumerable<IExternalTypeMapNode> GetExternalTypeMaps();

internal abstract IEnumerable<IProxyTypeMapNode> GetProxyTypeMaps();

public abstract void AddCompilationRoots(IRootingServiceProvider rootProvider);

protected abstract bool IsEmpty { get; }

public virtual void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, INativeFormatTypeReferenceProvider commonFixupsTableNode)
{
if (IsEmpty)
{
return; // No type maps to emit
}

header.Add(ReadyToRunSectionType.ExternalTypeMaps, new ExternalTypeMapObjectNode(this, commonFixupsTableNode));
header.Add(ReadyToRunSectionType.ProxyTypeMaps, new ProxyTypeMapObjectNode(this, commonFixupsTableNode));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,47 @@
using Internal.IL.Stubs;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
using static ILCompiler.TypeMapManager;
using static ILCompiler.UsageBasedTypeMapManager;

namespace ILCompiler
{
public sealed class TypeMapMetadata
{
internal sealed class Map
private enum TypeMapAttributeKind
{
None,
TypeMapAssemblyTarget,
TypeMap,
TypeMapAssociation
}

private static TypeMapAttributeKind LookupTypeMapType(TypeDesc attrType)
{
var typeDef = attrType.GetTypeDefinition() as MetadataType;
if (typeDef != null && typeDef.Namespace.SequenceEqual("System.Runtime.InteropServices"u8))
{
if (typeDef.Name.SequenceEqual("TypeMapAssemblyTargetAttribute`1"u8))
return TypeMapAttributeKind.TypeMapAssemblyTarget;
else if (typeDef.Name.SequenceEqual("TypeMapAttribute`1"u8))
return TypeMapAttributeKind.TypeMap;
else if (typeDef.Name.SequenceEqual("TypeMapAssociationAttribute`1"u8))
return TypeMapAttributeKind.TypeMapAssociation;
}
return TypeMapAttributeKind.None;
}

internal interface IExternalTypeMap
{
IReadOnlyDictionary<string, (TypeDesc type, TypeDesc trimmingType)> TypeMap { get; }
MethodDesc ThrowingMethodStub { get; }
}

internal interface IProxyTypeMap
{
IReadOnlyDictionary<TypeDesc, TypeDesc> TypeMap { get; }
MethodDesc ThrowingMethodStub { get; }
}

internal sealed class Map : IExternalTypeMap, IProxyTypeMap
{
private sealed class ThrowingMethodStub : ILStubMethod
{
Expand All @@ -36,7 +69,11 @@ public ThrowingMethodStub(TypeDesc owningType, TypeDesc typeMapGroup, bool exter
public override ReadOnlySpan<byte> Name => _name;
public override MethodIL EmitIL()
{
#if READYTORUN
throw new UnreachableException();
#else
return TypeSystemThrowingILEmitter.EmitIL(this, Exception);
#endif
}

protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer)
Expand Down Expand Up @@ -156,24 +193,6 @@ public void MergePendingMap(Map pendingMap)
_targetModules.AddRange(pendingMap._targetModules);
}

public IExternalTypeMapNode GetExternalTypeMapNode()
{
if (_externalTypeMapExceptionStub is not null)
{
return new InvalidExternalTypeMapNode(TypeMapGroup, _externalTypeMapExceptionStub);
}
return new ExternalTypeMapNode(TypeMapGroup, _externalTypeMap);
}

public IProxyTypeMapNode GetProxyTypeMapNode()
{
if (_associatedTypeMapExceptionStub is not null)
{
return new InvalidProxyTypeMapNode(TypeMapGroup, _associatedTypeMapExceptionStub);
}
return new ProxyTypeMapNode(TypeMapGroup, _associatedTypeMap);
}

public void AddTargetModule(ModuleDesc targetModule)
{
_targetModules.Add(targetModule);
Expand All @@ -182,7 +201,15 @@ public void AddTargetModule(ModuleDesc targetModule)
/// <summary>
/// The modules targeted with TypeMapAssemblyTarget attributes for this type map group. This is only populated when TypeMapMetadata is created with TypeMapAssemblyTargetsMode.Record. When TypeMapMetadata is created with TypeMapAssemblyTargetsMode.Traverse, this will be empty as the target assemblies will be traversed to include their type maps instead of just being recorded as targets.
/// </summary>
public IEnumerable<ModuleDesc> TargetModules => _targetModules;
public IReadOnlyList<ModuleDesc> TargetModules => _targetModules;

IReadOnlyDictionary<string, (TypeDesc type, TypeDesc trimmingType)> IExternalTypeMap.TypeMap => _externalTypeMap;

MethodDesc IExternalTypeMap.ThrowingMethodStub => _externalTypeMapExceptionStub;

IReadOnlyDictionary<TypeDesc, TypeDesc> IProxyTypeMap.TypeMap => _associatedTypeMap;

MethodDesc IProxyTypeMap.ThrowingMethodStub => _associatedTypeMapExceptionStub;
}

public static readonly TypeMapMetadata Empty = new TypeMapMetadata(new Dictionary<TypeDesc, Map>(), "No type maps");
Expand All @@ -195,8 +222,6 @@ private TypeMapMetadata(IReadOnlyDictionary<TypeDesc, Map> states, string diagno
DiagnosticName = diagnosticName;
}

internal Map this[TypeDesc typeMapGroup] => _states[typeMapGroup];

public bool IsEmpty => _states.Count == 0;

internal IEnumerable<KeyValuePair<TypeDesc, Map>> Maps => _states;
Expand Down
4 changes: 0 additions & 4 deletions src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,5 @@ internal enum ReflectionMapBlob
StaticsInfoHashtable = 34,
GenericMethodsHashtable = 35,
ExactMethodInstantiationsHashtable = 36,

// Type map blobs:
ExternalTypeMap = 40,
ProxyTypeMap = 41,
}
}
5 changes: 5 additions & 0 deletions src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ enum ReadyToRunSectionType
EnclosingTypeMap = 122, // Added in V9.0
TypeGenericInfoMap = 123, // Added in V9.0

// Shared ReadyToRun sections
ExternalTypeMaps = 124, // Added to CoreCLR in V18.1
ProxyTypeMaps = 125, // Added to CoreCLR in V18.1
TypeMapAssemblyTargets = 126, // Added in V18.1

//
// NativeAOT ReadyToRun sections
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,24 @@

namespace ILCompiler.DependencyAnalysis
{
internal sealed class AnalyzedExternalTypeMapNode(TypeDesc typeMapGroup, IReadOnlyDictionary<string, TypeDesc> entries) : DependencyNodeCore<NodeFactory>, IExternalTypeMapNode
internal sealed class AnalyzedExternalTypeMapNode(TypeDesc typeMapGroup, IReadOnlyDictionary<string, TypeDesc> entries) : SortableDependencyNode, IExternalTypeMapNode
{
public TypeDesc TypeMapGroup => typeMapGroup;

public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences)
public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider externalReferences)
{
VertexHashtable typeMapHashTable = new();

foreach ((string key, TypeDesc type) in entries)
{
Vertex keyVertex = writer.GetStringConstant(key);
Vertex valueVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MetadataTypeSymbol(type)));
Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, type);
Vertex entry = writer.GetTuple(keyVertex, valueVertex);
typeMapHashTable.Append((uint)TypeHashingAlgorithms.ComputeNameHashCode(key), section.Place(entry));
}

Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state
Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(TypeMapGroup)));
Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup);
Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable);
return section.Place(tuple);
}
Expand All @@ -51,9 +51,9 @@ public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFacto
public override bool HasConditionalStaticDependencies => false;

public override bool StaticDependenciesAreComputed => true;
public int ClassCode => -874354558;
public override int ClassCode => -874354558;

public int CompareToImpl(ISortableNode other, CompilerComparer comparer)
public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
{
AnalyzedExternalTypeMapNode otherEntry = (AnalyzedExternalTypeMapNode)other;
return comparer.Compare(TypeMapGroup, otherEntry.TypeMapGroup);
Expand Down
Loading
Loading