in src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs [738:1588]
public int GetNextOffset()
{
// WRAPPER_NO_CONTRACT;
// SUPPORTS_DAC;
if (!_ITERATION_STARTED)
{
int numRegistersUsed = 0;
if (HasThis)
numRegistersUsed++;
if (HasRetBuffArg() && _transitionBlock.IsRetBuffPassedAsFirstArg)
{
numRegistersUsed++;
}
Debug.Assert(!IsVarArg || !HasParamType);
// DESKTOP BEHAVIOR - This block is disabled for x86 as the param arg is the last argument on .NET Framework x86.
if (!_transitionBlock.IsX86)
{
if (HasParamType)
{
numRegistersUsed++;
}
}
if (!_transitionBlock.IsX86 && IsVarArg)
{
numRegistersUsed++;
}
switch (_transitionBlock.Architecture)
{
case TargetArchitecture.X86:
if (IsVarArg)
{
numRegistersUsed = _transitionBlock.NumArgumentRegisters; // Nothing else gets passed in registers for varargs
}
#if FEATURE_INTERPRETER
switch (_interpreterCallingConvention)
{
case CallingConventions.StdCall:
_numRegistersUsed = ArchitectureConstants.NUM_ARGUMENT_REGISTERS;
_ofsStack = TransitionBlock.GetOffsetOfArgs() + numRegistersUsed * _transitionBlock.PointerSize + initialArgOffset;
break;
case CallingConventions.ManagedStatic:
case CallingConventions.ManagedInstance:
_numRegistersUsed = numRegistersUsed;
// DESKTOP BEHAVIOR _curOfs = (int)(TransitionBlock.GetOffsetOfArgs() + SizeOfArgStack());
_ofsStack= (int)(TransitionBlock.GetOffsetOfArgs() + initialArgOffset);
break;
default:
Environment.FailFast("Unsupported calling convention.");
break;
}
#endif
_x86NumRegistersUsed = numRegistersUsed;
_x86OfsStack = (int)(_transitionBlock.OffsetOfArgs + SizeOfArgStack());
break;
case TargetArchitecture.X64:
if (_transitionBlock.IsX64UnixABI)
{
_x64UnixIdxGenReg = numRegistersUsed;
_x64UnixIdxStack = 0;
_x64UnixIdxFPReg = 0;
}
else
{
_x64WindowsCurOfs = _transitionBlock.OffsetOfArgs + numRegistersUsed * _transitionBlock.PointerSize;
}
break;
case TargetArchitecture.ARM:
_armIdxGenReg = numRegistersUsed;
_armOfsStack = 0;
_armWFPRegs = 0;
break;
case TargetArchitecture.ARM64:
_arm64IdxGenReg = numRegistersUsed;
_arm64OfsStack = 0;
_arm64IdxFPReg = 0;
break;
case TargetArchitecture.LoongArch64:
_loongarch64IdxGenReg = numRegistersUsed;
_loongarch64OfsStack = 0;
_loongarch64IdxFPReg = 0;
break;
case TargetArchitecture.RiscV64:
_riscv64IdxGenReg = numRegistersUsed;
_riscv64OfsStack = 0;
_riscv64IdxFPReg = 0;
break;
default:
throw new NotImplementedException();
}
_argNum = (_skipFirstArg ? 1 : 0);
_ITERATION_STARTED = true;
}
if (_argNum >= NumFixedArgs)
return TransitionBlock.InvalidOffset;
CorElementType argType = GetArgumentType(_argNum, out _argTypeHandle, out _argForceByRef);
_argTypeHandleOfByRefParam = (argType == CorElementType.ELEMENT_TYPE_BYREF ? _argData.GetByRefArgumentType(_argNum) : default(TypeHandle));
_argNum++;
int argSize = TypeHandle.GetElemSize(argType, _argTypeHandle);
_argType = argType;
_argSize = argSize;
argType = _argForceByRef ? CorElementType.ELEMENT_TYPE_BYREF : argType;
argSize = _argForceByRef ? _transitionBlock.PointerSize : argSize;
int argOfs;
switch (_transitionBlock.Architecture)
{
case TargetArchitecture.X86:
#if FEATURE_INTERPRETER
if (_interpreterCallingConvention != CallingConventions.ManagedStatic && _interpreterCallingConvention != CallingConventions.ManagedInstance)
{
argOfs = _curOfs;
_curOfs += ArchitectureConstants.StackElemSize(argSize);
return argOfs;
}
#endif
if (_transitionBlock.IsArgumentInRegister(ref _x86NumRegistersUsed, argType, _argTypeHandle))
{
return _transitionBlock.OffsetOfArgumentRegisters + (_transitionBlock.NumArgumentRegisters - _x86NumRegistersUsed) * _transitionBlock.PointerSize;
}
_x86OfsStack -= _transitionBlock.StackElemSize(argSize);
argOfs = _x86OfsStack;
Debug.Assert(argOfs >= _transitionBlock.OffsetOfArgs);
return argOfs;
case TargetArchitecture.X64:
if (_transitionBlock.IsX64UnixABI)
{
int cbArg = _transitionBlock.StackElemSize(argSize);
_hasArgLocDescForStructInRegs = false;
_fX64UnixArgInRegisters = true;
int cFPRegs = 0;
int cGenRegs = 0;
switch (argType)
{
case CorElementType.ELEMENT_TYPE_R4:
// 32-bit floating point argument.
cFPRegs = 1;
break;
case CorElementType.ELEMENT_TYPE_R8:
// 64-bit floating point argument.
cFPRegs = 1;
break;
case CorElementType.ELEMENT_TYPE_VALUETYPE:
{
SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR descriptor;
SystemVStructClassificator.GetSystemVAmd64PassStructInRegisterDescriptor(_argTypeHandle.GetRuntimeTypeHandle(), out descriptor);
if (descriptor.passedInRegisters)
{
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:
cGenRegs = cbArg / 8; // GP reg size
break;
}
if (cFPRegs > 0)
{
if (cFPRegs + _x64UnixIdxFPReg <= TransitionBlock.X64UnixTransitionBlock.NUM_FLOAT_ARGUMENT_REGISTERS)
{
int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _x64UnixIdxFPReg * 8;
_x64UnixIdxFPReg += cFPRegs;
return argOfsInner;
}
}
else if (cGenRegs > 0)
{
if (_x64UnixIdxGenReg + cGenRegs <= _transitionBlock.NumArgumentRegisters)
{
int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _x64UnixIdxGenReg * 8;
_x64UnixIdxGenReg += cGenRegs;
return argOfsInner;
}
}
_fX64UnixArgInRegisters = false;
argOfs = _transitionBlock.OffsetOfArgs + _x64UnixIdxStack * 8;
int cArgSlots = cbArg / _transitionBlock.PointerSize;
_x64UnixIdxStack += cArgSlots;
return argOfs;
}
else
{
int cFPRegs = 0;
switch (argType)
{
case CorElementType.ELEMENT_TYPE_R4:
// 32-bit floating point argument.
cFPRegs = 1;
break;
case CorElementType.ELEMENT_TYPE_R8:
// 64-bit floating point argument.
cFPRegs = 1;
break;
}
// Each argument takes exactly one slot on AMD64
argOfs = _x64WindowsCurOfs - _transitionBlock.OffsetOfArgs;
_x64WindowsCurOfs += _transitionBlock.PointerSize;
if ((cFPRegs == 0) || (argOfs >= _transitionBlock.SizeOfArgumentRegisters))
{
return argOfs + _transitionBlock.OffsetOfArgs;
}
else
{
int idxFpReg = argOfs / _transitionBlock.PointerSize;
return _transitionBlock.OffsetOfFloatArgumentRegisters + idxFpReg * TransitionBlock.SizeOfM128A;
}
}
case TargetArchitecture.ARM:
{
// First look at the underlying type of the argument to determine some basic properties:
// 1) The size of the argument in bytes (rounded up to the stack slot size of 4 if necessary).
// 2) Whether the argument represents a floating point primitive (ELEMENT_TYPE_R4 or ELEMENT_TYPE_R8).
// 3) Whether the argument requires 64-bit alignment (anything that contains a Int64/UInt64).
bool fFloatingPoint = false;
bool fRequiresAlign64Bit = false;
switch (argType)
{
case CorElementType.ELEMENT_TYPE_I8:
case CorElementType.ELEMENT_TYPE_U8:
// 64-bit integers require 64-bit alignment on ARM.
fRequiresAlign64Bit = true;
break;
case CorElementType.ELEMENT_TYPE_R4:
// 32-bit floating point argument.
fFloatingPoint = true;
break;
case CorElementType.ELEMENT_TYPE_R8:
// 64-bit floating point argument.
fFloatingPoint = true;
fRequiresAlign64Bit = true;
break;
case CorElementType.ELEMENT_TYPE_VALUETYPE:
{
// Value type case: extract the alignment requirement, note that this has to handle
// the interop "native value types".
fRequiresAlign64Bit = _argTypeHandle.RequiresAlign8();
// Handle HFAs: packed structures of 1-4 floats or doubles that are passed in FP argument
// registers if possible.
if (_argTypeHandle.IsHomogeneousAggregate())
fFloatingPoint = true;
break;
}
default:
// The default is are 4-byte arguments (or promoted to 4 bytes), non-FP and don't require any
// 64-bit alignment.
break;
}
// Now attempt to place the argument into some combination of floating point or general registers and
// the stack.
// Save the alignment requirement
_armRequires64BitAlignment = fRequiresAlign64Bit;
int cbArg = _transitionBlock.StackElemSize(argSize);
Debug.Assert((cbArg % _transitionBlock.PointerSize) == 0);
// Ignore floating point argument placement in registers if we're dealing with a vararg function (the ABI
// specifies this so that vararg processing on the callee side is simplified).
if (fFloatingPoint && _transitionBlock.IsArmhfABI && !IsVarArg)
{
// Handle floating point (primitive) arguments.
// First determine whether we can place the argument in VFP registers. There are 16 32-bit
// and 8 64-bit argument registers that share the same register space (e.g. D0 overlaps S0 and
// S1). The ABI specifies that VFP values will be passed in the lowest sequence of registers that
// haven't been used yet and have the required alignment. So the sequence (float, double, float)
// would be mapped to (S0, D1, S1) or (S0, S2/S3, S1).
//
// We use a 16-bit bitmap to record which registers have been used so far.
//
// So we can use the same basic loop for each argument type (float, double or HFA struct) we set up
// the following input parameters based on the size and alignment requirements of the arguments:
// wAllocMask : bitmask of the number of 32-bit registers we need (1 for 1, 3 for 2, 7 for 3 etc.)
// cSteps : number of loop iterations it'll take to search the 16 registers
// cShift : how many bits to shift the allocation mask on each attempt
ushort wAllocMask = checked((ushort)((1 << (cbArg / 4)) - 1));
ushort cSteps = (ushort)(fRequiresAlign64Bit ? 9 - (cbArg / 8) : 17 - (cbArg / 4));
ushort cShift = fRequiresAlign64Bit ? (ushort)2 : (ushort)1;
// Look through the availability bitmask for a free register or register pair.
for (ushort i = 0; i < cSteps; i++)
{
if ((_armWFPRegs & wAllocMask) == 0)
{
// We found one, mark the register or registers as used.
_armWFPRegs |= wAllocMask;
// Indicate the registers used to the caller and return.
return _transitionBlock.OffsetOfFloatArgumentRegisters + (i * cShift * 4);
}
wAllocMask <<= cShift;
}
// The FP argument is going to live on the stack. Once this happens the ABI demands we mark all FP
// registers as unavailable.
_armWFPRegs = 0xffff;
// Doubles or HFAs containing doubles need the stack aligned appropriately.
if (fRequiresAlign64Bit)
_armOfsStack = ALIGN_UP(_armOfsStack, _transitionBlock.PointerSize * 2);
// Indicate the stack location of the argument to the caller.
int argOfsInner = _transitionBlock.OffsetOfArgs + _armOfsStack;
// Record the stack usage.
_armOfsStack += cbArg;
return argOfsInner;
}
//
// Handle the non-floating point case.
//
if (_armIdxGenReg < 4)
{
if (fRequiresAlign64Bit)
{
// The argument requires 64-bit alignment. Align either the next general argument register if
// we have any left. See step C.3 in the algorithm in the ABI spec.
_armIdxGenReg = ALIGN_UP(_armIdxGenReg, 2);
}
int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _armIdxGenReg * 4;
int cRemainingRegs = 4 - _armIdxGenReg;
if (cbArg <= cRemainingRegs * _transitionBlock.PointerSize)
{
// Mark the registers just allocated as used.
_armIdxGenReg += ALIGN_UP(cbArg, _transitionBlock.PointerSize) / _transitionBlock.PointerSize;
return argOfsInner;
}
// The ABI supports splitting a non-FP argument across registers and the stack. But this is
// disabled if the FP arguments already overflowed onto the stack (i.e. the stack index is not
// zero). The following code marks the general argument registers as exhausted if this condition
// holds. See steps C.5 in the algorithm in the ABI spec.
_armIdxGenReg = 4;
if (_armOfsStack == 0)
{
_armOfsStack += cbArg - cRemainingRegs * _transitionBlock.PointerSize;
return argOfsInner;
}
}
if (fRequiresAlign64Bit)
{
// The argument requires 64-bit alignment. If it is going to be passed on the stack, align
// the next stack slot. See step C.6 in the algorithm in the ABI spec.
_armOfsStack = ALIGN_UP(_armOfsStack, _transitionBlock.PointerSize * 2);
}
argOfs = _transitionBlock.OffsetOfArgs + _armOfsStack;
// Advance the stack pointer over the argument just placed.
_armOfsStack += cbArg;
return argOfs;
}
case TargetArchitecture.ARM64:
{
int cFPRegs = 0;
bool isFloatHFA = false;
switch (argType)
{
case CorElementType.ELEMENT_TYPE_R4:
// 32-bit floating point argument.
cFPRegs = 1;
break;
case CorElementType.ELEMENT_TYPE_R8:
// 64-bit floating point argument.
cFPRegs = 1;
break;
case CorElementType.ELEMENT_TYPE_VALUETYPE:
{
// Handle HAs: packed structures of 1-4 floats, doubles, or short vectors
// that are passed in FP argument registers if possible.
if (_argTypeHandle.IsHomogeneousAggregate())
{
_argLocDescForStructInRegs = new ArgLocDesc();
_argLocDescForStructInRegs.m_idxFloatReg = _arm64IdxFPReg;
int haElementSize = _argTypeHandle.GetHomogeneousAggregateElementSize();
if (haElementSize == 4)
{
isFloatHFA = true;
}
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 16 bytes should be passed by reference
if (argSize > _transitionBlock.EnregisteredParamTypeMaxSize)
{
argSize = _transitionBlock.PointerSize;
}
}
break;
}
default:
break;
}
bool isValueType = (argType == CorElementType.ELEMENT_TYPE_VALUETYPE);
int cbArg = _transitionBlock.StackElemSize(argSize, isValueType, isFloatHFA);
if (cFPRegs > 0 && !IsVarArg)
{
if (cFPRegs + _arm64IdxFPReg <= 8)
{
// Each floating point register in the argument area is 16 bytes.
int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _arm64IdxFPReg * 16;
_arm64IdxFPReg += cFPRegs;
return argOfsInner;
}
else
{
_arm64IdxFPReg = 8;
}
}
else
{
Debug.Assert(_transitionBlock.IsAppleArm64ABI || (cbArg % _transitionBlock.PointerSize) == 0);
int regSlots = ALIGN_UP(cbArg, _transitionBlock.PointerSize) / _transitionBlock.PointerSize;
// Only x0-x7 are valid argument registers (x8 is always the return buffer)
if (_arm64IdxGenReg + regSlots <= 8)
{
// The entirety of the arg fits in the register slots.
int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _arm64IdxGenReg * 8;
_arm64IdxGenReg += regSlots;
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_ofsStack to account for the space used for the remainder of the arg after
// registers are filled.
_arm64OfsStack += cbArg + (_arm64IdxGenReg - 8) * _transitionBlock.PointerSize;
// 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;
}
}
if (_transitionBlock.IsAppleArm64ABI)
{
int alignment;
if (!isValueType)
{
Debug.Assert((cbArg & (cbArg - 1)) == 0);
alignment = cbArg;
}
else if (isFloatHFA)
{
alignment = 4;
}
else
{
alignment = 8;
}
_arm64OfsStack = ALIGN_UP(_arm64OfsStack, alignment);
}
argOfs = _transitionBlock.OffsetOfArgs + _arm64OfsStack;
_arm64OfsStack += cbArg;
return argOfs;
}
case TargetArchitecture.LoongArch64:
{
int cFPRegs = 0;
uint floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
_hasArgLocDescForStructInRegs = false;
switch (argType)
{
case CorElementType.ELEMENT_TYPE_R4:
// 32-bit floating point argument.
cFPRegs = 1;
break;
case CorElementType.ELEMENT_TYPE_R8:
// 64-bit floating point argument.
cFPRegs = 1;
break;
case CorElementType.ELEMENT_TYPE_VALUETYPE:
{
// Composite greater than 16 bytes should be passed by reference
if (argSize > _transitionBlock.EnregisteredParamTypeMaxSize)
{
argSize = _transitionBlock.PointerSize;
}
else
{
floatFieldFlags = LoongArch64PassStructInRegister.GetLoongArch64PassStructInRegisterFlags(_argTypeHandle.GetRuntimeTypeHandle());
if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO) != 0)
{
cFPRegs = 2;
}
else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_HAS_FLOAT_FIELDS_MASK) != 0)
{
cFPRegs = 1;
}
}
break;
}
default:
break;
}
bool isValueType = (argType == CorElementType.ELEMENT_TYPE_VALUETYPE);
int cbArg = _transitionBlock.StackElemSize(argSize, isValueType, false);
if (cFPRegs > 0 && !IsVarArg)
{
if (isValueType && ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_HAS_ONE_FLOAT_MASK) != 0))
{
Debug.Assert(cFPRegs == 1);
if ((_loongarch64IdxFPReg < 8) && (_loongarch64IdxGenReg < 8))
{
_argLocDescForStructInRegs = new ArgLocDesc();
_argLocDescForStructInRegs.m_idxFloatReg = _loongarch64IdxFPReg;
_argLocDescForStructInRegs.m_cFloatReg = 1;
_argLocDescForStructInRegs.m_idxGenReg = _loongarch64IdxGenReg;
_argLocDescForStructInRegs.m_cGenReg = 1;
_hasArgLocDescForStructInRegs = true;
_argLocDescForStructInRegs.m_floatFlags = floatFieldFlags;
int argOfsInner = 0;
if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND) != 0)
{
argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _loongarch64IdxGenReg * 8;
}
else
{
argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _loongarch64IdxFPReg * 8;
}
_loongarch64IdxFPReg++;
_loongarch64IdxGenReg++;
return argOfsInner;
}
}
else if (cFPRegs + _loongarch64IdxFPReg <= 8)
{
// Each floating point register in the argument area is 8 bytes.
int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _loongarch64IdxFPReg * 8;
if (floatFieldFlags == (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO)
{
// struct with two single-float fields.
_argLocDescForStructInRegs = new ArgLocDesc();
_argLocDescForStructInRegs.m_idxFloatReg = _loongarch64IdxFPReg;
_argLocDescForStructInRegs.m_cFloatReg = 2;
Debug.Assert(cFPRegs == 2);
Debug.Assert(argSize == 8);
_hasArgLocDescForStructInRegs = true;
_argLocDescForStructInRegs.m_floatFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO;
}
_loongarch64IdxFPReg += cFPRegs;
return argOfsInner;
}
else
{
_loongarch64IdxFPReg = 8;
}
}
{
Debug.Assert((cbArg % _transitionBlock.PointerSize) == 0);
int regSlots = ALIGN_UP(cbArg, _transitionBlock.PointerSize) / _transitionBlock.PointerSize;
// Only R4-R11 are valid argument registers.
if (_loongarch64IdxGenReg + regSlots <= 8)
{
// The entirety of the arg fits in the register slots.
int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _loongarch64IdxGenReg * 8;
_loongarch64IdxGenReg += regSlots;
return argOfsInner;
}
else if (_loongarch64IdxGenReg < 8)
{
int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _loongarch64IdxGenReg * 8;
_loongarch64IdxGenReg = 8;
_loongarch64OfsStack += 8;
return argOfsInner;
}
}
argOfs = _transitionBlock.OffsetOfArgs + _loongarch64OfsStack;
_loongarch64OfsStack += cbArg;
return argOfs;
}
case TargetArchitecture.RiscV64:
{
int cFPRegs = 0;
uint floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
_hasArgLocDescForStructInRegs = false;
switch (argType)
{
case CorElementType.ELEMENT_TYPE_R4:
// 32-bit floating point argument.
cFPRegs = 1;
break;
case CorElementType.ELEMENT_TYPE_R8:
// 64-bit floating point argument.
cFPRegs = 1;
break;
case CorElementType.ELEMENT_TYPE_VALUETYPE:
{
// Composite greater than 16 bytes should be passed by reference
if (argSize > _transitionBlock.EnregisteredParamTypeMaxSize)
{
argSize = _transitionBlock.PointerSize;
}
else
{
floatFieldFlags = RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(_argTypeHandle.GetRuntimeTypeHandle());
if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO) != 0)
{
cFPRegs = 2;
}
else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_HAS_FLOAT_FIELDS_MASK) != 0)
{
cFPRegs = 1;
}
}
break;
}
default:
break;
}
bool isValueType = (argType == CorElementType.ELEMENT_TYPE_VALUETYPE);
int cbArg = _transitionBlock.StackElemSize(argSize, isValueType, false);
if (cFPRegs > 0 && !IsVarArg)
{
if (isValueType && ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_HAS_ONE_FLOAT_MASK) != 0))
{
Debug.Assert(cFPRegs == 1);
if ((_riscv64IdxFPReg < 8) && (_riscv64IdxGenReg < 8))
{
_argLocDescForStructInRegs = new ArgLocDesc();
_argLocDescForStructInRegs.m_idxFloatReg = _riscv64IdxFPReg;
_argLocDescForStructInRegs.m_cFloatReg = 1;
_argLocDescForStructInRegs.m_idxGenReg = _riscv64IdxGenReg;
_argLocDescForStructInRegs.m_cGenReg = 1;
_hasArgLocDescForStructInRegs = true;
_argLocDescForStructInRegs.m_floatFlags = floatFieldFlags;
int argOfsInner =
((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND) != 0)
? _transitionBlock.OffsetOfArgumentRegisters + _riscv64IdxGenReg * 8
: _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * 8;
_riscv64IdxFPReg++;
_riscv64IdxGenReg++;
return argOfsInner;
}
}
else if (cFPRegs + _riscv64IdxFPReg <= 8)
{
// Each floating point register in the argument area is 8 bytes.
int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * 8;
if (floatFieldFlags == (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO)
{
// struct with two single-float fields.
_argLocDescForStructInRegs = new ArgLocDesc();
_argLocDescForStructInRegs.m_idxFloatReg = _riscv64IdxFPReg;
_argLocDescForStructInRegs.m_cFloatReg = 2;
Debug.Assert(cFPRegs == 2);
Debug.Assert(argSize == 8);
_hasArgLocDescForStructInRegs = true;
_argLocDescForStructInRegs.m_floatFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO;
}
_riscv64IdxFPReg += cFPRegs;
return argOfsInner;
}
else
{
_riscv64IdxFPReg = 8;
}
}
{
Debug.Assert((cbArg % _transitionBlock.PointerSize) == 0);
int regSlots = ALIGN_UP(cbArg, _transitionBlock.PointerSize) / _transitionBlock.PointerSize;
// Only a0-a7 are valid argument registers.
if (_riscv64IdxGenReg + regSlots <= 8)
{
// The entirety of the arg fits in the register slots.
int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _riscv64IdxGenReg * 8;
_riscv64IdxGenReg += regSlots;
return argOfsInner;
}
else if (_riscv64IdxGenReg < 8)
{
int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _riscv64IdxGenReg * 8;
_riscv64IdxGenReg = 8;
_riscv64OfsStack += 8;
return argOfsInner;
}
}
argOfs = _transitionBlock.OffsetOfArgs + _riscv64OfsStack;
_riscv64OfsStack += cbArg;
return argOfs;
}
default:
throw new NotImplementedException();
}
}