Skip to content

Commit

Permalink
Fix HFA detection in Crossgen2 (#80218)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
trylek authored Jan 9, 2023
1 parent ebd2cb6 commit 6f119cb
Showing 1 changed file with 38 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,14 @@ public override ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristi
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
Expand All @@ -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)
{
Expand All @@ -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;
Expand All @@ -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)
{
Expand All @@ -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;
Expand All @@ -1027,8 +1041,13 @@ private static ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacte
// - 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.
Expand Down

0 comments on commit 6f119cb

Please sign in to comment.