Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 6adb423

Browse files
committedDec 6, 2023
Generate interface lists for necessary EETypes
The compiler can generate two kinds of `MethodTable` structures: constructed and unconstructed. The constructed one has a fully populated vtable and GCInfo and is required whenever the object type could be allocated on the heap. The unconstructed one is generated for all other scenarios as a size optimization. We were previously also skipping emission of the interface list in the unconstructed case. But interface list might be required for variant casting. We could introduce yet another `MethodTable` kind for this specific scenario, but it doesn't seem to warrant the complexity. Emitting interface list for all types is a less than 0.1% size regression for the Todos app. It is a 0.5% size regression for Hello World. That part is unfortunate. It's mostly due to the useless numeric interfaces. We can get this size back if we do dotnet#66716 and start trimming interface lists. Fixes dotnet#95574.
1 parent 71f3bf1 commit 6adb423

File tree

4 files changed

+39
-24
lines changed

4 files changed

+39
-24
lines changed
 

‎src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public CanonicalEETypeNode(NodeFactory factory, TypeDesc type) : base(factory, t
2929

3030
public override bool StaticDependenciesAreComputed => true;
3131
public override bool IsShareable => IsTypeNodeShareable(_type);
32-
protected override bool EmitVirtualSlotsAndInterfaces => true;
32+
protected override bool EmitVirtualSlots => true;
3333
public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => false;
3434

3535
protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory)

‎src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public ConstructedEETypeNode(NodeFactory factory, TypeDesc type) : base(factory,
2020

2121
public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => false;
2222

23-
protected override bool EmitVirtualSlotsAndInterfaces => true;
23+
protected override bool EmitVirtualSlots => true;
2424

2525
protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory)
2626
{

‎src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs

+18-22
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public EETypeNode(NodeFactory factory, TypeDesc type)
9797
_writableDataNode = SupportsWritableData(factory.Target) && !_type.IsCanonicalSubtype(CanonicalFormKind.Any) ? new WritableDataNode(this) : null;
9898
_hasConditionalDependenciesFromMetadataManager = factory.MetadataManager.HasConditionalDependenciesDueToEETypePresence(type);
9999

100-
if (EmitVirtualSlotsAndInterfaces)
100+
if (EmitVirtualSlots)
101101
_virtualMethodAnalysisFlags = AnalyzeVirtualMethods(type);
102102

103103
factory.TypeSystemContext.EnsureLoadableType(type);
@@ -201,7 +201,7 @@ protected bool MightHaveInterfaceDispatchMap(NodeFactory factory)
201201
{
202202
if (!_mightHaveInterfaceDispatchMap.HasValue)
203203
{
204-
_mightHaveInterfaceDispatchMap = EmitVirtualSlotsAndInterfaces && InterfaceDispatchMapNode.MightHaveInterfaceDispatchMap(_type, factory);
204+
_mightHaveInterfaceDispatchMap = EmitVirtualSlots && InterfaceDispatchMapNode.MightHaveInterfaceDispatchMap(_type, factory);
205205
}
206206

207207
return _mightHaveInterfaceDispatchMap.Value;
@@ -238,7 +238,7 @@ protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory)
238238
public static int GetMinimumObjectSize(TypeSystemContext typeSystemContext)
239239
=> typeSystemContext.Target.PointerSize * 3;
240240

241-
protected virtual bool EmitVirtualSlotsAndInterfaces => false;
241+
protected virtual bool EmitVirtualSlots => false;
242242

243243
public override bool InterestingForDynamicDependencyAnalysis
244244
=> (_virtualMethodAnalysisFlags & VirtualMethodAnalysisFlags.InterestingForDynamicDependencies) != 0;
@@ -305,7 +305,7 @@ public sealed override bool HasConditionalStaticDependencies
305305
return true;
306306
}
307307

308-
if (!EmitVirtualSlotsAndInterfaces)
308+
if (!EmitVirtualSlots)
309309
return false;
310310

311311
// Since the vtable is dependency driven, generate conditional static dependencies for
@@ -373,7 +373,7 @@ public sealed override IEnumerable<CombinedDependencyListEntry> GetConditionalSt
373373
"Information about static bases for type with template"));
374374
}
375375

376-
if (!EmitVirtualSlotsAndInterfaces)
376+
if (!EmitVirtualSlots)
377377
return result;
378378

379379
DefType defType = _type.GetClosestDefType();
@@ -586,7 +586,7 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact
586586
// emitting it.
587587
dependencies.Add(new DependencyListEntry(_optionalFieldsNode, "Optional fields"));
588588

