Make generic composition information more efficient (#85623)
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>
Tue, 2 May 2023 06:40:30 +0000 (15:40 +0900)
committerGitHub <noreply@github.com>
Tue, 2 May 2023 06:40:30 +0000 (15:40 +0900)
Before this PR, we tracked information about generic composition of a `MethodTable` this way:

* Each generic instance `MethodTable` had a pointer to the generic definition `MethodTable`.
* Each generic instance `MethodTable` had a pointer to its generic composition: a variable sized data structure that had a small header (number of elements, a flag whether it's variant), followed by pointers to `MethodTable`s of components, optionally followed by variance information for each argument.

This works, but it's not particularly efficient, especially in light of some facts that didn't exist at the time this scheme was introduced.

In particular:

* The number of generic parameters can be obtained from the generic definition, no need to duplicate it into instances.
* The variance information can be obtained from the generic definition, no need to duplicate it into instances.
* It makes no sense to indirect to a single-element list - the list can be bypassed if arity is 1.

This PR addresses all of the above.

Saves about 0.5% in size for BasicMinimalApi, improves startup (the composition no longer needs to be dehydrated because it's relative pointers now), and improves working set (the composition stuff accounted for 100 kB of private dehydrated working set in BasicMinimalApi).

15 files changed:
src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/DispatchResolve.cs
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/EETypePtr.cs
src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs
src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs
src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeSystemContext.Runtime.cs
src/coreclr/tools/Common/Internal/NativeFormat/NativeFormat.cs
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericCompositionNode.cs
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericVarianceNode.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs
src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj

index 4764545..76dbab0 100644 (file)
@@ -262,7 +262,7 @@ namespace Internal.Runtime
 #endif
         }
 
-        internal ushort GenericArgumentCount
+        internal ushort GenericParameterCount
         {
             get
             {
@@ -523,36 +523,10 @@ namespace Internal.Runtime
 #endif
         }
 
-        [StructLayout(LayoutKind.Sequential)]
-        private readonly struct GenericComposition
-        {
-            public readonly ushort Arity;
-
-            private readonly EETypeRef _genericArgument1;
-            public EETypeRef* GenericArguments
-            {
-                get
-                {
-                    return (EETypeRef*)Unsafe.AsPointer(ref Unsafe.AsRef(in _genericArgument1));
-                }
-            }
-
-            public GenericVariance* GenericVariance
-            {
-                get
-                {
-                    // Generic variance directly follows the last generic argument
-                    return (GenericVariance*)(GenericArguments + Arity);
-                }
-            }
-        }
-
 #if TYPE_LOADER_IMPLEMENTATION
-        internal static int GetGenericCompositionSize(int numArguments, bool hasVariance)
+        internal static int GetGenericCompositionSize(int numArguments)
         {
-            return IntPtr.Size
-                + numArguments * IntPtr.Size
-                + (hasVariance ? numArguments * sizeof(GenericVariance) : 0);
+            return numArguments * IntPtr.Size;
         }
 
         internal void SetGenericComposition(IntPtr data)
@@ -571,31 +545,34 @@ namespace Internal.Runtime
             get
             {
                 Debug.Assert(IsGeneric);
-                if (IsDynamicType || !SupportsRelativePointers)
-                    return GetField<Pointer<GenericComposition>>(EETypeField.ETF_GenericComposition).Value->Arity;
-
-                return GetField<RelativePointer<GenericComposition>>(EETypeField.ETF_GenericComposition).Value->Arity;
-            }
-#if TYPE_LOADER_IMPLEMENTATION
-            set
-            {
-                Debug.Assert(IsDynamicType);
-                // GenericComposition is a readonly struct, so we just blit the bytes over. Asserts guard changes to the layout.
-                *((ushort*)GetField<Pointer<GenericComposition>>(EETypeField.ETF_GenericComposition).Value) = checked((ushort)value);
-                Debug.Assert(GenericArity == (ushort)value);
+                return GenericDefinition->GenericParameterCount;
             }
-#endif
         }
 
-        internal EETypeRef* GenericArguments
+        internal MethodTableList GenericArguments
         {
             get
             {
                 Debug.Assert(IsGeneric);
-                if (IsDynamicType || !SupportsRelativePointers)
-                    return GetField<Pointer<GenericComposition>>(EETypeField.ETF_GenericComposition).Value->GenericArguments;
 
-                return GetField<RelativePointer<GenericComposition>>(EETypeField.ETF_GenericComposition).Value->GenericArguments;
+                void* pField = (byte*)Unsafe.AsPointer(ref this) + GetFieldOffset(EETypeField.ETF_GenericComposition);
+                uint arity = GenericArity;
+
+                // If arity is 1, the field value is the component. For arity > 1, components are stored out-of-line
+                // and are shared.
+                if (IsDynamicType || !SupportsRelativePointers)
+                {
+                    // This is a full pointer [that points to a list of full pointers]
+                    MethodTable* pListStart = arity == 1 ? (MethodTable*)pField : *(MethodTable**)pField;
+                    return new MethodTableList(pListStart);
+                }
+                else
+                {
+                    // This is a relative pointer [that points to a list of relative pointers]
+                    RelativePointer<MethodTable>* pListStart = arity == 1 ?
+                        (RelativePointer<MethodTable>*)pField : (RelativePointer<MethodTable>*)((RelativePointer*)pField)->Value;
+                    return new MethodTableList(pListStart);
+                }
             }
         }
 
@@ -608,10 +585,13 @@ namespace Internal.Runtime
                 if (!HasGenericVariance)
                     return null;
 
+                if (IsGeneric)
+                    return GenericDefinition->GenericVariance;
+
                 if (IsDynamicType || !SupportsRelativePointers)
-                    return GetField<Pointer<GenericComposition>>(EETypeField.ETF_GenericComposition).Value->GenericVariance;
+                    return GetField<Pointer<GenericVariance>>(EETypeField.ETF_GenericComposition).Value;
 
-                return GetField<RelativePointer<GenericComposition>>(EETypeField.ETF_GenericComposition).Value->GenericVariance;
+                return GetField<RelativePointer<GenericVariance>>(EETypeField.ETF_GenericComposition).Value;
             }
         }
 
