diff --git a/src/coreclr/clr.featuredefines.props b/src/coreclr/clr.featuredefines.props index 511db52edc1020..386c2e67e3f3f2 100644 --- a/src/coreclr/clr.featuredefines.props +++ b/src/coreclr/clr.featuredefines.props @@ -11,6 +11,7 @@ true true true + true @@ -64,5 +65,6 @@ $(DefineConstants);PROFILING_SUPPORTED $(DefineConstants);FEATURE_PROFAPI_ATTACH_DETACH + $(DefineConstants);FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index ffadad35521002..08e4c5b77fc7c6 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -219,6 +219,9 @@ if (CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_AMD64) add_definitions(-DFEATURE_SPECIAL_USER_MODE_APC) endif() +if (FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS) + add_definitions(-DFEATURE_SEQUENTIAL_LAYOUT_WITH_REFS) +endif() # Use this function to enable building with a specific target OS and architecture set of defines # This is known to work for the set of defines used by the JIT and gcinfo, it is not likely correct for diff --git a/src/coreclr/clrfeatures.cmake b/src/coreclr/clrfeatures.cmake index f82ff1aa4e73e9..cfd7b0d9098439 100644 --- a/src/coreclr/clrfeatures.cmake +++ b/src/coreclr/clrfeatures.cmake @@ -39,3 +39,8 @@ endif() if (CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS) set(FEATURE_OBJCMARSHAL 1) endif() + +# should default this to 0 once we figure out how to enable in build scripting +if(NOT DEFINED FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS) + set(FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS 1) +endif(NOT DEFINED FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS) diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs index 362fd1763d230d..7bbebe81a8e604 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs @@ -104,6 +104,9 @@ public override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType defTyp type.Context.Target.GetWellKnownTypeAlignment(type), 0, alignUpInstanceByteSize: true, +#if FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + ignoreLayoutAlignment: false, +#endif out instanceByteSizeAndAlignment ); @@ -302,7 +305,11 @@ protected ComputedInstanceFieldLayout ComputeExplicitFieldLayout(MetadataType ty LayoutInt instanceSize = cumulativeInstanceFieldPos + offsetBias; var layoutMetadata = type.GetClassLayout(); +#if FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + int packingSize = ComputePackingSize(type, layoutMetadata, false); +#else int packingSize = ComputePackingSize(type, layoutMetadata); +#endif LayoutInt largestAlignmentRequired = LayoutInt.One; var offsets = new FieldAndOffset[numInstanceFields]; @@ -356,6 +363,9 @@ protected ComputedInstanceFieldLayout ComputeExplicitFieldLayout(MetadataType ty largestAlignmentRequired, layoutMetadata.Size, alignUpInstanceByteSize: AlignUpInstanceByteSizeForExplicitFieldLayoutCompatQuirk(type), +#if FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + ignoreLayoutAlignment: false, +#endif out instanceByteSizeAndAlignment); ComputedInstanceFieldLayout computedLayout = new ComputedInstanceFieldLayout(); @@ -376,7 +386,11 @@ private static LayoutInt AlignUpInstanceFieldOffset(TypeDesc typeWithField, Layo return LayoutInt.AlignUp(cumulativeInstanceFieldPos, alignment, target); } +#if FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + protected ComputedInstanceFieldLayout ComputeSequentialFieldLayout(MetadataType type, int numInstanceFields, bool isSequentialWithRefs) +#else protected ComputedInstanceFieldLayout ComputeSequentialFieldLayout(MetadataType type, int numInstanceFields) +#endif { var offsets = new FieldAndOffset[numInstanceFields]; @@ -390,7 +404,11 @@ protected ComputedInstanceFieldLayout ComputeSequentialFieldLayout(MetadataType LayoutInt largestAlignmentRequirement = LayoutInt.One; int fieldOrdinal = 0; +#if FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + int packingSize = ComputePackingSize(type, layoutMetadata, ignoreLayoutPacking: isSequentialWithRefs); +#else int packingSize = ComputePackingSize(type, layoutMetadata); +#endif bool layoutAbiStable = true; foreach (var field in type.GetFields()) @@ -418,6 +436,9 @@ protected ComputedInstanceFieldLayout ComputeSequentialFieldLayout(MetadataType largestAlignmentRequirement, layoutMetadata.Size, alignUpInstanceByteSize: true, +#if FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + ignoreLayoutAlignment: isSequentialWithRefs, +#endif out instanceByteSizeAndAlignment); ComputedInstanceFieldLayout computedLayout = new ComputedInstanceFieldLayout(); @@ -442,7 +463,11 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type, bool hasLayout = type.HasLayout(); var layoutMetadata = type.GetClassLayout(); +#if FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + int packingSize = ComputePackingSize(type, layoutMetadata, false); +#else int packingSize = ComputePackingSize(type, layoutMetadata); +#endif packingSize = Math.Min(context.Target.MaximumAutoLayoutPackingSize, packingSize); var offsets = new FieldAndOffset[numInstanceFields]; @@ -706,6 +731,9 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type, minAlign, classLayoutSize: 0, alignUpInstanceByteSize: true, +#if FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + ignoreLayoutAlignment: false, +#endif out instanceByteSizeAndAlignment); ComputedInstanceFieldLayout computedLayout = new ComputedInstanceFieldLayout(); @@ -828,15 +856,27 @@ private static SizeAndAlignment ComputeFieldSizeAndAlignment(TypeDesc fieldType, return result; } +#if FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + private static int ComputePackingSize(MetadataType type, ClassLayoutMetadata layoutMetadata, bool ignoreLayoutPacking) +#else private static int ComputePackingSize(MetadataType type, ClassLayoutMetadata layoutMetadata) +#endif { - if (layoutMetadata.PackingSize == 0) +#if FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + if (layoutMetadata.PackingSize == 0 || ignoreLayoutPacking) +#else + if (layoutMetadata.PackingSize == 0) +#endif return type.Context.Target.DefaultPackingSize; else return layoutMetadata.PackingSize; } +#if FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + private static SizeAndAlignment ComputeInstanceSize(MetadataType type, LayoutInt instanceSize, LayoutInt alignment, int classLayoutSize, bool alignUpInstanceByteSize, bool ignoreLayoutAlignment, out SizeAndAlignment byteCount) +#else private static SizeAndAlignment ComputeInstanceSize(MetadataType type, LayoutInt instanceSize, LayoutInt alignment, int classLayoutSize, bool alignUpInstanceByteSize, out SizeAndAlignment byteCount) +#endif { SizeAndAlignment result; @@ -862,6 +902,16 @@ private static SizeAndAlignment ComputeInstanceSize(MetadataType type, LayoutInt } else { +#if FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + if (ignoreLayoutAlignment) + { + // Even if we want to ignore layout alignment, the GC requires types with GC refs to be + // sized to a multiple of LayoutPointerSize + if (type.ContainsGCPointers) + instanceSize = LayoutInt.AlignUp(instanceSize, target.LayoutPointerSize, target); + } + else +#endif if (type.IsValueType) { instanceSize = LayoutInt.AlignUp(instanceSize, diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs index 76e237cec5c1c1..ecfad235739b36 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs @@ -803,10 +803,17 @@ protected override ComputedInstanceFieldLayout ComputeInstanceFieldLayout(Metada return ComputeExplicitFieldLayout(type, numInstanceFields); } else +#if FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + if (ShouldLayoutSequential(type, out var isSequentialWithRefs)) + { + return ComputeSequentialFieldLayout(type, numInstanceFields, isSequentialWithRefs); + } +#else if (type.IsEnum || MarshalUtils.IsBlittableType(type) || IsManagedSequentialType(type)) { return ComputeSequentialFieldLayout(type, numInstanceFields); } +#endif else { return ComputeAutoFieldLayout(type, numInstanceFields); @@ -865,5 +872,58 @@ public static bool IsManagedSequentialType(TypeDesc type) return true; } + +#if FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + private static bool ShouldLayoutSequential(TypeDesc type, out bool isSequentialWithRefs) + { + isSequentialWithRefs = false; + + var name = type.GetDisplayName(); + + if (type.IsEnum) + { + return true; + } + + if (MarshalUtils.IsBlittableType(type)) + { + return true; + } + + if (IsManagedSequentialType(type)) + { + return true; + } + + isSequentialWithRefs = HasValidSequentialLayout(type); + return isSequentialWithRefs; + } + + private static bool HasValidSequentialLayout(TypeDesc type) + { + var metadataType = type as MetadataType; + if (metadataType == null) + { + return false; + } + + if (!metadataType.IsSequentialLayout) + { + return false; + } + + if (!metadataType.HasBaseType) + { + return false; + } + + if (metadataType.BaseType.IsObject || metadataType.BaseType.IsWellKnownType(WellKnownType.ValueType)) + { + return true; + } + + return HasValidSequentialLayout(type.BaseType); + } +#endif } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index bce5efe4e0905e..aee3add894c1fb 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -17,6 +17,10 @@ Debug;Release;Checked + + + + diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/ILCompiler.TypeSystem.ReadyToRun.Tests.csproj b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/ILCompiler.TypeSystem.ReadyToRun.Tests.csproj index 558daa29f76b70..22b9f5d7c7ea8b 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/ILCompiler.TypeSystem.ReadyToRun.Tests.csproj +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/ILCompiler.TypeSystem.ReadyToRun.Tests.csproj @@ -16,6 +16,9 @@ true + + + diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/TestMetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/TestMetadataFieldLayoutAlgorithm.cs index fdd8382a5bdcfa..f23289a48ab9b5 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/TestMetadataFieldLayoutAlgorithm.cs +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/TestMetadataFieldLayoutAlgorithm.cs @@ -39,7 +39,11 @@ protected override ComputedInstanceFieldLayout ComputeInstanceFieldLayout(Metada } else if (type.IsSequentialLayout || type.IsEnum) { +#if FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + return ComputeSequentialFieldLayout(type, numInstanceFields, false); +#else return ComputeSequentialFieldLayout(type, numInstanceFields); +#endif } else { diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj index 86b9c06a6cf0d2..61688aa2950d64 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj @@ -19,6 +19,9 @@ Debug;Release;Checked + + + Internal.TypeSystem.Strings.resources diff --git a/src/coreclr/vm/class.h b/src/coreclr/vm/class.h index 1817658119d17e..174cb0ab74ea76 100644 --- a/src/coreclr/vm/class.h +++ b/src/coreclr/vm/class.h @@ -376,6 +376,11 @@ class EEClassLayoutInfo e_ZERO_SIZED = 0x04, // The size of the struct is explicitly specified in the meta-data. e_HAS_EXPLICIT_SIZE = 0x08 + +#ifdef FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + // The type has GC references, but respect sequential layout + , e_SEQUENTIAL_WITH_REFS = 0x10 +#endif }; BYTE m_bFlags; @@ -419,6 +424,14 @@ class EEClassLayoutInfo return (m_bFlags & e_HAS_EXPLICIT_SIZE) == e_HAS_EXPLICIT_SIZE; } +#ifdef FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + BOOL IsSequentialWithRefs() const + { + LIMITED_METHOD_CONTRACT; + return (m_bFlags & e_SEQUENTIAL_WITH_REFS) == e_SEQUENTIAL_WITH_REFS; + } +#endif + BYTE GetPackingSize() const { LIMITED_METHOD_CONTRACT; @@ -453,6 +466,13 @@ class EEClassLayoutInfo m_bFlags = hasExplicitSize ? (m_bFlags | e_HAS_EXPLICIT_SIZE) : (m_bFlags & ~e_HAS_EXPLICIT_SIZE); } + + void SetIsSequentialWithRefs(BOOL isSequenialWithRefs) + { + LIMITED_METHOD_CONTRACT; + m_bFlags = isSequenialWithRefs ? (m_bFlags | e_SEQUENTIAL_WITH_REFS) + : (m_bFlags & ~e_SEQUENTIAL_WITH_REFS); + } }; // @@ -1382,6 +1402,10 @@ class EEClass // DO NOT CREATE A NEW EEClass USING NEW! BOOL HasExplicitSize(); +#ifdef FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + BOOL IsSequentialWithRefs(); +#endif + static void GetBestFitMapping(MethodTable * pMT, BOOL *pfBestFitMapping, BOOL *pfThrowOnUnmappableChar); /* @@ -2062,6 +2086,12 @@ inline BOOL EEClass::HasExplicitSize() return HasLayout() && GetLayoutInfo()->HasExplicitSize(); } +inline BOOL EEClass::IsSequentialWithRefs() +{ + LIMITED_METHOD_CONTRACT; + return HasLayout() && GetLayoutInfo()->IsSequentialWithRefs(); +} + //========================================================================== // These routines manage the prestub (a bootstrapping stub that all // FunctionDesc's are initialized with.) diff --git a/src/coreclr/vm/classlayoutinfo.cpp b/src/coreclr/vm/classlayoutinfo.cpp index 749620609dfdd4..a5cff1dd099e55 100644 --- a/src/coreclr/vm/classlayoutinfo.cpp +++ b/src/coreclr/vm/classlayoutinfo.cpp @@ -710,6 +710,16 @@ VOID EEClassLayoutInfo::CollectLayoutFieldMetadataThrowing( } pEEClassLayoutInfoOut->SetIsManagedSequential(!fDisqualifyFromManagedSequential); + +#ifdef FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + BOOL fIsSequentialWithRefs = FALSE; + if (fDisqualifyFromManagedSequential && !fExplicitOffsets) + { + fIsSequentialWithRefs = !fHasNonTrivialParent || (fParentHasLayout && (pParentLayoutInfo->IsSequentialWithRefs() || pParentLayoutInfo->IsManagedSequential() || pParentLayoutInfo->IsBlittable())); + } + + pEEClassLayoutInfoOut->SetIsSequentialWithRefs(fIsSequentialWithRefs); +#endif } void EEClassNativeLayoutInfo::InitializeNativeLayoutFieldMetadataThrowing(MethodTable* pMT) diff --git a/src/coreclr/vm/managedmdimport.hpp b/src/coreclr/vm/managedmdimport.hpp index ffc1efb2b7a64c..08476ccfe10cb6 100644 --- a/src/coreclr/vm/managedmdimport.hpp +++ b/src/coreclr/vm/managedmdimport.hpp @@ -29,7 +29,9 @@ typedef struct I4Array * largeResult; int length; #ifdef HOST_64BIT +#ifndef FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS int padding; +#endif #endif int smallResult[16]; } MetadataEnumResult; diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index d5feeae4425e24..9dffe5de6c552a 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -1734,6 +1734,13 @@ MethodTableBuilder::BuildMethodTableThrowing( HandleExplicitLayout(pByValueClassCache); } } +#ifdef FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + else if (IsSequentialWithRefs() && !bmtGenerics->fContainsGenericVariables) + { + _ASSERTE(HasLayout()); + HandleExplicitLayout(pByValueClassCache); + } +#endif else { _ASSERTE(!IsBlittable()); @@ -1828,6 +1835,10 @@ MethodTableBuilder::BuildMethodTableThrowing( if (HasExplicitFieldOffsetLayout()) // Perform relevant GC calculations for tdexplicit HandleGCForExplicitLayout(); +#ifdef FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + else if (IsSequentialWithRefs()) + HandleGCForExplicitLayout(); +#endif else // Perform relevant GC calculations for value classes HandleGCForValueClasses(pByValueClassCache); @@ -4252,6 +4263,15 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, IfFailThrow(pFD->SetOffset(pLayoutFieldInfo->m_placement.m_offset)); } +#ifdef FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + else if (!fIsStatic && IsSequentialWithRefs()) + { + (DWORD_PTR &)pFD->m_pMTOfEnclosingClass = + (*pByValueClassCache)[dwCurrentDeclaredField]->GetNumInstanceFieldBytes(); + + IfFailThrow(pFD->SetOffset(pLayoutFieldInfo->m_placement.m_offset)); + } +#endif else { // static value class fields hold a handle, which is ptr sized @@ -4274,6 +4294,10 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, IfFailThrow(pFD->SetOffset(pLayoutFieldInfo->m_placement.m_offset)); else if (IsManagedSequential() && !fIsStatic) IfFailThrow(pFD->SetOffset(pLayoutFieldInfo->m_placement.m_offset)); +#ifdef FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + else if (IsSequentialWithRefs() && !fIsStatic) + IfFailThrow(pFD->SetOffset(pLayoutFieldInfo->m_placement.m_offset)); +#endif else if (bCurrentFieldIsGCPointer) pFD->SetOffset(FIELD_OFFSET_UNPLACED_GC_PTR); else @@ -8258,7 +8282,14 @@ DWORD MethodTableBuilder::GetFieldSize(FieldDesc *pFD) // We should only be calling this while this class is being built. _ASSERTE(GetHalfBakedMethodTable() == 0); - BAD_FORMAT_NOTHROW_ASSERT(! pFD->IsByValue() || HasExplicitFieldOffsetLayout()); + + +#ifdef FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + BAD_FORMAT_NOTHROW_ASSERT(! pFD->IsByValue() || HasExplicitFieldOffsetLayout() || IsSequentialWithRefs() ); +#else + BAD_FORMAT_NOTHROW_ASSERT(! pFD->IsByValue() || HasExplicitFieldOffsetLayout() ); +#endif + if (pFD->IsByValue()) return (DWORD)(DWORD_PTR&)(pFD->m_pMTOfEnclosingClass); diff --git a/src/coreclr/vm/methodtablebuilder.h b/src/coreclr/vm/methodtablebuilder.h index 42cae0b22c1e95..9d46519de6dfca 100644 --- a/src/coreclr/vm/methodtablebuilder.h +++ b/src/coreclr/vm/methodtablebuilder.h @@ -230,6 +230,9 @@ class MethodTableBuilder BOOL HasExplicitFieldOffsetLayout() { WRAPPER_NO_CONTRACT; return GetHalfBakedClass()->HasExplicitFieldOffsetLayout(); } BOOL IsManagedSequential() { WRAPPER_NO_CONTRACT; return GetHalfBakedClass()->IsManagedSequential(); } BOOL HasExplicitSize() { WRAPPER_NO_CONTRACT; return GetHalfBakedClass()->HasExplicitSize(); } +#ifdef FEATURE_SEQUENTIAL_LAYOUT_WITH_REFS + BOOL IsSequentialWithRefs() { WRAPPER_NO_CONTRACT; return GetHalfBakedClass()->IsSequentialWithRefs(); } +#endif #ifdef _DEBUG LPCUTF8 GetDebugClassName() { WRAPPER_NO_CONTRACT; return GetHalfBakedClass()->GetDebugClassName(); }