Skip to content

Commit

Permalink
Delete more type equivalence logic (#85507)
Browse files Browse the repository at this point in the history
* Delete more type equivalence logic

In .NET Native times we could have a situation where two different `MethodTable` pointers represented the same type. This could happen for cloned types, or structurally equivalent parameterized types. The cloned type concept was never used in Native AOT. The structural equivalence disappeared in dotnet/runtimelab@0ef7bd6. Neither of those seems useful. So getting rid of some of the vestiges.

* Delete more stuff

* Remember statically present pointer and mdarrays
  • Loading branch information
MichalStrehovsky authored May 1, 2023
1 parent 844cca8 commit 79647f4
Show file tree
Hide file tree
Showing 23 changed files with 307 additions and 140 deletions.
14 changes: 2 additions & 12 deletions src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -970,22 +970,12 @@ internal MethodTable* BaseType
}

internal MethodTable* NonArrayBaseType
{
get
{
Debug.Assert(!IsArray, "array type not supported in BaseType");
Debug.Assert(IsCanonical, "we expect canonical types here");
return _relatedType._pBaseType;
}
}

// TODO rename?
internal MethodTable* NonClonedNonArrayBaseType
{
get
{
Debug.Assert(!IsArray, "array type not supported in NonArrayBaseType");
Debug.Assert(IsCanonical || IsGenericTypeDefinition, "we expect canonical types here");
Debug.Assert(IsCanonical || IsGenericTypeDefinition, "we expect type definitions here");
Debug.Assert(!IsGenericTypeDefinition || _relatedType._pBaseType == null, "callers assume this would be null for a generic definition");
return _relatedType._pBaseType;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public static unsafe object RhBoxAny(ref byte data, MethodTable* pEEType)

private static unsafe bool UnboxAnyTypeCompare(MethodTable* pEEType, MethodTable* ptrUnboxToEEType)
{
if (TypeCast.AreTypesEquivalent(pEEType, ptrUnboxToEEType))
if (pEEType == ptrUnboxToEEType)
return true;

if (pEEType->ElementType == ptrUnboxToEEType->ElementType)
Expand Down Expand Up @@ -173,7 +173,7 @@ public static unsafe void RhUnboxAny(object? o, ref byte data, EETypePtr pUnboxT

if (ptrUnboxToEEType->IsNullable)
{
isValid = (o == null) || TypeCast.AreTypesEquivalent(o.GetMethodTable(), ptrUnboxToEEType->NullableType);
isValid = (o == null) || o.GetMethodTable() == ptrUnboxToEEType->NullableType;
}
else
{
Expand Down Expand Up @@ -220,7 +220,7 @@ public static unsafe ref byte RhUnbox2(MethodTable* pUnboxToEEType, object obj)
[RuntimeExport("RhUnboxNullable")]
public static unsafe void RhUnboxNullable(ref byte data, MethodTable* pUnboxToEEType, object obj)
{
if ((obj != null) && !TypeCast.AreTypesEquivalent(obj.GetMethodTable(), pUnboxToEEType->NullableType))
if (obj != null && obj.GetMethodTable() != pUnboxToEEType->NullableType)
{
throw pUnboxToEEType->GetClasslibException(ExceptionIDs.InvalidCast);
}
Expand Down
69 changes: 10 additions & 59 deletions src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ private static unsafe object IsInstanceOfClass_Helper(MethodTable* pTargetType,
// walk the type hierarchy looking for a match
while (true)
{
pObjType = pObjType->NonClonedNonArrayBaseType;
pObjType = pObjType->NonArrayBaseType;
if (pObjType == null)
{
return null;
Expand Down Expand Up @@ -400,7 +400,7 @@ internal static unsafe bool TypeParametersAreCompatible(int arity,
case GenericVariance.NonVariant:
// Non-variant type params need to be identical.

if (!AreTypesEquivalent(pSourceArgType, pTargetArgType))
if (pSourceArgType != pTargetArgType)
return false;

break;
Expand Down Expand Up @@ -484,7 +484,7 @@ public static unsafe bool AreTypesAssignable(MethodTable* pSourceType, MethodTab
{
MethodTable* pNullableType = pTargetType->NullableType;

return AreTypesEquivalent(pSourceType, pNullableType);
return pSourceType == pNullableType;
}

return AreTypesAssignableInternal(pSourceType, pTargetType, AssignmentVariation.BoxedSource, null);
Expand All @@ -503,7 +503,7 @@ internal static unsafe bool AreTypesAssignableInternalUncached(MethodTable* pSou
//
// Are the types identical?
//
if (AreTypesEquivalent(pSourceType, pTargetType))
if (pSourceType == pTargetType)
return true;

//
Expand Down Expand Up @@ -543,7 +543,7 @@ internal static unsafe bool AreTypesAssignableInternalUncached(MethodTable* pSou
if (pSourceRelatedParameterType->IsPointerType)
{
// If the parameter types are pointers, then only exact matches are correct.
// As we've already called AreTypesEquivalent at the start of this function,
// As we've already compared equality at the start of this function,
// return false as the exact match case has already been handled.
// int** is not compatible with uint**, nor is int*[] oompatible with uint*[].
return false;
Expand All @@ -558,7 +558,7 @@ internal static unsafe bool AreTypesAssignableInternalUncached(MethodTable* pSou
else if (pSourceRelatedParameterType->IsFunctionPointerType)
{
// If the parameter types are function pointers, then only exact matches are correct.
// As we've already called AreTypesEquivalent at the start of this function,
// As we've already compared equality at the start of this function,
// return false as the exact match case has already been handled.
return false;
}
Expand Down Expand Up @@ -697,36 +697,6 @@ public static unsafe void CheckArrayStore(object array, object obj)
throw array.GetMethodTable()->GetClasslibException(ExceptionIDs.ArrayTypeMismatch);
}

[RuntimeExport("RhTypeCast_CheckVectorElemAddr")]
public static unsafe void CheckVectorElemAddr(MethodTable* elemType, object array)
{
if (array == null)
{
return;
}

Debug.Assert(array.GetMethodTable()->IsArray, "second argument must be an array");

MethodTable* arrayElemType = array.GetMethodTable()->RelatedParameterType;

if (!AreTypesEquivalent(elemType, arrayElemType)
// In addition to the exactness check, add another check to allow non-exact matches through
// if the element type is a ValueType. The issue here is Universal Generics. The Universal
// Generic codegen will generate a call to this helper for all ldelema opcodes if the exact
// type is not known, and this can include ValueTypes. For ValueTypes, the exact check is not
// desirable as enum's are allowed to pass through this code if they are size matched.
// While this check is overly broad and allows non-enum valuetypes to also skip the check
// that is OK, because in the non-enum case the casting operations are sufficient to ensure
// type safety.
&& !elemType->IsValueType)
{
// Throw the array type mismatch exception defined by the classlib, using the input array's MethodTable*
// to find the correct classlib.

throw array.GetMethodTable()->GetClasslibException(ExceptionIDs.ArrayTypeMismatch);
}
}

internal struct ArrayElement
{
public object Value;
Expand Down Expand Up @@ -838,7 +808,7 @@ public static unsafe ref object LdelemaRef(Array array, nint index, IntPtr eleme
MethodTable* elemType = (MethodTable*)elementType;
MethodTable* arrayElemType = array.GetMethodTable()->RelatedParameterType;

if (AreTypesEquivalent(elemType, arrayElemType))
if (elemType == arrayElemType)
{
return ref element;
}
Expand All @@ -859,39 +829,20 @@ internal static unsafe bool IsDerived(MethodTable* pDerivedType, MethodTable* pB
Debug.Assert(pDerivedType->IsCanonical || pDerivedType->IsGenericTypeDefinition, "unexpected MethodTable");

// If a generic type definition reaches this function, then the function should return false unless the types are equivalent.
// This works as the NonClonedNonArrayBaseType of a GenericTypeDefinition is always null.
// This works as the NonArrayBaseType of a GenericTypeDefinition is always null.

do
{
if (pDerivedType == pBaseType)
return true;

pDerivedType = pDerivedType->NonClonedNonArrayBaseType;
pDerivedType = pDerivedType->NonArrayBaseType;
}
while (pDerivedType != null);

return false;
}

// Method to compare two types pointers for type equality
// We cannot just compare the pointers as there can be duplicate type instances
// for cloned and constructed types.
// There are three separate cases here
// 1. The pointers are Equal => true
// 2. Either one or both the types are CLONED, follow to the canonical MethodTable and check
// 3. For Arrays/Pointers, we have to further check for rank and element type equality
[RuntimeExport("RhTypeCast_AreTypesEquivalent")]
public static unsafe bool AreTypesEquivalent(MethodTable* pType1, MethodTable* pType2)
{
if (pType1 == pType2)
return true;

if (pType1->IsParameterizedType && pType2->IsParameterizedType)
return AreTypesEquivalent(pType1->RelatedParameterType, pType2->RelatedParameterType) && pType1->ParameterizedTypeShape == pType2->ParameterizedTypeShape;

return false;
}

// this is necessary for shared generic code - Foo<T> may be executing
// for T being an interface, an array or a class
[RuntimeExport("RhTypeCast_IsInstanceOf")]
Expand Down Expand Up @@ -927,7 +878,7 @@ public static unsafe bool IsInstanceOfException(MethodTable* pTargetType, object

while (true)
{
pObjType = pObjType->NonClonedNonArrayBaseType;
pObjType = pObjType->NonArrayBaseType;
if (pObjType == null)
return false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,12 @@ public static RuntimeTypeHandle GetRelatedParameterTypeHandle(RuntimeTypeHandle
return new RuntimeTypeHandle(elementType);
}

public static unsafe int GetArrayRankOrMinusOneForSzArray(RuntimeTypeHandle arrayHandle)
{
Debug.Assert(IsArrayType(arrayHandle));
return arrayHandle.ToMethodTable()->IsSzArray ? -1 : arrayHandle.ToMethodTable()->ArrayRank;
}

public static bool IsValueType(RuntimeTypeHandle type)
{
return type.ToEETypePtr().IsValueType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de
}
else
{
if (RuntimeImports.AreTypesEquivalent(sourceElementEEType, destinationElementEEType))
if (sourceElementEEType == destinationElementEEType)
{
if (sourceElementEEType.ContainsGCPointers)
{
Expand Down Expand Up @@ -387,7 +387,7 @@ private static unsafe void CopyImplReferenceArrayToValueTypeArray(Array sourceAr
//
private static unsafe void CopyImplValueTypeArrayWithInnerGcRefs(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable)
{
Debug.Assert(RuntimeImports.AreTypesEquivalent(sourceArray.GetEETypePtr(), destinationArray.GetEETypePtr()));
Debug.Assert(sourceArray.GetEETypePtr() == destinationArray.GetEETypePtr());
Debug.Assert(sourceArray.ElementEEType.IsValueType);

EETypePtr sourceElementEEType = sourceArray.GetEETypePtr().ArrayElementType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,7 @@ public bool Equals(EETypePtr p)

public static bool operator ==(EETypePtr value1, EETypePtr value2)
{
if (value1.IsNull)
return value2.IsNull;
else if (value2.IsNull)
return false;
else
return RuntimeImports.AreTypesEquivalent(value1, value2);
return value1._value == value2._value;
}

public static bool operator !=(EETypePtr value1, EETypePtr value2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,13 +296,6 @@ internal static IntPtr RhHandleAllocDependent(object primary, object secondary)
// calls to runtime for type equality checks
//

[MethodImpl(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhTypeCast_AreTypesEquivalent")]
private static extern unsafe bool AreTypesEquivalent(MethodTable* pType1, MethodTable* pType2);

internal static unsafe bool AreTypesEquivalent(EETypePtr pType1, EETypePtr pType2)
=> AreTypesEquivalent(pType1.ToPointer(), pType2.ToPointer());

[MethodImpl(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhTypeCast_AreTypesAssignable")]
internal static extern unsafe bool AreTypesAssignable(MethodTable* pSourceType, MethodTable* pTargetType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,7 @@ public override int GetHashCode()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(RuntimeTypeHandle handle)
{
if (_value == handle._value)
{
return true;
}
else if (this.IsNull || handle.IsNull)
{
return false;
}
else
{
return RuntimeImports.AreTypesEquivalent(this.ToEETypePtr(), handle.ToEETypePtr());
}
return _value == handle._value;
}

public static RuntimeTypeHandle FromIntPtr(IntPtr value) => new RuntimeTypeHandle(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,13 +370,12 @@ private static unsafe bool TryGetNamedTypeForTypeReference_Inner(MetadataReader
/// Preconditions:
/// elementTypeHandle is a valid RuntimeTypeHandle.
/// </summary>
/// <param name="elementTypeHandle">MethodTable of the array element type</param>
/// <param name="arrayTypeHandle">Resolved MethodTable of the array type</param>
public static unsafe bool TryGetArrayTypeForNonDynamicElementType(RuntimeTypeHandle elementTypeHandle, out RuntimeTypeHandle arrayTypeHandle)
public static unsafe bool TryGetArrayTypeForNonDynamicElementType(RuntimeTypeHandle elementTypeHandle, bool isMdArray, int rank, out RuntimeTypeHandle arrayTypeHandle)
{
arrayTypeHandle = new RuntimeTypeHandle();

int arrayHashcode = TypeHashingAlgorithms.ComputeArrayTypeHashCode(elementTypeHandle.GetHashCode(), -1);
Debug.Assert(isMdArray || rank == -1);
int arrayHashcode = TypeHashingAlgorithms.ComputeArrayTypeHashCode(elementTypeHandle.GetHashCode(), rank);

// Note: ReflectionMapBlob.ArrayMap may not exist in the module that contains the element type.
// So we must enumerate all loaded modules in order to find ArrayMap and the array type for
Expand All @@ -398,7 +397,8 @@ public static unsafe bool TryGetArrayTypeForNonDynamicElementType(RuntimeTypeHan
{
RuntimeTypeHandle foundArrayType = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned());
RuntimeTypeHandle foundArrayElementType = RuntimeAugments.GetRelatedParameterTypeHandle(foundArrayType);
if (foundArrayElementType.Equals(elementTypeHandle))
if (foundArrayElementType.Equals(elementTypeHandle)
&& rank == RuntimeAugments.GetArrayRankOrMinusOneForSzArray(foundArrayType))
{
arrayTypeHandle = foundArrayType;
return true;
Expand All @@ -410,6 +410,50 @@ public static unsafe bool TryGetArrayTypeForNonDynamicElementType(RuntimeTypeHan
return false;
}

public static unsafe bool TryGetByRefTypeForNonDynamicElementType(RuntimeTypeHandle elementTypeHandle, out RuntimeTypeHandle pointerTypeHandle)
{
int byRefHashcode = TypeHashingAlgorithms.ComputeByrefTypeHashCode(elementTypeHandle.GetHashCode());
return TryGetParameterizedTypeForNonDynamicElementType(elementTypeHandle, byRefHashcode, ReflectionMapBlob.ByRefTypeMap, out pointerTypeHandle);
}

public static unsafe bool TryGetPointerTypeForNonDynamicElementType(RuntimeTypeHandle elementTypeHandle, out RuntimeTypeHandle pointerTypeHandle)
{
int pointerHashcode = TypeHashingAlgorithms.ComputePointerTypeHashCode(elementTypeHandle.GetHashCode());
return TryGetParameterizedTypeForNonDynamicElementType(elementTypeHandle, pointerHashcode, ReflectionMapBlob.PointerTypeMap, out pointerTypeHandle);
}

private static unsafe bool TryGetParameterizedTypeForNonDynamicElementType(RuntimeTypeHandle elementTypeHandle, int hashCode, ReflectionMapBlob blob, out RuntimeTypeHandle parameterizedTypeHandle)
{
foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules())
{
NativeReader mapReader;
if (TryGetNativeReaderForBlob(module, blob, out mapReader))
{
NativeParser mapParser = new NativeParser(mapReader, 0);
NativeHashtable hashtable = new NativeHashtable(mapParser);

ExternalReferencesTable externalReferences = default(ExternalReferencesTable);
externalReferences.InitializeCommonFixupsTable(module);

var lookup = hashtable.Lookup(hashCode);
NativeParser entryParser;
while (!(entryParser = lookup.GetNext()).IsNull)
{
RuntimeTypeHandle foundParameterizedType = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned());
RuntimeTypeHandle foundElementType = RuntimeAugments.GetRelatedParameterTypeHandle(foundParameterizedType);
if (foundElementType.Equals(elementTypeHandle))
{
parameterizedTypeHandle = foundParameterizedType;
return true;
}
}
}
}

parameterizedTypeHandle = default;
return false;
}

public bool TryGetStaticFunctionPointerTypeForComponents(RuntimeTypeHandle returnTypeHandle, RuntimeTypeHandle[] parameterHandles, bool isUnmanaged, out RuntimeTypeHandle runtimeTypeHandle)
{
int hashCode = TypeHashingAlgorithms.ComputeMethodSignatureHashCode(returnTypeHandle.GetHashCode(), parameterHandles);
Expand Down
Loading

0 comments on commit 79647f4

Please sign in to comment.