Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/coreclr/inc/corhdr.h
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ typedef enum CorTypeAttr
enum class CorExtendedLayoutKind
{
CStruct = 0, // C-style struct
CUnion = 1, // C-style union
};

// Macros for accessing the members of the CorTypeAttr.
Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2118,6 +2118,14 @@ private uint getClassAttribsInternal(TypeDesc type)

if (metadataType.IsInlineArray)
result |= CorInfoFlag.CORINFO_FLG_INDEXABLE_FIELDS;

if (metadataType.IsExtendedLayout)
{
if (metadataType.GetClassLayout().Kind == MetadataLayoutKind.CUnion)
{
result |= CorInfoFlag.CORINFO_FLG_OVERLAPPING_FIELDS;
}
}
}

if (type.IsCanonicalSubtype(CanonicalFormKind.Any))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ protected ComputedInstanceFieldLayout ComputeSequentialFieldLayout(MetadataType

protected ComputedInstanceFieldLayout ComputeCStructFieldLayout(MetadataType type, int numInstanceFields)
{
if (type.ContainsGCPointers || !type.IsValueType)
if (type.ContainsGCPointers || type.IsByRefLike || !type.IsValueType)
{
// CStruct layout algorithm does not support GC pointers.
ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadBadFormat, type);
Expand Down Expand Up @@ -581,6 +581,91 @@ protected ComputedInstanceFieldLayout ComputeCStructFieldLayout(MetadataType typ
return computedLayout;
}

protected ComputedInstanceFieldLayout ComputeCUnionFieldLayout(MetadataType type, int numInstanceFields)
{
if (type.ContainsGCPointers || type.IsByRefLike || !type.IsValueType)
{
// CUnion layout algorithm does not support GC pointers.
ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadBadFormat, type);
}

var offsets = new FieldAndOffset[numInstanceFields];

LayoutInt largestFieldSize = LayoutInt.Zero;
LayoutInt largestAlignmentRequirement = LayoutInt.One;
int fieldOrdinal = 0;
int packingSize = type.Context.Target.MaximumAlignment;
bool layoutAbiStable = true;
bool hasAutoLayoutField = false;
bool hasInt128Field = false;
bool hasVectorTField = false;

foreach (var field in type.GetFields())
{
if (field.IsStatic)
continue;

var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(field.FieldType.UnderlyingType, hasLayout: true, packingSize, out ComputedFieldData fieldData);
if (!fieldData.LayoutAbiStable)
layoutAbiStable = false;
if (fieldData.HasAutoLayout)
hasAutoLayoutField = true;
if (fieldData.HasInt128Field)
hasInt128Field = true;
if (fieldData.HasVectorTField)
hasVectorTField = true;

largestAlignmentRequirement = LayoutInt.Max(fieldSizeAndAlignment.Alignment, largestAlignmentRequirement);
largestFieldSize = LayoutInt.Max(fieldSizeAndAlignment.Size, largestFieldSize);

// All fields are placed at offset 0 in a union
offsets[fieldOrdinal] = new FieldAndOffset(field, LayoutInt.Zero);

fieldOrdinal++;
}

if (hasAutoLayoutField)
{
// CUnion does not support auto layout fields
ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadBadFormat, type);
}

if (largestFieldSize == LayoutInt.Zero)
{
// CUnion cannot have zero size.
ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadBadFormat, type);
}

if (type.IsInlineArray)
{
// CUnion types cannot be inline arrays
ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadBadFormat, type);
}

SizeAndAlignment instanceByteSizeAndAlignment;
var instanceSizeAndAlignment = ComputeInstanceSize(
type,
largestFieldSize,
largestAlignmentRequirement,
classLayoutSize: 0, // CUnion does not use the size from metadata.
out instanceByteSizeAndAlignment);

ComputedInstanceFieldLayout computedLayout = new ComputedInstanceFieldLayout
{
IsAutoLayoutOrHasAutoLayoutFields = false,
IsInt128OrHasInt128Fields = hasInt128Field,
IsVectorTOrHasVectorTFields = hasVectorTField,
FieldAlignment = instanceSizeAndAlignment.Alignment,
FieldSize = instanceSizeAndAlignment.Size,
ByteCountUnaligned = instanceByteSizeAndAlignment.Size,
ByteCountAlignment = instanceByteSizeAndAlignment.Alignment,
Offsets = offsets,
LayoutAbiStable = layoutAbiStable
};

return computedLayout;
}

