diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs
index 5ff6417e52e41..1b779dea61bdf 100644
--- a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs
+++ b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs
@@ -945,7 +945,15 @@ public override ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristi
return ComputeHomogeneousAggregateCharacteristic(type);
}
- private ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic(DefType type)
+ ///
+ /// Identify whether a given type is a homogeneous floating-point aggregate. This code must be
+ /// kept in sync with the CoreCLR runtime method EEClass::CheckForHFA, as of this change it
+ /// can be found at
+ /// https://github.com/dotnet/runtime/blob/1928cd2b65c04ebe6fe528d4ebb581e46f1fed47/src/coreclr/vm/class.cpp#L1567
+ ///
+ /// Type to analyze
+ /// HFA classification of the type parameter
+ private static ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic(DefType type)
{
// Use this constant to make the code below more laconic
const ValueTypeShapeCharacteristics NotHA = ValueTypeShapeCharacteristics.None;
@@ -960,12 +968,7 @@ private ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic(
return NotHA;
MetadataType metadataType = (MetadataType)type;
-
- // No HAs with explicit layout. There may be cases where explicit layout may be still
- // eligible for HA, but it is hard to tell the real intent. Make it simple and just
- // unconditionally disable HAs for explicit layout.
- if (metadataType.IsExplicitLayout)
- return NotHA;
+ int haElementSize = 0;
switch (metadataType.Category)
{
@@ -978,12 +981,18 @@ private ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic(
case TypeFlags.ValueType:
// Find the common HA element type if any
ValueTypeShapeCharacteristics haResultType = NotHA;
+ bool hasZeroOffsetField = false;
foreach (FieldDesc field in metadataType.GetFields())
{
if (field.IsStatic)
continue;
+ if (field.Offset == LayoutInt.Zero)
+ {
+ hasZeroOffsetField = true;
+ }
+
// If a field isn't a DefType, then this type cannot be a HA type
if (!(field.FieldType is DefType fieldType))
return NotHA;
@@ -997,6 +1006,15 @@ private ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic(
{
// If we hadn't yet figured out what form of HA this type might be, we've now found one case
haResultType = haFieldType;
+
+ haElementSize = haResultType switch
+ {
+ ValueTypeShapeCharacteristics.Float32Aggregate => 4,
+ ValueTypeShapeCharacteristics.Float64Aggregate => 8,
+ ValueTypeShapeCharacteristics.Vector64Aggregate => 8,
+ ValueTypeShapeCharacteristics.Vector128Aggregate => 16,
+ _ => throw new ArgumentOutOfRangeException()
+ };
}
else if (haResultType != haFieldType)
{
@@ -1005,21 +1023,17 @@ private ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic(
// be a HA type.
return NotHA;
}
+
+ if (field.Offset.IsIndeterminate || field.Offset.AsInt % haElementSize != 0)
+ {
+ return NotHA;
+ }
}
- // If there are no instance fields, this is not a HA type
- if (haResultType == NotHA)
+ // If the struct doesn't have a zero-offset field, it's not an HFA.
+ if (!hasZeroOffsetField)
return NotHA;
- int haElementSize = haResultType switch
- {
- ValueTypeShapeCharacteristics.Float32Aggregate => 4,
- ValueTypeShapeCharacteristics.Float64Aggregate => 8,
- ValueTypeShapeCharacteristics.Vector64Aggregate => 8,
- ValueTypeShapeCharacteristics.Vector128Aggregate => 16,
- _ => throw new ArgumentOutOfRangeException()
- };
-
// Types which are indeterminate in field size are not considered to be HA
if (type.InstanceFieldSize.IsIndeterminate)
return NotHA;
@@ -1028,8 +1042,13 @@ private ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic(
// - Type of fields can be HA valuetype itself.
// - Managed C++ HA valuetypes have just one of type float to signal that
// the valuetype is HA and explicitly specified size.
- int maxSize = haElementSize * type.Context.Target.MaxHomogeneousAggregateElementCount;
- if (type.InstanceFieldSize.AsInt > maxSize)
+ int totalSize = type.InstanceFieldSize.AsInt;
+
+ if (totalSize % haElementSize != 0)
+ return NotHA;
+
+ // On ARM, HFAs can have a maximum of four fields regardless of whether those are float or double.
+ if (totalSize > haElementSize * type.Context.Target.MaxHomogeneousAggregateElementCount)
return NotHA;
// All the tests passed. This is a HA type.