From 524fdb862e02bcd5732fd622fac60df87ab5720a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 2 May 2023 16:04:34 +0900 Subject: [PATCH 1/2] Shuffle some `MethodTable` flags * `IDynamicInterfaceCastableFlag` cannot be set on types that have a component size, so move it from the valuable location in `Flags` to `FlagsEx`. * Move `HasSealedVTableEntriesFlag` from rare flags to flags. This flag is not so rare. All async state machine types set it, for example. * Delete `IsAbstractClassFlag`. This was introduce in .NET Native for `GetUninitializedObject` since accessing `Type.IsAbstract` could trigger a `MissingMetadataException` there. We got rid of that concept in NativeAOT because it cannot be reconciled with ILLink trimming. Rewrite the code to use `Type.IsAbstract`. Saves 15 kB on BasicMinimalApis, which is nice. Also makes things faster since we avoid reading optional fields and rare flags. --- .../src/Internal/Runtime/MethodTable.cs | 25 +++++++++--------- .../RuntimeHelpers.NativeAot.cs | 10 +++---- .../Runtime/TypeLoader/EETypeCreator.cs | 6 +++-- .../Internal/Runtime/EETypeBuilderHelpers.cs | 5 ++++ .../Internal/Runtime/MethodTable.Constants.cs | 19 +++++++------- .../Compiler/DependencyAnalysis/EETypeNode.cs | 26 ++++++------------- 6 files changed, 43 insertions(+), 48 deletions(-) diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs index 4764545fccc405..c0cac16f99fbf8 100644 --- a/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs @@ -639,14 +639,6 @@ internal bool IsInterface } } - internal bool IsAbstract - { - get - { - return IsInterface || (RareFlags & EETypeRareFlags.IsAbstractClassFlag) != 0; - } - } - internal bool IsByRefLike { get @@ -773,7 +765,7 @@ internal bool IsIDynamicInterfaceCastable { get { - return ((_uFlags & (uint)EETypeFlags.IDynamicInterfaceCastableFlag) != 0); + return ((ExtendedFlags & (ushort)EETypeFlagsEx.IDynamicInterfaceCastableFlag) != 0); } } @@ -794,6 +786,14 @@ internal bool IsPrimitive } } + internal bool HasSealedVTableEntries + { + get + { + return (_uFlags & (uint)EETypeFlags.HasSealedVTableEntriesFlag) != 0; + } + } + internal bool ContainsGCPointers { get @@ -1064,7 +1064,7 @@ private static IntPtr FollowRelativePointer(int* pDist) #endif void* GetSealedVirtualTable() { - Debug.Assert((RareFlags & EETypeRareFlags.HasSealedVTableEntriesFlag) != 0); + Debug.Assert(HasSealedVTableEntries); uint cbSealedVirtualSlotsTypeOffset = GetFieldOffset(EETypeField.ETF_SealedVirtualSlots); byte* pThis = (byte*)Unsafe.AsPointer(ref this); @@ -1387,10 +1387,8 @@ public uint GetFieldOffset(EETypeField eField) if (eField == EETypeField.ETF_SealedVirtualSlots) return cbOffset; - EETypeRareFlags rareFlags = RareFlags; - // in the case of sealed vtable entries on static types, we have a UInt sized relative pointer - if ((rareFlags & EETypeRareFlags.HasSealedVTableEntriesFlag) != 0) + if (HasSealedVTableEntries) cbOffset += relativeOrFullPointerOffset; if (eField == EETypeField.ETF_GenericDefinition) @@ -1431,6 +1429,7 @@ public uint GetFieldOffset(EETypeField eField) if (IsDynamicType) cbOffset += (uint)IntPtr.Size; + EETypeRareFlags rareFlags = RareFlags; if (eField == EETypeField.ETF_DynamicGcStatics) { Debug.Assert((rareFlags & EETypeRareFlags.IsDynamicTypeWithGcStatics) != 0); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs index 1048c76d583a06..1c7edec6d7f696 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs @@ -319,6 +319,11 @@ public static unsafe object GetUninitializedObject( throw new NotSupportedException(SR.NotSupported_ManagedActivation); } + if (type.IsAbstract) + { + throw new MemberAccessException(SR.Acc_CreateAbst); + } + MethodTable* mt = type.TypeHandle.ToMethodTable(); if (mt->ElementType == Internal.Runtime.EETypeElementType.Void) @@ -337,11 +342,6 @@ public static unsafe object GetUninitializedObject( throw new MemberAccessException(); } - if (mt->IsAbstract) - { - throw new MemberAccessException(SR.Acc_CreateAbst); - } - if (mt->IsByRefLike) { throw new NotSupportedException(SR.NotSupported_ByRefLike); diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs index 66e6835792cccc..2dd90578fc83dd 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs @@ -168,6 +168,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo bool isNullable; bool isArray; bool isGeneric; + bool hasSealedVTable; uint flags; ushort runtimeInterfacesLength = 0; IntPtr typeManager = IntPtr.Zero; @@ -184,6 +185,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo flags = pTemplateEEType->Flags; isArray = pTemplateEEType->IsArray; isGeneric = pTemplateEEType->IsGeneric; + hasSealedVTable = pTemplateEEType->HasSealedVTableEntries; typeManager = pTemplateEEType->PointerToTypeManager; Debug.Assert(pTemplateEEType->NumInterfaces == runtimeInterfacesLength); @@ -260,7 +262,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo runtimeInterfacesLength, hasFinalizer, cbOptionalFieldsSize > 0, - (rareFlags & (int)EETypeRareFlags.HasSealedVTableEntriesFlag) != 0, + hasSealedVTable, isGeneric, numFunctionPointerTypeParameters, allocatedNonGCDataSize != 0, @@ -315,7 +317,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo } // Copy the sealed vtable entries if they exist on the template type - if ((rareFlags & (int)EETypeRareFlags.HasSealedVTableEntriesFlag) != 0) + if (hasSealedVTable) { uint cbSealedVirtualSlotsTypeOffset = pEEType->GetFieldOffset(EETypeField.ETF_SealedVirtualSlots); *((void**)((byte*)pEEType + cbSealedVirtualSlotsTypeOffset)) = pTemplateEEType->GetSealedVirtualTable(); diff --git a/src/coreclr/tools/Common/Internal/Runtime/EETypeBuilderHelpers.cs b/src/coreclr/tools/Common/Internal/Runtime/EETypeBuilderHelpers.cs index 914b44d7df76c6..d61d2d41d427ef 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/EETypeBuilderHelpers.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/EETypeBuilderHelpers.cs @@ -123,6 +123,11 @@ mdType.Name is "WeakReference" or "WeakReference`1" && flagsEx |= (ushort)EETypeFlagsEx.IsTrackedReferenceWithFinalizerFlag; } + if (type.IsIDynamicInterfaceCastable) + { + flagsEx |= (ushort)EETypeFlagsEx.IDynamicInterfaceCastableFlag; + } + return flagsEx; } diff --git a/src/coreclr/tools/Common/Internal/Runtime/MethodTable.Constants.cs b/src/coreclr/tools/Common/Internal/Runtime/MethodTable.Constants.cs index 10edf5bf58ccf2..29a1044f2c8068 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/MethodTable.Constants.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/MethodTable.Constants.cs @@ -34,9 +34,9 @@ internal enum EETypeFlags : uint HasPointersFlag = 0x00200000, /// - /// This type implements IDynamicInterfaceCastable to allow dynamic resolution of interface casts. + /// This MethodTable has sealed vtable entries /// - IDynamicInterfaceCastableFlag = 0x00400000, + HasSealedVTableEntriesFlag = 0x00400000, /// /// This type is generic and one or more of its type parameters is co- or contra-variant. This @@ -81,6 +81,11 @@ internal enum EETypeFlagsEx : ushort HasEagerFinalizerFlag = 0x0001, HasCriticalFinalizerFlag = 0x0002, IsTrackedReferenceWithFinalizerFlag = 0x0004, + + /// + /// This type implements IDynamicInterfaceCastable to allow dynamic resolution of interface casts. + /// + IDynamicInterfaceCastableFlag = 0x0008, } internal enum EETypeKind : uint @@ -140,10 +145,7 @@ internal enum EETypeRareFlags : int /// IsHFAFlag = 0x00000100, - /// - /// This MethodTable has sealed vtable entries - /// - HasSealedVTableEntriesFlag = 0x00000200, + // Unused = 0x00000200, /// /// This dynamically created types has gc statics @@ -162,10 +164,7 @@ internal enum EETypeRareFlags : int // UNUSED = 0x00002000, - /// - /// This MethodTable is an abstract class (but not an interface). - /// - IsAbstractClassFlag = 0x00004000, + // UNUSED = 0x00004000, /// /// This MethodTable is for a Byref-like class (TypedReference, Span<T>,...) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs index 3689b9b2a9d082..7d0d3656ddc123 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs @@ -648,7 +648,7 @@ protected override ObjectData GetDehydratableData(NodeFactory factory, bool relo ComputeOptionalEETypeFields(factory, relocsOnly); OutputGCDesc(ref objData); - OutputFlags(factory, ref objData); + OutputFlags(factory, ref objData, relocsOnly); objData.EmitInt(BaseSize); OutputRelatedType(factory, ref objData); @@ -715,7 +715,7 @@ protected virtual void OutputGCDesc(ref ObjectDataBuilder builder) Debug.Assert(GCDescSize == 0); } - private void OutputFlags(NodeFactory factory, ref ObjectDataBuilder objData) + private void OutputFlags(NodeFactory factory, ref ObjectDataBuilder objData, bool relocsOnly) { uint flags = EETypeBuilderHelpers.ComputeFlags(_type); @@ -733,9 +733,11 @@ private void OutputFlags(NodeFactory factory, ref ObjectDataBuilder objData) flags |= (uint)EETypeFlags.GenericVarianceFlag; } - if (_type.IsIDynamicInterfaceCastable) + if (EmitVirtualSlotsAndInterfaces && !_type.IsArrayTypeWithoutGenericInterfaces()) { - flags |= (uint)EETypeFlags.IDynamicInterfaceCastableFlag; + SealedVTableNode sealedVTable = factory.SealedVTable(_type.ConvertToCanonForm(CanonicalFormKind.Specific)); + if (sealedVTable.BuildSealedVTableSlots(factory, relocsOnly) && sealedVTable.NumSealedVTableEntries > 0) + flags |= (uint)EETypeFlags.HasSealedVTableEntriesFlag; } if (HasOptionalFields) @@ -1189,12 +1191,12 @@ protected internal virtual void ComputeOptionalEETypeFields(NodeFactory factory, _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.DispatchMap, checked((uint)factory.InterfaceDispatchMapIndirection(canonType).IndexFromBeginningOfArray)); } - ComputeRareFlags(factory, relocsOnly); + ComputeRareFlags(factory); ComputeNullableValueOffset(); ComputeValueTypeFieldPadding(); } - private void ComputeRareFlags(NodeFactory factory, bool relocsOnly) + private void ComputeRareFlags(NodeFactory factory) { uint flags = 0; @@ -1219,23 +1221,11 @@ private void ComputeRareFlags(NodeFactory factory, bool relocsOnly) flags |= (uint)EETypeRareFlags.IsHFAFlag; } - if (metadataType != null && !_type.IsInterface && metadataType.IsAbstract) - { - flags |= (uint)EETypeRareFlags.IsAbstractClassFlag; - } - if (_type.IsByRefLike) { flags |= (uint)EETypeRareFlags.IsByRefLikeFlag; } - if (EmitVirtualSlotsAndInterfaces && !_type.IsArrayTypeWithoutGenericInterfaces()) - { - SealedVTableNode sealedVTable = factory.SealedVTable(_type.ConvertToCanonForm(CanonicalFormKind.Specific)); - if (sealedVTable.BuildSealedVTableSlots(factory, relocsOnly) && sealedVTable.NumSealedVTableEntries > 0) - flags |= (uint)EETypeRareFlags.HasSealedVTableEntriesFlag; - } - if (flags != 0) { _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.RareFlags, flags); From 694615665173bd6fff84d0dc2b59e111e1d66128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Wed, 3 May 2023 05:47:41 +0900 Subject: [PATCH 2/2] Update RuntimeHelpers.NativeAot.cs --- .../System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs index 1c7edec6d7f696..bcbc4008351d0a 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs @@ -304,7 +304,7 @@ public static unsafe object GetUninitializedObject( throw new SerializationException(SR.Format(SR.Serialization_InvalidType, type)); } - if (type.HasElementType || type.IsGenericParameter) + if (type.HasElementType || type.IsGenericParameter || type.IsFunctionPointer) { throw new ArgumentException(SR.Argument_InvalidValue); }