Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs52
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs7
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs44
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs12
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/SealedVtableNode.cs152
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs11
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs11
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugMethodInfoSection.cs2
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs14
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs27
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/VirtualMethodCallHelper.cs89
-rw-r--r--src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj1
-rw-r--r--src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyVTableSlotNode.cs2
-rw-r--r--src/JitInterface/src/CorInfoImpl.cs3
-rw-r--r--src/System.Private.Jit/src/Internal/Runtime/JitSupport/VirtualMethodSlotHelper.cs2
19 files changed, 375 insertions, 62 deletions
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs
index 2383df231..807f620b9 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs
@@ -46,7 +46,7 @@ namespace ILCompiler.DependencyAnalysis
DefType closestDefType = _type.GetClosestDefType();
- if (_type.RuntimeInterfaces.Length > 0)
+ if (_type.RuntimeInterfaces.Length > 0 && !_type.IsArrayTypeWithoutGenericInterfaces())
dependencyList.Add(factory.InterfaceDispatchMap(_type), "Canonical interface dispatch map");
dependencyList.Add(factory.VTable(closestDefType), "VTable");
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs
index a835bc1f7..4dc900b3e 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs
@@ -80,7 +80,7 @@ namespace ILCompiler.DependencyAnalysis
DefType closestDefType = _type.GetClosestDefType();
- if (_type.RuntimeInterfaces.Length > 0)
+ if (_type.RuntimeInterfaces.Length > 0 && !_type.IsArrayTypeWithoutGenericInterfaces())
{
dependencyList.Add(factory.InterfaceDispatchMap(_type), "Interface dispatch map");
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs
index dc09feaf8..3eeb0909d 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs
@@ -385,6 +385,12 @@ namespace ILCompiler.DependencyAnalysis
if (EmitVirtualSlotsAndInterfaces)
{
+ if (!_type.IsArrayTypeWithoutGenericInterfaces())
+ {
+ // Sealed vtables have relative pointers, so to minimize size, we build sealed vtables for the canonical types
+ dependencies.Add(new DependencyListEntry(factory.SealedVTable(_type.ConvertToCanonForm(CanonicalFormKind.Specific)), "Sealed Vtable"));
+ }
+
AddVirtualMethodUseDependencies(dependencies, factory);
}
@@ -470,6 +476,7 @@ namespace ILCompiler.DependencyAnalysis
OutputFinalizerMethod(factory, ref objData);
OutputOptionalFields(factory, ref objData);
OutputNullableTypeParameter(factory, ref objData);
+ OutputSealedVTable(factory, relocsOnly, ref objData);
OutputGenericInstantiationDetails(factory, ref objData);
return objData.ToObjectData();
@@ -690,7 +697,7 @@ namespace ILCompiler.DependencyAnalysis
}
}
- protected virtual void OutputVirtualSlots(NodeFactory factory, ref ObjectDataBuilder objData, TypeDesc implType, TypeDesc declType, TypeDesc templateType, bool relocsOnly)
+ private void OutputVirtualSlots(NodeFactory factory, ref ObjectDataBuilder objData, TypeDesc implType, TypeDesc declType, TypeDesc templateType, bool relocsOnly)
{
Debug.Assert(EmitVirtualSlotsAndInterfaces);
@@ -759,6 +766,11 @@ namespace ILCompiler.DependencyAnalysis
MethodDesc implMethod = implType.GetClosestDefType().FindVirtualFunctionTargetMethodOnObjectType(declMethod);
+ // Final NewSlot methods cannot be overridden, and therefore can be placed in the sealed-vtable to reduce the size of the vtable
+ // of this type and any type that inherits from it.
+ if (declMethod.CanMethodBeInSealedVTable() && !declType.IsArrayTypeWithoutGenericInterfaces() && !factory.IsCppCodegenTemporaryWorkaround)
+ continue;
+
if (!implMethod.IsAbstract)
{
MethodDesc canonImplMethod = implMethod.GetCanonMethodTarget(CanonicalFormKind.Specific);
@@ -812,6 +824,18 @@ namespace ILCompiler.DependencyAnalysis
}
}
+ private void OutputSealedVTable(NodeFactory factory, bool relocsOnly, ref ObjectDataBuilder objData)
+ {
+ if (EmitVirtualSlotsAndInterfaces && !_type.IsArrayTypeWithoutGenericInterfaces())
+ {
+ // Sealed vtables have relative pointers, so to minimize size, we build sealed vtables for the canonical types
+ SealedVTableNode sealedVTable = factory.SealedVTable(_type.ConvertToCanonForm(CanonicalFormKind.Specific));
+
+ if (sealedVTable.BuildSealedVTableSlots(factory, relocsOnly) && sealedVTable.NumSealedVTableEntries > 0)
+ objData.EmitReloc(sealedVTable, RelocType.IMAGE_REL_BASED_RELPTR32);
+ }
+ }
+
private void OutputGenericInstantiationDetails(NodeFactory factory, ref ObjectDataBuilder objData)
{
if (_type.HasInstantiation && !_type.IsTypeDefinition)
@@ -843,19 +867,19 @@ namespace ILCompiler.DependencyAnalysis
/// </summary>
protected internal virtual void ComputeOptionalEETypeFields(NodeFactory factory, bool relocsOnly)
{
- if (!relocsOnly && _type.RuntimeInterfaces.Length > 0 && factory.InterfaceDispatchMap(_type).Marked)
+ if (!relocsOnly && _type.RuntimeInterfaces.Length > 0 && !_type.IsArrayTypeWithoutGenericInterfaces() && factory.InterfaceDispatchMap(_type).Marked)
{
_optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.DispatchMap, checked((uint)factory.InterfaceDispatchMapIndirection(Type).IndexFromBeginningOfArray));
}
- ComputeRareFlags(factory);
+ ComputeRareFlags(factory, relocsOnly);
ComputeNullableValueOffset();
if (!relocsOnly)
ComputeICastableVirtualMethodSlots(factory);
ComputeValueTypeFieldPadding();
}
- void ComputeRareFlags(NodeFactory factory)
+ void ComputeRareFlags(NodeFactory factory, bool relocsOnly)
{
uint flags = 0;
@@ -897,6 +921,13 @@ namespace ILCompiler.DependencyAnalysis
flags |= (uint)EETypeRareFlags.IsByRefLikeFlag;
}
+ if (EmitVirtualSlotsAndInterfaces && !_type.IsArrayTypeWithoutGenericInterfaces())
+ {
+ SealedVTableNode sealedVTable = factory.SealedVTable(_type.ConvertToCanonForm(CanonicalFormKind.Specific));
+ if (sealedVTable.BuildSealedVTableSlots(factory, relocsOnly) && sealedVTable.NumSealedVTableEntries > 0)
+ flags |= (uint)EETypeRareFlags.HasSealedVTableEntriesFlag;
+ }
+
if (flags != 0)
{
_optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.RareFlags, flags);
@@ -945,13 +976,14 @@ namespace ILCompiler.DependencyAnalysis
MethodDesc isInstMethodImpl = _type.ResolveInterfaceMethodTarget(isInstDecl);
MethodDesc getImplTypeMethodImpl = _type.ResolveInterfaceMethodTarget(getImplTypeDecl);
- int isInstMethodSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, isInstMethodImpl);
- int getImplTypeMethodSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, getImplTypeMethodImpl);
-
- Debug.Assert(isInstMethodSlot != -1 && getImplTypeMethodSlot != -1);
+ int isInstMethodSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, isInstMethodImpl, _type);
+ int getImplTypeMethodSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, getImplTypeMethodImpl, _type);
- _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.ICastableIsInstSlot, (uint)isInstMethodSlot);
- _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.ICastableGetImplTypeSlot, (uint)getImplTypeMethodSlot);
+ // Slots are usually -1, since these methods are usually in the sealed vtable of the base type.
+ if (isInstMethodSlot != -1)
+ _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.ICastableIsInstSlot, (uint)isInstMethodSlot);
+ if (getImplTypeMethodSlot != -1)
+ _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.ICastableGetImplTypeSlot, (uint)getImplTypeMethodSlot);
}
}
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs
index a5c49fc66..6779e0a59 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs
@@ -108,7 +108,7 @@ namespace ILCompiler.DependencyAnalysis
// Avoid consulting VTable slots until they're guaranteed complete during final data emission
if (!relocsOnly)
{
- objData.EmitNaturalInt(VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _targetMethod));
+ objData.EmitNaturalInt(VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _targetMethod, _targetMethod.OwningType));
}
return objData.ToObjectData();
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs
index e2f6a6535..71966e20f 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs
@@ -17,6 +17,11 @@ namespace ILCompiler.DependencyAnalysis
public InterfaceDispatchMapNode(TypeDesc type)
{
+ // Multidimensional arrays should not get a sealed vtable or a dispatch map. Runtime should use the
+ // sealed vtable and dispatch map of the System.Array basetype instead.
+ // Pointer arrays also follow the same path
+ Debug.Assert(!type.IsArrayTypeWithoutGenericInterfaces());
+
_type = type;
}
@@ -80,7 +85,7 @@ namespace ILCompiler.DependencyAnalysis
{
builder.EmitShort(checked((short)interfaceIndex));
builder.EmitShort(checked((short)(interfaceMethodSlot + (interfaceType.HasGenericDictionarySlot() ? 1 : 0))));
- builder.EmitShort(checked((short)VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, implMethod)));
+ builder.EmitShort(checked((short)VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, implMethod, _type.GetClosestDefType())));
entryCount++;
}
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs
index 2c9122d7c..d83ba988c 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs
@@ -1059,7 +1059,7 @@ namespace ILCompiler.DependencyAnalysis
List<NativeLayoutVertexNode> vtableSignatureNodeEntries = null;
int currentVTableIndexUnused = 0;
ProcessVTableEntriesForCallingConventionSignatureGeneration(context, VTableEntriesToProcess.AllOnTypesThatShouldProduceFullVTables, ref currentVTableIndexUnused,
- (int vtableIndex, MethodDesc declMethod, MethodDesc implMethod) =>
+ (int vtableIndex, bool isSealedVTableSlot, MethodDesc declMethod, MethodDesc implMethod) =>
{
if (implMethod.IsAbstract)
return;
@@ -1096,7 +1096,7 @@ namespace ILCompiler.DependencyAnalysis
int currentVTableIndexUnused = 0;
ProcessVTableEntriesForCallingConventionSignatureGeneration(context, VTableEntriesToProcess.AllOnTypesThatProducePartialVTables, ref currentVTableIndexUnused,
- (int vtableIndex, MethodDesc declMethod, MethodDesc implMethod) =>
+ (int vtableIndex, bool isSealedVTableSlot, MethodDesc declMethod, MethodDesc implMethod) =>
{
if (implMethod.IsAbstract)
return;
@@ -1236,6 +1236,16 @@ namespace ILCompiler.DependencyAnalysis
if (typeFlags != default(Internal.NativeFormat.TypeFlags))
layoutInfo.AppendUnsigned(BagElementKind.TypeFlags, (uint)typeFlags);
+ if (!_type.IsArrayTypeWithoutGenericInterfaces() && ConstructedEETypeNode.CreationAllowed(_type))
+ {
+ SealedVTableNode sealedVTable = factory.SealedVTable(_type.ConvertToCanonForm(CanonicalFormKind.Specific));
+
+ sealedVTable.BuildSealedVTableSlots(factory, relocsOnly: false /* This is the final emission phase */);
+
+ if (sealedVTable.NumSealedVTableEntries > 0)
+ layoutInfo.AppendUnsigned(BagElementKind.SealedVTableEntries, (uint)sealedVTable.NumSealedVTableEntries);
+ }
+
if (_type.GetTypeDefinition().HasVariance || factory.TypeSystemContext.IsGenericArrayInterfaceType(_type))
{
// Runtime casting logic relies on all interface types implemented on arrays
@@ -1319,7 +1329,7 @@ namespace ILCompiler.DependencyAnalysis
VertexSequence vtableSignaturesSequence = null;
ProcessVTableEntriesForCallingConventionSignatureGeneration(factory, VTableEntriesToProcess.AllInVTable, ref currentVTableIndexUnused,
- (int vtableIndex, MethodDesc declMethod, MethodDesc implMethod) =>
+ (int vtableIndex, bool isSealedVTableSlot, MethodDesc declMethod, MethodDesc implMethod) =>
{
if (implMethod.IsAbstract)
return;
@@ -1333,7 +1343,7 @@ namespace ILCompiler.DependencyAnalysis
Vertex signatureVertex = GetNativeWriter(factory).GetRelativeOffsetSignature(methodSignature.WriteVertex(factory));
Vertex vtableSignatureEntry = writer.GetTuple(
- writer.GetUnsignedConstant(((uint)vtableIndex) << 1), // We currently do not use sealed vtable entries yet. Update when that happens
+ writer.GetUnsignedConstant((uint)((vtableIndex << 1) | (isSealedVTableSlot ? 1 : 0))),
factory.MetadataManager.NativeLayoutInfo.TemplatesSection.Place(signatureVertex));
vtableSignaturesSequence.Append(vtableSignatureEntry);
@@ -1391,7 +1401,7 @@ namespace ILCompiler.DependencyAnalysis
/// Do not adjust vtable index for generic dictionary slot
/// The vtable index is only actually valid if whichEntries is set to VTableEntriesToProcess.AllInVTable
/// </summary>
- private void ProcessVTableEntriesForCallingConventionSignatureGeneration(NodeFactory factory, VTableEntriesToProcess whichEntries, ref int currentVTableIndex, Action<int, MethodDesc, MethodDesc> operation, TypeDesc implType, TypeDesc declType, TypeDesc templateType)
+ private void ProcessVTableEntriesForCallingConventionSignatureGeneration(NodeFactory factory, VTableEntriesToProcess whichEntries, ref int currentVTableIndex, Action<int, bool, MethodDesc, MethodDesc> operation, TypeDesc implType, TypeDesc declType, TypeDesc templateType)
{
if (implType.IsInterface)
return;
@@ -1454,6 +1464,8 @@ namespace ILCompiler.DependencyAnalysis
if (declType.HasGenericDictionarySlot() || templateType.HasGenericDictionarySlot())
currentVTableIndex++;
+ int sealedVTableSlot = 0;
+
// Actual vtable slots follow
foreach (MethodDesc declMethod in vtableEntriesToProcess)
{
@@ -1462,8 +1474,17 @@ namespace ILCompiler.DependencyAnalysis
MethodDesc implMethod = implType.GetClosestDefType().FindVirtualFunctionTargetMethodOnObjectType(declMethod);
- operation(currentVTableIndex, declMethod, implMethod);
- currentVTableIndex++;
+ if (implMethod.CanMethodBeInSealedVTable() && !implType.IsArrayTypeWithoutGenericInterfaces())
+ {
+ // Sealed vtable entries on other types in the hierarchy should not be reported (types read entries
+ // from their own sealed vtables, and not from the sealed vtables of base types).
+ if (implMethod.OwningType == implType)
+ operation(sealedVTableSlot++, true, declMethod, implMethod);
+ }
+ else
+ {
+ operation(currentVTableIndex++, false, declMethod, implMethod);
+ }
}
}
}
@@ -1714,7 +1735,7 @@ namespace ILCompiler.DependencyAnalysis
if (method.IsRuntimeDeterminedExactMethod)
method = method.GetCanonMethodTarget(CanonicalFormKind.Specific);
- int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, method);
+ int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, method, method.OwningType);
return writer.GetMethodSlotSignature(_signature.WriteVertex(factory), checked((uint)slot));
}
@@ -1870,8 +1891,8 @@ namespace ILCompiler.DependencyAnalysis
protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeFactory factory)
{
NativeWriter nativeWriter = GetNativeWriter(factory);
-
- int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _slotDefiningMethod);
+
+ int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _slotDefiningMethod, _slotDefiningMethod.OwningType);
Vertex typeVertex = factory.NativeLayout.TypeSignatureVertex(_slotDefiningMethod.OwningType).WriteVertex(factory);
return nativeWriter.GetTuple(typeVertex, nativeWriter.GetUnsignedConstant((uint)slot));
}
@@ -2024,7 +2045,8 @@ namespace ILCompiler.DependencyAnalysis
{
Debug.Assert((SignatureKind == FixupSignatureKind.NonGenericConstrainedMethod) || (SignatureKind == FixupSignatureKind.NonGenericDirectConstrainedMethod));
Vertex methodType = factory.NativeLayout.TypeSignatureVertex(_constrainedMethod.OwningType).WriteVertex(factory);
- int interfaceSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _constrainedMethod.GetCanonMethodTarget(CanonicalFormKind.Specific));
+ var canonConstrainedMethod = _constrainedMethod.GetCanonMethodTarget(CanonicalFormKind.Specific);
+ int interfaceSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, canonConstrainedMethod, canonConstrainedMethod.OwningType);
Vertex interfaceSlotVertex = writer.GetUnsignedConstant(checked((uint)interfaceSlot));
return writer.GetTuple(constraintType, methodType, interfaceSlotVertex);
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs
index 6bfbf947d..597b2d55a 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs
@@ -382,6 +382,11 @@ namespace ILCompiler.DependencyAnalysis
return new InterfaceDispatchMapNode(type);
});
+ _sealedVtableNodes = new NodeCache<TypeDesc, SealedVTableNode>((TypeDesc type) =>
+ {
+ return new SealedVTableNode(type);
+ });
+
_runtimeMethodHandles = new NodeCache<MethodDesc, RuntimeMethodHandleNode>((MethodDesc method) =>
{
return new RuntimeMethodHandleNode(method);
@@ -641,6 +646,13 @@ namespace ILCompiler.DependencyAnalysis
return _readOnlyDataBlobs.GetOrAdd(new ReadOnlyDataBlobKey(name, blobData, alignment));
}
+ private NodeCache<TypeDesc, SealedVTableNode> _sealedVtableNodes;
+
+ internal SealedVTableNode SealedVTable(TypeDesc type)
+ {
+ return _sealedVtableNodes.GetOrAdd(type);
+ }
+
private NodeCache<TypeDesc, InterfaceDispatchMapNode> _interfaceDispatchMaps;
internal InterfaceDispatchMapNode InterfaceDispatchMap(TypeDesc type)
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs
index 2b5f45090..cc9f6a98a 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs
@@ -218,7 +218,7 @@ namespace ILCompiler.DependencyAnalysis
else
{
// Get the declaring method for slot on the instantiated declaring type
- int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, declaringMethodForSlot, factory.Target.Abi != TargetAbi.ProjectN);
+ int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, declaringMethodForSlot, declaringMethodForSlot.OwningType, factory.Target.Abi != TargetAbi.ProjectN);
Debug.Assert(slot != -1);
vertex = writer.GetTuple(
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/SealedVtableNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/SealedVtableNode.cs
new file mode 100644
index 000000000..4f7e5ebe3
--- /dev/null
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/SealedVtableNode.cs
@@ -0,0 +1,152 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+using Internal.Text;
+using Internal.TypeSystem;
+
+namespace ILCompiler.DependencyAnalysis
+{
+ public class SealedVTableNode : ObjectNode, ISymbolDefinitionNode
+ {
+ private readonly TypeDesc _type;
+ private List<MethodDesc> _sealedVTableEntries;
+
+ public SealedVTableNode(TypeDesc type)
+ {
+ // Multidimensional arrays should not get a sealed vtable or a dispatch map. Runtime should use the
+ // sealed vtable and dispatch map of the System.Array basetype instead.
+ // Pointer arrays also follow the same path
+ Debug.Assert(!type.IsArrayTypeWithoutGenericInterfaces());
+ Debug.Assert(!type.IsRuntimeDeterminedSubtype);
+
+ _type = type;
+ }
+
+ protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
+
+ public override ObjectNodeSection Section => _type.Context.Target.IsWindows ? ObjectNodeSection.FoldableReadOnlyDataSection : ObjectNodeSection.DataSection;
+
+ public virtual void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
+ {
+ sb.Append(nameMangler.CompilationUnitPrefix + "__SealedVTable_" + nameMangler.NodeMangler.EEType(_type));
+ }
+
+ int ISymbolNode.Offset => 0;
+ int ISymbolDefinitionNode.Offset => 0;
+ public override bool IsShareable => EETypeNode.IsTypeNodeShareable(_type);
+ public override bool StaticDependenciesAreComputed => true;
+
+ /// <summary>
+ /// Returns the number of sealed vtable slots on the type. This API should only be called after successfully
+ /// building the sealed vtable slots.
+ /// </summary>
+ public int NumSealedVTableEntries
+ {
+ get
+ {
+ if (_sealedVTableEntries == null)
+ throw new NotSupportedException();
+
+ return _sealedVTableEntries.Count;
+ }
+ }
+
+ /// <summary>
+ /// Returns the slot of a method in the sealed vtable, or -1 if not found. This API should only be called after
+ /// successfully building the sealed vtable slots.
+ /// </summary>
+ public int ComputeSealedVTableSlot(MethodDesc method)
+ {
+ if (_sealedVTableEntries == null)
+ throw new NotSupportedException();
+
+ for (int i = 0; i < _sealedVTableEntries.Count; i++)
+ {
+ if (_sealedVTableEntries[i] == method)
+ return i;
+ }
+
+ return -1;
+ }
+
+ public bool BuildSealedVTableSlots(NodeFactory factory, bool relocsOnly)
+ {
+ // Sealed vtable already built
+ if (_sealedVTableEntries != null)
+ return true;
+
+ TypeDesc declType = _type.GetClosestDefType();
+
+ // It's only okay to touch the actual list of slots if we're in the final emission phase
+ // or the vtable is not built lazily.
+ if (relocsOnly && !factory.VTable(declType).HasFixedSlots)
+ return false;
+
+ _sealedVTableEntries = new List<MethodDesc>();
+
+ // Cpp codegen does not support sealed vtables
+ if (factory.IsCppCodegenTemporaryWorkaround)
+ return true;
+
+ IReadOnlyList<MethodDesc> virtualSlots = factory.VTable(declType).Slots;
+
+ for (int i = 0; i < virtualSlots.Count; i++)
+ {
+ MethodDesc implMethod = _type.GetClosestDefType().FindVirtualFunctionTargetMethodOnObjectType(virtualSlots[i]);
+
+ if (implMethod.CanMethodBeInSealedVTable())
+ _sealedVTableEntries.Add(implMethod);
+ }
+
+ for (int interfaceIndex = 0; interfaceIndex < _type.RuntimeInterfaces.Length; interfaceIndex++)
+ {
+ var interfaceType = _type.RuntimeInterfaces[interfaceIndex];
+
+ virtualSlots = factory.VTable(interfaceType).Slots;
+
+ for (int interfaceMethodSlot = 0; interfaceMethodSlot < virtualSlots.Count; interfaceMethodSlot++)
+ {
+ MethodDesc declMethod = virtualSlots[interfaceMethodSlot];
+ var implMethod = _type.GetClosestDefType().ResolveInterfaceMethodToVirtualMethodOnType(declMethod);
+
+ // Interface methods first implemented by a base type in the hierarchy will return null for the implMethod (runtime interface
+ // dispatch will walk the inheritance chain).
+ if (implMethod != null && implMethod.CanMethodBeInSealedVTable() && implMethod.OwningType != _type)
+ _sealedVTableEntries.Add(implMethod);
+ }
+ }
+
+ return true;
+ }
+
+ public override ObjectData GetData(NodeFactory factory, bool relocsOnly)
+ {
+ ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly);
+ objData.RequireInitialPointerAlignment();
+ objData.AddSymbol(this);
+
+ if (BuildSealedVTableSlots(factory, relocsOnly))
+ {
+ for (int i = 0; i < _sealedVTableEntries.Count; i++)
+ {
+ MethodDesc canonImplMethod = _sealedVTableEntries[i].GetCanonMethodTarget(CanonicalFormKind.Specific);
+ objData.EmitReloc(factory.MethodEntrypoint(canonImplMethod, _sealedVTableEntries[i].OwningType.IsValueType), RelocType.IMAGE_REL_BASED_RELPTR32);
+ }
+ }
+
+ return objData.ToObjectData();
+ }
+
+ protected internal override int ClassCode => 1632890252;
+ protected internal override int CompareToImpl(SortableDependencyNode other, CompilerComparer comparer)
+ {
+ return comparer.Compare(_type, ((SealedVTableNode)other)._type);
+ }
+ }
+}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs
index d4bf28b31..a05365715 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs
@@ -33,13 +33,14 @@ namespace ILCompiler.DependencyAnalysis
MethodDesc targetMethod = (MethodDesc)Target;
Debug.Assert(!targetMethod.OwningType.IsInterface);
+ Debug.Assert(!targetMethod.CanMethodBeInSealedVTable());
int pointerSize = factory.Target.PointerSize;
int slot = 0;
if (!relocsOnly)
{
- slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod);
+ slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType);
Debug.Assert(slot != -1);
}
@@ -162,11 +163,13 @@ namespace ILCompiler.DependencyAnalysis
if (target.TargetNeedsVTableLookup)
{
+ Debug.Assert(!target.TargetMethod.CanMethodBeInSealedVTable());
+
encoder.EmitLDR(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg1);
int slot = 0;
if (!relocsOnly)
- slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod);
+ slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod, target.TargetMethod.OwningType);
Debug.Assert(slot != -1);
encoder.EmitLDR(encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2,
@@ -212,7 +215,9 @@ namespace ILCompiler.DependencyAnalysis
encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Arg0);
- int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod);
+ Debug.Assert(!targetMethod.CanMethodBeInSealedVTable());
+
+ int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType);
Debug.Assert(slot != -1);
encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result,
((short)(EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize))));
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs
index 614c171e6..026367d85 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs
@@ -32,6 +32,7 @@ namespace ILCompiler.DependencyAnalysis
MethodDesc targetMethod = (MethodDesc)Target;
Debug.Assert(!targetMethod.OwningType.IsInterface);
+ Debug.Assert(!targetMethod.CanMethodBeInSealedVTable());
AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64);
encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr);
@@ -41,7 +42,7 @@ namespace ILCompiler.DependencyAnalysis
int slot = 0;
if (!relocsOnly)
{
- slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod);
+ slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType);
Debug.Assert(slot != -1);
}
Debug.Assert(((INodeWithDebugInfo)this).DebugLocInfos[1].NativeOffset == encoder.Builder.CountBytes);
@@ -167,12 +168,14 @@ namespace ILCompiler.DependencyAnalysis
if (target.TargetNeedsVTableLookup)
{
+ Debug.Assert(!target.TargetMethod.CanMethodBeInSealedVTable());
+
AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64);
encoder.EmitMOV(encoder.TargetRegister.Arg2, ref loadFromThisPtr);
int slot = 0;
if (!relocsOnly)
- slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod);
+ slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod, target.TargetMethod.OwningType);
Debug.Assert(slot != -1);
AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Arg2, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64);
@@ -214,7 +217,9 @@ namespace ILCompiler.DependencyAnalysis
AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64);
encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr);
- int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod);
+ Debug.Assert(!targetMethod.CanMethodBeInSealedVTable());
+
+ int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType);
Debug.Assert(slot != -1);
AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64);
encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromSlot);
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugMethodInfoSection.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugMethodInfoSection.cs
index 7d4396fc7..03488c594 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugMethodInfoSection.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/WindowsDebugMethodInfoSection.cs
@@ -102,7 +102,7 @@ namespace ILCompiler.DependencyAnalysis
// find virtual method slot.
MethodDesc declaringMethodForSlot =
MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(md.GetTypicalMethodDefinition());
- int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, declaringMethodForSlot);
+ int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, declaringMethodForSlot, type);
if (slot != -1 && !methodList.ContainsKey(methodToken))
methodList.Add(methodToken, slot);
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs b/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs
index 15070a4b4..4b72c15d8 100644
--- a/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs
@@ -113,5 +113,19 @@ namespace ILCompiler
return false;
}
+
+ /// <summary>
+ /// Determine whether a method can go into the sealed vtable of a type. Such method must be a sealed virtual
+ /// method that is not overriding any method on a base type.
+ /// Given that such methods can never be overridden in any derived type, we can
+ /// save space in the vtable of a type, and all of its derived types by not emitting these methods in their vtables,
+ /// and storing them in a separate table on the side. This is especially beneficial for all array types,
+ /// since all of their collection interface methods are sealed and implemented on the System.Array and
+ /// System.Array&lt;T&gt; base types, and therefore we can minimize the vtable sizes of all derived array types.
+ /// </summary>
+ public static bool CanMethodBeInSealedVTable(this MethodDesc method)
+ {
+ return method.IsFinal && method.IsNewSlot;
+ }
}
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs b/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs
index 5cf56d172..99b0de66c 100644
--- a/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs
@@ -31,14 +31,11 @@ namespace ILCompiler
{
if (type.IsArray)
{
- var arrayType = (ArrayType)type;
- TypeDesc elementType = arrayType.ElementType;
- if (arrayType.IsSzArray && !elementType.IsPointer && !elementType.IsFunctionPointer)
- {
- MetadataType arrayShadowType = type.Context.SystemModule.GetKnownType("System", "Array`1");
- return arrayShadowType.MakeInstantiatedType(elementType);
- }
- return type.Context.GetWellKnownType(WellKnownType.Array);
+ if (type.IsArrayTypeWithoutGenericInterfaces())
+ return type.Context.GetWellKnownType(WellKnownType.Array);
+
+ MetadataType arrayShadowType = type.Context.SystemModule.GetKnownType("System", "Array`1");
+ return arrayShadowType.MakeInstantiatedType(((ArrayType)type).ElementType);
}
Debug.Assert(type is DefType);
@@ -183,5 +180,19 @@ namespace ILCompiler
return false;
}
+
+ /// <summary>
+ /// Determines whether an array type does implements the generic collection interfaces. This is the case
+ /// for multi-dimensional arrays, and arrays of pointers.
+ /// </summary>
+ public static bool IsArrayTypeWithoutGenericInterfaces(this TypeDesc type)
+ {
+ if (!type.IsArray)
+ return false;
+
+ var arrayType = (ArrayType)type;
+ TypeDesc elementType = arrayType.ElementType;
+ return type.IsMdArray || elementType.IsPointer || elementType.IsFunctionPointer;
+ }
}
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/VirtualMethodCallHelper.cs b/src/ILCompiler.Compiler/src/Compiler/VirtualMethodCallHelper.cs
index 27b1198c6..8266ed8df 100644
--- a/src/ILCompiler.Compiler/src/Compiler/VirtualMethodCallHelper.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/VirtualMethodCallHelper.cs
@@ -16,29 +16,76 @@ namespace ILCompiler
/// Given a virtual method decl, return its VTable slot if the method is used on its containing type.
/// Return -1 if the virtual method is not used.
/// </summary>
- public static int GetVirtualMethodSlot(NodeFactory factory, MethodDesc method, bool countDictionarySlots = true)
+ public static int GetVirtualMethodSlot(NodeFactory factory, MethodDesc method, TypeDesc implType, bool countDictionarySlots = true)
{
- // TODO: More efficient lookup of the slot
- TypeDesc owningType = method.OwningType;
- int baseSlots = GetNumberOfBaseSlots(factory, owningType, countDictionarySlots);
-
- // For types that have a generic dictionary, the introduced virtual method slots are
- // prefixed with a pointer to the generic dictionary.
- if (owningType.HasGenericDictionarySlot() && countDictionarySlots)
- baseSlots++;
-
- IReadOnlyList<MethodDesc> virtualSlots = factory.VTable(owningType).Slots;
- int methodSlot = -1;
- for (int slot = 0; slot < virtualSlots.Count; slot++)
+ // CppCodegen does not yet support sealed vtables.
+
+ if (method.CanMethodBeInSealedVTable() && !factory.IsCppCodegenTemporaryWorkaround)
{
- if (virtualSlots[slot] == method)
+ // If the method is a sealed newslot method, it will be put in the sealed vtable instead of the type's vtable. In this
+ // case, the slot index return should be the index in the sealed vtable, plus the total number of vtable slots.
+
+ // First, make sure we are not attempting to resolve the slot of a sealed vtable method on a special array type, which
+ // does not get any sealed vtable entries
+ Debug.Assert(!implType.IsArrayTypeWithoutGenericInterfaces());
+
+ // Ensure the sealed vtable is built before computing the slot
+ factory.SealedVTable(implType).BuildSealedVTableSlots(factory, relocsOnly: false /* GetVirtualMethodSlot is called in the final emission phase */);
+
+ int sealedVTableSlot = factory.SealedVTable(implType).ComputeSealedVTableSlot(method);
+ if (sealedVTableSlot == -1)
+ return -1;
+
+ // Now compute the total number of vtable slots that would exist on the type
+ int baseSlots = GetNumberOfBaseSlots(factory, implType, countDictionarySlots);
+
+ // For types that have a generic dictionary, the introduced virtual method slots are
+ // prefixed with a pointer to the generic dictionary.
+ if (implType.HasGenericDictionarySlot() && countDictionarySlots)
+ baseSlots++;
+
+ int numVTableSlots = baseSlots;
+ IReadOnlyList<MethodDesc> virtualSlots = factory.VTable(implType).Slots;
+ for (int slot = 0; slot < virtualSlots.Count; slot++)
{
- methodSlot = slot;
- break;
+ if (virtualSlots[slot].CanMethodBeInSealedVTable())
+ continue;
+ numVTableSlots++;
}
+
+ return numVTableSlots + sealedVTableSlot;
}
+ else
+ {
+ // TODO: More efficient lookup of the slot
+ TypeDesc owningType = method.OwningType;
+ int baseSlots = GetNumberOfBaseSlots(factory, owningType, countDictionarySlots);
- return methodSlot == -1 ? -1 : baseSlots + methodSlot;
+ // For types that have a generic dictionary, the introduced virtual method slots are
+ // prefixed with a pointer to the generic dictionary.
+ if (owningType.HasGenericDictionarySlot() && countDictionarySlots)
+ baseSlots++;
+
+ IReadOnlyList<MethodDesc> virtualSlots = factory.VTable(owningType).Slots;
+ int methodSlot = -1;
+ int numSealedVTableEntries = 0;
+ for (int slot = 0; slot < virtualSlots.Count; slot++)
+ {
+ if (virtualSlots[slot].CanMethodBeInSealedVTable() && !factory.IsCppCodegenTemporaryWorkaround)
+ {
+ numSealedVTableEntries++;
+ continue;
+ }
+
+ if (virtualSlots[slot] == method)
+ {
+ methodSlot = slot;
+ break;
+ }
+ }
+
+ return methodSlot == -1 ? -1 : baseSlots + methodSlot - numSealedVTableEntries;
+ }
}
private static int GetNumberOfBaseSlots(NodeFactory factory, TypeDesc owningType, bool countDictionarySlots)
@@ -83,7 +130,13 @@ namespace ILCompiler
baseSlots++;
IReadOnlyList<MethodDesc> baseVirtualSlots = factory.VTable(baseType).Slots;
- baseSlots += baseVirtualSlots.Count;
+ foreach (var vtableMethod in baseVirtualSlots)
+ {
+ // Methods in the sealed vtable should be excluded from the count
+ if (vtableMethod.CanMethodBeInSealedVTable() && !factory.IsCppCodegenTemporaryWorkaround)
+ continue;
+ baseSlots++;
+ }
baseType = baseType.BaseType;
templateBaseType = templateBaseType.BaseType;
diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj
index a29a5cd10..b799fb700 100644
--- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj
+++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj
@@ -283,6 +283,7 @@
<Compile Include="Compiler\DependencyAnalysis\UtcDictionaryLayoutNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\UtcNodeFactory.cs" />
<Compile Include="Compiler\DependencyAnalysis\VirtualMethodUseNode.cs" />
+ <Compile Include="Compiler\DependencyAnalysis\SealedVTableNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\StackTraceMethodMappingNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\StackTraceEmbeddedMetadataNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\Target_X64\Register.cs" />
diff --git a/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyVTableSlotNode.cs b/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyVTableSlotNode.cs
index 6f7897a8f..e48163267 100644
--- a/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyVTableSlotNode.cs
+++ b/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyVTableSlotNode.cs
@@ -62,7 +62,7 @@ namespace ILCompiler.DependencyAnalysis
if (!relocsOnly)
{
var tableOffset = EETypeNode.GetVTableOffset(factory.Target.PointerSize) / factory.Target.PointerSize;
- objData.EmitInt(tableOffset + VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _targetMethod));
+ objData.EmitInt(tableOffset + VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, _targetMethod, _targetMethod.OwningType));
}
return objData.ToObjectData();
}
diff --git a/src/JitInterface/src/CorInfoImpl.cs b/src/JitInterface/src/CorInfoImpl.cs
index a9eaceaab..535ac1cb7 100644
--- a/src/JitInterface/src/CorInfoImpl.cs
+++ b/src/JitInterface/src/CorInfoImpl.cs
@@ -859,8 +859,9 @@ namespace Internal.JitInterface
// Normalize to the slot defining method. We don't have slot information for the overrides.
methodDesc = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(methodDesc);
+ Debug.Assert(!methodDesc.CanMethodBeInSealedVTable());
- int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(_compilation.NodeFactory, methodDesc);
+ int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(_compilation.NodeFactory, methodDesc, methodDesc.OwningType);
Debug.Assert(slot != -1);
offsetAfterIndirection = (uint)(EETypeNode.GetVTableOffset(pointerSize) + slot * pointerSize);
diff --git a/src/System.Private.Jit/src/Internal/Runtime/JitSupport/VirtualMethodSlotHelper.cs b/src/System.Private.Jit/src/Internal/Runtime/JitSupport/VirtualMethodSlotHelper.cs
index 10e3466bd..ab82c2d6f 100644
--- a/src/System.Private.Jit/src/Internal/Runtime/JitSupport/VirtualMethodSlotHelper.cs
+++ b/src/System.Private.Jit/src/Internal/Runtime/JitSupport/VirtualMethodSlotHelper.cs
@@ -23,7 +23,7 @@ namespace ILCompiler
/// Given a virtual method decl, return its VTable slot if the method is used on its containing type.
/// Return -1 if the virtual method is not used.
/// </summary>
- public static int GetVirtualMethodSlot(NodeFactory factory, MethodDesc method)
+ public static int GetVirtualMethodSlot(NodeFactory factory, MethodDesc method, TypeDesc implType)
{
Debug.Assert(method.IsVirtual);