@@ -996,7 +976,7 @@ namespace Internal.Runtime
             {
                 Debug.Assert(IsNullable);
                 Debug.Assert(GenericArity == 1);
-                return GenericArguments[0].Value;
+                return GenericArguments[0];
             }
         }
 
@@ -1497,29 +1477,6 @@ namespace Internal.Runtime
 #endif
     }
 
-    // Wrapper around MethodTable pointers that may be indirected through the IAT if their low bit is set.
-    [StructLayout(LayoutKind.Sequential)]
-    internal unsafe struct EETypeRef
-    {
-        private byte* _value;
-
-        public MethodTable* Value
-        {
-            get
-            {
-                if (((int)_value & IndirectionConstants.IndirectionCellPointer) == 0)
-                    return (MethodTable*)_value;
-                return *(MethodTable**)(_value - IndirectionConstants.IndirectionCellPointer);
-            }
-#if TYPE_LOADER_IMPLEMENTATION
-            set
-            {
-                _value = (byte*)value;
-            }
-#endif
-        }
-    }
-
     // Wrapper around pointers
     [StructLayout(LayoutKind.Sequential)]
     internal readonly struct Pointer
index 3687dce..349415c 100644 (file)
@@ -123,7 +123,7 @@ namespace System.Runtime
             Debug.Assert(pTgtType->HasDispatchMap, "Missing dispatch map");
 
             MethodTable* pItfOpenGenericType = null;
-            EETypeRef* pItfInstantiation = null;
+            MethodTableList itfInstantiation = default;
             int itfArity = 0;
             GenericVariance* pItfVarianceInfo = null;
 
@@ -207,7 +207,7 @@ namespace System.Runtime
                         {
                             pItfOpenGenericType = pItfType->GenericDefinition;
                             itfArity = (int)pItfType->GenericArity;
-                            pItfInstantiation = pItfType->GenericArguments;
+                            itfInstantiation = pItfType->GenericArguments;
                             pItfVarianceInfo = pItfType->GenericVariance;
                         }
 
@@ -219,13 +219,13 @@ namespace System.Runtime
                             continue;
 
                         // Grab instantiation details for the candidate interface.
-                        EETypeRef* pCurEntryInstantiation = pCurEntryType->GenericArguments;
+                        MethodTableList curEntryInstantiation = pCurEntryType->GenericArguments;
 
                         // The types represent different instantiations of the same generic type. The
                         // arity of both had better be the same.
                         Debug.Assert(itfArity == (int)pCurEntryType->GenericArity, "arity mismatch between generic instantiations");
 
