Skip to content

Commit

Permalink
Update the auto-layout algorithm to better track alignment requiremen…
Browse files Browse the repository at this point in the history
…ts. Fixes dotnet#12977
  • Loading branch information
jkoritzinsky committed Dec 8, 2021
1 parent 6a90bb3 commit 880430a
Show file tree
Hide file tree
Showing 9 changed files with 49 additions and 48 deletions.
2 changes: 1 addition & 1 deletion src/coreclr/debug/ee/debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4360,7 +4360,7 @@ SIZE_T GetSetFrameHelper::GetValueClassSize(MetaSig* pSig)
// - but we don't care if it's shared (since it will be the same size either way)
_ASSERTE(!vcType.IsNull() && vcType.IsValueType());

return (vcType.GetMethodTable()->GetAlignedNumInstanceFieldBytes());
return (vcType.GetMethodTable()->GetNumInstanceFieldBytes());
}

//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -645,29 +645,14 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type,
// Place value class fields last
for (int i = 0; i < instanceValueClassFieldsArr.Length; i++)
{
// If the field has an indeterminate alignment, align the cumulative field offset to the indeterminate value
// Otherwise, align the cumulative field offset to the PointerSize
// This avoids issues with Universal Generic Field layouts whose fields may have Indeterminate sizes or alignments
// Align the cumulative field offset to the indeterminate value
var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(instanceValueClassFieldsArr[i].FieldType, hasLayout, packingSize, out bool fieldLayoutAbiStable);
if (!fieldLayoutAbiStable)
layoutAbiStable = false;

if (fieldSizeAndAlignment.Alignment.IsIndeterminate)
{
cumulativeInstanceFieldPos = AlignUpInstanceFieldOffset(type, cumulativeInstanceFieldPos, fieldSizeAndAlignment.Alignment, context.Target);
}
else
{
LayoutInt AlignmentRequired = LayoutInt.Max(fieldSizeAndAlignment.Alignment, context.Target.LayoutPointerSize);
cumulativeInstanceFieldPos = AlignUpInstanceFieldOffset(type, cumulativeInstanceFieldPos, AlignmentRequired, context.Target);
}
cumulativeInstanceFieldPos = AlignUpInstanceFieldOffset(type, cumulativeInstanceFieldPos, fieldSizeAndAlignment.Alignment, context.Target);
offsets[fieldOrdinal] = new FieldAndOffset(instanceValueClassFieldsArr[i], cumulativeInstanceFieldPos + offsetBias);

// If the field has an indeterminate size, align the cumulative field offset to the indeterminate value
// Otherwise, align the cumulative field offset to the aligned-instance field size
// This avoids issues with Universal Generic Field layouts whose fields may have Indeterminate sizes or alignments
LayoutInt alignedInstanceFieldBytes = fieldSizeAndAlignment.Size.IsIndeterminate ? fieldSizeAndAlignment.Size : GetAlignedNumInstanceFieldBytes(fieldSizeAndAlignment.Size);
cumulativeInstanceFieldPos = checked(cumulativeInstanceFieldPos + alignedInstanceFieldBytes);
cumulativeInstanceFieldPos = checked(cumulativeInstanceFieldPos + fieldSizeAndAlignment.Size);

fieldOrdinal++;
}
Expand All @@ -682,11 +667,18 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type,
}
else if (cumulativeInstanceFieldPos.AsInt > context.Target.PointerSize)
{
minAlign = context.Target.LayoutPointerSize;
if (requiresAlign8 && minAlign.AsInt == 4)
if (requiresAlign8)
{
minAlign = new LayoutInt(8);
}
else if (type.ContainsGCPointers)
{
minAlign = context.Target.LayoutPointerSize;
}
else
{
minAlign = largestAlignmentRequired;
}
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy
}
else if (index == 0)
{
skip = pElemMT->GetAlignedNumInstanceFieldBytes() - numPtrsInBytes;
skip = pElemMT->GetNumInstanceFieldBytes() - numPtrsInBytes;
}
else
{
Expand Down
3 changes: 0 additions & 3 deletions src/coreclr/vm/managedmdimport.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ typedef struct
{
I4Array * largeResult;
int length;
#ifdef HOST_64BIT
int padding;
#endif
int smallResult[16];
} MetadataEnumResult;

Expand Down
4 changes: 0 additions & 4 deletions src/coreclr/vm/methodtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -1603,10 +1603,6 @@ class MethodTable

inline WORD GetNumIntroducedInstanceFields();

// <TODO> Does this always return the same (or related) size as GetBaseSize()? </TODO>
inline DWORD GetAlignedNumInstanceFieldBytes();


// Note: This flag MUST be available even from an unrestored MethodTable - see GcScanRoots in siginfo.cpp.
DWORD ContainsPointers()
{
Expand Down
7 changes: 0 additions & 7 deletions src/coreclr/vm/methodtable.inl
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,6 @@ inline WORD MethodTable::GetNumIntroducedInstanceFields()
return(wNumFields);
}

//==========================================================================================
inline DWORD MethodTable::GetAlignedNumInstanceFieldBytes()
{
WRAPPER_NO_CONTRACT;
return((GetNumInstanceFieldBytes() + 3) & (~3));
}

//==========================================================================================
inline PTR_FieldDesc MethodTable::GetApproxFieldDescListRaw()
{
Expand Down
43 changes: 33 additions & 10 deletions src/coreclr/vm/methodtablebuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8169,36 +8169,59 @@ VOID MethodTableBuilder::PlaceInstanceFields(MethodTable ** pByValueClassCach
else
dwNumGCPointerSeries = bmtParent->NumParentPointerSeries;

bool containsGCPointers = bmtFP->NumInstanceGCPointerFields > 0;
// Place by value class fields last
// Update the number of GC pointer series
// Calculate largest alignment requirement
int largestAlignmentRequirement = 1;
for (i = 0; i < bmtEnumFields->dwNumInstanceFields; i++)
{
if (pFieldDescList[i].IsByValue())
{
MethodTable * pByValueMT = pByValueClassCache[i];

// value classes could have GC pointers in them, which need to be pointer-size aligned
// so do this if it has not been done already

#if !defined(TARGET_64BIT) && (DATA_ALIGNMENT > 4)
dwCumulativeInstanceFieldPos = (DWORD)ALIGN_UP(dwCumulativeInstanceFieldPos,
(pByValueMT->GetNumInstanceFieldBytes() >= DATA_ALIGNMENT) ? DATA_ALIGNMENT : TARGET_POINTER_SIZE);
#else // !(!defined(TARGET_64BIT) && (DATA_ALIGNMENT > 4))
#ifdef FEATURE_64BIT_ALIGNMENT
if (pByValueMT->GetNumInstanceFieldBytes() >= DATA_ALIGNMENT)
{
dwCumulativeInstanceFieldPos = (DWORD)ALIGN_UP(dwCumulativeInstanceFieldPos, DATA_ALIGNMENT);
largestAlignmentRequirement = max(largestAlignmentRequirement, DATA_ALIGNMENT);
}
else
#elif defined(FEATURE_64BIT_ALIGNMENT)
if (pByValueMT->RequiresAlign8())
{
dwCumulativeInstanceFieldPos = (DWORD)ALIGN_UP(dwCumulativeInstanceFieldPos, 8);
largestAlignmentRequirement = max(largestAlignmentRequirement, 8);
}
else
#endif // FEATURE_64BIT_ALIGNMENT
if (pByValueMT->ContainsPointers())
{
// this field type has GC pointers in it, which need to be pointer-size aligned
// so do this if it has not been done already
dwCumulativeInstanceFieldPos = (DWORD)ALIGN_UP(dwCumulativeInstanceFieldPos, TARGET_POINTER_SIZE);
#endif // !(!defined(TARGET_64BIT) && (DATA_ALIGNMENT > 4))
largestAlignmentRequirement = max(largestAlignmentRequirement, TARGET_POINTER_SIZE);
containsGCPointers = true;
}
else if (pByValueMT->HasLayout())
{
int fieldAlignmentRequirement = pByValueMT->GetLayoutInfo()->m_ManagedLargestAlignmentRequirementOfAllMembers;
largestAlignmentRequirement = max(largestAlignmentRequirement, fieldAlignmentRequirement);
dwCumulativeInstanceFieldPos = (DWORD)ALIGN_UP(dwCumulativeInstanceFieldPos, fieldAlignmentRequirement);
}

pFieldDescList[i].SetOffset(dwCumulativeInstanceFieldPos - dwOffsetBias);
dwCumulativeInstanceFieldPos += pByValueMT->GetAlignedNumInstanceFieldBytes();
dwCumulativeInstanceFieldPos += pByValueMT->GetNumInstanceFieldBytes();

// Add pointer series for by-value classes
dwNumGCPointerSeries += pByValueMT->ContainsPointers() ?
(DWORD)CGCDesc::GetCGCDescFromMT(pByValueMT)->GetNumSeries() : 0;
}
else
{
// non-value-type fields always require pointer alignment
largestAlignmentRequirement = max(largestAlignmentRequirement, TARGET_POINTER_SIZE);
}
}

// Can be unaligned
Expand All @@ -8223,7 +8246,7 @@ VOID MethodTableBuilder::PlaceInstanceFields(MethodTable ** pByValueClassCach
else
#endif // FEATURE_64BIT_ALIGNMENT
if (dwNumInstanceFieldBytes > TARGET_POINTER_SIZE) {
minAlign = TARGET_POINTER_SIZE;
minAlign = containsGCPointers ? TARGET_POINTER_SIZE : (unsigned)largestAlignmentRequirement;
}
else {
minAlign = 1;
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/mlinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2180,7 +2180,7 @@ MarshalInfo::MarshalInfo(Module* pModule,
IfFailGoto(E_FAIL, lFail);
}

UINT managedSize = m_pMT->GetAlignedNumInstanceFieldBytes();
UINT managedSize = m_pMT->GetNumInstanceFieldBytes();
UINT nativeSize = 0;

if ( nativeSize > 0xfff0 ||
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ void STDCALL CopyValueClassArgUnchecked(ArgDestination *argDest, void* src, Meth

if (argDest->IsHFA())
{
argDest->CopyHFAStructToRegister(src, pMT->GetAlignedNumInstanceFieldBytes());
argDest->CopyHFAStructToRegister(src, pMT->GetNumInstanceFieldBytes());
return;
}

Expand Down

0 comments on commit 880430a

Please sign in to comment.