using static SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR;
using static SystemVClassificationType;
- internal class SystemVStructClassificator
+ internal static class SystemVStructClassificator
{
- private Dictionary<TypeDesc, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR> _classificationCache = new Dictionary<TypeDesc, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR>();
-
private struct SystemVStructRegisterPassingHelper
{
internal SystemVStructRegisterPassingHelper(int totalStructSize)
}
}
- public unsafe bool getSystemVAmd64PassStructInRegisterDescriptor(TypeDesc typeDesc, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr)
+ public static void GetSystemVAmd64PassStructInRegisterDescriptor(TypeDesc typeDesc, out SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structPassInRegDescPtr)
{
- structPassInRegDescPtr->passedInRegisters = false;
+ structPassInRegDescPtr = default;
+ structPassInRegDescPtr.passedInRegisters = false;
int typeSize = typeDesc.GetElementSize().AsInt;
if (typeDesc.IsValueType && (typeSize <= CLR_SYSTEMV_MAX_STRUCT_BYTES_TO_PASS_IN_REGISTERS))
{
- Debug.Assert((TypeDef2SystemVClassification(typeDesc) == SystemVClassificationTypeStruct) ||
- (TypeDef2SystemVClassification(typeDesc) == SystemVClassificationTypeTypedReference));
-
- if (_classificationCache.TryGetValue(typeDesc, out SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR descriptor))
+ if ((TypeDef2SystemVClassification(typeDesc) != SystemVClassificationTypeStruct) &&
+ (TypeDef2SystemVClassification(typeDesc) != SystemVClassificationTypeTypedReference))
{
- *structPassInRegDescPtr = descriptor;
+ return;
}
- else
+
+ SystemVStructRegisterPassingHelper helper = new SystemVStructRegisterPassingHelper(typeSize);
+ bool canPassInRegisters = ClassifyEightBytes(typeDesc, ref helper, 0);
+ if (canPassInRegisters)
{
- SystemVStructRegisterPassingHelper helper = new SystemVStructRegisterPassingHelper(typeSize);
- bool canPassInRegisters = ClassifyEightBytes(typeDesc, ref helper, 0);
- if (canPassInRegisters)
- {
- structPassInRegDescPtr->passedInRegisters = canPassInRegisters;
- structPassInRegDescPtr->eightByteCount = (byte)helper.EightByteCount;
- Debug.Assert(structPassInRegDescPtr->eightByteCount <= CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS);
+ structPassInRegDescPtr.passedInRegisters = canPassInRegisters;
+ structPassInRegDescPtr.eightByteCount = (byte)helper.EightByteCount;
+ Debug.Assert(structPassInRegDescPtr.eightByteCount <= CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS);
- structPassInRegDescPtr->eightByteClassifications0 = helper.EightByteClassifications[0];
- structPassInRegDescPtr->eightByteSizes0 = (byte)helper.EightByteSizes[0];
- structPassInRegDescPtr->eightByteOffsets0 = (byte)helper.EightByteOffsets[0];
+ structPassInRegDescPtr.eightByteClassifications0 = helper.EightByteClassifications[0];
+ structPassInRegDescPtr.eightByteSizes0 = (byte)helper.EightByteSizes[0];
+ structPassInRegDescPtr.eightByteOffsets0 = (byte)helper.EightByteOffsets[0];
- structPassInRegDescPtr->eightByteClassifications1 = helper.EightByteClassifications[1];
- structPassInRegDescPtr->eightByteSizes1 = (byte)helper.EightByteSizes[1];
- structPassInRegDescPtr->eightByteOffsets1 = (byte)helper.EightByteOffsets[1];
- }
-
- _classificationCache.Add(typeDesc, *structPassInRegDescPtr);
+ structPassInRegDescPtr.eightByteClassifications1 = helper.EightByteClassifications[1];
+ structPassInRegDescPtr.eightByteSizes1 = (byte)helper.EightByteSizes[1];
+ structPassInRegDescPtr.eightByteOffsets1 = (byte)helper.EightByteOffsets[1];
}
}
-
- return true;
}
private static SystemVClassificationType TypeDef2SystemVClassification(TypeDesc typeDesc)
return _transitionBlock.OffsetOfArgumentRegisters + _argLocDescForStructInRegs.Value.m_idxGenReg * 8;
}
- private SystemVClassificationType GetTypeClassification(TypeDesc type)
- {
- switch (type.Category)
- {
- case Internal.TypeSystem.TypeFlags.Void:
- return SystemVClassificationType.SystemVClassificationTypeUnknown;
-
- case Internal.TypeSystem.TypeFlags.Boolean:
- case Internal.TypeSystem.TypeFlags.Char:
- case Internal.TypeSystem.TypeFlags.SByte:
- case Internal.TypeSystem.TypeFlags.Byte:
- case Internal.TypeSystem.TypeFlags.Int16:
- case Internal.TypeSystem.TypeFlags.UInt16:
- case Internal.TypeSystem.TypeFlags.Int32:
- case Internal.TypeSystem.TypeFlags.UInt32:
- case Internal.TypeSystem.TypeFlags.Int64:
- case Internal.TypeSystem.TypeFlags.UInt64:
- case Internal.TypeSystem.TypeFlags.IntPtr:
- case Internal.TypeSystem.TypeFlags.UIntPtr:
- case Internal.TypeSystem.TypeFlags.Enum:
- case Internal.TypeSystem.TypeFlags.Pointer:
- case Internal.TypeSystem.TypeFlags.FunctionPointer:
- return SystemVClassificationType.SystemVClassificationTypeInteger;
-
- case Internal.TypeSystem.TypeFlags.Single:
- case Internal.TypeSystem.TypeFlags.Double:
- return SystemVClassificationType.SystemVClassificationTypeSSE;
-
- case Internal.TypeSystem.TypeFlags.ByRef:
- return SystemVClassificationType.SystemVClassificationTypeIntegerByRef;
-
- case Internal.TypeSystem.TypeFlags.ValueType:
- return SystemVClassificationType.SystemVClassificationTypeStruct;
-
- case Internal.TypeSystem.TypeFlags.Class:
- case Internal.TypeSystem.TypeFlags.GenericParameter:
- case Internal.TypeSystem.TypeFlags.Array:
- case Internal.TypeSystem.TypeFlags.SzArray:
- case Internal.TypeSystem.TypeFlags.Interface:
- return SystemVClassificationType.SystemVClassificationTypeIntegerReference;
-
- default:
- return SystemVClassificationType.SystemVClassificationTypeUnknown;
- }
- }
-
// Report managed object pointers in the struct in registers
// Arguments:
// fn - promotion function to apply to each managed object pointer
// sc - scan context to pass to the promotion function
// fieldBytes - size of the structure
- void ReportPointersFromStructInRegisters(TypeDesc type, int delta, CORCOMPILE_GCREFMAP_TOKENS[] frame)
+ internal void ReportPointersFromStructInRegisters(TypeDesc type, int delta, CORCOMPILE_GCREFMAP_TOKENS[] frame)
{
// SPAN-TODO: GC reporting - https://github.com/dotnet/coreclr/issues/8517
Debug.Assert(IsStructPassedInRegs());
int genRegDest = GetStructGenRegDestinationAddress();
- foreach (FieldDesc field in type.GetFields())
+
+ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR descriptor;
+ SystemVStructClassificator.GetSystemVAmd64PassStructInRegisterDescriptor(type, out descriptor);
+
+ for (int i = 0; i < descriptor.eightByteCount; i++)
{
- if (field.IsStatic)
- {
- continue;
- }
- SystemVClassificationType eightByteClassification = GetTypeClassification(field.FieldType);
+ int eightByteSize = (i == 0) ? descriptor.eightByteSizes0 : descriptor.eightByteSizes1;
+ SystemVClassificationType eightByteClassification = (i == 0) ? descriptor.eightByteClassifications0 : descriptor.eightByteClassifications1;
if (eightByteClassification != SystemVClassificationType.SystemVClassificationTypeSSE)
{
if ((eightByteClassification == SystemVClassificationType.SystemVClassificationTypeIntegerReference) ||
(eightByteClassification == SystemVClassificationType.SystemVClassificationTypeIntegerByRef))
{
- int eightByteSize = field.FieldType.GetElementSize().AsInt;
+ Debug.Assert(eightByteSize == 8);
Debug.Assert((genRegDest & 7) == 0);
- CORCOMPILE_GCREFMAP_TOKENS token;
- if (eightByteClassification == SystemVClassificationType.SystemVClassificationTypeIntegerByRef)
- {
- token = CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_INTERIOR;
- }
- else
- {
- token = CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_REF;
- }
- int eightByteIndex = (genRegDest + field.Offset.AsInt) >> 3;
+ CORCOMPILE_GCREFMAP_TOKENS token = (eightByteClassification == SystemVClassificationType.SystemVClassificationTypeIntegerByRef) ? CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_INTERIOR : CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_REF;
+ int eightByteIndex = (genRegDest >> 3) + i;
frame[delta + eightByteIndex] = token;
}
+
+ genRegDest += eightByteSize;
}
}
}
private bool _skipFirstArg;
private bool _extraObjectFirstArg;
private CallingConventions _interpreterCallingConvention;
+ private bool _hasArgLocDescForStructInRegs;
+ private ArgLocDesc _argLocDescForStructInRegs;
public bool HasThis => _hasThis;
public bool IsVarArg => _argData.IsVarArg();
case TargetArchitecture.X64:
if (_transitionBlock.IsX64UnixABI)
{
+ _hasArgLocDescForStructInRegs = false;
+ _fX64UnixArgInRegisters = true;
int cFPRegs = 0;
+ int cGenRegs = 0;
switch (argType)
{
break;
case CorElementType.ELEMENT_TYPE_VALUETYPE:
+ {
+ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR descriptor;
+ SystemVStructClassificator.GetSystemVAmd64PassStructInRegisterDescriptor(_argTypeHandle.GetRuntimeTypeHandle(), out descriptor);
+
+ if (descriptor.passedInRegisters)
{
- // UNIXTODO: FEATURE_UNIX_AMD64_STRUCT_PASSING: Passing of structs, HFAs. For now, use the Windows convention.
- argSize = _transitionBlock.PointerSize;
- break;
+ cGenRegs = 0;
+ for (int i = 0; i < descriptor.eightByteCount; i++)
+ {
+ switch ((i == 0) ? descriptor.eightByteClassifications0 : descriptor.eightByteClassifications1)
+ {
+ case SystemVClassificationType.SystemVClassificationTypeInteger:
+ case SystemVClassificationType.SystemVClassificationTypeIntegerReference:
+ case SystemVClassificationType.SystemVClassificationTypeIntegerByRef:
+ cGenRegs++;
+ break;
+ case SystemVClassificationType.SystemVClassificationTypeSSE:
+ cFPRegs++;
+ break;
+ default:
+ Debug.Assert(false);
+ break;
+ }
+ }
+
+ // Check if we have enough registers available for the struct passing
+ if ((cFPRegs + _x64UnixIdxFPReg <= TransitionBlock.X64UnixTransitionBlock.NUM_FLOAT_ARGUMENT_REGISTERS) && (cGenRegs + _x64UnixIdxGenReg) <= _transitionBlock.NumArgumentRegisters)
+ {
+ _argLocDescForStructInRegs = new ArgLocDesc();
+ _argLocDescForStructInRegs.m_cGenReg = (short)cGenRegs;
+ _argLocDescForStructInRegs.m_cFloatReg = cFPRegs;
+ _argLocDescForStructInRegs.m_idxGenReg = _x64UnixIdxGenReg;
+ _argLocDescForStructInRegs.m_idxFloatReg = _x64UnixIdxFPReg;
+
+ _hasArgLocDescForStructInRegs = true;
+
+ _x64UnixIdxGenReg += cGenRegs;
+ _x64UnixIdxFPReg += cFPRegs;
+
+ return TransitionBlock.StructInRegsOffset;
+ }
}
+ // Set the register counts to indicate that this argument will not be passed in registers
+ cFPRegs = 0;
+ cGenRegs = 0;
+ break;
+ }
+
default:
break;
}
if (cFPRegs > 0)
{
- if (cFPRegs + _x64UnixIdxFPReg <= 8)
+ if (cFPRegs + _x64UnixIdxFPReg <= TransitionBlock.X64UnixTransitionBlock.NUM_FLOAT_ARGUMENT_REGISTERS)
{
int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _x64UnixIdxFPReg * 8;
_x64UnixIdxFPReg += cFPRegs;
}
else
{
- if (_x64UnixIdxGenReg + cArgSlots <= 6)
+ if (_x64UnixIdxGenReg + cArgSlots <= _transitionBlock.NumArgumentRegisters)
{
int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _x64UnixIdxGenReg * 8;
_x64UnixIdxGenReg += cArgSlots;
}
}
+ _fX64UnixArgInRegisters = false;
+
argOfs = _transitionBlock.OffsetOfArgs + _x64UnixIdxStack * 8;
_x64UnixIdxStack += cArgSlots;
return argOfs;
if (_transitionBlock.IsX64)
{
- // All stack arguments take just one stack slot on AMD64 because of arguments bigger
- // than a stack slot are passed by reference.
- stackElemSize = _transitionBlock.StackElemSize();
+ if (_transitionBlock.IsX64UnixABI)
+ {
+ if (_fX64UnixArgInRegisters)
+ {
+ continue;
+ }
+
+ stackElemSize = _transitionBlock.StackElemSize(GetArgSize());
+ }
+ else
+ {
+ // All stack arguments take just one stack slot on AMD64 because of arguments bigger
+ // than a stack slot are passed by reference.
+ stackElemSize = _transitionBlock.StackElemSize();
+ }
}
else
{
case TargetArchitecture.X64:
if (_transitionBlock.IsX64UnixABI)
{
- // LIMITED_METHOD_CONTRACT;
+ if (_hasArgLocDescForStructInRegs)
+ {
+ return _argLocDescForStructInRegs;
+ }
if (argOffset == TransitionBlock.StructInRegsOffset)
{
{
// Dividing by 8 as size of each register in FloatArgumentRegisters is 8 bytes.
pLoc.m_idxFloatReg = (argOffset - _transitionBlock.OffsetOfFloatArgumentRegisters) / 8;
-
- // UNIXTODO: Passing of structs, HFAs. For now, use the Windows convention.
pLoc.m_cFloatReg = 1;
- return pLoc;
}
-
- // UNIXTODO: Passing of structs, HFAs. For now, use the Windows convention.
- int cSlots = 1;
-
- if (!_transitionBlock.IsStackArgumentOffset(argOffset))
+ else if (!_transitionBlock.IsStackArgumentOffset(argOffset))
{
pLoc.m_idxGenReg = _transitionBlock.GetArgumentIndexFromOffset(argOffset);
- pLoc.m_cGenReg = (short)cSlots;
+ pLoc.m_cGenReg = 1;
}
else
{
pLoc.m_idxStack = (argOffset - _transitionBlock.OffsetOfArgs) / 8;
- pLoc.m_cStack = cSlots;
+ int argOnStackSize;
+ int stackElemSize = _transitionBlock.StackElemSize();
+ if (IsArgPassedByRef())
+ argOnStackSize = stackElemSize;
+ else
+ argOnStackSize = GetArgSize();
+ pLoc.m_cStack = (argOnStackSize + stackElemSize - 1) / stackElemSize;
}
return pLoc;
}
private int _x64UnixIdxGenReg;
private int _x64UnixIdxStack;
private int _x64UnixIdxFPReg;
+ private bool _fX64UnixArgInRegisters;
private int _x64WindowsCurOfs; // Current position of the stack iterator
private int _armIdxGenReg; // Next general register to be assigned a value