return ComputeHomogeneousAggregateCharacteristic(type);
}
+ /// <summary>
+ /// 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
+ /// </summary>
+ /// <param name="type">Type to analyze</param>
+ /// <returns>HFA classification of the type parameter</returns>
private static ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic(DefType type)
{
// Use this constant to make the code below more laconic
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)
{
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;
{
// 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)
{
// 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;
// - Type of fields can be HA valuetype itself.
// - Managed C++ HA valuetypes have just one <alignment member> 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.