private static void AdjustForInlineArray(
MetadataType type,
int instanceFieldCount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ public enum MetadataLayoutKind
Auto,
Sequential,
Explicit,
CStruct
CStruct,
CUnion
}
}
3 changes: 3 additions & 0 deletions src/coreclr/tools/Common/TypeSystem/Ecma/EcmaType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,9 @@ public override ClassLayoutMetadata GetClassLayout()
case 0:
layoutKind = MetadataLayoutKind.CStruct;
break;
case 1:
layoutKind = MetadataLayoutKind.CUnion;
break;
default:
ThrowHelper.ThrowTypeLoadException(this);
return default; // Invalid kind value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public static bool IsBlittableType(TypeDesc type)

if (mdType.IsExtendedLayout)
{
return mdType.GetClassLayout().Kind is MetadataLayoutKind.CStruct;
return mdType.GetClassLayout().Kind is MetadataLayoutKind.CStruct or MetadataLayoutKind.CUnion;
}

if (mdType.IsAutoLayout)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ protected override ComputedInstanceFieldLayout ComputeInstanceFieldLayout(Metada
// all types without GC references (ie C# unmanaged types).
MetadataLayoutKind.Sequential when !type.ContainsGCPointers => ComputeSequentialFieldLayout(type, numInstanceFields, layoutMetadata),
MetadataLayoutKind.CStruct => ComputeCStructFieldLayout(type, numInstanceFields),
MetadataLayoutKind.CUnion => ComputeCUnionFieldLayout(type, numInstanceFields),
_ => ComputeAutoFieldLayout(type, numInstanceFields, layoutMetadata),
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,8 @@ protected override ComputedInstanceFieldLayout ComputeInstanceFieldLayout(Metada
{
case MetadataLayoutKind.CStruct:
return ComputeCStructFieldLayout(type, numInstanceFields);
case MetadataLayoutKind.CUnion:
return ComputeCUnionFieldLayout(type, numInstanceFields);
case MetadataLayoutKind.Explicit:
// Works around https://github.com/dotnet/runtime/issues/102868
if (type is { IsValueType: false, BaseType.IsSequentialLayout: true })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ protected override ComputedInstanceFieldLayout ComputeInstanceFieldLayout(Metada
{
return ComputeCStructFieldLayout(type, numInstanceFields);
}
else if (layoutMetadata.Kind == MetadataLayoutKind.CUnion)
{
return ComputeCUnionFieldLayout(type, numInstanceFields);
}
else
{
return ComputeAutoFieldLayout(type, numInstanceFields, layoutMetadata);
Expand Down
9 changes: 8 additions & 1 deletion src/coreclr/vm/class.h
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,8 @@ class EEClassLayoutInfo
Auto = 0, // Make sure Auto is the default value as the default-constructed value represents the "auto layout" case
Sequential,
Explicit,
CStruct
CStruct,
CUnion
};
private:
enum {
Expand Down Expand Up @@ -496,6 +497,12 @@ class EEClassLayoutInfo
ULONG cFields
);

ULONG InitializeCUnionFieldLayout(
FieldDesc* pFields,
MethodTable** pByValueClassCache,
ULONG cFields
);

private:
void SetIsZeroSized(BOOL isZeroSized)
{
Expand Down
39 changes: 39 additions & 0 deletions src/coreclr/vm/classlayoutinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,45 @@ ULONG EEClassLayoutInfo::InitializeCStructFieldLayout(
return SetInstanceBytesSize(managedSize);
}

ULONG EEClassLayoutInfo::InitializeCUnionFieldLayout(
FieldDesc* pFields,
MethodTable** pByValueClassCache,
ULONG cFields
)
{
STANDARD_VM_CONTRACT;

SetLayoutType(LayoutType::CUnion);

NewArrayHolder<LayoutRawFieldInfo> pInfoArray = new LayoutRawFieldInfo[cFields + 1];
UINT32 numInstanceFields;
BYTE fieldsAlignmentRequirement;
InitializeLayoutFieldInfoArray(pFields, cFields, pByValueClassCache, DEFAULT_PACKING_SIZE, pInfoArray, &numInstanceFields, &fieldsAlignmentRequirement);

BYTE alignmentRequirement = max<BYTE>(1, fieldsAlignmentRequirement);

SetAlignmentRequirement(alignmentRequirement);
SetPackingSize(DEFAULT_PACKING_SIZE);

// For a union, all fields are placed at offset 0
// and the size is the maximum of all field sizes
UINT32 maxFieldSize = 0;
for (UINT32 i = 0; i < numInstanceFields; i++)
{
pInfoArray[i].m_placement.m_offset = 0;
if (pInfoArray[i].m_placement.m_size > maxFieldSize)
{
maxFieldSize = pInfoArray[i].m_placement.m_size;
}
}

SetFieldOffsets(pFields, cFields, pInfoArray, numInstanceFields);

UINT32 managedSize = AlignSize(maxFieldSize, alignmentRequirement);

return SetInstanceBytesSize(managedSize);
}

namespace
{
#ifdef UNIX_AMD64_ABI
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3724,6 +3724,9 @@ uint32_t CEEInfo::getClassAttribsInternal (CORINFO_CLASS_HANDLE clsHnd)

if (pClass->IsUnsafeValueClass())
ret |= CORINFO_FLG_UNSAFE_VALUECLASS;

if (pClass->HasLayout() && pClass->GetLayoutInfo()->GetLayoutType() == EEClassLayoutInfo::LayoutType::CUnion)
ret |= CORINFO_FLG_OVERLAPPING_FIELDS;
}
if (pClass->HasExplicitFieldOffsetLayout() && pClass->HasOverlaidField())
ret |= CORINFO_FLG_OVERLAPPING_FIELDS;
Expand Down
55 changes: 54 additions & 1 deletion src/coreclr/vm/methodtablebuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8319,10 +8319,11 @@ VOID MethodTableBuilder::PlaceInstanceFields(MethodTable** pByValueClassCache)
{
if (!pParentMT->IsValueTypeClass()
|| hasGCFields
|| bmtFP->fIsByRefLikeType
|| isAutoLayoutOrHasAutoLayoutField)
{
// CStruct layout types can't have a parent type, GC fields
// or auto layout fields.
// byreflike types, or auto layout fields.
BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT);
}

Expand All @@ -8335,6 +8336,27 @@ VOID MethodTableBuilder::PlaceInstanceFields(MethodTable** pByValueClassCache)
break;
}

case EEClassLayoutInfo::LayoutType::CUnion:
{
if (!pParentMT->IsValueTypeClass()
|| hasGCFields
|| bmtFP->fIsByRefLikeType
|| isAutoLayoutOrHasAutoLayoutField)
{
// CUnion layout types can't have a parent type, GC fields
// byreflike types, or auto layout fields.
BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT);
}

// Explicit size is not used for CUnion layout.
pLayoutInfo->SetHasExplicitSize(FALSE);
// CUnion layouts are always blittable
pLayoutInfo->SetIsBlittable(TRUE);

HandleCUnionLayout(pByValueClassCache);
break;
}

default:
UNREACHABLE();
break;
Expand Down Expand Up @@ -8824,6 +8846,33 @@ VOID MethodTableBuilder::HandleCStructLayout(MethodTable** pByValueClassCache)
}
}

VOID MethodTableBuilder::HandleCUnionLayout(MethodTable** pByValueClassCache)
{
STANDARD_VM_CONTRACT;

_ASSERTE(HasLayout());

EEClassLayoutInfo* pLayoutInfo = GetLayoutInfo();

CONSISTENCY_CHECK(pLayoutInfo != nullptr);

bmtFP->NumInstanceFieldBytes = pLayoutInfo->InitializeCUnionFieldLayout(
GetHalfBakedClass()->GetFieldDescList(),
pByValueClassCache,
bmtEnumFields->dwNumDeclaredFields
);

if (bmtFP->NumInlineArrayElements != 0)
{
BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT);
}

if (pLayoutInfo->IsZeroSized())
{
BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT);
}
}

