From c9190495a7b261325968e697b8924689d6c8910c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 16 Jun 2026 19:06:08 +0900 Subject: [PATCH 1/4] Dead code --- .../InterfaceDispatchCellCachePointerFlags.cs | 22 ------------------- .../ILCompiler.Compiler.csproj | 3 --- 2 files changed, 25 deletions(-) delete mode 100644 src/coreclr/tools/Common/Internal/Runtime/InterfaceDispatchCellCachePointerFlags.cs diff --git a/src/coreclr/tools/Common/Internal/Runtime/InterfaceDispatchCellCachePointerFlags.cs b/src/coreclr/tools/Common/Internal/Runtime/InterfaceDispatchCellCachePointerFlags.cs deleted file mode 100644 index bff58e834d218e..00000000000000 --- a/src/coreclr/tools/Common/Internal/Runtime/InterfaceDispatchCellCachePointerFlags.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Internal.Runtime -{ - // - // The low 2 bits of the interface dispatch cell's cache pointer are treated specially so that we can avoid the - // need for extra fields on the type. - // - // Keep these in sync with the native copy in src\Native\Runtime\inc\rhbinder.h - // - public enum InterfaceDispatchCellCachePointerFlags - { - CachePointerPointsAtCache = 0x0, - CachePointerIsInterfacePointerOrMetadataToken = 0x1, - CachePointerIsIndirectedInterfaceRelativePointer = 0x2, - CachePointerIsInterfaceRelativePointer = 0x3, - CachePointerMask = 0x3, - CachePointerMaskShift = 0x2, - MaxVTableOffsetPlusOne = 0x1000 - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 67602a68ac221c..de3562566c9a71 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -750,9 +750,6 @@ Common\EETypeBuilderHelpers.cs - - Common\InterfaceDispatchCellCachePointerFlags.cs - Common\ModuleHeaders.cs From 4cebf2e48831eec308784715d5fdf16f9e591182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 16 Jun 2026 19:07:09 +0900 Subject: [PATCH 2/4] Refactor: route dispatch resolution to CoreLib --- src/coreclr/nativeaot/Bootstrap/main.cpp | 3 +- .../System/Runtime/CachedInterfaceDispatch.cs | 129 +++--------------- .../src/System/Runtime/InternalCalls.cs | 19 +-- .../src/Internal/Runtime/Dispatch.cs | 67 +++++++++ .../src/System.Private.CoreLib.csproj | 4 + .../TypeLoader/GenericDictionaryCell.cs | 10 +- .../src/System.Private.TypeLoader.csproj | 3 + .../src/Internal/Runtime/Dispatch.cs | 20 +++ .../Test.CoreLib/src/Test.CoreLib.csproj | 4 + .../Common/Internal/Runtime/DispatchCell.cs | 24 ++++ 10 files changed, 147 insertions(+), 136 deletions(-) create mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Dispatch.cs create mode 100644 src/coreclr/nativeaot/Test.CoreLib/src/Internal/Runtime/Dispatch.cs create mode 100644 src/coreclr/tools/Common/Internal/Runtime/DispatchCell.cs diff --git a/src/coreclr/nativeaot/Bootstrap/main.cpp b/src/coreclr/nativeaot/Bootstrap/main.cpp index 8b09cd4c7ca667..fe349e7ba4e03b 100644 --- a/src/coreclr/nativeaot/Bootstrap/main.cpp +++ b/src/coreclr/nativeaot/Bootstrap/main.cpp @@ -128,6 +128,7 @@ void* PalGetModuleHandleFromPointer(void* pointer); MANAGED_RUNTIME_EXPORT(GetRuntimeException) MANAGED_RUNTIME_EXPORT(RuntimeFailFast) MANAGED_RUNTIME_EXPORT(AppendExceptionStackFrame) +MANAGED_RUNTIME_EXPORT(ResolveDispatch) MANAGED_RUNTIME_EXPORT(GetSystemArrayEEType) MANAGED_RUNTIME_EXPORT(OnFirstChanceException) MANAGED_RUNTIME_EXPORT(OnUnhandledException) @@ -151,7 +152,7 @@ static const pfn c_classlibFunctions[] = { &MANAGED_RUNTIME_EXPORT_NAME(RuntimeFailFast), (pfn)&ThreadEntryPoint, &MANAGED_RUNTIME_EXPORT_NAME(AppendExceptionStackFrame), - nullptr, // &CheckStaticClassConstruction, + &MANAGED_RUNTIME_EXPORT_NAME(ResolveDispatch), &MANAGED_RUNTIME_EXPORT_NAME(GetSystemArrayEEType), &MANAGED_RUNTIME_EXPORT_NAME(OnFirstChanceException), &MANAGED_RUNTIME_EXPORT_NAME(OnUnhandledException), diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs index 797927aba22446..a496be1e1ff024 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs @@ -1,14 +1,11 @@ // 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.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using Internal.Runtime; -using Internal.Runtime.CompilerHelpers; namespace System.Runtime { @@ -65,15 +62,8 @@ public override bool Equals(object obj) } #endif - [StructLayout(LayoutKind.Sequential)] - private struct DispatchCell - { - public nint MethodTable; - public nint Code; - } - [RuntimeExport("RhpCidResolve")] - private static unsafe IntPtr RhpCidResolve(IntPtr callerTransitionBlockParam, IntPtr pCell) + private static IntPtr RhpCidResolve(IntPtr callerTransitionBlockParam, IntPtr pCell) { IntPtr locationOfThisPointer = callerTransitionBlockParam + TransitionBlock.GetThisOffset(); object pObject = *(object*)locationOfThisPointer; @@ -83,13 +73,8 @@ private static unsafe IntPtr RhpCidResolve(IntPtr callerTransitionBlockParam, In private static IntPtr RhpCidResolve_Worker(object pObject, IntPtr pCell) { - DispatchCellInfo cellInfo; - // We're passing the type manager of the object, but we need a type manager associated with - // the dispatch cell region. This is fine for now since we don't worry about multifile scenarios much. - // We'll need an API to find the right containing section in multimodule. - GetDispatchCellInfo(pObject.GetMethodTable()->TypeManager, pCell, out cellInfo); - - IntPtr pTargetCode = RhResolveDispatchWorker(pObject, (void*)pCell, ref cellInfo); + var resolver = (delegate*)InternalCalls.RhpGetClasslibFunctionFromEEType(pObject.GetMethodTable(), ClassLibFunctionId.ResolveDispatch); + IntPtr pTargetCode = resolver(pObject, pCell); if (pTargetCode != IntPtr.Zero) { return UpdateDispatchCellCache(pCell, pTargetCode, pObject.GetMethodTable()); @@ -100,52 +85,6 @@ private static IntPtr RhpCidResolve_Worker(object pObject, IntPtr pCell) return IntPtr.Zero; } - private static void GetDispatchCellInfo(TypeManagerHandle typeManager, IntPtr pCell, out DispatchCellInfo info) - { - IntPtr dispatchCellRegion = RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.InterfaceDispatchCellRegion, out int length); - if (pCell >= dispatchCellRegion && pCell < dispatchCellRegion + length) - { - // Static dispatch cell: find the info in the associated info region - nint cellIndex = (pCell - dispatchCellRegion) / sizeof(DispatchCell); - - IntPtr dispatchCellInfoRegion = RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.InterfaceDispatchCellInfoRegion, out _); - if (MethodTable.SupportsRelativePointers) - { - var dispatchCellInfo = (int*)dispatchCellInfoRegion; - info = new DispatchCellInfo - { - CellType = DispatchCellType.InterfaceAndSlot, - InterfaceType = (MethodTable*)ReadRelPtr32(dispatchCellInfo + (cellIndex * 2)), - InterfaceSlot = (ushort)*(dispatchCellInfo + (cellIndex * 2 + 1)) - }; - - static void* ReadRelPtr32(void* address) - => (byte*)address + *(int*)address; - } - else - { - var dispatchCellInfo = (nint*)dispatchCellInfoRegion; - info = new DispatchCellInfo - { - CellType = DispatchCellType.InterfaceAndSlot, - InterfaceType = (MethodTable*)(*(dispatchCellInfo + (cellIndex * 2))), - InterfaceSlot = (ushort)*(dispatchCellInfo + (cellIndex * 2 + 1)) - }; - } - - } - else - { - // Dynamically allocated dispatch cell: info is next to the dispatch cell - info = new DispatchCellInfo - { - CellType = DispatchCellType.InterfaceAndSlot, - InterfaceType = *(MethodTable**)(pCell + sizeof(DispatchCell)), - InterfaceSlot = (ushort)*(nint*)(pCell + sizeof(DispatchCell) + sizeof(MethodTable*)) - }; - } - } - private static IntPtr UpdateDispatchCellCache(IntPtr pCell, IntPtr pTargetCode, MethodTable* pInstanceType) { DispatchCell* pDispatchCell = (DispatchCell*)pCell; @@ -213,12 +152,7 @@ private static IntPtr RhpResolveInterfaceMethod(object pObject, IntPtr pCell) [RuntimeExport("RhResolveDispatch")] private static IntPtr RhResolveDispatch(object pObject, MethodTable* interfaceType, ushort slot) { - DispatchCellInfo cellInfo = default; - cellInfo.CellType = DispatchCellType.InterfaceAndSlot; - cellInfo.InterfaceType = interfaceType; - cellInfo.InterfaceSlot = slot; - - return RhResolveDispatchWorker(pObject, null, ref cellInfo); + return RhResolveDispatchWorker(pObject, interfaceType, slot); } [RuntimeExport("RhResolveDispatchOnType")] @@ -266,52 +200,23 @@ private static IntPtr RhResolveDynamicInterfaceCastableDispatchOnType(MethodTabl [AnalysisCharacteristic] private static extern bool DynamicInterfaceCastablePresent(); - private static unsafe IntPtr RhResolveDispatchWorker(object pObject, void* cell, ref DispatchCellInfo cellInfo) + internal static IntPtr RhResolveDispatchWorker(object pObject, MethodTable* pItfType, ushort itfSlotNumber) { // Type of object we're dispatching on. MethodTable* pInstanceType = pObject.GetMethodTable(); - - if (cellInfo.CellType == DispatchCellType.InterfaceAndSlot) - { - IntPtr pTargetCode = DispatchResolve.FindInterfaceMethodImplementationTarget(pInstanceType, - cellInfo.InterfaceType, - cellInfo.InterfaceSlot, - flags: default, - ppGenericContext: null); - if (DynamicInterfaceCastablePresent() && pTargetCode == IntPtr.Zero && pInstanceType->IsIDynamicInterfaceCastable) - { - // Dispatch not resolved through normal dispatch map, try using the IDynamicInterfaceCastable - // This will either give us the appropriate result, or throw. - pTargetCode = IDynamicInterfaceCastable.GetDynamicInterfaceImplementation((IDynamicInterfaceCastable)pObject, cellInfo.InterfaceType, cellInfo.InterfaceSlot); - Diagnostics.Debug.Assert(pTargetCode != IntPtr.Zero); - } - return pTargetCode; - } - else if (cellInfo.CellType == DispatchCellType.VTableOffset) - { - // Dereference VTable - return *(IntPtr*)(((byte*)pInstanceType) + cellInfo.VTableOffset); - } - else - { -#if SUPPORTS_NATIVE_METADATA_TYPE_LOADING_AND_SUPPORTS_TOKEN_BASED_DISPATCH_CELLS - // Attempt to convert dispatch cell to non-metadata form if we haven't acquired a cache for this cell yet - if (cellInfo.HasCache == 0) - { - cellInfo = InternalTypeLoaderCalls.ConvertMetadataTokenDispatch(InternalCalls.RhGetModuleFromPointer(cell), cellInfo); - if (cellInfo.CellType != DispatchCellType.MetadataToken) - { - return RhResolveDispatchWorker(pObject, cell, ref cellInfo); - } - } - - // If that failed, go down the metadata resolution path - return InternalTypeLoaderCalls.ResolveMetadataTokenDispatch(InternalCalls.RhGetModuleFromPointer(cell), (int)cellInfo.MetadataToken, new IntPtr(pInstanceType)); -#else - EH.FallbackFailFast(RhFailFastReason.InternalError, null); - return IntPtr.Zero; -#endif + IntPtr pTargetCode = DispatchResolve.FindInterfaceMethodImplementationTarget(pInstanceType, + pItfType, + itfSlotNumber, + flags: default, + ppGenericContext: null); + if (DynamicInterfaceCastablePresent() && pTargetCode == IntPtr.Zero && pInstanceType->IsIDynamicInterfaceCastable) + { + // Dispatch not resolved through normal dispatch map, try using the IDynamicInterfaceCastable + // This will either give us the appropriate result, or throw. + pTargetCode = IDynamicInterfaceCastable.GetDynamicInterfaceImplementation((IDynamicInterfaceCastable)pObject, pItfType, itfSlotNumber); + Diagnostics.Debug.Assert(pTargetCode != IntPtr.Zero); } + return pTargetCode; } } } diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs index c52d85a1a09fdc..5ffff86ee20f2c 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs @@ -14,23 +14,6 @@ namespace System.Runtime { - internal enum DispatchCellType - { - InterfaceAndSlot = 0x0, - MetadataToken = 0x1, - VTableOffset = 0x2, - } - - internal unsafe struct DispatchCellInfo - { - public DispatchCellType CellType; - public MethodTable* InterfaceType; - public ushort InterfaceSlot; - public byte HasCache; - public uint MetadataToken; - public uint VTableOffset; - } - // Constants used with RhpGetClasslibFunction, to indicate which classlib function // we are interested in. // Note: make sure you change the def in ICodeManager.h if you change this! @@ -40,7 +23,7 @@ internal enum ClassLibFunctionId FailFast = 1, ThreadEntryPoint = 2, AppendExceptionStackFrame = 3, - // unused = 4, + ResolveDispatch = 4, GetSystemArrayEEType = 5, OnFirstChance = 6, OnUnhandledException = 7, diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Dispatch.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Dispatch.cs new file mode 100644 index 00000000000000..316a15f85fd687 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Dispatch.cs @@ -0,0 +1,67 @@ +// 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.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using Internal.Runtime.CompilerHelpers; + +namespace Internal.Runtime +{ + internal static class Dispatch + { + [RuntimeExport("ResolveDispatch")] + internal static unsafe MethodTable* ResolveDispatch(object pObject, DispatchCell* pCell) + { + // Assume this is a static dispatch cell first + foreach (TypeManagerHandle typeManager in StartupCodeHelpers.GetLoadedModules()) + { + var pDispatchCellRegion = (DispatchCell*)RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.InterfaceDispatchCellRegion, out int length); + if (pCell >= pDispatchCellRegion && pCell < (byte*)pDispatchCellRegion + length) + { + return ResolveStaticInterfaceDispatch(typeManager, pObject, (nint)(pCell - pDispatchCellRegion)); + } + } + + // Not found statically: must be a dynamic dispatch cell + var pDynamicCell = (DynamicDispatchCell*)pCell; + return (MethodTable*)CachedInterfaceDispatch.RhResolveDispatchWorker(pObject, pDynamicCell->InterfaceType, (ushort)pDynamicCell->Slot); + } + + private static unsafe MethodTable* ResolveStaticInterfaceDispatch(TypeManagerHandle typeManager, object pObject, nint cellIndex) + { + IntPtr pDispatchCellInfoRegion = RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.InterfaceDispatchCellInfoRegion, out _); + if (MethodTable.SupportsRelativePointers) + { + var dispatchCellInfo = &((RelativeInterfaceDispatchInfo*)pDispatchCellInfoRegion)[cellIndex]; + return (MethodTable*)CachedInterfaceDispatch.RhResolveDispatchWorker(pObject, dispatchCellInfo->InterfaceType, (ushort)dispatchCellInfo->Slot); + } + else + { + var dispatchCellInfo = &((InterfaceDispatchInfo*)pDispatchCellInfoRegion)[cellIndex]; + return (MethodTable*)CachedInterfaceDispatch.RhResolveDispatchWorker(pObject, dispatchCellInfo->InterfaceType, (ushort)dispatchCellInfo->Slot); + } + } + + [StructLayout(LayoutKind.Sequential)] + private struct RelativeInterfaceDispatchInfo + { + private int _interfaceTypeRelPtr; + public int Slot; + + public unsafe MethodTable* InterfaceType + => (MethodTable*)((byte*)Unsafe.AsPointer(ref _interfaceTypeRelPtr) + _interfaceTypeRelPtr); + } + + [StructLayout(LayoutKind.Sequential)] + private struct InterfaceDispatchInfo + { + public unsafe MethodTable* InterfaceType; + private nint _slot; + + public int Slot => (int)_slot; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index ffa900b4b08198..d9a6ee34ba9ee8 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -51,6 +51,9 @@ Internal\Runtime\DehydratedData.cs + + Internal\Runtime\DispatchCell.cs + Internal\Runtime\RuntimeConstants.cs @@ -112,6 +115,7 @@ Common\Experimentals.cs + diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs index 37fc0204b37e82..3ecca6e213aae1 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; +using System.Runtime; using Internal.NativeFormat; using Internal.Runtime.Augments; @@ -99,11 +100,10 @@ internal override void Prepare(TypeBuilder builder) internal override unsafe IntPtr Create(TypeBuilder builder) { - var dispatchCellAndComposition = (nint*)MemoryHelpers.AllocateMemory((2 + 2) * sizeof(nint)); - dispatchCellAndComposition[0] = 0; - dispatchCellAndComposition[1] = 0; - dispatchCellAndComposition[2] = builder.GetRuntimeTypeHandle(InterfaceType).Value; - dispatchCellAndComposition[3] = Slot; + var dispatchCellAndComposition = (DynamicDispatchCell*)MemoryHelpers.AllocateMemory(sizeof(DynamicDispatchCell)); + dispatchCellAndComposition->Cell = default; + dispatchCellAndComposition->InterfaceType = (MethodTable*)builder.GetRuntimeTypeHandle(InterfaceType).Value; + dispatchCellAndComposition->Slot = Slot; return (IntPtr)dispatchCellAndComposition; } diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj b/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj index 02327cc5905fb5..072b75a97b5600 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj @@ -26,6 +26,9 @@ TypeSystem\Common\VersionResilientHashCode.cs + + DispatchCell.cs + MethodTable.Constants.cs diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/Internal/Runtime/Dispatch.cs b/src/coreclr/nativeaot/Test.CoreLib/src/Internal/Runtime/Dispatch.cs new file mode 100644 index 00000000000000..0a580090cc3719 --- /dev/null +++ b/src/coreclr/nativeaot/Test.CoreLib/src/Internal/Runtime/Dispatch.cs @@ -0,0 +1,20 @@ +// 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.Runtime; +using System.Runtime.CompilerServices; + +using Internal.Runtime.CompilerHelpers; + +namespace Internal.Runtime +{ + internal static class Dispatch + { + [RuntimeExport("ResolveDispatch")] + internal static unsafe MethodTable* ResolveDispatch(object pObject, DispatchCell* pCell) + { + return null; + } + } +} diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj index e1b9c28a56c2c9..d5491456d550ef 100644 --- a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj +++ b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj @@ -95,6 +95,9 @@ Internal\Runtime\DehydratedData.cs + + Internal\Runtime\DispatchCell.cs + Internal\Runtime\RuntimeConstants.cs @@ -213,6 +216,7 @@ Common\System\Runtime\InteropServices\IDynamicInterfaceCastable.cs + diff --git a/src/coreclr/tools/Common/Internal/Runtime/DispatchCell.cs b/src/coreclr/tools/Common/Internal/Runtime/DispatchCell.cs new file mode 100644 index 00000000000000..84cd77d18c9ad4 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Runtime/DispatchCell.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +using Internal.Runtime; + +namespace System.Runtime +{ + [StructLayout(LayoutKind.Sequential)] + internal struct DispatchCell + { + public nint MethodTable; + public nint Code; + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct DynamicDispatchCell + { + public DispatchCell Cell; + public MethodTable* InterfaceType; + public nint Slot; + } +} From 0757b763613293b1e139bded85d54c23fef91bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 19 Jun 2026 12:13:05 +0900 Subject: [PATCH 3/4] Put GVM dispatch on the interface dispatch cell plan --- src/coreclr/inc/corinfo.h | 2 +- src/coreclr/jit/importer.cpp | 6 +- .../System/Runtime/CachedInterfaceDispatch.cs | 1 + src/coreclr/nativeaot/Runtime/EHHelpers.cpp | 8 + .../nativeaot/Runtime/amd64/DispatchResolve.S | 62 +++++--- .../Runtime/amd64/DispatchResolve.asm | 55 +++++-- .../nativeaot/Runtime/arm/DispatchResolve.S | 24 +++ .../nativeaot/Runtime/arm64/DispatchResolve.S | 61 +++++--- .../Runtime/arm64/DispatchResolve.asm | 46 ++++-- .../Runtime/i386/DispatchResolve.asm | 105 +++++++++++++ .../Runtime/loongarch64/DispatchResolve.S | 19 +++ .../Runtime/riscv64/DispatchResolve.S | 19 +++ .../src/CompatibilitySuppressions.xml | 8 + .../Runtime/Augments/TypeLoaderCallbacks.cs | 3 +- .../CompilerServices/OpenMethodResolver.cs | 15 +- .../src/Internal/Runtime/Dispatch.cs | 70 ++++++++- .../src/System/Delegate.cs | 4 +- .../src/System/Runtime/TypeLoaderExports.cs | 17 --- .../src/System/RuntimeMethodHandle.cs | 30 ++-- .../TypeLoader/GenericDictionaryCell.cs | 56 ++++++- .../TypeLoaderEnvironment.GVMResolution.cs | 37 ++++- ...peLoaderEnvironment.LdTokenResultLookup.cs | 49 +----- .../TypeLoaderEnvironment.Metadata.cs | 2 +- .../TypeLoader/TypeLoaderEnvironment.cs | 14 +- .../src/System.Private.TypeLoader.csproj | 4 +- .../DependencyAnalysis/GVMDependenciesNode.cs | 1 + .../SortableDependencyNode.cs | 2 + .../Internal/NativeFormat/NativeFormat.cs | 2 +- .../Common/Internal/Runtime/DispatchCell.cs | 87 ++++++++++- .../Common/Internal/Runtime/ModuleHeaders.cs | 2 + .../Internal/Runtime/RuntimeConstants.cs | 2 +- .../Compiler/Compilation.cs | 2 +- .../Compiler/DelegateCreationInfo.cs | 26 ++-- ...ispatchCellNode.cs => DispatchCellNode.cs} | 41 ++---- ...tionNode.cs => DispatchCellSectionNode.cs} | 39 +++-- .../DependencyAnalysis/GenericLookupResult.cs | 14 +- .../GvmDispatchCellInfoSectionNode.cs | 139 ++++++++++++++++++ .../InterfaceDispatchCellInfoSectionNode.cs | 43 +++++- .../NativeLayoutVertexNode.cs | 43 ++++++ .../NodeFactory.GenericLookups.cs | 10 +- .../NodeFactory.NativeLayout.cs | 13 +- .../DependencyAnalysis/NodeFactory.cs | 10 +- .../ReadyToRunGenericHelperNode.cs | 6 +- .../ReadyToRunHelperNode.cs | 2 +- .../RuntimeMethodHandleNode.cs | 16 +- .../ARMReadyToRunGenericHelperNode.cs | 2 +- .../Target_ARM/ARMReadyToRunHelperNode.cs | 2 +- .../ARM64ReadyToRunGenericHelperNode.cs | 2 +- .../Target_ARM64/ARM64ReadyToRunHelperNode.cs | 2 +- .../LoongArch64ReadyToRunGenericHelperNode.cs | 2 +- .../LoongArch64ReadyToRunHelperNode.cs | 2 +- .../RiscV64ReadyToRunGenericHelperNode.cs | 2 +- .../RiscV64ReadyToRunHelperNode.cs | 2 +- .../X64ReadyToRunGenericHelperNode.cs | 2 +- .../Target_X64/X64ReadyToRunHelperNode.cs | 2 +- .../X86ReadyToRunGenericHelperNode.cs | 2 +- .../Target_X86/X86ReadyToRunHelperNode.cs | 2 +- .../ILCompiler.Compiler/Compiler/JitHelper.cs | 2 +- .../Compiler/MetadataManager.cs | 16 +- .../IL/ILImporter.Scanner.cs | 8 +- .../ILCompiler.Compiler.csproj | 5 +- .../ReadyToRunSymbolNodeFactory.cs | 8 +- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 2 +- .../JitInterface/CorInfoImpl.RyuJit.cs | 32 +++- 64 files changed, 999 insertions(+), 315 deletions(-) rename src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/{InterfaceDispatchCellNode.cs => DispatchCellNode.cs} (66%) rename src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/{InterfaceDispatchCellSectionNode.cs => DispatchCellSectionNode.cs} (56%) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GvmDispatchCellInfoSectionNode.cs diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 93c37d0a7e5c3a..e1da6e75612509 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -562,7 +562,7 @@ enum CorInfoHelpFunc CORINFO_HELP_JIT_REVERSE_PINVOKE_EXIT, // Transition to preemptive mode in reverse P/Invoke epilog, frame is the first argument CORINFO_HELP_JIT_REVERSE_PINVOKE_EXIT_TRACK_TRANSITIONS, // Transition to preemptive mode and track transitions in reverse P/Invoke prolog. - CORINFO_HELP_GVMLOOKUP_FOR_SLOT, // Resolve a generic virtual method target from this pointer and runtime method handle + CORINFO_HELP_GVMLOOKUP_FOR_SLOT, // Resolve a generic virtual method target from this pointer and dispatch cell CORINFO_HELP_INTERFACEDISPATCH_FOR_SLOT, // Dispatch a non-generic interface method from this pointer and dispatch cell CORINFO_HELP_INTERFACELOOKUP_FOR_SLOT, // Resolve a non-generic interface method from this pointer and dispatch cell diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 9c860424faa158..34856dd5fe57f6 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -2723,10 +2723,10 @@ GenTree* Compiler::impImportLdvirtftn(GenTree* thisPtr, // NativeAOT generic virtual method if ((pCallInfo->sig.sigInst.methInstCount != 0) && IsTargetAbi(CORINFO_NATIVEAOT_ABI)) { - GenTree* runtimeMethodHandle = - impLookupToTree(&pCallInfo->codePointerLookup, GTF_ICON_METHOD_HDL, pCallInfo->hMethod); + GenTree* dispatchCell = + impLookupToTree(&pCallInfo->codePointerLookup, GTF_ICON_FTN_ADDR, pCallInfo->hMethod); call = gtNewVirtualFunctionLookupHelperCallNode(CORINFO_HELP_GVMLOOKUP_FOR_SLOT, TYP_I_IMPL, thisPtr, - runtimeMethodHandle); + dispatchCell); } // Wasm R2R cannot use the CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR fast path because it diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs index a496be1e1ff024..a01a291f6ab7cd 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs @@ -71,6 +71,7 @@ private static IntPtr RhpCidResolve(IntPtr callerTransitionBlockParam, IntPtr pC return dispatchResolveTarget; } + [RuntimeExport("RhpCidResolve_Worker")] private static IntPtr RhpCidResolve_Worker(object pObject, IntPtr pCell) { var resolver = (delegate*)InternalCalls.RhpGetClasslibFunctionFromEEType(pObject.GetMethodTable(), ClassLibFunctionId.ResolveDispatch); diff --git a/src/coreclr/nativeaot/Runtime/EHHelpers.cpp b/src/coreclr/nativeaot/Runtime/EHHelpers.cpp index e4cd5ba8bcc0ae..ce5fae6a71a1f6 100644 --- a/src/coreclr/nativeaot/Runtime/EHHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/EHHelpers.cpp @@ -268,8 +268,12 @@ static bool InWriteBarrierHelper(uintptr_t faultingIP) } EXTERN_C CODE_LOCATION RhpInterfaceDispatch; +#if !defined(TARGET_ARM) +EXTERN_C CODE_LOCATION RhpDispatchResolve; +#endif #if defined(TARGET_ARM) EXTERN_C CODE_LOCATION RhpInterfaceDispatchAVLocation; +EXTERN_C CODE_LOCATION RhpDispatchResolveAVLocation; #endif #if defined(TARGET_WINDOWS) && (defined(TARGET_AMD64) || defined(TARGET_ARM64)) EXTERN_C CODE_LOCATION RhpInterfaceDispatchGuarded; @@ -282,9 +286,13 @@ static bool InInterfaceDispatchHelper(uintptr_t faultingIP) { #if defined(TARGET_ARM) (uintptr_t)&RhpInterfaceDispatchAVLocation, + (uintptr_t)&RhpDispatchResolveAVLocation, #else (uintptr_t)&RhpInterfaceDispatch, #endif +#if !defined(TARGET_ARM) + (uintptr_t)&RhpDispatchResolve, +#endif #if defined(TARGET_WINDOWS) && (defined(TARGET_AMD64) || defined(TARGET_ARM64)) (uintptr_t)&RhpInterfaceDispatchGuarded, #endif diff --git a/src/coreclr/nativeaot/Runtime/amd64/DispatchResolve.S b/src/coreclr/nativeaot/Runtime/amd64/DispatchResolve.S index b9bdd9b7265cd4..7a60e50be059bb 100644 --- a/src/coreclr/nativeaot/Runtime/amd64/DispatchResolve.S +++ b/src/coreclr/nativeaot/Runtime/amd64/DispatchResolve.S @@ -5,7 +5,9 @@ #include // Dispatching version of RhpResolveInterfaceMethod -LEAF_ENTRY RhpInterfaceDispatch, _TEXT +.macro INTERFACE_DISPATCH DispatchName, ReturnTarget, DispatchCellReg + +LEAF_ENTRY \DispatchName, _TEXT // Load the MethodTable from the object instance in rdi. // Trigger an AV if we're dispatching on a null this. @@ -14,23 +16,29 @@ LEAF_ENTRY RhpInterfaceDispatch, _TEXT // to a NullReferenceException at the callsite. mov r10, [rdi] - // r11 currently contains the indirection cell address. - cmp qword ptr [r11], r10 // is this the monomorphic MethodTable? - jne LOCAL_LABEL(Hashtable) + // DispatchCellReg currently contains the indirection cell address. + cmp qword ptr [\DispatchCellReg], r10 // is this the monomorphic MethodTable? + jne 1f - mov rax, [r11 + 8] // load the cached monomorphic resolved code address into rax + mov rax, [\DispatchCellReg + 8] // load the cached monomorphic resolved code address into rax +.if \ReturnTarget + ret +.else jmp rax +.endif -LOCAL_LABEL(Hashtable): +1: - // r10 = MethodTable, r11 = indirection cell address + // r10 = MethodTable, DispatchCellReg = indirection cell address // Look up the target in the dispatch cache hashtable (GenericCache). +.if !\ReturnTarget // Spill argument registers to the red zone below rsp // so we don't modify rsp (this is a LEAF_ENTRY with no unwind info). mov [rsp - 8], rcx mov [rsp - 16], rdx mov [rsp - 24], r8 mov [rsp - 32], r9 +.endif // Load the _table field (Entry[]) from the cache struct. mov r8, qword ptr [C_VAR(g_pDispatchCache)] @@ -38,7 +46,7 @@ LOCAL_LABEL(Hashtable): // Compute 32-bit hash from Key.GetHashCode(): // hash = IntPtr.GetHashCode(RotateLeft(dispatchCell, 16) ^ objectType) - mov rcx, r11 + mov rcx, \DispatchCellReg rol rcx, 0x10 xor rcx, r10 mov r9, rcx @@ -53,7 +61,7 @@ LOCAL_LABEL(Hashtable): shr r9, cl xor ecx, ecx -LOCAL_LABEL(ProbeLoop): +2: // Compute entry address: Element(table, index) = table + 0x10 + (index + 1) * 0x20 lea eax, [r9 + 1] movsxd rax, eax @@ -63,33 +71,37 @@ LOCAL_LABEL(ProbeLoop): // Read version snapshot before key comparison (seqlock protocol). mov edx, dword ptr [rax] test edx, 1 - jne LOCAL_LABEL(ProbeMiss) + jne 3f // Compare key (dispatchCell, objectType) - cmp r11, qword ptr [rax + 8] - jne LOCAL_LABEL(ProbeMiss) + cmp \DispatchCellReg, qword ptr [rax + 8] + jne 3f cmp r10, qword ptr [rax + 0x10] - jne LOCAL_LABEL(ProbeMiss) + jne 3f // Read the cached code pointer, then re-verify the version has not changed. mov r10, qword ptr [rax + 0x18] cmp edx, dword ptr [rax] - jne LOCAL_LABEL(CacheMiss) + jne 4f // Dispatch to cached target. mov rax, r10 +.if \ReturnTarget + ret +.else mov r9, [rsp - 32] mov r8, [rsp - 24] mov rdx, [rsp - 16] mov rcx, [rsp - 8] jmp rax +.endif -LOCAL_LABEL(ProbeMiss): +3: // If version is zero the rest of the bucket is unclaimed — stop probing. test edx, edx - je LOCAL_LABEL(CacheMiss) + je 4f // Quadratic reprobe: i++; index = (index + i) & tableMask inc ecx @@ -98,17 +110,25 @@ LOCAL_LABEL(ProbeMiss): add eax, 0xFFFFFFFE and r9d, eax cmp ecx, 8 - jl LOCAL_LABEL(ProbeLoop) + jl 2b -LOCAL_LABEL(CacheMiss): +4: +.if \ReturnTarget + jmp C_FUNC(RhpCidResolve_Worker) +.else mov r9, [rsp - 32] mov r8, [rsp - 24] mov rdx, [rsp - 16] mov rcx, [rsp - 8] -LOCAL_LABEL(SlowPath): - // r11 contains indirection cell address + // DispatchCellReg contains indirection cell address lea r10, [C_VAR(RhpCidResolve)] jmp C_FUNC(RhpUniversalTransitionTailCall) +.endif + +LEAF_END \DispatchName, _TEXT + +.endm -LEAF_END RhpInterfaceDispatch, _TEXT + INTERFACE_DISPATCH RhpInterfaceDispatch, 0, r11 + INTERFACE_DISPATCH RhpDispatchResolve, 1, rsi diff --git a/src/coreclr/nativeaot/Runtime/amd64/DispatchResolve.asm b/src/coreclr/nativeaot/Runtime/amd64/DispatchResolve.asm index d58a99944b9744..722e67b3e2b128 100644 --- a/src/coreclr/nativeaot/Runtime/amd64/DispatchResolve.asm +++ b/src/coreclr/nativeaot/Runtime/amd64/DispatchResolve.asm @@ -5,6 +5,7 @@ include AsmMacros.inc EXTERN RhpCidResolve : PROC +EXTERN RhpCidResolve_Worker : PROC EXTERN RhpUniversalTransitionTailCall : PROC EXTERN RhpUniversalTransitionGuardedTailCall : PROC @@ -12,10 +13,13 @@ EXTERN g_pDispatchCache : QWORD EXTERN __guard_dispatch_icall_fptr : QWORD -;; Macro that generates an interface dispatch stub. +;; Macro that generates an interface dispatch or dispatch resolve stub. ;; DispatchName: the name of the dispatch entry point ;; Guarded: if non-zero, validate indirect call targets using Control Flow Guard -INTERFACE_DISPATCH macro DispatchName, Guarded +;; ReturnTarget: if non-zero, return the resolved target in rax instead of dispatching to it +;; DispatchCellReg: register containing the indirection cell address +;; VersionReg32: scratch register used for entry version checks +INTERFACE_DISPATCH macro DispatchName, Guarded, ReturnTarget, DispatchCellReg, VersionReg32 LOCAL Hashtable, ProbeLoop, ProbeMiss, CacheMiss, SlowPath @@ -28,27 +32,37 @@ LEAF_ENTRY DispatchName, _TEXT ;; to a NullReferenceException at the callsite. mov r10, [rcx] - ;; r11 currently contains the indirection cell address. - cmp qword ptr [r11], r10 ;; is this the monomorhpic MethodTable? + ;; DispatchCellReg currently contains the indirection cell address. + cmp qword ptr [DispatchCellReg], r10 ;; is this the monomorhpic MethodTable? jne Hashtable - mov rax, [r11 + 8] ;; load the cached monomorphic resolved code address into rax + mov rax, [DispatchCellReg + 8] ;; load the cached monomorphic resolved code address into rax +if ReturnTarget ne 0 + ret +else if Guarded ne 0 jmp [__guard_dispatch_icall_fptr] else jmp rax +endif endif Hashtable: - ;; r10 = MethodTable, r11 = indirection cell address + ;; r10 = MethodTable, DispatchCellReg = indirection cell address ;; Look up the target in the dispatch cache hashtable (GenericCache). +if ReturnTarget ne 0 + ;; Preserve this for the slow path. The return-only helper doesn't need + ;; to preserve the rest of the argument registers. + mov [rsp + 8h], rcx +else ;; Spill argument registers to the caller-provided shadow space ;; so we don't modify rsp (this is a LEAF_ENTRY with no unwind info). mov [rsp + 8h], rcx mov [rsp + 10h], rdx mov [rsp + 18h], r8 mov [rsp + 20h], r9 +endif ;; Load the _table field (Entry[]) from the cache struct. mov r8, qword ptr [g_pDispatchCache] @@ -56,7 +70,7 @@ endif ;; Compute 32-bit hash from Key.GetHashCode(): ;; hash = IntPtr.GetHashCode(RotateLeft(dispatchCell, 16) ^ objectType) - mov rcx, r11 + mov rcx, DispatchCellReg rol rcx, 10h xor rcx, r10 mov r9, rcx @@ -79,24 +93,27 @@ endif lea rax, [r8 + rax + 10h] ;; Read version snapshot before key comparison (seqlock protocol). - mov edx, dword ptr [rax] - test edx, 1 + mov VersionReg32, dword ptr [rax] + test VersionReg32, 1 jne ProbeMiss ;; Compare key (dispatchCell, objectType) - cmp r11, qword ptr [rax + 8] + cmp DispatchCellReg, qword ptr [rax + 8] jne ProbeMiss cmp r10, qword ptr [rax + 10h] jne ProbeMiss ;; Read the cached code pointer, then re-verify the version has not changed. mov r10, qword ptr [rax + 18h] - cmp edx, dword ptr [rax] + cmp VersionReg32, dword ptr [rax] jne CacheMiss ;; Dispatch to cached target. mov rax, r10 +if ReturnTarget ne 0 + ret +else mov r9, [rsp + 20h] mov r8, [rsp + 18h] mov rdx, [rsp + 10h] @@ -106,11 +123,12 @@ if Guarded ne 0 jmp [__guard_dispatch_icall_fptr] else jmp rax +endif endif ProbeMiss: ;; If version is zero the rest of the bucket is unclaimed - stop probing. - test edx, edx + test VersionReg32, VersionReg32 je CacheMiss ;; Quadratic reprobe: i++; index = (index + i) & tableMask @@ -123,25 +141,32 @@ endif jl ProbeLoop CacheMiss: +if ReturnTarget ne 0 + mov rcx, [rsp + 8h] + mov rdx, DispatchCellReg + jmp RhpCidResolve_Worker +else mov r9, [rsp + 20h] mov r8, [rsp + 18h] mov rdx, [rsp + 10h] mov rcx, [rsp + 8h] SlowPath: - ;; r11 contains indirection cell address + ;; DispatchCellReg contains indirection cell address lea r10, RhpCidResolve if Guarded ne 0 jmp RhpUniversalTransitionGuardedTailCall else jmp RhpUniversalTransitionTailCall endif +endif LEAF_END DispatchName, _TEXT endm - INTERFACE_DISPATCH RhpInterfaceDispatch, 0 - INTERFACE_DISPATCH RhpInterfaceDispatchGuarded, 1 + INTERFACE_DISPATCH RhpInterfaceDispatch, 0, 0, r11, edx + INTERFACE_DISPATCH RhpInterfaceDispatchGuarded, 1, 0, r11, edx + INTERFACE_DISPATCH RhpDispatchResolve, 0, 1, rdx, r11d end diff --git a/src/coreclr/nativeaot/Runtime/arm/DispatchResolve.S b/src/coreclr/nativeaot/Runtime/arm/DispatchResolve.S index 351fc74b10a2e4..d64a9ac06a8e09 100644 --- a/src/coreclr/nativeaot/Runtime/arm/DispatchResolve.S +++ b/src/coreclr/nativeaot/Runtime/arm/DispatchResolve.S @@ -124,3 +124,27 @@ LOCAL_LABEL(CacheMiss): b C_FUNC(RhpUniversalTransitionTailCall) NESTED_END RhpInterfaceDispatch, _TEXT + +// Resolve helper for the standard calling convention. +// r0 = object instance, r1 = indirection cell address, result in r0. +LEAF_ENTRY RhpDispatchResolve, _TEXT + + // Load the MethodTable from the object instance in r0. + // The label marks the location of a potential nullref for the unwinder. + GLOBAL_LABEL RhpDispatchResolveAVLocation + ldr r2, [r0] + + ldr r3, [r1] // load cached MethodTable + cmp r3, r2 // is this the monomorphic MethodTable? + bne LOCAL_LABEL(ResolveInterfaceMethod) + + // dmb ensures that the Code load below sees the value written before + // MethodTable. ARM32 has no load-acquire instruction (LDAR is ARMv8 only). + dmb + ldr r0, [r1, #4] // return cached monomorphic resolved code address + bx lr + +LOCAL_LABEL(ResolveInterfaceMethod): + b C_FUNC(RhpResolveInterfaceMethod) + +LEAF_END RhpDispatchResolve, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/arm64/DispatchResolve.S b/src/coreclr/nativeaot/Runtime/arm64/DispatchResolve.S index c3a7e517f9553b..0d86df48710219 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/DispatchResolve.S +++ b/src/coreclr/nativeaot/Runtime/arm64/DispatchResolve.S @@ -4,7 +4,9 @@ #include // Dispatching version of RhpResolveInterfaceMethod -LEAF_ENTRY RhpInterfaceDispatch, _TEXT +.macro INTERFACE_DISPATCH DispatchName, ReturnTarget, DispatchCellReg + +LEAF_ENTRY \DispatchName, _TEXT // Load the MethodTable from the object instance in x0. // Trigger an AV if we're dispatching on a null this. @@ -13,19 +15,24 @@ LEAF_ENTRY RhpInterfaceDispatch, _TEXT // to a NullReferenceException at the callsite. ldr x12, [x0] - // x11 currently contains the indirection cell address. + // DispatchCellReg currently contains the indirection cell address. // Load-acquire ensures that if we observe the cached MethodTable, // the subsequent load of Code will see the value written before it. - ldar x13, [x11] // load-acquire cached MethodTable + ldar x13, [\DispatchCellReg] // load-acquire cached MethodTable cmp x13, x12 // is this the monomorphic MethodTable? - bne LOCAL_LABEL(Hashtable) + bne 1f - ldr x9, [x11, #8] // load the cached monomorphic resolved code address + ldr x9, [\DispatchCellReg, #8] // load the cached monomorphic resolved code address +.if \ReturnTarget + mov x0, x9 + ret +.else br x9 +.endif -LOCAL_LABEL(Hashtable): +1: - // x12 = MethodTable, x11 = indirection cell address + // x12 = MethodTable, DispatchCellReg = indirection cell address // Look up the target in the dispatch cache hashtable (GenericCache). // Only volatile scratch registers (x9-x17) are used, so no argument // register spilling is needed. @@ -36,7 +43,7 @@ LOCAL_LABEL(Hashtable): // Compute 32-bit hash from Key.GetHashCode(): // hash = IntPtr.GetHashCode(RotateLeft(dispatchCell, 16) ^ objectType) - eor x14, x12, x11, ror #48 // combined = MethodTable ^ RotateLeft(cell, 16) + eor x14, x12, \DispatchCellReg, ror #48 // combined = MethodTable ^ RotateLeft(cell, 16) asr x16, x14, #32 // upper32 = combined >> 32 (arithmetic) eor w14, w14, w16 // hash = (int)lower32 ^ (int)upper32 sxtw x14, w14 // sign-extend hash to 64-bit @@ -54,7 +61,7 @@ LOCAL_LABEL(Hashtable): add x13, x13, #0x10 mov w15, wzr // loop counter i = 0 -LOCAL_LABEL(ProbeLoop): +2: // Compute entry address: Element(table, index) = base + (index + 1) * 0x20 add w9, w14, #1 sbfiz x9, x9, #5, #32 // sign-extend and shift left by 5 in one instruction @@ -63,14 +70,14 @@ LOCAL_LABEL(ProbeLoop): // Read version snapshot with load-acquire (seqlock protocol, ARM64 memory model). // This ensures subsequent loads of key and value see data written before this version. ldar w10, [x9] - tbnz w10, #0, LOCAL_LABEL(ProbeMiss) // skip if odd (entry being written) + tbnz w10, #0, 3f // skip if odd (entry being written) // Compare key (dispatchCell, objectType) — load both fields with a single ldp. ldp x16, x17, [x9, #8] - cmp x16, x11 - bne LOCAL_LABEL(ProbeMiss) + cmp x16, \DispatchCellReg + bne 3f cmp x17, x12 - bne LOCAL_LABEL(ProbeMiss) + bne 3f // Read the cached code pointer. ldr x16, [x9, #0x18] @@ -81,15 +88,20 @@ LOCAL_LABEL(ProbeLoop): // Re-verify version has not changed. ldr w17, [x9] cmp w10, w17 - bne LOCAL_LABEL(CacheMiss) + bne 4f // Dispatch to cached target. mov x9, x16 +.if \ReturnTarget + mov x0, x9 + ret +.else br x9 +.endif -LOCAL_LABEL(ProbeMiss): +3: // If version is zero the rest of the bucket is unclaimed — stop probing. - cbz w10, LOCAL_LABEL(CacheMiss) + cbz w10, 4f // Quadratic reprobe: i++; index = (index + i) & tableMask add w15, w15, #1 @@ -98,11 +110,20 @@ LOCAL_LABEL(ProbeMiss): sub w9, w9, #2 // TableMask = Length - 2 and w14, w14, w9 cmp w15, #8 - blt LOCAL_LABEL(ProbeLoop) + blt 2b -LOCAL_LABEL(CacheMiss): +4: +.if \ReturnTarget + b C_FUNC(RhpCidResolve_Worker) +.else PREPARE_EXTERNAL_VAR RhpCidResolve, xip0 - mov xip1, x11 + mov xip1, \DispatchCellReg b C_FUNC(RhpUniversalTransitionTailCall) +.endif + +LEAF_END \DispatchName, _TEXT + +.endm -LEAF_END RhpInterfaceDispatch, _TEXT + INTERFACE_DISPATCH RhpInterfaceDispatch, 0, x11 + INTERFACE_DISPATCH RhpDispatchResolve, 1, x1 diff --git a/src/coreclr/nativeaot/Runtime/arm64/DispatchResolve.asm b/src/coreclr/nativeaot/Runtime/arm64/DispatchResolve.asm index 098ab9c7872dc0..5ae00eca70b1ab 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/DispatchResolve.asm +++ b/src/coreclr/nativeaot/Runtime/arm64/DispatchResolve.asm @@ -6,17 +6,20 @@ TEXTAREA EXTERN RhpCidResolve + EXTERN RhpCidResolve_Worker EXTERN RhpUniversalTransitionTailCall EXTERN RhpUniversalTransitionGuardedTailCall EXTERN __guard_dispatch_icall_fptr EXTERN g_pDispatchCache -;; Macro that generates an interface dispatch stub. +;; Macro that generates an interface dispatch or dispatch resolve stub. ;; DispatchName: the name of the dispatch entry point ;; Guarded: if non-empty, validate indirect call targets using Control Flow Guard +;; ReturnTarget: if non-empty, return the resolved target in x0 instead of dispatching to it +;; DispatchCellReg: register containing the indirection cell address MACRO - INTERFACE_DISPATCH $DispatchName, $Guarded + INTERFACE_DISPATCH $DispatchName, $Guarded, $ReturnTarget, $DispatchCellReg LEAF_ENTRY $DispatchName, _TEXT @@ -27,24 +30,29 @@ ;; to a NullReferenceException at the callsite. ldr x12, [x0] - ;; x11 currently contains the indirection cell address. + ;; DispatchCellReg currently contains the indirection cell address. ;; Load-acquire ensures that if we observe the cached MethodTable, ;; the subsequent load of Code will see the value written before it. - ldar x13, [x11] ;; load-acquire cached MethodTable + ldar x13, [$DispatchCellReg] ;; load-acquire cached MethodTable cmp x13, x12 ;; is this the monomorphic MethodTable? bne %ft1 - ldr x9, [x11, #8] ;; load the cached monomorphic resolved code address - IF "$Guarded" != "" + ldr x9, [$DispatchCellReg, #8] ;; load the cached monomorphic resolved code address + IF "$ReturnTarget" != "0" + mov x0, x9 + ret + ELSE + IF "$Guarded" != "0" adrp x16, __guard_dispatch_icall_fptr ldr x16, [x16, __guard_dispatch_icall_fptr] br x16 ELSE br x9 ENDIF + ENDIF 1 - ;; x12 = MethodTable, x11 = indirection cell address + ;; x12 = MethodTable, DispatchCellReg = indirection cell address ;; Look up the target in the dispatch cache hashtable (GenericCache). ;; Only volatile scratch registers (x9-x17) are used, so no argument ;; register spilling is needed. @@ -56,7 +64,7 @@ ;; Compute 32-bit hash from Key.GetHashCode(): ;; hash = IntPtr.GetHashCode(RotateLeft(dispatchCell, 16) ^ objectType) - eor x14, x12, x11, ror #48 ;; combined = MethodTable ^ RotateLeft(cell, 16) + eor x14, x12, $DispatchCellReg, ror #48 ;; combined = MethodTable ^ RotateLeft(cell, 16) asr x16, x14, #32 ;; upper32 = combined >> 32 (arithmetic) eor w14, w14, w16 ;; hash = (int)lower32 ^ (int)upper32 sxtw x14, w14 ;; sign-extend hash to 64-bit @@ -87,7 +95,7 @@ ;; Compare key (dispatchCell, objectType) — load both fields with a single ldp. ldp x16, x17, [x9, #8] - cmp x16, x11 + cmp x16, $DispatchCellReg bne %ft3 cmp x17, x12 bne %ft3 @@ -106,13 +114,18 @@ ;; Dispatch to cached target. mov x9, x16 - IF "$Guarded" != "" + IF "$ReturnTarget" != "0" + mov x0, x9 + ret + ELSE + IF "$Guarded" != "0" adrp x16, __guard_dispatch_icall_fptr ldr x16, [x16, __guard_dispatch_icall_fptr] br x16 ELSE br x9 ENDIF + ENDIF 3 ;; ProbeMiss ;; If version is zero the rest of the bucket is unclaimed — stop probing. @@ -128,19 +141,24 @@ blt %bt2 4 ;; CacheMiss / SlowPath + IF "$ReturnTarget" != "0" + b RhpCidResolve_Worker + ELSE ldr xip0, =RhpCidResolve - mov xip1, x11 - IF "$Guarded" != "" + mov xip1, $DispatchCellReg + IF "$Guarded" != "0" b RhpUniversalTransitionGuardedTailCall ELSE b RhpUniversalTransitionTailCall ENDIF + ENDIF LEAF_END $DispatchName MEND - INTERFACE_DISPATCH RhpInterfaceDispatch - INTERFACE_DISPATCH RhpInterfaceDispatchGuarded, Guarded + INTERFACE_DISPATCH RhpInterfaceDispatch, 0, 0, x11 + INTERFACE_DISPATCH RhpInterfaceDispatchGuarded, 1, 0, x11 + INTERFACE_DISPATCH RhpDispatchResolve, 0, 1, x1 END diff --git a/src/coreclr/nativeaot/Runtime/i386/DispatchResolve.asm b/src/coreclr/nativeaot/Runtime/i386/DispatchResolve.asm index 8c634bfa00c20d..b012ac02d2f968 100644 --- a/src/coreclr/nativeaot/Runtime/i386/DispatchResolve.asm +++ b/src/coreclr/nativeaot/Runtime/i386/DispatchResolve.asm @@ -9,6 +9,7 @@ include AsmMacros.inc EXTERN RhpCidResolve : PROC +EXTERN RhpCidResolve_Worker : PROC EXTERN _RhpUniversalTransitionTailCall@0 : PROC EXTERN _g_pDispatchCache : DWORD @@ -140,4 +141,108 @@ ALTERNATE_ENTRY _RhpInterfaceDispatch FASTCALL_ENDFUNC +;; Resolve helper for standard x86 calling convention. +;; Arguments are passed on the stack: object, dispatch cell. +RhpDispatchResolve PROC public + + mov ecx, dword ptr [esp + 4] ;; this + mov eax, dword ptr [esp + 8] ;; dispatch cell + + ;; Load the MethodTable from the object instance in ecx. + ;; Trigger an AV if we're dispatching on a null this. + mov edx, dword ptr [ecx] + + cmp dword ptr [eax], edx ;; is this the monomorphic MethodTable? + jne DispatchResolveHashtable + + mov eax, dword ptr [eax + 4] ;; return cached monomorphic resolved code address + ret 8 + + DispatchResolveHashtable: + + ;; edx = MethodTable, eax = cell + ;; Look up the target in the dispatch cache hashtable (GenericCache). + push ebx + push esi + push edi + + ;; Stack layout from esp: + ;; [esp+0] = saved edi + ;; [esp+4] = saved esi + ;; [esp+8] = saved ebx + ;; [esp+12] = return address + ;; [esp+16] = this + ;; [esp+20] = cell + + ;; Load the _table field (Entry[]) from the cache struct. + mov edi, dword ptr [_g_pDispatchCache] + mov edi, dword ptr [edi] + + ;; Compute 32-bit hash from Key.GetHashCode(): + ;; hash = RotateLeft(dispatchCell, 16) ^ objectType + ;; On 32-bit, IntPtr.GetHashCode() is identity. + mov ecx, eax + rol ecx, 10h + xor ecx, edx + + ;; HashToBucket: bucket = ((uint)hash * 0x9E3779B9) >> hashShift + imul ebx, ecx, -1640531527 + movzx ecx, byte ptr [edi + 8] + shr ebx, cl + xor ecx, ecx + + DispatchResolveProbeLoop: + ;; Compute entry address: table + 8 + (index + 1) * 16 + lea eax, [ebx + 1] + shl eax, 4 + lea eax, [edi + eax + 8] + + ;; Read version snapshot before key comparison (seqlock protocol). + mov edx, dword ptr [eax] + test edx, 1 + jne DispatchResolveProbeMiss + + ;; Compare key (dispatchCell, objectType) + mov esi, dword ptr [esp + 20] + cmp esi, dword ptr [eax + 4] + jne DispatchResolveProbeMiss + mov esi, dword ptr [esp + 16] + mov esi, dword ptr [esi] + cmp esi, dword ptr [eax + 8] + jne DispatchResolveProbeMiss + + ;; Read the cached code pointer, then re-verify the version has not changed. + mov esi, dword ptr [eax + 0Ch] + cmp edx, dword ptr [eax] + jne DispatchResolveCacheMiss + + ;; Return cached target. + mov eax, esi + pop edi + pop esi + pop ebx + ret 8 + + DispatchResolveProbeMiss: + ;; If version is zero the rest of the bucket is unclaimed - stop probing. + test edx, edx + je DispatchResolveCacheMiss + + ;; Quadratic reprobe: i++; index = (index + i) & tableMask + inc ecx + add ebx, ecx + mov eax, dword ptr [edi + 4] + add eax, -2 + and ebx, eax + cmp ecx, 8 + jl DispatchResolveProbeLoop + + DispatchResolveCacheMiss: + pop edi + pop esi + pop ebx + jmp RhpCidResolve_Worker + +RhpDispatchResolve ENDP + end diff --git a/src/coreclr/nativeaot/Runtime/loongarch64/DispatchResolve.S b/src/coreclr/nativeaot/Runtime/loongarch64/DispatchResolve.S index c9b60c88872391..5b4638a4a87dbd 100644 --- a/src/coreclr/nativeaot/Runtime/loongarch64/DispatchResolve.S +++ b/src/coreclr/nativeaot/Runtime/loongarch64/DispatchResolve.S @@ -21,3 +21,22 @@ LEAF_ENTRY RhpInterfaceDispatch, _TEXT b C_FUNC(RhpUniversalTransitionTailCall) LEAF_END RhpInterfaceDispatch, _TEXT + +// Resolve helper for the standard calling convention. +// $a0 = object instance, $a1 = indirection cell address, result in $a0. +LEAF_ENTRY RhpDispatchResolve, _TEXT + + // Load the MethodTable from the object instance in $a0. + // Trigger an AV if we're dispatching on a null this. + ld.d $t0, $a0, 0 + + ld.d $t1, $a1, 0 // load cached MethodTable + bne $t0, $t1, LOCAL_LABEL(ResolveInterfaceMethod) + + ld.d $a0, $a1, 8 // return cached monomorphic resolved code address + jirl $r0, $ra, 0 + +LOCAL_LABEL(ResolveInterfaceMethod): + b C_FUNC(RhpResolveInterfaceMethod) + +LEAF_END RhpDispatchResolve, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/riscv64/DispatchResolve.S b/src/coreclr/nativeaot/Runtime/riscv64/DispatchResolve.S index 5602822379e15e..08e5e3c3fd5bd5 100644 --- a/src/coreclr/nativeaot/Runtime/riscv64/DispatchResolve.S +++ b/src/coreclr/nativeaot/Runtime/riscv64/DispatchResolve.S @@ -22,3 +22,22 @@ LEAF_ENTRY RhpInterfaceDispatch, _TEXT tail C_FUNC(RhpUniversalTransitionTailCall) LEAF_END RhpInterfaceDispatch, _TEXT + +// Resolve helper for the standard calling convention. +// a0 = object instance, a1 = indirection cell address, result in a0. +LEAF_ENTRY RhpDispatchResolve, _TEXT + + // Load the MethodTable from the object instance in a0. + // Trigger an AV if we're dispatching on a null this. + ld t0, 0(a0) + + ld t1, 0(a1) // load cached MethodTable + bne t0, t1, LOCAL_LABEL(ResolveInterfaceMethod) + + ld a0, 8(a1) // return cached monomorphic resolved code address + ret + +LOCAL_LABEL(ResolveInterfaceMethod): + tail C_FUNC(RhpResolveInterfaceMethod) + +LEAF_END RhpDispatchResolve, _TEXT diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml index b85912e6cf1ff0..c785befc8b8bd5 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml @@ -809,6 +809,14 @@ CP0001 T:Internal.TypeSystem.LockFreeReaderHashtableOfPointers`2 + + CP0001 + T:System.Runtime.DispatchCell + + + CP0001 + T:System.Runtime.DynamicDispatchCell + CP0002 M:System.Reflection.MethodBase.GetParametersAsSpan diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs index c7b5203ffebd4f..2aed7c06ea116e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs @@ -7,6 +7,7 @@ using Internal.Metadata.NativeFormat; using Internal.NativeFormat; +using Internal.Runtime; using Internal.Runtime.CompilerServices; namespace Internal.Runtime.Augments @@ -21,7 +22,7 @@ public abstract class TypeLoaderCallbacks public abstract IntPtr GenericLookupFromContextAndSignature(IntPtr context, IntPtr signature, out IntPtr auxResult); public abstract RuntimeMethodHandle GetRuntimeMethodHandleForComponents(RuntimeTypeHandle declaringTypeHandle, MethodHandle handle, RuntimeTypeHandle[] genericMethodArgs); public abstract IntPtr TryGetDefaultConstructorForType(RuntimeTypeHandle runtimeTypeHandle); - public abstract IntPtr ResolveGenericVirtualMethodTarget(RuntimeTypeHandle targetTypeHandle, RuntimeMethodHandle declMethod); + public abstract unsafe IntPtr ResolveGenericVirtualMethodTarget(RuntimeTypeHandle targetTypeHandle, RuntimeTypeHandle declaringTypeHandle, MethodHandle methodHandle, bool isAsyncVariant, void* methodInstantiation, bool isMethodInstantiationDataRelative); public abstract RuntimeFieldHandle GetRuntimeFieldHandleForComponents(RuntimeTypeHandle declaringTypeHandle, FieldHandle handle); public abstract bool TryGetPointerTypeForTargetType(RuntimeTypeHandle pointeeTypeHandle, out RuntimeTypeHandle pointerTypeHandle); public abstract bool TryGetArrayTypeForElementType(RuntimeTypeHandle elementTypeHandle, bool isMdArray, int rank, out RuntimeTypeHandle arrayTypeHandle); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/OpenMethodResolver.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/OpenMethodResolver.cs index e3ee1f3eedb79c..5210eec5bf4424 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/OpenMethodResolver.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/OpenMethodResolver.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Runtime; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Internal.Runtime.Augments; @@ -15,13 +16,9 @@ namespace Internal.Runtime.CompilerServices // 2) Use the ToIntPtr() method to get the interned instance of this type. This will permanently allocate // a block of memory that can be used to represent a virtual method resolution. This memory is interned // so that repeated allocation of the same resolver will not leak. - // 3) Use the ResolveMethod function to do the virtual lookup. This function takes advantage of - // a lockless cache so the resolution is very fast for repeated lookups. + // 3) Use the ResolveMethod function to do the virtual lookup. public unsafe struct OpenMethodResolver : IEquatable { - // Lazy initialized to point to the type loader method when the first `GVMResolve` resolver is created - private static delegate* s_lazyGvmLookupForSlot; - public const short DispatchResolve = 0; public const short GVMResolve = 1; public const short OpenNonVirtualResolve = 2; @@ -44,6 +41,9 @@ public OpenMethodResolver(RuntimeTypeHandle declaringTypeOfSlot, int slot, GCHan _nonVirtualOpenInvokeCodePointer = IntPtr.Zero; } + private static nint GVMLookupForSlot(object o, RuntimeMethodHandle method) + => method.ResolveGenericVirtualMethodTarget(o); + public OpenMethodResolver(RuntimeTypeHandle declaringTypeOfSlot, RuntimeMethodHandle gvmSlot, GCHandle readerGCHandle, int handle) { _resolveType = GVMResolve; @@ -52,9 +52,6 @@ public OpenMethodResolver(RuntimeTypeHandle declaringTypeOfSlot, RuntimeMethodHa _handle = handle; _readerGCHandle = readerGCHandle; _nonVirtualOpenInvokeCodePointer = IntPtr.Zero; - - if (s_lazyGvmLookupForSlot == null) - s_lazyGvmLookupForSlot = &TypeLoaderExports.GVMLookupForSlot; } public OpenMethodResolver(RuntimeTypeHandle declaringType, IntPtr codePointer, GCHandle readerGCHandle, int handle, short resolveType) @@ -145,7 +142,7 @@ private IntPtr ResolveMethod(object thisObject) } else if (_resolveType == GVMResolve) { - return s_lazyGvmLookupForSlot(thisObject, GVMMethodHandle); + return GVMMethodHandle.ResolveGenericVirtualMethodTarget(thisObject); } else { diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Dispatch.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Dispatch.cs index 316a15f85fd687..fa8ca0b4d045df 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Dispatch.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Dispatch.cs @@ -6,6 +6,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Internal.Metadata.NativeFormat; +using Internal.Runtime.Augments; using Internal.Runtime.CompilerHelpers; namespace Internal.Runtime @@ -23,11 +25,30 @@ internal static class Dispatch { return ResolveStaticInterfaceDispatch(typeManager, pObject, (nint)(pCell - pDispatchCellRegion)); } + + pDispatchCellRegion = (DispatchCell*)RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.GvmDispatchCellRegion, out length); + if (pCell >= pDispatchCellRegion && pCell < (byte*)pDispatchCellRegion + length) + { + return ResolveGvmDispatch(typeManager, pObject, (nint)(pCell - pDispatchCellRegion)); + } } // Not found statically: must be a dynamic dispatch cell var pDynamicCell = (DynamicDispatchCell*)pCell; - return (MethodTable*)CachedInterfaceDispatch.RhResolveDispatchWorker(pObject, pDynamicCell->InterfaceType, (ushort)pDynamicCell->Slot); + if (pDynamicCell->IsGvmDispatchCell) + { + DynamicDispatchCell.DynamicGvmDispatchCell* pDynamicGvmCell = pDynamicCell->AsGvmDispatchCell(); + return (MethodTable*)RuntimeAugments.TypeLoaderCallbacks.ResolveGenericVirtualMethodTarget( + new RuntimeTypeHandle(pObject.GetMethodTable()), + RuntimeTypeHandle.FromIntPtr(pDynamicGvmCell->OwningType), + pDynamicGvmCell->Handle, + pDynamicGvmCell->IsAsyncVariant, + pDynamicGvmCell->Instantiation, + isMethodInstantiationDataRelative: false); + } + + DynamicDispatchCell.DynamicInterfaceDispatchCell* pDynamicInterfaceCell = pDynamicCell->AsInterfaceDispatchCell(); + return (MethodTable*)CachedInterfaceDispatch.RhResolveDispatchWorker(pObject, (MethodTable*)pDynamicInterfaceCell->InterfaceType, (ushort)pDynamicInterfaceCell->Slot); } private static unsafe MethodTable* ResolveStaticInterfaceDispatch(TypeManagerHandle typeManager, object pObject, nint cellIndex) @@ -45,6 +66,23 @@ internal static class Dispatch } } + private static unsafe MethodTable* ResolveGvmDispatch(TypeManagerHandle typeManager, object pObject, nint cellIndex) + { + IntPtr pDispatchCellInfoRegion = RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.GvmDispatchCellInfoRegion, out _); + if (MethodTable.SupportsRelativePointers) + { + var dispatchCellInfo = &((RelativeGvmDispatchInfo*)pDispatchCellInfoRegion)[cellIndex]; + return (MethodTable*)RuntimeAugments.TypeLoaderCallbacks.ResolveGenericVirtualMethodTarget( + new RuntimeTypeHandle(pObject.GetMethodTable()), new RuntimeTypeHandle(dispatchCellInfo->OwningType), dispatchCellInfo->Handle, dispatchCellInfo->IsAsyncVariant, dispatchCellInfo->Instantiation, isMethodInstantiationDataRelative: true); + } + else + { + var dispatchCellInfo = &((GvmDispatchInfo*)pDispatchCellInfoRegion)[cellIndex]; + return (MethodTable*)RuntimeAugments.TypeLoaderCallbacks.ResolveGenericVirtualMethodTarget( + new RuntimeTypeHandle(pObject.GetMethodTable()), new RuntimeTypeHandle(dispatchCellInfo->OwningType), dispatchCellInfo->Handle, dispatchCellInfo->IsAsyncVariant, dispatchCellInfo->Instantiation, isMethodInstantiationDataRelative: false); + } + } + [StructLayout(LayoutKind.Sequential)] private struct RelativeInterfaceDispatchInfo { @@ -63,5 +101,35 @@ private struct InterfaceDispatchInfo public int Slot => (int)_slot; } + + [StructLayout(LayoutKind.Sequential)] + private struct RelativeGvmDispatchInfo + { + private int _owningTypeRelPtr; + private int _compositionRelPtr; + private int _flagsAndToken; + + public unsafe MethodTable* OwningType + => (MethodTable*)((byte*)Unsafe.AsPointer(ref _owningTypeRelPtr) + _owningTypeRelPtr); + + public unsafe void* Instantiation + => (byte*)Unsafe.AsPointer(ref _compositionRelPtr) + _compositionRelPtr; + + public MethodHandle Handle => new MethodHandle(_flagsAndToken & ~GvmDispatchCellFlags.IsAsyncVariant); + + public bool IsAsyncVariant => (_flagsAndToken & GvmDispatchCellFlags.IsAsyncVariant) != 0; + } + + [StructLayout(LayoutKind.Sequential)] + private struct GvmDispatchInfo + { + public unsafe MethodTable* OwningType; + public unsafe void* Instantiation; + private nint _flagsAndToken; + + public MethodHandle Handle => new MethodHandle((int)_flagsAndToken & ~GvmDispatchCellFlags.IsAsyncVariant); + + public bool IsAsyncVariant => ((int)_flagsAndToken & GvmDispatchCellFlags.IsAsyncVariant) != 0; + } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs index 9968de32d01574..ef45a70da427aa 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs @@ -139,12 +139,12 @@ private void InitializeClosedInstanceSlow(object firstParameter, IntPtr function } // This function is known to the compiler. - private void InitializeClosedInstanceWithGVMResolution(object firstParameter, RuntimeMethodHandle tokenOfGenericVirtualMethod) + private void InitializeClosedInstanceWithGVMResolution(object firstParameter, IntPtr dispatchCell) { if (firstParameter is null) throw new NullReferenceException(); - IntPtr functionResolution = TypeLoaderExports.GVMLookupForSlot(firstParameter, tokenOfGenericVirtualMethod); + IntPtr functionResolution = RuntimeImports.RhpResolveInterfaceMethod(firstParameter, dispatchCell); if (functionResolution == IntPtr.Zero) { diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs index 82065e8c510fce..2041e8fd527c92 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs @@ -96,23 +96,6 @@ public static IntPtr GenericLookup(IntPtr context, IntPtr signature) return v._result; } - public static unsafe IntPtr GVMLookupForSlot(object obj, RuntimeMethodHandle slot) - { - if (TryGetFromCache((IntPtr)obj.GetMethodTable(), RuntimeMethodHandle.ToIntPtr(slot), out var v)) - return v._result; - - return GVMLookupForSlotSlow(obj, slot); - } - - private static unsafe IntPtr GVMLookupForSlotSlow(object obj, RuntimeMethodHandle slot) - { - Value v = CacheMiss((IntPtr)obj.GetMethodTable(), RuntimeMethodHandle.ToIntPtr(slot), - (IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult) - => RuntimeAugments.TypeLoaderCallbacks.ResolveGenericVirtualMethodTarget(new RuntimeTypeHandle((MethodTable*)context), *(RuntimeMethodHandle*)&signature)); - - return v._result; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static unsafe IntPtr OpenInstanceMethodLookup(IntPtr openResolver, object obj) { diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs index 78f6863225ef4a..048fb0c7191d5b 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeMethodHandle.cs @@ -49,7 +49,7 @@ public unsafe bool Equals(RuntimeMethodHandle handle) return false; if (!thisInfo->Handle.Equals(thatInfo->Handle)) return false; - if (thisInfo->_numGenericArgsAndFlag != thatInfo->_numGenericArgsAndFlag) + if (thisInfo->NumGenericArgs != thatInfo->NumGenericArgs) return false; RuntimeTypeHandle* thisFirstArg = &thisInfo->FirstArgument; @@ -115,6 +115,20 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) { return (MethodHandleInfo*)_value; } + + internal readonly unsafe IntPtr ResolveGenericVirtualMethodTarget(object target) + { + MethodHandleInfo* info = ToMethodHandleInfo(); + void* methodInstantiation = info->NumGenericArgs != 0 ? (MethodTable*)&info->FirstArgument : null; + + return RuntimeAugments.TypeLoaderCallbacks.ResolveGenericVirtualMethodTarget( + new RuntimeTypeHandle(target.GetMethodTable()), + info->DeclaringType, + info->Handle, + isAsyncVariant: false, + methodInstantiation, + isMethodInstantiationDataRelative: false); + } } [CLSCompliant(false)] @@ -123,19 +137,7 @@ public struct MethodHandleInfo { public RuntimeTypeHandle DeclaringType; public MethodHandle Handle; - internal int _numGenericArgsAndFlag; + public int NumGenericArgs; public RuntimeTypeHandle FirstArgument; - - public int NumGenericArgs - { - get => _numGenericArgsAndFlag & ~RuntimeMethodHandleConstants.IsAsyncVariant; - set => _numGenericArgsAndFlag = (_numGenericArgsAndFlag & RuntimeMethodHandleConstants.IsAsyncVariant) | value; - } - - public bool IsAsyncVariant - { - get => (_numGenericArgsAndFlag & RuntimeMethodHandleConstants.IsAsyncVariant) != 0; - set => _numGenericArgsAndFlag = value ? _numGenericArgsAndFlag | RuntimeMethodHandleConstants.IsAsyncVariant : _numGenericArgsAndFlag & ~RuntimeMethodHandleConstants.IsAsyncVariant; - } } } diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs index 3ecca6e213aae1..422e725157c6c7 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Runtime; +using Internal.Metadata.NativeFormat; using Internal.NativeFormat; using Internal.Runtime.Augments; using Internal.Runtime.CompilerServices; @@ -100,12 +101,45 @@ internal override void Prepare(TypeBuilder builder) internal override unsafe IntPtr Create(TypeBuilder builder) { - var dispatchCellAndComposition = (DynamicDispatchCell*)MemoryHelpers.AllocateMemory(sizeof(DynamicDispatchCell)); - dispatchCellAndComposition->Cell = default; - dispatchCellAndComposition->InterfaceType = (MethodTable*)builder.GetRuntimeTypeHandle(InterfaceType).Value; - dispatchCellAndComposition->Slot = Slot; + var dispatchCell = (DynamicDispatchCell.DynamicInterfaceDispatchCell*)MemoryHelpers.AllocateMemory(sizeof(DynamicDispatchCell.DynamicInterfaceDispatchCell)); + dispatchCell->DispatchCell.Cell = default; + dispatchCell->InterfaceType = builder.GetRuntimeTypeHandle(InterfaceType).ToIntPtr(); + dispatchCell->Slot = Slot; - return (IntPtr)dispatchCellAndComposition; + return (IntPtr)dispatchCell; + } + } + + private class GvmDispatchCell : GenericDictionaryCell + { + internal MethodDesc Method; + + internal override void Prepare(TypeBuilder builder) + { + if (Method.IsCanonicalMethod(CanonicalFormKind.Any)) + Environment.FailFast("Unable to compute GVM dispatch information for a canonical method."); + + builder.RegisterForPreparation(Method.OwningType); + foreach (var type in Method.Instantiation) + builder.RegisterForPreparation(type); + } + + internal override unsafe IntPtr Create(TypeBuilder builder) + { + var dispatchCell = (DynamicDispatchCell.DynamicGvmDispatchCell*)MemoryHelpers.AllocateMemory(sizeof(DynamicDispatchCell.DynamicGvmDispatchCell)); + dispatchCell->DispatchCell.Cell = default; + dispatchCell->OwningType = builder.GetRuntimeTypeHandle(Method.OwningType).ToIntPtr(); + dispatchCell->Handle = Method.NameAndSignature.Handle; + dispatchCell->IsAsyncVariant = Method.AsyncVariant; + + MethodTable** instantiation = (MethodTable**)MemoryHelpers.AllocateMemory(sizeof(MethodTable*) * Method.Instantiation.Length); + for (int i = 0; i < Method.Instantiation.Length; i++) + { + instantiation[i] = (MethodTable*)builder.GetRuntimeTypeHandle(Method.Instantiation[i]).Value; + } + + dispatchCell->Instantiation = instantiation; + return (IntPtr)dispatchCell; } } @@ -334,8 +368,7 @@ internal override unsafe IntPtr Create(TypeBuilder builder) RuntimeMethodHandle handle = TypeLoaderEnvironment.Instance.GetRuntimeMethodHandleForComponents( builder.GetRuntimeTypeHandle(Method.OwningType), Method.NameAndSignature.Handle, - genericArgHandles, - Method.AsyncVariant); + genericArgHandles); return *(IntPtr*)&handle; } @@ -465,6 +498,15 @@ internal static GenericDictionaryCell ParseAndCreateCell(NativeLayoutInfoLoadCon } break; + case FixupSignatureKind.GvmDispatchCell: + { + var method = nativeLayoutInfoLoadContext.GetMethod(ref parser); + TypeLoaderLogger.WriteLine("GvmDispatchCell: " + method.OwningType.ToString() + "::" + method.NameAndSignature.GetName()); + + cell = new GvmDispatchCell() { Method = method }; + } + break; + case FixupSignatureKind.MethodDictionary: { var genericMethod = nativeLayoutInfoLoadContext.GetMethod(ref parser); diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs index f5a1336bdbe829..8a94c655e09e3b 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.GVMResolution.cs @@ -10,6 +10,7 @@ using System.Runtime.InteropServices; using System.Threading; +using Internal.Metadata.NativeFormat; using Internal.NativeFormat; using Internal.Runtime; using Internal.Runtime.Augments; @@ -119,12 +120,25 @@ internal static InstantiatedMethod GVMLookupForSlotWorker(DefType targetType, In return resolution; } - internal unsafe IntPtr ResolveGenericVirtualMethodTarget(RuntimeTypeHandle type, RuntimeMethodHandle slot) + internal unsafe IntPtr ResolveGenericVirtualMethodTarget(RuntimeTypeHandle targetTypeHandle, RuntimeTypeHandle declaringTypeHandle, MethodHandle methodHandle, bool isAsyncVariant, void* methodInstantiation, bool isMethodInstantiationDataRelative) { TypeSystemContext context = TypeSystemContextFactory.Create(); - DefType targetType = (DefType)context.ResolveRuntimeTypeHandle(type); - - InstantiatedMethod slotMethod = (InstantiatedMethod)GetMethodDescForRuntimeMethodHandle(context, slot); + TypeManagerHandle moduleHandle = RuntimeAugments.GetModuleFromTypeHandle(declaringTypeHandle); + MetadataReader reader = ModuleList.Instance.GetMetadataReaderForModule(moduleHandle); + + MethodNameAndSignature nameAndSignature = new MethodNameAndSignature(reader, methodHandle); + RuntimeTypeHandle[] methodInstantiationHandles = GetMethodInstantiationFromList(methodInstantiation, methodHandle.GetMethod(reader).GenericParameters.Count, isMethodInstantiationDataRelative); + + DefType targetType = (DefType)context.ResolveRuntimeTypeHandle(targetTypeHandle); + DefType declaringType = (DefType)context.ResolveRuntimeTypeHandle(declaringTypeHandle); + Instantiation methodInst = context.ResolveRuntimeTypeHandles(methodInstantiationHandles); + InstantiatedMethod slotMethod = (InstantiatedMethod)context.ResolveGenericMethodInstantiation( + unboxingStub: false, + asyncVariant: isAsyncVariant, + returnDroppingAsyncThunk: false, + declaringType, + nameAndSignature, + methodInst); InstantiatedMethod result = GVMLookupForSlotWorker(targetType, slotMethod); @@ -147,6 +161,21 @@ internal unsafe IntPtr ResolveGenericVirtualMethodTarget(RuntimeTypeHandle type, return FunctionPointerOps.GetGenericMethodFunctionPointer(methodPointer, dictionaryPointer); } + private static unsafe RuntimeTypeHandle[] GetMethodInstantiationFromList(void* methodInstantiation, int count, bool isRelative) + { + RuntimeTypeHandle[] result = new RuntimeTypeHandle[count]; + MethodTableList instantiation = isRelative + ? new MethodTableList((RelativePointer*)methodInstantiation) + : new MethodTableList((MethodTable*)methodInstantiation); + + for (int i = 0; i < count; i++) + { + result[i] = instantiation[i]->ToRuntimeTypeHandle(); + } + + return result; + } + public static MethodNameAndSignature GetMethodNameAndSignatureFromToken(TypeManagerHandle moduleHandle, uint token) { return new MethodNameAndSignature(ModuleList.Instance.GetMetadataReaderForModule(moduleHandle), token.AsHandle().ToMethodHandle(null)); diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs index e89c87ddaf487b..cac05dac5e76f9 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs @@ -8,14 +8,12 @@ using System.Reflection.Runtime.General; using System.Runtime; using System.Runtime.InteropServices; -using System.Text; using System.Threading; using Internal.Metadata.NativeFormat; using Internal.NativeFormat; using Internal.Runtime.Augments; using Internal.Runtime.CompilerServices; -using Internal.TypeSystem; using Debug = System.Diagnostics.Debug; @@ -57,9 +55,8 @@ private struct RuntimeMethodHandleKey : IEquatable private RuntimeTypeHandle _declaringType; private MethodHandle _handle; private RuntimeTypeHandle[] _genericArgs; - private bool _isAsyncVariant; - public RuntimeMethodHandleKey(RuntimeTypeHandle declaringType, MethodHandle handle, RuntimeTypeHandle[] genericArgs, bool isAsyncVariant) + public RuntimeMethodHandleKey(RuntimeTypeHandle declaringType, MethodHandle handle, RuntimeTypeHandle[] genericArgs) { // genericArgs will be null if this is a (typical or not) method definition // genericArgs are non-null only for instantiated generic methods. @@ -68,7 +65,6 @@ public RuntimeMethodHandleKey(RuntimeTypeHandle declaringType, MethodHandle hand _declaringType = declaringType; _handle = handle; _genericArgs = genericArgs; - _isAsyncVariant = isAsyncVariant; } public override bool Equals(object obj) @@ -85,9 +81,6 @@ public bool Equals(RuntimeMethodHandleKey other) if (!_declaringType.Equals(other._declaringType) || !_handle.Equals(other._handle)) return false; - if (_isAsyncVariant != other._isAsyncVariant) - return false; - if ((_genericArgs == null) != (other._genericArgs == null)) return false; @@ -109,10 +102,7 @@ public override int GetHashCode() int declaringTypeHashCode = _genericArgs == null ? _declaringType.GetHashCode() : VersionResilientHashCode.GenericInstanceHashCode(_declaringType.GetHashCode(), _genericArgs); - int hashCode = _handle.GetHashCode() ^ declaringTypeHashCode; - return _isAsyncVariant - ? unchecked((int)VersionResilientHashCode.CombineTwoValuesIntoHash((uint)hashCode, 1)) - : hashCode; + return _handle.GetHashCode() ^ declaringTypeHashCode; } } @@ -158,17 +148,17 @@ public unsafe bool TryGetRuntimeFieldHandleComponents(RuntimeFieldHandle runtime #region Method Ldtoken Functions - public unsafe RuntimeMethodHandle GetRuntimeMethodHandleForComponents(RuntimeTypeHandle declaringTypeHandle, int handle, RuntimeTypeHandle[] genericMethodArgs, bool isAsyncVariant) - => GetRuntimeMethodHandleForComponents(declaringTypeHandle, handle.AsHandle().ToMethodHandle(null), genericMethodArgs, isAsyncVariant); + public unsafe RuntimeMethodHandle GetRuntimeMethodHandleForComponents(RuntimeTypeHandle declaringTypeHandle, int handle, RuntimeTypeHandle[] genericMethodArgs) + => GetRuntimeMethodHandleForComponents(declaringTypeHandle, handle.AsHandle().ToMethodHandle(null), genericMethodArgs); /// /// Create a runtime method handle from name, signature and generic arguments. If the methodSignature /// is constructed from a metadata token, the methodName should be IntPtr.Zero, as it already encodes the method /// name. /// - public unsafe RuntimeMethodHandle GetRuntimeMethodHandleForComponents(RuntimeTypeHandle declaringTypeHandle, MethodHandle handle, RuntimeTypeHandle[] genericMethodArgs, bool isAsyncVariant) + public unsafe RuntimeMethodHandle GetRuntimeMethodHandleForComponents(RuntimeTypeHandle declaringTypeHandle, MethodHandle handle, RuntimeTypeHandle[] genericMethodArgs) { - RuntimeMethodHandleKey key = new RuntimeMethodHandleKey(declaringTypeHandle, handle, genericMethodArgs, isAsyncVariant); + RuntimeMethodHandleKey key = new RuntimeMethodHandleKey(declaringTypeHandle, handle, genericMethodArgs); lock (_runtimeMethodHandles) { @@ -183,7 +173,6 @@ public unsafe RuntimeMethodHandle GetRuntimeMethodHandleForComponents(RuntimeTyp methodData->DeclaringType = declaringTypeHandle; methodData->Handle = handle; methodData->NumGenericArgs = numGenericMethodArgs; - methodData->IsAsyncVariant = isAsyncVariant; RuntimeTypeHandle* genericArgPtr = &methodData->FirstArgument; for (int i = 0; i < numGenericMethodArgs; i++) { @@ -200,31 +189,10 @@ public unsafe RuntimeMethodHandle GetRuntimeMethodHandleForComponents(RuntimeTyp } } - public MethodDesc GetMethodDescForRuntimeMethodHandle(TypeSystemContext context, RuntimeMethodHandle runtimeMethodHandle) - { - bool success = TryGetRuntimeMethodHandleComponents(runtimeMethodHandle, out RuntimeTypeHandle declaringTypeHandle, - out MethodHandle handle, out RuntimeTypeHandle[] genericMethodArgs, out bool isAsyncVariant); - Debug.Assert(success); - - MetadataReader reader = ModuleList.Instance.GetMetadataReaderForModule(RuntimeAugments.GetModuleFromTypeHandle(declaringTypeHandle)); - MethodNameAndSignature nameAndSignature = new MethodNameAndSignature(reader, handle); - - DefType type = (DefType)context.ResolveRuntimeTypeHandle(declaringTypeHandle); - - if (genericMethodArgs != null) - { - Instantiation methodInst = context.ResolveRuntimeTypeHandles(genericMethodArgs); - return context.ResolveGenericMethodInstantiation(unboxingStub: false, isAsyncVariant, returnDroppingAsyncThunk: false, type, nameAndSignature, methodInst); - } - - return context.ResolveRuntimeMethod(unboxingStub: false, isAsyncVariant, returnDroppingAsyncThunk: false, type, nameAndSignature); - } - public unsafe bool TryGetRuntimeMethodHandleComponents(RuntimeMethodHandle runtimeMethodHandle, out RuntimeTypeHandle declaringTypeHandle, out QMethodDefinition handle, out RuntimeTypeHandle[] genericMethodArgs) { - if (TryGetRuntimeMethodHandleComponents(runtimeMethodHandle, out declaringTypeHandle, out MethodHandle methodHandle, out genericMethodArgs, out bool isAsyncVariant)) + if (TryGetRuntimeMethodHandleComponents(runtimeMethodHandle, out declaringTypeHandle, out MethodHandle methodHandle, out genericMethodArgs)) { - Debug.Assert(!isAsyncVariant, "How did reflection get a RuntimeMethodHandle for an async variant?"); MetadataReader reader = ModuleList.Instance.GetMetadataReaderForModule(RuntimeAugments.GetModuleFromTypeHandle(declaringTypeHandle)); handle = new QMethodDefinition(reader, methodHandle); return true; @@ -233,13 +201,12 @@ public unsafe bool TryGetRuntimeMethodHandleComponents(RuntimeMethodHandle runti return false; } - public unsafe bool TryGetRuntimeMethodHandleComponents(RuntimeMethodHandle runtimeMethodHandle, out RuntimeTypeHandle declaringTypeHandle, out MethodHandle handle, out RuntimeTypeHandle[] genericMethodArgs, out bool isAsyncVariant) + public unsafe bool TryGetRuntimeMethodHandleComponents(RuntimeMethodHandle runtimeMethodHandle, out RuntimeTypeHandle declaringTypeHandle, out MethodHandle handle, out RuntimeTypeHandle[] genericMethodArgs) { MethodHandleInfo* methodData = (MethodHandleInfo*)runtimeMethodHandle.Value; declaringTypeHandle = methodData->DeclaringType; handle = methodData->Handle; - isAsyncVariant = methodData->IsAsyncVariant; if (methodData->NumGenericArgs > 0) { diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs index 020ca4fdaf8bd2..e65b6230d845fa 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.Metadata.cs @@ -565,7 +565,7 @@ public static bool TryGetVirtualResolveData( if (isGenericVirtualMethod) { - RuntimeMethodHandle gvmSlot = TypeLoaderEnvironment.Instance.GetRuntimeMethodHandleForComponents(declaringTypeOfVirtualInvoke, token, genericArgs, isAsyncVariant: false); + RuntimeMethodHandle gvmSlot = TypeLoaderEnvironment.Instance.GetRuntimeMethodHandleForComponents(declaringTypeOfVirtualInvoke, token, genericArgs); lookupResult = new VirtualResolveDataResult { diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs index 16ff076bfe5b3a..f935da40de8a2c 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs @@ -11,6 +11,7 @@ using Internal.Metadata.NativeFormat; using Internal.NativeFormat; +using Internal.Runtime; using Internal.Runtime.Augments; using Internal.Runtime.CompilerServices; using Internal.TypeSystem; @@ -50,7 +51,7 @@ public override IntPtr GenericLookupFromContextAndSignature(IntPtr context, IntP public override RuntimeMethodHandle GetRuntimeMethodHandleForComponents(RuntimeTypeHandle declaringTypeHandle, MethodHandle handle, RuntimeTypeHandle[] genericMethodArgs) { - return TypeLoaderEnvironment.Instance.GetRuntimeMethodHandleForComponents(declaringTypeHandle, handle, genericMethodArgs, isAsyncVariant: false); + return TypeLoaderEnvironment.Instance.GetRuntimeMethodHandleForComponents(declaringTypeHandle, handle, genericMethodArgs); } public override IntPtr TryGetDefaultConstructorForType(RuntimeTypeHandle runtimeTypeHandle) @@ -58,9 +59,12 @@ public override IntPtr TryGetDefaultConstructorForType(RuntimeTypeHandle runtime return TypeLoaderEnvironment.Instance.TryGetDefaultConstructorForType(runtimeTypeHandle); } - public override IntPtr ResolveGenericVirtualMethodTarget(RuntimeTypeHandle targetTypeHandle, RuntimeMethodHandle declMethod) + public override unsafe IntPtr ResolveGenericVirtualMethodTarget(RuntimeTypeHandle targetTypeHandle, RuntimeTypeHandle declaringTypeHandle, MethodHandle methodHandle, bool isAsyncVariant, void* methodInstantiation, bool isMethodInstantiationDataRelative) { - return TypeLoaderEnvironment.Instance.ResolveGenericVirtualMethodTarget(targetTypeHandle, declMethod); + if (TypeLoaderEnvironment.GenericVirtualMethodsPresent()) + return TypeLoaderEnvironment.Instance.ResolveGenericVirtualMethodTarget(targetTypeHandle, declaringTypeHandle, methodHandle, isAsyncVariant, methodInstantiation, isMethodInstantiationDataRelative); + + return 0; } public override RuntimeFieldHandle GetRuntimeFieldHandleForComponents(RuntimeTypeHandle declaringTypeHandle, FieldHandle handle) @@ -111,6 +115,10 @@ private static TypeLoaderEnvironment InitializeInstance() [ThreadStatic] private static LowLevelDictionary t_moduleNativeReaders; + [Intrinsic] + [AnalysisCharacteristic] + internal static extern bool GenericVirtualMethodsPresent(); + // Eager initialization called from LibraryInitializer for the assembly. internal static void Initialize() { diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj b/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj index 072b75a97b5600..5190bcf2d52ca4 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj @@ -26,9 +26,6 @@ TypeSystem\Common\VersionResilientHashCode.cs - - DispatchCell.cs - MethodTable.Constants.cs @@ -57,6 +54,7 @@ IntrinsicAttribute.cs + Internal\Text\Utf8Span.cs diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/GVMDependenciesNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/GVMDependenciesNode.cs index da679b0644406a..c8958bb21fe8e7 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/GVMDependenciesNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/GVMDependenciesNode.cs @@ -55,6 +55,7 @@ public override IEnumerable GetStaticDependencies(NodeFacto { yield return new DependencyListEntry(factory.TypeGVMEntries(_method.OwningType.GetTypeDefinition()), "Resolution metadata"); } + yield return new DependencyListEntry(factory.AnalysisCharacteristic("GenericVirtualMethodsPresent"), "Runtime GVM resolution needed"); #endif } diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs index 32bec807844a6f..bf5f743320f7c2 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs @@ -107,6 +107,8 @@ protected internal enum ObjectNodeOrder ArrayOfEmbeddedDataNode, InterfaceDispatchCellInfoSection, InterfaceDispatchCellSection, + GvmDispatchCellInfoSection, + GvmDispatchCellSection, // // Wasm type signatures (need to be emitted some time before the unordered phase) diff --git a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormat.cs b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormat.cs index aa9664b09c226a..662cbc510dc98c 100644 --- a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormat.cs +++ b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormat.cs @@ -75,7 +75,7 @@ enum FixupSignatureKind : uint Null = 0x00, TypeHandle = 0x01, InterfaceCall = 0x02, - // unused = 0x03, + GvmDispatchCell = 0x03, MethodDictionary = 0x04, StaticData = 0x05, UnwrapNullableType = 0x06, diff --git a/src/coreclr/tools/Common/Internal/Runtime/DispatchCell.cs b/src/coreclr/tools/Common/Internal/Runtime/DispatchCell.cs index 84cd77d18c9ad4..416aa212883ade 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/DispatchCell.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/DispatchCell.cs @@ -1,24 +1,101 @@ // 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 System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using Internal.Runtime; +#if SYSTEM_PRIVATE_CORELIB +using Internal.Metadata.NativeFormat; +#endif namespace System.Runtime { [StructLayout(LayoutKind.Sequential)] - internal struct DispatchCell + public struct DispatchCell { public nint MethodTable; public nint Code; } +#if SYSTEM_PRIVATE_CORELIB [StructLayout(LayoutKind.Sequential)] - internal unsafe struct DynamicDispatchCell + [CLSCompliant(false)] + public unsafe struct DynamicDispatchCell { + private const nint GvmTypeFlag = 1; + public DispatchCell Cell; - public MethodTable* InterfaceType; - public nint Slot; + private nint _associatedTypeAndFlag; + + public bool IsGvmDispatchCell => ((nint)_associatedTypeAndFlag & GvmTypeFlag) != 0; + + public DynamicInterfaceDispatchCell* AsInterfaceDispatchCell() + { + return (DynamicInterfaceDispatchCell*)Unsafe.AsPointer(ref this); + } + + public DynamicGvmDispatchCell* AsGvmDispatchCell() + { + return (DynamicGvmDispatchCell*)Unsafe.AsPointer(ref this); + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe struct DynamicInterfaceDispatchCell + { + public DynamicDispatchCell DispatchCell; + public nint Slot; + + public nint InterfaceType + { + get + { + Debug.Assert(!DispatchCell.IsGvmDispatchCell); + return DispatchCell._associatedTypeAndFlag; + } + set + { + Debug.Assert((value & GvmTypeFlag) == 0); + DispatchCell._associatedTypeAndFlag = value; + } + } + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe struct DynamicGvmDispatchCell + { + public DynamicDispatchCell DispatchCell; + private MethodHandle _handle; + private bool _isAsyncVariant; + public void* Instantiation; + + public nint OwningType + { + get + { + Debug.Assert(DispatchCell.IsGvmDispatchCell); + return DispatchCell._associatedTypeAndFlag & ~GvmTypeFlag; + } + set + { + Debug.Assert((value & GvmTypeFlag) == 0); + DispatchCell._associatedTypeAndFlag = value | GvmTypeFlag; + } + } + + public MethodHandle Handle + { + get => _handle; + set => _handle = value; + } + + public bool IsAsyncVariant + { + get => _isAsyncVariant; + set => _isAsyncVariant = value; + } + } } +#endif } diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index 2ae792a640a3f7..d6814bdcafc2bd 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -102,6 +102,8 @@ enum ReadyToRunSectionType // 211 is unused - it was used by LoopHijackFlag ImportAddressTables = 212, ModuleInitializerList = 213, + GvmDispatchCellInfoRegion = 214, + GvmDispatchCellRegion = 215, // Sections 300 - 399 are reserved for RhFindBlob backwards compatibility ReadonlyBlobRegionStart = 300, diff --git a/src/coreclr/tools/Common/Internal/Runtime/RuntimeConstants.cs b/src/coreclr/tools/Common/Internal/Runtime/RuntimeConstants.cs index 629e738cb4022a..ec63216ece92c1 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/RuntimeConstants.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/RuntimeConstants.cs @@ -78,7 +78,7 @@ internal static class MethodFixupCellFlagsConstants public const int IsStdcall = 0x8; } - internal static class RuntimeMethodHandleConstants + internal static class GvmDispatchCellFlags { public const int IsAsyncVariant = unchecked((int)0x80000000); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs index ddcab414eb80d5..360b0404c2f65b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs @@ -255,7 +255,7 @@ public bool NeedsRuntimeLookup(ReadyToRunHelperId lookupKind, object targetOfLoo case ReadyToRunHelperId.MethodDictionary: case ReadyToRunHelperId.MethodEntry: - case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.DispatchCell: case ReadyToRunHelperId.MethodHandle: return ((MethodDesc)targetOfLookup).IsRuntimeDeterminedExactMethod; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DelegateCreationInfo.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DelegateCreationInfo.cs index fa5a5b2eef3477..a0f626fc8948b8 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DelegateCreationInfo.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DelegateCreationInfo.cs @@ -22,9 +22,8 @@ private enum TargetKind { CanonicalEntrypoint, ExactCallableAddress, - InterfaceDispatch, + Dispatch, VTableLookup, - MethodHandle, ConstrainedMethod, } @@ -74,7 +73,7 @@ public bool NeedsVirtualMethodUseTracking { get { - return _targetKind == TargetKind.VTableLookup || _targetKind == TargetKind.InterfaceDispatch; + return _targetKind == TargetKind.VTableLookup || (_targetKind == TargetKind.Dispatch && !TargetMethod.HasInstantiation); } } @@ -89,8 +88,7 @@ public bool NeedsRuntimeLookup case TargetKind.CanonicalEntrypoint: case TargetKind.ExactCallableAddress: - case TargetKind.InterfaceDispatch: - case TargetKind.MethodHandle: + case TargetKind.Dispatch: return TargetMethod.IsRuntimeDeterminedExactMethod; case TargetKind.ConstrainedMethod: @@ -116,11 +114,8 @@ public GenericLookupResult GetLookupKind(NodeFactory factory) case TargetKind.ExactCallableAddress: return factory.GenericLookup.MethodEntry(TargetMethod, TargetMethodIsUnboxingThunk); - case TargetKind.InterfaceDispatch: - return factory.GenericLookup.VirtualDispatchCell(TargetMethod); - - case TargetKind.MethodHandle: - return factory.GenericLookup.MethodHandle(TargetMethod); + case TargetKind.Dispatch: + return factory.GenericLookup.DispatchCell(TargetMethod); case TargetKind.ConstrainedMethod: return factory.GenericLookup.ConstrainedMethodUse(_targetMethod, _constrainedType, directCall: !_targetMethod.HasInstantiation); @@ -146,11 +141,8 @@ public ISymbolNode GetTargetNode(NodeFactory factory) case TargetKind.ExactCallableAddress: return factory.ExactCallableAddressTakenAddress(TargetMethod, TargetMethodIsUnboxingThunk); - case TargetKind.InterfaceDispatch: - return factory.InterfaceDispatchCell(TargetMethod); - - case TargetKind.MethodHandle: - return factory.RuntimeMethodHandle(TargetMethod); + case TargetKind.Dispatch: + return factory.DispatchCell(TargetMethod); case TargetKind.VTableLookup: Debug.Fail("Need to do runtime lookup"); @@ -256,7 +248,7 @@ public static DelegateCreationInfo Create(TypeDesc delegateType, MethodDesc targ if (followVirtualDispatch && targetMethod.IsVirtual) { initializeMethodName = "InitializeClosedInstanceWithGVMResolution"u8; - kind = TargetKind.MethodHandle; + kind = TargetKind.Dispatch; } else { @@ -278,7 +270,7 @@ public static DelegateCreationInfo Create(TypeDesc delegateType, MethodDesc targ { if (targetMethod.OwningType.IsInterface) { - kind = TargetKind.InterfaceDispatch; + kind = TargetKind.Dispatch; initializeMethodName = "InitializeClosedInstanceToInterface"u8; } else diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DispatchCellNode.cs similarity index 66% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs rename to src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DispatchCellNode.cs index 0c0a8624b51f47..c61e4fbf807eb8 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DispatchCellNode.cs @@ -11,7 +11,7 @@ namespace ILCompiler.DependencyAnalysis { - public sealed class InterfaceDispatchCellNode : SortableDependencyNode, ISymbolDefinitionNode + public sealed class DispatchCellNode : SortableDependencyNode, ISymbolDefinitionNode { private const int InvalidOffset = -1; @@ -24,9 +24,9 @@ public sealed class InterfaceDispatchCellNode : SortableDependencyNode, ISymbolD internal ISortableSymbolNode CallSiteIdentifier => _callSiteIdentifier; - public InterfaceDispatchCellNode(MethodDesc targetMethod, ISortableSymbolNode callSiteIdentifier) + public DispatchCellNode(MethodDesc targetMethod, ISortableSymbolNode callSiteIdentifier) { - Debug.Assert(targetMethod.OwningType.IsInterface); + Debug.Assert(targetMethod.HasInstantiation || targetMethod.OwningType.IsInterface); Debug.Assert(!targetMethod.IsSharedByGenericInstantiations); _targetMethod = targetMethod; _callSiteIdentifier = callSiteIdentifier; @@ -36,10 +36,10 @@ public InterfaceDispatchCellNode(MethodDesc targetMethod, ISortableSymbolNode ca public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix) - .Append("__InterfaceDispatchCell_"u8) + .Append("__DispatchCell_"u8) .Append(nameMangler.GetMangledMethodName(_targetMethod)); - if (_callSiteIdentifier != null) + if (_callSiteIdentifier is not null) { sb.Append('_'); _callSiteIdentifier.AppendMangledName(nameMangler, sb); @@ -77,42 +77,25 @@ public void InitializeOffset(int offset) public override bool StaticDependenciesAreComputed => true; - internal IEETypeNode GetInterfaceTypeNode(NodeFactory factory) + public override IEnumerable GetStaticDependencies(NodeFactory factory) { - // If this dispatch cell is ever used with an object that implements IDynamicIntefaceCastable, user code will - // see a RuntimeTypeHandle representing this interface. - if (factory.DevirtualizationManager.CanHaveDynamicInterfaceImplementations(_targetMethod.OwningType)) + if (_targetMethod.HasInstantiation) { - return factory.ConstructedTypeSymbol(_targetMethod.OwningType); + return GvmDispatchCellInfoSectionNode.GetCellDependencies(factory, _targetMethod); } else { - return factory.NecessaryTypeSymbol(_targetMethod.OwningType); + return InterfaceDispatchCellInfoSectionNode.GetCellDependencies(factory, _targetMethod); } } - public override IEnumerable GetStaticDependencies(NodeFactory factory) - { - DependencyList result = new DependencyList(); - - if (!factory.VTable(_targetMethod.OwningType).HasKnownVirtualMethodUse) - { - result.Add(factory.VirtualMethodUse(_targetMethod), "Interface method use"); - } - - factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref result, factory, _targetMethod); - - result.Add(GetInterfaceTypeNode(factory), "Interface type"); - - return result; - } - public override int ClassCode => -2023802120; public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { - var compare = comparer.Compare(_targetMethod, ((InterfaceDispatchCellNode)other)._targetMethod); - return compare != 0 ? compare : comparer.Compare(_callSiteIdentifier, ((InterfaceDispatchCellNode)other)._callSiteIdentifier); + var otherCell = (DispatchCellNode)other; + var compare = comparer.Compare(_targetMethod, otherCell._targetMethod); + return compare != 0 ? compare : comparer.Compare(_callSiteIdentifier, otherCell._callSiteIdentifier); } public bool RepresentsIndirectionCell => false; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellSectionNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DispatchCellSectionNode.cs similarity index 56% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellSectionNode.cs rename to src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DispatchCellSectionNode.cs index eb72b34e714860..336c8533fc2d1d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellSectionNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DispatchCellSectionNode.cs @@ -6,18 +6,16 @@ using Internal.Runtime; using Internal.Text; -using Internal.TypeSystem; - -using Debug = System.Diagnostics.Debug; namespace ILCompiler.DependencyAnalysis { /// - /// Represents a section of the executable where interface dispatch cells - /// are stored. + /// Represents a section of the executable where dispatch cells are stored. /// - public class InterfaceDispatchCellSectionNode : ObjectNode, ISymbolDefinitionNode + public abstract class DispatchCellSectionNode : ObjectNode, ISymbolDefinitionNode { + protected abstract bool ShouldEmitCell(DispatchCellNode cell); + public override ObjectData GetData(NodeFactory factory, bool relocsOnly) { if (relocsOnly) @@ -27,8 +25,11 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly) symbols.Add(this); int totalSize = 0; - foreach (InterfaceDispatchCellNode node in factory.MetadataManager.GetInterfaceDispatchCells()) + foreach (DispatchCellNode node in factory.MetadataManager.GetDispatchCells()) { + if (!ShouldEmitCell(node)) + continue; + symbols.Add(node); totalSize += node.Size; } @@ -36,14 +37,32 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly) return new ObjectData(new byte[totalSize], Array.Empty(), factory.Target.PointerSize, symbols.ToArray()); } - public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) - => sb.Append(nameMangler.CompilationUnitPrefix).Append("__InterfaceDispatchCellSection_Start"u8); + public abstract void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb); public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.BssSection; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); protected internal override int Phase => (int)ObjectNodePhase.Ordered; - public override int ClassCode => (int)ObjectNodeOrder.InterfaceDispatchCellSection; public int Offset => 0; public override bool IsShareable => false; public override bool StaticDependenciesAreComputed => true; } + + public class InterfaceDispatchCellSectionNode : DispatchCellSectionNode + { + protected override bool ShouldEmitCell(DispatchCellNode cell) => !cell.TargetMethod.HasInstantiation; + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + => sb.Append(nameMangler.CompilationUnitPrefix).Append("__InterfaceDispatchCellSection_Start"u8); + + public override int ClassCode => (int)ObjectNodeOrder.InterfaceDispatchCellSection; + } + + public class GvmDispatchCellSectionNode : DispatchCellSectionNode + { + protected override bool ShouldEmitCell(DispatchCellNode cell) => cell.TargetMethod.HasInstantiation; + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + => sb.Append(nameMangler.CompilationUnitPrefix).Append("__GvmDispatchCellSection_Start"u8); + + public override int ClassCode => (int)ObjectNodeOrder.GvmDispatchCellSection; + } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericLookupResult.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericLookupResult.cs index 86d6332a6279f6..48e119382659d2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericLookupResult.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericLookupResult.cs @@ -690,17 +690,17 @@ protected override bool EqualsImpl(GenericLookupResult obj) /// /// Generic lookup result that points to a dispatch cell. /// - internal sealed class VirtualDispatchCellGenericLookupResult : GenericLookupResult + internal sealed class DispatchCellGenericLookupResult : GenericLookupResult { private MethodDesc _method; protected override int ClassCode => 643566930; - public VirtualDispatchCellGenericLookupResult(MethodDesc method) + public DispatchCellGenericLookupResult(MethodDesc method) { Debug.Assert(method.IsRuntimeDeterminedExactMethod); Debug.Assert(method.IsVirtual); - Debug.Assert(method.OwningType.IsInterface); + Debug.Assert(method.HasInstantiation || method.OwningType.IsInterface); _method = method; } @@ -724,7 +724,7 @@ public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultCo dictionary = null; } - return factory.InterfaceDispatchCell(instantiatedMethod, dictionary); + return factory.DispatchCell(instantiatedMethod, dictionary); } else { @@ -748,12 +748,12 @@ public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilde public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory) { - return factory.NativeLayout.InterfaceCellDictionarySlot(_method); + return _method.HasInstantiation ? factory.NativeLayout.GvmCellDictionarySlot(_method) : factory.NativeLayout.InterfaceCellDictionarySlot(_method); } protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer) { - return comparer.Compare(_method, ((VirtualDispatchCellGenericLookupResult)other)._method); + return comparer.Compare(_method, ((DispatchCellGenericLookupResult)other)._method); } protected override int GetHashCodeImpl() @@ -763,7 +763,7 @@ protected override int GetHashCodeImpl() protected override bool EqualsImpl(GenericLookupResult obj) { - return ((VirtualDispatchCellGenericLookupResult)obj)._method == _method; + return ((DispatchCellGenericLookupResult)obj)._method == _method; } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GvmDispatchCellInfoSectionNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GvmDispatchCellInfoSectionNode.cs new file mode 100644 index 00000000000000..97ec014cede7c7 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GvmDispatchCellInfoSectionNode.cs @@ -0,0 +1,139 @@ +// 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 Internal.Runtime; +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; +using DependencyListEntry = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyListEntry; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a section of the executable where information about GVM dispatch cells + /// is stored. + /// + public class GvmDispatchCellInfoSectionNode : ObjectNode, ISymbolDefinitionNode + { + public override ObjectData GetData(NodeFactory factory, bool relocsOnly) + { + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, Array.Empty()); + + var builder = new ObjectDataBuilder(factory, relocsOnly); + builder.AddSymbol(this); + + builder.RequireInitialAlignment(factory.Target.PointerSize); + + int currentDispatchCellOffset = 0; + foreach (DispatchCellNode node in new SortedSet(factory.MetadataManager.GetDispatchCells(), new DispatchCellComparer())) + { + MethodDesc targetMethod = node.TargetMethod; + if (!targetMethod.HasInstantiation) + continue; + + node.InitializeOffset(currentDispatchCellOffset); + + int token = factory.MetadataManager.GetMetadataHandleForMethod(factory, GetMethodForMetadata(targetMethod, out bool isAsyncVariant)); + int flags = isAsyncVariant ? GvmDispatchCellFlags.IsAsyncVariant : 0; + int flagsAndToken = (token & MetadataManager.MetadataOffsetMask) | flags; + + if (factory.Target.SupportsRelativePointers) + { + builder.EmitReloc(factory.MaximallyConstructableType(targetMethod.OwningType), RelocType.IMAGE_REL_BASED_RELPTR32); + builder.EmitReloc(factory.ConstructedGenericComposition(targetMethod.Instantiation), RelocType.IMAGE_REL_BASED_RELPTR32); + builder.EmitInt(flagsAndToken); + } + else + { + builder.EmitPointerReloc(factory.MaximallyConstructableType(targetMethod.OwningType)); + builder.EmitPointerReloc(factory.ConstructedGenericComposition(targetMethod.Instantiation)); + builder.EmitNaturalInt(flagsAndToken); + } + + currentDispatchCellOffset += node.Size; + } + + return builder.ToObjectData(); + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + => sb.Append(nameMangler.CompilationUnitPrefix).Append("__GvmDispatchCellInfoSection_Start"u8); + public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.ReadOnlyDataSection; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + + public override int ClassCode => (int)ObjectNodeOrder.GvmDispatchCellInfoSection; + + public int Offset => 0; + + public override bool IsShareable => false; + + public override bool StaticDependenciesAreComputed => true; + + public static IEnumerable GetCellDependencies(NodeFactory factory, MethodDesc targetMethod) + { + DependencyList result = new DependencyList(); + + MethodDesc canonMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + result.Add(factory.GVMDependencies(canonMethod), "GVM dependencies"); + + // GVM analysis happens on canonical forms, but this is potentially injecting new genericness + // into the system. Ensure reflection analysis can still see this. + if (targetMethod.IsAbstract) + factory.MetadataManager.GetDependenciesDueToMethodCodePresence(ref result, factory, canonMethod, methodIL: null); + + factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref result, factory, targetMethod); + + factory.MetadataManager.GetNativeLayoutMetadataDependencies(ref result, factory, GetMethodForMetadata(targetMethod, out _)); + + result.Add(factory.MaximallyConstructableType(targetMethod.OwningType), "Owning type of GVM decl"); + result.Add(factory.ConstructedGenericComposition(targetMethod.Instantiation), "GVM instantiation info"); + + return result; + } + + public static MethodDesc GetMethodForMetadata(MethodDesc method, out bool isAsyncVariant) + { + isAsyncVariant = false; + MethodDesc targetMethodForMetadata = method.GetTypicalMethodDefinition(); + if (targetMethodForMetadata.IsAsyncVariant()) + { + targetMethodForMetadata = ((CompilerTypeSystemContext)method.Context).GetTargetOfAsyncVariantMethod(targetMethodForMetadata); + isAsyncVariant = true; + } + + return targetMethodForMetadata; + } + + /// + /// Comparer that groups GVM dispatch cells by their callsite. + /// + private sealed class DispatchCellComparer : IComparer + { + private readonly CompilerComparer _comparer = CompilerComparer.Instance; + + public int Compare(DispatchCellNode x, DispatchCellNode y) + { + int result = _comparer.Compare(x.CallSiteIdentifier, y.CallSiteIdentifier); + if (result != 0) + return result; + + MethodDesc methodX = x.TargetMethod; + MethodDesc methodY = y.TargetMethod; + + result = _comparer.Compare(methodX, methodY); + if (result != 0) + return result; + + Debug.Assert(x == y); + return 0; + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellInfoSectionNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellInfoSectionNode.cs index e4e065c0e349cc..25c39a6ad9da1f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellInfoSectionNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellInfoSectionNode.cs @@ -9,6 +9,8 @@ using Internal.TypeSystem; using Debug = System.Diagnostics.Debug; +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; +using DependencyListEntry = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyListEntry; namespace ILCompiler.DependencyAnalysis { @@ -29,14 +31,17 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly) builder.RequireInitialAlignment(factory.Target.PointerSize); int currentDispatchCellOffset = 0; - foreach (InterfaceDispatchCellNode node in new SortedSet(factory.MetadataManager.GetInterfaceDispatchCells(), new DispatchCellComparer())) + foreach (DispatchCellNode node in new SortedSet(factory.MetadataManager.GetDispatchCells(), new DispatchCellComparer())) { MethodDesc targetMethod = node.TargetMethod; + if (targetMethod.HasInstantiation) + continue; + int targetSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); node.InitializeOffset(currentDispatchCellOffset); - IEETypeNode interfaceType = node.GetInterfaceTypeNode(factory); + IEETypeNode interfaceType = GetInterfaceTypeNode(factory, targetMethod); if (factory.Target.SupportsRelativePointers) { builder.EmitReloc(interfaceType, RelocType.IMAGE_REL_BASED_RELPTR32); @@ -69,14 +74,44 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) public override bool StaticDependenciesAreComputed => true; + public static IEnumerable GetCellDependencies(NodeFactory factory, MethodDesc targetMethod) + { + DependencyList result = new DependencyList(); + + if (!factory.VTable(targetMethod.OwningType).HasKnownVirtualMethodUse) + { + result.Add(factory.VirtualMethodUse(targetMethod), "Interface method use"); + } + + factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref result, factory, targetMethod); + + result.Add(GetInterfaceTypeNode(factory, targetMethod), "Interface type"); + + return result; + } + + private static IEETypeNode GetInterfaceTypeNode(NodeFactory factory, MethodDesc targetMethod) + { + // If this dispatch cell is ever used with an object that implements IDynamicIntefaceCastable, user code will + // see a RuntimeTypeHandle representing this interface. + if (factory.DevirtualizationManager.CanHaveDynamicInterfaceImplementations(targetMethod.OwningType)) + { + return factory.ConstructedTypeSymbol(targetMethod.OwningType); + } + else + { + return factory.NecessaryTypeSymbol(targetMethod.OwningType); + } + } + /// /// Comparer that groups interface dispatch cells by their callsite. /// - private sealed class DispatchCellComparer : IComparer + private sealed class DispatchCellComparer : IComparer { private readonly CompilerComparer _comparer = CompilerComparer.Instance; - public int Compare(InterfaceDispatchCellNode x, InterfaceDispatchCellNode y) + public int Compare(DispatchCellNode x, DispatchCellNode y) { int result = _comparer.Compare(x.CallSiteIdentifier, y.CallSiteIdentifier); if (result != 0) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs index 0953abf3ad6e7e..e93b8c6a73f6a2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs @@ -1279,6 +1279,49 @@ protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeF } } + public sealed class NativeLayoutGvmDispatchGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode + { + private MethodDesc _method; + + public NativeLayoutGvmDispatchGenericDictionarySlotNode(NodeFactory factory, MethodDesc method) + { + Debug.Assert(method.HasInstantiation); + _method = method; + } + + protected sealed override string GetName(NodeFactory factory) => "NativeLayoutGvmDispatchGenericDictionarySlotNode_" + factory.NameMangler.GetMangledMethodName(_method); + + protected sealed override FixupSignatureKind SignatureKind => FixupSignatureKind.GvmDispatchCell; + + public sealed override IEnumerable GetStaticDependencies(NodeFactory factory) + { + var result = new DependencyList(); + + foreach (var dependency in factory.NativeLayout.TemplateConstructableTypes(_method.OwningType)) + { + result.Add(dependency, "template construction dependency for method OwningType"); + } + + foreach (var type in _method.Instantiation) + { + foreach (var dependency in factory.NativeLayout.TemplateConstructableTypes(type)) + result.Add(dependency, "template construction dependency for method Instantiation types"); + } + + MethodDesc canonMethod = _method.GetCanonMethodTarget(CanonicalFormKind.Specific); + result.Add(factory.GVMDependencies(canonMethod), "GVM dependencies"); + factory.MetadataManager.GetNativeLayoutMetadataDependencies(ref result, factory, GvmDispatchCellInfoSectionNode.GetMethodForMetadata(_method, out _)); + result.Add(factory.NativeLayout.MethodEntry(_method), "wrappednode"); + + return result; + } + + protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory) + { + return factory.NativeLayout.MethodEntry(_method).WriteVertex(factory); + } + } + public sealed class NativeLayoutMethodDictionaryGenericDictionarySlotNode : NativeLayoutGenericDictionarySlotNode { private MethodDesc _method; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs index 8e6bbaef113d21..e88edaff241c83 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs @@ -62,9 +62,9 @@ private void CreateNodeCaches() return new MethodEntryGenericLookupResult(key.Method, key.IsUnboxingStub); }); - _virtualDispatchCells = new NodeCache(method => + _dispatchCells = new NodeCache(method => { - return new VirtualDispatchCellGenericLookupResult(method); + return new DispatchCellGenericLookupResult(method); }); _typeThreadStaticBaseIndexSymbols = new NodeCache(type => @@ -179,11 +179,11 @@ public GenericLookupResult MethodDictionary(MethodDesc method) return _methodDictionaries.GetOrAdd(method); } - private NodeCache _virtualDispatchCells; + private NodeCache _dispatchCells; - public GenericLookupResult VirtualDispatchCell(MethodDesc method) + public GenericLookupResult DispatchCell(MethodDesc method) { - return _virtualDispatchCells.GetOrAdd(method); + return _dispatchCells.GetOrAdd(method); } private NodeCache _methodEntrypoints; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs index 5918b71a451b34..7921345e26077f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs @@ -112,7 +112,12 @@ private void CreateNodeCaches() return new NativeLayoutInterfaceDispatchGenericDictionarySlotNode(_factory, method); }); - _methodDictionary_GenericDictionarySlots = new NodeCache(method => + _gvmCell_GenericDictionarySlots = new NodeCache(method => + { + return new NativeLayoutGvmDispatchGenericDictionarySlotNode(_factory, method); + }); + + _methodDictionary_GenericDictionarySlots = new NodeCache(method => { return new NativeLayoutMethodDictionaryGenericDictionarySlotNode(_factory, method); }); @@ -379,6 +384,12 @@ public NativeLayoutInterfaceDispatchGenericDictionarySlotNode InterfaceCellDicti return _interfaceCell_GenericDictionarySlots.GetOrAdd(method); } + private NodeCache _gvmCell_GenericDictionarySlots; + public NativeLayoutGvmDispatchGenericDictionarySlotNode GvmCellDictionarySlot(MethodDesc method) + { + return _gvmCell_GenericDictionarySlots.GetOrAdd(method); + } + private NodeCache _methodDictionary_GenericDictionarySlots; public NativeLayoutMethodDictionaryGenericDictionarySlotNode MethodDictionaryDictionarySlot(MethodDesc method) { 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 144683183dc978..6f7e93106e9e11 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -450,9 +450,9 @@ private void CreateNodeCaches() return new FrozenRuntimeTypeNode(key, withMetadata: false); }); - _interfaceDispatchCells = new NodeCache(callSiteCell => + _dispatchCells = new NodeCache(callSiteCell => { - return new InterfaceDispatchCellNode(callSiteCell.Target, callSiteCell.CallsiteId); + return new DispatchCellNode(callSiteCell.Target, callSiteCell.CallsiteId); }); _interfaceDispatchMaps = new NodeCache((TypeDesc type) => @@ -874,11 +874,11 @@ public ISortableSymbolNode TypeThreadStaticIndex(MetadataType type) } } - private NodeCache _interfaceDispatchCells; + private NodeCache _dispatchCells; - public InterfaceDispatchCellNode InterfaceDispatchCell(MethodDesc method, ISortableSymbolNode callSite = null) + public DispatchCellNode DispatchCell(MethodDesc method, ISortableSymbolNode callSite = null) { - return _interfaceDispatchCells.GetOrAdd(new DispatchCellKey(method, callSite)); + return _dispatchCells.GetOrAdd(new DispatchCellKey(method, callSite)); } private NodeCache _runtimeMethodHandles; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs index 536c347ba3fdf4..ec6fa5d049cd3f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs @@ -75,8 +75,8 @@ public static GenericLookupResult GetLookupSignature(NodeFactory factory, ReadyT return factory.GenericLookup.TypeThreadStaticBaseIndex((TypeDesc)target); case ReadyToRunHelperId.MethodDictionary: return factory.GenericLookup.MethodDictionary((MethodDesc)target); - case ReadyToRunHelperId.VirtualDispatchCell: - return factory.GenericLookup.VirtualDispatchCell((MethodDesc)target); + case ReadyToRunHelperId.DispatchCell: + return factory.GenericLookup.DispatchCell((MethodDesc)target); case ReadyToRunHelperId.MethodEntry: return factory.GenericLookup.MethodEntry((MethodDesc)target); case ReadyToRunHelperId.DelegateCtor: @@ -316,7 +316,7 @@ public override int CompareToImpl(ISortableNode other, CompilerComparer comparer return comparer.Compare((TypeDesc)_target, (TypeDesc)((ReadyToRunGenericHelperNode)other)._target); case ReadyToRunHelperId.MethodHandle: case ReadyToRunHelperId.MethodDictionary: - case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.DispatchCell: case ReadyToRunHelperId.MethodEntry: return comparer.Compare((MethodDesc)_target, (MethodDesc)((ReadyToRunGenericHelperNode)other)._target); case ReadyToRunHelperId.FieldHandle: diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs index fe96e754c79882..0109f977ea27ad 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs @@ -37,7 +37,7 @@ public enum ReadyToRunHelperId MethodDictionary, TypeDictionary, MethodEntry, - VirtualDispatchCell, + DispatchCell, DefaultConstructor, TypeHandleForCasting, ObjectAllocator, diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs index fd12d1a30883bb..18462fbdc15e8e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs @@ -18,6 +18,7 @@ public class RuntimeMethodHandleNode : DehydratableObjectNode, ISymbolDefinition public RuntimeMethodHandleNode(MethodDesc targetMethod) { Debug.Assert(!targetMethod.IsSharedByGenericInstantiations); + Debug.Assert(!targetMethod.IsAsyncVariant()); // IL is allowed to LDTOKEN an uninstantiated thing. Do not check IsRuntimeDetermined for the nonexact thing. Debug.Assert((targetMethod.HasInstantiation && targetMethod.IsMethodDefinition) @@ -74,29 +75,20 @@ protected override ObjectData GetDehydratableData(NodeFactory factory, bool relo objData.RequireInitialPointerAlignment(); objData.AddSymbol(this); - int flags = 0; - - MethodDesc targetMethodForMetadata = _targetMethod.GetTypicalMethodDefinition(); - if (targetMethodForMetadata.IsAsyncVariant()) - { - targetMethodForMetadata = factory.TypeSystemContext.GetTargetOfAsyncVariantMethod(targetMethodForMetadata); - flags |= RuntimeMethodHandleConstants.IsAsyncVariant; - } - - int handle = relocsOnly ? 0 : factory.MetadataManager.GetMetadataHandleForMethod(factory, targetMethodForMetadata); + int handle = relocsOnly ? 0 : factory.MetadataManager.GetMetadataHandleForMethod(factory, _targetMethod.GetTypicalMethodDefinition()); objData.EmitPointerReloc(factory.MaximallyConstructableType(_targetMethod.OwningType)); objData.EmitInt(handle); if (_targetMethod != _targetMethod.GetMethodDefinition()) { - objData.EmitInt(_targetMethod.Instantiation.Length | flags); + objData.EmitInt(_targetMethod.Instantiation.Length); foreach (TypeDesc instParam in _targetMethod.Instantiation) objData.EmitPointerReloc(factory.NecessaryTypeSymbol(instParam)); } else { - objData.EmitInt(0 | flags); + objData.EmitInt(0); } return objData.ToObjectData(); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunGenericHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunGenericHelperNode.cs index 9a3a50d5f55612..0221403cae3669 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunGenericHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunGenericHelperNode.cs @@ -194,7 +194,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref ARMEmitter enco case ReadyToRunHelperId.FieldHandle: case ReadyToRunHelperId.MethodDictionary: case ReadyToRunHelperId.MethodEntry: - case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.DispatchCell: case ReadyToRunHelperId.DefaultConstructor: case ReadyToRunHelperId.ObjectAllocator: case ReadyToRunHelperId.TypeHandleForCasting: diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs index f02a6c9495c5e3..d78df7ae589309 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs @@ -130,7 +130,7 @@ protected override void EmitCode(NodeFactory factory, ref ARMEmitter encoder, bo MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { - encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); + encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.DispatchCell(targetMethod)); encoder.EmitJMP(factory.ExternFunctionSymbol(s_RhpResolveInterfaceMethod)); } else diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunGenericHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunGenericHelperNode.cs index bee77707a3d380..96fb057460d644 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunGenericHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunGenericHelperNode.cs @@ -192,7 +192,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref ARM64Emitter en case ReadyToRunHelperId.FieldHandle: case ReadyToRunHelperId.MethodDictionary: case ReadyToRunHelperId.MethodEntry: - case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.DispatchCell: case ReadyToRunHelperId.DefaultConstructor: case ReadyToRunHelperId.ObjectAllocator: case ReadyToRunHelperId.TypeHandleForCasting: diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs index ed2385426644d9..74345772b93cd6 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs @@ -145,7 +145,7 @@ protected override void EmitCode(NodeFactory factory, ref ARM64Emitter encoder, MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { - encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); + encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.DispatchCell(targetMethod)); encoder.EmitJMP(factory.ExternFunctionSymbol(s_RhpResolveInterfaceMethod)); } else diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunGenericHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunGenericHelperNode.cs index 609ab259aeeebe..4f804e2900892e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunGenericHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunGenericHelperNode.cs @@ -194,7 +194,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref LoongArch64Emit case ReadyToRunHelperId.FieldHandle: case ReadyToRunHelperId.MethodDictionary: case ReadyToRunHelperId.MethodEntry: - case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.DispatchCell: case ReadyToRunHelperId.DefaultConstructor: case ReadyToRunHelperId.ObjectAllocator: case ReadyToRunHelperId.TypeHandleForCasting: diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs index 9e7bf0d062914f..55f6ca6060f1b9 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs @@ -137,7 +137,7 @@ protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter enc MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { - encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); + encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.DispatchCell(targetMethod)); encoder.EmitJMP(factory.ExternFunctionSymbol(s_RhpResolveInterfaceMethod)); } else diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunGenericHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunGenericHelperNode.cs index 397054c946422b..5878bdcd0d845f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunGenericHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunGenericHelperNode.cs @@ -191,7 +191,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref RiscV64Emitter case ReadyToRunHelperId.FieldHandle: case ReadyToRunHelperId.MethodDictionary: case ReadyToRunHelperId.MethodEntry: - case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.DispatchCell: case ReadyToRunHelperId.DefaultConstructor: case ReadyToRunHelperId.ObjectAllocator: case ReadyToRunHelperId.TypeHandleForCasting: diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunHelperNode.cs index cc30e7da4d05fb..105c5cd68740cd 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunHelperNode.cs @@ -135,7 +135,7 @@ protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter encoder MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { - encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); + encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.DispatchCell(targetMethod)); encoder.EmitJMP(factory.ExternFunctionSymbol(s_RhpResolveInterfaceMethod)); } else diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs index 3bdb19a24ac5ac..55f3920225153e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs @@ -202,7 +202,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref X64Emitter enco case ReadyToRunHelperId.FieldHandle: case ReadyToRunHelperId.MethodDictionary: case ReadyToRunHelperId.MethodEntry: - case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.DispatchCell: case ReadyToRunHelperId.DefaultConstructor: case ReadyToRunHelperId.ObjectAllocator: case ReadyToRunHelperId.TypeHandleForCasting: diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs index 9c2c98275b8066..a67d543ee32483 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs @@ -150,7 +150,7 @@ protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bo MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { - encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); + encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.DispatchCell(targetMethod)); encoder.EmitJMP(factory.ExternFunctionSymbol(s_RhpResolveInterfaceMethod)); } else diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunGenericHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunGenericHelperNode.cs index 022b87dd817fd7..9f4eb2b7e8e159 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunGenericHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunGenericHelperNode.cs @@ -196,7 +196,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref X86Emitter enco case ReadyToRunHelperId.FieldHandle: case ReadyToRunHelperId.MethodDictionary: case ReadyToRunHelperId.MethodEntry: - case ReadyToRunHelperId.VirtualDispatchCell: + case ReadyToRunHelperId.DispatchCell: case ReadyToRunHelperId.DefaultConstructor: case ReadyToRunHelperId.ObjectAllocator: case ReadyToRunHelperId.TypeHandleForCasting: diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunHelperNode.cs index 3b42420932847f..5a17760ece65c9 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunHelperNode.cs @@ -159,7 +159,7 @@ protected override void EmitCode(NodeFactory factory, ref X86Emitter encoder, bo MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { - encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); + encoder.EmitMOV(encoder.TargetRegister.Arg1, factory.DispatchCell(targetMethod)); encoder.EmitJMP(factory.ExternFunctionSymbol(s_RhpResolveInterfaceMethod)); } else diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs index 332aa401bca13b..8c6140100a26ec 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs @@ -318,7 +318,7 @@ public static void GetEntryPoint(TypeSystemContext context, ReadyToRunHelper id, break; case ReadyToRunHelper.GVMLookupForSlot: - methodDesc = context.SystemModule.GetKnownType("System.Runtime"u8, "TypeLoaderExports"u8).GetKnownMethod("GVMLookupForSlot"u8, null); + mangledName = "RhpDispatchResolve"; break; case ReadyToRunHelper.TypeHandleToRuntimeType: diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index 6d99ee84ba317c..f3b9a941d9255d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -62,7 +62,7 @@ public abstract class MetadataManager : ICompilationRootProvider protected readonly ManifestResourceBlockingPolicy _resourceBlockingPolicy; protected readonly DynamicInvokeThunkGenerationPolicy _dynamicInvokeThunkGenerationPolicy; - private readonly List _interfaceDispatchCells = new List(); + private readonly List _dispatchCells = new List(); private readonly SortedSet _cctorContextsGenerated = new SortedSet(CompilerComparer.Instance); private readonly SortedSet _typesWithGCStaticsGenerated = new SortedSet(CompilerComparer.Instance); private readonly SortedSet _typesWithNonGCStaticsGenerated = new SortedSet(CompilerComparer.Instance); @@ -231,6 +231,12 @@ public virtual void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFacto var interfaceDispatchCellNode = new InterfaceDispatchCellSectionNode(); header.Add(ReadyToRunSectionType.InterfaceDispatchCellRegion, interfaceDispatchCellNode); + var gvmDispatchCellInfoNode = new GvmDispatchCellInfoSectionNode(); + header.Add(ReadyToRunSectionType.GvmDispatchCellInfoRegion, gvmDispatchCellInfoNode); + + var gvmDispatchCellNode = new GvmDispatchCellSectionNode(); + header.Add(ReadyToRunSectionType.GvmDispatchCellRegion, gvmDispatchCellNode); + // The external references tables should go last header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeReferences), nativeReferencesTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeStatics), nativeStaticsTableNode); @@ -314,9 +320,9 @@ protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) _genericDictionariesGenerated.Add(dictionaryNode); } - if (obj is InterfaceDispatchCellNode dispatchCell) + if (obj is DispatchCellNode dispatchCell) { - _interfaceDispatchCells.Add(dispatchCell); + _dispatchCells.Add(dispatchCell); } if (obj is StructMarshallingDataNode structMarshallingDataNode) @@ -1050,9 +1056,9 @@ public IEnumerable GetReflectionStackTraceMappings( return _reflectionStackTraceMappings; } - internal IEnumerable GetInterfaceDispatchCells() + internal IEnumerable GetDispatchCells() { - return _interfaceDispatchCells; + return _dispatchCells; } internal IEnumerable GetCctorContextMapping() diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs index c61590199c91f9..abf25c9c5dcf4e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs @@ -899,11 +899,11 @@ private void ImportCall(ILOpcode opcode, MethodDesc method, MethodDesc runtimeDe if (exactContextNeedsRuntimeLookup) { - _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodHandle, methodToLookup), reason); + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.DispatchCell, methodToLookup), reason); } else { - _dependencies.Add(_factory.RuntimeMethodHandle(methodToLookup), reason); + _dependencies.Add(_factory.DispatchCell(methodToLookup), reason); } _dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GVMLookupForSlot), reason); @@ -912,11 +912,11 @@ private void ImportCall(ILOpcode opcode, MethodDesc method, MethodDesc runtimeDe { if (exactContextNeedsRuntimeLookup) { - _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.VirtualDispatchCell, runtimeDeterminedMethod), reason); + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.DispatchCell, runtimeDeterminedMethod), reason); } else { - _dependencies.Add(_factory.InterfaceDispatchCell(method), reason); + _dependencies.Add(_factory.DispatchCell(method), reason); } } else if (_compilation.NeedsSlotUseTracking(method.OwningType)) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index de3562566c9a71..7cf82729635c95 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -473,9 +473,11 @@ + + + - @@ -596,7 +598,6 @@ - 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 41a70f79c87f65..379becc9189fbf 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs @@ -116,7 +116,7 @@ private void CreateNodeCaches() ); }); - _interfaceDispatchCells = new NodeCache(cellKey => + _dispatchCells = new NodeCache(cellKey => { return new DelayLoadHelperMethodImport( _codegenNodeFactory, @@ -470,12 +470,12 @@ public Import RvaFieldAddress(FieldWithToken fieldWithToken) return _rvaFieldAddressCache.GetOrAdd(fieldWithToken); } - private NodeCache _interfaceDispatchCells = new NodeCache(); + private NodeCache _dispatchCells = new NodeCache(); - public Import InterfaceDispatchCell(MethodWithToken method, MethodDesc callingMethod) + public Import DispatchCell(MethodWithToken method, MethodDesc callingMethod) { MethodAndCallSite cellKey = new MethodAndCallSite(method, null); - return _interfaceDispatchCells.GetOrAdd(cellKey); + return _dispatchCells.GetOrAdd(cellKey); } private NodeCache _delegateCtors = new NodeCache(); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 6537dd111808a6..ed683b728a0914 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -2588,7 +2588,7 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO } pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( - _compilation.SymbolNodeFactory.InterfaceDispatchCell( + _compilation.SymbolNodeFactory.DispatchCell( ComputeMethodWithToken(targetMethod, ref pResolvedToken, constrainedType: null, unboxing: false), MethodBeingCompiled)); diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 185f3863a3eb8f..342f111874de7e 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -1601,11 +1601,29 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO ((MethodILScope)HandleToObject((void*)pResolvedToken.tokenScope)).OwningMethod, targetOfLookup.GetCanonMethodTarget(CanonicalFormKind.Specific)); - ComputeLookup(ref pResolvedToken, - targetOfLookup, - ReadyToRunHelperId.MethodHandle, - HandleToObject(callerHandle), - ref pResult->codePointerOrStubLookup); + + if (pResult->exactContextNeedsRuntimeLookup) + { + ComputeLookup(ref pResolvedToken, + targetOfLookup, + ReadyToRunHelperId.DispatchCell, + HandleToObject(callerHandle), + ref pResult->codePointerOrStubLookup); + Debug.Assert(pResult->codePointerOrStubLookup.lookupKind.needsRuntimeLookup); + } + else + { + pResult->codePointerOrStubLookup.lookupKind.needsRuntimeLookup = false; + pResult->codePointerOrStubLookup.constLookup.accessType = InfoAccessType.IAT_VALUE; +#pragma warning disable SA1001, SA1113, SA1115 // Commas should be spaced correctly + pResult->codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle( + _compilation.NodeFactory.DispatchCell(targetOfLookup +#if !SUPPORT_JIT + , _methodCodeNode +#endif + )); +#pragma warning restore SA1001, SA1113, SA1115 // Commas should be spaced correctly + } // RyuJIT will assert if we report CORINFO_CALLCONV_PARAMTYPE for a result of a ldvirtftn // We don't need an instantiation parameter, so let's just not report it. Might be nice to @@ -1621,7 +1639,7 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO { ComputeLookup(ref pResolvedToken, GetRuntimeDeterminedObjectForToken(ref pResolvedToken), - ReadyToRunHelperId.VirtualDispatchCell, + ReadyToRunHelperId.DispatchCell, HandleToObject(callerHandle), ref pResult->codePointerOrStubLookup); Debug.Assert(pResult->codePointerOrStubLookup.lookupKind.needsRuntimeLookup); @@ -1632,7 +1650,7 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO pResult->codePointerOrStubLookup.constLookup.accessType = InfoAccessType.IAT_PVALUE; #pragma warning disable SA1001, SA1113, SA1115 // Commas should be spaced correctly pResult->codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle( - _compilation.NodeFactory.InterfaceDispatchCell(targetMethod + _compilation.NodeFactory.DispatchCell(targetMethod #if !SUPPORT_JIT , _methodCodeNode #endif From 3709bc0a5ee8ca47195d74f078c454f384df7f8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Wed, 24 Jun 2026 13:29:34 +0900 Subject: [PATCH 4/4] CR & CI feedback --- src/coreclr/nativeaot/Runtime/EHHelpers.cpp | 34 +++++++++++++++---- .../Runtime/i386/DispatchResolve.asm | 1 + .../Runtime/loongarch64/DispatchResolve.S | 7 ---- .../Runtime/riscv64/DispatchResolve.S | 7 ---- .../src/Internal/Runtime/Dispatch.cs | 22 ++++++------ .../src/Internal/Runtime/Dispatch.cs | 4 +-- 6 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/EHHelpers.cpp b/src/coreclr/nativeaot/Runtime/EHHelpers.cpp index ce5fae6a71a1f6..bad5b62eaa2a7f 100644 --- a/src/coreclr/nativeaot/Runtime/EHHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/EHHelpers.cpp @@ -286,13 +286,9 @@ static bool InInterfaceDispatchHelper(uintptr_t faultingIP) { #if defined(TARGET_ARM) (uintptr_t)&RhpInterfaceDispatchAVLocation, - (uintptr_t)&RhpDispatchResolveAVLocation, #else (uintptr_t)&RhpInterfaceDispatch, #endif -#if !defined(TARGET_ARM) - (uintptr_t)&RhpDispatchResolve, -#endif #if defined(TARGET_WINDOWS) && (defined(TARGET_AMD64) || defined(TARGET_ARM64)) (uintptr_t)&RhpInterfaceDispatchGuarded, #endif @@ -315,6 +311,26 @@ static bool InInterfaceDispatchHelper(uintptr_t faultingIP) return false; } +static bool InInterfaceResolveHelper(uintptr_t faultingIP) +{ +#ifndef FEATURE_PORTABLE_HELPERS +#if defined(TARGET_ARM) + return faultingIP == (uintptr_t)&RhpDispatchResolveAVLocation; +#else + uintptr_t interfaceResolveAVLocation = (uintptr_t)&RhpDispatchResolve; +#if defined(HOST_AMD64) || defined(HOST_X86) + // Verify that the runtime is not linked with incremental linking enabled. Incremental linking + // wraps every method symbol with a jump stub that breaks the following check. + ASSERT(*(uint8_t*)interfaceResolveAVLocation != 0xE9); // jmp XXXXXXXX +#endif + + return interfaceResolveAVLocation == faultingIP; +#endif +#else + return false; +#endif // FEATURE_PORTABLE_HELPERS +} + static uintptr_t UnwindSimpleHelperToCaller( #ifdef TARGET_UNIX PAL_LIMITED_CONTEXT * pContext @@ -325,7 +341,9 @@ static uintptr_t UnwindSimpleHelperToCaller( { #if defined(_DEBUG) uintptr_t faultingIP = pContext->GetIp(); - ASSERT(InWriteBarrierHelper(faultingIP) || InInterfaceDispatchHelper(faultingIP)); + ASSERT(InWriteBarrierHelper(faultingIP) || + InInterfaceDispatchHelper(faultingIP) || + InInterfaceResolveHelper(faultingIP)); #endif #if defined(HOST_AMD64) || defined(HOST_X86) // simulate a ret instruction @@ -386,8 +404,9 @@ int32_t RhpHardwareExceptionHandler(uintptr_t faultCode, uintptr_t faultAddress, // Could still be an AV in one of our assembly helpers that we know how to handle. bool inWriteBarrierHelper = InWriteBarrierHelper(faultingIP); bool inInterfaceDispatchHelper = InInterfaceDispatchHelper(faultingIP); + bool inInterfaceResolveHelper = InInterfaceResolveHelper(faultingIP); - if (inWriteBarrierHelper || inInterfaceDispatchHelper) + if (inWriteBarrierHelper || inInterfaceDispatchHelper || inInterfaceResolveHelper) { if (faultAddress < NULL_AREA_SIZE) { @@ -524,8 +543,9 @@ LONG WINAPI RhpVectoredExceptionHandler(PEXCEPTION_POINTERS pExPtrs) // Could still be an AV in one of our assembly helpers that we know how to handle. bool inWriteBarrierHelper = InWriteBarrierHelper(faultingIP); bool inInterfaceDispatchHelper = InInterfaceDispatchHelper(faultingIP); + bool inInterfaceResolveHelper = InInterfaceResolveHelper(faultingIP); - if (inWriteBarrierHelper || inInterfaceDispatchHelper) + if (inWriteBarrierHelper || inInterfaceDispatchHelper || inInterfaceResolveHelper) { if (pExPtrs->ExceptionRecord->ExceptionInformation[1] < NULL_AREA_SIZE) { diff --git a/src/coreclr/nativeaot/Runtime/i386/DispatchResolve.asm b/src/coreclr/nativeaot/Runtime/i386/DispatchResolve.asm index b012ac02d2f968..c93b8d4ce71923 100644 --- a/src/coreclr/nativeaot/Runtime/i386/DispatchResolve.asm +++ b/src/coreclr/nativeaot/Runtime/i386/DispatchResolve.asm @@ -144,6 +144,7 @@ FASTCALL_ENDFUNC ;; Resolve helper for standard x86 calling convention. ;; Arguments are passed on the stack: object, dispatch cell. RhpDispatchResolve PROC public +ALTERNATE_ENTRY _RhpDispatchResolve mov ecx, dword ptr [esp + 4] ;; this mov eax, dword ptr [esp + 8] ;; dispatch cell diff --git a/src/coreclr/nativeaot/Runtime/loongarch64/DispatchResolve.S b/src/coreclr/nativeaot/Runtime/loongarch64/DispatchResolve.S index 5b4638a4a87dbd..b352e3371e11a9 100644 --- a/src/coreclr/nativeaot/Runtime/loongarch64/DispatchResolve.S +++ b/src/coreclr/nativeaot/Runtime/loongarch64/DispatchResolve.S @@ -30,13 +30,6 @@ LEAF_ENTRY RhpDispatchResolve, _TEXT // Trigger an AV if we're dispatching on a null this. ld.d $t0, $a0, 0 - ld.d $t1, $a1, 0 // load cached MethodTable - bne $t0, $t1, LOCAL_LABEL(ResolveInterfaceMethod) - - ld.d $a0, $a1, 8 // return cached monomorphic resolved code address - jirl $r0, $ra, 0 - -LOCAL_LABEL(ResolveInterfaceMethod): b C_FUNC(RhpResolveInterfaceMethod) LEAF_END RhpDispatchResolve, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/riscv64/DispatchResolve.S b/src/coreclr/nativeaot/Runtime/riscv64/DispatchResolve.S index 08e5e3c3fd5bd5..4d8f61eec705a0 100644 --- a/src/coreclr/nativeaot/Runtime/riscv64/DispatchResolve.S +++ b/src/coreclr/nativeaot/Runtime/riscv64/DispatchResolve.S @@ -31,13 +31,6 @@ LEAF_ENTRY RhpDispatchResolve, _TEXT // Trigger an AV if we're dispatching on a null this. ld t0, 0(a0) - ld t1, 0(a1) // load cached MethodTable - bne t0, t1, LOCAL_LABEL(ResolveInterfaceMethod) - - ld a0, 8(a1) // return cached monomorphic resolved code address - ret - -LOCAL_LABEL(ResolveInterfaceMethod): tail C_FUNC(RhpResolveInterfaceMethod) LEAF_END RhpDispatchResolve, _TEXT diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Dispatch.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Dispatch.cs index fa8ca0b4d045df..3b5dfd8068dd4e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Dispatch.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Dispatch.cs @@ -15,19 +15,19 @@ namespace Internal.Runtime internal static class Dispatch { [RuntimeExport("ResolveDispatch")] - internal static unsafe MethodTable* ResolveDispatch(object pObject, DispatchCell* pCell) + internal static unsafe IntPtr ResolveDispatch(object pObject, DispatchCell* pCell) { // Assume this is a static dispatch cell first foreach (TypeManagerHandle typeManager in StartupCodeHelpers.GetLoadedModules()) { var pDispatchCellRegion = (DispatchCell*)RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.InterfaceDispatchCellRegion, out int length); - if (pCell >= pDispatchCellRegion && pCell < (byte*)pDispatchCellRegion + length) + if ((byte*)pCell >= (byte*)pDispatchCellRegion && (byte*)pCell < (byte*)pDispatchCellRegion + length) { return ResolveStaticInterfaceDispatch(typeManager, pObject, (nint)(pCell - pDispatchCellRegion)); } pDispatchCellRegion = (DispatchCell*)RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.GvmDispatchCellRegion, out length); - if (pCell >= pDispatchCellRegion && pCell < (byte*)pDispatchCellRegion + length) + if ((byte*)pCell >= (byte*)pDispatchCellRegion && (byte*)pCell < (byte*)pDispatchCellRegion + length) { return ResolveGvmDispatch(typeManager, pObject, (nint)(pCell - pDispatchCellRegion)); } @@ -38,7 +38,7 @@ internal static class Dispatch if (pDynamicCell->IsGvmDispatchCell) { DynamicDispatchCell.DynamicGvmDispatchCell* pDynamicGvmCell = pDynamicCell->AsGvmDispatchCell(); - return (MethodTable*)RuntimeAugments.TypeLoaderCallbacks.ResolveGenericVirtualMethodTarget( + return RuntimeAugments.TypeLoaderCallbacks.ResolveGenericVirtualMethodTarget( new RuntimeTypeHandle(pObject.GetMethodTable()), RuntimeTypeHandle.FromIntPtr(pDynamicGvmCell->OwningType), pDynamicGvmCell->Handle, @@ -48,37 +48,37 @@ internal static class Dispatch } DynamicDispatchCell.DynamicInterfaceDispatchCell* pDynamicInterfaceCell = pDynamicCell->AsInterfaceDispatchCell(); - return (MethodTable*)CachedInterfaceDispatch.RhResolveDispatchWorker(pObject, (MethodTable*)pDynamicInterfaceCell->InterfaceType, (ushort)pDynamicInterfaceCell->Slot); + return CachedInterfaceDispatch.RhResolveDispatchWorker(pObject, (MethodTable*)pDynamicInterfaceCell->InterfaceType, (ushort)pDynamicInterfaceCell->Slot); } - private static unsafe MethodTable* ResolveStaticInterfaceDispatch(TypeManagerHandle typeManager, object pObject, nint cellIndex) + private static unsafe IntPtr ResolveStaticInterfaceDispatch(TypeManagerHandle typeManager, object pObject, nint cellIndex) { IntPtr pDispatchCellInfoRegion = RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.InterfaceDispatchCellInfoRegion, out _); if (MethodTable.SupportsRelativePointers) { var dispatchCellInfo = &((RelativeInterfaceDispatchInfo*)pDispatchCellInfoRegion)[cellIndex]; - return (MethodTable*)CachedInterfaceDispatch.RhResolveDispatchWorker(pObject, dispatchCellInfo->InterfaceType, (ushort)dispatchCellInfo->Slot); + return CachedInterfaceDispatch.RhResolveDispatchWorker(pObject, dispatchCellInfo->InterfaceType, (ushort)dispatchCellInfo->Slot); } else { var dispatchCellInfo = &((InterfaceDispatchInfo*)pDispatchCellInfoRegion)[cellIndex]; - return (MethodTable*)CachedInterfaceDispatch.RhResolveDispatchWorker(pObject, dispatchCellInfo->InterfaceType, (ushort)dispatchCellInfo->Slot); + return CachedInterfaceDispatch.RhResolveDispatchWorker(pObject, dispatchCellInfo->InterfaceType, (ushort)dispatchCellInfo->Slot); } } - private static unsafe MethodTable* ResolveGvmDispatch(TypeManagerHandle typeManager, object pObject, nint cellIndex) + private static unsafe IntPtr ResolveGvmDispatch(TypeManagerHandle typeManager, object pObject, nint cellIndex) { IntPtr pDispatchCellInfoRegion = RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.GvmDispatchCellInfoRegion, out _); if (MethodTable.SupportsRelativePointers) { var dispatchCellInfo = &((RelativeGvmDispatchInfo*)pDispatchCellInfoRegion)[cellIndex]; - return (MethodTable*)RuntimeAugments.TypeLoaderCallbacks.ResolveGenericVirtualMethodTarget( + return RuntimeAugments.TypeLoaderCallbacks.ResolveGenericVirtualMethodTarget( new RuntimeTypeHandle(pObject.GetMethodTable()), new RuntimeTypeHandle(dispatchCellInfo->OwningType), dispatchCellInfo->Handle, dispatchCellInfo->IsAsyncVariant, dispatchCellInfo->Instantiation, isMethodInstantiationDataRelative: true); } else { var dispatchCellInfo = &((GvmDispatchInfo*)pDispatchCellInfoRegion)[cellIndex]; - return (MethodTable*)RuntimeAugments.TypeLoaderCallbacks.ResolveGenericVirtualMethodTarget( + return RuntimeAugments.TypeLoaderCallbacks.ResolveGenericVirtualMethodTarget( new RuntimeTypeHandle(pObject.GetMethodTable()), new RuntimeTypeHandle(dispatchCellInfo->OwningType), dispatchCellInfo->Handle, dispatchCellInfo->IsAsyncVariant, dispatchCellInfo->Instantiation, isMethodInstantiationDataRelative: false); } } diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/Internal/Runtime/Dispatch.cs b/src/coreclr/nativeaot/Test.CoreLib/src/Internal/Runtime/Dispatch.cs index 0a580090cc3719..fe08f804374fb7 100644 --- a/src/coreclr/nativeaot/Test.CoreLib/src/Internal/Runtime/Dispatch.cs +++ b/src/coreclr/nativeaot/Test.CoreLib/src/Internal/Runtime/Dispatch.cs @@ -12,9 +12,9 @@ namespace Internal.Runtime internal static class Dispatch { [RuntimeExport("ResolveDispatch")] - internal static unsafe MethodTable* ResolveDispatch(object pObject, DispatchCell* pCell) + internal static unsafe IntPtr ResolveDispatch(object pObject, DispatchCell* pCell) { - return null; + return IntPtr.Zero; } } }