type.Context.Target.GetWellKnownTypeSize(type),
type.Context.Target.GetWellKnownTypeAlignment(type),
0,
+ alignUpInstanceByteSize: true,
out instanceByteSizeAndAlignment
);
}
ref StaticsBlock block = ref GetStaticsBlockForField(ref result, field);
- SizeAndAlignment sizeAndAlignment = ComputeFieldSizeAndAlignment(fieldType, context.Target.DefaultPackingSize, out bool _);
+ SizeAndAlignment sizeAndAlignment = ComputeFieldSizeAndAlignment(fieldType, hasLayout: false, context.Target.DefaultPackingSize, out bool _);
block.Size = LayoutInt.AlignUp(block.Size, sizeAndAlignment.Alignment, context.Target);
result.Offsets[index] = new FieldAndOffset(field, block.Size);
{
}
- protected static ComputedInstanceFieldLayout ComputeExplicitFieldLayout(MetadataType type, int numInstanceFields)
+ protected virtual bool AlignUpInstanceByteSizeForExplicitFieldLayoutCompatQuirk(TypeDesc type) => true;
+
+ protected ComputedInstanceFieldLayout ComputeExplicitFieldLayout(MetadataType type, int numInstanceFields)
{
// Instance slice size is the total size of instance not including the base type.
// It is calculated as the field whose offset and size add to the greatest value.
LayoutInt offsetBias = !type.IsValueType ? new LayoutInt(type.Context.Target.PointerSize) : LayoutInt.Zero;
- LayoutInt cumulativeInstanceFieldPos =
- type.HasBaseType && !type.IsValueType ? type.BaseType.InstanceByteCount : LayoutInt.Zero;
- LayoutInt instanceSize = cumulativeInstanceFieldPos;
- cumulativeInstanceFieldPos -= offsetBias;
+ LayoutInt cumulativeInstanceFieldPos = CalculateFieldBaseOffset(type, requiresAlign8: false, requiresAlignedBase: false) - offsetBias;
+ LayoutInt instanceSize = cumulativeInstanceFieldPos + offsetBias;
var layoutMetadata = type.GetClassLayout();
-
int packingSize = ComputePackingSize(type, layoutMetadata);
LayoutInt largestAlignmentRequired = LayoutInt.One;
foreach (var fieldAndOffset in layoutMetadata.Offsets)
{
TypeDesc fieldType = fieldAndOffset.Field.FieldType;
- var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(fieldType, packingSize, out bool fieldLayoutAbiStable);
+ var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(fieldType, hasLayout: true, packingSize, out bool fieldLayoutAbiStable);
if (!fieldLayoutAbiStable)
layoutAbiStable = false;
}
SizeAndAlignment instanceByteSizeAndAlignment;
- var instanceSizeAndAlignment = ComputeInstanceSize(type, instanceSize, largestAlignmentRequired, layoutMetadata.Size, out instanceByteSizeAndAlignment);
+ var instanceSizeAndAlignment = ComputeInstanceSize(type,
+ instanceSize,
+ largestAlignmentRequired,
+ layoutMetadata.Size,
+ alignUpInstanceByteSize: AlignUpInstanceByteSizeForExplicitFieldLayoutCompatQuirk(type),
+ out instanceByteSizeAndAlignment);
ComputedInstanceFieldLayout computedLayout = new ComputedInstanceFieldLayout();
computedLayout.FieldAlignment = instanceSizeAndAlignment.Alignment;
computedLayout.Offsets = offsets;
computedLayout.LayoutAbiStable = layoutAbiStable;
-
ExplicitLayoutValidator.Validate(type, computedLayout);
return computedLayout;
if (field.IsStatic)
continue;
- var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(field.FieldType, packingSize, out bool fieldLayoutAbiStable);
+ var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(field.FieldType, hasLayout: true, packingSize, out bool fieldLayoutAbiStable);
if (!fieldLayoutAbiStable)
layoutAbiStable = false;
}
SizeAndAlignment instanceByteSizeAndAlignment;
- var instanceSizeAndAlignment = ComputeInstanceSize(type, cumulativeInstanceFieldPos + offsetBias, largestAlignmentRequirement, layoutMetadata.Size, out instanceByteSizeAndAlignment);
+ var instanceSizeAndAlignment = ComputeInstanceSize(
+ type,
+ cumulativeInstanceFieldPos + offsetBias,
+ largestAlignmentRequirement,
+ layoutMetadata.Size,
+ alignUpInstanceByteSize: true,
+ out instanceByteSizeAndAlignment);
ComputedInstanceFieldLayout computedLayout = new ComputedInstanceFieldLayout();
computedLayout.FieldAlignment = instanceSizeAndAlignment.Alignment;
{
TypeSystemContext context = type.Context;
+ bool hasLayout = type.HasLayout();
var layoutMetadata = type.GetClassLayout();
int packingSize = ComputePackingSize(type, layoutMetadata);
{
Debug.Assert(fieldType.IsPrimitive || fieldType.IsPointer || fieldType.IsFunctionPointer || fieldType.IsEnum);
- var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(fieldType, packingSize, out bool _);
+ var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(fieldType, hasLayout, packingSize, out bool _);
instanceNonGCPointerFieldsCount[CalculateLog2(fieldSizeAndAlignment.Size.AsInt)]++;
}
}
TypeDesc fieldType = field.FieldType;
- var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(fieldType, packingSize, out bool fieldLayoutAbiStable);
+ var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(fieldType, hasLayout, packingSize, out bool fieldLayoutAbiStable);
if (!fieldLayoutAbiStable)
layoutAbiStable = false;
// Place the field
j = instanceNonGCPointerFieldsCount[i];
FieldDesc field = instanceNonGCPointerFieldsArr[i][j];
- PlaceInstanceField(field, packingSize, offsets, ref cumulativeInstanceFieldPos, ref fieldOrdinal, offsetBias);
+ PlaceInstanceField(field, hasLayout, packingSize, offsets, ref cumulativeInstanceFieldPos, ref fieldOrdinal, offsetBias);
instanceNonGCPointerFieldsCount[i]++;
}
{
for (int j = 0; j < instanceGCPointerFieldsArr.Length; j++)
{
- PlaceInstanceField(instanceGCPointerFieldsArr[j], packingSize, offsets, ref cumulativeInstanceFieldPos, ref fieldOrdinal, offsetBias);
+ PlaceInstanceField(instanceGCPointerFieldsArr[j], hasLayout, packingSize, offsets, ref cumulativeInstanceFieldPos, ref fieldOrdinal, offsetBias);
}
}
// The start index will be the index that may have been increased in the previous optimization
for (int j = instanceNonGCPointerFieldsCount[i]; j < instanceNonGCPointerFieldsArr[i].Length; j++)
{
- PlaceInstanceField(instanceNonGCPointerFieldsArr[i][j], packingSize, offsets, ref cumulativeInstanceFieldPos, ref fieldOrdinal, offsetBias);
+ PlaceInstanceField(instanceNonGCPointerFieldsArr[i][j], hasLayout, packingSize, offsets, ref cumulativeInstanceFieldPos, ref fieldOrdinal, offsetBias);
}
}
// 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
- var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(instanceValueClassFieldsArr[i].FieldType, packingSize, out bool fieldLayoutAbiStable);
+ var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(instanceValueClassFieldsArr[i].FieldType, hasLayout, packingSize, out bool fieldLayoutAbiStable);
if (!fieldLayoutAbiStable)
layoutAbiStable = false;
}
SizeAndAlignment instanceByteSizeAndAlignment;
- var instanceSizeAndAlignment = ComputeInstanceSize(type, cumulativeInstanceFieldPos + offsetBias, minAlign, 0/* specified field size unused */, out instanceByteSizeAndAlignment);
+ var instanceSizeAndAlignment = ComputeInstanceSize(type,
+ cumulativeInstanceFieldPos + offsetBias,
+ minAlign,
+ classLayoutSize: 0,
+ alignUpInstanceByteSize: true,
+ out instanceByteSizeAndAlignment);
ComputedInstanceFieldLayout computedLayout = new ComputedInstanceFieldLayout();
computedLayout.FieldAlignment = instanceSizeAndAlignment.Alignment;
return computedLayout;
}
- private static void PlaceInstanceField(FieldDesc field, int packingSize, FieldAndOffset[] offsets, ref LayoutInt instanceFieldPos, ref int fieldOrdinal, LayoutInt offsetBias)
+ private static void PlaceInstanceField(FieldDesc field, bool hasLayout, int packingSize, FieldAndOffset[] offsets, ref LayoutInt instanceFieldPos, ref int fieldOrdinal, LayoutInt offsetBias)
{
- var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(field.FieldType, packingSize, out bool _);
+ var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(field.FieldType, hasLayout, packingSize, out bool _);
instanceFieldPos = AlignUpInstanceFieldOffset(field.OwningType, instanceFieldPos, fieldSizeAndAlignment.Alignment, field.Context.Target);
offsets[fieldOrdinal] = new FieldAndOffset(field, instanceFieldPos + offsetBias);
return cumulativeInstanceFieldPos;
}
- private static SizeAndAlignment ComputeFieldSizeAndAlignment(TypeDesc fieldType, int packingSize, out bool layoutAbiStable)
+ private static SizeAndAlignment ComputeFieldSizeAndAlignment(TypeDesc fieldType, bool hasLayout, int packingSize, out bool layoutAbiStable)
{
SizeAndAlignment result;
layoutAbiStable = true;
result.Alignment = fieldType.Context.Target.LayoutPointerSize;
}
- result.Alignment = LayoutInt.Min(result.Alignment, new LayoutInt(packingSize));
+ if (hasLayout)
+ {
+ result.Alignment = LayoutInt.Min(result.Alignment, new LayoutInt(packingSize));
+ }
+ else
+ {
+ result.Alignment = LayoutInt.Min(result.Alignment, fieldType.Context.Target.GetObjectAlignment(result.Alignment));
+ }
return result;
}
private static int ComputePackingSize(MetadataType type, ClassLayoutMetadata layoutMetadata)
{
- // If a type contains pointers then the metadata specified packing size is ignored (On .NET Framework this is disqualification from ManagedSequential)
- if (layoutMetadata.PackingSize == 0 || type.ContainsGCPointers)
+ if (layoutMetadata.PackingSize == 0)
return type.Context.Target.DefaultPackingSize;
else
return layoutMetadata.PackingSize;
}
- private static SizeAndAlignment ComputeInstanceSize(MetadataType type, LayoutInt instanceSize, LayoutInt alignment, int classLayoutSize, out SizeAndAlignment byteCount)
+ private static SizeAndAlignment ComputeInstanceSize(MetadataType type, LayoutInt instanceSize, LayoutInt alignment, int classLayoutSize, bool alignUpInstanceByteSize, out SizeAndAlignment byteCount)
{
SizeAndAlignment result;
{
if (type.IsValueType)
{
- instanceSize = LayoutInt.AlignUp(instanceSize, alignment, target);
+ instanceSize = LayoutInt.AlignUp(instanceSize,
+ alignUpInstanceByteSize ? alignment : LayoutInt.Min(alignment, target.LayoutPointerSize),
+ target);
}
}
// - Alignment
// - Position of GC references
//
-BOOL TypeLayoutCheck(MethodTable * pMT, PCCOR_SIGNATURE pBlob)
+BOOL TypeLayoutCheck(MethodTable * pMT, PCCOR_SIGNATURE pBlob, BOOL printDiff)
{
STANDARD_VM_CONTRACT;
uint32_t dwFlags;
IfFailThrow(p.GetData(&dwFlags));
+ BOOL result = TRUE;
+
// Size is checked unconditionally
uint32_t dwExpectedSize;
IfFailThrow(p.GetData(&dwExpectedSize));
DWORD dwActualSize = pMT->GetNumInstanceFieldBytes();
if (dwExpectedSize != dwActualSize)
- return FALSE;
+ {
+ if (printDiff)
+ {
+ result = FALSE;
+
+ DefineFullyQualifiedNameForClassW();
+ wprintf(W("Type %s: expected size 0x%08x, actual size 0x%08x\n"),
+ GetFullyQualifiedNameForClassW(pMT), dwExpectedSize, dwActualSize);
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
#ifdef FEATURE_HFA
if (dwFlags & READYTORUN_LAYOUT_HFA)
DWORD dwActualHFAType = pMT->GetHFAType();
if (dwExpectedHFAType != dwActualHFAType)
- return FALSE;
+ {
+ if (printDiff)
+ {
+ result = FALSE;
+
+ DefineFullyQualifiedNameForClassW();
+ wprintf(W("Type %s: expected HFA type %08x, actual %08x\n"),
+ GetFullyQualifiedNameForClassW(pMT), dwExpectedHFAType, dwActualHFAType);
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
}
else
{
if (pMT->IsHFA())
- return FALSE;
+ {
+ if (printDiff)
+ {
+ result = FALSE;
+
+ DefineFullyQualifiedNameForClassW();
+ wprintf(W("Type %s: type is HFA but READYTORUN_LAYOUT_HFA flag is not set\n"),
+ GetFullyQualifiedNameForClassW(pMT));
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
}
#else
_ASSERTE(!(dwFlags & READYTORUN_LAYOUT_HFA));
DWORD dwActualAlignment = CEEInfo::getClassAlignmentRequirementStatic(pMT);
if (dwExpectedAlignment != dwActualAlignment)
- return FALSE;
+ {
+ if (printDiff)
+ {
+ result = FALSE;
+
+ DefineFullyQualifiedNameForClassW();
+ wprintf(W("Type %s: expected alignment 0x%08x, actual 0x%08x\n"),
+ GetFullyQualifiedNameForClassW(pMT), dwExpectedAlignment, dwActualAlignment);
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
}
if (dwFlags & READYTORUN_LAYOUT_GCLayout_Empty)
{
if (pMT->ContainsPointers())
- return FALSE;
+ {
+ if (printDiff)
+ {
+ result = FALSE;
+
+ DefineFullyQualifiedNameForClassW();
+ wprintf(W("Type %s contains pointers but READYTORUN_LAYOUT_GCLayout_Empty is set\n"),
+ GetFullyQualifiedNameForClassW(pMT));
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
}
else
{
ComputeGCRefMap(pMT, pGCRefMap, cbGCRefMap);
if (memcmp(pGCRefMap, p.GetPtr(), cbGCRefMap) != 0)
- return FALSE;
+ {
+ if (printDiff)
+ {
+ result = FALSE;
+
+ DefineFullyQualifiedNameForClassW();
+ wprintf(W("Type %s: GC refmap content doesn't match\n"),
+ GetFullyQualifiedNameForClassW(pMT));
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
}
}
- return TRUE;
+ return result;
}
#endif // FEATURE_READYTORUN
MethodTable * pMT = th.AsMethodTable();
_ASSERTE(pMT->IsValueType());
- if (!TypeLayoutCheck(pMT, pBlob))
+ if (!TypeLayoutCheck(pMT, pBlob, /* printDiff */ kind == ENCODE_VERIFY_TYPE_LAYOUT))
{
if (kind == ENCODE_CHECK_TYPE_LAYOUT)
{
StackScratchBuffer buf;
_ASSERTE_MSG(false, fatalErrorString.GetUTF8(buf));
// Run through the type layout logic again, after the assert, makes debugging easy
- TypeLayoutCheck(pMT, pBlob);
+ TypeLayoutCheck(pMT, pBlob, /* printDiff */ TRUE);
}
#endif