int ArgIteratorTemplate::GetNextOffset()

in src/coreclr/vm/callingconvention.h [1131:1912]


int ArgIteratorTemplate<ARGITERATOR_BASE>::GetNextOffset()
{
    WRAPPER_NO_CONTRACT;
    SUPPORTS_DAC;

    if (!(m_dwFlags & ITERATION_STARTED))
    {
        int numRegistersUsed = 0;

        if (this->HasThis())
            numRegistersUsed++;

        if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
            numRegistersUsed++;

        _ASSERTE(!this->IsVarArg() || !this->HasParamType());

#ifndef TARGET_X86
        if (this->IsVarArg() || this->HasParamType())
        {
            numRegistersUsed++;
        }
#endif

#ifdef TARGET_X86
        if (this->IsVarArg())
        {
            numRegistersUsed = NUM_ARGUMENT_REGISTERS; // Nothing else gets passed in registers for varargs
        }

#ifdef FEATURE_INTERPRETER
        BYTE callconv = CallConv();
        switch (callconv)
        {
        case IMAGE_CEE_CS_CALLCONV_C:
        case IMAGE_CEE_CS_CALLCONV_STDCALL:
            m_numRegistersUsed = NUM_ARGUMENT_REGISTERS;
            m_ofsStack = TransitionBlock::GetOffsetOfArgs() + numRegistersUsed * sizeof(void *);
            m_fUnmanagedCallConv = true;
            break;

        case IMAGE_CEE_CS_CALLCONV_THISCALL:
        case IMAGE_CEE_CS_CALLCONV_FASTCALL:
            _ASSERTE_MSG(false, "Unsupported calling convention.");

        default:
            m_fUnmanagedCallConv = false;
            m_numRegistersUsed = numRegistersUsed;
            m_ofsStack = TransitionBlock::GetOffsetOfArgs() + SizeOfArgStack();
            break;
        }
#else
        m_numRegistersUsed = numRegistersUsed;
        m_ofsStack = TransitionBlock::GetOffsetOfArgs() + SizeOfArgStack();
#endif

#elif defined(TARGET_AMD64)
#ifdef UNIX_AMD64_ABI
        m_idxGenReg = numRegistersUsed;
        m_ofsStack = 0;
        m_idxFPReg = 0;
#else
        m_ofsStack = TransitionBlock::GetOffsetOfArgs() + numRegistersUsed * sizeof(void *);
#endif
#elif defined(TARGET_ARM)
        m_idxGenReg = numRegistersUsed;
        m_ofsStack = 0;

        m_wFPRegs = 0;
#elif defined(TARGET_ARM64)
        m_idxGenReg = numRegistersUsed;
        m_ofsStack = 0;

        m_idxFPReg = 0;
#elif defined(TARGET_LOONGARCH64)
        m_idxGenReg = numRegistersUsed;
        m_ofsStack = 0;
        m_idxFPReg = 0;
#elif defined(TARGET_RISCV64)
        m_idxGenReg = numRegistersUsed;
        m_ofsStack = 0;
        m_idxFPReg = 0;
#else
        PORTABILITY_ASSERT("ArgIteratorTemplate::GetNextOffset");
#endif

        m_argNum = 0;

        m_dwFlags |= ITERATION_STARTED;
    }

    // We're done going through the args for this MetaSig
    if (m_argNum == this->NumFixedArgs())
        return TransitionBlock::InvalidOffset;

    TypeHandle thValueType;
    CorElementType argType = this->GetNextArgumentType(m_argNum++, &thValueType);

    // TypedReference behaves like a valuetype
    if (argType == ELEMENT_TYPE_TYPEDBYREF)
    {
        argType = ELEMENT_TYPE_VALUETYPE;
        thValueType = TypeHandle(g_TypedReferenceMT);
    }

    int argSize = MetaSig::GetElemSize(argType, thValueType);

    m_argType = argType;
    m_argSize = argSize;
    m_argTypeHandle = thValueType;

#if defined(UNIX_AMD64_ABI) || defined (TARGET_ARM64) || defined (TARGET_LOONGARCH64) || defined (TARGET_RISCV64)
    m_hasArgLocDescForStructInRegs = false;
#endif

#ifdef TARGET_X86
#ifdef FEATURE_INTERPRETER
    if (m_fUnmanagedCallConv)
    {
        int argOfs = m_ofsStack;
        m_ofsStack += StackElemSize(argSize);
        return argOfs;
    }
#endif
    if (IsArgumentInRegister(&m_numRegistersUsed, argType, thValueType))
    {
        return TransitionBlock::GetOffsetOfArgumentRegisters() + (NUM_ARGUMENT_REGISTERS - m_numRegistersUsed) * sizeof(void *);
    }

    m_ofsStack -= StackElemSize(argSize);
    _ASSERTE(m_ofsStack >= TransitionBlock::GetOffsetOfArgs());
    return m_ofsStack;
#elif defined(TARGET_AMD64)
#ifdef UNIX_AMD64_ABI

    m_fArgInRegisters = true;

    int cFPRegs = 0;
    int cGenRegs = 0;
    int cbArg = StackElemSize(argSize);

    switch (argType)
    {

    case ELEMENT_TYPE_R4:
        // 32-bit floating point argument.
        cFPRegs = 1;
        break;

    case ELEMENT_TYPE_R8:
        // 64-bit floating point argument.
        cFPRegs = 1;
        break;

    case ELEMENT_TYPE_VALUETYPE:
    {
        MethodTable *pMT = m_argTypeHandle.GetMethodTable();
        if (this->IsRegPassedStruct(pMT))
        {
            EEClass* eeClass = pMT->GetClass();
            cGenRegs = 0;
            for (int i = 0; i < eeClass->GetNumberEightBytes(); i++)
            {
                switch (eeClass->GetEightByteClassification(i))
                {
                    case SystemVClassificationTypeInteger:
                    case SystemVClassificationTypeIntegerReference:
                    case SystemVClassificationTypeIntegerByRef:
                        cGenRegs++;
                        break;
                    case SystemVClassificationTypeSSE:
                        cFPRegs++;
                        break;
                    default:
                        _ASSERTE(false);
                        break;
                }
            }

            // Check if we have enough registers available for the struct passing
            if ((cFPRegs + m_idxFPReg <= NUM_FLOAT_ARGUMENT_REGISTERS) && (cGenRegs + m_idxGenReg) <= NUM_ARGUMENT_REGISTERS)
            {
                m_argLocDescForStructInRegs.Init();
                m_argLocDescForStructInRegs.m_cGenReg = cGenRegs;
                m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs;
                m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg;
                m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
                m_argLocDescForStructInRegs.m_eeClass = eeClass;

                m_hasArgLocDescForStructInRegs = true;

                m_idxGenReg += cGenRegs;
                m_idxFPReg += 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) && (cFPRegs + m_idxFPReg <= NUM_FLOAT_ARGUMENT_REGISTERS))
    {
        int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 16;
        m_idxFPReg += cFPRegs;
        return argOfs;
    }
    else if ((cGenRegs > 0) && (m_idxGenReg + cGenRegs <= NUM_ARGUMENT_REGISTERS))
    {
        int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
        m_idxGenReg += cGenRegs;
        return argOfs;
    }

    m_fArgInRegisters = false;

    int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;

    m_ofsStack += cbArg;

    return argOfs;
#else
    // Each argument takes exactly one slot on AMD64 on Windows
    int argOfs = m_ofsStack;
    m_ofsStack += sizeof(void *);
    return argOfs;
#endif
#elif defined(TARGET_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 ELEMENT_TYPE_I8:
    case ELEMENT_TYPE_U8:
        // 64-bit integers require 64-bit alignment on ARM.
        fRequiresAlign64Bit = true;
        break;

    case ELEMENT_TYPE_R4:
        // 32-bit floating point argument.
        fFloatingPoint = true;
        break;

    case ELEMENT_TYPE_R8:
        // 64-bit floating point argument.
        fFloatingPoint = true;
        fRequiresAlign64Bit = true;
        break;

    case ELEMENT_TYPE_VALUETYPE:
    {
        // Value type case: extract the alignment requirement, note that this has to handle
        // the interop "native value types".
        fRequiresAlign64Bit = thValueType.RequiresAlign8();

#ifdef FEATURE_HFA
        // Handle HFAs: packed structures of 1-4 floats or doubles that are passed in FP argument
        // registers if possible.
        if (thValueType.IsHFA())
        {
            fFloatingPoint = true;
        }
#endif

        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
    m_fRequires64BitAlignment = fRequiresAlign64Bit;

    int cbArg = StackElemSize(argSize);
    _ASSERTE((cbArg % TARGET_POINTER_SIZE) == 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).
#ifndef ARM_SOFTFP
    if (fFloatingPoint && !this->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

        WORD wAllocMask = (1 << (cbArg / 4)) - 1;
        WORD cSteps = (WORD)(fRequiresAlign64Bit ? 9 - (cbArg / 8) : 17 - (cbArg / 4));
        WORD cShift = fRequiresAlign64Bit ? 2 : 1;

        // Look through the availability bitmask for a free register or register pair.
        for (WORD i = 0; i < cSteps; i++)
        {
            if ((m_wFPRegs & wAllocMask) == 0)
            {
                // We found one, mark the register or registers as used.
                m_wFPRegs |= wAllocMask;

                // Indicate the registers used to the caller and return.
                return TransitionBlock::GetOffsetOfFloatArgumentRegisters() + (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.
        m_wFPRegs = 0xffff;

        // Doubles or HFAs containing doubles need the stack aligned appropriately.
        if (fRequiresAlign64Bit)
        {
            m_ofsStack = (int)ALIGN_UP(m_ofsStack, TARGET_POINTER_SIZE * 2);
        }

        // Indicate the stack location of the argument to the caller.
        int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;

        // Record the stack usage.
        m_ofsStack += cbArg;

        return argOfs;
    }
#endif // ARM_SOFTFP

    //
    // Handle the non-floating point case.
    //

    if (m_idxGenReg < 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.
            m_idxGenReg = (int)ALIGN_UP(m_idxGenReg, 2);
        }

        int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 4;

        int cRemainingRegs = 4 - m_idxGenReg;
        if (cbArg <= cRemainingRegs * TARGET_POINTER_SIZE)
        {
            // Mark the registers just allocated as used.
            m_idxGenReg += ALIGN_UP(cbArg, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE;
            return argOfs;
        }

        // 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.

        m_idxGenReg = 4;

        if (m_ofsStack == 0)
        {
            m_ofsStack += cbArg - cRemainingRegs * TARGET_POINTER_SIZE;
            return argOfs;
        }
    }

    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.
        m_ofsStack = (int)ALIGN_UP(m_ofsStack, TARGET_POINTER_SIZE * 2);
    }

    int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;

    // Advance the stack pointer over the argument just placed.
    m_ofsStack += cbArg;

    return argOfs;
#elif defined(TARGET_ARM64)

    int cFPRegs = 0;

    switch (argType)
    {

    case ELEMENT_TYPE_R4:
        // 32-bit floating point argument.
        cFPRegs = 1;
        break;

    case ELEMENT_TYPE_R8:
        // 64-bit floating point argument.
        cFPRegs = 1;
        break;

    case ELEMENT_TYPE_VALUETYPE:
    {
        // Handle HFAs: packed structures of 1-4 floats, doubles, or short vectors
        // that are passed in FP argument registers if possible.
        if (thValueType.IsHFA())
        {
            CorInfoHFAElemType type = thValueType.GetHFAType();

            m_argLocDescForStructInRegs.Init();
            m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;

            m_argLocDescForStructInRegs.setHFAFieldSize(type);
            cFPRegs = argSize/m_argLocDescForStructInRegs.m_hfaFieldSize;
            m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs;

            // Check if we have enough registers available for the HFA passing
            if ((cFPRegs + m_idxFPReg) <= 8)
            {
                m_hasArgLocDescForStructInRegs = true;
            }
        }
        else
        {
            // Composite greater than 16bytes should be passed by reference
            if (argSize > ENREGISTERED_PARAMTYPE_MAXSIZE)
            {
                argSize = sizeof(TADDR);
            }
        }

        break;
    }

    default:
        break;
    }
    const bool isValueType = (argType == ELEMENT_TYPE_VALUETYPE);
    const bool isFloatHfa = thValueType.IsFloatHfa();
    const int cbArg = StackElemSize(argSize, isValueType, isFloatHfa);
    if (cFPRegs>0 && !this->IsVarArg())
    {
        if (cFPRegs + m_idxFPReg <= 8)
        {
            // Each floating point register in the argument area is 16 bytes.
            int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 16;
            m_idxFPReg += cFPRegs;
            return argOfs;
        }
        else
        {
            m_idxFPReg = 8;
        }
    }
    else
    {
#if !defined(OSX_ARM64_ABI)
        _ASSERTE((cbArg% TARGET_POINTER_SIZE) == 0);
#endif
        const int regSlots = ALIGN_UP(cbArg, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE;
        // Only x0-x7 are valid argument registers (x8 is always the return buffer)
        if (m_idxGenReg + regSlots <= 8)
        {
            // The entirety of the arg fits in the register slots.

            int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
            m_idxGenReg += regSlots;
            return argOfs;
        }
        else
        {
#ifdef _WIN32
            if (this->IsVarArg() && m_idxGenReg < 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 argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;

                // Increase m_ofsStack to account for the space used for the remainder of the arg after
                // registers are filled.
                m_ofsStack += cbArg + (m_idxGenReg - 8) * TARGET_POINTER_SIZE;

                // We used up the remaining reg slots.
                m_idxGenReg = 8;

                return argOfs;
            }
            else
#endif
            {
                // Don't use reg slots for this. It will be passed purely on the stack arg space.
                m_idxGenReg = 8;
            }
        }
    }

#ifdef OSX_ARM64_ABI
    int alignment;
    if (!isValueType)
    {
        _ASSERTE((cbArg & (cbArg - 1)) == 0);
        alignment = cbArg;
    }
    else if (isFloatHfa)
    {
        alignment = 4;
    }
    else
    {
        alignment = 8;
    }
    m_ofsStack = (int)ALIGN_UP(m_ofsStack, alignment);
#endif // OSX_ARM64_ABI

    int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
    m_ofsStack += cbArg;
    return argOfs;
#elif defined(TARGET_LOONGARCH64)

    int cFPRegs = 0;
    int flags = 0;

    switch (argType)
    {

    case ELEMENT_TYPE_R4:
        // 32-bit floating point argument.
        cFPRegs = 1;
        break;

    case ELEMENT_TYPE_R8:
        // 64-bit floating point argument.
        cFPRegs = 1;
        break;

    case ELEMENT_TYPE_VALUETYPE:
    {
        // Handle struct which containing floats or doubles that can be passed
        // in FP registers if possible.

        // Composite greater than 16bytes should be passed by reference
        if (argSize > ENREGISTERED_PARAMTYPE_MAXSIZE)
        {
            argSize = sizeof(TADDR);
        }
        else
        {
            flags = MethodTable::GetLoongArch64PassStructInRegisterFlags(thValueType);
            if (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)
            {
                cFPRegs = (flags & STRUCT_FLOAT_FIELD_ONLY_TWO) ? 2 : 1;
            }
        }

        break;
    }

    default:
        break;
    }

    const bool isValueType = (argType == ELEMENT_TYPE_VALUETYPE);
    const bool isFloatHfa = thValueType.IsFloatHfa();
    const int cbArg = StackElemSize(argSize, isValueType, isFloatHfa);

    if (cFPRegs > 0 && !this->IsVarArg())
    {
        if (flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND))
        {
            assert(cFPRegs == 1);
            assert((STRUCT_FLOAT_FIELD_FIRST == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)) || (STRUCT_FLOAT_FIELD_SECOND == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)));

            if ((1 + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) && (m_idxGenReg + 1 <= NUM_ARGUMENT_REGISTERS))
            {
                int argOfs = 0;
                m_argLocDescForStructInRegs.Init();
                m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
                m_argLocDescForStructInRegs.m_cFloatReg = 1;
                m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg;
                m_argLocDescForStructInRegs.m_cGenReg = 1;
                m_argLocDescForStructInRegs.m_structFields = flags;

                if (flags & STRUCT_FLOAT_FIELD_SECOND)
                {
                    argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
                }
                else
                {
                    argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
                }

                m_idxFPReg  += 1;
                m_idxGenReg += 1;

                m_hasArgLocDescForStructInRegs = true;

                return argOfs;
            }
        }
        else if (cFPRegs + m_idxFPReg <= NUM_ARGUMENT_REGISTERS)
        {
            int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
            if (flags == STRUCT_FLOAT_FIELD_ONLY_TWO) // struct with two float-fields.
            {
                m_argLocDescForStructInRegs.Init();
                m_hasArgLocDescForStructInRegs = true;
                m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
                assert(cFPRegs == 2);
                m_argLocDescForStructInRegs.m_cFloatReg = 2;
                assert(argSize == 8);
                m_argLocDescForStructInRegs.m_structFields = STRUCT_FLOAT_FIELD_ONLY_TWO;
            }
            m_idxFPReg += cFPRegs;
            return argOfs;
        }
    }

    {
        const int regSlots = ALIGN_UP(cbArg, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE;
        if (m_idxGenReg + regSlots <= NUM_ARGUMENT_REGISTERS)
        {
            int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
            m_idxGenReg += regSlots;
            return argOfs;
        }
        else if (m_idxGenReg < NUM_ARGUMENT_REGISTERS)
        {
            int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
            m_ofsStack += (m_idxGenReg + regSlots - NUM_ARGUMENT_REGISTERS)*8;
            assert(m_ofsStack == 8);
            m_idxGenReg = NUM_ARGUMENT_REGISTERS;
            return argOfs;
        }
    }

    int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
    m_ofsStack += ALIGN_UP(cbArg, TARGET_POINTER_SIZE);

    return argOfs;
#elif defined(TARGET_RISCV64)

    int cFPRegs = 0;
    int flags = 0;

    switch (argType)
    {

    case ELEMENT_TYPE_R4:
        // 32-bit floating point argument.
        cFPRegs = 1;
        break;

    case ELEMENT_TYPE_R8:
        // 64-bit floating point argument.
        cFPRegs = 1;
        break;

    case ELEMENT_TYPE_VALUETYPE:
    {
        // Handle struct which containing floats or doubles that can be passed
        // in FP registers if possible.

        // Composite greater than 16bytes should be passed by reference
        if (argSize > ENREGISTERED_PARAMTYPE_MAXSIZE)
        {
            argSize = sizeof(TADDR);
        }
        else
        {
            flags = MethodTable::GetRiscV64PassStructInRegisterFlags(thValueType);
            if (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)
            {
                cFPRegs = (flags & STRUCT_FLOAT_FIELD_ONLY_TWO) ? 2 : 1;
            }
        }

        break;
    }

    default:
        break;
    }

    const bool isValueType = (argType == ELEMENT_TYPE_VALUETYPE);
    const bool isFloatHfa = thValueType.IsFloatHfa();
    const int cbArg = StackElemSize(argSize, isValueType, isFloatHfa);

    if (cFPRegs > 0 && !this->IsVarArg())
    {
        if (flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND))
        {
            assert(cFPRegs == 1);
            assert((STRUCT_FLOAT_FIELD_FIRST == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)) || (STRUCT_FLOAT_FIELD_SECOND == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)));

            if ((1 + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) && (m_idxGenReg + 1 <= NUM_ARGUMENT_REGISTERS))
            {
                m_argLocDescForStructInRegs.Init();
                m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
                m_argLocDescForStructInRegs.m_cFloatReg = 1;
                int argOfs = (flags & STRUCT_FLOAT_FIELD_SECOND)
                    ? TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8
                    : TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
                m_idxFPReg += 1;

                m_argLocDescForStructInRegs.m_structFields = flags;

                m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg;
                m_argLocDescForStructInRegs.m_cGenReg = 1;
                m_idxGenReg += 1;

                m_hasArgLocDescForStructInRegs = true;

                return argOfs;
            }
        }
        else if (cFPRegs + m_idxFPReg <= NUM_ARGUMENT_REGISTERS)
        {
            int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
            if (flags == STRUCT_FLOAT_FIELD_ONLY_TWO) // struct with two float-fields.
            {
                m_argLocDescForStructInRegs.Init();
                m_hasArgLocDescForStructInRegs = true;
                m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
                assert(cFPRegs == 2);
                m_argLocDescForStructInRegs.m_cFloatReg = 2;
                assert(argSize == 8);
                m_argLocDescForStructInRegs.m_structFields = STRUCT_FLOAT_FIELD_ONLY_TWO;
            }
            m_idxFPReg += cFPRegs;
            return argOfs;
        }
    }

    {
        const int regSlots = ALIGN_UP(cbArg, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE;
        if (m_idxGenReg + regSlots <= NUM_ARGUMENT_REGISTERS)
        {
            int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
            m_idxGenReg += regSlots;
            return argOfs;
        }
        else if (m_idxGenReg < NUM_ARGUMENT_REGISTERS)
        {
            int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
            m_ofsStack += (m_idxGenReg + regSlots - NUM_ARGUMENT_REGISTERS)*8;
            assert(m_ofsStack == 8);
            m_idxGenReg = NUM_ARGUMENT_REGISTERS;
            return argOfs;
        }
    }

    int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
    m_ofsStack += ALIGN_UP(cbArg, TARGET_POINTER_SIZE);

    return argOfs;
#else
    PORTABILITY_ASSERT("ArgIteratorTemplate::GetNextOffset");
    return TransitionBlock::InvalidOffset;
#endif
}