589-
if (EmitVirtualSlotsAndInterfaces)
589+
if (EmitVirtualSlots)
590590
{
591591
if (!_type.IsArrayTypeWithoutGenericInterfaces())
592592
{
@@ -677,7 +677,7 @@ protected override ObjectData GetDehydratableData(NodeFactory factory, bool relo
677677

678678
objData.EmitInt(_type.GetHashCode());
679679

680-
if (EmitVirtualSlotsAndInterfaces)
680+
if (EmitVirtualSlots)
681681
{
682682
// Emit VTable
683683
Debug.Assert(objData.CountBytes - ((ISymbolDefinitionNode)this).Offset == GetVTableOffset(objData.TargetPointerSize));
@@ -687,23 +687,21 @@ protected override ObjectData GetDehydratableData(NodeFactory factory, bool relo
687687
// Update slot count
688688
int numberOfVtableSlots = virtualSlotCounter.CountSlots(ref /* readonly */ objData);
689689
objData.EmitShort(vtableSlotCountReservation, checked((short)numberOfVtableSlots));
690-
691-
// Emit interface map
692-
SlotCounter interfaceSlotCounter = SlotCounter.BeginCounting(ref /* readonly */ objData);
693-
OutputInterfaceMap(factory, ref objData);
694-
695-
// Update slot count
696-
int numberOfInterfaceSlots = interfaceSlotCounter.CountSlots(ref /* readonly */ objData);
697-
objData.EmitShort(interfaceCountReservation, checked((short)numberOfInterfaceSlots));
698-
699690
}
700691
else
701692
{
702693
// If we're not emitting any slots, the number of slots is zero.
703694
objData.EmitShort(vtableSlotCountReservation, 0);
704-
objData.EmitShort(interfaceCountReservation, 0);
705695
}
706696

697+
// Emit interface map
698+
SlotCounter interfaceSlotCounter = SlotCounter.BeginCounting(ref /* readonly */ objData);
699+
OutputInterfaceMap(factory, ref objData);
700+
701+
// Update slot count
702+
int numberOfInterfaceSlots = interfaceSlotCounter.CountSlots(ref /* readonly */ objData);
703+
objData.EmitShort(interfaceCountReservation, checked((short)numberOfInterfaceSlots));
704+
707705
OutputTypeManagerIndirection(factory, ref objData);
708706
OutputWritableData(factory, ref objData);
709707
OutputDispatchMap(factory, ref objData);
@@ -751,7 +749,7 @@ private void OutputFlags(NodeFactory factory, ref ObjectDataBuilder objData, boo
751749
flags |= (uint)EETypeFlags.GenericVarianceFlag;
752750
}
753751

754-
if (EmitVirtualSlotsAndInterfaces && !_type.IsArrayTypeWithoutGenericInterfaces())
752+
if (EmitVirtualSlots && !_type.IsArrayTypeWithoutGenericInterfaces())
755753
{
756754
SealedVTableNode sealedVTable = factory.SealedVTable(_type.ConvertToCanonForm(CanonicalFormKind.Specific));
757755
if (sealedVTable.BuildSealedVTableSlots(factory, relocsOnly) && sealedVTable.NumSealedVTableEntries > 0)
@@ -949,7 +947,7 @@ protected virtual void OutputRelatedType(NodeFactory factory, ref ObjectDataBuil
949947

950948
private void OutputVirtualSlots(NodeFactory factory, ref ObjectDataBuilder objData, TypeDesc implType, TypeDesc declType, TypeDesc templateType, bool relocsOnly)
951949
{
952-
Debug.Assert(EmitVirtualSlotsAndInterfaces);
950+
Debug.Assert(EmitVirtualSlots);
953951

954952
declType = declType.GetClosestDefType();
955953
templateType = templateType.ConvertToCanonForm(CanonicalFormKind.Specific);
@@ -1096,8 +1094,6 @@ protected virtual IEETypeNode GetInterfaceTypeNode(NodeFactory factory, TypeDesc
10961094

10971095
protected virtual void OutputInterfaceMap(NodeFactory factory, ref ObjectDataBuilder objData)
10981096
{
1099-
Debug.Assert(EmitVirtualSlotsAndInterfaces);
1100-
11011097
foreach (var itf in _type.RuntimeInterfaces)
11021098
{
11031099
objData.EmitPointerReloc(GetInterfaceTypeNode(factory, itf));
@@ -1150,7 +1146,7 @@ protected void OutputOptionalFields(NodeFactory factory, ref ObjectDataBuilder o
11501146

11511147
private void OutputSealedVTable(NodeFactory factory, bool relocsOnly, ref ObjectDataBuilder objData)
11521148
{
1153-
if (EmitVirtualSlotsAndInterfaces && !_type.IsArrayTypeWithoutGenericInterfaces())
1149+
if (EmitVirtualSlots && !_type.IsArrayTypeWithoutGenericInterfaces())
11541150
{
11551151
// Sealed vtables have relative pointers, so to minimize size, we build sealed vtables for the canonical types
11561152
SealedVTableNode sealedVTable = factory.SealedVTable(_type.ConvertToCanonForm(CanonicalFormKind.Specific));

‎src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs

+19
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ internal static int Run()
5454
TestRefAny.Run();
5555
TestNullableCasting.Run();
5656
TestVariantCasting.Run();
57+
TestVariantDispatchUnconstructedTypes.Run();
5758
TestMDArrayAddressMethod.Run();
5859
TestNativeLayoutGeneration.Run();
5960
TestByRefLikeVTables.Run();
@@ -1159,6 +1160,24 @@ public static void Run()
11591160
}
11601161
}
11611162

1163+
class TestVariantDispatchUnconstructedTypes
1164+
{
1165+
interface IFoo { }
1166+
class Foo : IFoo { }
1167+
1168+
[MethodImpl(MethodImplOptions.NoInlining)]
1169+
static IEnumerable<IFoo> GetFoos() => new Foo[5];
1170+
1171+
public static void Run()
1172+
{
1173+
int j = 0;
1174+
foreach (var f in GetFoos())
1175+
j++;
1176+
if (j != 5)
1177+
throw new Exception();
1178+
}
1179+
}
1180+
11621181
class TestMDArrayAddressMethod
11631182
{
11641183
[MethodImpl(MethodImplOptions.NoInlining)]

0 commit comments

Comments
 (0)
Please sign in to comment.