-                        if (TypeCast.TypeParametersAreCompatible(itfArity, pCurEntryInstantiation, pItfInstantiation, pItfVarianceInfo, fArrayCovariance, null))
+                        if (TypeCast.TypeParametersAreCompatible(itfArity, curEntryInstantiation, itfInstantiation, pItfVarianceInfo, fArrayCovariance, null))
                         {
                             *pImplSlotNumber = i->_usImplMethodSlot;
 
index acd91f5..aad8078 100644 (file)
@@ -278,7 +278,7 @@ namespace System.Runtime
             {
                 // Grab details about the instantiation of the target generic interface.
                 MethodTable* pTargetGenericType = pTargetType->GenericDefinition;
-                EETypeRef* pTargetInstantiation = pTargetType->GenericArguments;
+                MethodTableList targetInstantiation = pTargetType->GenericArguments;
                 int targetArity = (int)pTargetType->GenericArity;
                 GenericVariance* pTargetVarianceInfo = pTargetType->GenericVariance;
 
@@ -303,7 +303,7 @@ namespace System.Runtime
                             continue;
 
                         // Grab instantiation details for the candidate interface.
-                        EETypeRef* pInterfaceInstantiation = pInterfaceType->GenericArguments;
+                        MethodTableList interfaceInstantiation = pInterfaceType->GenericArguments;
                         int interfaceArity = (int)pInterfaceType->GenericArity;
                         GenericVariance* pInterfaceVarianceInfo = pInterfaceType->GenericVariance;
 
@@ -315,8 +315,8 @@ namespace System.Runtime
 
                         // Compare the instantiations to see if they're compatible taking variance into account.
                         if (TypeParametersAreCompatible(targetArity,
-                                                        pInterfaceInstantiation,
-                                                        pTargetInstantiation,
+                                                        interfaceInstantiation,
+                                                        targetInstantiation,
                                                         pTargetVarianceInfo,
                                                         fArrayCovariance,
                                                         pVisited))
@@ -339,13 +339,13 @@ namespace System.Runtime
             {
                 // Get generic instantiation metadata for both types.
 
-                EETypeRef* pTargetInstantiation = pTargetType->GenericArguments;
+                MethodTableList targetInstantiation = pTargetType->GenericArguments;
                 int targetArity = (int)pTargetType->GenericArity;
                 GenericVariance* pTargetVarianceInfo = pTargetType->GenericVariance;
 
                 Debug.Assert(pTargetVarianceInfo != null, "did not expect empty variance info");
 
-                EETypeRef* pSourceInstantiation = pSourceType->GenericArguments;
+                MethodTableList sourceInstantiation = pSourceType->GenericArguments;
                 int sourceArity = (int)pSourceType->GenericArity;
                 GenericVariance* pSourceVarianceInfo = pSourceType->GenericVariance;
 
@@ -357,8 +357,8 @@ namespace System.Runtime
 
                 // Compare the instantiations to see if they're compatible taking variance into account.
                 if (TypeParametersAreCompatible(targetArity,
-                                                pSourceInstantiation,
-                                                pTargetInstantiation,
+                                                sourceInstantiation,
+                                                targetInstantiation,
                                                 pTargetVarianceInfo,
                                                 false,
                                                 pVisited))
@@ -376,8 +376,8 @@ namespace System.Runtime
         // override the defined variance of each parameter and instead assume it is covariant. This is used to
         // implement covariant array interfaces.
         internal static unsafe bool TypeParametersAreCompatible(int arity,
-                                                               EETypeRef* pSourceInstantiation,
-                                                               EETypeRef* pTargetInstantiation,
+                                                               MethodTableList sourceInstantiation,
+                                                               MethodTableList targetInstantiation,
                                                                GenericVariance* pVarianceInfo,
                                                                bool fForceCovariance,
                                                                EETypePairList* pVisited)
@@ -386,8 +386,8 @@ namespace System.Runtime
             // of type args.
             for (int i = 0; i < arity; i++)
             {
-                MethodTable* pTargetArgType = pTargetInstantiation[i].Value;
-                MethodTable* pSourceArgType = pSourceInstantiation[i].Value;
+                MethodTable* pTargetArgType = targetInstantiation[i];
+                MethodTable* pSourceArgType = sourceInstantiation[i];
 
                 GenericVariance varType;
                 if (fForceCovariance)
index 3ff94d3..19b1b12 100644 (file)
@@ -18,8 +18,8 @@ using System.Runtime.CompilerServices;
 using Internal.Runtime.CompilerServices;
 
 using MethodTable = Internal.Runtime.MethodTable;
+using MethodTableList = Internal.Runtime.MethodTableList;
 using EETypeElementType = Internal.Runtime.EETypeElementType;
-using EETypeRef = Internal.Runtime.EETypeRef;
 using CorElementType = System.Reflection.CorElementType;
 
 namespace System
@@ -446,10 +446,10 @@ namespace System
 
         public struct GenericArgumentCollection
         {
-            private EETypeRef* _arguments;
+            private MethodTableList _arguments;
             private uint _argumentCount;
 
-            internal GenericArgumentCollection(uint argumentCount, EETypeRef* arguments)
+            internal GenericArgumentCollection(uint argumentCount, MethodTableList arguments)
             {
                 _argumentCount = argumentCount;
                 _arguments = arguments;
@@ -468,7 +468,7 @@ namespace System
                 get
                 {
                     Debug.Assert((uint)index < _argumentCount);
-                    return new EETypePtr(_arguments[index].Value);
+                    return new EETypePtr(_arguments[index]);
                 }
             }
         }
index 66e6835..67f633e 100644 (file)
@@ -61,19 +61,10 @@ namespace Internal.Runtime.TypeLoader
             rtth.ToEETypePtr()->GenericDefinition = genericDefinitionHandle.ToEETypePtr();
         }
 
-        public static unsafe void SetGenericVariance(this RuntimeTypeHandle rtth, int argumentIndex, GenericVariance variance)
-        {
-            rtth.ToEETypePtr()->GenericVariance[argumentIndex] = variance;
-        }
-
-        public static unsafe void SetGenericArity(this RuntimeTypeHandle rtth, uint arity)
-        {
-            rtth.ToEETypePtr()->GenericArity = arity;
-        }
-
         public static unsafe void SetGenericArgument(this RuntimeTypeHandle rtth, int argumentIndex, RuntimeTypeHandle argumentType)
         {
-            rtth.ToEETypePtr()->GenericArguments[argumentIndex].Value = argumentType.ToEETypePtr();
+            MethodTableList argumentList = rtth.ToEETypePtr()->GenericArguments;
+            argumentList[argumentIndex] = argumentType.ToEETypePtr();
         }
 
         public static unsafe void SetRelatedParameterType(this RuntimeTypeHandle rtth, RuntimeTypeHandle relatedTypeHandle)
@@ -342,8 +333,11 @@ namespace Internal.Runtime.TypeLoader
 
                 if (isGeneric)
                 {
-                    genericComposition = MemoryHelpers.AllocateMemory(MethodTable.GetGenericCompositionSize(arity, pEEType->HasGenericVariance));
-                    pEEType->SetGenericComposition(genericComposition);
+                    if (arity > 1)
+                    {
+                        genericComposition = MemoryHelpers.AllocateMemory(MethodTable.GetGenericCompositionSize(arity));
+                        pEEType->SetGenericComposition(genericComposition);
+                    }
 
                     if (allocatedNonGCDataSize > 0)
                     {
index b66c60f..1cf54b2 100644 (file)
@@ -438,14 +438,6 @@ namespace Internal.Runtime.TypeLoader
                         state.ThreadStaticDesc = context.GetGCStaticInfo(typeInfoParser.GetUnsigned());
                         break;
 
-                    case BagElementKind.GenericVarianceInfo:
-                        TypeLoaderLogger.WriteLine("Found BagElementKind.GenericVarianceInfo");
-                        NativeParser varianceInfoParser = typeInfoParser.GetParserFromRelativeOffset();
-                        state.GenericVarianceFlags = new GenericVariance[varianceInfoParser.GetSequenceCount()];
-                        for (int i = 0; i < state.GenericVarianceFlags.Length; i++)
-                            state.GenericVarianceFlags[i] = checked((GenericVariance)varianceInfoParser.GetUnsigned());
-                        break;
-
                     case BagElementKind.FieldLayout:
                         TypeLoaderLogger.WriteLine("Found BagElementKind.FieldLayout");
                         typeInfoParser.SkipInteger(); // Handled in type layout algorithm
@@ -794,15 +786,9 @@ namespace Internal.Runtime.TypeLoader
 
                     state.HalfBakedRuntimeTypeHandle.SetGenericDefinition(GetRuntimeTypeHandle(typeAsDefType.GetTypeDefinition()));
                     Instantiation instantiation = typeAsDefType.Instantiation;
-                    state.HalfBakedRuntimeTypeHandle.SetGenericArity((uint)instantiation.Length);
                     for (int argIndex = 0; argIndex < instantiation.Length; argIndex++)
                     {
                         state.HalfBakedRuntimeTypeHandle.SetGenericArgument(argIndex, GetRuntimeTypeHandle(instantiation[argIndex]));
-                        if (state.GenericVarianceFlags != null)
-                        {
-                            Debug.Assert(state.GenericVarianceFlags.Length == instantiation.Length);
-                            state.HalfBakedRuntimeTypeHandle.SetGenericVariance(argIndex, state.GenericVarianceFlags[argIndex]);
-                        }
                     }
                 }
 
index 6f81634..d37e5a6 100644 (file)
@@ -197,7 +197,7 @@ namespace Internal.TypeSystem
             {
                 unsafe
                 {
-                    TypeDesc[] genericParameters = new TypeDesc[rtth.ToEETypePtr()->GenericArgumentCount];
+                    TypeDesc[] genericParameters = new TypeDesc[rtth.ToEETypePtr()->GenericParameterCount];
                     Runtime.GenericVariance* runtimeVariance = rtth.ToEETypePtr()->HasGenericVariance ?
                         rtth.ToEETypePtr()->GenericVariance : null;
                     for (int i = 0; i < genericParameters.Length; i++)
index 7ccaa59..19a7585 100644 (file)
@@ -55,7 +55,7 @@ namespace Internal.NativeFormat
         // unused                   = 0x4d,
         ClassConstructorPointer     = 0x4e,
         // unused                   = 0x4f,
-        GenericVarianceInfo         = 0x50,
+        // unused                   = 0x50,
         // unused                   = 0x51,
 
         // Add new custom bag elements that don't match to something you'd find in the ECMA metadata here.
index 3689b9b..6b019df 100644 (file)
@@ -1136,29 +1136,49 @@ namespace ILCompiler.DependencyAnalysis
                         objData.EmitRelativeRelocOrIndirectionReference(typeDefNode);
                     else
                         objData.EmitPointerRelocOrIndirectionReference(typeDefNode);
-                }
 
-                GenericCompositionDetails details;
-                if (_type.GetTypeDefinition() == factory.ArrayOfTEnumeratorType)
-                {
-                    // Generic array enumerators use special variance rules recognized by the runtime
-                    details = new GenericCompositionDetails(_type.Instantiation, new[] { GenericVariance.ArrayCovariant });
-                }
-                else if (factory.TypeSystemContext.IsGenericArrayInterfaceType(_type))
-                {
-                    // Runtime casting logic relies on all interface types implemented on arrays
-                    // to have the variant flag set (even if all the arguments are non-variant).
-                    // This supports e.g. casting uint[] to ICollection<int>
-                    details = new GenericCompositionDetails(_type, forceVarianceInfo: true);
+                    ISymbolNode compositionNode = _type.Instantiation.Length > 1
+                        ? factory.GenericComposition(_type.Instantiation)
+                        : factory.NecessaryTypeSymbol(_type.Instantiation[0]);
+
+                    if (factory.Target.SupportsRelativePointers)
+                        objData.EmitReloc(compositionNode, RelocType.IMAGE_REL_BASED_RELPTR32);
+                    else
+                        objData.EmitPointerReloc(compositionNode);
                 }
                 else
-                    details = new GenericCompositionDetails(_type);
+                {
+                    GenericVarianceDetails details;
+                    if (_type == factory.ArrayOfTEnumeratorType)
+                    {
+                        // Generic array enumerators use special variance rules recognized by the runtime
+                        details = new GenericVarianceDetails(new[] { GenericVariance.ArrayCovariant });
+                    }
+                    else if (factory.TypeSystemContext.IsGenericArrayInterfaceType(_type))
+                    {
+                        // Runtime casting logic relies on all interface types implemented on arrays
+                        // to have the variant flag set (even if all the arguments are non-variant).
+                        // This supports e.g. casting uint[] to ICollection<int>
+                        details = new GenericVarianceDetails(_type);
+                    }
+                    else if (_type.HasVariance)
+                    {
+                        details = new GenericVarianceDetails(_type);
+                    }
+                    else
+                    {
+                        details = default;
+                    }
 
-                ISymbolNode compositionNode = factory.GenericComposition(details);
-                if (factory.Target.SupportsRelativePointers)
-                    objData.EmitReloc(compositionNode, RelocType.IMAGE_REL_BASED_RELPTR32);
-                else
-                    objData.EmitPointerReloc(compositionNode);
+                    if (!details.IsNull)
+                    {
+                        ISymbolNode varianceInfoNode = factory.GenericVariance(details);
+                        if (factory.Target.SupportsRelativePointers)
+                            objData.EmitReloc(varianceInfoNode, RelocType.IMAGE_REL_BASED_RELPTR32);
+                        else
+                            objData.EmitPointerReloc(varianceInfoNode);
+                    }
+                }
             }
         }
 
index 3f26e1f..447bbfa 100644 (file)
@@ -1,25 +1,19 @@
 // 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 Internal.Text;
 using Internal.TypeSystem;
 
-using Debug = System.Diagnostics.Debug;
-using GenericVariance = Internal.Runtime.GenericVariance;
-
 namespace ILCompiler.DependencyAnalysis
 {
     /// <summary>
-    /// Describes how a generic type instance is composed - the number of generic arguments, their types,
-    /// and variance information.
+    /// Describes types of arguments of generic type instances.
     /// </summary>
-    public class GenericCompositionNode : DehydratableObjectNode, ISymbolDefinitionNode
+    public class GenericCompositionNode : ObjectNode, ISymbolDefinitionNode
     {
-        private GenericCompositionDetails _details;
+        private Instantiation _details;
 
-        internal GenericCompositionNode(GenericCompositionDetails details)
+        internal GenericCompositionNode(Instantiation details)
         {
             _details = details;
         }
@@ -28,24 +22,10 @@ namespace ILCompiler.DependencyAnalysis
         {
             sb.Append("__GenericInstance");
 
-            Debug.Assert(_details.Instantiation[0] != null || _details.Variance != null);
-            if (_details.Instantiation[0] != null)
-            {
-                for (int i = 0; i < _details.Instantiation.Length; i++)
-                {
-                    sb.Append('_');
-                    sb.Append(nameMangler.GetMangledTypeName(_details.Instantiation[i]));
-                }
-            }
-
-            if (_details.Variance != null)
+            foreach (TypeDesc instArg in _details)
             {
-                sb.Append("__Variance__");
-                for (int i = 0; i < _details.Variance.Length; i++)
-                {
-                    sb.Append('_');
-                    sb.Append((checked((byte)_details.Variance[i])).ToStringInvariant());
-                }
+                sb.Append('_');
+                sb.Append(nameMangler.GetMangledTypeName(instArg));
             }
         }
 
@@ -57,7 +37,7 @@ namespace ILCompiler.DependencyAnalysis
             }
         }
 
-        protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory)
+        public override ObjectNodeSection GetSection(NodeFactory factory)
         {
             if (factory.Target.IsWindows)
                 return ObjectNodeSection.FoldableReadOnlyDataSection;
@@ -69,36 +49,23 @@ namespace ILCompiler.DependencyAnalysis
 
         public override bool StaticDependenciesAreComputed => true;
 
-        protected override ObjectData GetDehydratableData(NodeFactory factory, bool relocsOnly = false)
+        public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
         {
-            bool hasVariance = _details.Variance != null;
-
             var builder = new ObjectDataBuilder(factory, relocsOnly);
             builder.AddSymbol(this);
 
-            builder.RequireInitialPointerAlignment();
-
-            builder.EmitShort((short)checked((ushort)_details.Instantiation.Length));
-
-            builder.EmitByte((byte)(hasVariance ? 1 : 0));
-
-            // TODO: general purpose padding
-            builder.EmitByte(0);
-            if (factory.Target.PointerSize == 8)
-                builder.EmitInt(0);
+            bool useRelativePointers = factory.Target.SupportsRelativePointers;
+            if (useRelativePointers)
+                builder.RequireInitialAlignment(4);
+            else
+                builder.RequireInitialPointerAlignment();
 
-            foreach (var typeArg in _details.Instantiation)
+            foreach (var typeArg in _details)
             {
-                if (typeArg == null)
-                    builder.EmitZeroPointer();
+                if (useRelativePointers)
+                    builder.EmitReloc(factory.NecessaryTypeSymbol(typeArg), RelocType.IMAGE_REL_BASED_RELPTR32);
                 else
-                    builder.EmitPointerRelocOrIndirectionReference(factory.NecessaryTypeSymbol(typeArg));
-            }
-
-            if (hasVariance)
-            {
-                foreach (var argVariance in _details.Variance)
-                    builder.EmitByte(checked((byte)argVariance));
+                    builder.EmitPointerReloc(factory.NecessaryTypeSymbol(typeArg));
             }
 
             return builder.ToObjectData();
@@ -109,136 +76,19 @@ namespace ILCompiler.DependencyAnalysis
         public override int ClassCode => -762680703;
         public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
         {
-            return _details.CompareToImpl(((GenericCompositionNode)other)._details, comparer);
-        }
-    }
-
-    internal struct GenericCompositionDetails : IEquatable<GenericCompositionDetails>
-    {
-        public readonly Instantiation Instantiation;
-
-        public readonly GenericVariance[] Variance;
-
-        public GenericCompositionDetails(TypeDesc genericTypeInstance, bool forceVarianceInfo = false)
-        {
-            if (genericTypeInstance.IsTypeDefinition)
-                Instantiation = new Instantiation(new TypeDesc[genericTypeInstance.Instantiation.Length]);
-            else
-                Instantiation = genericTypeInstance.Instantiation;
-
-            bool emitVarianceInfo = forceVarianceInfo;
-            if (!emitVarianceInfo)
-            {
-                foreach (GenericParameterDesc param in genericTypeInstance.GetTypeDefinition().Instantiation)
-                {
-                    if (param.Variance != Internal.TypeSystem.GenericVariance.None)
-                    {
-                        emitVarianceInfo = true;
-                        break;
-                    }
-                }
-            }
-
-            if (emitVarianceInfo)
-            {
-                Debug.Assert((byte)Internal.TypeSystem.GenericVariance.Contravariant == (byte)GenericVariance.Contravariant);
-                Debug.Assert((byte)Internal.TypeSystem.GenericVariance.Covariant == (byte)GenericVariance.Covariant);
-
-                Variance = new GenericVariance[Instantiation.Length];
-                int i = 0;
-                foreach (GenericParameterDesc param in genericTypeInstance.GetTypeDefinition().Instantiation)
-                {
-                    Variance[i++] = (GenericVariance)param.Variance;
-                }
-            }
-            else
-            {
-                Variance = null;
-            }
-        }
-
-        public GenericCompositionDetails(Instantiation instantiation, GenericVariance[] variance)
-        {
-            Debug.Assert(variance == null || instantiation.Length == variance.Length);
-            Instantiation = instantiation;
-            Variance = variance;
-        }
-
-        public bool Equals(GenericCompositionDetails other)
-        {
-            if (Instantiation.Length != other.Instantiation.Length)
-                return false;
-
-            if ((Variance == null) != (other.Variance == null))
-                return false;
-
-            for (int i = 0; i < Instantiation.Length; i++)
-            {
-                if (Instantiation[i] != other.Instantiation[i])
-                    return false;
-
-                if (Variance != null)
-                {
-                    if (Variance[i] != other.Variance[i])
-                        return false;
-                }
-            }
-
-            return true;
-        }
-
-        public int CompareToImpl(GenericCompositionDetails other, TypeSystemComparer comparer)
-        {
-            var compare = Instantiation.Length.CompareTo(other.Instantiation.Length);
+            var otherComposition = (GenericCompositionNode)other;
+            var compare = _details.Length.CompareTo(otherComposition._details.Length);
             if (compare != 0)
                 return compare;
 
-            if (Variance == null && other.Variance != null)
-                return -1;
-
-            if (Variance != null && other.Variance == null)
-                return 1;
-
-            for (int i = 0; i < Instantiation.Length; i++)
+            for (int i = 0; i < _details.Length; i++)
             {
-                compare = comparer.Compare(Instantiation[i], other.Instantiation[i]);
+                compare = comparer.Compare(_details[i], otherComposition._details[i]);
                 if (compare != 0)
                     return compare;
-
-                if (Variance != null)
-                {
-                    compare = Variance[i].CompareTo(other.Variance[i]);
-                    if (compare != 0)
-                        return compare;
-                }
             }
 
-            Debug.Assert(Equals(other));
             return 0;
         }
-
-        public override bool Equals(object obj)
-        {
-            return obj is GenericCompositionDetails && Equals((GenericCompositionDetails)obj);
-        }
-
-        public override int GetHashCode()
-        {
-            int hashCode = 13;
-
-            if (Variance != null)
-            {
-                foreach (var element in Variance)
-                {
-                    int value = (int)element * 0x5498341 + 0x832424;
-                    hashCode = hashCode * 31 + value;
-                }
-            }
-
-            // If the element is null, this is a variance-only composition info
-            // for generic definitions.
-            Debug.Assert(Instantiation[0] != null || Variance != null);
-            return Instantiation[0] == null ? hashCode : Instantiation.ComputeGenericInstanceHashCode(hashCode);
-        }
     }
 }
index 904475d..b42d932 100644 (file)
@@ -43,6 +43,13 @@ namespace ILCompiler.DependencyAnalysis
             EETypeRareFlags rareFlags = 0;
 
             uint flags = EETypeBuilderHelpers.ComputeFlags(_type);
+
+            // Generic array enumerators use special variance rules recognized by the runtime
+            // Runtime casting logic relies on all interface types implemented on arrays
+            // to have the variant flag set.
+            if (_type == factory.ArrayOfTEnumeratorType || factory.TypeSystemContext.IsGenericArrayInterfaceType(_type))
+                flags |= (uint)EETypeFlags.GenericVarianceFlag;
+
             if (_type.IsByRefLike)
                 rareFlags |= EETypeRareFlags.IsByRefLikeFlag;
 
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericVarianceNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericVarianceNode.cs
new file mode 100644 (file)
index 0000000..7fedd00
--- /dev/null
@@ -0,0 +1,147 @@
+// 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 Internal.Text;
+using Internal.TypeSystem;
+
+using Debug = System.Diagnostics.Debug;
+using GenericVariance = Internal.Runtime.GenericVariance;
+
+namespace ILCompiler.DependencyAnalysis
+{
+    /// <summary>
+    /// Describes variance of a generic type definition.
+    /// </summary>
+    public class GenericVarianceNode : ObjectNode, ISymbolDefinitionNode
+    {
+        private GenericVarianceDetails _details;
+
+        internal GenericVarianceNode(GenericVarianceDetails details)
+        {
+            _details = details;
+        }
+
+        public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
+        {
+            sb.Append("__GenericVariance");
+
+            for (int i = 0; i < _details.Variance.Length; i++)
+            {
+                sb.Append('_');
+                sb.Append((checked((byte)_details.Variance[i])).ToStringInvariant());
+            }
+        }
+
+        public int Offset => 0;
+
+        public override ObjectNodeSection GetSection(NodeFactory factory)
+        {
+            if (factory.Target.IsWindows)
+                return ObjectNodeSection.FoldableReadOnlyDataSection;
+            else
+                return ObjectNodeSection.DataSection;
+        }
+
+        public override bool IsShareable => true;
+
+        public override bool StaticDependenciesAreComputed => true;
+
+        public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
+        {
+            var builder = new ObjectDataBuilder(factory, relocsOnly);
+            builder.AddSymbol(this);
+
+            foreach (var argVariance in _details.Variance)
+                builder.EmitByte(checked((byte)argVariance));
+
+            return builder.ToObjectData();
+        }
+
+        protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
+
+        public override int ClassCode => -4687913;
+        public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
+        {
+            return _details.CompareToImpl(((GenericVarianceNode)other)._details);
+        }
+    }
+
+    internal struct GenericVarianceDetails : IEquatable<GenericVarianceDetails>
+    {
+        public readonly GenericVariance[] Variance;
+
+        public bool IsNull => Variance == null;
+
+        public GenericVarianceDetails(TypeDesc typeDefinition)
+        {
+            Debug.Assert(typeDefinition.IsTypeDefinition);
+            Debug.Assert(typeDefinition.HasInstantiation);
+
+            Debug.Assert((byte)Internal.TypeSystem.GenericVariance.Contravariant == (byte)GenericVariance.Contravariant);
+            Debug.Assert((byte)Internal.TypeSystem.GenericVariance.Covariant == (byte)GenericVariance.Covariant);
+
+            Variance = new GenericVariance[typeDefinition.Instantiation.Length];
+            int i = 0;
+            foreach (GenericParameterDesc param in typeDefinition.Instantiation)
+            {
+                Variance[i++] = (GenericVariance)param.Variance;
+            }
+        }
+
+        public GenericVarianceDetails(GenericVariance[] variance)
+        {
+            Variance = variance;
+        }
+
+        public bool Equals(GenericVarianceDetails other)
+        {
+            if (Variance.Length != other.Variance.Length)
+                return false;
+
+            for (int i = 0; i < Variance.Length; i++)
+            {
+                if (Variance[i] != other.Variance[i])
+                    return false;
+            }
+
+            return true;
+        }
+
+        public int CompareToImpl(GenericVarianceDetails other)
+        {
+            var compare = Variance.Length.CompareTo(other.Variance.Length);
+            if (compare != 0)
+                return compare;
+
+            for (int i = 0; i < Variance.Length; i++)
+            {
+                compare = Variance[i].CompareTo(other.Variance[i]);
+                if (compare != 0)
+                    return compare;
+            }
+
+            Debug.Assert(Equals(other));
+            return 0;
+        }
+
+        public override bool Equals(object obj)
+        {
+            return obj is GenericVarianceDetails && Equals((GenericVarianceDetails)obj);
+        }
+
+        public override int GetHashCode()
+        {
+            int hashCode = 13;
+
+            foreach (byte element in Variance)
+            {
+                int value = element * 0x5498341 + 0x832424;
+                hashCode = hashCode * 31 + value;
+            }
+
+            return hashCode;
+        }
+    }
+}
index 47de385..d0e19c3 100644 (file)
@@ -1261,27 +1261,6 @@ namespace ILCompiler.DependencyAnalysis
                 layoutInfo.Append(BagElementKind.BaseType, factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.TypeSignatureVertex(_type.BaseType)).WriteVertex(factory));
             }
 
