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).
#endif
}
- internal ushort GenericArgumentCount
+ internal ushort GenericParameterCount
{
get
{
#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)
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);
+ }
}
}
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;
}
}
{
Debug.Assert(IsNullable);
Debug.Assert(GenericArity == 1);
- return GenericArguments[0].Value;
+ return GenericArguments[0];
}
}
#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
Debug.Assert(pTgtType->HasDispatchMap, "Missing dispatch map");
MethodTable* pItfOpenGenericType = null;
- EETypeRef* pItfInstantiation = null;
+ MethodTableList itfInstantiation = default;
int itfArity = 0;
GenericVariance* pItfVarianceInfo = null;
{
pItfOpenGenericType = pItfType->GenericDefinition;
itfArity = (int)pItfType->GenericArity;
- pItfInstantiation = pItfType->GenericArguments;
+ itfInstantiation = pItfType->GenericArguments;
pItfVarianceInfo = pItfType->GenericVariance;
}
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;
{
// 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;
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;
// Compare the instantiations to see if they're compatible taking variance into account.
if (TypeParametersAreCompatible(targetArity,
- pInterfaceInstantiation,
- pTargetInstantiation,
+ interfaceInstantiation,
+ targetInstantiation,
pTargetVarianceInfo,
fArrayCovariance,
pVisited))
{
// 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;
// Compare the instantiations to see if they're compatible taking variance into account.
if (TypeParametersAreCompatible(targetArity,
- pSourceInstantiation,
- pTargetInstantiation,
+ sourceInstantiation,
+ targetInstantiation,
pTargetVarianceInfo,
false,
pVisited))
// 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)
// 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)
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
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;
get
{
Debug.Assert((uint)index < _argumentCount);
- return new EETypePtr(_arguments[index].Value);
+ return new EETypePtr(_arguments[index]);
}
}
}
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)
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)
{
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
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]);
- }
}
}
{
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++)
// 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.
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);
+ }
+ }
}
}
// 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;
}
{
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));
}
}
}
}
- protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory)
+ public override ObjectNodeSection GetSection(NodeFactory factory)
{
if (factory.Target.IsWindows)
return ObjectNodeSection.FoldableReadOnlyDataSection;
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();
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);
- }
}
}
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;
--- /dev/null
+// 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;
+ }
+ }
+}
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
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);
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)
<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" />