diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs index a2d2eaccdae46..f3b7b9442f5bb 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs @@ -944,6 +944,14 @@ public override ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristi return ComputeHomogeneousAggregateCharacteristic(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 @@ -959,12 +967,7 @@ private static ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacte 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) { @@ -977,12 +980,18 @@ private static ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacte 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; @@ -996,6 +1005,15 @@ private static ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacte { // 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) { @@ -1004,21 +1022,17 @@ private static ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacte // 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; @@ -1027,8 +1041,13 @@ private static ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacte // - 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.