@@ -944,6 +944,14 @@ public override ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristi
944944 return ComputeHomogeneousAggregateCharacteristic ( type ) ;
945945 }
946946
947+ /// <summary>
948+ /// Identify whether a given type is a homogeneous floating-point aggregate. This code must be
949+ /// kept in sync with the CoreCLR runtime method EEClass::CheckForHFA, as of this change it
950+ /// can be found at
951+ /// https://github.com/dotnet/runtime/blob/1928cd2b65c04ebe6fe528d4ebb581e46f1fed47/src/coreclr/vm/class.cpp#L1567
952+ /// </summary>
953+ /// <param name="type">Type to analyze</param>
954+ /// <returns>HFA classification of the type parameter</returns>
947955 private static ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic ( DefType type )
948956 {
949957 // Use this constant to make the code below more laconic
@@ -959,12 +967,7 @@ private static ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacte
959967 return NotHA ;
960968
961969 MetadataType metadataType = ( MetadataType ) type ;
962-
963- // No HAs with explicit layout. There may be cases where explicit layout may be still
964- // eligible for HA, but it is hard to tell the real intent. Make it simple and just
965- // unconditionally disable HAs for explicit layout.
966- if ( metadataType . IsExplicitLayout )
967- return NotHA ;
970+ int haElementSize = 0 ;
968971
969972 switch ( metadataType . Category )
970973 {
@@ -977,12 +980,18 @@ private static ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacte
977980 case TypeFlags . ValueType :
978981 // Find the common HA element type if any
979982 ValueTypeShapeCharacteristics haResultType = NotHA ;
983+ bool hasZeroOffsetField = false ;
980984
981985 foreach ( FieldDesc field in metadataType . GetFields ( ) )
982986 {
983987 if ( field . IsStatic )
984988 continue ;
985989
990+ if ( field . Offset == LayoutInt . Zero )
991+ {
992+ hasZeroOffsetField = true ;
993+ }
994+
986995 // If a field isn't a DefType, then this type cannot be a HA type
987996 if ( ! ( field . FieldType is DefType fieldType ) )
988997 return NotHA ;
@@ -996,6 +1005,15 @@ private static ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacte
9961005 {
9971006 // If we hadn't yet figured out what form of HA this type might be, we've now found one case
9981007 haResultType = haFieldType ;
1008+
1009+ haElementSize = haResultType switch
1010+ {
1011+ ValueTypeShapeCharacteristics . Float32Aggregate => 4 ,
1012+ ValueTypeShapeCharacteristics . Float64Aggregate => 8 ,
1013+ ValueTypeShapeCharacteristics . Vector64Aggregate => 8 ,
1014+ ValueTypeShapeCharacteristics . Vector128Aggregate => 16 ,
1015+ _ => throw new ArgumentOutOfRangeException ( )
1016+ } ;
9991017 }
10001018 else if ( haResultType != haFieldType )
10011019 {
@@ -1004,21 +1022,17 @@ private static ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacte
10041022 // be a HA type.
10051023 return NotHA ;
10061024 }
1025+
1026+ if ( field . Offset . IsIndeterminate || field . Offset . AsInt % haElementSize != 0 )
1027+ {
1028+ return NotHA ;
1029+ }
10071030 }
10081031
1009- // If there are no instance fields, this is not a HA type
1010- if ( haResultType == NotHA )
1032+ // If the struct doesn't have a zero-offset field, it's not an HFA.
1033+ if ( ! hasZeroOffsetField )
10111034 return NotHA ;
10121035
1013- int haElementSize = haResultType switch
1014- {
1015- ValueTypeShapeCharacteristics . Float32Aggregate => 4 ,
1016- ValueTypeShapeCharacteristics . Float64Aggregate => 8 ,
1017- ValueTypeShapeCharacteristics . Vector64Aggregate => 8 ,
1018- ValueTypeShapeCharacteristics . Vector128Aggregate => 16 ,
1019- _ => throw new ArgumentOutOfRangeException ( )
1020- } ;
1021-
10221036 // Types which are indeterminate in field size are not considered to be HA
10231037 if ( type . InstanceFieldSize . IsIndeterminate )
10241038 return NotHA ;
@@ -1027,8 +1041,13 @@ private static ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacte
10271041 // - Type of fields can be HA valuetype itself.
10281042 // - Managed C++ HA valuetypes have just one <alignment member> of type float to signal that
10291043 // the valuetype is HA and explicitly specified size.
1030- int maxSize = haElementSize * type . Context . Target . MaxHomogeneousAggregateElementCount ;
1031- if ( type . InstanceFieldSize . AsInt > maxSize )
1044+ int totalSize = type . InstanceFieldSize . AsInt ;
1045+
1046+ if ( totalSize % haElementSize != 0 )
1047+ return NotHA ;
1048+
1049+ // On ARM, HFAs can have a maximum of four fields regardless of whether those are float or double.
1050+ if ( totalSize > haElementSize * type . Context . Target . MaxHomogeneousAggregateElementCount )
10321051 return NotHA ;
10331052
10341053 // All the tests passed. This is a HA type.
0 commit comments