diff --git a/src/coreclr/vm/class.cpp b/src/coreclr/vm/class.cpp index 68349c9fe168c5..02ac7a99ecc63c 100644 --- a/src/coreclr/vm/class.cpp +++ b/src/coreclr/vm/class.cpp @@ -246,11 +246,6 @@ VOID EEClass::FixupFieldDescForEnC(MethodTable * pMT, EnCFieldDesc *pFD, mdField bmtEnumFields.dwNumInstanceFields = 1; } - // We shouldn't have to fill this in b/c we're not allowed to EnC value classes, or - // anything else with layout info associated with it. - // Provide 2, 1 placeholder and 1 for the actual field - see BuildMethodTableThrowing(). - LayoutRawFieldInfo layoutRawFieldInfos[2]; - // If not NULL, it means there are some by-value fields, and this contains an entry for each instance or static field, // which is NULL if not a by value field, and points to the EEClass of the field if a by value field. Instance fields // come first, statics come second. @@ -288,7 +283,6 @@ VOID EEClass::FixupFieldDescForEnC(MethodTable * pMT, EnCFieldDesc *pFD, mdField GCX_PREEMP(); unsigned totalDeclaredFieldSize = 0; builder.InitializeFieldDescs(pFD, - layoutRawFieldInfos, &bmtInternal, &genericsInfo, &bmtMetaData, diff --git a/src/coreclr/vm/class.h b/src/coreclr/vm/class.h index d95129ab096a00..3bf8417ef0cb0d 100644 --- a/src/coreclr/vm/class.h +++ b/src/coreclr/vm/class.h @@ -72,7 +72,6 @@ class EnCFieldDesc; class FieldDesc; class NativeFieldDescriptor; class EEClassNativeLayoutInfo; -struct LayoutRawFieldInfo; class MetaSig; class MethodDesc; class MethodDescChunk; @@ -126,7 +125,7 @@ class ExplicitFieldTrust }; //---------------------------------------------------------------------------------------------- -// This class is a helper for HandleExplicitLayout. To make it harder to introduce security holes +// This class is a helper for ValidateExplicitLayout. To make it harder to introduce security holes // into this function, we will manage all updates to the class's trust level through the ExplicitClassTrust // class. This abstraction enforces the rule that the overall class is only as trustworthy as // the least trustworthy field. @@ -175,7 +174,7 @@ class ExplicitClassTrust : private ExplicitFieldTrust }; //---------------------------------------------------------------------------------------------- -// This class is a helper for HandleExplicitLayout. To make it harder to introduce security holes +// This class is a helper for ValidateExplicitLayout. To make it harder to introduce security holes // into this function, this class will collect trust information about individual fields to be later // aggregated into the overall class level. // @@ -334,30 +333,13 @@ class SparseVTableMap //======================================================================= class EEClassLayoutInfo { - static VOID CollectLayoutFieldMetadataThrowing( - mdTypeDef cl, // cl of the NStruct being loaded - BYTE packingSize, // packing size (from @dll.struct) - BYTE nlType, // nltype (from @dll.struct) - BOOL fExplicitOffsets, // explicit offsets? - MethodTable *pParentMT, // the loaded superclass - ULONG cTotalFields, // total number of fields (instance and static) - HENUMInternal *phEnumField, // enumerator for fields - Module* pModule, // Module that defines the scope, loader and heap (for allocate FieldMarshalers) - const SigTypeContext *pTypeContext, // Type parameters for NStruct being loaded - EEClassLayoutInfo *pEEClassLayoutInfoOut, // caller-allocated structure to fill in. - LayoutRawFieldInfo *pInfoArrayOut, // caller-allocated array to fill in. Needs room for cTotalFields+1 elements - LoaderAllocator * pAllocator, - AllocMemTracker *pamTracker - ); - - friend class ClassLoader; - friend class EEClass; - friend class MethodTableBuilder; - UINT32 m_cbManagedSize; - public: - BYTE m_ManagedLargestAlignmentRequirementOfAllMembers; - + enum class LayoutType : BYTE + { + Auto = 0, // Make sure Auto is the default value as the default-constructed value represents the "auto layout" case + Sequential, + Explicit + }; private: enum { // TRUE if the GC layout of the class is bit-for-bit identical @@ -365,8 +347,8 @@ class EEClassLayoutInfo // (i.e. no internal reference fields, no ansi-unicode char conversions required, etc.) // Used to optimize marshaling. e_BLITTABLE = 0x01, - // Is this type also sequential in managed memory? - e_MANAGED_SEQUENTIAL = 0x02, + // unused = 0x02, + // When a sequential/explicit type has no fields, it is conceptually // zero-sized, but actually is 1 byte in length. This holds onto this // fact and allows us to revert the 1 byte of padding when another @@ -380,17 +362,16 @@ class EEClassLayoutInfo e_IS_OR_HAS_INT128_FIELD = 0x20, }; - BYTE m_bFlags; + LayoutType m_LayoutType; + + BYTE m_ManagedLargestAlignmentRequirementOfAllMembers; + + BYTE m_bFlags; // Packing size in bytes (1, 2, 4, 8 etc.) - BYTE m_cbPackingSize; + BYTE m_cbPackingSize; public: - UINT32 GetManagedSize() const - { - LIMITED_METHOD_CONTRACT; - return m_cbManagedSize; - } BOOL IsBlittable() const { @@ -398,10 +379,10 @@ class EEClassLayoutInfo return (m_bFlags & e_BLITTABLE) == e_BLITTABLE; } - BOOL IsManagedSequential() const + LayoutType GetLayoutType() const { LIMITED_METHOD_CONTRACT; - return (m_bFlags & e_MANAGED_SEQUENTIAL) == e_MANAGED_SEQUENTIAL; + return m_LayoutType; } // If true, this says that the type was originally zero-sized @@ -433,13 +414,18 @@ class EEClassLayoutInfo return (m_bFlags & e_IS_OR_HAS_INT128_FIELD) == e_IS_OR_HAS_INT128_FIELD; } + BYTE GetAlignmentRequirement() const + { + LIMITED_METHOD_CONTRACT; + return m_ManagedLargestAlignmentRequirementOfAllMembers; + } + BYTE GetPackingSize() const { LIMITED_METHOD_CONTRACT; return m_cbPackingSize; } - private: void SetIsBlittable(BOOL isBlittable) { LIMITED_METHOD_CONTRACT; @@ -447,40 +433,93 @@ class EEClassLayoutInfo : (m_bFlags & ~e_BLITTABLE); } - void SetIsManagedSequential(BOOL isManagedSequential) + void SetHasAutoLayoutField(BOOL hasAutoLayoutField) { LIMITED_METHOD_CONTRACT; - m_bFlags = isManagedSequential ? (m_bFlags | e_MANAGED_SEQUENTIAL) - : (m_bFlags & ~e_MANAGED_SEQUENTIAL); + m_bFlags = hasAutoLayoutField ? (m_bFlags | e_HAS_AUTO_LAYOUT_FIELD_IN_LAYOUT) + : (m_bFlags & ~e_HAS_AUTO_LAYOUT_FIELD_IN_LAYOUT); } - void SetIsZeroSized(BOOL isZeroSized) + void SetIsInt128OrHasInt128Fields(BOOL hasInt128Field) { LIMITED_METHOD_CONTRACT; - m_bFlags = isZeroSized ? (m_bFlags | e_ZERO_SIZED) - : (m_bFlags & ~e_ZERO_SIZED); + m_bFlags = hasInt128Field ? (m_bFlags | e_IS_OR_HAS_INT128_FIELD) + : (m_bFlags & ~e_IS_OR_HAS_INT128_FIELD); } void SetHasExplicitSize(BOOL hasExplicitSize) { LIMITED_METHOD_CONTRACT; m_bFlags = hasExplicitSize ? (m_bFlags | e_HAS_EXPLICIT_SIZE) - : (m_bFlags & ~e_HAS_EXPLICIT_SIZE); + : (m_bFlags & ~e_HAS_EXPLICIT_SIZE); } - void SetHasAutoLayoutField(BOOL hasAutoLayoutField) + void SetAlignmentRequirement(BYTE alignment) { LIMITED_METHOD_CONTRACT; - m_bFlags = hasAutoLayoutField ? (m_bFlags | e_HAS_AUTO_LAYOUT_FIELD_IN_LAYOUT) - : (m_bFlags & ~e_HAS_AUTO_LAYOUT_FIELD_IN_LAYOUT); + m_ManagedLargestAlignmentRequirementOfAllMembers = alignment; } - void SetIsInt128OrHasInt128Fields(BOOL hasInt128Field) + ULONG InitializeSequentialFieldLayout( + FieldDesc* pFields, + MethodTable** pByValueClassCache, + ULONG cFields, + BYTE packingSize, + ULONG classSizeInMetadata, + MethodTable* pParentMT + ); + + ULONG InitializeExplicitFieldLayout( + FieldDesc* pFields, + MethodTable** pByValueClassCache, + ULONG cFields, + BYTE packingSize, + ULONG classSizeInMetadata, + MethodTable* pParentMT, + Module* pModule, + mdTypeDef cl + ); + + private: + void SetIsZeroSized(BOOL isZeroSized) { LIMITED_METHOD_CONTRACT; - m_bFlags = hasInt128Field ? (m_bFlags | e_IS_OR_HAS_INT128_FIELD) - : (m_bFlags & ~e_IS_OR_HAS_INT128_FIELD); + m_bFlags = isZeroSized ? (m_bFlags | e_ZERO_SIZED) + : (m_bFlags & ~e_ZERO_SIZED); + } + + void SetPackingSize(BYTE cbPackingSize) + { + LIMITED_METHOD_CONTRACT; + m_cbPackingSize = cbPackingSize; } + + UINT32 SetInstanceBytesSize(UINT32 size) + { + LIMITED_METHOD_CONTRACT; + // Bump the managed size of the structure up to 1. + SetIsZeroSized(size == 0 ? TRUE : FALSE); + return size == 0 ? 1 : size; + } + + void SetLayoutType(LayoutType layoutType) + { + LIMITED_METHOD_CONTRACT; + m_LayoutType = layoutType; + } + public: + enum class NestedFieldFlags + { + support_use_as_flags = -1, + None = 0x0, + NonBlittable = 0x1, + GCPointer = 0x2, + Align8 = 0x4, + AutoLayout = 0x8, + Int128 = 0x10, + }; + + static NestedFieldFlags GetNestedFieldFlags(Module* pModule, FieldDesc *pFD, ULONG cFields, CorNativeLinkType nlType, MethodTable** pByValueClassCache); }; // @@ -1964,7 +2003,7 @@ inline BOOL EEClass::IsBlittable() inline BOOL EEClass::IsManagedSequential() { LIMITED_METHOD_CONTRACT; - return HasLayout() && GetLayoutInfo()->IsManagedSequential(); + return HasLayout() && GetLayoutInfo()->GetLayoutType() == EEClassLayoutInfo::LayoutType::Sequential; } inline BOOL EEClass::HasExplicitSize() diff --git a/src/coreclr/vm/classcompat.h b/src/coreclr/vm/classcompat.h index 8876334faa1674..a17d2a3719209e 100644 --- a/src/coreclr/vm/classcompat.h +++ b/src/coreclr/vm/classcompat.h @@ -42,7 +42,6 @@ class EEClass; class LayoutEEClass; class EnCFieldDesc; class FieldDesc; -struct LayoutRawFieldInfo; class MetaSig; class MethodDesc; class MethodDescChunk; diff --git a/src/coreclr/vm/classlayoutinfo.cpp b/src/coreclr/vm/classlayoutinfo.cpp index 0b0b54005cdba3..766f364e49c63b 100644 --- a/src/coreclr/vm/classlayoutinfo.cpp +++ b/src/coreclr/vm/classlayoutinfo.cpp @@ -4,222 +4,44 @@ #include "common.h" #include "class.h" #include "fieldmarshaler.h" +#include "enum_class_flags.h" #ifndef DACCESS_COMPILE +struct LayoutRawFieldInfo final +{ + mdFieldDef m_token; // mdMemberDefNil for end of array + RawFieldPlacementInfo m_placement; + NativeFieldDescriptor m_nfd; +}; + namespace { - void SetOffsetsAndSortFields( - IMDInternalImport* pInternalImport, - const mdTypeDef cl, - LayoutRawFieldInfo* pFieldInfoArray, - const ULONG cInstanceFields, - const BOOL fExplicitOffsets, - const UINT32 cbAdjustedParentLayoutNativeSize, - Module* pModule, - LayoutRawFieldInfo** pSortArrayOut - ) + bool TryGetParentLayoutInfo(MethodTable* pParentMT, UINT32* pSize, BYTE* pAlignment) { - HRESULT hr; - MD_CLASS_LAYOUT classlayout; - hr = pInternalImport->GetClassLayoutInit(cl, &classlayout); - if (FAILED(hr)) - { - COMPlusThrowHR(hr, BFA_CANT_GET_CLASSLAYOUT); - } - - LayoutRawFieldInfo* pfwalk = pFieldInfoArray; - mdFieldDef fd; - ULONG ulOffset; - while (SUCCEEDED(hr = pInternalImport->GetClassLayoutNext( - &classlayout, - &fd, - &ulOffset)) && - fd != mdFieldDefNil) - { - // watch for the last entry: must be mdFieldDefNil - while ((mdFieldDefNil != pfwalk->m_MD) && (pfwalk->m_MD < fd)) - pfwalk++; - - // if we haven't found a matching token, it must be a static field with layout -- ignore it - if (pfwalk->m_MD != fd) continue; - - if (fExplicitOffsets) - { - // ulOffset is the explicit offset - pfwalk->m_placement.m_offset = ulOffset; - pfwalk->m_sequence = (ULONG)-1; - - // Treat base class as an initial member. - if (!ClrSafeInt::addition(pfwalk->m_placement.m_offset, cbAdjustedParentLayoutNativeSize, pfwalk->m_placement.m_offset)) - COMPlusThrowOM(); - } - } - IfFailThrow(hr); - - LayoutRawFieldInfo** pSortArrayEnd = pSortArrayOut; - // now sort the array - if (!fExplicitOffsets) - { - // sort sequential by ascending sequence - for (ULONG i = 0; i < cInstanceFields; i++) - { - LayoutRawFieldInfo** pSortWalk = pSortArrayEnd; - while (pSortWalk != pSortArrayOut) - { - if (pFieldInfoArray[i].m_sequence >= (*(pSortWalk - 1))->m_sequence) - break; - - pSortWalk--; - } + if (!pParentMT || !pParentMT->HasLayout()) + return false; - // pSortWalk now points to the target location for new LayoutRawFieldInfo*. - MoveMemory(pSortWalk + 1, pSortWalk, (pSortArrayEnd - pSortWalk) * sizeof(LayoutRawFieldInfo*)); - *pSortWalk = &pFieldInfoArray[i]; - pSortArrayEnd++; - } - } - else // no sorting for explicit layout - { - for (ULONG i = 0; i < cInstanceFields; i++) - { - if (pFieldInfoArray[i].m_MD != mdFieldDefNil) - { - if (pFieldInfoArray[i].m_placement.m_offset == (UINT32)-1) - { - LPCUTF8 szFieldName; - if (FAILED(pInternalImport->GetNameOfFieldDef(pFieldInfoArray[i].m_MD, &szFieldName))) - { - szFieldName = "Invalid FieldDef record"; - } - pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, - cl, - szFieldName, - IDS_CLASSLOAD_NSTRUCT_EXPLICIT_OFFSET); - } - else if ((INT)pFieldInfoArray[i].m_placement.m_offset < 0) - { - LPCUTF8 szFieldName; - if (FAILED(pInternalImport->GetNameOfFieldDef(pFieldInfoArray[i].m_MD, &szFieldName))) - { - szFieldName = "Invalid FieldDef record"; - } - pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, - cl, - szFieldName, - IDS_CLASSLOAD_NSTRUCT_NEGATIVE_OFFSET); - } - } - - *pSortArrayEnd = &pFieldInfoArray[i]; - pSortArrayEnd++; - } + EEClassLayoutInfo* pLayoutInfo = pParentMT->GetLayoutInfo(); + // Treat base class as an initial member. + // If the parent was originally a zero-sized explicit type but + // got bumped up to a size of 1 for compatibility reasons, then + // we need to remove the padding, but ONLY for inheritance situations. + UINT32 size; + if (pLayoutInfo->IsZeroSized()) { + size = 0; } - } - - void CalculateSizeAndFieldOffsets( - const UINT32 parentSize, - ULONG numInstanceFields, - BOOL fExplicitOffsets, - LayoutRawFieldInfo* const* pSortedFieldInfoArray, // An array of pointers to LayoutRawFieldInfo's in ascending order when sequential layout. - ULONG classSizeInMetadata, - BYTE packingSize, - BYTE parentAlignmentRequirement, - BOOL limitToMaxInteropSize, - BYTE* pLargestAlignmentRequirementOut, - UINT32* pSizeOut - ) - { - UINT32 cbCurOffset = parentSize; - BYTE LargestAlignmentRequirement = max(1, min(packingSize, parentAlignmentRequirement)); - - // Start with the size inherited from the parent (if any). - uint32_t calcTotalSize = parentSize; - - LayoutRawFieldInfo* const* pSortWalk; - ULONG i; - for (pSortWalk = pSortedFieldInfoArray, i = numInstanceFields; i; i--, pSortWalk++) + else { - LayoutRawFieldInfo* pfwalk = *pSortWalk; - RawFieldPlacementInfo* placementInfo = &pfwalk->m_placement; - - BYTE alignmentRequirement = (BYTE)placementInfo->m_alignment; - - alignmentRequirement = min(alignmentRequirement, packingSize); - - LargestAlignmentRequirement = max(LargestAlignmentRequirement, alignmentRequirement); - - switch (alignmentRequirement) - { - case 1: - case 2: - case 4: - case 8: - case 16: - case 32: - case 64: - break; - default: - COMPlusThrowHR(COR_E_INVALIDPROGRAM, BFA_METADATA_CORRUPT); - } - - if (!fExplicitOffsets) - { - // Insert enough padding to align the current data member. - while (cbCurOffset % alignmentRequirement) - { - if (!ClrSafeInt::addition(cbCurOffset, 1, cbCurOffset)) - COMPlusThrowOM(); - } - - // if we overflow we will catch it below - placementInfo->m_offset = cbCurOffset; - cbCurOffset += placementInfo->m_size; - } - - uint32_t fieldEnd = placementInfo->m_offset + placementInfo->m_size; - if (fieldEnd < placementInfo->m_offset) - COMPlusThrowOM(); - - // size of the structure is the size of the last field. - if (fieldEnd > calcTotalSize) - calcTotalSize = fieldEnd; + size = pParentMT->GetNumInstanceFieldBytes(); } + *pSize = size; - if (classSizeInMetadata != 0) + if (pParentMT->IsManagedSequential() || (pParentMT->GetClass()->HasExplicitFieldOffsetLayout() && pParentMT->IsBlittable())) { - ULONG classSize; - if (!ClrSafeInt::addition(classSizeInMetadata, (ULONG)parentSize, classSize)) - COMPlusThrowOM(); - - // size must be large enough to accommodate layout. If not, we use the layout size instead. - calcTotalSize = max((uint32_t)classSize, calcTotalSize); + *pAlignment = pLayoutInfo->GetAlignmentRequirement(); } - else - { - // There was no class size given in metadata, so let's round up to a multiple of the alignment requirement - // to make array allocations of this structure simple to keep aligned. - calcTotalSize += (LargestAlignmentRequirement - calcTotalSize % LargestAlignmentRequirement) % LargestAlignmentRequirement; - - if (calcTotalSize % LargestAlignmentRequirement != 0) - { - if (!ClrSafeInt::addition(calcTotalSize, LargestAlignmentRequirement - (calcTotalSize % LargestAlignmentRequirement), calcTotalSize)) - COMPlusThrowOM(); - } - } - - // We'll cap the total native size at a (somewhat) arbitrary limit to ensure - // that we don't expose some overflow bug later on. - if (calcTotalSize >= MAX_SIZE_FOR_INTEROP && limitToMaxInteropSize) - COMPlusThrowOM(); - - // The packingSize acts as a ceiling on all individual alignment - // requirements so it follows that the largest alignment requirement - // is also capped. - _ASSERTE(LargestAlignmentRequirement <= packingSize); - - *pSizeOut = calcTotalSize; - *pLargestAlignmentRequirementOut = LargestAlignmentRequirement; + return true; } RawFieldPlacementInfo GetFieldPlacementInfo(CorElementType corElemType, TypeHandle pNestedType) @@ -295,6 +117,247 @@ namespace return placementInfo; } + void InitializeLayoutFieldInfoArray(FieldDesc* pFields, ULONG cFields, MethodTable** pByValueClassCache, BYTE packingSize, LayoutRawFieldInfo* pInfoArray, UINT32* pNumInstanceFields, BYTE* pAlignmentRequirement) + { + ULONG cInstanceFields = 0; + BYTE alignmentRequirement = 0; + for (ULONG i = 0; i < cFields; i++) + { + FieldDesc* pField = &pFields[i]; + if (pField->IsStatic()) + continue; + + cInstanceFields++; + CorElementType corElemType = pField->GetFieldType(); + TypeHandle typeHandleMaybe{}; + + if (corElemType == ELEMENT_TYPE_VALUETYPE) + { + typeHandleMaybe = pByValueClassCache[i]; + + corElemType = typeHandleMaybe.AsMethodTable()->GetInternalCorElementType(); + if (corElemType != ELEMENT_TYPE_VALUETYPE) + typeHandleMaybe = TypeHandle{}; + } + + pInfoArray[i].m_token = pField->GetMemberDef(); + pInfoArray[i].m_placement = GetFieldPlacementInfo(corElemType, typeHandleMaybe); + + BYTE fieldAlignmentRequirement = (BYTE)pInfoArray[i].m_placement.m_alignment; + + fieldAlignmentRequirement = min(fieldAlignmentRequirement, packingSize); + + alignmentRequirement = max(alignmentRequirement, fieldAlignmentRequirement); + + switch (fieldAlignmentRequirement) + { + case 1: + case 2: + case 4: + case 8: + case 16: + case 32: + case 64: + break; + default: + COMPlusThrowHR(COR_E_INVALIDPROGRAM, BFA_METADATA_CORRUPT); + } + } + + *pNumInstanceFields = cInstanceFields; + *pAlignmentRequirement = alignmentRequirement; + } + + void SetFieldOffsets(FieldDesc* pFields, ULONG cFields, LayoutRawFieldInfo* pInfoArray, ULONG cInstanceFields) + { + for (ULONG i = 0, iInstanceFieldInfo = 0; i < cFields; i++) + { + FieldDesc* pField = &pFields[i]; + if (pField->IsStatic()) + continue; + + // We should only be placing unplaced fields at this point. + _ASSERTE(pField->GetOffset() == FIELD_OFFSET_UNPLACED + || pField->GetOffset() == FIELD_OFFSET_UNPLACED_GC_PTR + || pField->GetOffset() == FIELD_OFFSET_VALUE_CLASS); + + _ASSERTE(iInstanceFieldInfo < cInstanceFields); + IfFailThrow(pField->SetOffset(pInfoArray[iInstanceFieldInfo++].m_placement.m_offset)); + } + } + + /// @brief Read the offsets for a type's fields from metadata for explicit layout. + /// @param pModule The module containing the type. + /// @param cl The metadata token of the type. + /// @param pFieldInfoArray The information about the instance fields of the type. + /// @param cInstanceFields The numer of instance fields in the type. + /// @param parentSize The size of the parent type's layout. + /// @return The end of the last field in this layout + UINT32 ReadOffsetsForExplicitLayout( + Module* pModule, + const mdTypeDef cl, + LayoutRawFieldInfo* pFieldInfoArray, + const ULONG cInstanceFields, + const UINT32 parentSize + ) + { + HRESULT hr; + MD_CLASS_LAYOUT classlayout; + IMDInternalImport* pInternalImport = pModule->GetMDImport(); + hr = pInternalImport->GetClassLayoutInit(cl, &classlayout); + if (FAILED(hr)) + { + COMPlusThrowHR(hr, BFA_CANT_GET_CLASSLAYOUT); + } + + LayoutRawFieldInfo* pfwalk = pFieldInfoArray; + mdFieldDef fd; + ULONG ulOffset; + UINT32 calcTotalSize = 0; + while (SUCCEEDED(hr = pInternalImport->GetClassLayoutNext( + &classlayout, + &fd, + &ulOffset)) && + fd != mdFieldDefNil) + { + // watch for the last entry: must be mdFieldDefNil + while ((mdFieldDefNil != pfwalk->m_token) && (pfwalk->m_token < fd)) + pfwalk++; + + // if we haven't found a matching token, either we have invalid metadata + // or the field doesn't have an entry. We'll error out in the next loop. + if (pfwalk->m_token != fd) continue; + + // ulOffset is the explicit offset + pfwalk->m_placement.m_offset = ulOffset; + + // Treat base class as an initial member. + if (!ClrSafeInt::addition(pfwalk->m_placement.m_offset, parentSize, pfwalk->m_placement.m_offset)) + COMPlusThrowOM(); + + uint32_t fieldEnd; + if (!ClrSafeInt::addition(pfwalk->m_placement.m_offset, pfwalk->m_placement.m_size, fieldEnd)) + COMPlusThrowOM(); + + // size of the structure is the size of the last field. + if (fieldEnd > calcTotalSize) + calcTotalSize = fieldEnd; + } + IfFailThrow(hr); + + for (ULONG i = 0; i < cInstanceFields; i++) + { + if (pFieldInfoArray[i].m_token != mdFieldDefNil) + { + if (pFieldInfoArray[i].m_placement.m_offset == (UINT32)-1) + { + LPCUTF8 szFieldName; + if (FAILED(pInternalImport->GetNameOfFieldDef(pFieldInfoArray[i].m_token, &szFieldName))) + { + szFieldName = "Invalid FieldDef record"; + } + pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, + cl, + szFieldName, + IDS_CLASSLOAD_NSTRUCT_EXPLICIT_OFFSET); + } + else if (pFieldInfoArray[i].m_placement.m_offset > INT32_MAX) + { + LPCUTF8 szFieldName; + if (FAILED(pInternalImport->GetNameOfFieldDef(pFieldInfoArray[i].m_token, &szFieldName))) + { + szFieldName = "Invalid FieldDef record"; + } + pModule->GetAssembly()->ThrowTypeLoadException(pInternalImport, + cl, + szFieldName, + IDS_CLASSLOAD_NSTRUCT_NEGATIVE_OFFSET); + } + } + } + + return calcTotalSize; + } + + /// @brief Calculate the offsets of the fields if they were to be laid out in sequential order at their alignment requirements. + /// @param pFieldInfoArray The information about the instance fields of the type. + /// @param cInstanceFields The numer of instance fields in the type. + /// @param parentSize The size of the parent type's layout. + /// @param packingSize The packing size of the type. + /// @return The end of the last field in this layout + ULONG CalculateOffsetsForSequentialLayout( + LayoutRawFieldInfo* pFieldInfoArray, + const ULONG numInstanceFields, + const UINT32 parentSize, + const BYTE packingSize + ) + { + _ASSERTE(packingSize != 0); + UINT32 cbCurOffset = parentSize; + + // Start with the size inherited from the parent (if any). + uint32_t calcTotalSize = parentSize; + + for (UINT32 i = 0; i < numInstanceFields; i++) + { + RawFieldPlacementInfo& placementInfo = pFieldInfoArray[i].m_placement; + + BYTE alignmentRequirement = min((BYTE)placementInfo.m_alignment, packingSize); + + // Insert enough padding to align the current data member. + if (!ClrSafeInt::addition(cbCurOffset, (alignmentRequirement - (cbCurOffset % alignmentRequirement)) % alignmentRequirement, cbCurOffset)) + COMPlusThrowOM(); + + placementInfo.m_offset = cbCurOffset; + + if (!ClrSafeInt::addition(cbCurOffset, placementInfo.m_size, cbCurOffset)) + { + COMPlusThrowOM(); + } + + // size of the structure is the size of the last field. + if (cbCurOffset > calcTotalSize) + calcTotalSize = cbCurOffset; + } + + return calcTotalSize; + } + + ULONG CalculateSizeWithMetadataSize( + const ULONG parentSize, + const UINT32 lastFieldEnd, + const ULONG classSizeInMetadata + ) + { + // If we have successfully fetched the class size from metadata, + // we'll try to use it. Add the parent size to the metadata size, + // so it represents the full size of the class. + ULONG classSize; + if (!ClrSafeInt::addition(classSizeInMetadata, (ULONG)parentSize, classSize)) + COMPlusThrowOM(); + + // size must be large enough to accommodate layout. If not, we use the layout size instead. + return max((uint32_t)classSize, lastFieldEnd); + } + + UINT32 AlignSize( + const UINT32 lastFieldEnd, + BYTE alignmentRequirement + ) + { + ULONG calcTotalSize = lastFieldEnd; + + // There was no class size given in metadata, so let's round up to a multiple of the alignment requirement + // to make array allocations of this structure simple to keep aligned. + if (calcTotalSize % alignmentRequirement != 0) + { + if (!ClrSafeInt::addition(calcTotalSize, (alignmentRequirement - (calcTotalSize % alignmentRequirement)) % alignmentRequirement, calcTotalSize)) + COMPlusThrowOM(); + } + + return calcTotalSize; + } + BOOL TypeHasGCPointers(CorElementType corElemType, TypeHandle pNestedType) { if (CorTypeInfo::IsPrimitiveType(corElemType) || corElemType == ELEMENT_TYPE_PTR || corElemType == ELEMENT_TYPE_FNPTR || @@ -334,7 +397,219 @@ namespace return FALSE; } -#ifdef UNIX_AMD64_ABI + ParseNativeTypeFlags NlTypeToNativeTypeFlags(CorNativeLinkType nlType) + { + ParseNativeTypeFlags nativeTypeFlags = ParseNativeTypeFlags::None; + if (nlType == nltAnsi) + nativeTypeFlags = ParseNativeTypeFlags::IsAnsi; + + return nativeTypeFlags; + } + +#ifdef FEATURE_DOUBLE_ALIGNMENT_HINT + //******************************************************************************* + // + // Heuristic to determine if we should have instances of this class 8 byte aligned + // + bool ShouldAlign8(ULONG dwR8Fields, ULONG dwTotalFields) + { + LIMITED_METHOD_CONTRACT; + + return dwR8Fields*2>dwTotalFields && dwR8Fields>=2; + } +#endif +} + +auto EEClassLayoutInfo::GetNestedFieldFlags(Module* pModule, FieldDesc *pFields, ULONG cFields, CorNativeLinkType nlType, MethodTable** pByValueClassCache) -> NestedFieldFlags +{ + STANDARD_VM_CONTRACT; + + NestedFieldFlags flags = NestedFieldFlags::None; + const ParseNativeTypeFlags nativeTypeFlags = NlTypeToNativeTypeFlags(nlType); + + ULONG numR8Fields = 0; + ULONG numInstanceFields = 0; + + for (ULONG i = 0; i < cFields; i++) + { + FieldDesc* pField = &pFields[i]; + if (pField->IsStatic()) + continue; + + numInstanceFields++; + CorElementType corElemType = pField->GetFieldType(); + TypeHandle typeHandleMaybe{}; + + if (corElemType == ELEMENT_TYPE_VALUETYPE) + { + typeHandleMaybe = pByValueClassCache[i]; + + corElemType = typeHandleMaybe.AsMethodTable()->GetInternalCorElementType(); + if (corElemType != ELEMENT_TYPE_VALUETYPE) + typeHandleMaybe = TypeHandle(); + } + + if (corElemType == ELEMENT_TYPE_R8) + { + numR8Fields++; + } + +#ifdef FEATURE_64BIT_ALIGNMENT + if (!typeHandleMaybe.IsNull() && typeHandleMaybe.GetMethodTable()->GetClass()->IsAlign8Candidate()) + { + flags |= NestedFieldFlags::Align8; + } + + if (corElemType == ELEMENT_TYPE_I8 + || corElemType == ELEMENT_TYPE_U8 + || corElemType == ELEMENT_TYPE_R8 + IN_TARGET_64BIT(|| corElemType == ELEMENT_TYPE_I || corElemType == ELEMENT_TYPE_U)) + { + flags |= NestedFieldFlags::Align8; + } +#endif + + if (!IsFieldBlittable(pModule, pField->GetMemberDef(), corElemType, typeHandleMaybe, nativeTypeFlags)) + { + flags |= NestedFieldFlags::NonBlittable; + } + + if (TypeHasGCPointers(corElemType, typeHandleMaybe)) + { + flags |= NestedFieldFlags::GCPointer; + } + + if (TypeHasAutoLayoutField(corElemType, typeHandleMaybe)) + { + flags |= NestedFieldFlags::AutoLayout; + } + + if (TypeHasInt128Field(corElemType, typeHandleMaybe)) + { + flags |= NestedFieldFlags::Int128; + } + } + +#ifdef FEATURE_DOUBLE_ALIGNMENT_HINT + if (ShouldAlign8(numR8Fields, numInstanceFields)) + { + flags |= NestedFieldFlags::Align8; + } +#endif + + return flags; +} + +ULONG EEClassLayoutInfo::InitializeSequentialFieldLayout( + FieldDesc* pFields, + MethodTable** pByValueClassCache, + ULONG cFields, + BYTE packingSize, + ULONG classSizeInMetadata, + MethodTable* pParentMT +) +{ + STANDARD_VM_CONTRACT; + + SetLayoutType(LayoutType::Sequential); + + UINT32 cbAdjustedParentLayoutSize; + BYTE parentAlignmentRequirement; + if (!TryGetParentLayoutInfo(pParentMT, &cbAdjustedParentLayoutSize, &parentAlignmentRequirement)) + { + cbAdjustedParentLayoutSize = 0; + parentAlignmentRequirement = 0; + } + + NewArrayHolder pInfoArray = new LayoutRawFieldInfo[cFields + 1]; + UINT32 numInstanceFields; + BYTE fieldsAlignmentRequirement; + InitializeLayoutFieldInfoArray(pFields, cFields, pByValueClassCache, packingSize, pInfoArray, &numInstanceFields, &fieldsAlignmentRequirement); + + BYTE alignmentRequirement = max(max(1, min(packingSize, parentAlignmentRequirement)), fieldsAlignmentRequirement); + + // The packingSize acts as a ceiling on all individual alignment + // requirements so it follows that the largest alignment requirement + // is also capped. + _ASSERTE(alignmentRequirement <= packingSize); + SetAlignmentRequirement(alignmentRequirement); + SetPackingSize(packingSize); + + UINT32 lastFieldEnd = CalculateOffsetsForSequentialLayout(pInfoArray, numInstanceFields, cbAdjustedParentLayoutSize, packingSize); + + SetFieldOffsets(pFields, cFields, pInfoArray, numInstanceFields); + + UINT32 managedSize; + if (classSizeInMetadata != 0) + { + managedSize = CalculateSizeWithMetadataSize(cbAdjustedParentLayoutSize, lastFieldEnd, classSizeInMetadata); + } + else + { + managedSize = AlignSize(lastFieldEnd, alignmentRequirement); + } + + return SetInstanceBytesSize(managedSize); +} + +ULONG EEClassLayoutInfo::InitializeExplicitFieldLayout( + FieldDesc* pFields, + MethodTable** pByValueClassCache, + ULONG cFields, + BYTE packingSize, + ULONG classSizeInMetadata, + MethodTable* pParentMT, + Module* pModule, + mdTypeDef cl +) +{ + STANDARD_VM_CONTRACT; + + SetLayoutType(LayoutType::Explicit); + + UINT32 cbAdjustedParentLayoutSize; + BYTE parentAlignmentRequirement; + if (!TryGetParentLayoutInfo(pParentMT, &cbAdjustedParentLayoutSize, &parentAlignmentRequirement)) + { + cbAdjustedParentLayoutSize = 0; + parentAlignmentRequirement = 0; + } + + NewArrayHolder pInfoArray = new LayoutRawFieldInfo[cFields + 1]; + UINT32 numInstanceFields; + BYTE fieldsAlignmentRequirement; + InitializeLayoutFieldInfoArray(pFields, cFields, pByValueClassCache, packingSize, pInfoArray, &numInstanceFields, &fieldsAlignmentRequirement); + + BYTE alignmentRequirement = max(max(1, min(packingSize, parentAlignmentRequirement)), fieldsAlignmentRequirement); + + // The packingSize acts as a ceiling on all individual alignment + // requirements so it follows that the largest alignment requirement + // is also capped. + _ASSERTE(alignmentRequirement <= packingSize); + SetAlignmentRequirement(alignmentRequirement); + SetPackingSize(packingSize); + + UINT32 lastFieldEnd = 0; + lastFieldEnd = ReadOffsetsForExplicitLayout(pModule, cl, pInfoArray, numInstanceFields, cbAdjustedParentLayoutSize); + + SetFieldOffsets(pFields, cFields, pInfoArray, numInstanceFields); + + UINT32 managedSize; + if (classSizeInMetadata != 0) + { + managedSize = CalculateSizeWithMetadataSize(cbAdjustedParentLayoutSize, lastFieldEnd, classSizeInMetadata); + } + else + { + managedSize = AlignSize(lastFieldEnd, alignmentRequirement); + } + + return SetInstanceBytesSize(managedSize); +} + +namespace +{ + #ifdef UNIX_AMD64_ABI void SystemVAmd64CheckForPassNativeStructInRegister(MethodTable* pMT, EEClassNativeLayoutInfo* pNativeLayoutInfo) { STANDARD_VM_CONTRACT; @@ -419,9 +694,8 @@ namespace pFieldDesc->GetSig(&pCOMSignature, &cbCOMSignature); // fill the appropriate entry in pInfoArray - pFieldInfoArrayOut->m_MD = fd; + pFieldInfoArrayOut->m_token = fd; pFieldInfoArrayOut->m_placement.m_offset = (UINT32)-1; - pFieldInfoArrayOut->m_sequence = 0; #ifdef _DEBUG LPCUTF8 szFieldName; @@ -449,137 +723,7 @@ namespace } // NULL out the last entry - pFieldInfoArrayOut->m_MD = mdFieldDefNil; - } - - void DetermineBlittabilityAndManagedSequential( - IMDInternalImport* pInternalImport, - HENUMInternal* phEnumField, - Module* pModule, - mdTypeDef cl, - ParseNativeTypeFlags nativeTypeFlags, - const SigTypeContext* pTypeContext, - BOOL* fDisqualifyFromManagedSequential, - BOOL* fHasAutoLayoutField, - BOOL* fHasInt128Field, - LayoutRawFieldInfo* pFieldInfoArrayOut, - BOOL* pIsBlittableOut, - ULONG* cInstanceFields - #ifdef _DEBUG - , - const ULONG cTotalFields, - LPCUTF8 szNamespace, - LPCUTF8 szName - #endif - ) - { - STANDARD_VM_CONTRACT; - - HRESULT hr; - mdFieldDef fd; - ULONG maxRid = pInternalImport->GetCountWithTokenKind(mdtFieldDef); - *pIsBlittableOut = TRUE; // Assume is blittable until proven otherwise. - - ULONG i; - for (i = 0; pInternalImport->EnumNext(phEnumField, &fd); i++) - { - DWORD dwFieldAttrs; - ULONG rid = RidFromToken(fd); - - if ((rid == 0) || (rid > maxRid)) - { - COMPlusThrowHR(COR_E_TYPELOAD, BFA_BAD_FIELD_TOKEN); - } - - IfFailThrow(pInternalImport->GetFieldDefProps(fd, &dwFieldAttrs)); - - PCCOR_SIGNATURE pNativeType = NULL; - ULONG cbNativeType; - // We ignore marshaling data attached to statics and literals, - // since these do not contribute to instance data. - if (!IsFdStatic(dwFieldAttrs) && !IsFdLiteral(dwFieldAttrs)) - { - PCCOR_SIGNATURE pCOMSignature; - ULONG cbCOMSignature; - - if (IsFdHasFieldMarshal(dwFieldAttrs)) - { - hr = pInternalImport->GetFieldMarshal(fd, &pNativeType, &cbNativeType); - if (FAILED(hr)) - { - cbNativeType = 0; - } - } - else - { - cbNativeType = 0; - } - - IfFailThrow(pInternalImport->GetSigOfFieldDef(fd, &cbCOMSignature, &pCOMSignature)); - - IfFailThrow(::validateTokenSig(fd, pCOMSignature, cbCOMSignature, dwFieldAttrs, pInternalImport)); - - // fill the appropriate entry in pInfoArray - pFieldInfoArrayOut->m_MD = fd; - pFieldInfoArrayOut->m_sequence = 0; - - #ifdef _DEBUG - LPCUTF8 szFieldName; - if (FAILED(pInternalImport->GetNameOfFieldDef(fd, &szFieldName))) - { - szFieldName = "Invalid FieldDef record"; - } - #endif - MetaSig fsig(pCOMSignature, cbCOMSignature, pModule, pTypeContext, MetaSig::sigField); - CorElementType corElemType = fsig.NextArg(); - - TypeHandle typeHandleMaybe; - if (corElemType == ELEMENT_TYPE_VALUETYPE) // Only look up the next element in the signature if it is a value type to avoid causing recursive type loads in valid scenarios. - { - SigPointer::HandleRecursiveGenericsForFieldLayoutLoad recursiveControl; - recursiveControl.pModuleWithTokenToAvoidIfPossible = pModule; - recursiveControl.tkTypeDefToAvoidIfPossible = cl; - typeHandleMaybe = fsig.GetArgProps().GetTypeHandleThrowing(pModule, - pTypeContext, - ClassLoader::LoadTypes, - CLASS_LOAD_APPROXPARENTS, - TRUE, NULL, NULL, NULL, - &recursiveControl); - - if (typeHandleMaybe.IsNull()) - { - // Everett C++ compiler can generate a TypeRef with RS=0 - // without respective TypeDef for unmanaged valuetypes, - // referenced only by pointers to them. - // In such case, GetTypeHandleThrowing returns null handle, - // and we return E_T_VOID - typeHandleMaybe = TypeHandle(CoreLibBinder::GetElementType(ELEMENT_TYPE_VOID)); - } - corElemType = typeHandleMaybe.AsMethodTable()->GetInternalCorElementType(); - if (corElemType != ELEMENT_TYPE_VALUETYPE) - typeHandleMaybe = TypeHandle(); - } - else if (corElemType == ELEMENT_TYPE_TYPEDBYREF) - { - typeHandleMaybe = TypeHandle(g_TypedReferenceMT); - } - - pFieldInfoArrayOut->m_placement = GetFieldPlacementInfo(corElemType, typeHandleMaybe); - *fDisqualifyFromManagedSequential |= TypeHasGCPointers(corElemType, typeHandleMaybe); - *fHasAutoLayoutField |= TypeHasAutoLayoutField(corElemType, typeHandleMaybe); - *fHasInt128Field |= TypeHasInt128Field(corElemType, typeHandleMaybe); - - if (!IsFieldBlittable(pModule, fd, corElemType, typeHandleMaybe, nativeTypeFlags)) - *pIsBlittableOut = FALSE; - - (*cInstanceFields)++; - pFieldInfoArrayOut++; - } - } - - _ASSERTE(i == cTotalFields); - // NULL out the last entry - pFieldInfoArrayOut->m_MD = mdFieldDefNil; + pFieldInfoArrayOut->m_token = mdFieldDefNil; } #ifdef FEATURE_HFA @@ -611,198 +755,23 @@ namespace pNativeLayoutInfo->SetHFAType(hfaType); } #endif // FEATURE_HFA -} - -//======================================================================= -// Called from the clsloader to load up and summarize the field metadata -// for layout classes. -// -// Warning: This function can load other classes (esp. for nested structs.) -//======================================================================= -VOID EEClassLayoutInfo::CollectLayoutFieldMetadataThrowing( - mdTypeDef cl, // cl of the NStruct being loaded - BYTE packingSize, // packing size (from @dll.struct) - BYTE nlType, // nltype (from @dll.struct) - BOOL fExplicitOffsets, // explicit offsets? - MethodTable *pParentMT, // the loaded superclass - ULONG cTotalFields, // total number of fields (instance and static) - HENUMInternal *phEnumField, // enumerator for field - Module *pModule, // Module that defines the scope, loader and heap (for allocate FieldMarshalers) - const SigTypeContext *pTypeContext, // Type parameters for NStruct being loaded - EEClassLayoutInfo *pEEClassLayoutInfoOut, // caller-allocated structure to fill in. - LayoutRawFieldInfo *pInfoArrayOut, // caller-allocated array to fill in. Needs room for cMember+1 elements - LoaderAllocator *pAllocator, - AllocMemTracker *pamTracker -) -{ - CONTRACTL - { - STANDARD_VM_CHECK; - PRECONDITION(CheckPointer(pModule)); - } - CONTRACTL_END; - - // Internal interface for the NStruct being loaded. - IMDInternalImport *pInternalImport = pModule->GetMDImport(); - -#ifdef _DEBUG - LPCUTF8 szName; - LPCUTF8 szNamespace; - if (FAILED(pInternalImport->GetNameOfTypeDef(cl, &szName, &szNamespace))) - { - szName = szNamespace = "Invalid TypeDef record"; - } - - if (g_pConfig->ShouldBreakOnStructMarshalSetup(szName)) - CONSISTENCY_CHECK_MSGF(false, ("BreakOnStructMarshalSetup: '%s' ", szName)); -#endif - - // Running tote - if anything in this type disqualifies it from being ManagedSequential, somebody will set this to TRUE by the time - // function exits. - BOOL fDisqualifyFromManagedSequential; - BOOL hasAutoLayoutField = FALSE; - BOOL hasInt128Field = FALSE; - - // Check if this type might be ManagedSequential. Only valuetypes marked Sequential can be - // ManagedSequential. Other issues checked below might also disqualify the type. - if ( (!fExplicitOffsets) && // Is it marked sequential? - (pParentMT && (pParentMT->IsObjectClass() || pParentMT->IsValueTypeClass() || pParentMT->IsManagedSequential())) // Is it a valuetype or derived from a qualifying valuetype? - ) - { - fDisqualifyFromManagedSequential = FALSE; - } - else - { - fDisqualifyFromManagedSequential = TRUE; - } - - if (pParentMT && !pParentMT->IsValueTypeClass()) - { - if (pParentMT->IsAutoLayoutOrHasAutoLayoutField()) - hasAutoLayoutField = TRUE; - if (pParentMT->IsInt128OrHasInt128Fields()) - hasInt128Field = TRUE; - } - - - BOOL fHasNonTrivialParent = pParentMT && - !pParentMT->IsObjectClass() && - !pParentMT->IsValueTypeClass(); - - - // Set some defaults based on the parent type of this type (if one exists). - _ASSERTE(!(fHasNonTrivialParent && !(pParentMT->HasLayout()))); - - pEEClassLayoutInfoOut->SetIsZeroSized(FALSE); - pEEClassLayoutInfoOut->SetHasExplicitSize(FALSE); - pEEClassLayoutInfoOut->m_cbPackingSize = packingSize; - - BOOL fParentHasLayout = pParentMT && pParentMT->HasLayout(); - UINT32 cbAdjustedParentLayoutSize = 0; - EEClassLayoutInfo *pParentLayoutInfo = NULL; - if (fParentHasLayout) - { - pParentLayoutInfo = pParentMT->GetLayoutInfo(); - // Treat base class as an initial member. - // If the parent was originally a zero-sized explicit type but - // got bumped up to a size of 1 for compatibility reasons, then - // we need to remove the padding, but ONLY for inheritance situations. - if (pParentLayoutInfo->IsZeroSized()) { - cbAdjustedParentLayoutSize = 0; - } - else - { - cbAdjustedParentLayoutSize = pParentMT->GetNumInstanceFieldBytes(); - } - } - - ULONG cInstanceFields = 0; - - ParseNativeTypeFlags nativeTypeFlags = ParseNativeTypeFlags::None; - if (nlType == nltAnsi) - nativeTypeFlags = ParseNativeTypeFlags::IsAnsi; - - BOOL isBlittable; - - DetermineBlittabilityAndManagedSequential( - pInternalImport, - phEnumField, - pModule, - cl, - nativeTypeFlags, - pTypeContext, - &fDisqualifyFromManagedSequential, - &hasAutoLayoutField, - &hasInt128Field, - pInfoArrayOut, - &isBlittable, - &cInstanceFields - DEBUGARG(cTotalFields) - DEBUGARG(szNamespace) - DEBUGARG(szName) - ); - // Type is blittable only if parent is also blittable - isBlittable = isBlittable && (fHasNonTrivialParent ? pParentMT->IsBlittable() : TRUE); - pEEClassLayoutInfoOut->SetIsBlittable(isBlittable); - - pEEClassLayoutInfoOut->SetHasAutoLayoutField(hasAutoLayoutField); - - pEEClassLayoutInfoOut->SetIsInt128OrHasInt128Fields(hasInt128Field); - - S_UINT32 cbSortArraySize = S_UINT32(cTotalFields) * S_UINT32(sizeof(LayoutRawFieldInfo*)); - if (cbSortArraySize.IsOverflow()) + EEClassNativeLayoutInfo const* FindParentNativeLayoutInfo(MethodTable* pParentMT) { - ThrowHR(COR_E_TYPELOAD); - } - CQuickArray pSortArray; - pSortArray.ReSizeThrows(cbSortArraySize.Value()); - SetOffsetsAndSortFields(pInternalImport, cl, pInfoArrayOut, cInstanceFields, fExplicitOffsets, cbAdjustedParentLayoutSize, pModule, pSortArray.Ptr()); + STANDARD_VM_CONTRACT; - ULONG classSizeInMetadata = 0; - if (FAILED(pInternalImport->GetClassTotalSize(cl, &classSizeInMetadata))) - { - classSizeInMetadata = 0; - } - else - { - // If we can get the class size from metadata, that means that the user - // explicitly provided a value to the StructLayoutAttribute.Size field - // or explicitly provided the size in IL. - pEEClassLayoutInfoOut->SetHasExplicitSize(TRUE); - } + if (!pParentMT || !pParentMT->HasLayout()) + return nullptr; - BYTE parentAlignmentRequirement = 0; - if (fParentHasLayout) - { - parentAlignmentRequirement = pParentLayoutInfo->m_ManagedLargestAlignmentRequirementOfAllMembers; - } + bool fHasNonTrivialParent = pParentMT && + !pParentMT->IsObjectClass() && + !pParentMT->IsValueTypeClass(); - BYTE parentManagedAlignmentRequirement = 0; - if (pParentMT && (pParentMT->IsManagedSequential() || (pParentMT->GetClass()->HasExplicitFieldOffsetLayout() && pParentMT->IsBlittable()))) - { - parentManagedAlignmentRequirement = pParentLayoutInfo->m_ManagedLargestAlignmentRequirementOfAllMembers; - } + // Set some defaults based on the parent type of this type (if one exists). + _ASSERTE(!(fHasNonTrivialParent && !(pParentMT->HasLayout()))); - CalculateSizeAndFieldOffsets( - cbAdjustedParentLayoutSize, - cInstanceFields, - fExplicitOffsets, - pSortArray.Ptr(), - classSizeInMetadata, - packingSize, - parentManagedAlignmentRequirement, - /*limitToMaxInteropSize*/ FALSE, - &pEEClassLayoutInfoOut->m_ManagedLargestAlignmentRequirementOfAllMembers, - &pEEClassLayoutInfoOut->m_cbManagedSize); - - if (pEEClassLayoutInfoOut->m_cbManagedSize == 0) - { - pEEClassLayoutInfoOut->SetIsZeroSized(TRUE); - pEEClassLayoutInfoOut->m_cbManagedSize = 1; // Bump the managed size of the structure up to 1. + return pParentMT->GetNativeLayoutInfo(); } - - pEEClassLayoutInfoOut->SetIsManagedSequential(!fDisqualifyFromManagedSequential); } void EEClassNativeLayoutInfo::InitializeNativeLayoutFieldMetadataThrowing(MethodTable* pMT) @@ -875,19 +844,10 @@ EEClassNativeLayoutInfo* EEClassNativeLayoutInfo::CollectNativeLayoutFieldMetada MethodTable* pParentMT = pMT->GetParentMethodTable(); - BOOL fHasNonTrivialParent = pParentMT && - !pParentMT->IsObjectClass() && - !pParentMT->IsValueTypeClass(); - - // Set some defaults based on the parent type of this type (if one exists). - _ASSERTE(!(fHasNonTrivialParent && !(pParentMT->HasLayout()))); - - BOOL fParentHasLayout = pParentMT && pParentMT->HasLayout(); UINT32 cbAdjustedParentLayoutNativeSize = 0; - EEClassNativeLayoutInfo const* pParentLayoutInfo = NULL; - if (fParentHasLayout) + EEClassNativeLayoutInfo const* pParentLayoutInfo = FindParentNativeLayoutInfo(pParentMT); + if (pParentLayoutInfo != nullptr) { - pParentLayoutInfo = pParentMT->GetNativeLayoutInfo(); // Treat base class as an initial member. cbAdjustedParentLayoutNativeSize = pParentLayoutInfo->GetSize(); // If the parent was originally a zero-sized explicit type but @@ -899,12 +859,9 @@ EEClassNativeLayoutInfo* EEClassNativeLayoutInfo::CollectNativeLayoutFieldMetada } } - CorNativeLinkType charSet = pMT->GetCharSet(); - ParseNativeTypeFlags nativeTypeFlags = ParseNativeTypeFlags::None; - if (charSet == nltAnsi) - nativeTypeFlags = ParseNativeTypeFlags::IsAnsi; + ParseNativeTypeFlags nativeTypeFlags = NlTypeToNativeTypeFlags(charSet); ApproxFieldDescIterator fieldDescs(pMT, ApproxFieldDescIterator::INSTANCE_FIELDS); @@ -934,33 +891,53 @@ EEClassNativeLayoutInfo* EEClassNativeLayoutInfo::CollectNativeLayoutFieldMetada pNativeLayoutInfo->m_numFields = numTotalInstanceFields; + BYTE parentAlignmentRequirement = 0; + if (pParentLayoutInfo != nullptr) + { + parentAlignmentRequirement = pParentLayoutInfo->GetLargestAlignmentRequirement(); + } + + BYTE fieldAlignmentRequirement = 0; // Now compute the native size of each field - for (LayoutRawFieldInfo* pfwalk = pInfoArray; pfwalk->m_MD != mdFieldDefNil; pfwalk++) + for (LayoutRawFieldInfo* pfwalk = pInfoArray; pfwalk->m_token != mdFieldDefNil; pfwalk++) { pfwalk->m_placement.m_size = pfwalk->m_nfd.NativeSize(); pfwalk->m_placement.m_alignment = pfwalk->m_nfd.AlignmentRequirement(); + if (pfwalk->m_placement.m_alignment > fieldAlignmentRequirement) + { + fieldAlignmentRequirement = (BYTE)pfwalk->m_placement.m_alignment; + } } - S_UINT32 cbSortArraySize = S_UINT32(cInstanceFields) * S_UINT32(sizeof(LayoutRawFieldInfo*)); - if (cbSortArraySize.IsOverflow()) - { - ThrowHR(COR_E_TYPELOAD); - } + pNativeLayoutInfo->m_alignmentRequirement = max(max(1, parentAlignmentRequirement), fieldAlignmentRequirement); BOOL fExplicitOffsets = pMT->GetClass()->HasExplicitFieldOffsetLayout(); - CQuickArray pSortArray; - pSortArray.ReSizeThrows(cbSortArraySize.Value()); - SetOffsetsAndSortFields(pInternalImport, pMT->GetCl(), pInfoArray, cInstanceFields, fExplicitOffsets, cbAdjustedParentLayoutNativeSize, pModule, pSortArray.Ptr()); + ULONG lastFieldEnd = 0; + if (fExplicitOffsets) + { + lastFieldEnd = ReadOffsetsForExplicitLayout(pModule, pMT->GetCl(), pInfoArray, cInstanceFields, cbAdjustedParentLayoutNativeSize); + } + else + { + BYTE packingSize = pMT->GetLayoutInfo()->GetPackingSize(); + if (packingSize == 0) + { + packingSize = DEFAULT_PACKING_SIZE; + } + lastFieldEnd = CalculateOffsetsForSequentialLayout(pInfoArray, cInstanceFields, cbAdjustedParentLayoutNativeSize, packingSize); + } EEClassLayoutInfo* pEEClassLayoutInfo = pMT->GetLayoutInfo(); - ULONG classSizeInMetadata = 0; if (pEEClassLayoutInfo->HasExplicitSize()) { + ULONG classSizeInMetadata = 0; HRESULT hr = pInternalImport->GetClassTotalSize(pMT->GetCl(), &classSizeInMetadata); CONSISTENCY_CHECK(hr == S_OK); + + pNativeLayoutInfo->m_size = CalculateSizeWithMetadataSize(cbAdjustedParentLayoutNativeSize, lastFieldEnd, classSizeInMetadata); } else if (pMT->GetClass()->IsInlineArray()) { @@ -982,29 +959,20 @@ EEClassNativeLayoutInfo* EEClassNativeLayoutInfo::CollectNativeLayoutFieldMetada INT32 repeat = GET_UNALIGNED_VAL32((byte*)pVal + 2); if (repeat > 0) { - classSizeInMetadata = repeat * pInfoArray[0].m_nfd.NativeSize(); + pNativeLayoutInfo->m_size = repeat * pInfoArray[0].m_nfd.NativeSize(); } } } } - - BYTE parentAlignmentRequirement = 0; - if (fParentHasLayout) + else { - parentAlignmentRequirement = pParentLayoutInfo->GetLargestAlignmentRequirement(); + pNativeLayoutInfo->m_size = AlignSize(lastFieldEnd, pNativeLayoutInfo->GetLargestAlignmentRequirement()); } - CalculateSizeAndFieldOffsets( - cbAdjustedParentLayoutNativeSize, - cInstanceFields, - fExplicitOffsets, - pSortArray.Ptr(), - classSizeInMetadata, - pMT->GetLayoutInfo()->GetPackingSize(), - parentAlignmentRequirement, - /*limitToMaxInteropSize*/ TRUE, - &pNativeLayoutInfo->m_alignmentRequirement, - &pNativeLayoutInfo->m_size); + // We'll cap the total native size at a (somewhat) arbitrary limit to ensure + // that we don't expose some overflow bug later on. + if (pNativeLayoutInfo->m_size >= MAX_SIZE_FOR_INTEROP) + COMPlusThrowOM(); if (pNativeLayoutInfo->m_size == 0) { @@ -1020,8 +988,8 @@ EEClassNativeLayoutInfo* EEClassNativeLayoutInfo::CollectNativeLayoutFieldMetada // from the managed size and alignment. if (pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__VECTORT))) { - pNativeLayoutInfo->m_size = pEEClassLayoutInfo->GetManagedSize(); - pNativeLayoutInfo->m_alignmentRequirement = pEEClassLayoutInfo->m_ManagedLargestAlignmentRequirementOfAllMembers; + pNativeLayoutInfo->m_size = pMT->GetNumInstanceFieldBytes(); + pNativeLayoutInfo->m_alignmentRequirement = pEEClassLayoutInfo->GetAlignmentRequirement(); } else if (pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__INT128)) || @@ -1031,18 +999,15 @@ EEClassNativeLayoutInfo* EEClassNativeLayoutInfo::CollectNativeLayoutFieldMetada pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__VECTOR256T)) || pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__VECTOR512T))) { - pNativeLayoutInfo->m_alignmentRequirement = pEEClassLayoutInfo->m_ManagedLargestAlignmentRequirementOfAllMembers; + pNativeLayoutInfo->m_alignmentRequirement = pEEClassLayoutInfo->GetAlignmentRequirement(); } } PTR_NativeFieldDescriptor pNativeFieldDescriptors = pNativeLayoutInfo->GetNativeFieldDescriptors(); // Bring in the parent's fieldmarshalers - if (fHasNonTrivialParent) + if (pParentLayoutInfo != nullptr) { - CONSISTENCY_CHECK(fParentHasLayout); - _ASSERTE(pParentLayoutInfo != NULL); // See if (fParentHasLayout) branch above - UINT numChildCTMFields = cInstanceFields; NativeFieldDescriptor const* pParentCTMFieldSrcArray = pParentLayoutInfo->GetNativeFieldDescriptors(); @@ -1054,19 +1019,14 @@ EEClassNativeLayoutInfo* EEClassNativeLayoutInfo::CollectNativeLayoutFieldMetada } } + bool isMarshalable = pParentLayoutInfo != nullptr ? pParentLayoutInfo->IsMarshalable() : true; for (UINT i = 0; i < cInstanceFields; i++) { pInfoArray[i].m_nfd.SetExternalOffset(pInfoArray[i].m_placement.m_offset); pNativeFieldDescriptors[i] = pInfoArray[i].m_nfd; - } - - bool isMarshalable = true; - for (UINT i = 0; i < numTotalInstanceFields; i++) - { if (pNativeFieldDescriptors[i].IsUnmarshalable()) { isMarshalable = false; - break; } } @@ -1083,8 +1043,8 @@ EEClassNativeLayoutInfo* EEClassNativeLayoutInfo::CollectNativeLayoutFieldMetada _ASSERTE(pNativeFieldDescriptors[i].GetExternalOffset() == pNativeFieldDescriptors[i].GetFieldDesc()->GetOffset()); _ASSERTE(pNativeFieldDescriptors[i].NativeSize() == pNativeFieldDescriptors[i].GetFieldDesc()->GetSize()); } - _ASSERTE(pNativeLayoutInfo->GetSize() == pEEClassLayoutInfo->GetManagedSize()); - _ASSERTE(pNativeLayoutInfo->GetLargestAlignmentRequirement() == pEEClassLayoutInfo->m_ManagedLargestAlignmentRequirementOfAllMembers); + _ASSERTE(pNativeLayoutInfo->GetSize() == pMT->GetNumInstanceFieldBytes()); + _ASSERTE(pNativeLayoutInfo->GetLargestAlignmentRequirement() == pEEClassLayoutInfo->GetAlignmentRequirement()); } LOG((LF_INTEROP, LL_INFO100000, "\n\n")); @@ -1092,10 +1052,10 @@ EEClassNativeLayoutInfo* EEClassNativeLayoutInfo::CollectNativeLayoutFieldMetada LOG((LF_INTEROP, LL_INFO100000, "Packsize = %lu\n", (ULONG)pEEClassLayoutInfo->GetPackingSize())); LOG((LF_INTEROP, LL_INFO100000, "Max align req = %lu\n", (ULONG)(pNativeLayoutInfo->GetLargestAlignmentRequirement()))); LOG((LF_INTEROP, LL_INFO100000, "----------------------------\n")); - for (LayoutRawFieldInfo* pfwalk = pInfoArray; pfwalk->m_MD != mdFieldDefNil; pfwalk++) + for (LayoutRawFieldInfo* pfwalk = pInfoArray; pfwalk->m_token != mdFieldDefNil; pfwalk++) { LPCUTF8 fieldname; - if (FAILED(pInternalImport->GetNameOfFieldDef(pfwalk->m_MD, &fieldname))) + if (FAILED(pInternalImport->GetNameOfFieldDef(pfwalk->m_token, &fieldname))) { fieldname = "??"; } diff --git a/src/coreclr/vm/fieldmarshaler.cpp b/src/coreclr/vm/fieldmarshaler.cpp index 192d27508e68c1..81b52188444982 100644 --- a/src/coreclr/vm/fieldmarshaler.cpp +++ b/src/coreclr/vm/fieldmarshaler.cpp @@ -402,7 +402,7 @@ UINT32 NativeFieldDescriptor::AlignmentRequirement() const MethodTable* pMT = GetNestedNativeMethodTable(); if (pMT->IsBlittable()) { - return pMT->GetLayoutInfo()->m_ManagedLargestAlignmentRequirementOfAllMembers; + return pMT->GetLayoutInfo()->GetAlignmentRequirement(); } return pMT->GetNativeLayoutInfo()->GetLargestAlignmentRequirement(); } diff --git a/src/coreclr/vm/fieldmarshaler.h b/src/coreclr/vm/fieldmarshaler.h index fa778f3ba2f337..568322cab21106 100644 --- a/src/coreclr/vm/fieldmarshaler.h +++ b/src/coreclr/vm/fieldmarshaler.h @@ -188,26 +188,6 @@ VOID ParseNativeType(Module* pModule, #endif ); -//======================================================================= -// The classloader stores an intermediate representation of the layout -// metadata in an array of these structures. The dual-pass nature -// is a bit extra overhead but building this structure requiring loading -// other classes (for nested structures) and I'd rather keep this -// next to the other places where we load other classes (e.g. the superclass -// and implemented interfaces.) -// -// Each redirected field gets one entry in LayoutRawFieldInfo. -// The array is terminated by one dummy record whose m_MD == mdMemberDefNil. -//======================================================================= -struct LayoutRawFieldInfo -{ - mdFieldDef m_MD; // mdMemberDefNil for end of array - ULONG m_sequence; // sequence # from metadata - RawFieldPlacementInfo m_placement; - NativeFieldDescriptor m_nfd; -}; - - class EEClassNativeLayoutInfo { private: diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 5b00eab6389d96..2eca711b4cb516 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -1954,12 +1954,12 @@ unsigned CEEInfo::getClassAlignmentRequirementStatic(TypeHandle clsHnd) // if it's the unmanaged view of the managed type, we always use the unmanaged alignment requirement result = pMT->GetNativeLayoutInfo()->GetLargestAlignmentRequirement(); } - else if (pInfo->IsManagedSequential() || pInfo->IsBlittable()) + else if (pInfo->GetLayoutType() == EEClassLayoutInfo::LayoutType::Sequential || pInfo->IsBlittable()) { _ASSERTE(!pMT->ContainsGCPointers()); // if it's managed sequential, we use the managed alignment requirement - result = pInfo->m_ManagedLargestAlignmentRequirementOfAllMembers; + result = pInfo->GetAlignmentRequirement(); } } diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index 25c9cc28077574..53485bf3df4523 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -28,7 +28,6 @@ class FCallMethodDesc; class FieldDesc; class NDirect; class MethodDescChunk; -struct LayoutRawFieldInfo; class InstantiatedMethodDesc; class DictionaryLayout; class Dictionary; diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 422b4a95a19fa7..e04484a74274e6 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -8605,7 +8605,7 @@ int MethodTable::GetFieldAlignmentRequirement() { if (HasLayout()) { - return GetLayoutInfo()->m_ManagedLargestAlignmentRequirementOfAllMembers; + return GetLayoutInfo()->GetAlignmentRequirement(); } else if (GetClass()->HasCustomFieldAlignment()) { @@ -8629,7 +8629,7 @@ UINT32 MethodTable::GetNativeSize() CONTRACTL_END; if (IsBlittable()) { - return GetClass()->GetLayoutInfo()->GetManagedSize(); + return GetNumInstanceFieldBytes(); } return GetNativeLayoutInfo()->GetSize(); } diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 78009bcf95dd23..5dd3f85bdd5920 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -40,7 +40,6 @@ class EEClass; class EnCFieldDesc; class FieldDesc; class JIT_TrialAlloc; -struct LayoutRawFieldInfo; class MetaSig; class MethodDesc; class MethodDescChunk; diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 3d7e5478d08dad..44797143737284 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -736,7 +736,8 @@ void MethodTableBuilder::SetBMTData( bmtGCSeriesInfo *bmtGCSeries, bmtMethodImplInfo *bmtMethodImpl, const bmtGenericsInfo *bmtGenerics, - bmtEnumFieldInfo *bmtEnumFields) + bmtEnumFieldInfo *bmtEnumFields, + bmtLayoutInfo *bmtFieldLayout) { LIMITED_METHOD_CONTRACT; this->bmtAllocator = bmtAllocator; @@ -754,6 +755,7 @@ void MethodTableBuilder::SetBMTData( this->bmtMethodImpl = bmtMethodImpl; this->bmtGenerics = bmtGenerics; this->bmtEnumFields = bmtEnumFields; + this->bmtLayout = bmtFieldLayout; } //******************************************************************************* @@ -1165,7 +1167,6 @@ MethodTableBuilder::CopyParentVtable() // support. // If so: // - Update the NumInstanceFieldBytes on the bmtFieldPlacement. -// - Update the m_cbNativeSize and m_cbManagedSize if HasLayout() is true. // Return a BOOL result to indicate whether the size has been updated. // BOOL MethodTableBuilder::CheckIfSIMDAndUpdateSize() @@ -1203,12 +1204,6 @@ BOOL MethodTableBuilder::CheckIfSIMDAndUpdateSize() if (numInstanceFieldBytes != 16) { bmtFP->NumInstanceFieldBytes = numInstanceFieldBytes; - - if (HasLayout()) - { - GetLayoutInfo()->m_cbManagedSize = numInstanceFieldBytes; - } - return true; } #endif // TARGET_X86 || TARGET_AMD64 @@ -1282,7 +1277,7 @@ MethodTableBuilder::BuildMethodTableThrowing( Module * pModule, mdToken cl, BuildingInterfaceInfo_t * pBuildingInterfaceList, - const LayoutRawFieldInfo * pLayoutRawFieldInfos, + const bmtLayoutInfo * initialLayoutInfo, MethodTable * pParentMethodTable, const bmtGenericsInfo * bmtGenericsInfo, SigPointer parentInst, @@ -1315,7 +1310,8 @@ MethodTableBuilder::BuildMethodTableThrowing( new (GetStackingAllocator()) bmtGCSeriesInfo(), new (GetStackingAllocator()) bmtMethodImplInfo(), bmtGenericsInfo, - new (GetStackingAllocator()) bmtEnumFieldInfo(pModule->GetMDImport())); + new (GetStackingAllocator()) bmtEnumFieldInfo(pModule->GetMDImport()), + new (GetStackingAllocator()) bmtLayoutInfo(*initialLayoutInfo)); //Initialize structs @@ -1738,7 +1734,7 @@ MethodTableBuilder::BuildMethodTableThrowing( MethodTable ** pByValueClassCache = NULL; // Go thru all fields and initialize their FieldDescs. - InitializeFieldDescs(GetApproxFieldDescListRaw(), pLayoutRawFieldInfos, bmtInternal, bmtGenerics, + InitializeFieldDescs(GetApproxFieldDescListRaw(), bmtInternal, bmtGenerics, bmtMetaData, bmtEnumFields, bmtError, &pByValueClassCache, bmtMFDescs, bmtFP, &totalDeclaredFieldSize); @@ -1791,55 +1787,7 @@ MethodTableBuilder::BuildMethodTableThrowing( GetNumStaticFields(), GetNumHandleRegularStatics() + GetNumHandleThreadStatics(), pszDebugName)); - if (IsBlittable() || IsManagedSequential()) - { - bmtFP->NumGCPointerSeries = 0; - bmtFP->NumInstanceGCPointerFields = 0; - - _ASSERTE(HasLayout()); - - if (bmtFP->NumInlineArrayElements != 0) - { - INT64 extendedSize = (INT64)GetLayoutInfo()->m_cbManagedSize * (INT64)bmtFP->NumInlineArrayElements; - if (extendedSize > FIELD_OFFSET_LAST_REAL_OFFSET) - { - BuildMethodTableThrowException(IDS_CLASSLOAD_FIELDTOOLARGE); - } - - GetLayoutInfo()->m_cbManagedSize = (UINT32)extendedSize; - } - - bmtFP->NumInstanceFieldBytes = GetLayoutInfo()->m_cbManagedSize; - - // For simple Blittable types we still need to check if they have any overlapping - // fields and call the method SetHasOverlaidFields() when they are detected. - // - if (HasExplicitFieldOffsetLayout()) - { - _ASSERTE(!bmtGenerics->fContainsGenericVariables); // A simple Blittable type can't ever be an open generic type. - HandleExplicitLayout(pByValueClassCache); - } - } - else - { - _ASSERTE(!IsBlittable()); - // HandleExplicitLayout fails for the GenericTypeDefinition when - // it will succeed for some particular instantiations. - // Thus we only do explicit layout for real instantiations, e.g. C, not - // the open types such as the GenericTypeDefinition C or any - // of the "fake" types involving generic type variables which are - // used for reflection and verification, e.g. C>. - // - if (!bmtGenerics->fContainsGenericVariables && HasExplicitFieldOffsetLayout()) - { - HandleExplicitLayout(pByValueClassCache); - } - else - { - // Place instance fields - PlaceInstanceFields(pByValueClassCache); - } - } + PlaceInstanceFields(pByValueClassCache); if (IsValueClass()) { @@ -3583,6 +3531,14 @@ MethodTableBuilder::EnumerateClassFields() BuildMethodTableThrowException(hr, *bmtError); } + // Variant delegates should not have any instance fields of the variant. + // type parameter. For now, we just completely disallow all fields even + // if they are non-variant or static, as it is not a useful scenario. + if ((hEnumField.EnumGetCount() != 0) && IsDelegate() && (bmtGenerics->pVarianceInfo != NULL)) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_VARIANCE_IN_DELEGATE); + } + bmtMetaData->cFields = hEnumField.EnumGetCount(); // Retrieve the fields and store them in a temp array. @@ -3862,19 +3818,6 @@ VOID MethodTableBuilder::AllocateFieldDescs() } } -#ifdef FEATURE_DOUBLE_ALIGNMENT_HINT -//******************************************************************************* -// -// Heuristic to determine if we should have instances of this class 8 byte aligned -// -BOOL MethodTableBuilder::ShouldAlign8(DWORD dwR8Fields, DWORD dwTotalFields) -{ - LIMITED_METHOD_CONTRACT; - - return dwR8Fields*2>dwTotalFields && dwR8Fields>=2; -} -#endif - //******************************************************************************* BOOL MethodTableBuilder::IsSelfReferencingStaticValueTypeField(mdToken dwByValueClassToken, bmtInternalInfo* bmtInternal, @@ -3940,7 +3883,6 @@ static BOOL IsSelfRef(MethodTable * pMT) // Go thru all fields and initialize their FieldDescs. // VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, - const LayoutRawFieldInfo* pLayoutRawFieldInfos, bmtInternalInfo* bmtInternal, const bmtGenericsInfo* bmtGenerics, bmtMetaDataInfo* bmtMetaData, @@ -3979,13 +3921,6 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, DWORD dwCurrentStaticField = 0; DWORD dwCurrentThreadStaticField = 0; - - DWORD dwR8Fields = 0; // Number of R8's the class has - -#ifdef FEATURE_64BIT_ALIGNMENT - // Track whether any field in this type requires 8-byte alignment - BOOL fFieldRequiresAlign8 = HasParent() ? GetParentMethodTable()->RequiresAlign8() : FALSE; -#endif #if defined(FEATURE_METADATA_UPDATER) bool isEnCField = pFieldDescList != NULL && pFieldDescList->IsEnCNew(); #else @@ -4126,23 +4061,11 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, } case ELEMENT_TYPE_R8: - { - dwR8Fields++; - - // Deliberate fall through... - FALLTHROUGH; - } - case ELEMENT_TYPE_I8: case ELEMENT_TYPE_U8: IN_TARGET_64BIT(case ELEMENT_TYPE_I:) IN_TARGET_64BIT(case ELEMENT_TYPE_U:) { -#ifdef FEATURE_64BIT_ALIGNMENT - // Record that this field requires alignment for Int64/UInt64. - if(!fIsStatic) - fFieldRequiresAlign8 = true; -#endif dwLog2FieldSize = 3; break; } @@ -4371,12 +4294,6 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, // Inherit instance attributes EEClass * pFieldClass = pByValueClass->GetClass(); -#ifdef FEATURE_64BIT_ALIGNMENT - // If a value type requires 8-byte alignment this requirement must be inherited by any - // class/struct that embeds it as a field. - if (pFieldClass->IsAlign8Candidate()) - fFieldRequiresAlign8 = true; -#endif if (pFieldClass->HasNonPublicFields()) SetHasNonPublicFields(); if (pFieldClass->HasFieldsWhichMustBeInited()) @@ -4454,22 +4371,6 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, bmtMFDescs->ppFieldDescList[i] = pFD; - const LayoutRawFieldInfo *pLayoutFieldInfo = NULL; - - if (HasLayout()) - { - const LayoutRawFieldInfo *pwalk = pLayoutRawFieldInfos; - while (pwalk->m_MD != mdFieldDefNil) - { - if (pwalk->m_MD == bmtMetaData->pFields[i]) - { - pLayoutFieldInfo = pwalk; - break; - } - pwalk++; - } - } - LPCSTR pszFieldName = NULL; #ifdef _DEBUG if (FAILED(pInternalImport->GetNameOfFieldDef(bmtMetaData->pFields[i], &pszFieldName))) @@ -4492,31 +4393,12 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, // if (fIsByValue) { - if (!fIsStatic && - (IsBlittable() || HasExplicitFieldOffsetLayout())) - { - (DWORD_PTR &)pFD->m_pMTOfEnclosingClass = - (*pByValueClassCache)[dwCurrentDeclaredField]->GetNumInstanceFieldBytes(); - - if (pLayoutFieldInfo) - IfFailThrow(pFD->SetOffset(pLayoutFieldInfo->m_placement.m_offset)); - else - pFD->SetOffset(FIELD_OFFSET_VALUE_CLASS); - } - else if (!fIsStatic && IsManagedSequential()) - { - (DWORD_PTR &)pFD->m_pMTOfEnclosingClass = - (*pByValueClassCache)[dwCurrentDeclaredField]->GetNumInstanceFieldBytes(); - - IfFailThrow(pFD->SetOffset(pLayoutFieldInfo->m_placement.m_offset)); - } - else - { - // static value class fields hold a handle, which is ptr sized - // (instance field layout ignores this value) - (DWORD_PTR&)(pFD->m_pMTOfEnclosingClass) = LOG2_PTRSIZE; - pFD->SetOffset(FIELD_OFFSET_VALUE_CLASS); - } + pFD->SetOffset(FIELD_OFFSET_VALUE_CLASS); + DWORD_PTR& fieldSizeStorage = (DWORD_PTR&)(pFD->m_pMTOfEnclosingClass); + // static value class fields hold a handle, which is ptr sized + fieldSizeStorage = fIsStatic + ? LOG2_PTRSIZE + : (*pByValueClassCache)[dwCurrentDeclaredField]->GetNumInstanceFieldBytes(); } else { @@ -4525,14 +4407,7 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, // -1 (FIELD_OFFSET_UNPLACED) means that this is a non-GC field that has not yet been placed // -2 (FIELD_OFFSET_UNPLACED_GC_PTR) means that this is a GC pointer field that has not yet been placed - // If there is any kind of explicit layout information for this field, use it. If not, then - // mark it as either GC or non-GC and as unplaced; it will get placed later on in an optimized way. - - if ((IsBlittable() || HasExplicitFieldOffsetLayout()) && !fIsStatic) - IfFailThrow(pFD->SetOffset(pLayoutFieldInfo->m_placement.m_offset)); - else if (IsManagedSequential() && !fIsStatic) - IfFailThrow(pFD->SetOffset(pLayoutFieldInfo->m_placement.m_offset)); - else if (bCurrentFieldIsObjectRef) + if (bCurrentFieldIsObjectRef) pFD->SetOffset(FIELD_OFFSET_UNPLACED_GC_PTR); else pFD->SetOffset(FIELD_OFFSET_UNPLACED); @@ -4674,31 +4549,6 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, GetHalfBakedClass()->SetHasFixedAddressVTStatics(); } -#ifdef FEATURE_64BIT_ALIGNMENT - // For types with layout we drop any 64-bit alignment requirement if the packing size was less than 8 - // bytes (this mimics what the native compiler does and ensures we match up calling conventions during - // interop). - // We don't do this for types that are marked as sequential but end up with auto-layout due to containing pointers, - // as auto-layout ignores any Pack directives. - if (HasLayout() && (HasExplicitFieldOffsetLayout() || IsManagedSequential()) && GetLayoutInfo()->GetPackingSize() < 8) - { - fFieldRequiresAlign8 = false; - } - - if (fFieldRequiresAlign8) - { - SetAlign8Candidate(); - } -#endif // FEATURE_64BIT_ALIGNMENT - -#ifdef FEATURE_DOUBLE_ALIGNMENT_HINT - if (ShouldAlign8(dwR8Fields, dwNumInstanceFields)) - { - SetAlign8Candidate(); - } -#endif // FEATURE_DOUBLE_ALIGNMENT_HINT - - //======================================================================== // END: // Go thru all fields and initialize their FieldDescs. @@ -8287,7 +8137,148 @@ VOID MethodTableBuilder::PlaceThreadStaticFields() // // Place instance fields // -VOID MethodTableBuilder::PlaceInstanceFields(MethodTable ** pByValueClassCache) +VOID MethodTableBuilder::PlaceInstanceFields(MethodTable** pByValueClassCache) +{ + MethodTable* pParentMT = GetParentMethodTable(); + bool hasNonTrivialParent = pParentMT && !pParentMT->IsObjectClass() && !pParentMT->IsValueTypeClass(); + + if (bmtLayout->layoutType == EEClassLayoutInfo::LayoutType::Auto) + { + // Auto layout has been requested. + // We never switch away from auto layout, so just go use it right away. + +#if defined(FEATURE_64BIT_ALIGNMENT) || defined(FEATURE_DOUBLE_ALIGNMENT_HINT) + // Check for 8-byte alignment requirements for this type. + // We don't need to check any of the other nested field flags + // for auto layout, so only check this flag when targeting + // a platform that can have the align8 requirement for a type. + EEClassLayoutInfo::NestedFieldFlags nestedFieldFlags = + EEClassLayoutInfo::GetNestedFieldFlags( + GetModule(), + GetHalfBakedClass()->GetFieldDescList(), + bmtEnumFields->dwNumDeclaredFields, + bmtLayout->nlFlags, + pByValueClassCache); + + bool isAlign8 = ((nestedFieldFlags & EEClassLayoutInfo::NestedFieldFlags::Align8) == EEClassLayoutInfo::NestedFieldFlags::Align8) +#if defined(FEATURE_64BIT_ALIGNMENT) + || (pParentMT && pParentMT->RequiresAlign8()) +#endif // FEATURE_64BIT_ALIGNMENT + ; + + if (isAlign8) + { + GetHalfBakedClass()->SetAlign8Candidate(); + } +#endif // FEATURE_64BIT_ALIGNMENT || FEATURE_DOUBLE_ALIGNMENT_HINT + + HandleAutoLayout(pByValueClassCache); + return; + } + + // We are not using auto layout, so we need to check all of the nested field flags. + // All other layouts need to consider these flags. + EEClassLayoutInfo::NestedFieldFlags nestedFieldFlags = + EEClassLayoutInfo::GetNestedFieldFlags( + GetModule(), + GetHalfBakedClass()->GetFieldDescList(), + bmtEnumFields->dwNumDeclaredFields, + bmtLayout->nlFlags, + pByValueClassCache); + + bool hasGCFields = (pParentMT && pParentMT->ContainsGCPointers()) + || ((nestedFieldFlags & EEClassLayoutInfo::NestedFieldFlags::GCPointer) == EEClassLayoutInfo::NestedFieldFlags::GCPointer); + + bool isBlittable = ((nestedFieldFlags & EEClassLayoutInfo::NestedFieldFlags::NonBlittable) != EEClassLayoutInfo::NestedFieldFlags::NonBlittable); + if (hasNonTrivialParent) + { + isBlittable &= pParentMT->IsBlittable() == TRUE; + } + + bool isAutoLayoutOrHasAutoLayoutField = ((nestedFieldFlags & EEClassLayoutInfo::NestedFieldFlags::AutoLayout) == EEClassLayoutInfo::NestedFieldFlags::AutoLayout); + if (hasNonTrivialParent) + { + isAutoLayoutOrHasAutoLayoutField &= pParentMT->IsAutoLayoutOrHasAutoLayoutField() == TRUE; + } + + bool hasInt128Field = (pParentMT && pParentMT->IsInt128OrHasInt128Fields()) + || ((nestedFieldFlags & EEClassLayoutInfo::NestedFieldFlags::Int128) == EEClassLayoutInfo::NestedFieldFlags::Int128); + + bool isAlign8 = ((nestedFieldFlags & EEClassLayoutInfo::NestedFieldFlags::Align8) == EEClassLayoutInfo::NestedFieldFlags::Align8) +#if defined(FEATURE_64BIT_ALIGNMENT) + || (pParentMT && pParentMT->RequiresAlign8()) +#endif // FEATURE_64BIT_ALIGNMENT + ; + + _ASSERTE(HasLayout()); + + EEClassLayoutInfo* pLayoutInfo = GetLayoutInfo(); + pLayoutInfo->SetIsBlittable(isBlittable ? TRUE : FALSE); + pLayoutInfo->SetHasAutoLayoutField(isAutoLayoutOrHasAutoLayoutField ? TRUE : FALSE); + pLayoutInfo->SetIsInt128OrHasInt128Fields(hasInt128Field ? TRUE : FALSE); + pLayoutInfo->SetHasExplicitSize(bmtLayout->classSize); + + if (bmtLayout->layoutType == EEClassLayoutInfo::LayoutType::Sequential) + { + if (hasNonTrivialParent && !pParentMT->IsManagedSequential()) + { + // If the parent type is not Object, ValueType or Sequential, then we need to use Auto layout. + bmtLayout->layoutType = EEClassLayoutInfo::LayoutType::Auto; + } + + if (hasGCFields) + { + // If this type has GC fields, we will use Auto layout instead of Sequential layout. + bmtLayout->layoutType = EEClassLayoutInfo::LayoutType::Auto; + } + } + + if (bmtLayout->layoutType == EEClassLayoutInfo::LayoutType::Auto) + { + if (isAlign8) + { + GetHalfBakedClass()->SetAlign8Candidate(); + } + HandleAutoLayout(pByValueClassCache); + return; + } + + // For types with layout we drop any 64-bit alignment requirement if the packing size was less than 8 + // bytes (this mimics what the native compiler does and ensures we match up calling conventions during + // interop). + // We don't do this for types that are marked as sequential but end up with auto-layout due to containing pointers, + // as auto-layout ignores any Pack directives. + if (bmtLayout->packingSize < 8) + { + isAlign8 = false; + } + + if (isAlign8) + { + GetHalfBakedClass()->SetAlign8Candidate(); + } + + if (!hasGCFields) + { + bmtFP->NumGCPointerSeries = 0; + bmtFP->NumInstanceGCPointerFields = 0; + } + + switch (bmtLayout->layoutType) + { + case EEClassLayoutInfo::LayoutType::Sequential: + HandleSequentialLayout(pByValueClassCache); + break; + case EEClassLayoutInfo::LayoutType::Explicit: + HandleExplicitLayout(pByValueClassCache); + break; + default: + UNREACHABLE(); + break; + } +} + +VOID MethodTableBuilder::HandleAutoLayout(MethodTable ** pByValueClassCache) { STANDARD_VM_CONTRACT; @@ -8392,7 +8383,7 @@ VOID MethodTableBuilder::PlaceInstanceFields(MethodTable ** pByValueClassCach break; // TODO: since we will refuse to place GC references we should filter them out here. // otherwise the "back-filling" process stops completely. - // (PlaceInstanceFields) + // (HandleAutoLayout) // the following code would fix the issue (a replacement for the code above this comment): // if (bmtFP->NumInstanceFieldsOfSize[j] != 0 && // (j != LOG2SLOT || bmtFP->NumInstanceFieldsOfSize[j] > bmtFP->NumInstanceGCPointerFields)) @@ -8665,6 +8656,85 @@ VOID MethodTableBuilder::PlaceInstanceFields(MethodTable ** pByValueClassCach //=============================================================== } +VOID MethodTableBuilder::HandleSequentialLayout(MethodTable** pByValueClassCache) +{ + STANDARD_VM_CONTRACT; + + _ASSERTE(HasLayout()); + + EEClassLayoutInfo* pLayoutInfo = GetLayoutInfo(); + + CONSISTENCY_CHECK(pLayoutInfo != nullptr); + + bmtFP->NumInstanceFieldBytes = pLayoutInfo->InitializeSequentialFieldLayout( + GetHalfBakedClass()->GetFieldDescList(), + pByValueClassCache, + bmtEnumFields->dwNumDeclaredFields, + bmtLayout->packingSize, + bmtLayout->classSize, + GetParentMethodTable() + ); + + // Handle InlineArray element layout + if (bmtFP->NumInlineArrayElements != 0) + { + INT64 extendedSize = (INT64)bmtFP->NumInstanceFieldBytes * (INT64)bmtFP->NumInlineArrayElements; + if (extendedSize > FIELD_OFFSET_LAST_REAL_OFFSET) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_FIELDTOOLARGE); + } + bmtFP->NumInstanceFieldBytes = (DWORD)extendedSize; + } +} + +VOID MethodTableBuilder::HandleExplicitLayout(MethodTable** pByValueClassCache) +{ + STANDARD_VM_CONTRACT; + + _ASSERTE(HasLayout()); + + EEClassLayoutInfo* pLayoutInfo = GetLayoutInfo(); + + CONSISTENCY_CHECK(pLayoutInfo != nullptr); + + bmtFP->NumInstanceFieldBytes = pLayoutInfo->InitializeExplicitFieldLayout( + GetHalfBakedClass()->GetFieldDescList(), + pByValueClassCache, + bmtEnumFields->dwNumDeclaredFields, + bmtLayout->packingSize, + bmtLayout->classSize, + GetParentMethodTable(), + GetModule(), + GetCl() + ); + + // Handle InlineArray element layout + if (bmtFP->NumInlineArrayElements != 0) + { + INT64 extendedSize = (INT64)bmtFP->NumInstanceFieldBytes * (INT64)bmtFP->NumInlineArrayElements; + if (extendedSize > FIELD_OFFSET_LAST_REAL_OFFSET) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_FIELDTOOLARGE); + } + bmtFP->NumInstanceFieldBytes = (DWORD)extendedSize; + } + + // ValidateExplicitLayout fails for the GenericTypeDefinition when + // it will succeed for some particular instantiations. + // Thus we only do explicit layout for real instantiations, e.g. C, not + // the open types such as the GenericTypeDefinition C or any + // of the "fake" types involving generic type variables which are + // used for reflection and verification, e.g. C>. + // + if (!bmtGenerics->fContainsGenericVariables) + { + // For simple Blittable types we still need to check if they have any overlapping + // fields and call the method SetHasOverlaidFields() when they are detected, + // so we do this for Explicit layout whether or not there's any GC fields. + ValidateExplicitLayout(pByValueClassCache); + } +} + //******************************************************************************* // this accesses the field size which is temporarily stored in m_pMTOfEnclosingClass // during class loading. Don't use any other time @@ -8751,7 +8821,7 @@ void MethodTableBuilder::StoreEightByteClassification(SystemVStructRegisterPassi // for object ref fields so we don't need to try to align it // VOID -MethodTableBuilder::HandleExplicitLayout( +MethodTableBuilder::ValidateExplicitLayout( MethodTable ** pByValueClassCache) { STANDARD_VM_CONTRACT; @@ -9029,7 +9099,7 @@ MethodTableBuilder::HandleExplicitLayout( else { // align up to the alignment requirements of the members of this value type. - numInstanceFieldBytes.AlignUp(GetLayoutInfo()->m_ManagedLargestAlignmentRequirementOfAllMembers); + numInstanceFieldBytes.AlignUp(GetLayoutInfo()->GetAlignmentRequirement()); if (numInstanceFieldBytes.IsOverflow()) { // addition overflow or cast truncation @@ -9073,7 +9143,7 @@ MethodTableBuilder::HandleExplicitLayout( BuildMethodTableThrowException(hr, *bmtError); } } -} // MethodTableBuilder::HandleExplicitLayout +} // MethodTableBuilder::ValidateExplicitLayout //******************************************************************************* // make sure that no object fields are overlapped incorrectly, returns the trust level @@ -10222,16 +10292,16 @@ void MethodTableBuilder::CheckForSystemTypes() // The System V ABI for i386 defaults to 8-byte alignment for __m64, except for parameter passing, // where it has an alignment of 4. - pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 8; // sizeof(__m64) + pLayout->SetAlignmentRequirement(8); // sizeof(__m64) } else if (strcmp(name, g_Vector128Name) == 0) { #ifdef TARGET_ARM // The Procedure Call Standard for ARM defaults to 8-byte alignment for __m128 - pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 8; + pLayout->SetAlignmentRequirement(8); #else - pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 16; // sizeof(__m128) + pLayout->SetAlignmentRequirement(16); // sizeof(__m128) #endif // TARGET_ARM } else if (strcmp(name, g_Vector256Name) == 0) @@ -10240,22 +10310,22 @@ void MethodTableBuilder::CheckForSystemTypes() // No such type exists for the Procedure Call Standard for ARM. We will default // to the same alignment as __m128, which is supported by the ABI. - pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 8; + pLayout->SetAlignmentRequirement(8); #elif defined(TARGET_ARM64) // The Procedure Call Standard for ARM 64-bit (with SVE support) defaults to // 16-byte alignment for __m256. - pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 16; + pLayout->SetAlignmentRequirement(16); #elif defined(TARGET_LOONGARCH64) // TODO-LoongArch64: Update alignment to proper value when implement LoongArch64 intrinsic. - pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 16; + pLayout->SetAlignmentRequirement(16); #elif defined(TARGET_RISCV64) // TODO-RISCV64: Update alignment to proper value when we implement RISC-V intrinsic. // RISC-V Vector Extenstion Intrinsic Document // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/vector_type_infos.adoc - pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 16; + pLayout->SetAlignmentRequirement(16); #else - pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 32; // sizeof(__m256) + pLayout->SetAlignmentRequirement(32); // sizeof(__m256) #endif // TARGET_ARM elif TARGET_ARM64 } else if (strcmp(name, g_Vector512Name) == 0) @@ -10264,23 +10334,23 @@ void MethodTableBuilder::CheckForSystemTypes() // No such type exists for the Procedure Call Standard for ARM. We will default // to the same alignment as __m128, which is supported by the ABI. - pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 8; + pLayout->SetAlignmentRequirement(8); #elif defined(TARGET_ARM64) // The Procedure Call Standard for ARM 64-bit (with SVE support) defaults to // 16-byte alignment for __m256. - pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 16; + pLayout->SetAlignmentRequirement(16); #elif defined(TARGET_LOONGARCH64) // TODO-LoongArch64: Update alignment to proper value when implement LoongArch64 intrinsic. - pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 16; + pLayout->SetAlignmentRequirement(16); #elif defined(TARGET_RISCV64) // TODO-RISCV64: Update alignment to proper value when we implement RISC-V intrinsic. // RISC-V Vector Extenstion Intrinsic Document // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/vector_type_infos.adoc - pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 16; + pLayout->SetAlignmentRequirement(16); #else - pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 64; // sizeof(__m512) + pLayout->SetAlignmentRequirement(64); // sizeof(__m512) #endif // TARGET_ARM elif TARGET_ARM64 } else @@ -10361,7 +10431,7 @@ void MethodTableBuilder::CheckForSystemTypes() case ELEMENT_TYPE_R8: { EEClassLayoutInfo * pLayout = pClass->GetLayoutInfo(); - pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 4; + pLayout->SetAlignmentRequirement(4); break; } @@ -10402,7 +10472,7 @@ void MethodTableBuilder::CheckForSystemTypes() // No such type exists for the Procedure Call Standard for ARM. We will default // to the same alignment as __m128, which is supported by the ABI. - pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 8; + pLayout->SetAlignmentRequirement(8); #elif defined(TARGET_64BIT) || defined(TARGET_X86) // These types correspond to fundamental data types in the underlying ABIs: @@ -10413,9 +10483,9 @@ void MethodTableBuilder::CheckForSystemTypes() // On Windows, no standard for Int128 has been established yet, // although applying 16 byte alignment is consistent with treatment of 128 bit SSE types // even on X86 - pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 16; // sizeof(__int128) + pLayout->SetAlignmentRequirement(16); // sizeof(__int128) #elif defined(TARGET_WASM) - pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 16; // sizeof(v128) + pLayout->SetAlignmentRequirement(16); // sizeof(v128) #else #error Unknown architecture #endif // TARGET_64BIT @@ -12341,7 +12411,7 @@ MethodTableBuilder::GatherGenericsInfo( // *pPackingSize declared packing size // *pfExplicitoffsets offsets explicit in metadata or computed? //======================================================================= -BOOL HasLayoutMetadata(Assembly* pAssembly, IMDInternalImport* pInternalImport, mdTypeDef cl, MethodTable* pParentMT, BYTE* pPackingSize, BYTE* pNLTType, BOOL* pfExplicitOffsets) +BOOL HasLayoutMetadata(Assembly* pAssembly, IMDInternalImport* pInternalImport, mdTypeDef cl, MethodTable* pParentMT, BYTE* pPackingSize, ULONG* pClassSize, CorNativeLinkType* pNLTType, BOOL* pfExplicitOffsets) { CONTRACTL { @@ -12350,6 +12420,7 @@ BOOL HasLayoutMetadata(Assembly* pAssembly, IMDInternalImport* pInternalImport, MODE_ANY; PRECONDITION(CheckPointer(pInternalImport)); PRECONDITION(CheckPointer(pPackingSize)); + PRECONDITION(CheckPointer(pClassSize)); PRECONDITION(CheckPointer(pNLTType)); PRECONDITION(CheckPointer(pfExplicitOffsets)); } @@ -12417,6 +12488,11 @@ BOOL HasLayoutMetadata(Assembly* pAssembly, IMDInternalImport* pInternalImport, pAssembly->ThrowTypeLoadException(pInternalImport, cl, IDS_CLASSLOAD_BADFORMAT); } + if (FAILED(pInternalImport->GetClassTotalSize(cl, pClassSize))) + { + *pClassSize = 0; + } + *pPackingSize = (BYTE)dwPackSize; return TRUE; @@ -12453,7 +12529,6 @@ ClassLoader::CreateTypeHandleForTypeDefThrowing( DWORD cInterfaces; BuildingInterfaceInfo_t * pInterfaceBuildInfo = NULL; IMDInternalImport * pInternalImport = NULL; - LayoutRawFieldInfo * pLayoutRawFieldInfos = NULL; MethodTableBuilder::bmtGenericsInfo genericsInfo; Assembly * pAssembly = pModule->GetAssembly(); @@ -12516,8 +12591,8 @@ ClassLoader::CreateTypeHandleForTypeDefThrowing( GetEnclosingClassThrowing(pInternalImport, pModule, cl, &tdEnclosing); - BYTE nstructPackingSize = 0, nstructNLT = 0; BOOL fExplicitOffsets = FALSE; + MethodTableBuilder::bmtLayoutInfo layoutInfo; // NOTE: HasLayoutMetadata does not load classes BOOL fHasLayout = !genericsInfo.fContainsGenericVariables && @@ -12526,10 +12601,22 @@ ClassLoader::CreateTypeHandleForTypeDefThrowing( pInternalImport, cl, pParentMethodTable, - &nstructPackingSize, - &nstructNLT, + &layoutInfo.packingSize, + &layoutInfo.classSize, + &layoutInfo.nlFlags, &fExplicitOffsets); + if (fHasLayout) + { + layoutInfo.layoutType = fExplicitOffsets + ? EEClassLayoutInfo::LayoutType::Explicit + : EEClassLayoutInfo::LayoutType::Sequential; + } + else + { + layoutInfo.layoutType = EEClassLayoutInfo::LayoutType::Auto; + } + BOOL fIsEnum = ((g_pEnumClass != NULL) && (pParentMethodTable == g_pEnumClass)); // enums may not have layout because they derive from g_pEnumClass and that has no layout @@ -12680,64 +12767,22 @@ ClassLoader::CreateTypeHandleForTypeDefThrowing( _ASSERTE(i == cInterfaces); } - if (fHasLayout || - /* Variant delegates should not have any instance fields of the variant. - type parameter. For now, we just completely disallow all fields even - if they are non-variant or static, as it is not a useful scenario. - @TODO: A more logical place for this check would be in - MethodTableBuilder::EnumerateClassMembers() */ - (fIsDelegate && genericsInfo.pVarianceInfo)) + if (fHasLayout) { - // check for fields and variance ULONG cFields; HENUMInternalHolder hEnumField(pInternalImport); hEnumField.EnumInit(mdtFieldDef, cl); cFields = pInternalImport->EnumGetCount(&hEnumField); - - if ((cFields != 0) && fIsDelegate && (genericsInfo.pVarianceInfo != NULL)) + // Though we fail on this condition, we should never run into it. + CONSISTENCY_CHECK(layoutInfo.packingSize != 0); + // MD Val check: PackingSize + if((layoutInfo.packingSize == 0) || + (layoutInfo.packingSize > 128) || + (layoutInfo.packingSize & (layoutInfo.packingSize-1))) { - pAssembly->ThrowTypeLoadException(pInternalImport, cl, IDS_CLASSLOAD_VARIANCE_IN_DELEGATE); - } - - if (fHasLayout) - { - // Though we fail on this condition, we should never run into it. - CONSISTENCY_CHECK(nstructPackingSize != 0); - // MD Val check: PackingSize - if((nstructPackingSize == 0) || - (nstructPackingSize > 128) || - (nstructPackingSize & (nstructPackingSize-1))) - { - THROW_BAD_FORMAT_MAYBE(!"ClassLayout:Invalid PackingSize", BFA_BAD_PACKING_SIZE, pModule); - pAssembly->ThrowTypeLoadException(pInternalImport, cl, IDS_CLASSLOAD_BADFORMAT); - } - - pLayoutRawFieldInfos = (LayoutRawFieldInfo *)pStackingAllocator->Alloc( - (S_UINT32(1) + S_UINT32(cFields)) * S_UINT32(sizeof(LayoutRawFieldInfo))); - - { - // Warning: this can load classes - CONTRACT_VIOLATION(LoadsTypeViolation); - - // Set a flag that allows us to break dead-locks that are result of the LoadsTypeViolation - ThreadStateNCStackHolder tsNC(TRUE, Thread::TSNC_LoadsTypeViolation); - - EEClassLayoutInfo::CollectLayoutFieldMetadataThrowing( - cl, - nstructPackingSize, - nstructNLT, - fExplicitOffsets, - pParentMethodTable, - cFields, - &hEnumField, - pModule, - &genericsInfo.typeContext, - &(((LayoutEEClass *)pClass)->m_LayoutInfo), - pLayoutRawFieldInfos, - pAllocator, - pamTracker); - } + THROW_BAD_FORMAT_MAYBE(!"ClassLayout:Invalid PackingSize", BFA_BAD_PACKING_SIZE, pModule); + pAssembly->ThrowTypeLoadException(pInternalImport, cl, IDS_CLASSLOAD_BADFORMAT); } } @@ -12756,7 +12801,7 @@ ClassLoader::CreateTypeHandleForTypeDefThrowing( pModule, cl, pInterfaceBuildInfo, - pLayoutRawFieldInfos, + &layoutInfo, pParentMethodTable, &genericsInfo, parentInst, diff --git a/src/coreclr/vm/methodtablebuilder.h b/src/coreclr/vm/methodtablebuilder.h index 17e8a1fbe703d3..ebd987467d9c61 100644 --- a/src/coreclr/vm/methodtablebuilder.h +++ b/src/coreclr/vm/methodtablebuilder.h @@ -62,6 +62,23 @@ class MethodTableBuilder #endif //_DEBUG }; // struct bmtGenericsInfo + + struct bmtLayoutInfo + { + bmtLayoutInfo() + : nlFlags(nltNone), + packingSize(0), + layoutType(EEClassLayoutInfo::LayoutType::Auto) + { + LIMITED_METHOD_CONTRACT; + } + + CorNativeLinkType nlFlags; + BYTE packingSize; + ULONG classSize; + EEClassLayoutInfo::LayoutType layoutType; + }; + MethodTableBuilder( MethodTable * pHalfBakedMT, EEClass * pHalfBakedClass, @@ -101,7 +118,7 @@ class MethodTableBuilder Module * pModule, mdToken cl, BuildingInterfaceInfo_t * pBuildingInterfaceList, - const LayoutRawFieldInfo * pLayoutRawFieldInfos, + const bmtLayoutInfo * initialLayoutInfo, MethodTable * pParentMethodTable, const bmtGenericsInfo * bmtGenericsInfo, SigPointer parentInst, @@ -2269,6 +2286,7 @@ class MethodTableBuilder bmtMethodImplInfo *bmtMethodImpl; const bmtGenericsInfo *bmtGenerics; bmtEnumFieldInfo *bmtEnumFields; + bmtLayoutInfo* bmtLayout; void SetBMTData( LoaderAllocator *bmtAllocator = NULL, @@ -2285,7 +2303,8 @@ class MethodTableBuilder bmtGCSeriesInfo *bmtGCSeries = NULL, bmtMethodImplInfo *bmtMethodImpl = NULL, const bmtGenericsInfo *bmtGenerics = NULL, - bmtEnumFieldInfo *bmtEnumFields = NULL); + bmtEnumFieldInfo *bmtEnumFields = NULL, + bmtLayoutInfo *bmtLayout = NULL); // -------------------------------------------------------------------------------------------- // Returns the parent bmtRTType pointer. Can be null if no parent exists. @@ -2641,7 +2660,6 @@ class MethodTableBuilder VOID InitializeFieldDescs( FieldDesc *, - const LayoutRawFieldInfo*, bmtInternalInfo*, const bmtGenericsInfo*, bmtMetaDataInfo*, @@ -2900,9 +2918,7 @@ class MethodTableBuilder VOID PlaceThreadStaticFields(); - VOID - PlaceInstanceFields( - MethodTable **); + VOID PlaceInstanceFields(MethodTable** pByValueClassCache); BOOL CheckForVtsEventMethod( @@ -2951,7 +2967,17 @@ class MethodTableBuilder VOID SetFinalizationSemantics(); + VOID + HandleAutoLayout( + MethodTable **); + + VOID HandleSequentialLayout( + MethodTable **); + VOID HandleExplicitLayout( + MethodTable **); + + VOID ValidateExplicitLayout( MethodTable **pByValueClassCache); static ExplicitFieldTrust::TrustLevel CheckValueClassLayout( @@ -3011,11 +3037,6 @@ class MethodTableBuilder bmtMethodHandle hDeclMethod, bmtMethodHandle hImplMethod); - // Heuristic to detemine if we would like instances of this class 8 byte aligned - BOOL ShouldAlign8( - DWORD dwR8Fields, - DWORD dwTotalFields); - MethodTable * AllocateNewMT(Module *pLoaderModule, DWORD dwVtableSlots, DWORD dwVirtuals,