• Change FieldLayoutAlgorithm.ComputeValueTypeShapeCharacteristics method to compute the homogeneous aggregate element type and cache it in the existing field. That allows to remove all ComputeHomogeneousFloatAggregateElementType methods.
• Change MetadataFieldLayoutAlgorithm.ComputeHomogeneousAggregateCharacteristic to compute HVAs in addition to HFAs.
• Change CorInfoImpl.getHFAType JIT callback to handle HVAs. Note that returning ELEMENT_TYPE_VALUETYPE indicates the TYP_SIMD16 type (see Compiler::GetHfaType).
• Change TypeFixupSignature.EncodeTypeLayout to handle HVAs.
• Support HVAs in the ArgIterator class.
• Fix TransitionBlock.OffsetFromGCRefMapPos for ARM64. R2RDump used to dump incorrect offsets.
• Use TransitionBlock.OffsetFromGCRefMapPos in GCRefMapBuilder.GetCallRefMap to simplify logic.
• Remove ARM64 .NET Native-specific code from TransitionBlock.cs and ArgIterator.cs files.
Minor:
• Remove a redundant GetVectorSize check in MethodTable::GetHFAType.
• Improve an assertion in Compiler::raUpdateRegStateForArg.
• Fix comments in JIT code.
// If the argType is a struct, then check if it is an HFA
if (varTypeIsStruct(argType))
{
- // hfaType is set to float, double or SIMD type if it is an HFA, otherwise TYP_UNDEF.
+ // hfaType is set to float, double, or SIMD type if it is an HFA, otherwise TYP_UNDEF
hfaType = GetHfaType(typeHnd);
isHfaArg = varTypeIsValidHfaType(hfaType);
}
if (isHfaArg)
{
- // We have an HFA argument, so from here on out treat the type as a float, double or vector.
- // The orginal struct type is available by using origArgType
- // We also update the cSlots to be the number of float/double fields in the HFA
+ // We have an HFA argument, so from here on out treat the type as a float, double, or vector.
+ // The orginal struct type is available by using origArgType.
+ // We also update the cSlots to be the number of float/double/vector fields in the HFA.
argType = hfaType;
varDsc->SetHfaType(hfaType);
cSlots = varDsc->lvHfaSlots();
}
#endif // FEATURE_SIMD
#ifdef FEATURE_HFA
- // for structs that are small enough, we check and set lvIsHfa and lvHfaTypeIsFloat
+ // For structs that are small enough, we check and set HFA element type
if (varDsc->lvExactSize <= MAX_PASS_MULTIREG_BYTES)
{
- var_types hfaType = GetHfaType(typeHnd); // set to float or double if it is an HFA, otherwise TYP_UNDEF
+ // hfaType is set to float, double or SIMD type if it is an HFA, otherwise TYP_UNDEF
+ var_types hfaType = GetHfaType(typeHnd);
if (varTypeIsValidHfaType(hfaType))
{
varDsc->SetHfaType(hfaType);
}
else
{
+ assert(!regState->rsIsFloat);
unsigned cSlots = argDsc->lvSize() / TARGET_POINTER_SIZE;
for (unsigned i = 1; i < cSlots; i++)
{
{
break;
}
- assert(regState->rsIsFloat == false);
regState->rsCalleeRegArgMaskLiveIn |= genRegMask(nextArgReg);
}
}
public override ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristics(DefType type)
{
- return _fallbackAlgorithm.ComputeValueTypeShapeCharacteristics(type);
- }
-
- public override DefType ComputeHomogeneousFloatAggregateElementType(DefType type)
- {
- return _fallbackAlgorithm.ComputeHomogeneousFloatAggregateElementType(type);
+ if (type.Context.Target.Architecture == TargetArchitecture.ARM64)
+ {
+ return type.InstanceFieldSize.AsInt switch
+ {
+ 8 => ValueTypeShapeCharacteristics.Vector64Aggregate,
+ 16 => ValueTypeShapeCharacteristics.Vector128Aggregate,
+ 32 => ValueTypeShapeCharacteristics.Vector256Aggregate,
+ _ => ValueTypeShapeCharacteristics.None
+ };
+ }
+ return ValueTypeShapeCharacteristics.None;
}
public static bool IsVectorType(DefType type)
type.Namespace == "System.Runtime.Intrinsics" &&
(type.Name == "Vector64`1" ||
type.Name == "Vector128`1" ||
- type.Name == "Vector256`1");
+ type.Name == "Vector256`1") &&
+ type.Instantiation[0].IsPrimitive;
}
}
}
private ExceptionDispatchInfo _lastException;
- [DllImport(JitLibrary, CallingConvention=CallingConvention.StdCall)] // stdcall in CoreCLR!
+ [DllImport(JitLibrary, CallingConvention = CallingConvention.StdCall)] // stdcall in CoreCLR!
private extern static IntPtr jitStartup(IntPtr host);
- [DllImport(JitLibrary, CallingConvention=CallingConvention.StdCall)]
+ [DllImport(JitLibrary, CallingConvention = CallingConvention.StdCall)]
private extern static IntPtr getJit();
[DllImport(JitSupportLibrary)]
// This optimization may cause static fields in reference types to be accessed without cctor being triggered
// for NULL "this" object. It does not conform with what the spec says. However, we have been historically
// doing it for perf reasons.
- if (!typeToInit.IsValueType && ! typeToInit.IsInterface && !typeToInit.IsBeforeFieldInit)
+ if (!typeToInit.IsValueType && !typeToInit.IsInterface && !typeToInit.IsBeforeFieldInit)
{
if (typeToInit == typeFromContext(context) || typeToInit == MethodBeingCompiled.OwningType)
{
{
var td = HandleToObject(cls) as ArrayType;
Debug.Assert(td != null);
- return (uint) td.Rank;
+ return (uint)td.Rank;
}
private void* getArrayInitializationData(CORINFO_FIELD_STRUCT_* field, uint size)
private CorInfoType getHFAType(CORINFO_CLASS_STRUCT_* hClass)
{
var type = (DefType)HandleToObject(hClass);
- return type.IsHfa ? asCorInfoType(type.HfaElementType) : CorInfoType.CORINFO_TYPE_UNDEF;
+
+ // For 8-byte vectors return CORINFO_TYPE_DOUBLE, which is mapped by JIT to SIMD8.
+ // Otherwise, return CORINFO_TYPE_VALUECLASS, which is mapped by JIT to SIMD16.
+ // See MethodTable::GetHFAType and Compiler::GetHfaType.
+ return (type.ValueTypeShapeCharacteristics & ValueTypeShapeCharacteristics.AggregateMask) switch
+ {
+ ValueTypeShapeCharacteristics.Float32Aggregate => CorInfoType.CORINFO_TYPE_FLOAT,
+ ValueTypeShapeCharacteristics.Float64Aggregate => CorInfoType.CORINFO_TYPE_DOUBLE,
+ ValueTypeShapeCharacteristics.Vector64Aggregate => CorInfoType.CORINFO_TYPE_DOUBLE,
+ ValueTypeShapeCharacteristics.Vector128Aggregate => CorInfoType.CORINFO_TYPE_VALUECLASS,
+ ValueTypeShapeCharacteristics.Vector256Aggregate => CorInfoType.CORINFO_TYPE_VALUECLASS,
+ _ => CorInfoType.CORINFO_TYPE_UNDEF
+ };
}
private HRESULT GetErrorHRESULT(_EXCEPTION_POINTERS* pExceptionPointers)
}
}
- /// <summary>
- /// Gets a value indicating whether the fields of the type satisfy the Homogeneous Float Aggregate classification.
- /// </summary>
- public bool IsHfa
+ public ValueTypeShapeCharacteristics ValueTypeShapeCharacteristics
{
get
{
{
ComputeValueTypeShapeCharacteristics();
}
- return (_valueTypeShapeCharacteristics & ValueTypeShapeCharacteristics.HomogenousFloatAggregate) != 0;
+ return _valueTypeShapeCharacteristics;
}
}
- internal ValueTypeShapeCharacteristics ValueTypeShapeCharacteristics
+ private void ComputeValueTypeShapeCharacteristics()
{
- get
- {
- if (!_fieldLayoutFlags.HasFlags(FieldLayoutFlags.ComputedValueTypeShapeCharacteristics))
- {
- ComputeValueTypeShapeCharacteristics();
- }
- return _valueTypeShapeCharacteristics;
- }
+ _valueTypeShapeCharacteristics = this.Context.GetLayoutAlgorithmForType(this).ComputeValueTypeShapeCharacteristics(this);
+ _fieldLayoutFlags.AddFlags(FieldLayoutFlags.ComputedValueTypeShapeCharacteristics);
}
/// <summary>
- /// Get the Homogeneous Float Aggregate element type if this is a HFA type (<see cref="IsHfa"/> is true).
+ /// Gets a value indicating whether the type is a homogeneous floating-point or short-vector aggregate.
/// </summary>
- public DefType HfaElementType
+ public bool IsHomogeneousAggregate
{
get
{
- // We are not caching this because this is rare and not worth wasting space in DefType.
- return this.Context.GetLayoutAlgorithmForType(this).ComputeHomogeneousFloatAggregateElementType(this);
+ return (ValueTypeShapeCharacteristics & ValueTypeShapeCharacteristics.AggregateMask) != 0;
}
}
- private void ComputeValueTypeShapeCharacteristics()
+ /// <summary>
+ /// If the type is a homogeneous floating-point or short-vector aggregate, returns its element size.
+ /// </summary>
+ public int GetHomogeneousAggregateElementSize()
{
- _valueTypeShapeCharacteristics = this.Context.GetLayoutAlgorithmForType(this).ComputeValueTypeShapeCharacteristics(this);
- _fieldLayoutFlags.AddFlags(FieldLayoutFlags.ComputedValueTypeShapeCharacteristics);
+ return (ValueTypeShapeCharacteristics & ValueTypeShapeCharacteristics.AggregateMask) switch
+ {
+ ValueTypeShapeCharacteristics.Float32Aggregate => 4,
+ ValueTypeShapeCharacteristics.Float64Aggregate => 8,
+ ValueTypeShapeCharacteristics.Vector64Aggregate => 8,
+ ValueTypeShapeCharacteristics.Vector128Aggregate => 16,
+ ValueTypeShapeCharacteristics.Vector256Aggregate => 16,
+ _ => throw new InvalidOperationException()
+ };
}
-
public void ComputeInstanceLayout(InstanceLayoutKind layoutKind)
{
if (_fieldLayoutFlags.HasFlags(FieldLayoutFlags.ComputedInstanceTypeFieldsLayout | FieldLayoutFlags.ComputedInstanceTypeLayout))
public abstract bool ComputeContainsGCPointers(DefType type);
/// <summary>
- /// Compute the shape of a valuetype. The shape information is used to control code generation and allocation
- /// (such as vectorization, passing the valuetype by value across method calls, or boxing alignment).
+ /// Compute the shape of a value type. The shape information is used to control code generation and allocation
+ /// (such as vectorization, passing the value type by value across method calls, or boxing alignment).
/// </summary>
public abstract ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristics(DefType type);
-
- /// <summary>
- /// If the type has <see cref="ValueTypeShapeCharacteristics.HomogenousFloatAggregate"/> characteristic, returns
- /// the element type of the homogenous float aggregate. This will either be System.Double or System.Float.
- /// </summary>
- public abstract DefType ComputeHomogeneousFloatAggregateElementType(DefType type);
}
/// <summary>
None = 0x00,
/// <summary>
- /// The structure is an aggregate of floating point values of the same type.
+ /// The type is an aggregate of 32-bit floating-point values.
+ /// </summary>
+ Float32Aggregate = 0x01,
+
+ /// <summary>
+ /// The type is an aggregate of 64-bit floating-point values.
+ /// </summary>
+ Float64Aggregate = 0x02,
+
+ /// <summary>
+ /// The type is an aggregate of 64-bit short-vector values.
+ /// </summary>
+ Vector64Aggregate = 0x04,
+
+ /// <summary>
+ /// The type is an aggregate of 128-bit short-vector values.
+ /// </summary>
+ Vector128Aggregate = 0x08,
+
+ /// <summary>
+ /// The type is an aggregate of 256-bit short-vector values.
+ /// </summary>
+ Vector256Aggregate = 0x10,
+
+ /// <summary>
+ /// The mask for homogeneous aggregates of floating-point values.
+ /// </summary>
+ FloatingPointAggregateMask = Float32Aggregate | Float64Aggregate,
+
+ /// <summary>
+ /// The mask for homogeneous aggregates of short-vector values.
+ /// </summary>
+ ShortVectorAggregateMask = Vector64Aggregate | Vector128Aggregate | Vector256Aggregate,
+
+ /// <summary>
+ /// The mask for homogeneous aggregates.
/// </summary>
- HomogenousFloatAggregate = 0x01,
+ AggregateMask = FloatingPointAggregateMask | ShortVectorAggregateMask,
}
}
if (!type.IsValueType)
return ValueTypeShapeCharacteristics.None;
- ValueTypeShapeCharacteristics result = ComputeHomogeneousFloatAggregateCharacteristic(type);
+ ValueTypeShapeCharacteristics result = ComputeHomogeneousAggregateCharacteristic(type);
// TODO: System V AMD64 characteristics (https://github.com/dotnet/corert/issues/158)
return result;
}
- private ValueTypeShapeCharacteristics ComputeHomogeneousFloatAggregateCharacteristic(DefType type)
+ private ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic(DefType type)
{
+ // Use this constant to make the code below more laconic
+ const ValueTypeShapeCharacteristics NotHA = ValueTypeShapeCharacteristics.None;
+
Debug.Assert(type.IsValueType);
- MetadataType metadataType = (MetadataType)type;
+ TargetArchitecture targetArch = type.Context.Target.Architecture;
+ if ((targetArch != TargetArchitecture.ARM) && (targetArch != TargetArchitecture.ARM64))
+ return NotHA;
- if (type.Context.Target.Architecture != TargetArchitecture.ARM && type.Context.Target.Architecture != TargetArchitecture.ARM64)
- return ValueTypeShapeCharacteristics.None;
+ MetadataType metadataType = (MetadataType)type;
- // No HFAs with explicit layout. There may be cases where explicit layout may be still
- // eligible for HFA, but it is hard to tell the real intent. Make it simple and just
- // unconditionally disable HFAs for explicit layout.
+ // 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 ValueTypeShapeCharacteristics.None;
+ return NotHA;
switch (metadataType.Category)
{
+ // These are the primitive types that constitute a HFA type
case TypeFlags.Single:
+ return ValueTypeShapeCharacteristics.Float32Aggregate;
case TypeFlags.Double:
- // These are the primitive types that constitute a HFA type.
- return ValueTypeShapeCharacteristics.HomogenousFloatAggregate;
+ return ValueTypeShapeCharacteristics.Float64Aggregate;
case TypeFlags.ValueType:
- DefType expectedElementType = null;
+ // Find the common HA element type if any
+ ValueTypeShapeCharacteristics haResultType = NotHA;
foreach (FieldDesc field in metadataType.GetFields())
{
if (field.IsStatic)
continue;
- // If a field isn't a DefType, then this type cannot be an HFA type
- // If a field isn't a HFA type, then this type cannot be an HFA type
- DefType fieldType = field.FieldType as DefType;
- if (fieldType == null || !fieldType.IsHfa)
- return ValueTypeShapeCharacteristics.None;
+ // If a field isn't a DefType, then this type cannot be a HA type
+ if (!(field.FieldType is DefType fieldType))
+ return NotHA;
+
+ // If a field isn't a HA type, then this type cannot be a HA type
+ ValueTypeShapeCharacteristics haFieldType = fieldType.ValueTypeShapeCharacteristics & ValueTypeShapeCharacteristics.AggregateMask;
+ if (haFieldType == NotHA)
+ return NotHA;
- if (expectedElementType == null)
+ if (haResultType == NotHA)
{
- // If we hadn't yet figured out what form of HFA this type might be, we've
- // now found one case.
- expectedElementType = fieldType.HfaElementType;
- Debug.Assert(expectedElementType != null);
+ // If we hadn't yet figured out what form of HA this type might be, we've now found one case
+ haResultType = haFieldType;
}
- else if (expectedElementType != fieldType.HfaElementType)
+ else if (haResultType != haFieldType)
{
- // If we had already determined the possible HFA type of the current type, but
+ // If we had already determined the possible HA type of the current type, but
// the field we've encountered is not of that type, then the current type cannot
- // be an HFA type.
- return ValueTypeShapeCharacteristics.None;
+ // be a HA type.
+ return NotHA;
}
}
- // No fields means this is not HFA.
- if (expectedElementType == null)
- return ValueTypeShapeCharacteristics.None;
+ // If there are no instance fields, this is not a HA type
+ if (haResultType == NotHA)
+ return NotHA;
- // Types which are indeterminate in field size are not considered to be HFA
- if (expectedElementType.InstanceFieldSize.IsIndeterminate)
- return ValueTypeShapeCharacteristics.None;
+ int haElementSize = haResultType switch
+ {
+ ValueTypeShapeCharacteristics.Float32Aggregate => 4,
+ ValueTypeShapeCharacteristics.Float64Aggregate => 8,
+ ValueTypeShapeCharacteristics.Vector64Aggregate => 8,
+ ValueTypeShapeCharacteristics.Vector128Aggregate => 16,
+ ValueTypeShapeCharacteristics.Vector256Aggregate => 32,
+ _ => throw new ArgumentOutOfRangeException()
+ };
- // Types which are indeterminate in field size are not considered to be HFA
+ // Types which are indeterminate in field size are not considered to be HA
if (type.InstanceFieldSize.IsIndeterminate)
- return ValueTypeShapeCharacteristics.None;
+ return NotHA;
// Note that we check the total size, but do not perform any checks on number of fields:
- // - Type of fields can be HFA valuetype itself
- // - Managed C++ HFA valuetypes have just one <alignment member> of type float to signal that
- // the valuetype is HFA and explicitly specified size
- int maxSize = expectedElementType.InstanceFieldSize.AsInt * expectedElementType.Context.Target.MaximumHfaElementCount;
+ // - 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)
- return ValueTypeShapeCharacteristics.None;
+ return NotHA;
- // All the tests passed. This is an HFA type.
- return ValueTypeShapeCharacteristics.HomogenousFloatAggregate;
+ // All the tests passed. This is a HA type.
+ return haResultType;
}
- return ValueTypeShapeCharacteristics.None;
- }
-
- public override DefType ComputeHomogeneousFloatAggregateElementType(DefType type)
- {
- if (!type.IsHfa)
- return null;
-
- if (type.IsWellKnownType(WellKnownType.Double) || type.IsWellKnownType(WellKnownType.Single))
- return type;
-
- for (; ; )
- {
- Debug.Assert(type.IsValueType);
-
- // All HFA fields have to be of the same HFA type, so we can just return the type of the first field
- TypeDesc firstFieldType = null;
- foreach (var field in type.GetFields())
- {
- if (field.IsStatic)
- continue;
-
- firstFieldType = field.FieldType;
- break;
- }
- Debug.Assert(firstFieldType != null, "Why is IsHfa true on this type?");
-
- switch (firstFieldType.Category)
- {
- case TypeFlags.Single:
- case TypeFlags.Double:
- return (DefType)firstFieldType;
-
- case TypeFlags.ValueType:
- // Drill into the struct and find the type of its first field
- type = (DefType)firstFieldType;
- break;
-
- default:
- Debug.Fail("Why is IsHfa true on this type?");
- return null;
- }
- }
+ return NotHA;
}
private struct SizeAndAlignment
}
/// <summary>
- /// Maximum number of elements in a HFA type.
+ /// Maximum number of elements in a homogeneous aggregate type.
/// </summary>
- public int MaximumHfaElementCount
+ public int MaxHomogeneousAggregateElementCount
{
get
{
- // There is a hard limit of 4 elements on an HFA type, see
+ // There is a hard limit of 4 elements on an HFA/HVA type, see
// https://devblogs.microsoft.com/cppblog/introducing-vector-calling-convention/
+ // and Procedure Call Standard for the Arm 64-bit Architecture.
Debug.Assert(Architecture == TargetArchitecture.ARM ||
Architecture == TargetArchitecture.ARM64 ||
Architecture == TargetArchitecture.X64 ||
return canonicalType.ValueTypeShapeCharacteristics;
}
-
- public override DefType ComputeHomogeneousFloatAggregateElementType(DefType type)
- {
- RuntimeDeterminedType runtimeDeterminedType = (RuntimeDeterminedType)type;
- DefType canonicalType = runtimeDeterminedType.CanonicalType;
-
- return canonicalType.HfaElementType;
- }
}
}
}
return _type.RequiresAlign8();
}
- public bool IsHFA()
+
+ public bool IsHomogeneousAggregate()
{
- if (_type.Context.Target.Architecture != TargetArchitecture.ARM &&
- _type.Context.Target.Architecture != TargetArchitecture.ARM64)
+ TargetArchitecture targetArch = _type.Context.Target.Architecture;
+ if ((targetArch != TargetArchitecture.ARM) && (targetArch != TargetArchitecture.ARM64))
{
return false;
}
{
return false;
}
- return _type is DefType defType && defType.IsHfa;
+ return _type is DefType defType && defType.IsHomogeneousAggregate;
}
- public CorElementType GetHFAType()
+ public int GetHomogeneousAggregateElementSize()
{
- Debug.Assert(IsHFA());
+ Debug.Assert(IsHomogeneousAggregate());
switch (_type.Context.Target.Architecture)
{
case TargetArchitecture.ARM:
- if (RequiresAlign8())
- {
- return CorElementType.ELEMENT_TYPE_R8;
- }
- break;
+ return RequiresAlign8() ? 8 : 4;
case TargetArchitecture.ARM64:
- if (_type is DefType defType && defType.InstanceFieldAlignment.Equals(new LayoutInt(_type.Context.Target.PointerSize)))
- {
- return CorElementType.ELEMENT_TYPE_R8;
- }
- break;
+ return ((DefType)_type).GetHomogeneousAggregateElementSize();
}
- return CorElementType.ELEMENT_TYPE_R4;
+ throw new InvalidOperationException();
}
public CorElementType GetCorElementType()
public int m_idxGenReg; // First general register used (or -1)
public short m_cGenReg; // Count of general registers used (or 0)
- public bool m_isSinglePrecision; // ARM64 - For determining if HFA is single or double precision
public bool m_fRequires64BitAlignment; // ARM - True if the argument should always be aligned (in registers or on the stack
public int m_idxStack; // First stack slot used (or -1)
m_idxStack = -1;
m_cStack = 0;
- m_isSinglePrecision = false;
m_fRequires64BitAlignment = false;
}
};
frame[_offset + delta] = interior ? CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_INTERIOR : CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_REF;
}
- // Returns true if the ArgDestination represents an HFA struct
- bool IsHFA()
+ // Returns true if the ArgDestination represents a homogeneous aggregate struct
+ bool IsHomogeneousAggregate()
{
return _argLocDescForStructInRegs.HasValue;
}
{
switch (_transitionBlock.Architecture)
{
-
case TargetArchitecture.X64:
return _transitionBlock.IsArgPassedByRef(_argSize);
case TargetArchitecture.ARM64:
if (_argType == CorElementType.ELEMENT_TYPE_VALUETYPE)
{
Debug.Assert(!_argTypeHandle.IsNull());
- return ((_argSize > _transitionBlock.EnregisteredParamTypeMaxSize) && (!_argTypeHandle.IsHFA() || IsVarArg));
+ return ((_argSize > _transitionBlock.EnregisteredParamTypeMaxSize) && (!_argTypeHandle.IsHomogeneousAggregate() || IsVarArg));
}
return false;
default:
- throw new NotImplementedException(_transitionBlock.Architecture.ToString());
+ throw new NotImplementedException();
}
}
else
break;
default:
- throw new NotImplementedException(_transitionBlock.Architecture.ToString());
+ throw new NotImplementedException();
}
_argNum = (_skipFirstArg ? 1 : 0);
int argSize = TypeHandle.GetElemSize(argType, _argTypeHandle);
- bool processingFloatsAsDoublesFromTransitionBlock = false;
- if (_transitionBlock.IsARM64)
- {
- // NOT DESKTOP BEHAVIOR: The S and D registers overlap, and the UniversalTransitionThunk copies D registers to the transition blocks. We'll need
- // to work with the D registers here as well.
- if (argType == CorElementType.ELEMENT_TYPE_VALUETYPE && _argTypeHandle.IsHFA() && _argTypeHandle.GetHFAType() == CorElementType.ELEMENT_TYPE_R4)
- {
- if ((argSize / sizeof(float)) + _arm64IdxFPReg <= 8)
- {
- argSize *= 2;
- processingFloatsAsDoublesFromTransitionBlock = true;
- }
- }
- }
-
_argType = argType;
_argSize = argSize;
// Handle HFAs: packed structures of 1-4 floats or doubles that are passed in FP argument
// registers if possible.
- if (_argTypeHandle.IsHFA())
+ if (_argTypeHandle.IsHomogeneousAggregate())
fFloatingPoint = true;
break;
case CorElementType.ELEMENT_TYPE_VALUETYPE:
{
- // Handle HFAs: packed structures of 2-4 floats or doubles that are passed in FP argument
- // registers if possible.
- if (_argTypeHandle.IsHFA())
+ // Handle HAs: packed structures of 1-4 floats, doubles, or short vectors
+ // that are passed in FP argument registers if possible.
+ if (_argTypeHandle.IsHomogeneousAggregate())
{
- CorElementType type = _argTypeHandle.GetHFAType();
- if (processingFloatsAsDoublesFromTransitionBlock)
- cFPRegs = argSize / sizeof(double);
- else
- cFPRegs = (type == CorElementType.ELEMENT_TYPE_R4) ? (argSize / sizeof(float)) : (argSize / sizeof(double));
+ _argLocDescForStructInRegs = new ArgLocDesc();
+ _argLocDescForStructInRegs.m_idxFloatReg = _arm64IdxFPReg;
+
+ int haElementSize = _argTypeHandle.GetHomogeneousAggregateElementSize();
+ cFPRegs = argSize / haElementSize;
+ _argLocDescForStructInRegs.m_cFloatReg = cFPRegs;
+
+ // Check if we have enough registers available for the HA passing
+ if (cFPRegs + _arm64IdxFPReg <= 8)
+ {
+ _hasArgLocDescForStructInRegs = true;
+ }
}
else
{
- // Composite greater than 16bytes should be passed by reference
+ // Composite greater than 16 bytes should be passed by reference
if (argSize > _transitionBlock.EnregisteredParamTypeMaxSize)
{
argSize = _transitionBlock.PointerSize;
{
if (cFPRegs + _arm64IdxFPReg <= 8)
{
- int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _arm64IdxFPReg * 8;
+ // Each floating point register in the argument area is 16 bytes.
+ int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _arm64IdxFPReg * 16;
_arm64IdxFPReg += cFPRegs;
return argOfsInner;
}
}
else
{
+ // Only x0-x7 are valid argument registers (x8 is always the return buffer)
if (_arm64IdxGenReg + cArgSlots <= 8)
{
+ // The entirety of the arg fits in the register slots.
int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _arm64IdxGenReg * 8;
_arm64IdxGenReg += cArgSlots;
return argOfsInner;
}
+ else if (_context.Target.IsWindows && IsVarArg && (_arm64IdxGenReg < 8))
+ {
+ // Address the Windows ARM64 varargs case where an arg is split between regs and stack.
+ // This can happen in the varargs case because the first 64 bytes of the stack are loaded
+ // into x0-x7, and any remaining stack arguments are placed normally.
+ int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _arm64IdxGenReg * 8;
+
+ // Increase m_idxStack to account for the space used for the remainder of the arg after
+ // register slots are filled.
+ _arm64IdxStack += (_arm64IdxGenReg + cArgSlots - 8);
+
+ // We used up the remaining reg slots.
+ _arm64IdxGenReg = 8;
+
+ return argOfsInner;
+ }
else
{
+ // Don't use reg slots for this. It will be passed purely on the stack arg space.
_arm64IdxGenReg = 8;
}
}
}
default:
- throw new NotImplementedException(_transitionBlock.Architecture.ToString());
+ throw new NotImplementedException();
}
}
if (_transitionBlock.IsFloatArgumentRegisterOffset(argOffset))
{
- // Dividing by 8 as size of each register in FloatArgumentRegisters is 8 bytes.
- pLoc.m_idxFloatReg = (argOffset - _transitionBlock.OffsetOfFloatArgumentRegisters) / 8;
+ // Dividing by 16 as size of each register in FloatArgumentRegisters is 16 bytes.
+ pLoc.m_idxFloatReg = (argOffset - _transitionBlock.OffsetOfFloatArgumentRegisters) / 16;
- if (!_argTypeHandle.IsNull() && _argTypeHandle.IsHFA())
+ if (!_argTypeHandle.IsNull() && _argTypeHandle.IsHomogeneousAggregate())
{
- CorElementType type = _argTypeHandle.GetHFAType();
- bool isFloatType = (type == CorElementType.ELEMENT_TYPE_R4);
-
- // DESKTOP BEHAVIOR pLoc->m_cFloatReg = isFloatType ? GetArgSize() / sizeof(float) : GetArgSize() / sizeof(double);
- pLoc.m_cFloatReg = GetArgSize() / sizeof(double);
- pLoc.m_isSinglePrecision = isFloatType;
+ int haElementSize = _argTypeHandle.GetHomogeneousAggregateElementSize();
+ pLoc.m_cFloatReg = GetArgSize() / haElementSize;
}
else
{
return null;
default:
- throw new NotImplementedException(_transitionBlock.Architecture.ToString());
+ throw new NotImplementedException();
}
}
for (uint pos = 0; pos < nStackSlots; pos++)
{
- int ofs;
-
- if (_target.Architecture == TargetArchitecture.X86)
- {
- ofs = (int)(pos < _transitionBlock.NumArgumentRegisters ?
- _transitionBlock.OffsetOfArgumentRegisters + _transitionBlock.SizeOfArgumentRegisters - (pos + 1) * _target.PointerSize :
- _transitionBlock.OffsetOfArgs + (pos - _transitionBlock.NumArgumentRegisters) * _target.PointerSize);
- }
- else
- {
- ofs = (int)(_transitionBlock.OffsetOfFirstGCRefMapSlot + pos * _target.PointerSize);
- }
-
- CORCOMPILE_GCREFMAP_TOKENS token = fakeStack[ofs];
+ int offset = _transitionBlock.OffsetFromGCRefMapPos(checked((int)pos));
+ CORCOMPILE_GCREFMAP_TOKENS token = fakeStack[offset];
if (token != CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_SKIP)
{
public abstract int OffsetOfArgumentRegisters { get; }
/// <summary>
- /// Only overridden on ARM64 to return offset of the X8 register.
+ /// The offset of the first slot in a GC ref map. Overridden on ARM64 to return the offset of the X8 register.
/// </summary>
public virtual int OffsetOfFirstGCRefMapSlot => OffsetOfArgumentRegisters;
/// Recalculate pos in GC ref map to actual offset. This is the default implementation for all architectures
/// except for X86 where it's overridden to supply a more complex algorithm.
/// </summary>
- /// <param name="pos"></param>
- /// <returns></returns>
public virtual int OffsetFromGCRefMapPos(int pos)
{
- return OffsetOfArgumentRegisters + pos * PointerSize;
+ return OffsetOfFirstGCRefMapSlot + pos * PointerSize;
}
/// <summary>
{
if (descriptor.eightByteClassifications0 == SystemVClassificationType.SystemVClassificationTypeSSE)
{
+ // Structs occupying just one eightbyte are treated as int / double
fpReturnSize = sizeof(double);
}
}
else
{
+ // Size of the struct is 16 bytes
fpReturnSize = 16;
+ // The lowest two bits of the size encode the order of the int and SSE fields
if (descriptor.eightByteClassifications0 == SystemVClassificationType.SystemVClassificationTypeSSE)
{
fpReturnSize += 1;
}
else
{
- if (thRetType.IsHFA() && !isVarArgMethod)
+ if (thRetType.IsHomogeneousAggregate() && !isVarArgMethod)
{
- CorElementType hfaType = thRetType.GetHFAType();
-
- switch (Architecture)
- {
- case TargetArchitecture.ARM:
- fpReturnSize = (hfaType == CorElementType.ELEMENT_TYPE_R4) ?
- (4 * (uint)sizeof(float)) :
- (4 * (uint)sizeof(double));
- break;
-
- case TargetArchitecture.ARM64:
- // DESKTOP BEHAVIOR fpReturnSize = (hfaType == CorElementType.ELEMENT_TYPE_R4) ? (4 * (uint)sizeof(float)) : (4 * (uint)sizeof(double));
- // S and D registers overlap. Since we copy D registers in the UniversalTransitionThunk, we'll
- // treat floats like doubles during copying.
- fpReturnSize = 4 * (uint)sizeof(double);
- break;
-
- default:
- throw new NotImplementedException();
- }
+ int haElementSize = thRetType.GetHomogeneousAggregateElementSize();
+ fpReturnSize = 4 * (uint)haElementSize;
break;
}
{
public static TransitionBlock Instance = new Arm64TransitionBlock();
- private int OffsetOfX8Register => OffsetOfArgumentRegisters - PointerSize;
-
public override TargetArchitecture Architecture => TargetArchitecture.ARM64;
public override int PointerSize => 8;
// X0 .. X7
// Callee-saves, padding, m_x8RetBuffReg, argument registers
public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + 2 * PointerSize + SizeOfArgumentRegisters;
public override int OffsetOfArgumentRegisters => SizeOfCalleeSavedRegisters + 2 * PointerSize;
+ private int OffsetOfX8Register => OffsetOfArgumentRegisters - PointerSize;
public override int OffsetOfFirstGCRefMapSlot => OffsetOfX8Register;
// D0..D7
Debug.Assert(th.IsValueType());
// Composites greater than 16 bytes are passed by reference
- return (th.GetSize() > EnregisteredParamTypeMaxSize) && !th.IsHFA();
+ return (th.GetSize() > EnregisteredParamTypeMaxSize) && !th.IsHomogeneousAggregate();
}
public override int GetRetBuffArgOffset(bool hasThis) => OffsetOfX8Register;
public override bool IsRetBuffPassedAsFirstArg => true;
}
- };
+ }
}
flags |= ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_GCLayout_Empty;
}
- if (defType.IsHfa)
+ if (defType.IsHomogeneousAggregate)
{
flags |= ReadyToRunTypeLayoutFlags.READYTORUN_LAYOUT_HFA;
}
dataBuilder.EmitUInt((uint)flags);
dataBuilder.EmitUInt((uint)size);
- if (defType.IsHfa)
+ if (defType.IsHomogeneousAggregate)
{
- switch (defType.HfaElementType.Category)
+ CorElementType elementType = (defType.ValueTypeShapeCharacteristics & ValueTypeShapeCharacteristics.AggregateMask) switch
{
- case TypeFlags.Single:
- dataBuilder.EmitUInt((uint)CorElementType.ELEMENT_TYPE_R4);
- break;
- case TypeFlags.Double:
- dataBuilder.EmitUInt((uint)CorElementType.ELEMENT_TYPE_R8);
- break;
- }
+ ValueTypeShapeCharacteristics.Float32Aggregate => CorElementType.ELEMENT_TYPE_R4,
+ ValueTypeShapeCharacteristics.Float64Aggregate => CorElementType.ELEMENT_TYPE_R8,
+ ValueTypeShapeCharacteristics.Vector64Aggregate => CorElementType.ELEMENT_TYPE_R8,
+ // See MethodTable::GetHFAType
+ ValueTypeShapeCharacteristics.Vector128Aggregate => CorElementType.ELEMENT_TYPE_VALUETYPE,
+ ValueTypeShapeCharacteristics.Vector256Aggregate => CorElementType.ELEMENT_TYPE_VALUETYPE,
+ _ => CorElementType.Invalid
+ };
+ dataBuilder.EmitUInt((uint)elementType);
}
if (alignment != pointerSize)
return false;
}
- public override DefType ComputeHomogeneousFloatAggregateElementType(DefType type)
- {
- return _fallbackAlgorithm.ComputeHomogeneousFloatAggregateElementType(type);
- }
-
public override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType type, InstanceLayoutKind layoutKind)
{
DefType similarSpecifiedVector = GetSimilarVector(type);
public override ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristics(DefType type)
{
- return _fallbackAlgorithm.ComputeValueTypeShapeCharacteristics(type);
+ if (type.Context.Target.Architecture == TargetArchitecture.ARM64)
+ {
+ return type.InstanceFieldSize.AsInt switch
+ {
+ 16 => ValueTypeShapeCharacteristics.Vector128Aggregate,
+ _ => ValueTypeShapeCharacteristics.None
+ };
+ }
+ return ValueTypeShapeCharacteristics.None;
}
public static bool IsVectorOfTType(DefType type)
{
return _fallbackAlgorithm.ComputeValueTypeShapeCharacteristics(type);
}
-
- public override DefType ComputeHomogeneousFloatAggregateElementType(DefType type)
- {
- return _fallbackAlgorithm.ComputeHomogeneousFloatAggregateElementType(type);
- }
-
}
}
public abstract int OffsetOfArgumentRegisters { get; }
/// <summary>
+ /// The offset of the first slot in a GC ref map. Overridden on ARM64 to return the offset of the X8 register.
+ /// </summary>
+ public virtual int OffsetOfFirstGCRefMapSlot => OffsetOfArgumentRegisters;
+
+ /// <summary>
/// Recalculate pos in GC ref map to actual offset. This is the default implementation for all architectures
/// except for X86 where it's overridden to supply a more complex algorithm.
/// </summary>
- /// <param name="pos"></param>
- /// <returns></returns>
public virtual int OffsetFromGCRefMapPos(int pos)
{
- return OffsetOfArgumentRegisters + pos * PointerSize;
+ return OffsetOfFirstGCRefMapSlot + pos * PointerSize;
}
/// <summary>
// Callee-saves, padding, m_x8RetBuffReg, argument registers
public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + 2 * PointerSize + SizeOfArgumentRegisters;
public override int OffsetOfArgumentRegisters => SizeOfCalleeSavedRegisters + 2 * PointerSize;
+ private int OffsetOfX8Register => OffsetOfArgumentRegisters - PointerSize;
+ public override int OffsetOfFirstGCRefMapSlot => OffsetOfX8Register;
}
}
-
}
-
int cSlots = (GetArgSize() + 7)/ 8;
- // Composites greater than 16bytes are passed by reference
+ // Composites greater than 16 bytes are passed by reference
if (GetArgType() == ELEMENT_TYPE_VALUETYPE && GetArgSize() > ENREGISTERED_PARAMTYPE_MAXSIZE)
{
cSlots = 1;
case ELEMENT_TYPE_VALUETYPE:
{
- // Handle HFAs: packed structures of 2-4 floats or doubles that are passed in FP argument
- // registers if possible.
+ // Handle HFAs: packed structures of 1-4 floats, doubles, or short vectors
+ // that are passed in FP argument registers if possible.
if (thValueType.IsHFA())
{
CorElementType type = thValueType.GetHFAType();
{
case ELEMENT_TYPE_VALUETYPE:
pMT = pFirstField->LookupApproxFieldTypeHandle().GetMethodTable();
- vectorSize = pMT->GetVectorSize();
- if (vectorSize != 0)
- {
- return (vectorSize == 8) ? ELEMENT_TYPE_R8 : ELEMENT_TYPE_VALUETYPE;
- }
break;
case ELEMENT_TYPE_R4: