Repository: dotnet/runtime
Pull request #113454: Refactor Sequential/Explicit layout to happen in the same phase where we do Auto layout
Author: login: jkoritzinsky
Labels: name: area-VM-coreclr
Created: 2025-03-12T23:26:57Z | Merged: 2025-05-21T17:52:59Z

This will make it easier to implement https://github.com/dotnet/runtime/issues/100896


Diff:
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<UINT32>::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<BYTE>(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<UINT32>::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<ULONG>::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<uint32_t>::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<UINT32>::addition(pfwalk->m_placement.m_offset, parentSize, pfwalk->m_placement.m_offset))
+                COMPlusThrowOM();
+
+            uint32_t fieldEnd;
+            if (!ClrSafeInt<uint32_t>::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<uint32_t>::addition(cbCurOffset, (alignmentRequirement - (cbCurOffset % alignmentRequirement)) % alignmentRequirement, cbCurOffset))
+                COMPlusThrowOM();
+
+            placementInfo.m_offset = cbCurOffset;
+
+            if (!ClrSafeInt<uint32_t>::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<ULONG>::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<ULONG>::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<LayoutRawFieldInfo> pInfoArray = new LayoutRawFieldInfo[cFields + 1];
+    UINT32 numInstanceFields;
+    BYTE fieldsAlignmentRequirement;
+    InitializeLayoutFieldInfoArray(pFields, cFields, pByValueClassCache, packingSize, pInfoArray, &numInstanceFields, &fieldsAlignmentRequirement);
+
+    BYTE alignmentRequirement = max(max<BYTE>(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<LayoutRawFieldInfo> pInfoArray = new LayoutRawFieldInfo[cFields + 1];
+    UINT32 numInstanceFields;
+    BYTE fieldsAlignmentRequirement;
+    InitializeLayoutFieldInfoArray(pFields, cFields, pByValueClassCache, packingSize, pInfoArray, &numInstanceFields, &fieldsAlignmentRequirement);
+
+    BYTE alignmentRequirement = max(max<BYTE>(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<LayoutRawFieldInfo*> 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<BYTE>(1, parentAlignmentRequirement), fieldAlignmentRequirement);
 
     BOOL fExplicitOffsets = pMT->GetClass()->HasExplicitFieldOffsetLayout();
 
-    CQuickArray<LayoutRawFieldInfo*> 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<int>, not
-        // the open types such as the GenericTypeDefinition C<!0> or any
-        // of the "fake" types involving generic type variables which are
-        // used for reflection and verification, e.g. C<List<!0>>.
-        //
-        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<int>, not
+    // the open types such as the GenericTypeDefinition C<!0> or any
+    // of the "fake" types involving generic type variables which are
+    // used for reflection and verification, e.g. C<List<!0>>.
+    //
+    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,


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: 

Copilot reviewed 2 out of 12 changed files in this pull request and generated no comments.

<details>
<summary>Files not reviewed (10)</summary>

* **src/coreclr/vm/class.cpp**: Language not supported
* **src/coreclr/vm/class.h**: Language not supported
* **src/coreclr/vm/classcompat.h**: Language not supported
* **src/coreclr/vm/fieldmarshaler.cpp**: Language not supported
* **src/coreclr/vm/fieldmarshaler.h**: Language not supported
* **src/coreclr/vm/jitinterface.cpp**: Language not supported
* **src/coreclr/vm/method.hpp**: Language not supported
* **src/coreclr/vm/methodtable.cpp**: Language not supported
* **src/coreclr/vm/methodtable.h**: Language not supported
* **src/coreclr/vm/methodtablebuilder.h**: Language not supported
</details>



state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T20:54:25Z
user: login: AaronRobinsonMSFT
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-07T23:54:03Z
user: login: AaronRobinsonMSFT
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-08T00:32:18Z
user: login: AaronRobinsonMSFT
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-08T00:32:33Z
user: login: AaronRobinsonMSFT
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-08T00:37:19Z
user: login: AaronRobinsonMSFT
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-08T00:39:25Z
user: login: AaronRobinsonMSFT
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-08T00:41:47Z
user: login: AaronRobinsonMSFT
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-08T00:45:03Z
user: login: jkoritzinsky
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-08T17:51:57Z
user: login: jkoritzinsky
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-08T17:52:46Z
user: login: jkoritzinsky
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-08T17:56:18Z
user: login: davidwrighton
body: Looks like a very nice simplification of quite a lot of code. I only saw 1 very small nit, and 1 straightforward issue to fix.
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-20T20:54:28Z
user: login: davidwrighton
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-21T17:48:58Z

Review comments:
diff_hunk: @@ -8054,7 +7905,136 @@ VOID MethodTableBuilder::PlaceThreadStaticFields()
 //
 // Place instance fields
 //
-VOID    MethodTableBuilder::PlaceInstanceFields(MethodTable ** pByValueClassCache)
+VOID MethodTableBuilder::PlaceInstanceFields(MethodTable** pByValueClassCache)
+{
+#ifdef _DEBUG
+    LPCUTF8 szName;
+    LPCUTF8 szNamespace;
+    if (FAILED(GetMDImport()->GetNameOfTypeDef(GetCl(), &szName, &szNamespace)))
+    {
+        szName = szNamespace = "Invalid TypeDef record";
+    }
+
+    if (g_pConfig->ShouldBreakOnStructMarshalSetup(szName))
+        CONSISTENCY_CHECK_MSGF(false, ("BreakOnStructMarshalSetup: '%s' ", szName));
+#endif // _DEBUG
+
+    MethodTable* pParentMT = GetParentMethodTable();
+    bool hasNonTrivialParent = pParentMT && !pParentMT->IsObjectClass() && !pParentMT->IsValueTypeClass();
+
+    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)
+#ifdef FEATURE_64BIT_ALIGNMENT
+        || (pParentMT && pParentMT->RequiresAlign8())
+#endif // FEATURE_64BIT_ALIGNMENT
+        ;
+
+    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 (isAlign8)
+        {
+            GetHalfBakedClass()->SetAlign8Candidate();
+        }
+        HandleAutoLayout(pByValueClassCache);
+        return;
+    }
+
+    _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;
+    }
path: src/coreclr/vm/methodtablebuilder.cpp
user: login: AaronRobinsonMSFT
body: ```suggestion
    isAlign8 = !(bmtLayout->packingSize <= 8);
```
created_at: 2025-05-07T23:54:03Z
author_association: MEMBER
diff_hunk: @@ -8054,7 +7905,136 @@ VOID MethodTableBuilder::PlaceThreadStaticFields()
 //
 // Place instance fields
 //
-VOID    MethodTableBuilder::PlaceInstanceFields(MethodTable ** pByValueClassCache)
+VOID MethodTableBuilder::PlaceInstanceFields(MethodTable** pByValueClassCache)
+{
+#ifdef _DEBUG
+    LPCUTF8 szName;
+    LPCUTF8 szNamespace;
+    if (FAILED(GetMDImport()->GetNameOfTypeDef(GetCl(), &szName, &szNamespace)))
+    {
+        szName = szNamespace = "Invalid TypeDef record";
+    }
+
+    if (g_pConfig->ShouldBreakOnStructMarshalSetup(szName))
+        CONSISTENCY_CHECK_MSGF(false, ("BreakOnStructMarshalSetup: '%s' ", szName));
path: src/coreclr/vm/methodtablebuilder.cpp
user: login: AaronRobinsonMSFT
body: This break seems off. Is this only for marshal field placement?
created_at: 2025-05-08T00:32:18Z
author_association: MEMBER
diff_hunk: @@ -8054,7 +7905,136 @@ VOID MethodTableBuilder::PlaceThreadStaticFields()
 //
 // Place instance fields
 //
-VOID    MethodTableBuilder::PlaceInstanceFields(MethodTable ** pByValueClassCache)
+VOID MethodTableBuilder::PlaceInstanceFields(MethodTable** pByValueClassCache)
+{
+#ifdef _DEBUG
+    LPCUTF8 szName;
+    LPCUTF8 szNamespace;
+    if (FAILED(GetMDImport()->GetNameOfTypeDef(GetCl(), &szName, &szNamespace)))
+    {
+        szName = szNamespace = "Invalid TypeDef record";
+    }
+
+    if (g_pConfig->ShouldBreakOnStructMarshalSetup(szName))
+        CONSISTENCY_CHECK_MSGF(false, ("BreakOnStructMarshalSetup: '%s' ", szName));
path: src/coreclr/vm/methodtablebuilder.cpp
user: login: AaronRobinsonMSFT
body: If so, the name of the method should be updated.
created_at: 2025-05-08T00:32:32Z
author_association: MEMBER
diff_hunk: @@ -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();
path: src/coreclr/vm/classlayoutinfo.cpp
user: login: AaronRobinsonMSFT
body: ```suggestion
                    typeHandleMaybe = TypeHandle{};
```
created_at: 2025-05-08T00:37:19Z
author_association: MEMBER
diff_hunk: @@ -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_MD = pField->GetMemberDef();
path: src/coreclr/vm/classlayoutinfo.cpp
user: login: AaronRobinsonMSFT
body: Nit: Can we change `m_MD` to be `m_Token` instead?
created_at: 2025-05-08T00:39:25Z
author_association: MEMBER
diff_hunk: @@ -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_MD = 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
path: src/coreclr/vm/classlayoutinfo.cpp
user: login: AaronRobinsonMSFT
body: Is this doxygen? Do we have any other comments like this in runtime?
created_at: 2025-05-08T00:41:47Z
author_association: MEMBER
diff_hunk: @@ -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_MD = 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_MD) && (pfwalk->m_MD < 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_MD != fd) continue;
+
+            // ulOffset is the explicit offset
+            pfwalk->m_placement.m_offset = ulOffset;
+
+            // Treat base class as an initial member.
+            if (!ClrSafeInt<UINT32>::addition(pfwalk->m_placement.m_offset, parentSize, pfwalk->m_placement.m_offset))
+                COMPlusThrowOM();
+
+            uint32_t fieldEnd;
+            if (!ClrSafeInt<uint32_t>::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_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)
path: src/coreclr/vm/classlayoutinfo.cpp
user: login: AaronRobinsonMSFT
body: ```suggestion
                else if ((INT32)pFieldInfoArray[i].m_placement.m_offset < 0)
```

This casting doesn't make me all that happy. Can we define a maximum size instead?
created_at: 2025-05-08T00:45:02Z
author_association: MEMBER
diff_hunk: @@ -8054,7 +7905,136 @@ VOID MethodTableBuilder::PlaceThreadStaticFields()
 //
 // Place instance fields
 //
-VOID    MethodTableBuilder::PlaceInstanceFields(MethodTable ** pByValueClassCache)
+VOID MethodTableBuilder::PlaceInstanceFields(MethodTable** pByValueClassCache)
+{
+#ifdef _DEBUG
+    LPCUTF8 szName;
+    LPCUTF8 szNamespace;
+    if (FAILED(GetMDImport()->GetNameOfTypeDef(GetCl(), &szName, &szNamespace)))
+    {
+        szName = szNamespace = "Invalid TypeDef record";
+    }
+
+    if (g_pConfig->ShouldBreakOnStructMarshalSetup(szName))
+        CONSISTENCY_CHECK_MSGF(false, ("BreakOnStructMarshalSetup: '%s' ", szName));
path: src/coreclr/vm/methodtablebuilder.cpp
user: login: jkoritzinsky
body: Back before I started refactoring layout the first time, native and managed layout were done at the same time, so they both hit this. I've maintained that experience since. I can remove it here and keep it only for unmanaged layout.
created_at: 2025-05-08T17:51:57Z
author_association: MEMBER
diff_hunk: @@ -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_MD = 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
path: src/coreclr/vm/classlayoutinfo.cpp
user: login: jkoritzinsky
body: Yeah this is the doxygen format. Got auto-inserted and was useful for setting things up. I see usages in Mono and the repo minipal, but not in coreclr. I can remove them if you want
created_at: 2025-05-08T17:52:46Z
author_association: MEMBER
diff_hunk: @@ -8054,7 +7905,136 @@ VOID MethodTableBuilder::PlaceThreadStaticFields()
 //
 // Place instance fields
 //
-VOID    MethodTableBuilder::PlaceInstanceFields(MethodTable ** pByValueClassCache)
+VOID MethodTableBuilder::PlaceInstanceFields(MethodTable** pByValueClassCache)
+{
+#ifdef _DEBUG
+    LPCUTF8 szName;
+    LPCUTF8 szNamespace;
+    if (FAILED(GetMDImport()->GetNameOfTypeDef(GetCl(), &szName, &szNamespace)))
+    {
+        szName = szNamespace = "Invalid TypeDef record";
+    }
+
+    if (g_pConfig->ShouldBreakOnStructMarshalSetup(szName))
+        CONSISTENCY_CHECK_MSGF(false, ("BreakOnStructMarshalSetup: '%s' ", szName));
+#endif // _DEBUG
+
+    MethodTable* pParentMT = GetParentMethodTable();
+    bool hasNonTrivialParent = pParentMT && !pParentMT->IsObjectClass() && !pParentMT->IsValueTypeClass();
+
+    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)
+#ifdef FEATURE_64BIT_ALIGNMENT
+        || (pParentMT && pParentMT->RequiresAlign8())
+#endif // FEATURE_64BIT_ALIGNMENT
+        ;
+
+    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 (isAlign8)
+        {
+            GetHalfBakedClass()->SetAlign8Candidate();
+        }
+        HandleAutoLayout(pByValueClassCache);
+        return;
+    }
+
+    _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;
+    }
path: src/coreclr/vm/methodtablebuilder.cpp
user: login: jkoritzinsky
body: We don't want to set `isAlign` to true if the `packingSize` would be the only reason we'd set the flag. There needs to be a field the requires it for us to set it (packing can't force wider alignment, only narrower alignment).
created_at: 2025-05-08T17:56:18Z
author_association: MEMBER
diff_hunk: @@ -1165,7 +1167,7 @@ MethodTableBuilder::CopyParentVtable()
 // support.
 // If so:
 //   - Update the NumInstanceFieldBytes on the bmtFieldPlacement.
-//   - Update the m_cbNativeSize and m_cbManagedSize if HasLayout() is true.
+//   - Update the ManagedSize if HasLayout() is true.
path: src/coreclr/vm/methodtablebuilder.cpp
user: login: davidwrighton
body: Obsolete comment
created_at: 2025-05-20T17:13:22Z
author_association: MEMBER
diff_hunk: @@ -8287,7 +8138,124 @@ 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();
+
+    EEClassLayoutInfo::NestedFieldFlags nestedFieldFlags =
+        EEClassLayoutInfo::GetNestedFieldFlags(
path: src/coreclr/vm/methodtablebuilder.cpp
user: login: davidwrighton
body: We don't need to call this before `if (bmtLayout->LayoutType == EEClassLayoutInfo::LayoutType::Auto)` line unless the current target might need to have the isAlign8 bit set, and doing so adds an extra loop through all of the fields of the type. I'd like to see
1. The NestedFieldFlags::Align8 enum entry only exist if the FEATURE_64_BIT_ALIGNMENT or FEATURE_DOUBLE_ALIGNMENT_HINT defines are set.
2. If we don't support Align8, we shouldn't be calling GetNestedFieldFlags if the LayoutKind is specified as Auto.
created_at: 2025-05-20T20:38:55Z
author_association: MEMBER

PR comments:
user: login: dotnet-policy-service[bot]
created_at: 2025-03-12T23:27:42Z
body: Tagging subscribers to this area: @mangod9
See info in [area-owners.md](https://github.com/dotnet/runtime/blob/main/docs/area-owners.md) if you want to be subscribed.
<!-- Policy app identification https://img.shields.io/static/v1?label=PullRequestIssueManagement. -->
author_association: CONTRIBUTOR
user: login: jkoritzinsky
created_at: 2025-05-15T22:58:30Z
body: /azp run runtime-coreclr crossgen2 outerloop
author_association: MEMBER
user: login: azure-pipelines[bot]
created_at: 2025-05-15T22:58:48Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: jkoritzinsky
created_at: 2025-05-20T04:55:10Z
body: The only failure is an existing issue in the crossgen2 outerloop pipeline.

We've now validated the new implementation in CoreCLR matches crossgen2 for all types in the BCL and in the runtime test tree.
author_association: MEMBER
user: login: jkoritzinsky
created_at: 2025-05-21T17:52:47Z
body: /ba-g host failure unrelated
author_association: MEMBER