From d96073b33f5ad04d1799d55f031dea1f93b076c7 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 4 Feb 2026 18:24:16 -0800 Subject: [PATCH 01/19] Add support to the ReadyToRun format for an R2R variant of the type map format and move NativeAOT to use the same R2R section identifier for its Type Map support --- src/coreclr/inc/readytorun.h | 3 + .../TypeMapLazyDictionary.NativeAot.cs | 8 +- .../Common/Internal/Runtime/MetadataBlob.cs | 4 - .../Common/Internal/Runtime/ModuleHeaders.cs | 4 + .../Compiler/TypeMapManager.cs | 9 +- src/coreclr/vm/assemblynative.cpp | 145 +++++++++++++- src/coreclr/vm/assemblynative.hpp | 14 ++ src/coreclr/vm/nativeformatreader.h | 85 ++++++++ src/coreclr/vm/readytoruninfo.cpp | 184 ++++++++++++++++++ src/coreclr/vm/readytoruninfo.h | 9 + .../InteropServices/TypeMapLazyDictionary.cs | 59 +++++- 11 files changed, 507 insertions(+), 17 deletions(-) diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index 7956fa7b77d0cb..0e97406316f9de 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -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 and ProxyTypeMaps sections struct READYTORUN_CORE_HEADER { @@ -117,6 +118,8 @@ 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 // 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 diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.NativeAot.cs index 9784f5d0a5b306..4ace72371f0001 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.NativeAot.cs @@ -23,7 +23,7 @@ public static IReadOnlyDictionary 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; } @@ -66,7 +66,7 @@ public static IReadOnlyDictionary 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; } @@ -104,12 +104,12 @@ public static IReadOnlyDictionary 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; - if (RuntimeImports.RhFindBlob(module, (uint)blob, &pBlob, &cbBlob)) + if (RuntimeImports.RhGetModuleSection(module, (uint)sectionType, &pBlob, &cbBlob)) { reader = new NativeReader(pBlob, cbBlob); return true; diff --git a/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs b/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs index 9a53c2e9dd3b8d..1803e980e3a411 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs @@ -44,9 +44,5 @@ internal enum ReflectionMapBlob StaticsInfoHashtable = 34, GenericMethodsHashtable = 35, ExactMethodInstantiationsHashtable = 36, - - // Type map blobs: - ExternalTypeMap = 40, - ProxyTypeMap = 41, } } diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index 0e849fb950ae3e..5b196f29f26c01 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -80,6 +80,10 @@ 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 + // // NativeAOT ReadyToRun sections // diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs index c59b9e854b3e94..8bb70eb83f151a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs @@ -9,10 +9,7 @@ using System.Reflection.Metadata; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; -using Internal.IL; -using Internal.IL.Stubs; -using Internal.TypeSystem; -using ReflectionMapBlob = Internal.Runtime.ReflectionMapBlob; +using Internal.Runtime; namespace ILCompiler { @@ -64,8 +61,8 @@ public void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeF return; // No type maps to emit } - header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.ExternalTypeMap), new ExternalTypeMapObjectNode(commonFixupsTableNode)); - header.Add(MetadataManager.BlobIdToReadyToRunSection(ReflectionMapBlob.ProxyTypeMap), new ProxyTypeMapObjectNode(commonFixupsTableNode)); + header.Add(ReadyToRunSectionType.ExternalTypeMaps, new ExternalTypeMapObjectNode(commonFixupsTableNode)); + header.Add(ReadyToRunSectionType.ProxyTypeMaps, new ProxyTypeMapObjectNode(commonFixupsTableNode)); } } } diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp index a962e22b5fe31c..8ecbf384308005 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -1535,6 +1535,57 @@ namespace } } +#ifdef FEATURE_READYTORUN + template + void ProcessPrecachedTypeMaps( + TExternalTypeMapCallback onNewExternalTypeMap, + TProxyTypeMapCallback onNewProxyTypeMap, + MethodTable* groupTypeMT, + Assembly* pAssembly) + { + STANDARD_VM_CONTRACT; + + PTR_Module pModule = pAssembly->GetModule(); + if (!m_pModule->IsReadyToRun()) + { + return; + } + PTR_ReadyToRunInfo pR2RInfo = pModule->GetReadyToRunInfo(); + + if (pR2RInfo->HasPrecachedExternalTypeMap(groupTypeMT)) + { + GCX_COOP(); + REFLECTIONMODULEBASEREF moduleRef = pModule->GetExposedObject(); + + GCPROTECT_BEGIN(&moduleRef); + { + GCX_PREEMP(); + if (!onNewExternalTypeMap(&moduleRef)) + { + return; + } + } + GCPROTECT_END(); + } + + if (pR2RInfo->HasPrecachedProxyTypeMap(groupTypeMT)) + { + GCX_COOP(); + REFLECTIONMODULEBASEREF moduleRef = pModule->GetExposedObject(); + + GCPROTECT_BEGIN(&moduleRef); + { + GCX_PREEMP(); + if (!onNewProxyTypeMap(&moduleRef)) + { + return; + } + } + GCPROTECT_END(); + } + } +#endif + class AssemblyPtrCollectionTraits : public DefaultSHashTraits { public: @@ -1652,6 +1703,8 @@ extern "C" void QCALLTYPE TypeMapLazyDictionary_ProcessAttributes( QCall::TypeHandle pGroupType, BOOL (*newExternalTypeEntry)(CallbackContext* context, ProcessAttributesCallbackArg* arg), BOOL (*newProxyTypeEntry)(CallbackContext* context, ProcessAttributesCallbackArg* arg), + BOOL (*newPrecachedExternalTypeMap)(CallbackContext* context, REFLECTMODULEBASEREF* module), + BOOL (*newPrecachedProxyTypeMap)(CallbackContext* context, REFLECTMODULEBASEREF* module), CallbackContext* context) { QCALL_CONTRACT; @@ -1683,7 +1736,29 @@ extern "C" void QCALLTYPE TypeMapLazyDictionary_ProcessAttributes( groupTypeMT, currAssembly); - if (newExternalTypeEntry != NULL) + bool foundPrecachedExternalTypeMap = false; + bool foundPrecachedProxyTypeMap = false; + ProcessPrecachedTypeMaps( + [=, &foundPrecachedExternalTypeMap](REFLECTMODULEBASEREF* module) -> BOOL + { + STANDARD_VM_CONTRACT; + foundPrecachedExternalTypeMap = true; + ASSERT_PROTECTED(module); + return newPrecachedExternalTypeMap(context, module); + }, + [=, &foundPrecachedProxyTypeMap](REFLECTMODULEBASEREF* module) -> BOOL + { + STANDARD_VM_CONTRACT; + foundPrecachedProxyTypeMap = true; + ASSERT_PROTECTED(module); + return newPrecachedProxyTypeMap(context, module); + }, + groupTypeMT, + currAssembly); + + // We will only process the specific type maps if we have a callback to process + // the entry and the precached map was not calculated for this module. + if (newExternalTypeEntry != NULL && !foundPrecachedExternalTypeMap) { MappingsProcessor onExternalType{ newExternalTypeEntry, context }; ProcessTypeMapAttribute( @@ -1693,7 +1768,7 @@ extern "C" void QCALLTYPE TypeMapLazyDictionary_ProcessAttributes( currAssembly); } - if (newProxyTypeEntry != NULL) + if (newProxyTypeEntry != NULL && !foundPrecachedProxyTypeMap) { MappingsProcessor onProxyType{ newProxyTypeEntry, context }; ProcessTypeMapAttribute( @@ -1706,3 +1781,69 @@ extern "C" void QCALLTYPE TypeMapLazyDictionary_ProcessAttributes( END_QCALL; } + +extern "C" TypeHandle QCALLTYPE TypeMapLazyDictionary_FindPrecachedExternalTypeMapEntry( + QCall::ModuleHandle pModule, + QCall::TypeHandle pGroupType, + LPCUTF8 pKey) +{ + QCALL_CONTRACT; + _ASSERTE(pModule != NULL); + _ASSERTE(!pGroupType.AsTypeHandle().IsNull()); + + TypeHandle resultTypeHnd; + + BEGIN_QCALL; + +#ifdef FEATURE_READYTORUN + if (pModule->IsReadyToRun()) + { + PTR_ReadyToRunInfo pR2RInfo = pModule->GetReadyToRunInfo(); + + TypeHandle groupTypeTH = pGroupType.AsTypeHandle(); + _ASSERTE(!groupTypeTH.IsTypeDesc()); + MethodTable* groupTypeMT = groupTypeTH.AsMethodTable(); + + resultTypeHnd = pR2RInfo->FindPrecachedExternalTypeMapEntry( + groupTypeMT, + pKey); + } +#endif // FEATURE_READYTORUN + + END_QCALL; + + return resultTypeHnd; +} + +extern "C" TypeHandle QCALLTYPE TypeMapLazyDictionary_FindPrecachedProxyTypeMapEntry( + QCall::ModuleHandle pModule, + QCall::TypeHandle pGroupType, + QCall::TypeHandle pType) +{ + QCALL_CONTRACT; + _ASSERTE(pModule != NULL); + _ASSERTE(!pGroupType.AsTypeHandle().IsNull()); + + TypeHandle resultTypeHnd; + + BEGIN_QCALL; + +#ifdef FEATURE_READYTORUN + if (pModule->IsReadyToRun()) + { + PTR_ReadyToRunInfo pR2RInfo = pModule->GetReadyToRunInfo(); + + TypeHandle groupTypeTH = pGroupType.AsTypeHandle(); + _ASSERTE(!groupTypeTH.IsTypeDesc()); + MethodTable* groupTypeMT = groupTypeTH.AsMethodTable(); + + resultTypeHnd = pR2RInfo->FindPrecachedProxyTypeMapEntry( + groupTypeMT, + pType.AsTypeHandle()); + } +#endif // FEATURE_READYTORUN + + END_QCALL; + + return resultTypeHnd; +} diff --git a/src/coreclr/vm/assemblynative.hpp b/src/coreclr/vm/assemblynative.hpp index cba6b322391d2a..d427645811b9fa 100644 --- a/src/coreclr/vm/assemblynative.hpp +++ b/src/coreclr/vm/assemblynative.hpp @@ -168,6 +168,20 @@ extern "C" void QCALLTYPE TypeMapLazyDictionary_ProcessAttributes( QCall::TypeHandle pTypeGroup, BOOL (*newExternalTypeEntry)(CallbackContext* context, ProcessAttributesCallbackArg* arg), BOOL (*newProxyTypeEntry)(CallbackContext* context, ProcessAttributesCallbackArg* arg), + BOOL (*newPrecachedExternalTypeMap)(CallbackContext* context, REFLECTMODULEBASEREF* module), + BOOL (*newPrecachedProxyTypeMap)(CallbackContext* context, REFLECTMODULEBASEREF* module), CallbackContext* context); +extern "C" TypeHandle TypeMapLazyDictionary_FindPrecachedExternalTypeMapEntry( + QCall::ModuleHandle pModule, + QCall::TypeHandle pGroupType, + QCall::TypeHandle pType +); + +extern "C" TypeHandle TypeMapLazyDictionary_FindPrecachedProxyTypeMapEntry( + QCall::ModuleHandle pModule, + QCall::TypeHandle pGroupType, + QCall::TypeHandle pType +); + #endif diff --git a/src/coreclr/vm/nativeformatreader.h b/src/coreclr/vm/nativeformatreader.h index d20f324610b8fb..5c95f0a9d1b809 100644 --- a/src/coreclr/vm/nativeformatreader.h +++ b/src/coreclr/vm/nativeformatreader.h @@ -101,6 +101,13 @@ namespace NativeFormat return *dac_cast(_base + offset); // Assumes little endian and unaligned access } + UInt64 ReadUInt64(uint offset) + { + if ((int)offset < 0 || offset + 7 >= _size) + ThrowBadImageFormatException(); + return *dac_cast(_base + offset); // Assumes little endian and unaligned access + } + uint DecodeUnsigned(uint offset, uint * pValue) { if (offset >= _size) @@ -247,6 +254,69 @@ namespace NativeFormat } } + uint DecodeString(uint offset, PTR_CBYTE * ppValue, uint * pLength) + { + uint numBytes; + offset = DecodeUnsigned(offset, &numBytes); + + if (numBytes == 0) + { + *ppValue = NULL; + *pLength = 0; + return offset; + } + + uint endOffset = offset + numBytes; + if (endOffset < numBytes || endOffset > _size) + ThrowBadImageFormatException(); + + *ppValue = _base + offset; + *pLength = numBytes; + + return endOffset; + } + + uint SkipString(uint offset) + { + uint numBytes; + offset = DecodeUnsigned(offset, &numBytes); + + if (numBytes == 0) + { + return offset; + } + + uint endOffset = offset + numBytes; + if (endOffset < numBytes || endOffset > _size) + ThrowBadImageFormatException(); + + return endOffset; + } + + bool StringEquals(uint offset, LPCUTF8 value, uint valueLength) + { + uint originalOffset = offset; + + uint numBytes; + offset = DecodeUnsigned(offset, &numBytes); + + uint endOffset = offset + numBytes; + if (endOffset < numBytes || offset > _size) + ThrowBadImageFormatException(); + + if (numBytes != valueLength) + return false; + + PTR_CBYTE pData = _base + offset; + for (uint i = 0; i < numBytes; i++) + { + if (pData[i] != (byte)value[i]) + return false; + } + + return true; + } + #ifndef DACCESS_COMPILE const BYTE* GetBlob(uint offset) { @@ -331,6 +401,21 @@ namespace NativeFormat _offset = _pReader->SkipInteger(_offset); } + void GetString(PTR_CBYTE * ppValue, uint * pLength) + { + _offset = _pReader->DecodeString(_offset, ppValue, pLength); + } + + void SkipString() + { + _offset = _pReader->SkipString(_offset); + } + + bool StringEquals(LPCUTF8 value, uint valueLength) + { + return _pReader->StringEquals(_offset, value, valueLength); + } + NativeParser GetParserFromRelativeOffset() { return NativeParser(_pReader, GetRelativeOffset()); diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index 3cd42e18b8ff5a..cef3b2d6c2d355 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -930,6 +930,21 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocat } #endif + if (IsImageVersionAtLeast(18, 1)) + { + IMAGE_DATA_DIRECTORY* pExternalTypeMapsDir = m_component.FindSection(ReadyToRunSectionType::ExternalTypeMaps); + if (pExternalTypeMapsDir != NULL) + { + m_externalTypeMaps = NativeHashtable(NativeParser(&m_nativeReader, pExternalTypeMapsDir->VirtualAddress)); + } + + IMAGE_DATA_DIRECTORY* pProxyTypeMapsDir = m_component.FindSection(ReadyToRunSectionType::ProxyTypeMaps); + if (pProxyTypeMapsDir != NULL) + { + m_proxyTypeMaps = NativeHashtable(NativeParser(&m_nativeReader, pProxyTypeMapsDir->VirtualAddress)); + } + } + if (!m_isComponentAssembly) { // For component assemblies we don't initialize the reverse lookup map mapping entry points to MethodDescs; @@ -1553,6 +1568,175 @@ void ReadyToRunInfo::DisableCustomAttributeFilter() m_attributesPresence.DisableFilter(); } +namespace +{ + TypeHandle GetTypeHandleForNativeFormatFixupReference(PTR_ReadyToRunInfo pR2RInfo, PTR_Module pModule, uint32_t importSection, uint32_t fixupIndex) + { + STANDARD_VM_CONTRACT; + + COUNT_T countImportSections; + PTR_READYTORUN_IMPORT_SECTION pImportSections = pR2RInfo->GetImportSections(&countImportSections); + + if (importSection >= countImportSections) + { + _ASSERTE(!"Malformed PGO type or method handle data"); + return TypeHandle(); + } + + PTR_READYTORUN_IMPORT_SECTION pImportSection = &pImportSections[importSection]; + COUNT_T cbData; + TADDR pData = pR2RInfo->GetImage()->GetDirectoryData(&pImportSection->Section, &cbData); + PTR_SIZE_T fixupAddress = dac_cast(pData + fixupIndex * sizeof(TADDR)); + if (!pModule->FixupNativeEntry(pImportSections + importSection, fixupIndex, fixupAddress)) + { + return TypeHandle(); + } + + return *(TypeHandle*)fixupAddress; + } +} + +bool ReadyToRunInfo::HasPrecachedExternalTypeMap(MethodTable* pGroupTypeMT) +{ + STANDARD_VM_CONTRACT; + + _ASSERTE(pGroupTypeMT != nullptr); + + if (m_externalTypeMaps.IsNull()) + { + return false; + } + + UINT32 hash = GetVersionResilientTypeHashCode(pGroupTypeMT); + NativeHashtable::Enumerator lookup = m_externalTypeMaps.Lookup(hash); + NativeParser entryParser; + while (lookup.GetNext(entryParser)) + { + if (GetTypeHandleForNativeFormatFixupReference(this, m_pModule, entryParser.GetUnsigned(), entryParser.GetUnsigned()) == TypeHandle(pGroupTypeMT)) + { + return true; + } + } + return false; +} + +TypeHandle ReadyToRunInfo::FindPrecachedExternalTypeMapEntry(MethodTable* pGroupType, LPCUTF8 pKey) +{ + STANDARD_VM_CONTRACT; + + _ASSERTE(pGroupType != nullptr); + if (m_externalTypeMaps.IsNull()) + { + return TypeHandle(); + } + + UINT32 hash = GetVersionResilientTypeHashCode(pGroupType); + NativeHashtable::Enumerator lookup = m_externalTypeMaps.Lookup(hash); + NativeParser entryParser; + while (lookup.GetNext(entryParser)) + { + if (GetTypeHandleForNativeFormatFixupReference(this, m_pModule, entryParser.GetUnsigned(), entryParser.GetUnsigned()) != TypeHandle(pGroupType)) + { + continue; + } + + if (entryParser.GetUnsigned() == 0) + { + // Table is not valid + return TypeHandle(); + } + + NativeHashtable typeMapTable = NativeHashtable(entryParser); + + UINT32 typeArgHash = ComputeNameHashCode(pKey); + NativeHashtable::Enumerator typeMapLookup = typeMapTable.Lookup(typeArgHash); + NativeParser typeMapEntryParser; + while (typeMapLookup.GetNext(typeMapEntryParser)) + { + if (typeMapEntryParser.StringEquals(pKey, (uint32_t)strlen(pKey))) + { + return GetTypeHandleForNativeFormatFixupReference(this, m_pModule, typeMapEntryParser.GetUnsigned(), typeMapEntryParser.GetUnsigned()); + } + } + + // No matching entry found in the table. + return TypeHandle(); + } + + // No table found for the group type. + return TypeHandle(); +} + +bool ReadyToRunInfo::HasPrecachedProxyTypeMap(MethodTable* pGroupType) +{ + STANDARD_VM_CONTRACT; + _ASSERTE(pGroupType != nullptr); + if (m_proxyTypeMaps.IsNull()) + { + return false; + } + UINT32 hash = GetVersionResilientTypeHashCode(pGroupType); + NativeHashtable::Enumerator lookup = m_proxyTypeMaps.Lookup(hash); + NativeParser entryParser; + while (lookup.GetNext(entryParser)) + { + if (GetTypeHandleForNativeFormatFixupReference(this, m_pModule, entryParser.GetUnsigned(), entryParser.GetUnsigned()) == TypeHandle(pGroupType)) + { + return true; + } + } + return false; +} + +TypeHandle ReadyToRunInfo::FindPrecachedProxyTypeMapEntry(MethodTable* pGroupType, TypeHandle key) +{ + STANDARD_VM_CONTRACT; + + _ASSERTE(pGroupType != nullptr); + if (m_proxyTypeMaps.IsNull()) + { + return TypeHandle(); + } + + UINT32 hash = GetVersionResilientTypeHashCode(pGroupType); + NativeHashtable::Enumerator lookup = m_proxyTypeMaps.Lookup(hash); + NativeParser entryParser; + while (lookup.GetNext(entryParser)) + { + if (GetTypeHandleForNativeFormatFixupReference(this, m_pModule, entryParser.GetUnsigned(), entryParser.GetUnsigned()) != TypeHandle(pGroupType)) + { + continue; + } + + if (entryParser.GetUnsigned() == 0) + { + // Table is not valid + return TypeHandle(); + } + + NativeHashtable typeMapTable = NativeHashtable(entryParser); + + UINT32 typeArgHash = GetVersionResilientTypeHashCode(key); + NativeHashtable::Enumerator typeMapLookup = typeMapTable.Lookup(typeArgHash); + NativeParser typeMapEntryParser; + while (typeMapLookup.GetNext(typeMapEntryParser)) + { + if (GetTypeHandleForNativeFormatFixupReference(this, m_pModule, entryParser.GetUnsigned(), entryParser.GetUnsigned()) != TypeHandle(pGroupType)) + { + continue; + } + + return GetTypeHandleForNativeFormatFixupReference(this, m_pModule, typeMapEntryParser.GetUnsigned(), typeMapEntryParser.GetUnsigned()); + } + + // No matching entry found in the table. + return TypeHandle(); + } + + // No table found for the group type. + return TypeHandle(); +} + class NativeManifestModule : public ModuleBase { IMDInternalImport* m_pMDImport; diff --git a/src/coreclr/vm/readytoruninfo.h b/src/coreclr/vm/readytoruninfo.h index 9f58e81c736c93..9746a42d6f9d0a 100644 --- a/src/coreclr/vm/readytoruninfo.h +++ b/src/coreclr/vm/readytoruninfo.h @@ -143,6 +143,9 @@ class ReadyToRunInfo PTR_PersistentInlineTrackingMapR2R m_pPersistentInlineTrackingMap; PTR_PersistentInlineTrackingMapR2R m_pCrossModulePersistentInlineTrackingMap; + NativeFormat::NativeHashtable m_externalTypeMaps; + NativeFormat::NativeHashtable m_proxyTypeMaps; + PTR_ReadyToRunInfo m_pNextR2RForUnrelatedCode; public: @@ -330,6 +333,12 @@ class ReadyToRunInfo bool MayHaveCustomAttribute(WellKnownAttribute attribute, mdToken token); void DisableCustomAttributeFilter(); + bool HasPrecachedExternalTypeMap(MethodTable* pGroupType); + TypeHandle FindPrecachedExternalTypeMapEntry(MethodTable* pGroupType, LPCUTF8 pKey); + + bool HasPrecachedProxyTypeMap(MethodTable* pGroupType); + TypeHandle FindPrecachedProxyTypeMapEntry(MethodTable* pGroupType, TypeHandle key); + BOOL IsImageVersionAtLeast(int majorVersion, int minorVersion); private: BOOL GetTypeNameFromToken(IMDInternalImport * pImport, mdToken mdType, LPCUTF8 * ppszName, LPCUTF8 * ppszNameSpace); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs index 048b0a4679f83f..04677cf0790fa7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs @@ -78,6 +78,8 @@ private static unsafe partial void ProcessAttributes( QCallTypeHandle groupType, delegate* unmanaged newExternalTypeEntry, delegate* unmanaged newProxyTypeEntry, + delegate* unmanaged newPrecachedExternalTypeMap, + delegate* unmanaged newPrecachedProxyTypeMap, CallbackContext* context); public ref struct Utf16SharedBuffer @@ -118,6 +120,44 @@ private static void ConvertUtf8ToUtf16(ReadOnlySpan utf8TypeName, out Utf1 utf16Buffer = new Utf16SharedBuffer(buffer, converted); } + [UnmanagedCallersOnly] + private static unsafe Interop.BOOL NewPrecachedExternalTypeMap(CallbackContext* context, RuntimeModule* module) + { + Debug.Assert(context != null); + Debug.Assert(module != default); + + try + { + context->ExternalTypeMap.AddPreCachedModule(*module); + } + catch (Exception ex) + { + context->CreationException = ExceptionDispatchInfo.Capture(ex); + return Interop.BOOL.FALSE; // Stop processing. + } + + return Interop.BOOL.TRUE; // Continue processing. + } + + [UnmanagedCallersOnly] + private static unsafe Interop.BOOL NewPrecachedProxyTypeMap(CallbackContext* context, RuntimeModule* module) + { + Debug.Assert(context != null); + Debug.Assert(module != default); + + try + { + context->ProxyTypeMap.AddPreCachedModule(*module); + } + catch (Exception ex) + { + context->CreationException = ExceptionDispatchInfo.Capture(ex); + return Interop.BOOL.FALSE; // Stop processing. + } + + return Interop.BOOL.TRUE; // Continue processing. + } + [UnmanagedCallersOnly] private static unsafe Interop.BOOL NewExternalTypeEntry(CallbackContext* context, ProcessAttributesCallbackArg* arg) { @@ -243,12 +283,24 @@ public static IReadOnlyDictionary CreateProxyTypeMap(RuntimeType gro private abstract class LazyTypeLoadDictionary : IReadOnlyDictionary where TKey : notnull { + private readonly List _preCachedModules = []; + protected abstract bool TryGetOrLoadType(TKey key, [NotNullWhen(true)] out Type? type); + protected abstract bool TryGetOrLoadTypeFromPreCachedDictionary(RuntimeModule module, TKey key, [NotNullWhen(true)] out Type? type); + public Type this[TKey key] { get { + foreach (RuntimeModule module in _preCachedModules) + { + if (TryGetOrLoadTypeFromPreCachedDictionary(module, key, out Type? type)) + { + return type; + } + } + if (!TryGetOrLoadType(key, out Type? type)) { ThrowHelper.ThrowKeyNotFoundException(key); @@ -258,6 +310,11 @@ public Type this[TKey key] } } + public void AddPreCachedModule(RuntimeModule module) + { + _preCachedModules.Add(module); + } + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out Type value) => TryGetOrLoadType(key, out value); // Not supported to avoid exposing TypeMap entries in a manner that @@ -317,7 +374,7 @@ public unsafe Type GetOrLoadType() [RequiresUnreferencedCode("Lazy TypeMap isn't supported for Trimmer scenarios")] private sealed class LazyExternalTypeDictionary : LazyTypeLoadDictionary { - private readonly Dictionary _lazyData = new(); + private readonly Dictionary _lazyData = []; protected override bool TryGetOrLoadType(string key, [NotNullWhen(true)] out Type? type) { From f3dd68e82e9b61f7cddf34d9916faaf2d1609f1a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 4 Feb 2026 18:26:20 -0800 Subject: [PATCH 02/19] Rename HeaderNode to ReadyToRunHeaderNode to provide better support for sharing between NativeAOT and R2R. --- ...{HeaderNode.cs => ReadyToRunHeaderNode.cs} | 16 ++++--- .../ReadyToRunCodegenNodeFactory.cs | 42 +++++++++---------- .../Compiler/ReadyToRunCodegenCompilation.cs | 2 +- .../ILCompiler.ReadyToRun.csproj | 2 +- 4 files changed, 34 insertions(+), 28 deletions(-) rename src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/{HeaderNode.cs => ReadyToRunHeaderNode.cs} (94%) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunHeaderNode.cs similarity index 94% rename from src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs rename to src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunHeaderNode.cs index 702e69f6192c42..3f556dda8aedc9 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunHeaderNode.cs @@ -79,7 +79,7 @@ public sealed override void AppendMangledName(NameMangler nameMangler, Utf8Strin } } - public abstract class HeaderNode : ObjectNode, ISymbolDefinitionNode + public abstract class ReadyToRunHeaderNode : ObjectNode, ISymbolDefinitionNode { struct HeaderItem { @@ -99,7 +99,7 @@ public HeaderItem(ReadyToRunSectionType id, DependencyNodeCore node private readonly ReadyToRunFlags _flags; private readonly Task<(bool canSkipValidation, string[] reasons)> _shouldAddSkipTypeValidationFlag; - public HeaderNode(ReadyToRunFlags flags, EcmaModule moduleToCheckForSkipTypeValidation) + public ReadyToRunHeaderNode(ReadyToRunFlags flags, EcmaModule moduleToCheckForSkipTypeValidation) { if (moduleToCheckForSkipTypeValidation != null) @@ -118,6 +118,12 @@ public void Add(ReadyToRunSectionType id, DependencyNodeCore node, _items.Add(new HeaderItem(id, node, startSymbol)); } + public void Add(ReadyToRunSectionType id, T node) + where T : DependencyNodeCore, ISymbolNode + { + Add(id, node, node); + } + public int Offset => 0; public override bool IsShareable => false; @@ -200,7 +206,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) protected internal override int Phase => (int)ObjectNodePhase.Ordered; } - public class GlobalHeaderNode : HeaderNode + public class GlobalHeaderNode : ReadyToRunHeaderNode { public GlobalHeaderNode(ReadyToRunFlags flags, EcmaModule moduleToCheckForSkipValidation) : base(flags, moduleToCheckForSkipValidation) @@ -223,10 +229,10 @@ protected override void EmitHeaderPrefix(ref ObjectDataBuilder builder) builder.EmitShort((short)(ReadyToRunHeaderConstants.CurrentMinorVersion)); } - public override int ClassCode => (int)ObjectNodeOrder.ReadyToRunHeaderNode; + public override int ClassCode => (int)ObjectNodeOrder.GlobalHeaderNode; } - public class AssemblyHeaderNode : HeaderNode + public class AssemblyHeaderNode : ReadyToRunHeaderNode { private readonly int _index; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index a2a846481e3091..ab63001517fe84 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -99,7 +99,7 @@ public void GenerateHotColdMap(DependencyAnalyzerBase dependencyGra if (HotColdMap == null) { HotColdMap = new HotColdMapNode(); - Header.Add(Internal.Runtime.ReadyToRunSectionType.HotColdMap, HotColdMap, HotColdMap); + Header.Add(Internal.Runtime.ReadyToRunSectionType.HotColdMap, HotColdMap); dependencyGraph.AddRoot(HotColdMap, "HotColdMap is generated because there is cold code"); } } @@ -701,37 +701,37 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph, I graph.ComputingDependencyPhaseChange += Graph_ComputingDependencyPhaseChange; var compilerIdentifierNode = new CompilerIdentifierNode(Target); - Header.Add(Internal.Runtime.ReadyToRunSectionType.CompilerIdentifier, compilerIdentifierNode, compilerIdentifierNode); + Header.Add(Internal.Runtime.ReadyToRunSectionType.CompilerIdentifier, compilerIdentifierNode); RuntimeFunctionsTable = new RuntimeFunctionsTableNode(this); - Header.Add(Internal.Runtime.ReadyToRunSectionType.RuntimeFunctions, RuntimeFunctionsTable, RuntimeFunctionsTable); + Header.Add(Internal.Runtime.ReadyToRunSectionType.RuntimeFunctions, RuntimeFunctionsTable); RuntimeFunctionsGCInfo = new RuntimeFunctionsGCInfoNode(); graph.AddRoot(RuntimeFunctionsGCInfo, "GC info is always generated"); DelayLoadMethodCallThunks = new SymbolNodeRange("DelayLoadMethodCallThunkNodeRange"); graph.AddRoot(DelayLoadMethodCallThunks, "DelayLoadMethodCallThunks header entry is always generated"); - Header.Add(Internal.Runtime.ReadyToRunSectionType.DelayLoadMethodCallThunks, DelayLoadMethodCallThunks, DelayLoadMethodCallThunks); + Header.Add(Internal.Runtime.ReadyToRunSectionType.DelayLoadMethodCallThunks, DelayLoadMethodCallThunks); ExceptionInfoLookupTableNode exceptionInfoLookupTableNode = new ExceptionInfoLookupTableNode(this); - Header.Add(Internal.Runtime.ReadyToRunSectionType.ExceptionInfo, exceptionInfoLookupTableNode, exceptionInfoLookupTableNode); + Header.Add(Internal.Runtime.ReadyToRunSectionType.ExceptionInfo, exceptionInfoLookupTableNode); graph.AddRoot(exceptionInfoLookupTableNode, "ExceptionInfoLookupTable is always generated"); ManifestMetadataTable = new ManifestMetadataTableNode(this); - Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestMetadata, ManifestMetadataTable, ManifestMetadataTable); + Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestMetadata, ManifestMetadataTable); Resolver.SetModuleIndexLookup(ManifestMetadataTable.ModuleToIndex); ((ReadyToRunILProvider)ilProvider).InitManifestMutableModule(ManifestMetadataTable._mutableModule); Resolver.InitManifestMutableModule(ManifestMetadataTable._mutableModule); ManifestAssemblyMvidHeaderNode mvidTableNode = new ManifestAssemblyMvidHeaderNode(ManifestMetadataTable); - Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestAssemblyMvids, mvidTableNode, mvidTableNode); + Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestAssemblyMvids, mvidTableNode); AssemblyTableNode assemblyTable = null; if (CompilationModuleGroup.IsCompositeBuildMode) { assemblyTable = new AssemblyTableNode(); - Header.Add(Internal.Runtime.ReadyToRunSectionType.ComponentAssemblies, assemblyTable, assemblyTable); + Header.Add(Internal.Runtime.ReadyToRunSectionType.ComponentAssemblies, assemblyTable); } // Generate per assembly header tables @@ -739,7 +739,7 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph, I foreach (EcmaModule inputModule in CompilationModuleGroup.CompilationModuleSet) { assemblyIndex++; - HeaderNode tableHeader = Header; + ReadyToRunHeaderNode tableHeader = Header; if (assemblyTable != null) { AssemblyHeaderNode perAssemblyHeader = new AssemblyHeaderNode(ReadyToRunFlags.READYTORUN_FLAG_Component, assemblyIndex); @@ -748,15 +748,15 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph, I } MethodEntryPointTableNode methodEntryPointTable = new MethodEntryPointTableNode(inputModule); - tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.MethodDefEntryPoints, methodEntryPointTable, methodEntryPointTable); + tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.MethodDefEntryPoints, methodEntryPointTable); TypesTableNode typesTable = new TypesTableNode(inputModule); - tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.AvailableTypes, typesTable, typesTable); + tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.AvailableTypes, typesTable); if (CompilationModuleGroup.IsCompositeBuildMode) { InliningInfoNode inliningInfoTable = new InliningInfoNode(inputModule, InliningInfoNode.InfoType.InliningInfo2); - tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.InliningInfo2, inliningInfoTable, inliningInfoTable); + tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.InliningInfo2, inliningInfoTable); } // Core library attributes are checked FAR more often than other dlls @@ -766,41 +766,41 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph, I if (inputModule == TypeSystemContext.SystemModule) { AttributePresenceFilterNode attributePresenceTable = new AttributePresenceFilterNode(inputModule); - tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.AttributePresence, attributePresenceTable, attributePresenceTable); + tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.AttributePresence, attributePresenceTable); } if (EnclosingTypeMapNode.IsSupported(inputModule.MetadataReader)) { var node = new EnclosingTypeMapNode(inputModule); - tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.EnclosingTypeMap, node, node); + tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.EnclosingTypeMap, node); } if (TypeGenericInfoMapNode.IsSupported(inputModule.MetadataReader)) { var node = new TypeGenericInfoMapNode(inputModule); - tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.TypeGenericInfoMap, node, node); + tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.TypeGenericInfoMap, node); } if (MethodIsGenericMapNode.IsSupported(inputModule.MetadataReader)) { var node = new MethodIsGenericMapNode(inputModule); - tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.MethodIsGenericMap, node, node); + tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.MethodIsGenericMap, node); } } InliningInfoNode crossModuleInliningInfoTable = new InliningInfoNode(null, CompilationModuleGroup.IsCompositeBuildMode ? InliningInfoNode.InfoType.CrossModuleInliningForCrossModuleDataOnly : InliningInfoNode.InfoType.CrossModuleAllMethods); - Header.Add(Internal.Runtime.ReadyToRunSectionType.CrossModuleInlineInfo, crossModuleInliningInfoTable, crossModuleInliningInfoTable); + Header.Add(Internal.Runtime.ReadyToRunSectionType.CrossModuleInlineInfo, crossModuleInliningInfoTable); this.CrossModuleInlningInfo = crossModuleInliningInfoTable; InstanceEntryPointTable = new InstanceEntryPointTableNode(this); - Header.Add(Internal.Runtime.ReadyToRunSectionType.InstanceMethodEntryPoints, InstanceEntryPointTable, InstanceEntryPointTable); + Header.Add(Internal.Runtime.ReadyToRunSectionType.InstanceMethodEntryPoints, InstanceEntryPointTable); ImportSectionsTable = new ImportSectionsTableNode(this); - Header.Add(Internal.Runtime.ReadyToRunSectionType.ImportSections, ImportSectionsTable, ImportSectionsTable); + Header.Add(Internal.Runtime.ReadyToRunSectionType.ImportSections, ImportSectionsTable); DebugInfoTable = new DebugInfoTableNode(); - Header.Add(Internal.Runtime.ReadyToRunSectionType.DebugInfo, DebugInfoTable, DebugInfoTable); + Header.Add(Internal.Runtime.ReadyToRunSectionType.DebugInfo, DebugInfoTable); EagerImports = new ImportSectionNode( "EagerImports", @@ -854,7 +854,7 @@ bool HasAnyProfileDataForInput() if (ProfileDataManager.SynthesizeRandomPgoData || HasAnyProfileDataForInput()) { InstrumentationDataTable = new InstrumentationDataTableNode(this, ProfileDataManager); - Header.Add(Internal.Runtime.ReadyToRunSectionType.PgoInstrumentationData, InstrumentationDataTable, InstrumentationDataTable); + Header.Add(Internal.Runtime.ReadyToRunSectionType.PgoInstrumentationData, InstrumentationDataTable); } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index 122682028f3353..f5cac979155365 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -531,7 +531,7 @@ private void RewriteComponentFile(string inputFile, string outputFile, string ow componentGraph.AddRoot(componentFactory.Win32ResourcesNode, "Win32 resources"); } componentGraph.ComputeMarkedNodes(); - componentFactory.Header.Add(Internal.Runtime.ReadyToRunSectionType.OwnerCompositeExecutable, ownerExecutableNode, ownerExecutableNode); + componentFactory.Header.Add(Internal.Runtime.ReadyToRunSectionType.OwnerCompositeExecutable, ownerExecutableNode); ReadyToRunObjectWriter.EmitObject( outputFile, componentModule: inputModule, diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index cb7b001082722b..b5c2250d6ba410 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -234,7 +234,7 @@ - + From 3f3b5ebdfcc3b44a84c70b41ca0555b30d8dc73f Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 4 Feb 2026 18:34:14 -0800 Subject: [PATCH 03/19] Move TypeMapManager to the shared partition --- .../Compiler}/ExternalTypeMapObjectNode.cs | 0 .../Compiler}/IExternalTypeMapNode.cs | 0 .../Compiler}/IProxyTypeMapNode.cs | 0 .../Compiler}/ProxyTypeMapObjectNode.cs | 0 .../Compiler/TypeMapManager.cs | 4 ++++ .../ILCompiler.Compiler/Compiler/TypeMapMetadata.cs | 5 ++++- .../ILCompiler.Compiler/ILCompiler.Compiler.csproj | 12 +++++++----- .../ILCompiler.ReadyToRun.csproj | 6 ++++++ 8 files changed, 21 insertions(+), 6 deletions(-) rename src/coreclr/tools/{aot/ILCompiler.Compiler/Compiler/DependencyAnalysis => Common/Compiler}/ExternalTypeMapObjectNode.cs (100%) rename src/coreclr/tools/{aot/ILCompiler.Compiler/Compiler/DependencyAnalysis => Common/Compiler}/IExternalTypeMapNode.cs (100%) rename src/coreclr/tools/{aot/ILCompiler.Compiler/Compiler/DependencyAnalysis => Common/Compiler}/IProxyTypeMapNode.cs (100%) rename src/coreclr/tools/{aot/ILCompiler.Compiler/Compiler/DependencyAnalysis => Common/Compiler}/ProxyTypeMapObjectNode.cs (100%) rename src/coreclr/tools/{aot/ILCompiler.Compiler => Common}/Compiler/TypeMapManager.cs (96%) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs b/src/coreclr/tools/Common/Compiler/ExternalTypeMapObjectNode.cs similarity index 100% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapObjectNode.cs rename to src/coreclr/tools/Common/Compiler/ExternalTypeMapObjectNode.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IExternalTypeMapNode.cs b/src/coreclr/tools/Common/Compiler/IExternalTypeMapNode.cs similarity index 100% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IExternalTypeMapNode.cs rename to src/coreclr/tools/Common/Compiler/IExternalTypeMapNode.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IProxyTypeMapNode.cs b/src/coreclr/tools/Common/Compiler/IProxyTypeMapNode.cs similarity index 100% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/IProxyTypeMapNode.cs rename to src/coreclr/tools/Common/Compiler/IProxyTypeMapNode.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs b/src/coreclr/tools/Common/Compiler/ProxyTypeMapObjectNode.cs similarity index 100% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapObjectNode.cs rename to src/coreclr/tools/Common/Compiler/ProxyTypeMapObjectNode.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs b/src/coreclr/tools/Common/Compiler/TypeMapManager.cs similarity index 96% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs rename to src/coreclr/tools/Common/Compiler/TypeMapManager.cs index 8bb70eb83f151a..8fa7d9e7b5daa8 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapManager.cs +++ b/src/coreclr/tools/Common/Compiler/TypeMapManager.cs @@ -8,8 +8,12 @@ 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 { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs index 88290a47d718cc..9bef013a3f96c8 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs @@ -11,7 +11,6 @@ using Internal.TypeSystem; using Internal.TypeSystem.Ecma; using static ILCompiler.TypeMapManager; -using static ILCompiler.UsageBasedTypeMapManager; namespace ILCompiler { @@ -36,7 +35,11 @@ public ThrowingMethodStub(TypeDesc owningType, TypeDesc typeMapGroup, bool exter public override ReadOnlySpan 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) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 8d138b97ce980e..90f7635ad3f5c8 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -83,6 +83,13 @@ TypeSystem\Common\VersionResilientHashCode.cs + + + + + + + Interop\IL\MarshalHelpers.cs @@ -442,12 +449,8 @@ - - - - @@ -686,7 +689,6 @@ - diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index b5c2250d6ba410..120d2af83ced80 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -41,6 +41,7 @@ + @@ -123,20 +124,25 @@ + + + + + From 9de4cadc521c56752a3bb778dd9d750c3dfede11 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 9 Feb 2026 16:44:38 -0800 Subject: [PATCH 04/19] Use SortableDependencyNode as the base type --- .../AnalyzedExternalTypeMapNode.cs | 12 ++++++------ .../DependencyAnalysis/AnalyzedProxyTypeMapNode.cs | 14 +++++++------- .../DependencyAnalysis/ExternalTypeMapNode.cs | 12 ++++++------ .../InvalidExternalTypeMapNode.cs | 12 ++++++------ .../DependencyAnalysis/InvalidProxyTypeMapNode.cs | 12 ++++++------ .../Compiler/DependencyAnalysis/NodeFactory.cs | 11 ++++++++++- .../DependencyAnalysis/ProxyTypeMapNode.cs | 14 +++++++------- 7 files changed, 48 insertions(+), 39 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedExternalTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedExternalTypeMapNode.cs index a0c397a6180afe..0d8535488348f3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedExternalTypeMapNode.cs @@ -10,24 +10,24 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class AnalyzedExternalTypeMapNode(TypeDesc typeMapGroup, IReadOnlyDictionary entries) : DependencyNodeCore, IExternalTypeMapNode + internal sealed class AnalyzedExternalTypeMapNode(TypeDesc typeMapGroup, IReadOnlyDictionary 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); } @@ -51,9 +51,9 @@ public override IEnumerable 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); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedProxyTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedProxyTypeMapNode.cs index 1689bc15716e59..416497cbf46bd2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/AnalyzedProxyTypeMapNode.cs @@ -11,23 +11,23 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class AnalyzedProxyTypeMapNode(TypeDesc typeMapGroup, IReadOnlyDictionary entries) : DependencyNodeCore, IProxyTypeMapNode + internal sealed class AnalyzedProxyTypeMapNode(TypeDesc typeMapGroup, IReadOnlyDictionary entries) : SortableDependencyNode, IProxyTypeMapNode { 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 ((TypeDesc key, TypeDesc type) in entries) { - Vertex keyVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MaximallyConstructableType(key))); - Vertex valueVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MetadataTypeSymbol(type))); + Vertex keyVertex = externalReferences.EncodeReferenceToType(writer, key); + Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, type); Vertex entry = writer.GetTuple(keyVertex, valueVertex); typeMapHashTable.Append((uint)key.GetHashCode(), 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); } @@ -52,9 +52,9 @@ public override IEnumerable GetStaticDependencies(NodeFacto public override bool StaticDependenciesAreComputed => true; - public int ClassCode => 171742984; + public override int ClassCode => 171742984; - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { AnalyzedProxyTypeMapNode otherEntry = (AnalyzedProxyTypeMapNode)other; return comparer.Compare(TypeMapGroup, otherEntry.TypeMapGroup); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs index 2d23d46991f93f..bc3baed8f8f440 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternalTypeMapNode.cs @@ -11,7 +11,7 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class ExternalTypeMapNode : DependencyNodeCore, IExternalTypeMapNode + internal sealed class ExternalTypeMapNode : SortableDependencyNode, IExternalTypeMapNode { private readonly IEnumerable> _mapEntries; @@ -63,9 +63,9 @@ public override IEnumerable GetStaticDependencies(NodeFacto public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => Array.Empty(); protected override string GetName(NodeFactory context) => $"External type map: {TypeMapGroup}"; - public int ClassCode => -785190502; + public override int ClassCode => -785190502; - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { ExternalTypeMapNode otherEntry = (ExternalTypeMapNode)other; return comparer.Compare(TypeMapGroup, otherEntry.TypeMapGroup); @@ -87,20 +87,20 @@ public int CompareToImpl(ISortableNode other, CompilerComparer comparer) } } - 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, IEETypeNode valueNode) in GetMarkedEntries(factory)) { Vertex keyVertex = writer.GetStringConstant(key); - Vertex valueVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(valueNode)); + Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, valueNode.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); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs index 4674e713ec7a38..657382714ac04c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidExternalTypeMapNode.cs @@ -10,7 +10,7 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class InvalidExternalTypeMapNode(TypeDesc typeMapGroup, MethodDesc throwingMethodStub) : DependencyNodeCore, ISortableNode, IExternalTypeMapNode + internal sealed class InvalidExternalTypeMapNode(TypeDesc typeMapGroup, MethodDesc throwingMethodStub) : SortableDependencyNode, IExternalTypeMapNode { public override bool InterestingForDynamicDependencyAnalysis => false; @@ -34,15 +34,15 @@ public override IEnumerable GetStaticDependencies(NodeFacto public TypeDesc TypeMapGroup { get; } = typeMapGroup; public MethodDesc ThrowingMethodStub { get; } = throwingMethodStub; - public int ClassCode => 36910224; + public override int ClassCode => 36910224; - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) => comparer.Compare(TypeMapGroup, ((InvalidExternalTypeMapNode)other).TypeMapGroup); + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) => comparer.Compare(TypeMapGroup, ((InvalidExternalTypeMapNode)other).TypeMapGroup); - public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences) + public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider externalReferences) { Vertex typeMapStateVertex = writer.GetUnsignedConstant(0); // Invalid type map state - Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(TypeMapGroup))); - Vertex throwingMethodStubVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MethodEntrypoint(ThrowingMethodStub))); + Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup); + Vertex throwingMethodStubVertex = externalReferences.EncodeReferenceToMethod(writer, ThrowingMethodStub); Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex); return section.Place(tuple); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs index a929fbb205ac8e..db0fb70e2a8cbe 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InvalidProxyTypeMapNode.cs @@ -10,7 +10,7 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class InvalidProxyTypeMapNode(TypeDesc typeMapGroup, MethodDesc throwingMethodStub) : DependencyNodeCore, IProxyTypeMapNode + internal sealed class InvalidProxyTypeMapNode(TypeDesc typeMapGroup, MethodDesc throwingMethodStub) : SortableDependencyNode, IProxyTypeMapNode { public TypeDesc TypeMapGroup { get; } = typeMapGroup; public MethodDesc ThrowingMethodStub { get; } = throwingMethodStub; @@ -34,14 +34,14 @@ public override IEnumerable GetStaticDependencies(NodeFacto public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => Array.Empty(); protected override string GetName(NodeFactory context) => $"Invalid proxy type map: {TypeMapGroup}"; - public int ClassCode => 36910224; + public override int ClassCode => 36910224; - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) => comparer.Compare(TypeMapGroup, ((InvalidProxyTypeMapNode)other).TypeMapGroup); - public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, ExternalReferencesTableNode externalReferences) + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) => comparer.Compare(TypeMapGroup, ((InvalidProxyTypeMapNode)other).TypeMapGroup); + public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider externalReferences) { Vertex typeMapStateVertex = writer.GetUnsignedConstant(0); // Invalid type map state - Vertex typeMapGroupVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.NecessaryTypeSymbol(TypeMapGroup))); - Vertex throwingMethodStubVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(factory.MethodEntrypoint(ThrowingMethodStub))); + Vertex typeMapGroupVertex = externalReferences.EncodeReferenceToType(writer, TypeMapGroup); + Vertex throwingMethodStubVertex = externalReferences.EncodeReferenceToMethod(writer, ThrowingMethodStub); Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, throwingMethodStubVertex); return section.Place(tuple); } 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 820cf40f5813be..e228370554ef06 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -10,6 +10,7 @@ using ILCompiler.DependencyAnalysisFramework; using Internal.IL; +using Internal.NativeFormat; using Internal.Runtime; using Internal.Text; using Internal.TypeSystem; @@ -1641,13 +1642,21 @@ public virtual void AttachToDependencyGraph(DependencyAnalyzerBase var commonFixupsTableNode = new ExternalReferencesTableNode("CommonFixupsTable", this); InteropStubManager.AddToReadyToRunHeader(ReadyToRunHeader, this, commonFixupsTableNode); - TypeMapManager.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); } + private sealed class ExternalReferencesTableIndex(ExternalReferencesTableNode table, NodeFactory factory) : INativeFormatTypeReferenceProvider + { + public Vertex EncodeReferenceToMethod(NativeWriter writer, MethodDesc method) + => writer.GetUnsignedConstant(table.GetIndex(factory.MethodEntrypoint(method))); + public Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type) + => writer.GetUnsignedConstant(table.GetIndex(factory.NecessaryTypeSymbol(type))); + } + protected struct MethodKey : IEquatable { public readonly MethodDesc Method; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs index 0053d0b88a3664..ebcadfb3ee9b34 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ProxyTypeMapNode.cs @@ -13,7 +13,7 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class ProxyTypeMapNode : DependencyNodeCore, IProxyTypeMapNode + internal sealed class ProxyTypeMapNode : SortableDependencyNode, IProxyTypeMapNode { private readonly IEnumerable> _mapEntries; @@ -34,9 +34,9 @@ public ProxyTypeMapNode(TypeDesc typeMapGroup, IEnumerable true; - public int ClassCode => 779513676; + public override int ClassCode => 779513676; - public int CompareToImpl(ISortableNode other, CompilerComparer comparer) => comparer.Compare(TypeMapGroup, ((ProxyTypeMapNode)other).TypeMapGroup); + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) => comparer.Compare(TypeMapGroup, ((ProxyTypeMapNode)other).TypeMapGroup); public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) { @@ -78,20 +78,20 @@ public override IEnumerable GetConditionalStaticDep } } - 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 VertexHashtable(); foreach ((IEETypeNode keyNode, IEETypeNode valueNode) in GetMarkedEntries(factory)) { - Vertex keyVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(keyNode)); - Vertex valueVertex = writer.GetUnsignedConstant(externalReferences.GetIndex(valueNode)); + Vertex keyVertex = externalReferences.EncodeReferenceToType(writer, keyNode.Type); + Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, valueNode.Type); Vertex entry = writer.GetTuple(keyVertex, valueVertex); typeMapHashTable.Append((uint)keyNode.Type.GetHashCode(), 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); } From 89824c885975d96819bc1b117e6455cd4ccf29c8 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 9 Feb 2026 22:51:17 -0800 Subject: [PATCH 05/19] Hook up support for emitting type map entries in R2R --- .../Compiler/ExternalTypeMapObjectNode.cs | 6 +- .../Common/Compiler/IExternalTypeMapNode.cs | 7 +- .../INativeFormatTypeReferenceProvider.cs | 18 +++ .../Common/Compiler/IProxyTypeMapNode.cs | 9 +- .../Common/Compiler/ProxyTypeMapObjectNode.cs | 6 +- .../tools/Common/Compiler/TypeMapManager.cs | 29 +---- .../Compiler/TypeMapMetadata.cs | 66 ++++++---- .../Compiler/UsageBasedTypeMapManager.cs | 19 ++- .../ILCompiler.Compiler.csproj | 3 +- .../ImportReferenceProvider.cs | 33 +++++ .../ReadyToRunCodegenNodeFactory.cs | 9 ++ .../ReadyToRunSymbolNodeFactory.cs | 122 +++++++++--------- .../Compiler/ReadyToRunCodegenCompilation.cs | 2 + .../Compiler/ReadyToRunExternalTypeMapNode.cs | 82 ++++++++++++ .../Compiler/ReadyToRunProxyTypeMapNode.cs | 85 ++++++++++++ .../Compiler/ReadyToRunTypeMapManager.cs | 61 +++++++++ .../ILCompiler.ReadyToRun.csproj | 6 + 17 files changed, 438 insertions(+), 125 deletions(-) create mode 100644 src/coreclr/tools/Common/Compiler/INativeFormatTypeReferenceProvider.cs rename src/coreclr/tools/{aot/ILCompiler.Compiler => Common}/Compiler/TypeMapMetadata.cs (92%) create mode 100644 src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProxyTypeMapNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTypeMapManager.cs diff --git a/src/coreclr/tools/Common/Compiler/ExternalTypeMapObjectNode.cs b/src/coreclr/tools/Common/Compiler/ExternalTypeMapObjectNode.cs index 8f72dee452374f..bf5113d93be6a8 100644 --- a/src/coreclr/tools/Common/Compiler/ExternalTypeMapObjectNode.cs +++ b/src/coreclr/tools/Common/Compiler/ExternalTypeMapObjectNode.cs @@ -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, INodeWithSize { public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { @@ -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)); } @@ -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; diff --git a/src/coreclr/tools/Common/Compiler/IExternalTypeMapNode.cs b/src/coreclr/tools/Common/Compiler/IExternalTypeMapNode.cs index 32e059da731210..e4e571f4da091e 100644 --- a/src/coreclr/tools/Common/Compiler/IExternalTypeMapNode.cs +++ b/src/coreclr/tools/Common/Compiler/IExternalTypeMapNode.cs @@ -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 } } diff --git a/src/coreclr/tools/Common/Compiler/INativeFormatTypeReferenceProvider.cs b/src/coreclr/tools/Common/Compiler/INativeFormatTypeReferenceProvider.cs new file mode 100644 index 00000000000000..99a95a4fec8770 --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/INativeFormatTypeReferenceProvider.cs @@ -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); + } +} diff --git a/src/coreclr/tools/Common/Compiler/IProxyTypeMapNode.cs b/src/coreclr/tools/Common/Compiler/IProxyTypeMapNode.cs index 331ec8357b6f71..13961085edd497 100644 --- a/src/coreclr/tools/Common/Compiler/IProxyTypeMapNode.cs +++ b/src/coreclr/tools/Common/Compiler/IProxyTypeMapNode.cs @@ -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 } } diff --git a/src/coreclr/tools/Common/Compiler/ProxyTypeMapObjectNode.cs b/src/coreclr/tools/Common/Compiler/ProxyTypeMapObjectNode.cs index cdca8804ba5a80..eb83780a32b5c3 100644 --- a/src/coreclr/tools/Common/Compiler/ProxyTypeMapObjectNode.cs +++ b/src/coreclr/tools/Common/Compiler/ProxyTypeMapObjectNode.cs @@ -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, INodeWithSize { public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { @@ -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)); @@ -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; diff --git a/src/coreclr/tools/Common/Compiler/TypeMapManager.cs b/src/coreclr/tools/Common/Compiler/TypeMapManager.cs index 8fa7d9e7b5daa8..e6469dad59d471 100644 --- a/src/coreclr/tools/Common/Compiler/TypeMapManager.cs +++ b/src/coreclr/tools/Common/Compiler/TypeMapManager.cs @@ -23,29 +23,6 @@ namespace ILCompiler /// public abstract class TypeMapManager : ICompilationRootProvider { - public enum TypeMapAttributeKind - { - None, - TypeMapAssemblyTarget, - TypeMap, - TypeMapAssociation - } - - public 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; - } - public virtual void AttachToDependencyGraph(DependencyAnalyzerBase graph) { } @@ -58,15 +35,15 @@ public virtual void AttachToDependencyGraph(DependencyAnalyzerBase protected abstract bool IsEmpty { get; } - public void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ExternalReferencesTableNode commonFixupsTableNode) + public virtual void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, INativeFormatTypeReferenceProvider commonFixupsTableNode) { if (IsEmpty) { return; // No type maps to emit } - header.Add(ReadyToRunSectionType.ExternalTypeMaps, new ExternalTypeMapObjectNode(commonFixupsTableNode)); - header.Add(ReadyToRunSectionType.ProxyTypeMaps, new ProxyTypeMapObjectNode(commonFixupsTableNode)); + header.Add(ReadyToRunSectionType.ExternalTypeMaps, new ExternalTypeMapObjectNode(this, commonFixupsTableNode)); + header.Add(ReadyToRunSectionType.ProxyTypeMaps, new ProxyTypeMapObjectNode(this, commonFixupsTableNode)); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs b/src/coreclr/tools/Common/Compiler/TypeMapMetadata.cs similarity index 92% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs rename to src/coreclr/tools/Common/Compiler/TypeMapMetadata.cs index 9bef013a3f96c8..fa7945bc78d459 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypeMapMetadata.cs +++ b/src/coreclr/tools/Common/Compiler/TypeMapMetadata.cs @@ -10,13 +10,47 @@ using Internal.IL.Stubs; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; -using static ILCompiler.TypeMapManager; 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 TypeMap { get; } + MethodDesc ThrowingMethodStub { get; } + } + + internal interface IProxyTypeMap + { + IReadOnlyDictionary TypeMap { get; } + MethodDesc ThrowingMethodStub { get; } + } + + internal sealed class Map : IExternalTypeMap, IProxyTypeMap { private sealed class ThrowingMethodStub : ILStubMethod { @@ -159,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); @@ -186,6 +202,14 @@ public void AddTargetModule(ModuleDesc targetModule) /// 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. /// public IEnumerable TargetModules => _targetModules; + + IReadOnlyDictionary IExternalTypeMap.TypeMap => _externalTypeMap; + + MethodDesc IExternalTypeMap.ThrowingMethodStub => _externalTypeMapExceptionStub; + + IReadOnlyDictionary IProxyTypeMap.TypeMap => _associatedTypeMap; + + MethodDesc IProxyTypeMap.ThrowingMethodStub => _associatedTypeMapExceptionStub; } public static readonly TypeMapMetadata Empty = new TypeMapMetadata(new Dictionary(), "No type maps"); @@ -198,8 +222,6 @@ private TypeMapMetadata(IReadOnlyDictionary states, string diagno DiagnosticName = diagnosticName; } - internal Map this[TypeDesc typeMapGroup] => _states[typeMapGroup]; - public bool IsEmpty => _states.Count == 0; internal IEnumerable> Maps => _states; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs index 54be1c847d7e6f..cdc4dd3f8b8494 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedTypeMapManager.cs @@ -10,6 +10,7 @@ using Internal.IL.Stubs; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; +using static ILCompiler.TypeMapMetadata; namespace ILCompiler { @@ -30,8 +31,8 @@ public override IEnumerable GetConditionalStaticDep List entries = []; foreach ((TypeDesc typeMapGroup, TypeMapMetadata.Map typeMap) in typeMapState.Maps) { - entries.Add(new CombinedDependencyListEntry(typeMap.GetExternalTypeMapNode(), context.ExternalTypeMapRequest(typeMapGroup), "ExternalTypeMap")); - entries.Add(new CombinedDependencyListEntry(typeMap.GetProxyTypeMapNode(), context.ProxyTypeMapRequest(typeMapGroup), "ProxyTypeMap")); + entries.Add(new CombinedDependencyListEntry(GetExternalTypeMapNode(typeMapGroup, typeMap), context.ExternalTypeMapRequest(typeMapGroup), "ExternalTypeMap")); + entries.Add(new CombinedDependencyListEntry(GetProxyTypeMapNode(typeMapGroup, typeMap), context.ProxyTypeMapRequest(typeMapGroup), "ProxyTypeMap")); } return entries; @@ -40,6 +41,20 @@ public override IEnumerable GetConditionalStaticDep public override IEnumerable GetStaticDependencies(NodeFactory context) => Array.Empty(); public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => Array.Empty(); protected override string GetName(NodeFactory context) => $"Type maps root node: {typeMapState.DiagnosticName}"; + + private static IExternalTypeMapNode GetExternalTypeMapNode(TypeDesc typeMapGroup, IExternalTypeMap map) + { + return map.ThrowingMethodStub is not null + ? new InvalidExternalTypeMapNode(typeMapGroup, map.ThrowingMethodStub) + : new ExternalTypeMapNode(typeMapGroup, map.TypeMap); + } + + private static IProxyTypeMapNode GetProxyTypeMapNode(TypeDesc typeMapGroup, IProxyTypeMap map) + { + return map.ThrowingMethodStub is not null + ? new InvalidProxyTypeMapNode(typeMapGroup, map.ThrowingMethodStub) + : new ProxyTypeMapNode(typeMapGroup, map.TypeMap); + } } private readonly HashSet _requestedExternalTypeMaps = []; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 90f7635ad3f5c8..b5c28d50c941cb 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -85,6 +85,8 @@ + + @@ -666,7 +668,6 @@ - diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs new file mode 100644 index 00000000000000..2e1f2640f75e90 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs @@ -0,0 +1,33 @@ +// 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 ILCompiler.DependencyAnalysis.ReadyToRun; +using Internal.TypeSystem; +using Internal.NativeFormat; + +namespace ILCompiler.DependencyAnalysis +{ + public sealed class ImportReferenceProvider : INativeFormatTypeReferenceProvider + { + private ReadyToRunSymbolNodeFactory _symbolNodeFactory; + + public Import GetImportToType(TypeDesc type) + { + return _symbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeHandle, type); + } + + public void Initialize(ReadyToRunSymbolNodeFactory symbolNodeFactory) + { + _symbolNodeFactory = symbolNodeFactory; + } + + Vertex INativeFormatTypeReferenceProvider.EncodeReferenceToMethod(NativeWriter writer, MethodDesc method) => throw new NotImplementedException(); + Vertex INativeFormatTypeReferenceProvider.EncodeReferenceToType(NativeWriter writer, TypeDesc type) + { + Import typeImport = GetImportToType(type); + return writer.GetTuple(writer.GetUnsignedConstant((uint)typeImport.Table.IndexFromBeginningOfArray), writer.GetUnsignedConstant((uint)typeImport.IndexFromBeginningOfArray)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index ab63001517fe84..126741df47fcd6 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -20,6 +20,7 @@ using Internal.CorConstants; using Internal.ReadyToRunConstants; using ILCompiler.ReadyToRun.TypeSystem; +using ILCompiler.ReadyToRun; namespace ILCompiler.DependencyAnalysis { @@ -396,6 +397,7 @@ private void CreateNodeCaches() public InstrumentationDataTableNode InstrumentationDataTable; public InliningInfoNode CrossModuleInlningInfo; + public ImportReferenceProvider FixupCellProvider; public Import ModuleImport; @@ -786,6 +788,13 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph, I var node = new MethodIsGenericMapNode(inputModule); tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.MethodIsGenericMap, node); } + FixupCellProvider ??= new ImportReferenceProvider(); + + TypeMapMetadata metadata = TypeMapMetadata.CreateFromAssembly((EcmaAssembly)inputModule.Assembly, TypeSystemContext, TypeSystemContext.SystemModule, TypeMapAssemblyTargetsMode.Record); + + ReadyToRunTypeMapManager typeMapManager = new(inputModule, metadata, FixupCellProvider); + typeMapManager.AttachToDependencyGraph(graph); + typeMapManager.AddToReadyToRunHeader(tableHeader, this, FixupCellProvider); } InliningInfoNode crossModuleInliningInfoTable = new InliningInfoNode(null, diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs index 981ae21d19507e..769ab6a95c8d56 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs @@ -51,21 +51,21 @@ public ReadyToRunSymbolNodeFactory(NodeFactory codegenNodeFactory, bool verifyTy private void CreateNodeCaches() { - _importStrings = new NodeCache(key => + _importStrings = new NodeCache(key => { return new StringImport(_codegenNodeFactory.StringImports, key); }); - _r2rHelpers = new NodeCache(CreateReadyToRunHelper); + _r2rHelpers = new NodeCache(CreateReadyToRunHelper); - _instructionSetSupportFixups = new NodeCache(key => + _instructionSetSupportFixups = new NodeCache(key => { return new PrecodeHelperImport( _codegenNodeFactory, new ReadyToRunInstructionSetSupportSignature(key)); }); - _fieldAddressCache = new NodeCache(key => + _fieldAddressCache = new NodeCache(key => { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -75,7 +75,7 @@ private void CreateNodeCaches() ); }); - _fieldOffsetCache = new NodeCache(key => + _fieldOffsetCache = new NodeCache(key => { return new PrecodeHelperImport( _codegenNodeFactory, @@ -83,7 +83,7 @@ private void CreateNodeCaches() ); }); - _fieldBaseOffsetCache = new NodeCache(key => + _fieldBaseOffsetCache = new NodeCache(key => { return new PrecodeHelperImport( _codegenNodeFactory, @@ -91,7 +91,7 @@ private void CreateNodeCaches() ); }); - _rvaFieldAddressCache = new NodeCache(key => + _rvaFieldAddressCache = new NodeCache(key => { return new PrecodeHelperImport( _codegenNodeFactory, @@ -99,7 +99,7 @@ private void CreateNodeCaches() ); }); - _checkFieldOffsetCache = new NodeCache(key => + _checkFieldOffsetCache = new NodeCache(key => { return new PrecodeHelperImport( _codegenNodeFactory, @@ -107,7 +107,7 @@ private void CreateNodeCaches() ); }); - _interfaceDispatchCells = new NodeCache(cellKey => + _interfaceDispatchCells = new NodeCache(cellKey => { return new DelayLoadHelperMethodImport( _codegenNodeFactory, @@ -122,7 +122,7 @@ private void CreateNodeCaches() cellKey.CallingMethod); }); - _delegateCtors = new NodeCache(ctorKey => + _delegateCtors = new NodeCache(ctorKey => { IMethodNode targetMethodNode = _codegenNodeFactory.MethodEntrypoint( ctorKey.Method, @@ -137,7 +137,7 @@ private void CreateNodeCaches() new DelegateCtorSignature(ctorKey.Type, targetMethodNode, ctorKey.Method)); }); - _checkTypeLayoutCache = new NodeCache(key => + _checkTypeLayoutCache = new NodeCache(key => { return new PrecodeHelperImport( _codegenNodeFactory, @@ -145,14 +145,14 @@ private void CreateNodeCaches() ); }); - _virtualFunctionOverrideCache = new NodeCache(key => + _virtualFunctionOverrideCache = new NodeCache(key => { return new PrecodeHelperImport(_codegenNodeFactory, key); }); _ilBodyFixupsCache = new NodeCache(key => new PrecodeHelperImport(_codegenNodeFactory.ILBodyPrecodeImports, key)); - _genericLookupHelpers = new NodeCache(key => + _genericLookupHelpers = new NodeCache(key => { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -167,7 +167,7 @@ private void CreateNodeCaches() key.MethodContext)); }); - _pInvokeTargetNodes = new NodeCache(key => + _pInvokeTargetNodes = new NodeCache(key => { return new PrecodeHelperImport( _codegenNodeFactory, @@ -193,9 +193,9 @@ public ISymbolNode ContinuationTypeSymbol(AsyncContinuationType key) return _continuationTypeFixups.GetOrAdd(key); } - private NodeCache _importStrings; + private NodeCache _importStrings; - public ISymbolNode StringLiteral(ModuleToken moduleToken) + public Import StringLiteral(ModuleToken moduleToken) { return _importStrings.GetOrAdd(moduleToken); } @@ -227,9 +227,9 @@ public override int GetHashCode() } } - private NodeCache _r2rHelpers; + private NodeCache _r2rHelpers; - private ISymbolNode CreateReadyToRunHelper(ReadyToRunHelperKey key) + private Import CreateReadyToRunHelper(ReadyToRunHelperKey key) { switch (key.Id) { @@ -280,20 +280,20 @@ private ISymbolNode CreateReadyToRunHelper(ReadyToRunHelperKey key) } } - public ISymbolNode CreateReadyToRunHelper(ReadyToRunHelperId id, object target) + public Import CreateReadyToRunHelper(ReadyToRunHelperId id, object target) { return _r2rHelpers.GetOrAdd(new ReadyToRunHelperKey(id, target)); } - private NodeCache _instructionSetSupportFixups; + private NodeCache _instructionSetSupportFixups; - public ISymbolNode PerMethodInstructionSetSupportFixup(InstructionSetSupport instructionSetSupport) + public Import PerMethodInstructionSetSupportFixup(InstructionSetSupport instructionSetSupport) { string key = ReadyToRunInstructionSetSupportSignature.ToInstructionSetSupportString(instructionSetSupport); return _instructionSetSupportFixups.GetOrAdd(key); } - private ISymbolNode CreateNewHelper(TypeDesc type) + private Import CreateNewHelper(TypeDesc type) { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -302,7 +302,7 @@ private ISymbolNode CreateNewHelper(TypeDesc type) new NewObjectFixupSignature(type)); } - private ISymbolNode CreateNewArrayHelper(ArrayType type) + private Import CreateNewArrayHelper(ArrayType type) { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -311,7 +311,7 @@ private ISymbolNode CreateNewArrayHelper(ArrayType type) new NewArrayFixupSignature(type)); } - private ISymbolNode CreateGCStaticBaseHelper(TypeDesc type) + private Import CreateGCStaticBaseHelper(TypeDesc type) { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -320,7 +320,7 @@ private ISymbolNode CreateGCStaticBaseHelper(TypeDesc type) _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.StaticBaseGC, type)); } - private ISymbolNode CreateNonGCStaticBaseHelper(TypeDesc type) + private Import CreateNonGCStaticBaseHelper(TypeDesc type) { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -329,7 +329,7 @@ private ISymbolNode CreateNonGCStaticBaseHelper(TypeDesc type) _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.StaticBaseNonGC, type)); } - private ISymbolNode CreateThreadGcStaticBaseHelper(TypeDesc type) + private Import CreateThreadGcStaticBaseHelper(TypeDesc type) { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -338,7 +338,7 @@ private ISymbolNode CreateThreadGcStaticBaseHelper(TypeDesc type) _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.ThreadStaticBaseGC, type)); } - private ISymbolNode CreateThreadNonGcStaticBaseHelper(TypeDesc type) + private Import CreateThreadNonGcStaticBaseHelper(TypeDesc type) { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -347,7 +347,7 @@ private ISymbolNode CreateThreadNonGcStaticBaseHelper(TypeDesc type) _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.ThreadStaticBaseNonGC, type)); } - private ISymbolNode CreateIsInstanceOfHelper(TypeDesc type) + private Import CreateIsInstanceOfHelper(TypeDesc type) { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -356,7 +356,7 @@ private ISymbolNode CreateIsInstanceOfHelper(TypeDesc type) _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.IsInstanceOf, type)); } - private ISymbolNode CreateCastClassHelper(TypeDesc type) + private Import CreateCastClassHelper(TypeDesc type) { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -365,14 +365,14 @@ private ISymbolNode CreateCastClassHelper(TypeDesc type) _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.ChkCast, type)); } - private ISymbolNode CreateTypeHandleHelper(TypeDesc type) + private Import CreateTypeHandleHelper(TypeDesc type) { return new PrecodeHelperImport( _codegenNodeFactory, _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.TypeHandle, type)); } - private ISymbolNode CreateMethodHandleHelper(MethodWithToken method) + private Import CreateMethodHandleHelper(MethodWithToken method) { bool useInstantiatingStub = method.Method.GetCanonMethodTarget(CanonicalFormKind.Specific) != method.Method; @@ -384,14 +384,14 @@ private ISymbolNode CreateMethodHandleHelper(MethodWithToken method) isInstantiatingStub: useInstantiatingStub)); } - private ISymbolNode CreateFieldHandleHelper(FieldWithToken field) + private Import CreateFieldHandleHelper(FieldWithToken field) { return new PrecodeHelperImport( _codegenNodeFactory, new FieldFixupSignature(ReadyToRunFixupKind.FieldHandle, field, _codegenNodeFactory)); } - private ISymbolNode CreateCctorTrigger(TypeDesc type) + private Import CreateCctorTrigger(TypeDesc type) { return new DelayLoadHelperImport( _codegenNodeFactory, @@ -400,7 +400,7 @@ private ISymbolNode CreateCctorTrigger(TypeDesc type) _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.CctorTrigger, type)); } - private ISymbolNode CreateTypeDictionary(TypeDesc type) + private Import CreateTypeDictionary(TypeDesc type) { return new PrecodeHelperImport( _codegenNodeFactory, @@ -408,7 +408,7 @@ private ISymbolNode CreateTypeDictionary(TypeDesc type) ); } - private ISymbolNode CreateMethodDictionary(MethodWithToken method) + private Import CreateMethodDictionary(MethodWithToken method) { return new PrecodeHelperImport( _codegenNodeFactory, @@ -418,52 +418,52 @@ private ISymbolNode CreateMethodDictionary(MethodWithToken method) isInstantiatingStub: true)); } - private NodeCache _fieldAddressCache; + private NodeCache _fieldAddressCache; - public ISymbolNode FieldAddress(FieldWithToken fieldWithToken) + public Import FieldAddress(FieldWithToken fieldWithToken) { return _fieldAddressCache.GetOrAdd(fieldWithToken); } - private NodeCache _fieldOffsetCache; + private NodeCache _fieldOffsetCache; - public ISymbolNode FieldOffset(FieldWithToken fieldWithToken) + public Import FieldOffset(FieldWithToken fieldWithToken) { return _fieldOffsetCache.GetOrAdd(fieldWithToken); } - private NodeCache _checkFieldOffsetCache; + private NodeCache _checkFieldOffsetCache; - public ISymbolNode CheckFieldOffset(FieldWithToken fieldWithToken) + public Import CheckFieldOffset(FieldWithToken fieldWithToken) { return _checkFieldOffsetCache.GetOrAdd(fieldWithToken); } - private NodeCache _fieldBaseOffsetCache; + private NodeCache _fieldBaseOffsetCache; - public ISymbolNode FieldBaseOffset(TypeDesc typeDesc) + public Import FieldBaseOffset(TypeDesc typeDesc) { return _fieldBaseOffsetCache.GetOrAdd(typeDesc); } - private NodeCache _rvaFieldAddressCache; + private NodeCache _rvaFieldAddressCache; - public ISymbolNode RvaFieldAddress(FieldWithToken fieldWithToken) + public Import RvaFieldAddress(FieldWithToken fieldWithToken) { return _rvaFieldAddressCache.GetOrAdd(fieldWithToken); } - private NodeCache _interfaceDispatchCells = new NodeCache(); + private NodeCache _interfaceDispatchCells = new NodeCache(); - public ISymbolNode InterfaceDispatchCell(MethodWithToken method, MethodDesc callingMethod) + public Import InterfaceDispatchCell(MethodWithToken method, MethodDesc callingMethod) { MethodAndCallSite cellKey = new MethodAndCallSite(method, null); return _interfaceDispatchCells.GetOrAdd(cellKey); } - private NodeCache _delegateCtors = new NodeCache(); + private NodeCache _delegateCtors = new NodeCache(); - public ISymbolNode DelegateCtor(TypeDesc delegateType, MethodWithToken method) + public Import DelegateCtor(TypeDesc delegateType, MethodWithToken method) { TypeAndMethod ctorKey = new TypeAndMethod( delegateType, @@ -474,16 +474,16 @@ public ISymbolNode DelegateCtor(TypeDesc delegateType, MethodWithToken method) return _delegateCtors.GetOrAdd(ctorKey); } - private NodeCache _checkTypeLayoutCache; + private NodeCache _checkTypeLayoutCache; - public ISymbolNode CheckTypeLayout(TypeDesc type) + public Import CheckTypeLayout(TypeDesc type) { return _checkTypeLayoutCache.GetOrAdd(type); } - private NodeCache _virtualFunctionOverrideCache; + private NodeCache _virtualFunctionOverrideCache; - public ISymbolNode CheckVirtualFunctionOverride(MethodWithToken declMethod, TypeDesc implType, MethodWithToken implMethod) + public Import CheckVirtualFunctionOverride(MethodWithToken declMethod, TypeDesc implType, MethodWithToken implMethod) { return _virtualFunctionOverrideCache.GetOrAdd(_codegenNodeFactory.VirtualResolutionFixupSignature( _verifyTypeAndFieldLayout ? ReadyToRunFixupKind.Verify_VirtualFunctionOverride : ReadyToRunFixupKind.Check_VirtualFunctionOverride, @@ -577,9 +577,9 @@ public override int GetHashCode() } } - private NodeCache _genericLookupHelpers; + private NodeCache _genericLookupHelpers; - public ISymbolNode GenericLookupHelper( + public Import GenericLookupHelper( CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind, ReadyToRunHelperId helperId, object helperArgument, @@ -641,7 +641,7 @@ public ISymbolNode GenericLookupHelper( } } - private ISymbolNode GenericLookupTypeHelper( + private Import GenericLookupTypeHelper( CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind, ReadyToRunFixupKind fixupKind, object helperArgument, @@ -665,7 +665,7 @@ private ISymbolNode GenericLookupTypeHelper( return _genericLookupHelpers.GetOrAdd(key); } - private ISymbolNode GenericLookupFieldHelper( + private Import GenericLookupFieldHelper( CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind, ReadyToRunFixupKind fixupKind, FieldWithToken fieldArgument, @@ -675,7 +675,7 @@ private ISymbolNode GenericLookupFieldHelper( return _genericLookupHelpers.GetOrAdd(key); } - private ISymbolNode GenericLookupMethodHelper( + private Import GenericLookupMethodHelper( CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind, ReadyToRunFixupKind fixupKind, MethodWithToken methodArgument, @@ -712,14 +712,14 @@ public override int GetHashCode() } } - private NodeCache _pInvokeTargetNodes = new NodeCache(); + private NodeCache _pInvokeTargetNodes = new NodeCache(); - public ISymbolNode GetIndirectPInvokeTargetNode(MethodWithToken methodWithToken) + public Import GetIndirectPInvokeTargetNode(MethodWithToken methodWithToken) { return _pInvokeTargetNodes.GetOrAdd(new PInvokeTargetKey(methodWithToken, isIndirect: true)); } - public ISymbolNode GetPInvokeTargetNode(MethodWithToken methodWithToken) + public Import GetPInvokeTargetNode(MethodWithToken methodWithToken) { return _pInvokeTargetNodes.GetOrAdd(new PInvokeTargetKey(methodWithToken, isIndirect: false)); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index f5cac979155365..2935d6628a50d0 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -377,6 +377,8 @@ internal ReadyToRunCodegenCompilation( nodeFactory.InstrumentationDataTable.Initialize(SymbolNodeFactory); if (nodeFactory.CrossModuleInlningInfo != null) nodeFactory.CrossModuleInlningInfo.Initialize(SymbolNodeFactory); + if (nodeFactory.FixupCellProvider != null) + nodeFactory.FixupCellProvider.Initialize(SymbolNodeFactory); _inputFiles = inputFiles; _compositeRootPath = compositeRootPath; _printReproInstructions = printReproInstructions; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs new file mode 100644 index 00000000000000..2916ed538b9bb3 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; +using Internal.NativeFormat; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.ReadyToRun +{ + internal class ReadyToRunExternalTypeMapNode(ModuleDesc triggeringModule, TypeDesc group, TypeMapMetadata.IExternalTypeMap map, ImportReferenceProvider importProvider) : SortableDependencyNode, IExternalTypeMapNode + { + public TypeDesc TypeMapGroup => group; + + public override int ClassCode => 565222977; + + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => false; + + public override bool StaticDependenciesAreComputed => true; + + private ModuleDesc TriggeringModule { get; } = triggeringModule; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + ReadyToRunExternalTypeMapNode otherNode = (ReadyToRunExternalTypeMapNode)other; + int result = comparer.Compare(TypeMapGroup, otherNode.TypeMapGroup); + if (result != 0) + return result; + + return comparer.Compare(TriggeringModule, otherNode.TriggeringModule); + } + + public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider externalReferences) + { + if (map.ThrowingMethodStub is not null) + { + // We don't write out the throwing method stub for R2R + // as emitting loose methods is not supported/very expensive. + // Instead, we defer to the runtime to generate the type map + // and throw on error cases. + return section.Place(writer.GetUnsignedConstant(0)); // Invalid type map state + } + + VertexHashtable typeMapHashTable = new(); + + foreach ((string key, (TypeDesc type, _)) in map.TypeMap) + { + Vertex keyVertex = writer.GetStringConstant(key); + 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 = externalReferences.EncodeReferenceToType(writer, TypeMapGroup); + Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); + return section.Place(tuple); + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => []; + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + if (map.ThrowingMethodStub is not null) + { + yield break; + } + + foreach (var entry in map.TypeMap) + { + yield return new DependencyListEntry(importProvider.GetImportToType(entry.Value.type), $"External type map entry target for key '{entry.Key}'"); + } + } + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; + protected override string GetName(NodeFactory context) => $"ExternalTypeMap {TypeMapGroup} entries in assembly {TriggeringModule.GetDisplayName()}"; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProxyTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProxyTypeMapNode.cs new file mode 100644 index 00000000000000..58e6adb4c68844 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProxyTypeMapNode.cs @@ -0,0 +1,85 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; +using Internal.NativeFormat; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.ReadyToRun +{ + internal class ReadyToRunProxyTypeMapNode(ModuleDesc triggeringModule, TypeDesc group, TypeMapMetadata.IProxyTypeMap map, ImportReferenceProvider importProvider) : SortableDependencyNode, IProxyTypeMapNode + { + public TypeDesc TypeMapGroup => group; + + public override int ClassCode => 210131165; + + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => false; + + public override bool StaticDependenciesAreComputed => true; + + private ModuleDesc TriggeringModule { get; } = triggeringModule; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + ReadyToRunProxyTypeMapNode otherNode = (ReadyToRunProxyTypeMapNode)other; + int result = comparer.Compare(TypeMapGroup, otherNode.TypeMapGroup); + if (result != 0) + return result; + + return comparer.Compare(TriggeringModule, otherNode.TriggeringModule); + } + + public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section section, INativeFormatTypeReferenceProvider ProxyReferences) + { + if (map.ThrowingMethodStub is not null) + { + // We don't write out the throwing method stub for R2R + // as emitting loose methods is not supported/very expensive. + // Also, matching CoreCLR's exact set of exceptions is difficult + // in the managed type system. + // Instead, we defer to the runtime to generate the type map + // and throw on error cases. + return section.Place(writer.GetUnsignedConstant(0)); // Invalid type map state + } + + VertexHashtable typeMapHashTable = new(); + + foreach ((TypeDesc type, TypeDesc targetType) in map.TypeMap) + { + Vertex keyVertex = ProxyReferences.EncodeReferenceToType(writer, type); + Vertex valueVertex = ProxyReferences.EncodeReferenceToType(writer, targetType); + Vertex entry = writer.GetTuple(keyVertex, valueVertex); + typeMapHashTable.Append((uint)type.GetHashCode(), section.Place(entry)); + } + + Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state + Vertex typeMapGroupVertex = ProxyReferences.EncodeReferenceToType(writer, TypeMapGroup); + Vertex tuple = writer.GetTuple(typeMapGroupVertex, typeMapStateVertex, typeMapHashTable); + return section.Place(tuple); + } + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => []; + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + if (map.ThrowingMethodStub is not null) + { + yield break; + } + + foreach (var entry in map.TypeMap) + { + yield return new DependencyListEntry(importProvider.GetImportToType(entry.Key), $"Key type of Proxy type map entry"); + yield return new DependencyListEntry(importProvider.GetImportToType(entry.Value), $"Proxy type map entry target for key '{entry.Key}'"); + } + } + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => []; + protected override string GetName(NodeFactory context) => $"ProxyTypeMap {TypeMapGroup} entries in assembly {TriggeringModule.GetDisplayName()}"; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTypeMapManager.cs new file mode 100644 index 00000000000000..ff2edf78a153ef --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTypeMapManager.cs @@ -0,0 +1,61 @@ +// 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.DependencyAnalysis; +using ILCompiler.DependencyAnalysis.ReadyToRun; +using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; + +namespace ILCompiler.ReadyToRun +{ + public sealed class ReadyToRunTypeMapManager(ModuleDesc triggeringModule, TypeMapMetadata assemblyTypeMaps, ImportReferenceProvider importProvider) : TypeMapManager + { + public override void AttachToDependencyGraph(DependencyAnalyzerBase graph) + { + base.AttachToDependencyGraph(graph); + foreach (var map in GetExternalTypeMaps()) + { + graph.AddRoot(map, "External type map"); + } + foreach (var map in GetProxyTypeMaps()) + { + graph.AddRoot(map, "Proxy type map"); + } + } + + protected override bool IsEmpty => assemblyTypeMaps.IsEmpty; + + public override void AddCompilationRoots(IRootingServiceProvider rootProvider) + { + } + + internal override IEnumerable GetExternalTypeMaps() + { + foreach (var map in assemblyTypeMaps.Maps) + { + yield return new ReadyToRunExternalTypeMapNode(triggeringModule, map.Key, map.Value, importProvider); + } + } + + internal override IEnumerable GetProxyTypeMaps() + { + foreach (var map in assemblyTypeMaps.Maps) + { + yield return new ReadyToRunProxyTypeMapNode(triggeringModule, map.Key, map.Value, importProvider); + } + } + + public override void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, INativeFormatTypeReferenceProvider commonFixupsTableNode) + { + base.AddToReadyToRunHeader(header, nodeFactory, commonFixupsTableNode); + + if (IsEmpty) + return; + + + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 120d2af83ced80..9d0396b3f5f554 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -143,6 +143,8 @@ + + @@ -192,6 +194,7 @@ + @@ -202,6 +205,9 @@ + + + From 66ea394b9ede5bdb34527a958bcd34de5afbade7 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 10 Feb 2026 10:19:54 -0800 Subject: [PATCH 06/19] Add support for precaching assembly target lookup and refactor R2R integration for type maps generally --- src/coreclr/inc/readytorun.h | 3 +- .../tools/Common/Compiler/TypeMapMetadata.cs | 2 +- .../Common/Internal/Runtime/ModuleHeaders.cs | 1 + .../ImportReferenceProvider.cs | 20 ++ .../ReadyToRun/ReadyToRunModuleSignature.cs | 41 ++++ .../ReadyToRunCodegenNodeFactory.cs | 10 +- .../ReadyToRunSymbolNodeFactory.cs | 22 ++- .../Compiler/ReadyToRunCodegenCompilation.cs | 4 +- .../Compiler/ReadyToRunTypeMapManager.cs | 17 +- .../Compiler/TypeMapAssemblyTargetsNode.cs | 78 ++++++++ .../ILCompiler.ReadyToRun.csproj | 2 + src/coreclr/vm/assemblynative.cpp | 178 ++++++++++-------- src/coreclr/vm/assemblynative.hpp | 10 +- src/coreclr/vm/qcallentrypoints.cpp | 2 + src/coreclr/vm/readytoruninfo.cpp | 84 +++++++++ src/coreclr/vm/readytoruninfo.h | 5 + .../InteropServices/TypeMapLazyDictionary.cs | 67 +++++-- 17 files changed, 429 insertions(+), 117 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/TypeMapAssemblyTargetsNode.cs diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index 0e97406316f9de..14b3d5e6217776 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -52,7 +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 and ProxyTypeMaps sections +// R2R Version 18.1 adds the ExternalTypeMaps, ProxyTypeMaps, TypeMapAssemblyTargets sections struct READYTORUN_CORE_HEADER { @@ -120,6 +120,7 @@ enum class ReadyToRunSectionType : uint32_t 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 diff --git a/src/coreclr/tools/Common/Compiler/TypeMapMetadata.cs b/src/coreclr/tools/Common/Compiler/TypeMapMetadata.cs index fa7945bc78d459..c74903c38d6946 100644 --- a/src/coreclr/tools/Common/Compiler/TypeMapMetadata.cs +++ b/src/coreclr/tools/Common/Compiler/TypeMapMetadata.cs @@ -201,7 +201,7 @@ public void AddTargetModule(ModuleDesc targetModule) /// /// 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. /// - public IEnumerable TargetModules => _targetModules; + public IReadOnlyList TargetModules => _targetModules; IReadOnlyDictionary IExternalTypeMap.TypeMap => _externalTypeMap; diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index 5b196f29f26c01..5b43b8f565238e 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -83,6 +83,7 @@ enum ReadyToRunSectionType // 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 diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs index 2e1f2640f75e90..1cfef8c4636d80 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs @@ -6,6 +6,9 @@ using ILCompiler.DependencyAnalysis.ReadyToRun; using Internal.TypeSystem; using Internal.NativeFormat; +using Internal.ReadyToRunConstants; +using System.Diagnostics; +using Internal.TypeSystem.Ecma; namespace ILCompiler.DependencyAnalysis { @@ -18,16 +21,33 @@ public Import GetImportToType(TypeDesc type) return _symbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeHandle, type); } + public Import GetImportToModule(ModuleDesc module) + { + Debug.Assert(module is IEcmaModule); + return _symbolNodeFactory.ModuleLookup((IEcmaModule)module); + } + public void Initialize(ReadyToRunSymbolNodeFactory symbolNodeFactory) { _symbolNodeFactory = symbolNodeFactory; } + internal Vertex EncodeReferenceToModule(NativeWriter writer, ModuleDesc module) + { + Import typeImport = GetImportToModule(module); + return writer.GetTuple(writer.GetUnsignedConstant((uint)typeImport.Table.IndexFromBeginningOfArray), writer.GetUnsignedConstant((uint)typeImport.IndexFromBeginningOfArray)); + } + Vertex INativeFormatTypeReferenceProvider.EncodeReferenceToMethod(NativeWriter writer, MethodDesc method) => throw new NotImplementedException(); Vertex INativeFormatTypeReferenceProvider.EncodeReferenceToType(NativeWriter writer, TypeDesc type) { Import typeImport = GetImportToType(type); return writer.GetTuple(writer.GetUnsignedConstant((uint)typeImport.Table.IndexFromBeginningOfArray), writer.GetUnsignedConstant((uint)typeImport.IndexFromBeginningOfArray)); } + internal Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type) + { + Import typeImport = GetImportToType(type); + return writer.GetTuple(writer.GetUnsignedConstant((uint)typeImport.Table.IndexFromBeginningOfArray), writer.GetUnsignedConstant((uint)typeImport.IndexFromBeginningOfArray)); + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs new file mode 100644 index 00000000000000..1e0cd48c38963f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs @@ -0,0 +1,41 @@ +// 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.Diagnostics; + +using Internal.Text; +using Internal.ReadyToRunConstants; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.DependencyAnalysis.ReadyToRun +{ + public class ReadyToRunModuleSignature(IEcmaModule module) : Signature + { + public override int ClassCode => 208107955; + + public IEcmaModule Module { get; } = module; + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataSignatureBuilder builder = new ObjectDataSignatureBuilder(factory, relocsOnly); + builder.AddSymbol(this); + builder.EmitFixup(factory, ReadyToRunFixupKind.Helper, Module, factory.SignatureContext); + builder.EmitUInt((uint)ReadyToRunHelper.Module); + return builder.ToObjectData(); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix); + sb.Append("ReadyToRunModule_"u8); + sb.Append(Module.Assembly.Name); + } + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return Module.CompareTo(((ReadyToRunModuleSignature)other).Module); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index 126741df47fcd6..c92ebe5da1de15 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -397,7 +397,7 @@ private void CreateNodeCaches() public InstrumentationDataTableNode InstrumentationDataTable; public InliningInfoNode CrossModuleInlningInfo; - public ImportReferenceProvider FixupCellProvider; + public ImportReferenceProvider ImportReferenceProvider; public Import ModuleImport; @@ -788,13 +788,13 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph, I var node = new MethodIsGenericMapNode(inputModule); tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.MethodIsGenericMap, node); } - FixupCellProvider ??= new ImportReferenceProvider(); + ImportReferenceProvider ??= new ImportReferenceProvider(); - TypeMapMetadata metadata = TypeMapMetadata.CreateFromAssembly((EcmaAssembly)inputModule.Assembly, TypeSystemContext, TypeSystemContext.SystemModule, TypeMapAssemblyTargetsMode.Record); + TypeMapMetadata metadata = TypeMapMetadata.CreateFromAssembly((EcmaAssembly)inputModule.Assembly, TypeSystemContext.SystemModule, TypeMapAssemblyTargetsMode.Record); - ReadyToRunTypeMapManager typeMapManager = new(inputModule, metadata, FixupCellProvider); + ReadyToRunTypeMapManager typeMapManager = new(inputModule, metadata); + typeMapManager.AddToReadyToRunHeader(tableHeader, this, ImportReferenceProvider); typeMapManager.AttachToDependencyGraph(graph); - typeMapManager.AddToReadyToRunHeader(tableHeader, this, FixupCellProvider); } InliningInfoNode crossModuleInliningInfoTable = new InliningInfoNode(null, diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs index 769ab6a95c8d56..b65e1b376d04c1 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs @@ -184,6 +184,13 @@ private void CreateNodeCaches() _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.ContinuationLayout, key) ); }); + + _ecmaModuleFixupCache = new NodeCache(key => + { + return new PrecodeHelperImport( + _codegenNodeFactory, + new ReadyToRunModuleSignature(key)); + }); } private NodeCache _continuationTypeFixups; @@ -191,6 +198,13 @@ private void CreateNodeCaches() public ISymbolNode ContinuationTypeSymbol(AsyncContinuationType key) { return _continuationTypeFixups.GetOrAdd(key); + + _ecmaModuleFixupCache = new NodeCache(key => + { + return new PrecodeHelperImport( + _codegenNodeFactory, + new ReadyToRunModuleSignature(key)); + }); } private NodeCache _importStrings; @@ -413,7 +427,7 @@ private Import CreateMethodDictionary(MethodWithToken method) return new PrecodeHelperImport( _codegenNodeFactory, _codegenNodeFactory.MethodSignature( - ReadyToRunFixupKind.MethodDictionary, + ReadyToRunFixupKind.MethodDictionary, method, isInstantiatingStub: true)); } @@ -498,6 +512,12 @@ public Import CheckILBodyFixupSignature(EcmaMethod method) method)); } + private NodeCache _ecmaModuleFixupCache; + public Import ModuleLookup(IEcmaModule module) + { + return _ecmaModuleFixupCache.GetOrAdd(module); + } + struct MethodAndCallSite : IEquatable { public readonly MethodWithToken Method; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index 2935d6628a50d0..702d90fc736ab6 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -377,8 +377,8 @@ internal ReadyToRunCodegenCompilation( nodeFactory.InstrumentationDataTable.Initialize(SymbolNodeFactory); if (nodeFactory.CrossModuleInlningInfo != null) nodeFactory.CrossModuleInlningInfo.Initialize(SymbolNodeFactory); - if (nodeFactory.FixupCellProvider != null) - nodeFactory.FixupCellProvider.Initialize(SymbolNodeFactory); + if (nodeFactory.ImportReferenceProvider != null) + nodeFactory.ImportReferenceProvider.Initialize(SymbolNodeFactory); _inputFiles = inputFiles; _compositeRootPath = compositeRootPath; _printReproInstructions = printReproInstructions; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTypeMapManager.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTypeMapManager.cs index ff2edf78a153ef..61ed5314d0880a 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTypeMapManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTypeMapManager.cs @@ -7,12 +7,15 @@ using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysis.ReadyToRun; using ILCompiler.DependencyAnalysisFramework; +using Internal.Runtime; using Internal.TypeSystem; namespace ILCompiler.ReadyToRun { - public sealed class ReadyToRunTypeMapManager(ModuleDesc triggeringModule, TypeMapMetadata assemblyTypeMaps, ImportReferenceProvider importProvider) : TypeMapManager + public sealed class ReadyToRunTypeMapManager(ModuleDesc triggeringModule, TypeMapMetadata assemblyTypeMaps) : TypeMapManager { + private ImportReferenceProvider _importReferenceProvider; + public override void AttachToDependencyGraph(DependencyAnalyzerBase graph) { base.AttachToDependencyGraph(graph); @@ -36,7 +39,7 @@ internal override IEnumerable GetExternalTypeMaps() { foreach (var map in assemblyTypeMaps.Maps) { - yield return new ReadyToRunExternalTypeMapNode(triggeringModule, map.Key, map.Value, importProvider); + yield return new ReadyToRunExternalTypeMapNode(triggeringModule, map.Key, map.Value, _importReferenceProvider); } } @@ -44,18 +47,20 @@ internal override IEnumerable GetProxyTypeMaps() { foreach (var map in assemblyTypeMaps.Maps) { - yield return new ReadyToRunProxyTypeMapNode(triggeringModule, map.Key, map.Value, importProvider); + yield return new ReadyToRunProxyTypeMapNode(triggeringModule, map.Key, map.Value, _importReferenceProvider); } } - public override void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, INativeFormatTypeReferenceProvider commonFixupsTableNode) + public void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFactory nodeFactory, ImportReferenceProvider importReferenceProvider) { - base.AddToReadyToRunHeader(header, nodeFactory, commonFixupsTableNode); + base.AddToReadyToRunHeader(header, nodeFactory, importReferenceProvider); + + _importReferenceProvider = importReferenceProvider; if (IsEmpty) return; - + header.Add(ReadyToRunSectionType.TypeMapAssemblyTargets, new TypeMapAssemblyTargetsNode(assemblyTypeMaps, importReferenceProvider)); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/TypeMapAssemblyTargetsNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/TypeMapAssemblyTargetsNode.cs new file mode 100644 index 00000000000000..b867b6fd78769f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/TypeMapAssemblyTargetsNode.cs @@ -0,0 +1,78 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; +using Internal.NativeFormat; +using Internal.Text; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.ReadyToRun +{ + internal class TypeMapAssemblyTargetsNode : ObjectNode, ISymbolDefinitionNode + { + private TypeMapMetadata _assemblyTypeMaps; + private ImportReferenceProvider _importReferenceProvider; + + public TypeMapAssemblyTargetsNode(TypeMapMetadata assemblyTypeMaps, ImportReferenceProvider importReferenceProvider) + { + _assemblyTypeMaps = assemblyTypeMaps; + _importReferenceProvider = importReferenceProvider; + } + + public override bool IsShareable => false; + + public override int ClassCode => 1564556383; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => throw new System.NotImplementedException(); + + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList dependencies = []; + foreach (var map in _assemblyTypeMaps.Maps) + { + var groupType = map.Key; + dependencies.Add(new DependencyListEntry(_importReferenceProvider.GetImportToType(groupType), "Type Map Assembly Target")); + foreach (var targetModule in map.Value.TargetModules) + { + dependencies.Add(new DependencyListEntry(_importReferenceProvider.GetImportToModule(targetModule), "Type Map Assembly Target")); + } + } + return dependencies; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + NativeWriter writer = new(); + Section section = writer.NewSection(); + + VertexHashtable table = new(); + + foreach (var map in _assemblyTypeMaps.Maps) + { + var groupType = map.Key; + Vertex groupTypeVertex = _importReferenceProvider.EncodeReferenceToType(writer, groupType); + VertexSequence modules = new(); + foreach (var targetModule in map.Value.TargetModules) + { + Vertex targetModuleVertex = _importReferenceProvider.EncodeReferenceToModule(writer, targetModule); + modules.Append(targetModuleVertex); + } + Vertex modulesVertex = writer.GetTuple(groupTypeVertex, modules); + table.Append((uint)groupType.GetHashCode(), modulesVertex); + } + + section.Place(table); + + ObjectDataBuilder builder = new(); + builder.AddSymbol(this); + builder.EmitBytes(writer.Save()); + return builder.ToObjectData(); + } + public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.ReadOnlyDataSection; + protected override string GetName(NodeFactory context) => "Type Map Assembly Targets Tables"; + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) => sb.Append(nameMangler.CompilationUnitPrefix).Append("__TypeMapAssemblyTargets"u8); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 9d0396b3f5f554..ff5223e9528584 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -197,6 +197,7 @@ + @@ -211,6 +212,7 @@ + diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp index 8ecbf384308005..167def617806a1 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -1536,53 +1536,29 @@ namespace } #ifdef FEATURE_READYTORUN - template - void ProcessPrecachedTypeMaps( - TExternalTypeMapCallback onNewExternalTypeMap, - TProxyTypeMapCallback onNewProxyTypeMap, - MethodTable* groupTypeMT, - Assembly* pAssembly) + template + bool ProcessPrecachedTypeMapInfo( + TReadyToRunInfoFilter filter, + TReadyToRunInfoCallback callback, + Assembly* pAssembly, + bool* pHasPrecachedInfo + ) { STANDARD_VM_CONTRACT; PTR_Module pModule = pAssembly->GetModule(); - if (!m_pModule->IsReadyToRun()) + if (!pModule->IsReadyToRun()) { - return; + return false; } - PTR_ReadyToRunInfo pR2RInfo = pModule->GetReadyToRunInfo(); - - if (pR2RInfo->HasPrecachedExternalTypeMap(groupTypeMT)) - { - GCX_COOP(); - REFLECTIONMODULEBASEREF moduleRef = pModule->GetExposedObject(); - GCPROTECT_BEGIN(&moduleRef); - { - GCX_PREEMP(); - if (!onNewExternalTypeMap(&moduleRef)) - { - return; - } - } - GCPROTECT_END(); - } + PTR_ReadyToRunInfo pR2RInfo = pModule->GetReadyToRunInfo(); - if (pR2RInfo->HasPrecachedProxyTypeMap(groupTypeMT)) + if (filter(pR2RInfo)) { - GCX_COOP(); - REFLECTIONMODULEBASEREF moduleRef = pModule->GetExposedObject(); - - GCPROTECT_BEGIN(&moduleRef); - { - GCX_PREEMP(); - if (!onNewProxyTypeMap(&moduleRef)) - { - return; - } - } - GCPROTECT_END(); + return callback(pR2RInfo); } + return true; } #endif @@ -1636,6 +1612,15 @@ namespace return TRUE; } + void Add(Assembly* assembly) + { + if (_toProcess.Lookup(assembly) == NULL + && _processed.Lookup(assembly) == NULL) + { + _toProcess.Add(assembly); + } + } + bool IsEmpty() const { return _toProcess.GetCount() == 0; @@ -1703,8 +1688,8 @@ extern "C" void QCALLTYPE TypeMapLazyDictionary_ProcessAttributes( QCall::TypeHandle pGroupType, BOOL (*newExternalTypeEntry)(CallbackContext* context, ProcessAttributesCallbackArg* arg), BOOL (*newProxyTypeEntry)(CallbackContext* context, ProcessAttributesCallbackArg* arg), - BOOL (*newPrecachedExternalTypeMap)(CallbackContext* context, REFLECTMODULEBASEREF* module), - BOOL (*newPrecachedProxyTypeMap)(CallbackContext* context, REFLECTMODULEBASEREF* module), + BOOL (*newPrecachedExternalTypeMap)(CallbackContext* context), + BOOL (*newPrecachedProxyTypeMap)(CallbackContext* context), CallbackContext* context) { QCALL_CONTRACT; @@ -1729,63 +1714,90 @@ extern "C" void QCALLTYPE TypeMapLazyDictionary_ProcessAttributes( GCX_COOP(); context->_currAssembly = currAssembly->GetExposedObject(); } + bool hasPrecachedInfo = false; - ProcessTypeMapAttribute( - TypeMapAssemblyTargetAttributeName, - assemblies, - groupTypeMT, - currAssembly); - - bool foundPrecachedExternalTypeMap = false; - bool foundPrecachedProxyTypeMap = false; - ProcessPrecachedTypeMaps( - [=, &foundPrecachedExternalTypeMap](REFLECTMODULEBASEREF* module) -> BOOL - { - STANDARD_VM_CONTRACT; - foundPrecachedExternalTypeMap = true; - ASSERT_PROTECTED(module); - return newPrecachedExternalTypeMap(context, module); - }, - [=, &foundPrecachedProxyTypeMap](REFLECTMODULEBASEREF* module) -> BOOL - { - STANDARD_VM_CONTRACT; - foundPrecachedProxyTypeMap = true; - ASSERT_PROTECTED(module); - return newPrecachedProxyTypeMap(context, module); - }, - groupTypeMT, - currAssembly); +#ifdef FEATURE_READYTORUN + if (!ProcessPrecachedTypeMapInfo( + [=](PTR_ReadyToRunInfo pR2RInfo) { return pR2RInfo->HasPrecachedExternalTypeMap(groupTypeMT); }, + [=](PTR_ReadyToRunInfo pR2RInfo) { return newPrecachedExternalTypeMap(context); }, + currAssembly, + &hasPrecachedInfo)) + { + // Return now so we can throw the exception encountered during creation. + return; + } - // We will only process the specific type maps if we have a callback to process - // the entry and the precached map was not calculated for this module. - if (newExternalTypeEntry != NULL && !foundPrecachedExternalTypeMap) + if (!ProcessPrecachedTypeMapInfo( + [=](PTR_ReadyToRunInfo pR2RInfo) { return pR2RInfo->HasPrecachedProxyTypeMap(groupTypeMT); }, + [=](PTR_ReadyToRunInfo pR2RInfo) { return newPrecachedProxyTypeMap(context); }, + currAssembly, + &hasPrecachedInfo)) { - MappingsProcessor onExternalType{ newExternalTypeEntry, context }; - ProcessTypeMapAttribute( - TypeMapAttributeName, - onExternalType, - groupTypeMT, - currAssembly); + // Return now so we can throw the exception encountered during creation. + return; } - if (newProxyTypeEntry != NULL && !foundPrecachedProxyTypeMap) + COUNT_T assemblyTargetCount = 0; + ProcessPrecachedTypeMapInfo( + [=, &assemblyTargetCount](PTR_ReadyToRunInfo pR2RInfo) { return pR2RInfo->HasTypeMapAssemblyTargets(groupTypeMT, &assemblyTargetCount); }, + [&](PTR_ReadyToRunInfo pR2RInfo) + { + CQuickArray targetModules; + targetModules.ReSizeThrows(assemblyTargetCount); + COUNT_T numReturnedTargets = pR2RInfo->GetTypeMapAssemblyTargets(groupTypeMT, targetModules.Ptr(), assemblyTargetCount); + _ASSERTE(numReturnedTargets == assemblyTargetCount); + for (COUNT_T i = 0; i < assemblyTargetCount; i++) + { + Assembly* targetAssembly = targetModules[i]->GetAssembly(); + assemblies.Add(targetAssembly); + } + return true; + }, + currAssembly, + &hasPrecachedInfo + ); +#endif // FEATURE_READYTORUN + + if (!hasPrecachedInfo) { - MappingsProcessor onProxyType{ newProxyTypeEntry, context }; ProcessTypeMapAttribute( - TypeMapAssociationAttributeName, - onProxyType, + TypeMapAssemblyTargetAttributeName, + assemblies, groupTypeMT, currAssembly); + + + // We will only process the specific type maps if we have a callback to process + // the entry and the precached map was not calculated for this module. + if (newExternalTypeEntry != NULL && !hasPrecachedInfo) + { + MappingsProcessor onExternalType{ newExternalTypeEntry, context }; + ProcessTypeMapAttribute( + TypeMapAttributeName, + onExternalType, + groupTypeMT, + currAssembly); + } + + if (newProxyTypeEntry != NULL && !hasPrecachedInfo) + { + MappingsProcessor onProxyType{ newProxyTypeEntry, context }; + ProcessTypeMapAttribute( + TypeMapAssociationAttributeName, + onProxyType, + groupTypeMT, + currAssembly); + } } } END_QCALL; } -extern "C" TypeHandle QCALLTYPE TypeMapLazyDictionary_FindPrecachedExternalTypeMapEntry( +extern "C" TADDR QCALLTYPE TypeMapLazyDictionary_FindPrecachedExternalTypeMapEntry( QCall::ModuleHandle pModule, QCall::TypeHandle pGroupType, - LPCUTF8 pKey) + LPCUTF8 key) { QCALL_CONTRACT; _ASSERTE(pModule != NULL); @@ -1806,16 +1818,16 @@ extern "C" TypeHandle QCALLTYPE TypeMapLazyDictionary_FindPrecachedExternalTypeM resultTypeHnd = pR2RInfo->FindPrecachedExternalTypeMapEntry( groupTypeMT, - pKey); + key); } #endif // FEATURE_READYTORUN END_QCALL; - return resultTypeHnd; + return resultTypeHnd.AsTAddr(); } -extern "C" TypeHandle QCALLTYPE TypeMapLazyDictionary_FindPrecachedProxyTypeMapEntry( +extern "C" TADDR QCALLTYPE TypeMapLazyDictionary_FindPrecachedProxyTypeMapEntry( QCall::ModuleHandle pModule, QCall::TypeHandle pGroupType, QCall::TypeHandle pType) @@ -1845,5 +1857,5 @@ extern "C" TypeHandle QCALLTYPE TypeMapLazyDictionary_FindPrecachedProxyTypeMapE END_QCALL; - return resultTypeHnd; + return resultTypeHnd.AsTAddr(); } diff --git a/src/coreclr/vm/assemblynative.hpp b/src/coreclr/vm/assemblynative.hpp index d427645811b9fa..8187cfe42ff86c 100644 --- a/src/coreclr/vm/assemblynative.hpp +++ b/src/coreclr/vm/assemblynative.hpp @@ -168,17 +168,17 @@ extern "C" void QCALLTYPE TypeMapLazyDictionary_ProcessAttributes( QCall::TypeHandle pTypeGroup, BOOL (*newExternalTypeEntry)(CallbackContext* context, ProcessAttributesCallbackArg* arg), BOOL (*newProxyTypeEntry)(CallbackContext* context, ProcessAttributesCallbackArg* arg), - BOOL (*newPrecachedExternalTypeMap)(CallbackContext* context, REFLECTMODULEBASEREF* module), - BOOL (*newPrecachedProxyTypeMap)(CallbackContext* context, REFLECTMODULEBASEREF* module), + BOOL (*newPrecachedExternalTypeMap)(CallbackContext* context), + BOOL (*newPrecachedProxyTypeMap)(CallbackContext* context), CallbackContext* context); -extern "C" TypeHandle TypeMapLazyDictionary_FindPrecachedExternalTypeMapEntry( +extern "C" TADDR TypeMapLazyDictionary_FindPrecachedExternalTypeMapEntry( QCall::ModuleHandle pModule, QCall::TypeHandle pGroupType, - QCall::TypeHandle pType + LPCUTF8 key ); -extern "C" TypeHandle TypeMapLazyDictionary_FindPrecachedProxyTypeMapEntry( +extern "C" TADDR TypeMapLazyDictionary_FindPrecachedProxyTypeMapEntry( QCall::ModuleHandle pModule, QCall::TypeHandle pGroupType, QCall::TypeHandle pType diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 0a03fbce6b82a2..cc76fbe08ac8b5 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -278,6 +278,8 @@ static const Entry s_QCall[] = DllImportEntry(AssemblyNative_GetAssemblyCount) DllImportEntry(AssemblyNative_GetEntryAssembly) DllImportEntry(AssemblyNative_GetExecutingAssembly) + DllImportEntry(TypeMapLazyDictionary_FindPrecachedExternalTypeMapEntry) + DllImportEntry(TypeMapLazyDictionary_FindPrecachedProxyTypeMapEntry) DllImportEntry(TypeMapLazyDictionary_ProcessAttributes) #if defined(FEATURE_MULTICOREJIT) DllImportEntry(MultiCoreJIT_InternalSetProfileRoot) diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index cef3b2d6c2d355..fdb3750f67f35f 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -943,6 +943,12 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocat { m_proxyTypeMaps = NativeHashtable(NativeParser(&m_nativeReader, pProxyTypeMapsDir->VirtualAddress)); } + + IMAGE_DATA_DIRECTORY* pTypeMapAssemblyTargetsDir = m_component.FindSection(ReadyToRunSectionType::TypeMapAssemblyTargets); + if (pTypeMapAssemblyTargetsDir != NULL) + { + m_typeMapAssemblyTargets = NativeHashtable(NativeParser(&m_nativeReader, pTypeMapAssemblyTargetsDir->VirtualAddress)); + } } if (!m_isComponentAssembly) @@ -1594,6 +1600,31 @@ namespace return *(TypeHandle*)fixupAddress; } + + Module* GetModuleForNativeFormatFixupReference(PTR_ReadyToRunInfo pR2RInfo, PTR_Module pModule, uint32_t importSection, uint32_t fixupIndex) + { + STANDARD_VM_CONTRACT; + + COUNT_T countImportSections; + PTR_READYTORUN_IMPORT_SECTION pImportSections = pR2RInfo->GetImportSections(&countImportSections); + + if (importSection >= countImportSections) + { + _ASSERTE(!"Malformed PGO type or method handle data"); + return nullptr; + } + + PTR_READYTORUN_IMPORT_SECTION pImportSection = &pImportSections[importSection]; + COUNT_T cbData; + TADDR pData = pR2RInfo->GetImage()->GetDirectoryData(&pImportSection->Section, &cbData); + PTR_SIZE_T fixupAddress = dac_cast(pData + fixupIndex * sizeof(TADDR)); + if (!pModule->FixupNativeEntry(pImportSections + importSection, fixupIndex, fixupAddress)) + { + return nullptr; + } + + return *(Module**)fixupAddress; + } } bool ReadyToRunInfo::HasPrecachedExternalTypeMap(MethodTable* pGroupTypeMT) @@ -1737,6 +1768,59 @@ TypeHandle ReadyToRunInfo::FindPrecachedProxyTypeMapEntry(MethodTable* pGroupTyp return TypeHandle(); } +bool ReadyToRunInfo::HasTypeMapAssemblyTargets(MethodTable* pGroupType, COUNT_T* pCount) +{ + STANDARD_VM_CONTRACT; + _ASSERTE(pGroupType != nullptr); + if (m_typeMapAssemblyTargets.IsNull()) + { + return false; + } + UINT32 hash = GetVersionResilientTypeHashCode(pGroupType); + NativeHashtable::Enumerator lookup = m_typeMapAssemblyTargets.Lookup(hash); + NativeParser entryParser; + while (lookup.GetNext(entryParser)) + { + if (GetTypeHandleForNativeFormatFixupReference(this, m_pModule, entryParser.GetUnsigned(), entryParser.GetUnsigned()) == TypeHandle(pGroupType)) + { + *pCount = entryParser.GetUnsigned(); + return true; + } + } + + return false; +} + +COUNT_T ReadyToRunInfo::GetTypeMapAssemblyTargets(MethodTable* pGroupType, Module** pTargetModules, COUNT_T count) +{ + STANDARD_VM_CONTRACT; + _ASSERTE(pGroupType != nullptr); + if (m_typeMapAssemblyTargets.IsNull()) + { + return 0; + } + + UINT32 hash = GetVersionResilientTypeHashCode(pGroupType); + NativeHashtable::Enumerator lookup = m_typeMapAssemblyTargets.Lookup(hash); + NativeParser entryParser; + while (lookup.GetNext(entryParser)) + { + if (GetTypeHandleForNativeFormatFixupReference(this, m_pModule, entryParser.GetUnsigned(), entryParser.GetUnsigned()) != TypeHandle(pGroupType)) + { + continue; + } + + COUNT_T numTargets = entryParser.GetUnsigned(); + COUNT_T resultCount = min(numTargets, count); + for (COUNT_T i = 0; i < resultCount; i++) + { + pTargetModules[i] = GetModuleForNativeFormatFixupReference(this, m_pModule, entryParser.GetUnsigned(), entryParser.GetUnsigned()); + } + } + + return 0; +} + class NativeManifestModule : public ModuleBase { IMDInternalImport* m_pMDImport; diff --git a/src/coreclr/vm/readytoruninfo.h b/src/coreclr/vm/readytoruninfo.h index 9746a42d6f9d0a..eb78a2e23a94f2 100644 --- a/src/coreclr/vm/readytoruninfo.h +++ b/src/coreclr/vm/readytoruninfo.h @@ -145,6 +145,7 @@ class ReadyToRunInfo NativeFormat::NativeHashtable m_externalTypeMaps; NativeFormat::NativeHashtable m_proxyTypeMaps; + NativeFormat::NativeHashtable m_typeMapAssemblyTargets; PTR_ReadyToRunInfo m_pNextR2RForUnrelatedCode; @@ -339,6 +340,10 @@ class ReadyToRunInfo bool HasPrecachedProxyTypeMap(MethodTable* pGroupType); TypeHandle FindPrecachedProxyTypeMapEntry(MethodTable* pGroupType, TypeHandle key); + bool HasTypeMapAssemblyTargets(MethodTable* pGroupType, COUNT_T* pCount); + + COUNT_T GetTypeMapAssemblyTargets(MethodTable* pGroupType, Module** pTargetModules, COUNT_T count); + BOOL IsImageVersionAtLeast(int majorVersion, int minorVersion); private: BOOL GetTypeNameFromToken(IMDInternalImport * pImport, mdToken mdType, LPCUTF8 * ppszName, LPCUTF8 * ppszNameSpace); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs index 04677cf0790fa7..21b344190a5e8e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs @@ -22,10 +22,16 @@ internal static partial class TypeMapLazyDictionary private ref struct CallbackContext { private RuntimeAssembly? _currAssembly; + private readonly RuntimeType _groupType; private LazyExternalTypeDictionary? _externalTypeMap; private LazyProxyTypeDictionary? _proxyTypeMap; private ExceptionDispatchInfo? _creationException; + public CallbackContext(RuntimeType groupType) + { + _groupType = groupType; + } + public RuntimeAssembly CurrentAssembly { get @@ -41,7 +47,7 @@ public LazyExternalTypeDictionary ExternalTypeMap [RequiresUnreferencedCode("Lazy TypeMap isn't supported for Trimmer scenarios")] get { - _externalTypeMap ??= new LazyExternalTypeDictionary(); + _externalTypeMap ??= new LazyExternalTypeDictionary(_groupType); return _externalTypeMap; } } @@ -51,7 +57,7 @@ public LazyProxyTypeDictionary ProxyTypeMap [RequiresUnreferencedCode("Lazy TypeMap isn't supported for Trimmer scenarios")] get { - _proxyTypeMap ??= new LazyProxyTypeDictionary(); + _proxyTypeMap ??= new LazyProxyTypeDictionary(_groupType); return _proxyTypeMap; } } @@ -78,10 +84,16 @@ private static unsafe partial void ProcessAttributes( QCallTypeHandle groupType, delegate* unmanaged newExternalTypeEntry, delegate* unmanaged newProxyTypeEntry, - delegate* unmanaged newPrecachedExternalTypeMap, - delegate* unmanaged newPrecachedProxyTypeMap, + delegate* unmanaged newPrecachedExternalTypeMap, + delegate* unmanaged newPrecachedProxyTypeMap, CallbackContext* context); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "TypeMapLazyDictionary_FindPrecachedExternalTypeMapEntry", StringMarshalling = StringMarshalling.Utf8)] + private static unsafe partial IntPtr FindPrecachedExternalTypeMapEntry(QCallModule module, QCallTypeHandle groupType, string key); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "TypeMapLazyDictionary_FindPrecachedProxyTypeMapEntry")] + private static unsafe partial IntPtr FindPrecachedProxyTypeMapEntry(QCallModule module, QCallTypeHandle groupType, QCallTypeHandle type); + public ref struct Utf16SharedBuffer { private char[]? _backingArray; @@ -121,14 +133,13 @@ private static void ConvertUtf8ToUtf16(ReadOnlySpan utf8TypeName, out Utf1 } [UnmanagedCallersOnly] - private static unsafe Interop.BOOL NewPrecachedExternalTypeMap(CallbackContext* context, RuntimeModule* module) + private static unsafe Interop.BOOL NewPrecachedExternalTypeMap(CallbackContext* context) { Debug.Assert(context != null); - Debug.Assert(module != default); try { - context->ExternalTypeMap.AddPreCachedModule(*module); + context->ExternalTypeMap.AddPreCachedModule((RuntimeModule)context->CurrentAssembly.ManifestModule); } catch (Exception ex) { @@ -140,14 +151,13 @@ private static unsafe Interop.BOOL NewPrecachedExternalTypeMap(CallbackContext* } [UnmanagedCallersOnly] - private static unsafe Interop.BOOL NewPrecachedProxyTypeMap(CallbackContext* context, RuntimeModule* module) + private static unsafe Interop.BOOL NewPrecachedProxyTypeMap(CallbackContext* context) { Debug.Assert(context != null); - Debug.Assert(module != default); try { - context->ProxyTypeMap.AddPreCachedModule(*module); + context->ProxyTypeMap.AddPreCachedModule((RuntimeModule)context->CurrentAssembly.ManifestModule); } catch (Exception ex) { @@ -244,12 +254,14 @@ private static unsafe CallbackContext CreateMaps( throw new InvalidOperationException(SR.InvalidOperation_TypeMapMissingEntryAssembly); } - CallbackContext context; + CallbackContext context = new(groupType); ProcessAttributes( new QCallAssembly(ref startingAssembly), new QCallTypeHandle(ref groupType), newExternalTypeEntry, newProxyTypeEntry, + &NewPrecachedExternalTypeMap, + &NewPrecachedProxyTypeMap, &context); // If an exception was thrown during the processing of @@ -283,6 +295,7 @@ public static IReadOnlyDictionary CreateProxyTypeMap(RuntimeType gro private abstract class LazyTypeLoadDictionary : IReadOnlyDictionary where TKey : notnull { + protected RuntimeType _groupType; private readonly List _preCachedModules = []; protected abstract bool TryGetOrLoadType(TKey key, [NotNullWhen(true)] out Type? type); @@ -295,9 +308,9 @@ public Type this[TKey key] { foreach (RuntimeModule module in _preCachedModules) { - if (TryGetOrLoadTypeFromPreCachedDictionary(module, key, out Type? type)) + if (TryGetOrLoadTypeFromPreCachedDictionary(module, key, out Type? precachedType)) { - return type; + return precachedType; } } @@ -310,6 +323,11 @@ public Type this[TKey key] } } + protected LazyTypeLoadDictionary(RuntimeType groupType) + { + _groupType = groupType; + } + public void AddPreCachedModule(RuntimeModule module) { _preCachedModules.Add(module); @@ -376,6 +394,13 @@ private sealed class LazyExternalTypeDictionary : LazyTypeLoadDictionary { private readonly Dictionary _lazyData = []; + protected override bool TryGetOrLoadTypeFromPreCachedDictionary(RuntimeModule module, string key, [NotNullWhen(true)] out Type? type) + { + IntPtr handle = FindPrecachedExternalTypeMapEntry(new QCallModule(ref module), new QCallTypeHandle(ref _groupType), key); + type = RuntimeTypeHandle.GetRuntimeTypeFromHandleMaybeNull(handle); + return type != null; + } + protected override bool TryGetOrLoadType(string key, [NotNullWhen(true)] out Type? type) { if (!_lazyData.TryGetValue(key, out DelayedType? value)) @@ -388,6 +413,10 @@ protected override bool TryGetOrLoadType(string key, [NotNullWhen(true)] out Typ return true; } + public LazyExternalTypeDictionary(RuntimeType groupType) : base(groupType) + { + } + public void Add(string key, TypeNameUtf8 targetType, RuntimeAssembly fallbackAssembly) { if (_lazyData.ContainsKey(key)) @@ -428,6 +457,14 @@ public void Add(SourceProxyPair newEntryMaybe) private readonly Dictionary _lazyData = new(); + protected override bool TryGetOrLoadTypeFromPreCachedDictionary(RuntimeModule module, Type key, [NotNullWhen(true)] out Type? type) + { + RuntimeType rtKey = (RuntimeType)key; + IntPtr handle = FindPrecachedProxyTypeMapEntry(new QCallModule(ref module), new QCallTypeHandle(ref _groupType), new QCallTypeHandle(ref rtKey)); + type = RuntimeTypeHandle.GetRuntimeTypeFromHandleMaybeNull(handle); + return type != null; + } + protected override bool TryGetOrLoadType(Type key, [NotNullWhen(true)] out Type? type) { if (key is not RuntimeType rtType) @@ -462,6 +499,10 @@ protected override bool TryGetOrLoadType(Type key, [NotNullWhen(true)] out Type? return false; } + public LazyProxyTypeDictionary(RuntimeType groupType) : base(groupType) + { + } + public void Add( TypeName parsedSourceTypeName, TypeNameUtf8 sourceTypeName, From 7b64145f9d82464d712874646f6b6a35bb37ef7f Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 10 Feb 2026 12:38:45 -0800 Subject: [PATCH 07/19] Add verification of CallbackContext struct layout --- src/coreclr/vm/assemblynative.hpp | 1 + src/coreclr/vm/binder.cpp | 1 + src/coreclr/vm/corelib.h | 3 +++ 3 files changed, 5 insertions(+) diff --git a/src/coreclr/vm/assemblynative.hpp b/src/coreclr/vm/assemblynative.hpp index 8187cfe42ff86c..c0aa373d8d1867 100644 --- a/src/coreclr/vm/assemblynative.hpp +++ b/src/coreclr/vm/assemblynative.hpp @@ -149,6 +149,7 @@ extern "C" void QCALLTYPE AssemblyName_InitializeAssemblySpec(NativeAssemblyName struct CallbackContext final { OBJECTREF _currAssembly; + OBJECTREF _groupType; OBJECTREF _externalTypeMap; OBJECTREF _proxyTypeMap; OBJECTREF _creationException; diff --git a/src/coreclr/vm/binder.cpp b/src/coreclr/vm/binder.cpp index eb919caf5ed03d..0a2326d1143def 100644 --- a/src/coreclr/vm/binder.cpp +++ b/src/coreclr/vm/binder.cpp @@ -23,6 +23,7 @@ #include "configuration.h" #include "conditionalweaktable.h" #include "interoplibinterface_comwrappers.h" +#include "assemblynative.hpp" // // Retrieve structures from ID. diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 185cae0dd920bc..2593bc8293bbe5 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -453,6 +453,9 @@ DEFINE_METHOD(OBJCMARSHAL, AVAILABLEUNHANDLEDEXCEPTIONPROPAGATION, AvailableUn DEFINE_METHOD(OBJCMARSHAL, INVOKEUNHANDLEDEXCEPTIONPROPAGATION, InvokeUnhandledExceptionPropagation, SM_Exception_Obj_RefIntPtr_RetVoidPtr) #endif // FEATURE_OBJCMARSHAL +DEFINE_CLASS_U(Interop, TypeMapLazyDictionary+CallbackContext, CallbackContext) +DEFINE_FIELD_U(_currAssembly, CallbackContext, _currAssembly) + DEFINE_CLASS(IENUMERATOR, Collections, IEnumerator) DEFINE_CLASS(IENUMERABLE, Collections, IEnumerable) From 6a199284347e5627bfde0db798f86de84f576c95 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 12 Feb 2026 11:46:17 -0800 Subject: [PATCH 08/19] Get the code to the point of loading the R2R image. We still have some issues with the hash-table writing (reading entries in some cases seems to result in incorrect reads). --- .../TypeMapLazyDictionary.NativeAot.cs | 7 ++- .../SortableDependencyNode.cs | 2 +- .../ReadyToRun/ManifestMetadataTableNode.cs | 1 - .../ReadyToRun/ReadyToRunModuleSignature.cs | 20 +++++++- .../Compiler/TypeMapAssemblyTargetsNode.cs | 18 ++++--- src/coreclr/vm/assemblynative.cpp | 7 +-- src/coreclr/vm/readytoruninfo.cpp | 48 +++++++++++++++---- .../InteropServices/TypeMapLazyDictionary.cs | 12 ++++- 8 files changed, 86 insertions(+), 29 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.NativeAot.cs index 4ace72371f0001..7f41a30cb81095 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.NativeAot.cs @@ -106,12 +106,11 @@ public static IReadOnlyDictionary CreateProxyTypeMap(RuntimeType typ 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.RhGetModuleSection(module, (uint)sectionType, &pBlob, &cbBlob)) + if (pBlob != null) { - reader = new NativeReader(pBlob, cbBlob); + reader = new NativeReader(pBlob, (uint)length); return true; } diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs index b515c093ba5240..525fdca00dc444 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs @@ -61,7 +61,7 @@ protected internal enum ObjectNodeOrder // Win32ResourcesNode, CorHeaderNode, - ReadyToRunHeaderNode, + GlobalHeaderNode, ReadyToRunAssemblyHeaderNode, ImportSectionsTableNode, ImportSectionNode, diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs index 26ac359f10f6ec..3ecf636ee1bee3 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs @@ -253,7 +253,6 @@ private int ModuleToIndexInternal(IEcmaModule module) } else { - Debug.Assert(_nodeFactory.CompilationModuleGroup.CrossModuleInlineableModule(emodule)); _manifestAssemblyMvids.Add(default(Guid)); } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs index 1e0cd48c38963f..25d41cda690bc0 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs @@ -19,10 +19,26 @@ public class ReadyToRunModuleSignature(IEcmaModule module) : Signature public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { - ObjectDataSignatureBuilder builder = new ObjectDataSignatureBuilder(factory, relocsOnly); + // Don't use ObjectDataSignatureBuilder as that validates cross-module references. + // For just the module helper fixup, we aren't bound by that restriction. + ObjectDataBuilder builder = new(factory, relocsOnly); builder.AddSymbol(this); - builder.EmitFixup(factory, ReadyToRunFixupKind.Helper, Module, factory.SignatureContext); + + if (relocsOnly) + { + return builder.ToObjectData(); + } + + if (Module == factory.SignatureContext) + { + builder.EmitByte((byte)ReadyToRunFixupKind.Helper); + } + else + { + builder.EmitByte((byte)(ReadyToRunFixupKind.Helper | ReadyToRunFixupKind.ModuleOverride)); + } builder.EmitUInt((uint)ReadyToRunHelper.Module); + return builder.ToObjectData(); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/TypeMapAssemblyTargetsNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/TypeMapAssemblyTargetsNode.cs index b867b6fd78769f..38a50c71fbc5c2 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/TypeMapAssemblyTargetsNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/TypeMapAssemblyTargetsNode.cs @@ -26,7 +26,7 @@ public TypeMapAssemblyTargetsNode(TypeMapMetadata assemblyTypeMaps, ImportRefere public override bool StaticDependenciesAreComputed => true; - public int Offset => throw new System.NotImplementedException(); + public int Offset => 0; protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { @@ -45,10 +45,18 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData([], [], 1, [this]); + + ObjectDataBuilder builder = new(factory, relocsOnly); + builder.AddSymbol(this); + NativeWriter writer = new(); Section section = writer.NewSection(); VertexHashtable table = new(); + section.Place(table); foreach (var map in _assemblyTypeMaps.Maps) { @@ -60,14 +68,10 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) Vertex targetModuleVertex = _importReferenceProvider.EncodeReferenceToModule(writer, targetModule); modules.Append(targetModuleVertex); } - Vertex modulesVertex = writer.GetTuple(groupTypeVertex, modules); - table.Append((uint)groupType.GetHashCode(), modulesVertex); + Vertex entry = writer.GetTuple(groupTypeVertex, modules); + table.Append((uint)groupType.GetHashCode(), section.Place(entry)); } - section.Place(table); - - ObjectDataBuilder builder = new(); - builder.AddSymbol(this); builder.EmitBytes(writer.Save()); return builder.ToObjectData(); } diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp index 167def617806a1..d151acbdc82ff1 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -1549,13 +1549,14 @@ namespace PTR_Module pModule = pAssembly->GetModule(); if (!pModule->IsReadyToRun()) { - return false; + return true; } PTR_ReadyToRunInfo pR2RInfo = pModule->GetReadyToRunInfo(); if (filter(pR2RInfo)) { + *pHasPrecachedInfo = true; return callback(pR2RInfo); } return true; @@ -1769,7 +1770,7 @@ extern "C" void QCALLTYPE TypeMapLazyDictionary_ProcessAttributes( // We will only process the specific type maps if we have a callback to process // the entry and the precached map was not calculated for this module. - if (newExternalTypeEntry != NULL && !hasPrecachedInfo) + if (newExternalTypeEntry != NULL) { MappingsProcessor onExternalType{ newExternalTypeEntry, context }; ProcessTypeMapAttribute( @@ -1779,7 +1780,7 @@ extern "C" void QCALLTYPE TypeMapLazyDictionary_ProcessAttributes( currAssembly); } - if (newProxyTypeEntry != NULL && !hasPrecachedInfo) + if (newProxyTypeEntry != NULL) { MappingsProcessor onProxyType{ newProxyTypeEntry, context }; ProcessTypeMapAttribute( diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index fdb3750f67f35f..8ad0da6f48d363 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -1643,7 +1643,10 @@ bool ReadyToRunInfo::HasPrecachedExternalTypeMap(MethodTable* pGroupTypeMT) NativeParser entryParser; while (lookup.GetNext(entryParser)) { - if (GetTypeHandleForNativeFormatFixupReference(this, m_pModule, entryParser.GetUnsigned(), entryParser.GetUnsigned()) == TypeHandle(pGroupTypeMT)) + uint32_t importSection = entryParser.GetUnsigned(); + uint32_t fixupIndex = entryParser.GetUnsigned(); + TypeHandle typeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, importSection, fixupIndex); + if (typeHandle == TypeHandle(pGroupTypeMT)) { return true; } @@ -1666,7 +1669,10 @@ TypeHandle ReadyToRunInfo::FindPrecachedExternalTypeMapEntry(MethodTable* pGroup NativeParser entryParser; while (lookup.GetNext(entryParser)) { - if (GetTypeHandleForNativeFormatFixupReference(this, m_pModule, entryParser.GetUnsigned(), entryParser.GetUnsigned()) != TypeHandle(pGroupType)) + uint32_t groupTypeImportSection = entryParser.GetUnsigned(); + uint32_t groupTypeFixupIndex = entryParser.GetUnsigned(); + TypeHandle groupTypeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, groupTypeImportSection, groupTypeFixupIndex); + if (groupTypeHandle != TypeHandle(pGroupType)) { continue; } @@ -1686,7 +1692,10 @@ TypeHandle ReadyToRunInfo::FindPrecachedExternalTypeMapEntry(MethodTable* pGroup { if (typeMapEntryParser.StringEquals(pKey, (uint32_t)strlen(pKey))) { - return GetTypeHandleForNativeFormatFixupReference(this, m_pModule, typeMapEntryParser.GetUnsigned(), typeMapEntryParser.GetUnsigned()); + typeMapEntryParser.SkipString(); + uint32_t resultImportSection = typeMapEntryParser.GetUnsigned(); + uint32_t resultFixupIndex = typeMapEntryParser.GetUnsigned(); + return GetTypeHandleForNativeFormatFixupReference(this, m_pModule, resultImportSection, resultFixupIndex); } } @@ -1711,7 +1720,10 @@ bool ReadyToRunInfo::HasPrecachedProxyTypeMap(MethodTable* pGroupType) NativeParser entryParser; while (lookup.GetNext(entryParser)) { - if (GetTypeHandleForNativeFormatFixupReference(this, m_pModule, entryParser.GetUnsigned(), entryParser.GetUnsigned()) == TypeHandle(pGroupType)) + uint32_t importSection = entryParser.GetUnsigned(); + uint32_t fixupIndex = entryParser.GetUnsigned(); + TypeHandle typeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, importSection, fixupIndex); + if (typeHandle == TypeHandle(pGroupType)) { return true; } @@ -1734,7 +1746,10 @@ TypeHandle ReadyToRunInfo::FindPrecachedProxyTypeMapEntry(MethodTable* pGroupTyp NativeParser entryParser; while (lookup.GetNext(entryParser)) { - if (GetTypeHandleForNativeFormatFixupReference(this, m_pModule, entryParser.GetUnsigned(), entryParser.GetUnsigned()) != TypeHandle(pGroupType)) + uint32_t groupTypeImportSection = entryParser.GetUnsigned(); + uint32_t groupTypeFixupIndex = entryParser.GetUnsigned(); + TypeHandle groupTypeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, groupTypeImportSection, groupTypeFixupIndex); + if (groupTypeHandle != TypeHandle(pGroupType)) { continue; } @@ -1752,12 +1767,17 @@ TypeHandle ReadyToRunInfo::FindPrecachedProxyTypeMapEntry(MethodTable* pGroupTyp NativeParser typeMapEntryParser; while (typeMapLookup.GetNext(typeMapEntryParser)) { - if (GetTypeHandleForNativeFormatFixupReference(this, m_pModule, entryParser.GetUnsigned(), entryParser.GetUnsigned()) != TypeHandle(pGroupType)) + uint32_t keyImportSection = entryParser.GetUnsigned(); + uint32_t keyFixupIndex = entryParser.GetUnsigned(); + TypeHandle keyTypeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, keyImportSection, keyFixupIndex); + if (keyTypeHandle != TypeHandle(pGroupType)) { continue; } - return GetTypeHandleForNativeFormatFixupReference(this, m_pModule, typeMapEntryParser.GetUnsigned(), typeMapEntryParser.GetUnsigned()); + uint32_t resultImportSection = typeMapEntryParser.GetUnsigned(); + uint32_t resultFixupIndex = typeMapEntryParser.GetUnsigned(); + return GetTypeHandleForNativeFormatFixupReference(this, m_pModule, resultImportSection, resultFixupIndex); } // No matching entry found in the table. @@ -1781,7 +1801,10 @@ bool ReadyToRunInfo::HasTypeMapAssemblyTargets(MethodTable* pGroupType, COUNT_T* NativeParser entryParser; while (lookup.GetNext(entryParser)) { - if (GetTypeHandleForNativeFormatFixupReference(this, m_pModule, entryParser.GetUnsigned(), entryParser.GetUnsigned()) == TypeHandle(pGroupType)) + uint32_t importSection = entryParser.GetUnsigned(); + uint32_t fixupIndex = entryParser.GetUnsigned(); + TypeHandle typeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, importSection, fixupIndex); + if (typeHandle == TypeHandle(pGroupType)) { *pCount = entryParser.GetUnsigned(); return true; @@ -1805,7 +1828,10 @@ COUNT_T ReadyToRunInfo::GetTypeMapAssemblyTargets(MethodTable* pGroupType, Modul NativeParser entryParser; while (lookup.GetNext(entryParser)) { - if (GetTypeHandleForNativeFormatFixupReference(this, m_pModule, entryParser.GetUnsigned(), entryParser.GetUnsigned()) != TypeHandle(pGroupType)) + uint32_t importSection = entryParser.GetUnsigned(); + uint32_t fixupIndex = entryParser.GetUnsigned(); + TypeHandle typeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, importSection, fixupIndex); + if (typeHandle == TypeHandle(pGroupType)) { continue; } @@ -1814,7 +1840,9 @@ COUNT_T ReadyToRunInfo::GetTypeMapAssemblyTargets(MethodTable* pGroupType, Modul COUNT_T resultCount = min(numTargets, count); for (COUNT_T i = 0; i < resultCount; i++) { - pTargetModules[i] = GetModuleForNativeFormatFixupReference(this, m_pModule, entryParser.GetUnsigned(), entryParser.GetUnsigned()); + uint32_t moduleImportSection = entryParser.GetUnsigned(); + uint32_t moduleFixupIndex = entryParser.GetUnsigned(); + pTargetModules[i] = GetModuleForNativeFormatFixupReference(this, m_pModule, moduleImportSection, moduleFixupIndex); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs index 21b344190a5e8e..22e237239ab473 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs @@ -333,7 +333,17 @@ public void AddPreCachedModule(RuntimeModule module) _preCachedModules.Add(module); } - public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out Type value) => TryGetOrLoadType(key, out value); + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out Type value) + { + foreach (RuntimeModule module in _preCachedModules) + { + if (TryGetOrLoadTypeFromPreCachedDictionary(module, key, out value)) + { + return true; + } + } + return TryGetOrLoadType(key, out value); + } // Not supported to avoid exposing TypeMap entries in a manner that // would violate invariants the Trimmer is attempting to enforce. From e6ffdebfa363a8a93e167ff8da0211e9b484d2ea Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 12 Feb 2026 14:46:32 -0800 Subject: [PATCH 09/19] Fix inverted logic on assembly target parsing --- src/coreclr/vm/readytoruninfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index 8ad0da6f48d363..f7f8a000753914 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -1831,7 +1831,7 @@ COUNT_T ReadyToRunInfo::GetTypeMapAssemblyTargets(MethodTable* pGroupType, Modul uint32_t importSection = entryParser.GetUnsigned(); uint32_t fixupIndex = entryParser.GetUnsigned(); TypeHandle typeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, importSection, fixupIndex); - if (typeHandle == TypeHandle(pGroupType)) + if (typeHandle != TypeHandle(pGroupType)) { continue; } From c593f18a9e7c1c3ebc469ec24ee9a1b4e3537b9d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 12 Feb 2026 14:56:17 -0800 Subject: [PATCH 10/19] Basic PR feedback --- .../tools/Common/Compiler/ExternalTypeMapObjectNode.cs | 2 +- .../tools/Common/Compiler/ProxyTypeMapObjectNode.cs | 2 +- src/coreclr/vm/readytoruninfo.cpp | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ExternalTypeMapObjectNode.cs b/src/coreclr/tools/Common/Compiler/ExternalTypeMapObjectNode.cs index bf5113d93be6a8..b9231da9dd4447 100644 --- a/src/coreclr/tools/Common/Compiler/ExternalTypeMapObjectNode.cs +++ b/src/coreclr/tools/Common/Compiler/ExternalTypeMapObjectNode.cs @@ -12,7 +12,7 @@ namespace ILCompiler.DependencyAnalysis { - public sealed class ExternalTypeMapObjectNode(TypeMapManager manager, INativeFormatTypeReferenceProvider externalReferences) : ObjectNode, ISymbolDefinitionNode, INodeWithSize + public sealed class ExternalTypeMapObjectNode(TypeMapManager manager, INativeFormatTypeReferenceProvider externalReferences) : ObjectNode, ISymbolDefinitionNode { public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { diff --git a/src/coreclr/tools/Common/Compiler/ProxyTypeMapObjectNode.cs b/src/coreclr/tools/Common/Compiler/ProxyTypeMapObjectNode.cs index eb83780a32b5c3..a3dc386877ffa1 100644 --- a/src/coreclr/tools/Common/Compiler/ProxyTypeMapObjectNode.cs +++ b/src/coreclr/tools/Common/Compiler/ProxyTypeMapObjectNode.cs @@ -13,7 +13,7 @@ namespace ILCompiler.DependencyAnalysis { - internal sealed class ProxyTypeMapObjectNode(TypeMapManager manager, INativeFormatTypeReferenceProvider externalReferences) : ObjectNode, ISymbolDefinitionNode, INodeWithSize + internal sealed class ProxyTypeMapObjectNode(TypeMapManager manager, INativeFormatTypeReferenceProvider externalReferences) : ObjectNode, ISymbolDefinitionNode { public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index f7f8a000753914..dc269ab5cad5e5 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -1767,10 +1767,10 @@ TypeHandle ReadyToRunInfo::FindPrecachedProxyTypeMapEntry(MethodTable* pGroupTyp NativeParser typeMapEntryParser; while (typeMapLookup.GetNext(typeMapEntryParser)) { - uint32_t keyImportSection = entryParser.GetUnsigned(); - uint32_t keyFixupIndex = entryParser.GetUnsigned(); + uint32_t keyImportSection = typeMapEntryParser.GetUnsigned(); + uint32_t keyFixupIndex = typeMapEntryParser.GetUnsigned(); TypeHandle keyTypeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, keyImportSection, keyFixupIndex); - if (keyTypeHandle != TypeHandle(pGroupType)) + if (keyTypeHandle != key) { continue; } @@ -1844,6 +1844,8 @@ COUNT_T ReadyToRunInfo::GetTypeMapAssemblyTargets(MethodTable* pGroupType, Modul uint32_t moduleFixupIndex = entryParser.GetUnsigned(); pTargetModules[i] = GetModuleForNativeFormatFixupReference(this, m_pModule, moduleImportSection, moduleFixupIndex); } + + return resultCount; } return 0; From bd71ab45fc97da504545034ac8609fb6be37f269 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 12 Feb 2026 16:16:39 -0800 Subject: [PATCH 11/19] Use a new section to store the data for each hash table to avoid issues with negative offsets. Fix issues in handling invalid maps. Fix an issue in R2RModuleSignature (though that approach may still have issues). --- .../ReadyToRun/ReadyToRunModuleSignature.cs | 3 ++- .../Compiler/ReadyToRunExternalTypeMapNode.cs | 4 +++- .../Compiler/ReadyToRunProxyTypeMapNode.cs | 4 +++- src/coreclr/vm/assemblynative.cpp | 14 +++++++++++--- src/coreclr/vm/readytoruninfo.cpp | 6 ++++-- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs index 25d41cda690bc0..f6e9a6669f3379 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs @@ -36,8 +36,9 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) else { builder.EmitByte((byte)(ReadyToRunFixupKind.Helper | ReadyToRunFixupKind.ModuleOverride)); + builder.EmitCompressedUInt((uint)factory.ManifestMetadataTable.ModuleToIndex(Module)); } - builder.EmitUInt((uint)ReadyToRunHelper.Module); + builder.EmitCompressedUInt((uint)ReadyToRunHelper.Module); return builder.ToObjectData(); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs index 2916ed538b9bb3..de8ddd46052c23 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunExternalTypeMapNode.cs @@ -49,12 +49,14 @@ public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section se VertexHashtable typeMapHashTable = new(); + Section typeMapEntriesSection = writer.NewSection(); + foreach ((string key, (TypeDesc type, _)) in map.TypeMap) { Vertex keyVertex = writer.GetStringConstant(key); Vertex valueVertex = externalReferences.EncodeReferenceToType(writer, type); Vertex entry = writer.GetTuple(keyVertex, valueVertex); - typeMapHashTable.Append((uint)TypeHashingAlgorithms.ComputeNameHashCode(key), section.Place(entry)); + typeMapHashTable.Append((uint)TypeHashingAlgorithms.ComputeNameHashCode(key), typeMapEntriesSection.Place(entry)); } Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProxyTypeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProxyTypeMapNode.cs index 58e6adb4c68844..ee1951902deef5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProxyTypeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProxyTypeMapNode.cs @@ -51,12 +51,14 @@ public Vertex CreateTypeMap(NodeFactory factory, NativeWriter writer, Section se VertexHashtable typeMapHashTable = new(); + Section typeMapEntriesSection = writer.NewSection(); + foreach ((TypeDesc type, TypeDesc targetType) in map.TypeMap) { Vertex keyVertex = ProxyReferences.EncodeReferenceToType(writer, type); Vertex valueVertex = ProxyReferences.EncodeReferenceToType(writer, targetType); Vertex entry = writer.GetTuple(keyVertex, valueVertex); - typeMapHashTable.Append((uint)type.GetHashCode(), section.Place(entry)); + typeMapHashTable.Append((uint)type.GetHashCode(), typeMapEntriesSection.Place(entry)); } Vertex typeMapStateVertex = writer.GetUnsignedConstant(1); // Valid type map state diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp index d151acbdc82ff1..764c4613a1e4e9 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -1549,6 +1549,7 @@ namespace PTR_Module pModule = pAssembly->GetModule(); if (!pModule->IsReadyToRun()) { + *pHasPrecachedInfo = false; return true; } @@ -1718,7 +1719,9 @@ extern "C" void QCALLTYPE TypeMapLazyDictionary_ProcessAttributes( bool hasPrecachedInfo = false; #ifdef FEATURE_READYTORUN - if (!ProcessPrecachedTypeMapInfo( + // Only process the external type map if requested. + if (newExternalTypeEntry != nullptr + && !ProcessPrecachedTypeMapInfo( [=](PTR_ReadyToRunInfo pR2RInfo) { return pR2RInfo->HasPrecachedExternalTypeMap(groupTypeMT); }, [=](PTR_ReadyToRunInfo pR2RInfo) { return newPrecachedExternalTypeMap(context); }, currAssembly, @@ -1728,7 +1731,9 @@ extern "C" void QCALLTYPE TypeMapLazyDictionary_ProcessAttributes( return; } - if (!ProcessPrecachedTypeMapInfo( + // Only process the proxy type map if requested. + if (newProxyTypeEntry != nullptr + && !ProcessPrecachedTypeMapInfo( [=](PTR_ReadyToRunInfo pR2RInfo) { return pR2RInfo->HasPrecachedProxyTypeMap(groupTypeMT); }, [=](PTR_ReadyToRunInfo pR2RInfo) { return newPrecachedProxyTypeMap(context); }, currAssembly, @@ -1738,6 +1743,9 @@ extern "C" void QCALLTYPE TypeMapLazyDictionary_ProcessAttributes( return; } + // Don't count having external assembly targets as having precached info. + // We don't want valid external assembly targets to make us mistakenly think that the pre-cached maps are valid when they are not. + bool hasExternalTypeMapAssemblyTargets; COUNT_T assemblyTargetCount = 0; ProcessPrecachedTypeMapInfo( [=, &assemblyTargetCount](PTR_ReadyToRunInfo pR2RInfo) { return pR2RInfo->HasTypeMapAssemblyTargets(groupTypeMT, &assemblyTargetCount); }, @@ -1755,7 +1763,7 @@ extern "C" void QCALLTYPE TypeMapLazyDictionary_ProcessAttributes( return true; }, currAssembly, - &hasPrecachedInfo + &hasExternalTypeMapAssemblyTargets ); #endif // FEATURE_READYTORUN diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index dc269ab5cad5e5..448821cb440863 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -1648,7 +1648,8 @@ bool ReadyToRunInfo::HasPrecachedExternalTypeMap(MethodTable* pGroupTypeMT) TypeHandle typeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, importSection, fixupIndex); if (typeHandle == TypeHandle(pGroupTypeMT)) { - return true; + // A non-zero value next in the entry indicates that the table is valid. + return entryParser.GetUnsigned() != 0; } } return false; @@ -1725,7 +1726,8 @@ bool ReadyToRunInfo::HasPrecachedProxyTypeMap(MethodTable* pGroupType) TypeHandle typeHandle = GetTypeHandleForNativeFormatFixupReference(this, m_pModule, importSection, fixupIndex); if (typeHandle == TypeHandle(pGroupType)) { - return true; + // A non-zero value next in the entry indicates that the table is valid. + return entryParser.GetUnsigned() != 0; } } return false; From 8cf27807b9071373bca411df4419ace3371962ef Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 13 Feb 2026 11:33:06 -0800 Subject: [PATCH 12/19] Use the right compressed integer format (we have two) --- .../ReadyToRun/ReadyToRunModuleSignature.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs index f6e9a6669f3379..13a23c4f6daf31 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ReadyToRunModuleSignature.cs @@ -19,9 +19,7 @@ public class ReadyToRunModuleSignature(IEcmaModule module) : Signature public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { - // Don't use ObjectDataSignatureBuilder as that validates cross-module references. - // For just the module helper fixup, we aren't bound by that restriction. - ObjectDataBuilder builder = new(factory, relocsOnly); + ObjectDataSignatureBuilder builder = new(factory, relocsOnly); builder.AddSymbol(this); if (relocsOnly) @@ -29,6 +27,9 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) return builder.ToObjectData(); } + // Don't use EmitFixup as that validates cross-module references. + // For just the module helper fixup, we aren't bound by that restriction + // as the module helper's result is not affected by changes in the target module. if (Module == factory.SignatureContext) { builder.EmitByte((byte)ReadyToRunFixupKind.Helper); @@ -36,9 +37,9 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) else { builder.EmitByte((byte)(ReadyToRunFixupKind.Helper | ReadyToRunFixupKind.ModuleOverride)); - builder.EmitCompressedUInt((uint)factory.ManifestMetadataTable.ModuleToIndex(Module)); + builder.EmitUInt((uint)factory.ManifestMetadataTable.ModuleToIndex(Module)); } - builder.EmitCompressedUInt((uint)ReadyToRunHelper.Module); + builder.EmitUInt((uint)ReadyToRunHelper.Module); return builder.ToObjectData(); } From 585b559e94743953c66d528813aad99ae8d3754a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 13 Feb 2026 11:52:45 -0800 Subject: [PATCH 13/19] Update r2rdump to handle the assembly ref we add now in a non-composite scenario --- .../ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index a960fcfdee3a2a..f1f55173e7d817 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -1281,9 +1281,15 @@ public Guid GetAssemblyMvid(int assemblyIndex) } return new Guid(mvidBytes); } + else if (assemblyIndex != 0) + { + // It's possible to have an index for an assembly in a non-composite image in one case: + // If the assembly index is only used for a module fixup, then we won't have an MVID for it + // as we haven't taken a dependency on any image details, just existence of the assembly. + return default(Guid); + } else { - Debug.Assert(assemblyIndex == 0); MetadataReader mdReader = GetGlobalMetadata().MetadataReader; return mdReader.GetGuid(mdReader.GetModuleDefinition().Mvid); } From 9a46b19c920205fe7fd2ce5c20cba14f3b09dc77 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 13 Feb 2026 12:19:59 -0800 Subject: [PATCH 14/19] zero-initialize returns for the non-r2r case --- src/coreclr/vm/assemblynative.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp index 764c4613a1e4e9..d9e115625d12f5 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -1812,7 +1812,7 @@ extern "C" TADDR QCALLTYPE TypeMapLazyDictionary_FindPrecachedExternalTypeMapEnt _ASSERTE(pModule != NULL); _ASSERTE(!pGroupType.AsTypeHandle().IsNull()); - TypeHandle resultTypeHnd; + TypeHandle resultTypeHnd{}; BEGIN_QCALL; @@ -1845,7 +1845,7 @@ extern "C" TADDR QCALLTYPE TypeMapLazyDictionary_FindPrecachedProxyTypeMapEntry( _ASSERTE(pModule != NULL); _ASSERTE(!pGroupType.AsTypeHandle().IsNull()); - TypeHandle resultTypeHnd; + TypeHandle resultTypeHnd{}; BEGIN_QCALL; From ea59e9693657a8d1edda110eac88f4e73d05dc41 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 13 Feb 2026 16:52:12 -0800 Subject: [PATCH 15/19] Fix typo --- src/coreclr/vm/nativeformatreader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/nativeformatreader.h b/src/coreclr/vm/nativeformatreader.h index 5c95f0a9d1b809..b49e7f53a8dc3f 100644 --- a/src/coreclr/vm/nativeformatreader.h +++ b/src/coreclr/vm/nativeformatreader.h @@ -301,7 +301,7 @@ namespace NativeFormat offset = DecodeUnsigned(offset, &numBytes); uint endOffset = offset + numBytes; - if (endOffset < numBytes || offset > _size) + if (endOffset < numBytes || endOffset > _size) ThrowBadImageFormatException(); if (numBytes != valueLength) From 86e9eb31c73e2c0b662b133101fc79564c11005b Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 17 Feb 2026 12:04:07 -0800 Subject: [PATCH 16/19] Fixclang build failures --- src/coreclr/vm/readytoruninfo.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index 448821cb440863..9dc3348099659f 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -935,19 +935,22 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocat IMAGE_DATA_DIRECTORY* pExternalTypeMapsDir = m_component.FindSection(ReadyToRunSectionType::ExternalTypeMaps); if (pExternalTypeMapsDir != NULL) { - m_externalTypeMaps = NativeHashtable(NativeParser(&m_nativeReader, pExternalTypeMapsDir->VirtualAddress)); + NativeParser parser = NativeParser(&m_nativeReader, pExternalTypeMapsDir->VirtualAddress); + m_externalTypeMaps = NativeHashtable(parser); } IMAGE_DATA_DIRECTORY* pProxyTypeMapsDir = m_component.FindSection(ReadyToRunSectionType::ProxyTypeMaps); if (pProxyTypeMapsDir != NULL) { - m_proxyTypeMaps = NativeHashtable(NativeParser(&m_nativeReader, pProxyTypeMapsDir->VirtualAddress)); + NativeParser parser = NativeParser(&m_nativeReader, pProxyTypeMapsDir->VirtualAddress); + m_proxyTypeMaps = NativeHashtable(parser); } IMAGE_DATA_DIRECTORY* pTypeMapAssemblyTargetsDir = m_component.FindSection(ReadyToRunSectionType::TypeMapAssemblyTargets); if (pTypeMapAssemblyTargetsDir != NULL) { - m_typeMapAssemblyTargets = NativeHashtable(NativeParser(&m_nativeReader, pTypeMapAssemblyTargetsDir->VirtualAddress)); + NativeParser parser = NativeParser(&m_nativeReader, pTypeMapAssemblyTargetsDir->VirtualAddress); + m_typeMapAssemblyTargets = NativeHashtable(parser); } } From d4d05daf2587dc29bdfad9e3d5be0e746049be9a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 17 Feb 2026 16:38:21 -0800 Subject: [PATCH 17/19] Copilot feedback --- .../DependencyAnalysis/ImportReferenceProvider.cs | 9 +++------ .../DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs | 7 ------- src/coreclr/vm/assemblynative.hpp | 1 - .../Runtime/InteropServices/TypeMapLazyDictionary.cs | 8 +------- 4 files changed, 4 insertions(+), 21 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs index 1cfef8c4636d80..1fdb5029a286d5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ImportReferenceProvider.cs @@ -38,16 +38,13 @@ internal Vertex EncodeReferenceToModule(NativeWriter writer, ModuleDesc module) return writer.GetTuple(writer.GetUnsignedConstant((uint)typeImport.Table.IndexFromBeginningOfArray), writer.GetUnsignedConstant((uint)typeImport.IndexFromBeginningOfArray)); } - Vertex INativeFormatTypeReferenceProvider.EncodeReferenceToMethod(NativeWriter writer, MethodDesc method) => throw new NotImplementedException(); - Vertex INativeFormatTypeReferenceProvider.EncodeReferenceToType(NativeWriter writer, TypeDesc type) - { - Import typeImport = GetImportToType(type); - return writer.GetTuple(writer.GetUnsignedConstant((uint)typeImport.Table.IndexFromBeginningOfArray), writer.GetUnsignedConstant((uint)typeImport.IndexFromBeginningOfArray)); - } internal Vertex EncodeReferenceToType(NativeWriter writer, TypeDesc type) { Import typeImport = GetImportToType(type); return writer.GetTuple(writer.GetUnsignedConstant((uint)typeImport.Table.IndexFromBeginningOfArray), writer.GetUnsignedConstant((uint)typeImport.IndexFromBeginningOfArray)); } + + Vertex INativeFormatTypeReferenceProvider.EncodeReferenceToMethod(NativeWriter writer, MethodDesc method) => throw new NotImplementedException(); + Vertex INativeFormatTypeReferenceProvider.EncodeReferenceToType(NativeWriter writer, TypeDesc type) => EncodeReferenceToType(writer, type); } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs index b65e1b376d04c1..d846e4c36d84b8 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs @@ -198,13 +198,6 @@ private void CreateNodeCaches() public ISymbolNode ContinuationTypeSymbol(AsyncContinuationType key) { return _continuationTypeFixups.GetOrAdd(key); - - _ecmaModuleFixupCache = new NodeCache(key => - { - return new PrecodeHelperImport( - _codegenNodeFactory, - new ReadyToRunModuleSignature(key)); - }); } private NodeCache _importStrings; diff --git a/src/coreclr/vm/assemblynative.hpp b/src/coreclr/vm/assemblynative.hpp index c0aa373d8d1867..8187cfe42ff86c 100644 --- a/src/coreclr/vm/assemblynative.hpp +++ b/src/coreclr/vm/assemblynative.hpp @@ -149,7 +149,6 @@ extern "C" void QCALLTYPE AssemblyName_InitializeAssemblySpec(NativeAssemblyName struct CallbackContext final { OBJECTREF _currAssembly; - OBJECTREF _groupType; OBJECTREF _externalTypeMap; OBJECTREF _proxyTypeMap; OBJECTREF _creationException; diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs index 22e237239ab473..e25db3e97aac99 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs @@ -22,16 +22,10 @@ internal static partial class TypeMapLazyDictionary private ref struct CallbackContext { private RuntimeAssembly? _currAssembly; - private readonly RuntimeType _groupType; private LazyExternalTypeDictionary? _externalTypeMap; private LazyProxyTypeDictionary? _proxyTypeMap; private ExceptionDispatchInfo? _creationException; - public CallbackContext(RuntimeType groupType) - { - _groupType = groupType; - } - public RuntimeAssembly CurrentAssembly { get @@ -254,7 +248,7 @@ private static unsafe CallbackContext CreateMaps( throw new InvalidOperationException(SR.InvalidOperation_TypeMapMissingEntryAssembly); } - CallbackContext context = new(groupType); + CallbackContext context = new(); ProcessAttributes( new QCallAssembly(ref startingAssembly), new QCallTypeHandle(ref groupType), From 557ae0e71cce68d09da564374d3f9b8e90871071 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 18 Feb 2026 16:51:09 -0800 Subject: [PATCH 18/19] Bring back groupType argument because its actually used --- src/coreclr/vm/assemblynative.hpp | 1 + .../Runtime/InteropServices/TypeMapLazyDictionary.cs | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/assemblynative.hpp b/src/coreclr/vm/assemblynative.hpp index 8187cfe42ff86c..c0aa373d8d1867 100644 --- a/src/coreclr/vm/assemblynative.hpp +++ b/src/coreclr/vm/assemblynative.hpp @@ -149,6 +149,7 @@ extern "C" void QCALLTYPE AssemblyName_InitializeAssemblySpec(NativeAssemblyName struct CallbackContext final { OBJECTREF _currAssembly; + OBJECTREF _groupType; OBJECTREF _externalTypeMap; OBJECTREF _proxyTypeMap; OBJECTREF _creationException; diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs index e25db3e97aac99..22e237239ab473 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.cs @@ -22,10 +22,16 @@ internal static partial class TypeMapLazyDictionary private ref struct CallbackContext { private RuntimeAssembly? _currAssembly; + private readonly RuntimeType _groupType; private LazyExternalTypeDictionary? _externalTypeMap; private LazyProxyTypeDictionary? _proxyTypeMap; private ExceptionDispatchInfo? _creationException; + public CallbackContext(RuntimeType groupType) + { + _groupType = groupType; + } + public RuntimeAssembly CurrentAssembly { get @@ -248,7 +254,7 @@ private static unsafe CallbackContext CreateMaps( throw new InvalidOperationException(SR.InvalidOperation_TypeMapMissingEntryAssembly); } - CallbackContext context = new(); + CallbackContext context = new(groupType); ProcessAttributes( new QCallAssembly(ref startingAssembly), new QCallTypeHandle(ref groupType), From d54e1ffa3d849b3c0215d02c2681840beecc1200 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 18 Feb 2026 16:52:27 -0800 Subject: [PATCH 19/19] Put nodes back in ReadOnly --- src/coreclr/tools/Common/Compiler/ExternalTypeMapObjectNode.cs | 2 +- src/coreclr/tools/Common/Compiler/ProxyTypeMapObjectNode.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ExternalTypeMapObjectNode.cs b/src/coreclr/tools/Common/Compiler/ExternalTypeMapObjectNode.cs index ac220dd0bfba2e..96b421624262be 100644 --- a/src/coreclr/tools/Common/Compiler/ExternalTypeMapObjectNode.cs +++ b/src/coreclr/tools/Common/Compiler/ExternalTypeMapObjectNode.cs @@ -42,7 +42,7 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) public int Offset => 0; public override bool IsShareable => false; - public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.DataSection; + public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.ReadOnlyDataSection; protected internal override int Phase => (int)ObjectNodePhase.Ordered; public override int ClassCode => (int)ObjectNodeOrder.ExternalTypeMapObjectNode; diff --git a/src/coreclr/tools/Common/Compiler/ProxyTypeMapObjectNode.cs b/src/coreclr/tools/Common/Compiler/ProxyTypeMapObjectNode.cs index 43e1bcc244fb12..765c606ac13059 100644 --- a/src/coreclr/tools/Common/Compiler/ProxyTypeMapObjectNode.cs +++ b/src/coreclr/tools/Common/Compiler/ProxyTypeMapObjectNode.cs @@ -44,7 +44,7 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) public int Offset => 0; public override bool IsShareable => false; - public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.DataSection; + public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.ReadOnlyDataSection; protected internal override int Phase => (int)ObjectNodePhase.Ordered; public override int ClassCode => (int)ObjectNodeOrder.ProxyTypeMapObjectNode;