1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
7 // Provides an abstraction over platform specific calling conventions (specifically, the calling convention
8 // utilized by the JIT on that platform). The caller enumerates each argument of a signature in turn, and is
9 // provided with information mapping that argument into registers and/or stack locations.
12 #ifndef __CALLING_CONVENTION_INCLUDED
13 #define __CALLING_CONVENTION_INCLUDED
15 BOOL IsRetBuffPassedAsFirstArg();
17 // Describes how a single argument is laid out in registers and/or stack locations when given as an input to a
18 // managed method as part of a larger signature.
20 // Locations are split into floating point registers, general registers and stack offsets. Registers are
21 // obviously architecture dependent but are represented as a zero-based index into the usual sequence in which
22 // such registers are allocated for input on the platform in question. For instance:
23 // X86: 0 == ecx, 1 == edx
24 // ARM: 0 == r0, 1 == r1, 2 == r2 etc.
26 // Stack locations are represented as offsets from the stack pointer (at the point of the call). The offset is
27 // given as an index of a pointer sized slot. Similarly the size of data on the stack is given in slot-sized
28 // units. For instance, given an index of 2 and a size of 3:
29 // X86: argument starts at [ESP + 8] and is 12 bytes long
30 // AMD64: argument starts at [RSP + 16] and is 24 bytes long
32 // The structure is flexible enough to describe an argument that is split over several (consecutive) registers
33 // and possibly on to the stack as well.
36 int m_idxFloatReg; // First floating point register used (or -1)
37 int m_cFloatReg; // Count of floating point registers used (or 0)
39 int m_idxGenReg; // First general register used (or -1)
40 int m_cGenReg; // Count of general registers used (or 0)
42 int m_byteStackIndex; // Stack offset in bytes (or -1)
43 int m_byteStackSize; // Stack size in bytes
44 #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
45 int m_structFields; // Struct field info when using Float-register except two-doubles case.
48 #if defined(UNIX_AMD64_ABI)
50 EEClass* m_eeClass; // For structs passed in register, it points to the EEClass of the struct
52 #endif // UNIX_AMD64_ABI
55 static unsigned getHFAFieldSize(CorInfoHFAElemType hfaType)
59 case CORINFO_HFA_ELEM_FLOAT: return 4;
60 case CORINFO_HFA_ELEM_DOUBLE: return 8;
61 case CORINFO_HFA_ELEM_VECTOR64: return 8;
62 case CORINFO_HFA_ELEM_VECTOR128: return 16;
63 default: _ASSERTE(!"Invalid HFA Type"); return 0;
67 #if defined(TARGET_ARM64)
68 unsigned m_hfaFieldSize; // Size of HFA field in bytes.
69 void setHFAFieldSize(CorInfoHFAElemType hfaType)
71 m_hfaFieldSize = getHFAFieldSize(hfaType);
73 #endif // defined(TARGET_ARM64)
75 #if defined(TARGET_ARM)
76 BOOL m_fRequires64BitAlignment; // True if the argument should always be aligned (in registers or on the stack
84 // Initialize to represent a non-placed argument (no register or stack slots referenced).
91 m_byteStackIndex = -1;
93 #if defined(TARGET_ARM)
94 m_fRequires64BitAlignment = FALSE;
96 #if defined(TARGET_ARM64)
98 #endif // defined(TARGET_ARM64)
99 #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
100 m_structFields = STRUCT_NO_FLOAT_FIELD;
102 #if defined(UNIX_AMD64_ABI)
109 // TransitionBlock is layout of stack frame of method call, saved argument registers and saved callee saved registers. Even though not
110 // all fields are used all the time, we use uniform form for simplicity.
112 struct TransitionBlock
114 #if defined(TARGET_X86)
115 ArgumentRegisters m_argumentRegisters;
116 CalleeSavedRegisters m_calleeSavedRegisters;
117 TADDR m_ReturnAddress;
118 #elif defined(TARGET_AMD64)
119 #ifdef UNIX_AMD64_ABI
120 ArgumentRegisters m_argumentRegisters;
122 CalleeSavedRegisters m_calleeSavedRegisters;
123 TADDR m_ReturnAddress;
124 #elif defined(TARGET_ARM)
126 CalleeSavedRegisters m_calleeSavedRegisters;
127 // alias saved link register as m_ReturnAddress
129 INT32 r4, r5, r6, r7, r8, r9, r10;
131 TADDR m_ReturnAddress;
134 ArgumentRegisters m_argumentRegisters;
135 #elif defined(TARGET_ARM64)
137 CalleeSavedRegisters m_calleeSavedRegisters;
139 INT64 x29; // frame pointer
140 TADDR m_ReturnAddress;
141 INT64 x19, x20, x21, x22, x23, x24, x25, x26, x27, x28;
144 TADDR padding; // Keep size of TransitionBlock as multiple of 16-byte. Simplifies code in PROLOG_WITH_TRANSITION_BLOCK
145 INT64 m_x8RetBuffReg;
146 ArgumentRegisters m_argumentRegisters;
147 #elif defined(TARGET_LOONGARCH64)
149 CalleeSavedRegisters m_calleeSavedRegisters;
151 INT64 fp; // frame pointer
152 TADDR m_ReturnAddress;
165 //TADDR padding; // Keep size of TransitionBlock as multiple of 16-byte. Simplifies code in PROLOG_WITH_TRANSITION_BLOCK
166 ArgumentRegisters m_argumentRegisters;
167 #elif defined(TARGET_RISCV64)
169 CalleeSavedRegisters m_calleeSavedRegisters;
171 INT64 s0; // frame pointer
172 TADDR m_ReturnAddress;
188 //TADDR padding; // Keep size of TransitionBlock as multiple of 16-byte. Simplifies code in PROLOG_WITH_TRANSITION_BLOCK
189 ArgumentRegisters m_argumentRegisters;
191 PORTABILITY_ASSERT("TransitionBlock");
194 // The transition block should define everything pushed by callee. The code assumes in number of places that
195 // end of the transition block is caller's stack pointer.
197 static int GetOffsetOfReturnAddress()
199 LIMITED_METHOD_CONTRACT;
200 return offsetof(TransitionBlock, m_ReturnAddress);
204 static int GetOffsetOfRetBuffArgReg()
206 LIMITED_METHOD_CONTRACT;
207 return offsetof(TransitionBlock, m_x8RetBuffReg);
210 static int GetOffsetOfFirstGCRefMapSlot()
212 return GetOffsetOfRetBuffArgReg();
215 static int GetOffsetOfFirstGCRefMapSlot()
217 return GetOffsetOfArgumentRegisters();
221 static BYTE GetOffsetOfArgs()
223 LIMITED_METHOD_CONTRACT;
225 // Offset of the stack args (which are after the TransitionBlock)
226 return sizeof(TransitionBlock);
229 static int GetOffsetOfArgumentRegisters()
231 LIMITED_METHOD_CONTRACT;
233 #if defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI)
234 offs = sizeof(TransitionBlock);
236 offs = offsetof(TransitionBlock, m_argumentRegisters);
241 static BOOL IsStackArgumentOffset(int offset)
243 LIMITED_METHOD_CONTRACT;
245 #if defined(UNIX_AMD64_ABI)
246 return offset >= (int)sizeof(TransitionBlock);
248 int ofsArgRegs = GetOffsetOfArgumentRegisters();
250 return offset >= (int) (ofsArgRegs + ARGUMENTREGISTERS_SIZE);
254 static BOOL IsArgumentRegisterOffset(int offset)
256 LIMITED_METHOD_CONTRACT;
258 int ofsArgRegs = GetOffsetOfArgumentRegisters();
260 return offset >= ofsArgRegs && offset < (int) (ofsArgRegs + ARGUMENTREGISTERS_SIZE);
263 static UINT GetArgumentIndexFromOffset(int offset)
265 LIMITED_METHOD_CONTRACT;
267 #if defined(UNIX_AMD64_ABI)
268 _ASSERTE(offset != TransitionBlock::StructInRegsOffset);
270 offset -= GetOffsetOfArgumentRegisters();
271 _ASSERTE((offset % TARGET_POINTER_SIZE) == 0);
272 return offset / TARGET_POINTER_SIZE;
275 static UINT GetStackArgumentIndexFromOffset(int offset)
277 LIMITED_METHOD_CONTRACT;
279 return (offset - TransitionBlock::GetOffsetOfArgs()) / TARGET_POINTER_SIZE;
282 static UINT GetStackArgumentByteIndexFromOffset(int offset)
284 LIMITED_METHOD_CONTRACT;
286 return (offset - TransitionBlock::GetOffsetOfArgs());
289 #ifdef CALLDESCR_FPARGREGS
290 static BOOL IsFloatArgumentRegisterOffset(int offset)
292 LIMITED_METHOD_CONTRACT;
293 #if defined(UNIX_AMD64_ABI)
294 return (offset != TransitionBlock::StructInRegsOffset) && (offset < 0);
300 // Check if an argument has floating point register, that means that it is
301 // either a floating point argument or a struct passed in registers that
302 // has a floating point member.
303 static BOOL HasFloatRegister(int offset, ArgLocDesc* argLocDescForStructInRegs)
305 LIMITED_METHOD_CONTRACT;
306 #if defined(UNIX_AMD64_ABI)
307 if (offset == TransitionBlock::StructInRegsOffset)
309 return argLocDescForStructInRegs->m_cFloatReg > 0;
315 static int GetOffsetOfFloatArgumentRegisters()
317 LIMITED_METHOD_CONTRACT;
318 return -GetNegSpaceSize();
320 #endif // CALLDESCR_FPARGREGS
322 static int GetOffsetOfCalleeSavedRegisters()
324 LIMITED_METHOD_CONTRACT;
325 return offsetof(TransitionBlock, m_calleeSavedRegisters);
328 static int GetNegSpaceSize()
330 LIMITED_METHOD_CONTRACT;
331 int negSpaceSize = 0;
332 #ifdef CALLDESCR_FPARGREGS
333 negSpaceSize += sizeof(FloatArgumentRegisters);
336 negSpaceSize += TARGET_POINTER_SIZE; // padding to make FloatArgumentRegisters address 8-byte aligned
341 static const int InvalidOffset = -1;
342 #if defined(UNIX_AMD64_ABI)
343 // Special offset value to represent struct passed in registers. Such a struct can span both
344 // general purpose and floating point registers, so it can have two different offsets.
345 static const int StructInRegsOffset = -2;
349 //-----------------------------------------------------------------------
350 // ArgIterator is helper for dealing with calling conventions.
351 // It is tightly coupled with TransitionBlock. It uses offsets into
352 // TransitionBlock to represent argument locations for efficiency
353 // reasons. Alternatively, it can also return ArgLocDesc for less
354 // performance critical code.
356 // The ARGITERATOR_BASE argument of the template is provider of the parsed
357 // method signature. Typically, the arg iterator works on top of MetaSig.
358 // Reflection invoke uses alternative implementation to save signature parsing
359 // time because of it has the parsed signature available.
360 //-----------------------------------------------------------------------
361 template<class ARGITERATOR_BASE>
362 class ArgIteratorTemplate : public ARGITERATOR_BASE
365 //------------------------------------------------------------
367 //------------------------------------------------------------
368 ArgIteratorTemplate()
374 UINT SizeOfArgStack()
377 if (!(m_dwFlags & SIZE_OF_ARG_STACK_COMPUTED))
379 _ASSERTE((m_dwFlags & SIZE_OF_ARG_STACK_COMPUTED) != 0);
380 _ASSERTE((m_nSizeOfArgStack % TARGET_POINTER_SIZE) == 0);
381 return m_nSizeOfArgStack;
384 // For use with ArgIterator. This function computes the amount of additional
385 // memory required above the TransitionBlock. The parameter offsets
386 // returned by ArgIteratorTemplate::GetNextOffset are relative to a
387 // FramedMethodFrame, and may be in either of these regions.
388 UINT SizeOfFrameArgumentArray()
392 UINT size = SizeOfArgStack();
394 #if defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI)
395 // The argument registers are not included in the stack size on AMD64
396 size += ARGUMENTREGISTERS_SIZE;
398 _ASSERTE((size % TARGET_POINTER_SIZE) == 0);
402 //------------------------------------------------------------------------
409 if (this->IsVarArg())
412 return SizeOfArgStack();
416 // Is there a hidden parameter for the return parameter?
421 if (!(m_dwFlags & RETURN_FLAGS_COMPUTED))
422 ComputeReturnFlags();
423 return (m_dwFlags & RETURN_HAS_RET_BUFFER);
426 UINT GetFPReturnSize()
429 if (!(m_dwFlags & RETURN_FLAGS_COMPUTED))
430 ComputeReturnFlags();
431 return m_dwFlags >> RETURN_FP_SIZE_SHIFT;
435 //=========================================================================
436 // Indicates whether an argument is to be put in a register using the
437 // default IL calling convention. This should be called on each parameter
438 // in the order it appears in the call signature. For a non-static method,
439 // this function should also be called once for the "this" argument, prior
440 // to calling it for the "real" arguments. Pass in a typ of ELEMENT_TYPE_CLASS.
442 // *pNumRegistersUsed: [in,out]: keeps track of the number of argument
443 // registers assigned previously. The caller should
444 // initialize this variable to 0 - then each call
447 // typ: the signature type
448 //=========================================================================
449 static BOOL IsArgumentInRegister(int * pNumRegistersUsed, CorElementType typ, TypeHandle hnd)
451 LIMITED_METHOD_CONTRACT;
452 if ( (*pNumRegistersUsed) < NUM_ARGUMENT_REGISTERS)
454 if (typ == ELEMENT_TYPE_VALUETYPE)
456 // The JIT enables passing trivial pointer sized structs in registers.
457 MethodTable* pMT = hnd.GetMethodTable();
459 while (typ == ELEMENT_TYPE_VALUETYPE &&
460 pMT->GetNumInstanceFields() == 1 && (!pMT->HasLayout() ||
461 pMT->GetNumInstanceFieldBytes() == 4
462 )) // Don't do the optimization if we're getting specified anything but the trivial layout.
464 FieldDesc * pFD = pMT->GetApproxFieldDescListRaw();
465 CorElementType type = pFD->GetFieldType();
467 bool exitLoop = false;
470 case ELEMENT_TYPE_VALUETYPE:
472 //@todo: Is it more apropos to call LookupApproxFieldTypeHandle() here?
473 TypeHandle fldHnd = pFD->GetApproxFieldTypeHandleThrowing();
474 CONSISTENCY_CHECK(!fldHnd.IsNull());
475 pMT = fldHnd.GetMethodTable();
478 case ELEMENT_TYPE_PTR:
481 case ELEMENT_TYPE_I4:
482 case ELEMENT_TYPE_U4:
498 if (gElementTypeInfo[typ].m_enregister)
500 (*pNumRegistersUsed)++;
509 #if defined(ENREGISTERED_PARAMTYPE_MAXSIZE)
511 // Note that this overload does not handle varargs
512 static BOOL IsArgPassedByRef(TypeHandle th)
514 LIMITED_METHOD_CONTRACT;
516 _ASSERTE(!th.IsNull());
518 // This method only works for valuetypes. It includes true value types,
519 // primitives, enums and TypedReference.
520 _ASSERTE(th.IsValueType());
522 size_t size = th.GetSize();
524 return IsArgPassedByRef(size);
525 #elif defined(TARGET_ARM64)
526 // Composites greater than 16 bytes are passed by reference
527 return ((size > ENREGISTERED_PARAMTYPE_MAXSIZE) && !th.IsHFA());
528 #elif defined(TARGET_LOONGARCH64)
529 // Composites greater than 16 bytes are passed by reference
530 return (size > ENREGISTERED_PARAMTYPE_MAXSIZE);
531 #elif defined(TARGET_RISCV64)
532 return (size > ENREGISTERED_PARAMTYPE_MAXSIZE);
534 PORTABILITY_ASSERT("ArgIteratorTemplate::IsArgPassedByRef");
540 // This overload should only be used in AMD64-specific code only.
541 static BOOL IsArgPassedByRef(size_t size)
543 LIMITED_METHOD_CONTRACT;
545 #ifdef UNIX_AMD64_ABI
546 // No arguments are passed by reference on AMD64 on Unix
549 // If the size is bigger than ENREGISTERED_PARAM_TYPE_MAXSIZE, or if the size is NOT a power of 2, then
550 // the argument is passed by reference.
551 return (size > ENREGISTERED_PARAMTYPE_MAXSIZE) || ((size & (size-1)) != 0);
554 #endif // TARGET_AMD64
556 // This overload should be used for varargs only.
557 static BOOL IsVarArgPassedByRef(size_t size)
559 LIMITED_METHOD_CONTRACT;
562 #ifdef UNIX_AMD64_ABI
563 PORTABILITY_ASSERT("ArgIteratorTemplate::IsVarArgPassedByRef");
565 #else // UNIX_AMD64_ABI
566 return IsArgPassedByRef(size);
567 #endif // UNIX_AMD64_ABI
570 return (size > ENREGISTERED_PARAMTYPE_MAXSIZE);
574 BOOL IsArgPassedByRef()
576 LIMITED_METHOD_CONTRACT;
579 return IsArgPassedByRef(m_argSize);
580 #elif defined(TARGET_ARM64)
581 if (m_argType == ELEMENT_TYPE_VALUETYPE)
583 _ASSERTE(!m_argTypeHandle.IsNull());
584 return ((m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE) && (!m_argTypeHandle.IsHFA() || this->IsVarArg()));
587 #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
588 if (m_argType == ELEMENT_TYPE_VALUETYPE)
590 _ASSERTE(!m_argTypeHandle.IsNull());
591 return (m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE);
595 PORTABILITY_ASSERT("ArgIteratorTemplate::IsArgPassedByRef");
600 #endif // ENREGISTERED_PARAMTYPE_MAXSIZE
602 //------------------------------------------------------------
603 // Return the offsets of the special arguments
604 //------------------------------------------------------------
606 static int GetThisOffset();
608 int GetRetBuffArgOffset();
609 int GetVASigCookieOffset();
610 int GetParamTypeArgOffset();
612 //------------------------------------------------------------
613 // Each time this is called, this returns a byte offset of the next
614 // argument from the TransitionBlock* pointer.
616 // Returns TransitionBlock::InvalidOffset once you've hit the end
618 //------------------------------------------------------------
621 CorElementType GetArgType(TypeHandle *pTypeHandle = NULL)
623 LIMITED_METHOD_CONTRACT;
624 if (pTypeHandle != NULL)
626 *pTypeHandle = m_argTypeHandle;
633 LIMITED_METHOD_CONTRACT;
640 // Accessors for built in argument descriptions of the special implicit parameters not mentioned directly
641 // in signatures (this pointer and the like). Whether or not these can be used successfully before all the
642 // explicit arguments have been scanned is platform dependent.
643 void GetThisLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetThisOffset(), pLoc); }
644 void GetParamTypeLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetParamTypeArgOffset(), pLoc); }
645 void GetVASigCookieLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetVASigCookieOffset(), pLoc); }
647 #ifndef CALLDESCR_RETBUFFARGREG
648 void GetRetBuffArgLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetRetBuffArgOffset(), pLoc); }
651 #endif // !TARGET_X86
653 ArgLocDesc* GetArgLocDescForStructInRegs()
655 #if defined(UNIX_AMD64_ABI) || defined (TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined (TARGET_RISCV64)
656 return m_hasArgLocDescForStructInRegs ? &m_argLocDescForStructInRegs : NULL;
663 // Get layout information for the argument that the ArgIterator is currently visiting.
664 void GetArgLoc(int argOffset, ArgLocDesc *pLoc)
666 LIMITED_METHOD_CONTRACT;
670 if (!TransitionBlock::IsStackArgumentOffset(argOffset))
672 pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset);
673 _ASSERTE(GetArgSize() <= TARGET_POINTER_SIZE);
678 pLoc->m_byteStackSize = GetArgSize();
679 pLoc->m_byteStackIndex = TransitionBlock::GetStackArgumentByteIndexFromOffset(argOffset);
685 // Get layout information for the argument that the ArgIterator is currently visiting.
686 void GetArgLoc(int argOffset, ArgLocDesc *pLoc)
688 LIMITED_METHOD_CONTRACT;
692 pLoc->m_fRequires64BitAlignment = m_fRequires64BitAlignment;
694 const int byteArgSize = GetArgSize();
695 if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset))
697 const int floatRegOfsInBytes = argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters();
698 _ASSERTE((floatRegOfsInBytes % FLOAT_REGISTER_SIZE) == 0);
699 pLoc->m_idxFloatReg = floatRegOfsInBytes / FLOAT_REGISTER_SIZE;
700 pLoc->m_cFloatReg = ALIGN_UP(byteArgSize, FLOAT_REGISTER_SIZE) / FLOAT_REGISTER_SIZE;
704 if (!TransitionBlock::IsStackArgumentOffset(argOffset))
706 pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset);
708 if (byteArgSize <= (4 - pLoc->m_idxGenReg) * TARGET_POINTER_SIZE)
710 pLoc->m_cGenReg = ALIGN_UP(byteArgSize, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE;
714 pLoc->m_cGenReg = 4 - pLoc->m_idxGenReg;
715 pLoc->m_byteStackIndex = 0;
716 pLoc->m_byteStackSize = StackElemSize(byteArgSize) - pLoc->m_cGenReg * TARGET_POINTER_SIZE;
721 pLoc->m_byteStackIndex = TransitionBlock::GetStackArgumentByteIndexFromOffset(argOffset);
722 pLoc->m_byteStackSize = StackElemSize(byteArgSize);
728 // Get layout information for the argument that the ArgIterator is currently visiting.
729 void GetArgLoc(int argOffset, ArgLocDesc *pLoc)
731 LIMITED_METHOD_CONTRACT;
736 if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset))
738 const int floatRegOfsInBytes = argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters();
739 _ASSERTE((floatRegOfsInBytes % FLOAT_REGISTER_SIZE) == 0);
740 pLoc->m_idxFloatReg = floatRegOfsInBytes / FLOAT_REGISTER_SIZE;
742 if (!m_argTypeHandle.IsNull() && m_argTypeHandle.IsHFA())
744 CorInfoHFAElemType type = m_argTypeHandle.GetHFAType();
745 pLoc->setHFAFieldSize(type);
746 pLoc->m_cFloatReg = GetArgSize() / pLoc->m_hfaFieldSize;
751 pLoc->m_cFloatReg = 1;
756 unsigned byteArgSize = GetArgSize();
758 // On ARM64 some composites are implicitly passed by reference.
759 if (IsArgPassedByRef())
761 byteArgSize = TARGET_POINTER_SIZE;
765 // Sanity check to make sure no caller is trying to get an ArgLocDesc that
766 // describes the return buffer reg field that's in the TransitionBlock.
767 _ASSERTE(argOffset != TransitionBlock::GetOffsetOfRetBuffArgReg());
769 if (!TransitionBlock::IsStackArgumentOffset(argOffset))
771 pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset);
772 pLoc->m_cGenReg = ALIGN_UP(byteArgSize, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE;;
776 pLoc->m_byteStackIndex = TransitionBlock::GetStackArgumentByteIndexFromOffset(argOffset);
777 const bool isValueType = (m_argType == ELEMENT_TYPE_VALUETYPE);
778 const bool isFloatHfa = (isValueType && !m_argTypeHandle.IsNull() && m_argTypeHandle.IsHFA());
781 CorInfoHFAElemType type = m_argTypeHandle.GetHFAType();
782 pLoc->setHFAFieldSize(type);
784 pLoc->m_byteStackSize = StackElemSize(byteArgSize, isValueType, isFloatHfa);
787 #endif // TARGET_ARM64
789 #if defined(TARGET_AMD64)
790 // Get layout information for the argument that the ArgIterator is currently visiting.
791 void GetArgLoc(int argOffset, ArgLocDesc* pLoc)
793 LIMITED_METHOD_CONTRACT;
795 #if defined(UNIX_AMD64_ABI)
796 if (m_hasArgLocDescForStructInRegs)
798 *pLoc = m_argLocDescForStructInRegs;
802 if (argOffset == TransitionBlock::StructInRegsOffset)
804 // We always already have argLocDesc for structs passed in registers, we
805 // compute it in the GetNextOffset for those since it is always needed.
809 #endif // UNIX_AMD64_ABI
813 #if defined(UNIX_AMD64_ABI)
814 if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset))
816 const int floatRegOfsInBytes = argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters();
817 _ASSERTE((floatRegOfsInBytes % FLOAT_REGISTER_SIZE) == 0);
818 pLoc->m_idxFloatReg = floatRegOfsInBytes / FLOAT_REGISTER_SIZE;
819 pLoc->m_cFloatReg = 1;
822 #endif // UNIX_AMD64_ABI
823 if (!TransitionBlock::IsStackArgumentOffset(argOffset))
825 #if !defined(UNIX_AMD64_ABI)
826 // On Windows x64, we re-use the location in the transition block for both the integer and floating point registers
827 if ((m_argType == ELEMENT_TYPE_R4) || (m_argType == ELEMENT_TYPE_R8))
829 pLoc->m_idxFloatReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset);
830 pLoc->m_cFloatReg = 1;
835 pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset);
841 pLoc->m_byteStackIndex = TransitionBlock::GetStackArgumentByteIndexFromOffset(argOffset);
843 if (IsArgPassedByRef())
844 argSizeInBytes = TARGET_POINTER_SIZE;
846 argSizeInBytes = GetArgSize();
847 pLoc->m_byteStackSize = StackElemSize(argSizeInBytes);
850 #endif // TARGET_AMD64
852 #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
853 // Get layout information for the argument that the ArgIterator is currently visiting.
854 // TODO-RISCV64: support SIMD.
855 void GetArgLoc(int argOffset, ArgLocDesc *pLoc)
857 LIMITED_METHOD_CONTRACT;
861 if (m_hasArgLocDescForStructInRegs)
863 *pLoc = m_argLocDescForStructInRegs;
867 if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset))
869 // Dividing by 8 as size of each register in FloatArgumentRegisters is 8 bytes.
870 const int floatRegOfsInBytes = argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters();
871 _ASSERTE((floatRegOfsInBytes % FLOAT_REGISTER_SIZE) == 0);
873 pLoc->m_idxFloatReg = floatRegOfsInBytes / FLOAT_REGISTER_SIZE;
875 pLoc->m_cFloatReg = 1;
880 int cSlots = (GetArgSize() + 7)/ 8;
882 // Composites greater than 16bytes are passed by reference
883 if (IsArgPassedByRef())
888 if (!TransitionBlock::IsStackArgumentOffset(argOffset))
890 // At least one used integer register passed.
891 pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset);
892 pLoc->m_cGenReg = cSlots;
896 pLoc->m_byteStackIndex = TransitionBlock::GetStackArgumentByteIndexFromOffset(argOffset);
897 pLoc->m_byteStackSize = cSlots << 3;
903 #endif // TARGET_LOONGARCH64 || TARGET_RISCV64
905 DWORD m_dwFlags; // Cached flags
906 int m_nSizeOfArgStack; // Cached value of SizeOfArgStack
910 // Cached information about last argument
911 CorElementType m_argType;
913 TypeHandle m_argTypeHandle;
914 #if (defined(TARGET_AMD64) && defined(UNIX_AMD64_ABI)) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
915 ArgLocDesc m_argLocDescForStructInRegs;
916 bool m_hasArgLocDescForStructInRegs;
917 #endif // (TARGET_AMD64 && UNIX_AMD64_ABI) || TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64
919 int m_ofsStack; // Current position of the stack iterator, in bytes
922 int m_numRegistersUsed;
923 #ifdef FEATURE_INTERPRETER
924 bool m_fUnmanagedCallConv;
928 #ifdef UNIX_AMD64_ABI
929 int m_idxGenReg; // Next general register to be assigned a value
930 int m_idxFPReg; // Next floating point register to be assigned a value
931 bool m_fArgInRegisters; // Indicates that the current argument is stored in registers
935 int m_idxGenReg; // Next general register to be assigned a value
936 WORD m_wFPRegs; // Bitmask of available floating point argument registers (s0-s15/d0-d7)
937 bool m_fRequires64BitAlignment; // Cached info about the current arg
941 int m_idxGenReg; // Next general register to be assigned a value
942 int m_idxFPReg; // Next FP register to be assigned a value
945 #ifdef TARGET_LOONGARCH64
946 int m_idxGenReg; // Next general register to be assigned a value
947 int m_idxStack; // Next stack slot to be assigned a value
948 int m_idxFPReg; // Next FP register to be assigned a value
951 #ifdef TARGET_RISCV64
952 int m_idxGenReg; // Next general register to be assigned a value
953 int m_idxStack; // Next stack slot to be assigned a value
954 int m_idxFPReg; // Next FP register to be assigned a value
958 ITERATION_STARTED = 0x0001, // Started iterating over arguments
959 SIZE_OF_ARG_STACK_COMPUTED = 0x0002,
960 RETURN_FLAGS_COMPUTED = 0x0004,
961 RETURN_HAS_RET_BUFFER = 0x0008, // Cached value of HasRetBuffArg
964 PARAM_TYPE_REGISTER_MASK = 0x0030,
965 PARAM_TYPE_REGISTER_STACK = 0x0010,
966 PARAM_TYPE_REGISTER_ECX = 0x0020,
967 PARAM_TYPE_REGISTER_EDX = 0x0030,
970 METHOD_INVOKE_NEEDS_ACTIVATION = 0x0040, // Flag used by ArgIteratorForMethodInvoke
972 RETURN_FP_SIZE_SHIFT = 8, // The rest of the flags is cached value of GetFPReturnSize
975 void ComputeReturnFlags();
978 void GetSimpleLoc(int offset, ArgLocDesc * pLoc)
982 #ifdef CALLDESCR_RETBUFFARGREG
983 // Codepaths where this could happen have been removed. If this occurs, something
984 // has been missed and this needs another look.
985 _ASSERTE(offset != TransitionBlock::GetOffsetOfRetBuffArgReg());
989 pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(offset);
996 template<class ARGITERATOR_BASE>
997 int ArgIteratorTemplate<ARGITERATOR_BASE>::GetThisOffset()
1001 // This pointer is in the first argument register by default
1002 int ret = TransitionBlock::GetOffsetOfArgumentRegisters();
1005 // x86 is special as always
1006 ret += offsetof(ArgumentRegisters, ECX);
1012 template<class ARGITERATOR_BASE>
1013 int ArgIteratorTemplate<ARGITERATOR_BASE>::GetRetBuffArgOffset()
1015 WRAPPER_NO_CONTRACT;
1017 _ASSERTE(this->HasRetBuffArg());
1019 // RetBuf arg is in the second argument register by default
1020 int ret = TransitionBlock::GetOffsetOfArgumentRegisters();
1023 // x86 is special as always
1024 ret += this->HasThis() ? offsetof(ArgumentRegisters, EDX) : offsetof(ArgumentRegisters, ECX);
1026 ret = TransitionBlock::GetOffsetOfRetBuffArgReg();
1028 if (this->HasThis())
1029 ret += TARGET_POINTER_SIZE;
1035 template<class ARGITERATOR_BASE>
1036 int ArgIteratorTemplate<ARGITERATOR_BASE>::GetVASigCookieOffset()
1038 WRAPPER_NO_CONTRACT;
1040 _ASSERTE(this->IsVarArg());
1042 #if defined(TARGET_X86)
1043 // x86 is special as always
1044 return sizeof(TransitionBlock);
1046 // VaSig cookie is after this and retbuf arguments by default.
1047 int ret = TransitionBlock::GetOffsetOfArgumentRegisters();
1049 if (this->HasThis())
1051 ret += TARGET_POINTER_SIZE;
1054 if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
1056 ret += TARGET_POINTER_SIZE;
1063 //-----------------------------------------------------------
1064 // Get the extra param offset for shared generic code
1065 //-----------------------------------------------------------
1066 template<class ARGITERATOR_BASE>
1067 int ArgIteratorTemplate<ARGITERATOR_BASE>::GetParamTypeArgOffset()
1072 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
1073 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
1074 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
1079 _ASSERTE(this->HasParamType());
1082 // x86 is special as always
1083 if (!(m_dwFlags & SIZE_OF_ARG_STACK_COMPUTED))
1086 switch (m_dwFlags & PARAM_TYPE_REGISTER_MASK)
1088 case PARAM_TYPE_REGISTER_ECX:
1089 return TransitionBlock::GetOffsetOfArgumentRegisters() + offsetof(ArgumentRegisters, ECX);
1090 case PARAM_TYPE_REGISTER_EDX:
1091 return TransitionBlock::GetOffsetOfArgumentRegisters() + offsetof(ArgumentRegisters, EDX);
1096 // The param type arg is last stack argument otherwise
1097 return sizeof(TransitionBlock);
1099 // The hidden arg is after this and retbuf arguments by default.
1100 int ret = TransitionBlock::GetOffsetOfArgumentRegisters();
1102 if (this->HasThis())
1104 ret += TARGET_POINTER_SIZE;
1107 if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
1109 ret += TARGET_POINTER_SIZE;
1116 // To avoid corner case bugs, limit maximum size of the arguments with sufficient margin
1117 #define MAX_ARG_SIZE 0xFFFFFF
1119 //------------------------------------------------------------
1120 // Each time this is called, this returns a byte offset of the next
1121 // argument from the Frame* pointer. This offset can be positive *or* negative.
1123 // Returns TransitionBlock::InvalidOffset once you've hit the end of the list.
1124 //------------------------------------------------------------
1125 template<class ARGITERATOR_BASE>
1126 int ArgIteratorTemplate<ARGITERATOR_BASE>::GetNextOffset()
1128 WRAPPER_NO_CONTRACT;
1131 if (!(m_dwFlags & ITERATION_STARTED))
1133 int numRegistersUsed = 0;
1135 if (this->HasThis())
1138 if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
1141 _ASSERTE(!this->IsVarArg() || !this->HasParamType());
1144 if (this->IsVarArg() || this->HasParamType())
1151 if (this->IsVarArg())
1153 numRegistersUsed = NUM_ARGUMENT_REGISTERS; // Nothing else gets passed in registers for varargs
1156 #ifdef FEATURE_INTERPRETER
1157 BYTE callconv = CallConv();
1160 case IMAGE_CEE_CS_CALLCONV_C:
1161 case IMAGE_CEE_CS_CALLCONV_STDCALL:
1162 m_numRegistersUsed = NUM_ARGUMENT_REGISTERS;
1163 m_ofsStack = TransitionBlock::GetOffsetOfArgs() + numRegistersUsed * sizeof(void *);
1164 m_fUnmanagedCallConv = true;
1167 case IMAGE_CEE_CS_CALLCONV_THISCALL:
1168 case IMAGE_CEE_CS_CALLCONV_FASTCALL:
1169 _ASSERTE_MSG(false, "Unsupported calling convention.");
1172 m_fUnmanagedCallConv = false;
1173 m_numRegistersUsed = numRegistersUsed;
1174 m_ofsStack = TransitionBlock::GetOffsetOfArgs() + SizeOfArgStack();
1178 m_numRegistersUsed = numRegistersUsed;
1179 m_ofsStack = TransitionBlock::GetOffsetOfArgs() + SizeOfArgStack();
1182 #elif defined(TARGET_AMD64)
1183 #ifdef UNIX_AMD64_ABI
1184 m_idxGenReg = numRegistersUsed;
1188 m_ofsStack = TransitionBlock::GetOffsetOfArgs() + numRegistersUsed * sizeof(void *);
1190 #elif defined(TARGET_ARM)
1191 m_idxGenReg = numRegistersUsed;
1195 #elif defined(TARGET_ARM64)
1196 m_idxGenReg = numRegistersUsed;
1200 #elif defined(TARGET_LOONGARCH64)
1201 m_idxGenReg = numRegistersUsed;
1204 #elif defined(TARGET_RISCV64)
1205 m_idxGenReg = numRegistersUsed;
1209 PORTABILITY_ASSERT("ArgIteratorTemplate::GetNextOffset");
1214 m_dwFlags |= ITERATION_STARTED;
1217 // We're done going through the args for this MetaSig
1218 if (m_argNum == this->NumFixedArgs())
1219 return TransitionBlock::InvalidOffset;
1221 TypeHandle thValueType;
1222 CorElementType argType = this->GetNextArgumentType(m_argNum++, &thValueType);
1224 // TypedReference behaves like a valuetype
1225 if (argType == ELEMENT_TYPE_TYPEDBYREF)
1227 argType = ELEMENT_TYPE_VALUETYPE;
1228 thValueType = TypeHandle(g_TypedReferenceMT);
1231 int argSize = MetaSig::GetElemSize(argType, thValueType);
1233 m_argType = argType;
1234 m_argSize = argSize;
1235 m_argTypeHandle = thValueType;
1237 #if defined(UNIX_AMD64_ABI) || defined (TARGET_ARM64) || defined (TARGET_LOONGARCH64) || defined (TARGET_RISCV64)
1238 m_hasArgLocDescForStructInRegs = false;
1242 #ifdef FEATURE_INTERPRETER
1243 if (m_fUnmanagedCallConv)
1245 int argOfs = m_ofsStack;
1246 m_ofsStack += StackElemSize(argSize);
1250 if (IsArgumentInRegister(&m_numRegistersUsed, argType, thValueType))
1252 return TransitionBlock::GetOffsetOfArgumentRegisters() + (NUM_ARGUMENT_REGISTERS - m_numRegistersUsed) * sizeof(void *);
1255 m_ofsStack -= StackElemSize(argSize);
1256 _ASSERTE(m_ofsStack >= TransitionBlock::GetOffsetOfArgs());
1258 #elif defined(TARGET_AMD64)
1259 #ifdef UNIX_AMD64_ABI
1261 m_fArgInRegisters = true;
1265 int cbArg = StackElemSize(argSize);
1270 case ELEMENT_TYPE_R4:
1271 // 32-bit floating point argument.
1275 case ELEMENT_TYPE_R8:
1276 // 64-bit floating point argument.
1280 case ELEMENT_TYPE_VALUETYPE:
1282 MethodTable *pMT = m_argTypeHandle.GetMethodTable();
1283 if (this->IsRegPassedStruct(pMT))
1285 EEClass* eeClass = pMT->GetClass();
1287 for (int i = 0; i < eeClass->GetNumberEightBytes(); i++)
1289 switch (eeClass->GetEightByteClassification(i))
1291 case SystemVClassificationTypeInteger:
1292 case SystemVClassificationTypeIntegerReference:
1293 case SystemVClassificationTypeIntegerByRef:
1296 case SystemVClassificationTypeSSE:
1305 // Check if we have enough registers available for the struct passing
1306 if ((cFPRegs + m_idxFPReg <= NUM_FLOAT_ARGUMENT_REGISTERS) && (cGenRegs + m_idxGenReg) <= NUM_ARGUMENT_REGISTERS)
1308 m_argLocDescForStructInRegs.Init();
1309 m_argLocDescForStructInRegs.m_cGenReg = cGenRegs;
1310 m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs;
1311 m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg;
1312 m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
1313 m_argLocDescForStructInRegs.m_eeClass = eeClass;
1315 m_hasArgLocDescForStructInRegs = true;
1317 m_idxGenReg += cGenRegs;
1318 m_idxFPReg += cFPRegs;
1320 return TransitionBlock::StructInRegsOffset;
1324 // Set the register counts to indicate that this argument will not be passed in registers
1331 cGenRegs = cbArg / 8; // GP reg size
1335 if ((cFPRegs > 0) && (cFPRegs + m_idxFPReg <= NUM_FLOAT_ARGUMENT_REGISTERS))
1337 int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 16;
1338 m_idxFPReg += cFPRegs;
1341 else if ((cGenRegs > 0) && (m_idxGenReg + cGenRegs <= NUM_ARGUMENT_REGISTERS))
1343 int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1344 m_idxGenReg += cGenRegs;
1348 m_fArgInRegisters = false;
1350 int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
1352 m_ofsStack += cbArg;
1356 // Each argument takes exactly one slot on AMD64 on Windows
1357 int argOfs = m_ofsStack;
1358 m_ofsStack += sizeof(void *);
1361 #elif defined(TARGET_ARM)
1362 // First look at the underlying type of the argument to determine some basic properties:
1363 // 1) The size of the argument in bytes (rounded up to the stack slot size of 4 if necessary).
1364 // 2) Whether the argument represents a floating point primitive (ELEMENT_TYPE_R4 or ELEMENT_TYPE_R8).
1365 // 3) Whether the argument requires 64-bit alignment (anything that contains a Int64/UInt64).
1367 bool fFloatingPoint = false;
1368 bool fRequiresAlign64Bit = false;
1372 case ELEMENT_TYPE_I8:
1373 case ELEMENT_TYPE_U8:
1374 // 64-bit integers require 64-bit alignment on ARM.
1375 fRequiresAlign64Bit = true;
1378 case ELEMENT_TYPE_R4:
1379 // 32-bit floating point argument.
1380 fFloatingPoint = true;
1383 case ELEMENT_TYPE_R8:
1384 // 64-bit floating point argument.
1385 fFloatingPoint = true;
1386 fRequiresAlign64Bit = true;
1389 case ELEMENT_TYPE_VALUETYPE:
1391 // Value type case: extract the alignment requirement, note that this has to handle
1392 // the interop "native value types".
1393 fRequiresAlign64Bit = thValueType.RequiresAlign8();
1396 // Handle HFAs: packed structures of 1-4 floats or doubles that are passed in FP argument
1397 // registers if possible.
1398 if (thValueType.IsHFA())
1400 fFloatingPoint = true;
1408 // The default is are 4-byte arguments (or promoted to 4 bytes), non-FP and don't require any
1409 // 64-bit alignment.
1413 // Now attempt to place the argument into some combination of floating point or general registers and
1416 // Save the alignment requirement
1417 m_fRequires64BitAlignment = fRequiresAlign64Bit;
1419 int cbArg = StackElemSize(argSize);
1420 _ASSERTE((cbArg % TARGET_POINTER_SIZE) == 0);
1422 // Ignore floating point argument placement in registers if we're dealing with a vararg function (the ABI
1423 // specifies this so that vararg processing on the callee side is simplified).
1425 if (fFloatingPoint && !this->IsVarArg())
1427 // Handle floating point (primitive) arguments.
1429 // First determine whether we can place the argument in VFP registers. There are 16 32-bit
1430 // and 8 64-bit argument registers that share the same register space (e.g. D0 overlaps S0 and
1431 // S1). The ABI specifies that VFP values will be passed in the lowest sequence of registers that
1432 // haven't been used yet and have the required alignment. So the sequence (float, double, float)
1433 // would be mapped to (S0, D1, S1) or (S0, S2/S3, S1).
1435 // We use a 16-bit bitmap to record which registers have been used so far.
1437 // So we can use the same basic loop for each argument type (float, double or HFA struct) we set up
1438 // the following input parameters based on the size and alignment requirements of the arguments:
1439 // wAllocMask : bitmask of the number of 32-bit registers we need (1 for 1, 3 for 2, 7 for 3 etc.)
1440 // cSteps : number of loop iterations it'll take to search the 16 registers
1441 // cShift : how many bits to shift the allocation mask on each attempt
1443 WORD wAllocMask = (1 << (cbArg / 4)) - 1;
1444 WORD cSteps = (WORD)(fRequiresAlign64Bit ? 9 - (cbArg / 8) : 17 - (cbArg / 4));
1445 WORD cShift = fRequiresAlign64Bit ? 2 : 1;
1447 // Look through the availability bitmask for a free register or register pair.
1448 for (WORD i = 0; i < cSteps; i++)
1450 if ((m_wFPRegs & wAllocMask) == 0)
1452 // We found one, mark the register or registers as used.
1453 m_wFPRegs |= wAllocMask;
1455 // Indicate the registers used to the caller and return.
1456 return TransitionBlock::GetOffsetOfFloatArgumentRegisters() + (i * cShift * 4);
1458 wAllocMask <<= cShift;
1461 // The FP argument is going to live on the stack. Once this happens the ABI demands we mark all FP
1462 // registers as unavailable.
1465 // Doubles or HFAs containing doubles need the stack aligned appropriately.
1466 if (fRequiresAlign64Bit)
1468 m_ofsStack = (int)ALIGN_UP(m_ofsStack, TARGET_POINTER_SIZE * 2);
1471 // Indicate the stack location of the argument to the caller.
1472 int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
1474 // Record the stack usage.
1475 m_ofsStack += cbArg;
1479 #endif // ARM_SOFTFP
1482 // Handle the non-floating point case.
1485 if (m_idxGenReg < 4)
1487 if (fRequiresAlign64Bit)
1489 // The argument requires 64-bit alignment. Align either the next general argument register if
1490 // we have any left. See step C.3 in the algorithm in the ABI spec.
1491 m_idxGenReg = (int)ALIGN_UP(m_idxGenReg, 2);
1494 int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 4;
1496 int cRemainingRegs = 4 - m_idxGenReg;
1497 if (cbArg <= cRemainingRegs * TARGET_POINTER_SIZE)
1499 // Mark the registers just allocated as used.
1500 m_idxGenReg += ALIGN_UP(cbArg, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE;
1504 // The ABI supports splitting a non-FP argument across registers and the stack. But this is
1505 // disabled if the FP arguments already overflowed onto the stack (i.e. the stack index is not
1506 // zero). The following code marks the general argument registers as exhausted if this condition
1507 // holds. See steps C.5 in the algorithm in the ABI spec.
1511 if (m_ofsStack == 0)
1513 m_ofsStack += cbArg - cRemainingRegs * TARGET_POINTER_SIZE;
1518 if (fRequiresAlign64Bit)
1520 // The argument requires 64-bit alignment. If it is going to be passed on the stack, align
1521 // the next stack slot. See step C.6 in the algorithm in the ABI spec.
1522 m_ofsStack = (int)ALIGN_UP(m_ofsStack, TARGET_POINTER_SIZE * 2);
1525 int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
1527 // Advance the stack pointer over the argument just placed.
1528 m_ofsStack += cbArg;
1531 #elif defined(TARGET_ARM64)
1538 case ELEMENT_TYPE_R4:
1539 // 32-bit floating point argument.
1543 case ELEMENT_TYPE_R8:
1544 // 64-bit floating point argument.
1548 case ELEMENT_TYPE_VALUETYPE:
1550 // Handle HFAs: packed structures of 1-4 floats, doubles, or short vectors
1551 // that are passed in FP argument registers if possible.
1552 if (thValueType.IsHFA())
1554 CorInfoHFAElemType type = thValueType.GetHFAType();
1556 m_argLocDescForStructInRegs.Init();
1557 m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
1559 m_argLocDescForStructInRegs.setHFAFieldSize(type);
1560 cFPRegs = argSize/m_argLocDescForStructInRegs.m_hfaFieldSize;
1561 m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs;
1563 // Check if we have enough registers available for the HFA passing
1564 if ((cFPRegs + m_idxFPReg) <= 8)
1566 m_hasArgLocDescForStructInRegs = true;
1571 // Composite greater than 16bytes should be passed by reference
1572 if (argSize > ENREGISTERED_PARAMTYPE_MAXSIZE)
1574 argSize = sizeof(TADDR);
1584 const bool isValueType = (argType == ELEMENT_TYPE_VALUETYPE);
1585 const bool isFloatHfa = thValueType.IsFloatHfa();
1586 const int cbArg = StackElemSize(argSize, isValueType, isFloatHfa);
1587 if (cFPRegs>0 && !this->IsVarArg())
1589 if (cFPRegs + m_idxFPReg <= 8)
1591 // Each floating point register in the argument area is 16 bytes.
1592 int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 16;
1593 m_idxFPReg += cFPRegs;
1603 #if !defined(OSX_ARM64_ABI)
1604 _ASSERTE((cbArg% TARGET_POINTER_SIZE) == 0);
1606 const int regSlots = ALIGN_UP(cbArg, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE;
1607 // Only x0-x7 are valid argument registers (x8 is always the return buffer)
1608 if (m_idxGenReg + regSlots <= 8)
1610 // The entirety of the arg fits in the register slots.
1612 int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1613 m_idxGenReg += regSlots;
1619 if (this->IsVarArg() && m_idxGenReg < 8)
1621 // Address the Windows ARM64 varargs case where an arg is split between regs and stack.
1622 // This can happen in the varargs case because the first 64 bytes of the stack are loaded
1623 // into x0-x7, and any remaining stack arguments are placed normally.
1624 int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1626 // Increase m_ofsStack to account for the space used for the remainder of the arg after
1627 // registers are filled.
1628 m_ofsStack += cbArg + (m_idxGenReg - 8) * TARGET_POINTER_SIZE;
1630 // We used up the remaining reg slots.
1638 // Don't use reg slots for this. It will be passed purely on the stack arg space.
1644 #ifdef OSX_ARM64_ABI
1648 _ASSERTE((cbArg & (cbArg - 1)) == 0);
1651 else if (isFloatHfa)
1659 m_ofsStack = (int)ALIGN_UP(m_ofsStack, alignment);
1660 #endif // OSX_ARM64_ABI
1662 int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
1663 m_ofsStack += cbArg;
1665 #elif defined(TARGET_LOONGARCH64)
1673 case ELEMENT_TYPE_R4:
1674 // 32-bit floating point argument.
1678 case ELEMENT_TYPE_R8:
1679 // 64-bit floating point argument.
1683 case ELEMENT_TYPE_VALUETYPE:
1685 // Handle struct which containing floats or doubles that can be passed
1686 // in FP registers if possible.
1688 // Composite greater than 16bytes should be passed by reference
1689 if (argSize > ENREGISTERED_PARAMTYPE_MAXSIZE)
1691 argSize = sizeof(TADDR);
1695 MethodTable* pMethodTable = nullptr;
1697 if (!thValueType.IsTypeDesc())
1698 pMethodTable = thValueType.AsMethodTable();
1701 _ASSERTE(thValueType.IsNativeValueType());
1702 pMethodTable = thValueType.AsNativeValueType();
1704 _ASSERTE(pMethodTable != nullptr);
1705 flags = MethodTable::GetLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable);
1706 if (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)
1708 cFPRegs = (flags & STRUCT_FLOAT_FIELD_ONLY_TWO) ? 2 : 1;
1719 const bool isValueType = (argType == ELEMENT_TYPE_VALUETYPE);
1720 const bool isFloatHfa = thValueType.IsFloatHfa();
1721 const int cbArg = StackElemSize(argSize, isValueType, isFloatHfa);
1723 if (cFPRegs > 0 && !this->IsVarArg())
1725 if (flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND))
1727 assert(cFPRegs == 1);
1728 assert((STRUCT_FLOAT_FIELD_FIRST == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)) || (STRUCT_FLOAT_FIELD_SECOND == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)));
1730 if ((1 + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) && (m_idxGenReg + 1 <= NUM_ARGUMENT_REGISTERS))
1732 m_argLocDescForStructInRegs.Init();
1733 m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
1734 m_argLocDescForStructInRegs.m_cFloatReg = 1;
1735 int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
1738 m_argLocDescForStructInRegs.m_structFields = flags;
1740 m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg;
1741 m_argLocDescForStructInRegs.m_cGenReg = 1;
1744 m_hasArgLocDescForStructInRegs = true;
1749 else if (cFPRegs + m_idxFPReg <= NUM_ARGUMENT_REGISTERS)
1751 int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
1752 if (flags == STRUCT_FLOAT_FIELD_ONLY_TWO) // struct with two float-fields.
1754 m_argLocDescForStructInRegs.Init();
1755 m_hasArgLocDescForStructInRegs = true;
1756 m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
1757 assert(cFPRegs == 2);
1758 m_argLocDescForStructInRegs.m_cFloatReg = 2;
1759 assert(argSize == 8);
1760 m_argLocDescForStructInRegs.m_structFields = STRUCT_FLOAT_FIELD_ONLY_TWO;
1762 m_idxFPReg += cFPRegs;
1768 const int regSlots = ALIGN_UP(cbArg, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE;
1769 if (m_idxGenReg + regSlots <= NUM_ARGUMENT_REGISTERS)
1771 int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1772 m_idxGenReg += regSlots;
1775 else if (m_idxGenReg < NUM_ARGUMENT_REGISTERS)
1777 int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1778 m_ofsStack += (m_idxGenReg + regSlots - NUM_ARGUMENT_REGISTERS)*8;
1779 assert(m_ofsStack == 8);
1780 m_idxGenReg = NUM_ARGUMENT_REGISTERS;
1785 int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
1786 m_ofsStack += ALIGN_UP(cbArg, TARGET_POINTER_SIZE);
1789 #elif defined(TARGET_RISCV64)
1797 case ELEMENT_TYPE_R4:
1798 // 32-bit floating point argument.
1802 case ELEMENT_TYPE_R8:
1803 // 64-bit floating point argument.
1807 case ELEMENT_TYPE_VALUETYPE:
1809 // Handle struct which containing floats or doubles that can be passed
1810 // in FP registers if possible.
1812 // Composite greater than 16bytes should be passed by reference
1813 if (argSize > ENREGISTERED_PARAMTYPE_MAXSIZE)
1815 argSize = sizeof(TADDR);
1819 MethodTable* pMethodTable = nullptr;
1821 if (!thValueType.IsTypeDesc())
1822 pMethodTable = thValueType.AsMethodTable();
1825 _ASSERTE(thValueType.IsNativeValueType());
1826 pMethodTable = thValueType.AsNativeValueType();
1828 _ASSERTE(pMethodTable != nullptr);
1829 flags = MethodTable::GetRiscV64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable);
1830 if (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)
1832 cFPRegs = (flags & STRUCT_FLOAT_FIELD_ONLY_TWO) ? 2 : 1;
1843 const bool isValueType = (argType == ELEMENT_TYPE_VALUETYPE);
1844 const bool isFloatHfa = thValueType.IsFloatHfa();
1845 const int cbArg = StackElemSize(argSize, isValueType, isFloatHfa);
1847 if (cFPRegs > 0 && !this->IsVarArg())
1849 if (flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND))
1851 assert(cFPRegs == 1);
1852 assert((STRUCT_FLOAT_FIELD_FIRST == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)) || (STRUCT_FLOAT_FIELD_SECOND == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)));
1854 if ((1 + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) && (m_idxGenReg + 1 <= NUM_ARGUMENT_REGISTERS))
1856 m_argLocDescForStructInRegs.Init();
1857 m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
1858 m_argLocDescForStructInRegs.m_cFloatReg = 1;
1859 int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
1862 m_argLocDescForStructInRegs.m_structFields = flags;
1864 m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg;
1865 m_argLocDescForStructInRegs.m_cGenReg = 1;
1868 m_hasArgLocDescForStructInRegs = true;
1873 else if (cFPRegs + m_idxFPReg <= NUM_ARGUMENT_REGISTERS)
1875 int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
1876 if (flags == STRUCT_FLOAT_FIELD_ONLY_TWO) // struct with two float-fields.
1878 m_argLocDescForStructInRegs.Init();
1879 m_hasArgLocDescForStructInRegs = true;
1880 m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
1881 assert(cFPRegs == 2);
1882 m_argLocDescForStructInRegs.m_cFloatReg = 2;
1883 assert(argSize == 8);
1884 m_argLocDescForStructInRegs.m_structFields = STRUCT_FLOAT_FIELD_ONLY_TWO;
1886 m_idxFPReg += cFPRegs;
1892 const int regSlots = ALIGN_UP(cbArg, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE;
1893 if (m_idxGenReg + regSlots <= NUM_ARGUMENT_REGISTERS)
1895 int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1896 m_idxGenReg += regSlots;
1899 else if (m_idxGenReg < NUM_ARGUMENT_REGISTERS)
1901 int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1902 m_ofsStack += (m_idxGenReg + regSlots - NUM_ARGUMENT_REGISTERS)*8;
1903 assert(m_ofsStack == 8);
1904 m_idxGenReg = NUM_ARGUMENT_REGISTERS;
1909 int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
1910 m_ofsStack += ALIGN_UP(cbArg, TARGET_POINTER_SIZE);
1914 PORTABILITY_ASSERT("ArgIteratorTemplate::GetNextOffset");
1915 return TransitionBlock::InvalidOffset;
1919 template<class ARGITERATOR_BASE>
1920 void ArgIteratorTemplate<ARGITERATOR_BASE>::ComputeReturnFlags()
1925 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
1926 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
1927 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
1932 TypeHandle thValueType;
1933 CorElementType type = this->GetReturnType(&thValueType);
1935 DWORD flags = RETURN_FLAGS_COMPUTED;
1938 case ELEMENT_TYPE_TYPEDBYREF:
1939 #ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
1940 if (sizeof(TypedByRef) > ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
1941 flags |= RETURN_HAS_RET_BUFFER;
1943 flags |= RETURN_HAS_RET_BUFFER;
1947 case ELEMENT_TYPE_R4:
1948 #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1949 flags |= STRUCT_FLOAT_FIELD_ONLY_ONE << RETURN_FP_SIZE_SHIFT;
1952 flags |= sizeof(float) << RETURN_FP_SIZE_SHIFT;
1957 case ELEMENT_TYPE_R8:
1958 #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1959 flags |= (STRUCT_FLOAT_FIELD_ONLY_ONE | STRUCT_FIRST_FIELD_SIZE_IS8) << RETURN_FP_SIZE_SHIFT;
1962 flags |= sizeof(double) << RETURN_FP_SIZE_SHIFT;
1967 case ELEMENT_TYPE_VALUETYPE:
1968 #ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
1970 _ASSERTE(!thValueType.IsNull());
1972 #if defined(UNIX_AMD64_ABI)
1973 MethodTable *pMT = thValueType.AsMethodTable();
1974 if (pMT->IsRegPassedStruct())
1976 EEClass* eeClass = pMT->GetClass();
1978 if (eeClass->GetNumberEightBytes() == 1)
1980 // Structs occupying just one eightbyte are treated as int / double
1981 if (eeClass->GetEightByteClassification(0) == SystemVClassificationTypeSSE)
1983 flags |= sizeof(double) << RETURN_FP_SIZE_SHIFT;
1988 // Size of the struct is 16 bytes
1989 flags |= (16 << RETURN_FP_SIZE_SHIFT);
1990 // The lowest two bits of the size encode the order of the int and SSE fields
1991 if (eeClass->GetEightByteClassification(0) == SystemVClassificationTypeSSE)
1993 flags |= (1 << RETURN_FP_SIZE_SHIFT);
1996 if (eeClass->GetEightByteClassification(1) == SystemVClassificationTypeSSE)
1998 flags |= (2 << RETURN_FP_SIZE_SHIFT);
2004 #else // UNIX_AMD64_ABI
2007 if (thValueType.IsHFA() && !this->IsVarArg())
2009 CorInfoHFAElemType hfaType = thValueType.GetHFAType();
2011 int hfaFieldSize = ArgLocDesc::getHFAFieldSize(hfaType);
2012 flags |= ((4 * hfaFieldSize) << RETURN_FP_SIZE_SHIFT);
2017 size_t size = thValueType.GetSize();
2019 #if defined(TARGET_X86) || defined(TARGET_AMD64)
2020 // Return value types of size which are not powers of 2 using a RetBuffArg
2021 if ((size & (size-1)) != 0)
2023 flags |= RETURN_HAS_RET_BUFFER;
2026 #endif // defined(TARGET_X86) || defined(TARGET_AMD64)
2028 #if defined(TARGET_LOONGARCH64)
2029 if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
2031 assert(!thValueType.IsTypeDesc());
2033 MethodTable *pMethodTable = thValueType.AsMethodTable();
2034 flags = (MethodTable::GetLoongArch64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable) & 0xff) << RETURN_FP_SIZE_SHIFT;
2037 #elif defined(TARGET_RISCV64)
2038 if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
2040 assert(!thValueType.IsTypeDesc());
2042 MethodTable *pMethodTable = thValueType.AsMethodTable();
2043 flags = (MethodTable::GetRiscV64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable) & 0xff) << RETURN_FP_SIZE_SHIFT;
2047 if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
2050 #endif // UNIX_AMD64_ABI
2052 #endif // ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
2054 // Value types are returned using return buffer by default
2055 flags |= RETURN_HAS_RET_BUFFER;
2065 template<class ARGITERATOR_BASE>
2066 void ArgIteratorTemplate<ARGITERATOR_BASE>::ForceSigWalk()
2071 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
2072 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
2073 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
2078 // This can be only used before the actual argument iteration started
2079 _ASSERTE((m_dwFlags & ITERATION_STARTED) == 0);
2083 // x86 is special as always
2086 int numRegistersUsed = 0;
2087 int nSizeOfArgStack = 0;
2089 if (this->HasThis())
2092 if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
2095 if (this->IsVarArg())
2097 nSizeOfArgStack += sizeof(void *);
2098 numRegistersUsed = NUM_ARGUMENT_REGISTERS; // Nothing else gets passed in registers for varargs
2101 #ifdef FEATURE_INTERPRETER
2102 BYTE callconv = CallConv();
2105 case IMAGE_CEE_CS_CALLCONV_C:
2106 case IMAGE_CEE_CS_CALLCONV_STDCALL:
2107 numRegistersUsed = NUM_ARGUMENT_REGISTERS;
2108 nSizeOfArgStack = TransitionBlock::GetOffsetOfArgs() + numRegistersUsed * sizeof(void *);
2111 case IMAGE_CEE_CS_CALLCONV_THISCALL:
2112 case IMAGE_CEE_CS_CALLCONV_FASTCALL:
2113 _ASSERTE_MSG(false, "Unsupported calling convention.");
2117 #endif // FEATURE_INTERPRETER
2119 DWORD nArgs = this->NumFixedArgs();
2120 for (DWORD i = 0; i < nArgs; i++)
2122 TypeHandle thValueType;
2123 CorElementType type = this->GetNextArgumentType(i, &thValueType);
2125 if (!IsArgumentInRegister(&numRegistersUsed, type, thValueType))
2127 int structSize = MetaSig::GetElemSize(type, thValueType);
2129 nSizeOfArgStack += StackElemSize(structSize);
2131 #ifndef DACCESS_COMPILE
2132 if (nSizeOfArgStack > MAX_ARG_SIZE)
2135 // We should not ever throw exception in the "FORBIDGC_LOADER_USE_ENABLED" mode.
2136 // The contract violation is required to workaround bug in the static contract analyzer.
2137 _ASSERTE(!FORBIDGC_LOADER_USE_ENABLED());
2138 CONTRACT_VIOLATION(ThrowsViolation);
2140 COMPlusThrow(kNotSupportedException);
2146 if (this->HasParamType())
2148 DWORD paramTypeFlags = 0;
2149 if (numRegistersUsed < NUM_ARGUMENT_REGISTERS)
2152 paramTypeFlags = (numRegistersUsed == 1) ?
2153 PARAM_TYPE_REGISTER_ECX : PARAM_TYPE_REGISTER_EDX;
2157 nSizeOfArgStack += sizeof(void *);
2158 paramTypeFlags = PARAM_TYPE_REGISTER_STACK;
2160 m_dwFlags |= paramTypeFlags;
2165 int maxOffset = TransitionBlock::GetOffsetOfArgs();
2168 while (TransitionBlock::InvalidOffset != (ofs = GetNextOffset()))
2173 #ifdef UNIX_AMD64_ABI
2174 if (m_fArgInRegisters)
2176 // Arguments passed in registers don't consume any stack
2180 stackElemSize = StackElemSize(GetArgSize());
2181 #else // UNIX_AMD64_ABI
2182 // All stack arguments take just one stack slot on AMD64 because of arguments bigger
2183 // than a stack slot are passed by reference.
2184 stackElemSize = TARGET_POINTER_SIZE;
2185 #endif // UNIX_AMD64_ABI
2186 #else // TARGET_AMD64
2188 TypeHandle thValueType;
2189 const CorElementType argType = GetArgType(&thValueType);
2190 const bool isValueType = (argType == ELEMENT_TYPE_VALUETYPE);
2191 stackElemSize = StackElemSize(GetArgSize(), isValueType, thValueType.IsFloatHfa());
2192 #if defined(ENREGISTERED_PARAMTYPE_MAXSIZE)
2193 if (IsArgPassedByRef())
2194 stackElemSize = TARGET_POINTER_SIZE;
2196 #endif // TARGET_AMD64
2198 int endOfs = ofs + stackElemSize;
2199 if (endOfs > maxOffset)
2201 #if !defined(DACCESS_COMPILE)
2202 if (endOfs > MAX_ARG_SIZE)
2205 // We should not ever throw exception in the "FORBIDGC_LOADER_USE_ENABLED" mode.
2206 // The contract violation is required to workaround bug in the static contract analyzer.
2207 _ASSERTE(!FORBIDGC_LOADER_USE_ENABLED());
2208 CONTRACT_VIOLATION(ThrowsViolation);
2210 COMPlusThrow(kNotSupportedException);
2216 // Clear the iterator started flag
2217 m_dwFlags &= ~ITERATION_STARTED;
2219 int nSizeOfArgStack = maxOffset - TransitionBlock::GetOffsetOfArgs();
2221 #if defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI)
2222 nSizeOfArgStack = (nSizeOfArgStack > (int)sizeof(ArgumentRegisters)) ?
2223 (nSizeOfArgStack - sizeof(ArgumentRegisters)) : 0;
2226 #endif // TARGET_X86
2228 // arg stack size is rounded to the pointer size on all platforms.
2229 nSizeOfArgStack = (int)ALIGN_UP(nSizeOfArgStack, TARGET_POINTER_SIZE);
2232 m_nSizeOfArgStack = nSizeOfArgStack;
2233 m_dwFlags |= SIZE_OF_ARG_STACK_COMPUTED;
2238 class ArgIteratorBase
2243 FORCEINLINE CorElementType GetReturnType(TypeHandle * pthValueType)
2245 WRAPPER_NO_CONTRACT;
2246 #ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
2247 return m_pSig->GetReturnTypeNormalized(pthValueType);
2249 return m_pSig->GetReturnTypeNormalized();
2253 FORCEINLINE CorElementType GetNextArgumentType(DWORD iArg, TypeHandle * pthValueType)
2255 WRAPPER_NO_CONTRACT;
2256 _ASSERTE(iArg == m_pSig->GetArgNum());
2257 CorElementType et = m_pSig->PeekArgNormalized(pthValueType);
2262 FORCEINLINE void Reset()
2264 WRAPPER_NO_CONTRACT;
2268 FORCEINLINE BOOL IsRegPassedStruct(MethodTable* pMT)
2270 return pMT->IsRegPassedStruct();
2276 LIMITED_METHOD_CONTRACT;
2277 return m_pSig->HasThis();
2282 LIMITED_METHOD_CONTRACT;
2283 return m_pSig->GetCallingConventionInfo() & CORINFO_CALLCONV_PARAMTYPE;
2288 LIMITED_METHOD_CONTRACT;
2289 return m_pSig->IsVarArg() || m_pSig->IsTreatAsVarArg();
2292 DWORD NumFixedArgs()
2294 LIMITED_METHOD_CONTRACT;
2295 return m_pSig->NumFixedArgs();
2298 #ifdef FEATURE_INTERPRETER
2301 return m_pSig->GetCallingConvention();
2303 #endif // FEATURE_INTERPRETER
2306 // The following is used by the profiler to dig into the iterator for
2307 // discovering if the method has a This pointer or a return buffer.
2308 // Do not use this to re-initialize the signature, use the exposed Init()
2309 // method in this class.
2311 MetaSig *GetSig(void)
2317 class ArgIterator : public ArgIteratorTemplate<ArgIteratorBase>
2320 ArgIterator(MetaSig * pSig)
2325 // This API returns true if we are returning a structure in registers instead of using a byref return buffer
2326 BOOL HasNonStandardByvalReturn()
2328 WRAPPER_NO_CONTRACT;
2330 #ifdef ENREGISTERED_RETURNTYPE_MAXSIZE
2331 CorElementType type = m_pSig->GetReturnTypeNormalized();
2332 return (type == ELEMENT_TYPE_VALUETYPE || type == ELEMENT_TYPE_TYPEDBYREF) && !HasRetBuffArg();
2338 BOOL HasValueTypeReturn()
2340 WRAPPER_NO_CONTRACT;
2342 TypeHandle thValueType;
2343 CorElementType type = m_pSig->GetReturnTypeNormalized(&thValueType);
2344 // Enums are normalized to their underlying type when passing to and from functions.
2345 // This occurs in both managed and native calling conventions.
2346 return type == ELEMENT_TYPE_VALUETYPE && !thValueType.IsEnum();
2350 // Conventience helper
2351 inline BOOL HasRetBuffArg(MetaSig * pSig)
2353 WRAPPER_NO_CONTRACT;
2354 ArgIterator argit(pSig);
2355 return argit.HasRetBuffArg();
2359 // For UNIX_X86_ABI and unmanaged function, we always need RetBuf if the return type is VALUETYPE
2360 inline BOOL HasRetBuffArgUnmanagedFixup(MetaSig * pSig)
2362 WRAPPER_NO_CONTRACT;
2363 // We cannot just pSig->GetReturnType() here since it will return ELEMENT_TYPE_VALUETYPE for enums
2364 CorElementType type = pSig->GetRetTypeHandleThrowing().GetVerifierCorElementType();
2365 return type == ELEMENT_TYPE_VALUETYPE;
2369 inline BOOL IsRetBuffPassedAsFirstArg()
2371 WRAPPER_NO_CONTRACT;
2372 #ifndef TARGET_ARM64
2379 #endif // __CALLING_CONVENTION_INCLUDED