From d4175a847a11c724e25407cc17929d35836e455e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 12 May 2023 04:07:15 +0900 Subject: [PATCH] Make casting helpers look more like in CoreCLR (#86029) * Make casting helpers look more like in CoreCLR Speeds up casting to interfaces by about 50%. Speeds up casting to classes by about 20%. There are small differences compared to CoreCLR: * NativeAOT runtime considers interfaces implementable by arrays to be variant. We don't need to special case casting arrays to non-variant (in IL sense) interfaces because those naturally take the variant paths. * The base type field on arrays holds the array element type so we need to check for the array case before starting to chase the base hierarchy. Otherwise the top level helpers now look mostly the same as the CoreCLR ones. * Make it tailcallable Co-authored-by: Jan Kotas --- .../Common/src/Internal/Runtime/MethodTable.cs | 12 +- .../src/System/Runtime/MethodTable.Runtime.cs | 26 +- .../src/System/Runtime/RuntimeExports.cs | 8 +- .../Runtime.Base/src/System/Runtime/TypeCast.cs | 418 +++++++++++++++------ .../Reflection/Core/Execution/MethodInvoker.cs | 12 +- .../Internal/Runtime/Augments/RuntimeAugments.cs | 5 - .../src/System/Runtime/RuntimeImports.cs | 15 - .../Internal/Runtime/MethodTable.Constants.cs | 5 - .../Common/Internal/Runtime/ReadyToRunConstants.cs | 1 + .../aot/ILCompiler.Compiler/Compiler/JitHelper.cs | 18 +- .../JitInterface/CorInfoImpl.RyuJit.cs | 13 +- 11 files changed, 327 insertions(+), 206 deletions(-) diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs index 13b0100..4f0d64a 100644 --- a/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs @@ -294,6 +294,7 @@ namespace Internal.Runtime internal ushort ExtendedFlags { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return HasComponentSize ? (ushort)0 : (ushort)_uFlags; @@ -845,6 +846,7 @@ namespace Internal.Runtime internal MethodTable** InterfaceMap { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { // interface info table starts after the vtable and has _usNumInterfaces entries @@ -943,16 +945,6 @@ namespace Internal.Runtime } } - internal MethodTable* RawBaseType - { - get - { - Debug.Assert(!IsParameterizedType, "array type not supported in NonArrayBaseType"); - Debug.Assert(IsCanonical, "we expect canonical types here"); - return _relatedType._pBaseType; - } - } - internal MethodTable* NullableType { get diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/MethodTable.Runtime.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/MethodTable.Runtime.cs index d46c837..676c508 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/MethodTable.Runtime.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/MethodTable.Runtime.cs @@ -45,22 +45,6 @@ namespace Internal.Runtime return (IntPtr)InternalCalls.RhpGetClasslibFunctionFromEEType((MethodTable*)Unsafe.AsPointer(ref this), id); } - /// - /// Return true if type is good for simple casting : canonical, no generic variance - /// - internal bool SimpleCasting() - { - return (_uFlags & (uint)EETypeFlags.ComplexCastingMask) == (uint)EETypeKind.CanonicalEEType; - } - - /// - /// Return true if both types are good for simple casting: canonical, no related type via IAT, no generic variance - /// - internal static bool BothSimpleCasting(MethodTable* pThis, MethodTable* pOther) - { - return ((pThis->_uFlags | pOther->_uFlags) & (uint)EETypeFlags.ComplexCastingMask) == 0; - } - internal static bool AreSameType(MethodTable* mt1, MethodTable* mt2) { if (mt1 == mt2) @@ -103,11 +87,13 @@ namespace Internal.Runtime return (pEEType->NonArrayBaseType == null) && !pEEType->IsInterface; } - // Returns true if the passed in MethodTable is the MethodTable for System.Array. - // The binder sets a special CorElementType for this well known type - internal static unsafe bool IsSystemArray(MethodTable* pEEType) + // Returns true if the passed in MethodTable is the MethodTable for System.Array or System.Object. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe bool IsValidArrayBaseType(MethodTable* pEEType) { - return (pEEType->ElementType == EETypeElementType.SystemArray); + EETypeElementType elementType = pEEType->ElementType; + return elementType == EETypeElementType.SystemArray + || (elementType == EETypeElementType.Class && pEEType->NonArrayBaseType == null); } } } diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs index a457770..9a2aea0 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs @@ -394,16 +394,20 @@ namespace System.Runtime case RuntimeHelperKind.IsInst: if (pEEType->IsArray) return (IntPtr)(delegate*)&TypeCast.IsInstanceOfArray; + else if (pEEType->HasGenericVariance) + return (IntPtr)(delegate*)&TypeCast.IsInstanceOf; else if (pEEType->IsInterface) - return (IntPtr)(delegate*)&TypeCast.IsInstanceOfInterface; + return (IntPtr)(delegate*)&TypeCast.IsInstanceOfInterface; else if (pEEType->IsParameterizedType || pEEType->IsFunctionPointerType) return (IntPtr)(delegate*)&TypeCast.IsInstanceOf; // Array handled above; pointers and byrefs handled here else - return (IntPtr)(delegate*)&TypeCast.IsInstanceOfClass; + return (IntPtr)(delegate*)&TypeCast.IsInstanceOfClass; case RuntimeHelperKind.CastClass: if (pEEType->IsArray) return (IntPtr)(delegate*)&TypeCast.CheckCastArray; + else if (pEEType->HasGenericVariance) + return (IntPtr)(delegate*)&TypeCast.CheckCast; else if (pEEType->IsInterface) return (IntPtr)(delegate*)&TypeCast.CheckCastInterface; else if (pEEType->IsParameterizedType || pEEType->IsFunctionPointerType) diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs index aad8078..3cd06a1 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs @@ -42,119 +42,147 @@ namespace System.Runtime } [RuntimeExport("RhTypeCast_IsInstanceOfClass")] - public static unsafe object IsInstanceOfClass(MethodTable* pTargetType, object obj) + public static unsafe object? IsInstanceOfClass(MethodTable* pTargetType, object? obj) { + Debug.Assert(!pTargetType->IsParameterizedType, "IsInstanceOfClass called with parameterized MethodTable"); + Debug.Assert(!pTargetType->IsFunctionPointerType, "IsInstanceOfClass called with function pointer MethodTable"); + Debug.Assert(!pTargetType->IsInterface, "IsInstanceOfClass called with interface MethodTable"); + Debug.Assert(!pTargetType->HasGenericVariance, "IsInstanceOfClass with variant MethodTable"); + if (obj == null || obj.GetMethodTable() == pTargetType) - { return obj; - } - MethodTable* pObjType = obj.GetMethodTable(); + if (!obj.GetMethodTable()->IsCanonical) + { + // Arrays should be the only non-canonical types that can exist on GC heap + Debug.Assert(obj.GetMethodTable()->IsArray); - Debug.Assert(!pTargetType->IsParameterizedType, "IsInstanceOfClass called with parameterized MethodTable"); - Debug.Assert(!pTargetType->IsFunctionPointerType, "IsInstanceOfClass called with function pointer MethodTable"); - Debug.Assert(!pTargetType->IsInterface, "IsInstanceOfClass called with interface MethodTable"); + // arrays can be cast to System.Object or System.Array + if (WellKnownEETypes.IsValidArrayBaseType(pTargetType)) + goto done; - // Quick check if both types are good for simple casting: canonical, no generic variance - if (Internal.Runtime.MethodTable.BothSimpleCasting(pObjType, pTargetType)) + // They don't cast to any other class + goto fail; + } + + MethodTable* mt = obj.GetMethodTable()->NonArrayBaseType; + for (; ; ) { - // walk the type hierarchy looking for a match - do - { - pObjType = pObjType->RawBaseType; + if (mt == pTargetType) + goto done; - if (pObjType == null) - { - return null; - } + if (mt == null) + break; - if (pObjType == pTargetType) - { - return obj; - } - } - while (pObjType->SimpleCasting()); + mt = mt->NonArrayBaseType; + if (mt == pTargetType) + goto done; + + if (mt == null) + break; + + mt = mt->NonArrayBaseType; + if (mt == pTargetType) + goto done; + + if (mt == null) + break; + + mt = mt->NonArrayBaseType; + if (mt == pTargetType) + goto done; + + if (mt == null) + break; + + mt = mt->NonArrayBaseType; } - return IsInstanceOfClass_Helper(pTargetType, obj, pObjType); + fail: + obj = null; + + done: + return obj; } - private static unsafe object IsInstanceOfClass_Helper(MethodTable* pTargetType, object obj, MethodTable* pObjType) + [RuntimeExport("RhTypeCast_CheckCastClass")] + public static unsafe object CheckCastClass(MethodTable* pTargetType, object obj) { - // if the EETypes pointers match, we're done - if (pObjType == pTargetType) + Debug.Assert(!pTargetType->IsParameterizedType, "CheckCastClass called with parameterized MethodTable"); + Debug.Assert(!pTargetType->IsFunctionPointerType, "CheckCastClass called with function pointer MethodTable"); + Debug.Assert(!pTargetType->IsInterface, "CheckCastClass called with interface MethodTable"); + Debug.Assert(!pTargetType->HasGenericVariance, "CheckCastClass with variant MethodTable"); + + if (obj == null || obj.GetMethodTable() == pTargetType) { return obj; } - if (pTargetType->HasGenericVariance && pObjType->HasGenericVariance) - { - // Only generic interfaces and delegates can have generic variance and we shouldn't see - // interfaces for either input here. So if the canonical types are marked as having variance - // we know we've hit the delegate case. We've dealt with the identical case just above. And - // the regular path below will handle casting to Object, Delegate and MulticastDelegate. Since - // we don't support deriving from user delegate classes any further all we have to check here - // is that the uninstantiated generic delegate definitions are the same and the type - // parameters are compatible. + return CheckCastClassSpecial(pTargetType, obj); + } - // NOTE: using general assignable path for the cache because of the cost of the variance checks - if (AreTypesAssignableInternal(pObjType, pTargetType, AssignmentVariation.BoxedSource, null)) - return obj; - return null; - } + [RuntimeExport("RhTypeCast_CheckCastClassSpecial")] + private static unsafe object CheckCastClassSpecial(MethodTable* pTargetType, object obj) + { + Debug.Assert(!pTargetType->IsParameterizedType, "CheckCastClass called with parameterized MethodTable"); + Debug.Assert(!pTargetType->IsFunctionPointerType, "CheckCastClass called with function pointer MethodTable"); + Debug.Assert(!pTargetType->IsInterface, "CheckCastClass called with interface MethodTable"); + Debug.Assert(!pTargetType->HasGenericVariance, "CheckCastClass with variant MethodTable"); - if (pObjType->IsArray) + MethodTable* mt = obj.GetMethodTable(); + Debug.Assert(mt != pTargetType, "The check for the trivial cases should be inlined by the JIT"); + + if (!mt->IsCanonical) { - // arrays can be cast to System.Object - if (WellKnownEETypes.IsSystemObject(pTargetType)) - { - return obj; - } + // Arrays should be the only non-canonical types that can exist on GC heap + Debug.Assert(mt->IsArray); - // arrays can be cast to System.Array - if (WellKnownEETypes.IsSystemArray(pTargetType)) - { - return obj; - } + // arrays can be cast to System.Object or System.Array + if (WellKnownEETypes.IsValidArrayBaseType(pTargetType)) + goto done; - return null; + // They don't cast to any other class + goto fail; } - - // walk the type hierarchy looking for a match - while (true) + for (; ; ) { - pObjType = pObjType->NonArrayBaseType; - if (pObjType == null) - { - return null; - } + mt = mt->NonArrayBaseType; + if (mt == pTargetType) + goto done; - if (pObjType == pTargetType) - { - return obj; - } - } - } + if (mt == null) + break; - [RuntimeExport("RhTypeCast_CheckCastClass")] - public static unsafe object CheckCastClass(MethodTable* pTargetEEType, object obj) - { - // a null value can be cast to anything - if (obj == null) - return null; + mt = mt->NonArrayBaseType; + if (mt == pTargetType) + goto done; - object result = IsInstanceOfClass(pTargetEEType, obj); + if (mt == null) + break; - if (result == null) - { - // Throw the invalid cast exception defined by the classlib, using the input MethodTable* - // to find the correct classlib. + mt = mt->NonArrayBaseType; + if (mt == pTargetType) + goto done; + + if (mt == null) + break; - throw pTargetEEType->GetClasslibException(ExceptionIDs.InvalidCast); + mt = mt->NonArrayBaseType; + if (mt == pTargetType) + goto done; + + if (mt == null) + break; } - return result; + goto fail; + + done: + return obj; + + fail: + return ThrowInvalidCastException(pTargetType); } [RuntimeExport("RhTypeCast_IsInstanceOfArray")] @@ -215,31 +243,95 @@ namespace System.Runtime // Throw the invalid cast exception defined by the classlib, using the input MethodTable* // to find the correct classlib. - throw pTargetEEType->GetClasslibException(ExceptionIDs.InvalidCast); + return ThrowInvalidCastException(pTargetEEType); } return result; } [RuntimeExport("RhTypeCast_IsInstanceOfInterface")] - public static unsafe object IsInstanceOfInterface(MethodTable* pTargetType, object obj) + public static unsafe object? IsInstanceOfInterface(MethodTable* pTargetType, object? obj) { - if (obj == null) + Debug.Assert(pTargetType->IsInterface); + Debug.Assert(!pTargetType->HasGenericVariance); + + const int unrollSize = 4; + + if (obj != null) { - return null; + MethodTable* mt = obj.GetMethodTable(); + nint interfaceCount = mt->NumInterfaces; + if (interfaceCount != 0) + { + MethodTable** interfaceMap = mt->InterfaceMap; + if (interfaceCount < unrollSize) + { + // If not enough for unrolled, jmp straight to small loop + // as we already know there is one or more interfaces so don't need to check again. + goto few; + } + + do + { + if (interfaceMap[0] == pTargetType || + interfaceMap[1] == pTargetType || + interfaceMap[2] == pTargetType || + interfaceMap[3] == pTargetType) + { + goto done; + } + + interfaceMap += unrollSize; + interfaceCount -= unrollSize; + } while (interfaceCount >= unrollSize); + + if (interfaceCount == 0) + { + // If none remaining, skip the short loop + goto extra; + } + + few: + do + { + if (interfaceMap[0] == pTargetType) + { + goto done; + } + + // Assign next offset + interfaceMap++; + interfaceCount--; + } while (interfaceCount > 0); + + extra: + if (mt->IsIDynamicInterfaceCastable) + { + goto slowPath; + } + } + + obj = null; } - MethodTable* pObjType = obj.GetMethodTable(); + done: - if (AreTypesAssignableInternal_SourceNotTarget_BoxedSource(pObjType, pTargetType, null)) - return obj; + return obj; + slowPath: + + return IsInstanceOfInterface_Helper(pTargetType, obj); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static unsafe object? IsInstanceOfInterface_Helper(MethodTable* pTargetType, object? obj) + { // If object type implements IDynamicInterfaceCastable then there's one more way to check whether it implements // the interface. - if (pObjType->IsIDynamicInterfaceCastable && IsInstanceOfInterfaceViaIDynamicInterfaceCastable(pTargetType, obj, throwing: false)) - return obj; + if (!IsInstanceOfInterfaceViaIDynamicInterfaceCastable(pTargetType, obj, throwing: false)) + obj = null; - return null; + return obj; } private static unsafe bool IsInstanceOfInterfaceViaIDynamicInterfaceCastable(MethodTable* pTargetType, object obj, bool throwing) @@ -585,7 +677,7 @@ namespace System.Runtime if (pSourceType->IsArray) { // Target type is not an array. But we can still cast arrays to Object or System.Array. - return WellKnownEETypes.IsSystemObject(pTargetType) || WellKnownEETypes.IsSystemArray(pTargetType); + return WellKnownEETypes.IsValidArrayBaseType(pTargetType); } else if (pSourceType->IsParameterizedType) { @@ -648,20 +740,79 @@ namespace System.Runtime [RuntimeExport("RhTypeCast_CheckCastInterface")] public static unsafe object CheckCastInterface(MethodTable* pTargetType, object obj) { - // a null value can be cast to anything - if (obj == null) + Debug.Assert(pTargetType->IsInterface); + Debug.Assert(!pTargetType->HasGenericVariance); + + const int unrollSize = 4; + + if (obj != null) { - return null; + MethodTable* mt = obj.GetMethodTable(); + nint interfaceCount = mt->NumInterfaces; + if (interfaceCount == 0) + { + goto slowPath; + } + + MethodTable** interfaceMap = mt->InterfaceMap; + if (interfaceCount < unrollSize) + { + // If not enough for unrolled, jmp straight to small loop + // as we already know there is one or more interfaces so don't need to check again. + goto few; + } + + do + { + if (interfaceMap[0] == pTargetType || + interfaceMap[1] == pTargetType || + interfaceMap[2] == pTargetType || + interfaceMap[3] == pTargetType) + { + goto done; + } + + // Assign next offset + interfaceMap += unrollSize; + interfaceCount -= unrollSize; + } while (interfaceCount >= unrollSize); + + if (interfaceCount == 0) + { + // If none remaining, skip the short loop + goto slowPath; + } + + few: + do + { + if (interfaceMap[0] == pTargetType) + { + goto done; + } + + // Assign next offset + interfaceMap++; + interfaceCount--; + } while (interfaceCount > 0); + + goto slowPath; } - MethodTable* pObjType = obj.GetMethodTable(); + done: + return obj; - if (AreTypesAssignableInternal_SourceNotTarget_BoxedSource(pObjType, pTargetType, null)) - return obj; + slowPath: + return CheckCastInterface_Helper(pTargetType, obj); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static unsafe object CheckCastInterface_Helper(MethodTable* pTargetType, object obj) + { // If object type implements IDynamicInterfaceCastable then there's one more way to check whether it implements // the interface. - if (pObjType->IsIDynamicInterfaceCastable + if (obj.GetMethodTable()->IsIDynamicInterfaceCastable && IsInstanceOfInterfaceViaIDynamicInterfaceCastable(pTargetType, obj, throwing: true)) { return obj; @@ -669,7 +820,7 @@ namespace System.Runtime // Throw the invalid cast exception defined by the classlib, using the input MethodTable* to find the // correct classlib. - throw pTargetType->GetClasslibException(ExceptionIDs.InvalidCast); + return ThrowInvalidCastException(pTargetType); } [RuntimeExport("RhTypeCast_CheckArrayStore")] @@ -851,6 +1002,8 @@ namespace System.Runtime // @TODO: consider using the cache directly, but beware of IDynamicInterfaceCastable in the interface case if (pTargetType->IsArray) return IsInstanceOfArray(pTargetType, obj); + else if (pTargetType->HasGenericVariance) + return IsInstanceOfVariantType(pTargetType, obj); else if (pTargetType->IsInterface) return IsInstanceOfInterface(pTargetType, obj); else if (pTargetType->IsParameterizedType || pTargetType->IsFunctionPointerType) @@ -859,6 +1012,23 @@ namespace System.Runtime return IsInstanceOfClass(pTargetType, obj); } + private static unsafe object IsInstanceOfVariantType(MethodTable* pTargetType, object obj) + { + if (obj == null) + { + return obj; + } + + if (!AreTypesAssignableInternal(obj.GetMethodTable(), pTargetType, AssignmentVariation.BoxedSource, null) + && (!obj.GetMethodTable()->IsIDynamicInterfaceCastable + || !IsInstanceOfInterfaceViaIDynamicInterfaceCastable(pTargetType, obj, throwing: false))) + { + return null; + } + + return obj; + } + [RuntimeExport("RhTypeCast_IsInstanceOfException")] public static unsafe bool IsInstanceOfException(MethodTable* pTargetType, object? obj) { @@ -874,7 +1044,7 @@ namespace System.Runtime // arrays can be cast to System.Object and System.Array if (pObjType->IsArray) - return WellKnownEETypes.IsSystemObject(pTargetType) || WellKnownEETypes.IsSystemArray(pTargetType); + return WellKnownEETypes.IsValidArrayBaseType(pTargetType); while (true) { @@ -893,6 +1063,8 @@ namespace System.Runtime // @TODO: consider using the cache directly, but beware of IDynamicInterfaceCastable in the interface case if (pTargetType->IsArray) return CheckCastArray(pTargetType, obj); + else if (pTargetType->HasGenericVariance) + return CheckCastVariantType(pTargetType, obj); else if (pTargetType->IsInterface) return CheckCastInterface(pTargetType, obj); else if (pTargetType->IsParameterizedType || pTargetType->IsFunctionPointerType) @@ -901,6 +1073,23 @@ namespace System.Runtime return CheckCastClass(pTargetType, obj); } + private static unsafe object CheckCastVariantType(MethodTable* pTargetType, object obj) + { + if (obj == null) + { + return obj; + } + + if (!AreTypesAssignableInternal(obj.GetMethodTable(), pTargetType, AssignmentVariation.BoxedSource, null) + && (!obj.GetMethodTable()->IsIDynamicInterfaceCastable + || !IsInstanceOfInterfaceViaIDynamicInterfaceCastable(pTargetType, obj, throwing: true))) + { + return ThrowInvalidCastException(pTargetType); + } + + return obj; + } + private static unsafe object CheckCastNonboxableType(MethodTable* pTargetType, object obj) { // a null value can be cast to anything @@ -910,7 +1099,7 @@ namespace System.Runtime } // Parameterized types are not boxable, so nothing can be an instance of these. - throw pTargetType->GetClasslibException(ExceptionIDs.InvalidCast); + return ThrowInvalidCastException(pTargetType); } private static unsafe EETypeElementType GetNormalizedIntegralArrayElementType(MethodTable* type) @@ -929,6 +1118,13 @@ namespace System.Runtime return elementType; } + // Would not be inlined, but still need to mark NoInlining so that it doesn't throw off tail calls + [MethodImpl(MethodImplOptions.NoInlining)] + private static unsafe object ThrowInvalidCastException(MethodTable* pMT) + { + throw pMT->GetClasslibException(ExceptionIDs.InvalidCast); + } + internal unsafe struct EETypePairList { private MethodTable* _eetype1; @@ -972,26 +1168,6 @@ namespace System.Runtime return CacheMiss(pSourceType, pTargetType, variation, pVisited); } - // This method is an optimized and customized version of AreTypesAssignable that achieves better performance - // than AreTypesAssignableInternal through 2 significant changes - // 1. Removal of sourceType to targetType check (This property must be known before calling this function. At time - // of writing, this is true as its is only used if sourceType is from an object, and targetType is an interface.) - // 2. Force inlining (This particular variant is only used in a small number of dispatch scenarios that are particularly - // high in performance impact.) - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe bool AreTypesAssignableInternal_SourceNotTarget_BoxedSource(MethodTable* pSourceType, MethodTable* pTargetType, EETypePairList* pVisited) - { - Debug.Assert(pSourceType != pTargetType, "target is source"); - nuint sourceAndVariation = (nuint)pSourceType + (int)AssignmentVariation.BoxedSource; - CastResult result = CastCache.TryGet(sourceAndVariation, (nuint)(pTargetType)); - if (result != CastResult.MaybeCast) - { - return result == CastResult.CanCast; - } - - return CacheMiss(pSourceType, pTargetType, AssignmentVariation.BoxedSource, pVisited); - } - [MethodImpl(MethodImplOptions.NoInlining)] private static unsafe bool CacheMiss(MethodTable* pSourceType, MethodTable* pTargetType, AssignmentVariation variation, EETypePairList* pVisited) { diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/MethodInvoker.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/MethodInvoker.cs index 6063726..8363b2b 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/MethodInvoker.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/MethodInvoker.cs @@ -40,16 +40,8 @@ namespace Internal.Reflection.Core.Execution if (thisObject == null) throw new TargetException(SR.RFLCT_Targ_StatMethReqTarg); - if (RuntimeAugments.IsAssignable(thisObject, declaringTypeHandle)) - return; - - if (RuntimeAugments.IsInterface(declaringTypeHandle)) - { - if (RuntimeAugments.IsInstanceOfInterface(thisObject, declaringTypeHandle)) - return; - } - - throw new TargetException(SR.RFLCT_Targ_ITargMismatch); + if (!RuntimeAugments.IsAssignable(thisObject, declaringTypeHandle)) + throw new TargetException(SR.RFLCT_Targ_ITargMismatch); } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs index e07b3e4..31cfac7 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs @@ -455,11 +455,6 @@ namespace Internal.Runtime.Augments return RuntimeImports.AreTypesAssignable(srcEEType, dstEEType); } - public static bool IsInstanceOfInterface(object obj, RuntimeTypeHandle interfaceTypeHandle) - { - return (null != RuntimeImports.IsInstanceOfInterface(interfaceTypeHandle.ToEETypePtr(), obj)); - } - // // Return a type's base type using the runtime type system. If the underlying runtime type system does not support // this operation, return false and TypeInfo.BaseType will fall back to metadata. diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index ebc9fdb..eb77477 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -321,21 +321,6 @@ namespace System.Runtime internal static unsafe object IsInstanceOf(EETypePtr pTargetType, object obj) => IsInstanceOf(pTargetType.ToPointer(), obj); - [MethodImpl(MethodImplOptions.InternalCall)] - [RuntimeImport(RuntimeLibrary, "RhTypeCast_IsInstanceOfClass")] - private static extern unsafe object IsInstanceOfClass(MethodTable* pTargetType, object obj); - - [MethodImpl(MethodImplOptions.InternalCall)] - [RuntimeImport(RuntimeLibrary, "RhTypeCast_IsInstanceOfInterface")] - internal static extern unsafe object IsInstanceOfInterface(MethodTable* pTargetType, object obj); - - [MethodImpl(MethodImplOptions.InternalCall)] - [RuntimeImport(RuntimeLibrary, "RhTypeCast_IsInstanceOfException")] - internal static extern unsafe bool IsInstanceOfException(MethodTable* pTargetType, object obj); - - internal static unsafe object IsInstanceOfInterface(EETypePtr pTargetType, object obj) - => IsInstanceOfInterface(pTargetType.ToPointer(), obj); - // // calls to runtime for allocation // These calls are needed in types which cannot use "new" to allocate and need to do it manually diff --git a/src/coreclr/tools/Common/Internal/Runtime/MethodTable.Constants.cs b/src/coreclr/tools/Common/Internal/Runtime/MethodTable.Constants.cs index 866c142..b528195 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/MethodTable.Constants.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/MethodTable.Constants.cs @@ -64,11 +64,6 @@ namespace Internal.Runtime ElementTypeShift = 26, /// - /// Single mark to check TypeKind and two flags. When non-zero, casting is more complicated. - /// - ComplexCastingMask = EETypeKindMask | GenericVarianceFlag, - - /// /// The _usComponentSize is a number (not holding FlagsEx). /// HasComponentSizeFlag = 0x80000000, diff --git a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs index f46904c..731fc14 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs @@ -343,6 +343,7 @@ namespace Internal.ReadyToRunConstants GetRuntimeType, CheckCastClass, + CheckCastClassSpecial, CheckInstanceClass, CheckCastArray, CheckInstanceArray, diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs index b126d06..bfff591 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs @@ -259,6 +259,9 @@ namespace ILCompiler case ReadyToRunHelper.CheckCastClass: mangledName = "RhTypeCast_CheckCastClass"; break; + case ReadyToRunHelper.CheckCastClassSpecial: + mangledName = "RhTypeCast_CheckCastClassSpecial"; + break; case ReadyToRunHelper.CheckInstanceClass: mangledName = "RhTypeCast_IsInstanceOfClass"; break; @@ -334,20 +337,5 @@ namespace ILCompiler return "RhpNewArray"; } - - public static string GetCastingHelperNameForType(TypeDesc type, bool throwing) - { - if (type.IsArray) - return throwing ? "RhTypeCast_CheckCastArray" : "RhTypeCast_IsInstanceOfArray"; - - if (type.IsInterface) - return throwing ? "RhTypeCast_CheckCastInterface" : "RhTypeCast_IsInstanceOfInterface"; - - if (type.IsDefType) - return throwing ? "RhTypeCast_CheckCastClass" : "RhTypeCast_IsInstanceOfClass"; - - // No specialized helper for the rest of the types because they don't make much sense anyway. - return throwing ? "RhTypeCast_CheckCast" : "RhTypeCast_IsInstanceOf"; - } } } diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index b222bd7..7832520 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -700,10 +700,11 @@ namespace Internal.JitInterface id = ReadyToRunHelper.CheckInstanceAny; break; case CorInfoHelpFunc.CORINFO_HELP_CHKCASTCLASS: - case CorInfoHelpFunc.CORINFO_HELP_CHKCASTCLASS_SPECIAL: - // TODO: separate helper for the _SPECIAL case id = ReadyToRunHelper.CheckCastClass; break; + case CorInfoHelpFunc.CORINFO_HELP_CHKCASTCLASS_SPECIAL: + id = ReadyToRunHelper.CheckCastClassSpecial; + break; case CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOFCLASS: id = ReadyToRunHelper.CheckInstanceClass; break; @@ -1047,9 +1048,15 @@ namespace Internal.JitInterface // This optimizations does not seem to be warranted at the moment. helper = CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOFANY; } + else if (type.HasVariance + // The runtime considers generic interfaces that can be implemented by arrays variant + || _compilation.TypeSystemContext.IsGenericArrayInterfaceType(type)) + { + helper = CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOFANY; + } else if (type.IsInterface) { - // If it is an interface, use the fast interface helper + // If it is a non-variant interface, use the fast interface helper helper = CorInfoHelpFunc.CORINFO_HELP_ISINSTANCEOFINTERFACE; } else if (type.IsArray) -- 2.7.4