Skip to content

Commit 9214958

Browse files
authored
Manual .NET 6 backport of #80218 fixing #79327 (#80872)
Description: This change puts HFA calculation in Crossgen2 in sync with native CoreCLR runtime for value types with explicit layout. Previously Crossgen2 had a shortcut in the routine deciding that structs with explicit layouts are never marked as HFA that disagreed with the CoreCLR runtime; consequently, on arm64, Crossgen2 disagreed with the runtime on whether or not a function returning such a type should allocate the stack slot for return value, basically messing up the calling convention and GC refmap, resulting in various random AVs and corruptions. This was first observed by an internal customer in WPF apps where MilRectD is the type in question, later JanK filed the issue 79327 for the same problem. Customer impact: Random runtime crashes on arm64. Regression: Nope, I believe the incomplete implementation was the original one, this change just "improves it" by putting it in better sync with the native runtime. I have also added a code comment mentioning that these two need to be kept in sync. Risk: Low - the error in the previous implementation is obvious, R2RDump and my new runtime diagnostics clearly show the GC refmap mismatch caused by this problem and its fixing after applying the Crossgen2 fix. Link to issue: #79327 Link to PR against main: #80218 Publishing impact: In the particular case of the WPF app the problem was in the PresentationCore.dll assembly. The assembly (or rather the entire WPF) need to be recompiled with Crossgen2 with the fix applied for this to take effect. For now I assume that is an automated part of the servicing process. Thanks Tomas
1 parent c03b845 commit 9214958

File tree

1 file changed

+39
-23
lines changed

1 file changed

+39
-23
lines changed

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

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,15 @@ public override ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristi
920920
return result;
921921
}
922922

923-
private ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic(DefType type)
923+
/// <summary>
924+
/// Identify whether a given type is a homogeneous floating-point aggregate. This code must be
925+
/// kept in sync with the CoreCLR runtime method EEClass::CheckForHFA, as of this change it
926+
/// can be found at
927+
/// https://github.com/dotnet/runtime/blob/1928cd2b65c04ebe6fe528d4ebb581e46f1fed47/src/coreclr/vm/class.cpp#L1567
928+
/// </summary>
929+
/// <param name="type">Type to analyze</param>
930+
/// <returns>HFA classification of the type parameter</returns>
931+
private static ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic(DefType type)
924932
{
925933
// Use this constant to make the code below more laconic
926934
const ValueTypeShapeCharacteristics NotHA = ValueTypeShapeCharacteristics.None;
@@ -931,16 +939,8 @@ private ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic(
931939
if ((targetArch != TargetArchitecture.ARM) && (targetArch != TargetArchitecture.ARM64))
932940
return NotHA;
933941

934-
if (type.Context.Target.Abi == TargetAbi.CoreRTArmel)
935-
return NotHA;
936-
937942
MetadataType metadataType = (MetadataType)type;
938-
939-
// No HAs with explicit layout. There may be cases where explicit layout may be still
940-
// eligible for HA, but it is hard to tell the real intent. Make it simple and just
941-
// unconditionally disable HAs for explicit layout.
942-
if (metadataType.IsExplicitLayout)
943-
return NotHA;
943+
int haElementSize = 0;
944944

945945
switch (metadataType.Category)
946946
{
@@ -953,12 +953,18 @@ private ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic(
953953
case TypeFlags.ValueType:
954954
// Find the common HA element type if any
955955
ValueTypeShapeCharacteristics haResultType = NotHA;
956+
bool hasZeroOffsetField = false;
956957

957958
foreach (FieldDesc field in metadataType.GetFields())
958959
{
959960
if (field.IsStatic)
960961
continue;
961962

963+
if (field.Offset == LayoutInt.Zero)
964+
{
965+
hasZeroOffsetField = true;
966+
}
967+
962968
// If a field isn't a DefType, then this type cannot be a HA type
963969
if (!(field.FieldType is DefType fieldType))
964970
return NotHA;
@@ -972,6 +978,15 @@ private ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic(
972978
{
973979
// If we hadn't yet figured out what form of HA this type might be, we've now found one case
974980
haResultType = haFieldType;
981+
982+
haElementSize = haResultType switch
983+
{
984+
ValueTypeShapeCharacteristics.Float32Aggregate => 4,
985+
ValueTypeShapeCharacteristics.Float64Aggregate => 8,
986+
ValueTypeShapeCharacteristics.Vector64Aggregate => 8,
987+
ValueTypeShapeCharacteristics.Vector128Aggregate => 16,
988+
_ => throw new ArgumentOutOfRangeException()
989+
};
975990
}
976991
else if (haResultType != haFieldType)
977992
{
@@ -980,21 +995,17 @@ private ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic(
980995
// be a HA type.
981996
return NotHA;
982997
}
998+
999+
if (field.Offset.IsIndeterminate || field.Offset.AsInt % haElementSize != 0)
1000+
{
1001+
return NotHA;
1002+
}
9831003
}
9841004

985-
// If there are no instance fields, this is not a HA type
986-
if (haResultType == NotHA)
1005+
// If the struct doesn't have a zero-offset field, it's not an HFA.
1006+
if (!hasZeroOffsetField)
9871007
return NotHA;
9881008

989-
int haElementSize = haResultType switch
990-
{
991-
ValueTypeShapeCharacteristics.Float32Aggregate => 4,
992-
ValueTypeShapeCharacteristics.Float64Aggregate => 8,
993-
ValueTypeShapeCharacteristics.Vector64Aggregate => 8,
994-
ValueTypeShapeCharacteristics.Vector128Aggregate => 16,
995-
_ => throw new ArgumentOutOfRangeException()
996-
};
997-
9981009
// Types which are indeterminate in field size are not considered to be HA
9991010
if (type.InstanceFieldSize.IsIndeterminate)
10001011
return NotHA;
@@ -1003,8 +1014,13 @@ private ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic(
10031014
// - Type of fields can be HA valuetype itself.
10041015
// - Managed C++ HA valuetypes have just one <alignment member> of type float to signal that
10051016
// the valuetype is HA and explicitly specified size.
1006-
int maxSize = haElementSize * type.Context.Target.MaxHomogeneousAggregateElementCount;
1007-
if (type.InstanceFieldSize.AsInt > maxSize)
1017+
int totalSize = type.InstanceFieldSize.AsInt;
1018+
1019+
if (totalSize % haElementSize != 0)
1020+
return NotHA;
1021+
1022+
// On ARM, HFAs can have a maximum of four fields regardless of whether those are float or double.
1023+
if (totalSize > haElementSize * type.Context.Target.MaxHomogeneousAggregateElementCount)
10081024
return NotHA;
10091025

10101026
// All the tests passed. This is a HA type.

0 commit comments

Comments
 (0)