Skip to content

Commit 6f119cb

Browse files
authored
Fix HFA detection in Crossgen2 (#80218)
According to customer feedback some WPF apps are crashing on arm64 at runtime in debug mode when compiled with Crossgen2. Based on the initial investigation by Anton Lapounov and with help from Jan Vorlicek I have managed to identify that the problem is caused by a mismatch between the native CoreCLR runtime and Crossgen2 w.r.t. identification of HFA types. This change puts Crossgen2 behavior in sync with the CoreCLR runtime. I have verified locally that this makes the GC ref map for the method System.Windows.Media.PathGeometry.GetPathBoundsAsRB identical with the runtime version and avoids the assertion failure that was previously triggered in debug CoreCLR builds due to this mismatch. Thanks Tomas
1 parent ebd2cb6 commit 6f119cb

File tree

1 file changed

+38
-19
lines changed

1 file changed

+38
-19
lines changed

src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)