//*******************************************************************************
// this accesses the field size which is temporarily stored in m_pMTOfEnclosingClass
// during class loading. Don't use any other time
Expand Down Expand Up @@ -12572,6 +12621,10 @@ BOOL HasLayoutMetadata(Assembly* pAssembly, IMDInternalImport* pInternalImport,
{
*pLayoutType = EEClassLayoutInfo::LayoutType::CStruct;
}
else if (kind == CorExtendedLayoutKind::CUnion)
{
*pLayoutType = EEClassLayoutInfo::LayoutType::CUnion;
}
else
{
pAssembly->ThrowTypeLoadException(pInternalImport, cl, IDS_CLASSLOAD_BADFORMAT);
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/vm/methodtablebuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -3000,6 +3000,9 @@ class MethodTableBuilder
VOID HandleCStructLayout(
MethodTable **);

VOID HandleCUnionLayout(
MethodTable **);

VOID CheckForHFA(MethodTable ** pByValueClassCache);

VOID CheckForNativeHFA();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,11 @@ public enum ExtendedLayoutKind
/// The value type should have its fields laid out in accordance with the C language struct layout rules.
/// </summary>
CStruct = 0,

/// <summary>
/// The value type should have its fields laid out like a C union, where all fields are placed at offset 0.
/// This layout only supports value types without GC pointers.
/// </summary>
CUnion = 1,
}
}
1 change: 1 addition & 0 deletions src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14534,6 +14534,7 @@ public ExtendedLayoutAttribute(System.Runtime.InteropServices.ExtendedLayoutKind
public enum ExtendedLayoutKind
{
CStruct = 0,
CUnion = 1,
}
public partial class ExternalException : System.SystemException
{
Expand Down
Loading