-            if (_type.GetTypeDefinition().HasVariance || factory.TypeSystemContext.IsGenericArrayInterfaceType(_type))
-            {
-                // Runtime casting logic relies on all interface types implemented on arrays
-                // to have the variant flag set (even if all the arguments are non-variant).
-                // This supports e.g. casting uint[] to ICollection<int>
-                List<uint> varianceFlags = new List<uint>();
-                foreach (GenericParameterDesc param in _type.GetTypeDefinition().Instantiation)
-                {
-                    varianceFlags.Add((uint)param.Variance);
-                }
-
-                layoutInfo.Append(BagElementKind.GenericVarianceInfo, factory.NativeLayout.PlacedUIntVertexSequence(varianceFlags).WriteVertex(factory));
-            }
-            else if (_type.GetTypeDefinition() == factory.ArrayOfTEnumeratorType)
-            {
-                // Generic array enumerators use special variance rules recognized by the runtime
-                List<uint> varianceFlag = new List<uint>();
-                varianceFlag.Add((uint)Internal.Runtime.GenericVariance.ArrayCovariant);
-                layoutInfo.Append(BagElementKind.GenericVarianceInfo, factory.NativeLayout.PlacedUIntVertexSequence(varianceFlag).WriteVertex(factory));
-            }
-
             if (_isUniversalCanon)
             {
                 // For universal canonical template types, we need to write out field layout information so that we
index ef75211..043f2ae 100644 (file)
@@ -396,11 +396,16 @@ namespace ILCompiler.DependencyAnalysis
                 return DispatchMapTable.NewNodeWithSymbol(InterfaceDispatchMap(type));
             });
 
-            _genericCompositions = new NodeCache<GenericCompositionDetails, GenericCompositionNode>((GenericCompositionDetails details) =>
+            _genericCompositions = new NodeCache<Instantiation, GenericCompositionNode>((Instantiation details) =>
             {
                 return new GenericCompositionNode(details);
             });
 
+            _genericVariances = new NodeCache<GenericVarianceDetails, GenericVarianceNode>((GenericVarianceDetails details) =>
+            {
+                return new GenericVarianceNode(details);
+            });
+
             _eagerCctorIndirectionNodes = new NodeCache<MethodDesc, EmbeddedObjectNode>((MethodDesc method) =>
             {
                 Debug.Assert(method.IsStaticConstructor);
@@ -761,13 +766,20 @@ namespace ILCompiler.DependencyAnalysis
             return _interfaceDispatchMapIndirectionNodes.GetOrAdd(type);
         }
 
-        private NodeCache<GenericCompositionDetails, GenericCompositionNode> _genericCompositions;
+        private NodeCache<Instantiation, GenericCompositionNode> _genericCompositions;
 
-        internal ISymbolNode GenericComposition(GenericCompositionDetails details)
+        internal ISymbolNode GenericComposition(Instantiation details)
         {
             return _genericCompositions.GetOrAdd(details);
         }
 
+        private NodeCache<GenericVarianceDetails, GenericVarianceNode> _genericVariances;
+
+        internal ISymbolNode GenericVariance(GenericVarianceDetails details)
+        {
+            return _genericVariances.GetOrAdd(details);
+        }
+
         private NodeCache<string, ExternSymbolNode> _externSymbols;
 
         public ISortableSymbolNode ExternSymbol(string name)
index 04eadf9..fc41ba9 100644 (file)
     <Compile Include="Compiler\DependencyAnalysis\FieldRvaDataNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\FunctionPointerMapNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\GenericStaticBaseInfoNode.cs" />
+    <Compile Include="Compiler\DependencyAnalysis\GenericVarianceNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\GenericVirtualMethodImplNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\IndirectionExtensions.cs" />
     <Compile Include="Compiler\DependencyAnalysis\InlineableStringsResourceNode.cs" />