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 flags = MethodTable::GetLoongArch64PassStructInRegisterFlags(thValueType);
1696 if (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)
1698 cFPRegs = (flags & STRUCT_FLOAT_FIELD_ONLY_TWO) ? 2 : 1;
1709 const bool isValueType = (argType == ELEMENT_TYPE_VALUETYPE);
1710 const bool isFloatHfa = thValueType.IsFloatHfa();
1711 const int cbArg = StackElemSize(argSize, isValueType, isFloatHfa);
1713 if (cFPRegs > 0 && !this->IsVarArg())
1715 if (flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND))
1717 assert(cFPRegs == 1);
1718 assert((STRUCT_FLOAT_FIELD_FIRST == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)) || (STRUCT_FLOAT_FIELD_SECOND == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)));
1720 if ((1 + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) && (m_idxGenReg + 1 <= NUM_ARGUMENT_REGISTERS))
1722 m_argLocDescForStructInRegs.Init();
1723 m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
1724 m_argLocDescForStructInRegs.m_cFloatReg = 1;
1725 int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
1728 m_argLocDescForStructInRegs.m_structFields = flags;
1730 m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg;
1731 m_argLocDescForStructInRegs.m_cGenReg = 1;
1734 m_hasArgLocDescForStructInRegs = true;
1739 else if (cFPRegs + m_idxFPReg <= NUM_ARGUMENT_REGISTERS)
1741 int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
1742 if (flags == STRUCT_FLOAT_FIELD_ONLY_TWO) // struct with two float-fields.
1744 m_argLocDescForStructInRegs.Init();
1745 m_hasArgLocDescForStructInRegs = true;
1746 m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
1747 assert(cFPRegs == 2);
1748 m_argLocDescForStructInRegs.m_cFloatReg = 2;
1749 assert(argSize == 8);
1750 m_argLocDescForStructInRegs.m_structFields = STRUCT_FLOAT_FIELD_ONLY_TWO;
1752 m_idxFPReg += cFPRegs;
1758 const int regSlots = ALIGN_UP(cbArg, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE;
1759 if (m_idxGenReg + regSlots <= NUM_ARGUMENT_REGISTERS)
1761 int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1762 m_idxGenReg += regSlots;
1765 else if (m_idxGenReg < NUM_ARGUMENT_REGISTERS)
1767 int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1768 m_ofsStack += (m_idxGenReg + regSlots - NUM_ARGUMENT_REGISTERS)*8;
1769 assert(m_ofsStack == 8);
1770 m_idxGenReg = NUM_ARGUMENT_REGISTERS;
1775 int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
1776 m_ofsStack += ALIGN_UP(cbArg, TARGET_POINTER_SIZE);
1779 #elif defined(TARGET_RISCV64)
1787 case ELEMENT_TYPE_R4:
1788 // 32-bit floating point argument.
1792 case ELEMENT_TYPE_R8:
1793 // 64-bit floating point argument.
1797 case ELEMENT_TYPE_VALUETYPE:
1799 // Handle struct which containing floats or doubles that can be passed
1800 // in FP registers if possible.
1802 // Composite greater than 16bytes should be passed by reference
1803 if (argSize > ENREGISTERED_PARAMTYPE_MAXSIZE)
1805 argSize = sizeof(TADDR);
1809 flags = MethodTable::GetRiscV64PassStructInRegisterFlags(thValueType);
1810 if (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)
1812 cFPRegs = (flags & STRUCT_FLOAT_FIELD_ONLY_TWO) ? 2 : 1;
1823 const bool isValueType = (argType == ELEMENT_TYPE_VALUETYPE);
1824 const bool isFloatHfa = thValueType.IsFloatHfa();
1825 const int cbArg = StackElemSize(argSize, isValueType, isFloatHfa);
1827 if (cFPRegs > 0 && !this->IsVarArg())
1829 if (flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND))
1831 assert(cFPRegs == 1);
1832 assert((STRUCT_FLOAT_FIELD_FIRST == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)) || (STRUCT_FLOAT_FIELD_SECOND == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)));
1834 if ((1 + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) && (m_idxGenReg + 1 <= NUM_ARGUMENT_REGISTERS))
1836 m_argLocDescForStructInRegs.Init();
1837 m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
1838 m_argLocDescForStructInRegs.m_cFloatReg = 1;
1839 int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
1842 m_argLocDescForStructInRegs.m_structFields = flags;
1844 m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg;
1845 m_argLocDescForStructInRegs.m_cGenReg = 1;
1848 m_hasArgLocDescForStructInRegs = true;
1853 else if (cFPRegs + m_idxFPReg <= NUM_ARGUMENT_REGISTERS)
1855 int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
1856 if (flags == STRUCT_FLOAT_FIELD_ONLY_TWO) // struct with two float-fields.
1858 m_argLocDescForStructInRegs.Init();
1859 m_hasArgLocDescForStructInRegs = true;
1860 m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
1861 assert(cFPRegs == 2);
1862 m_argLocDescForStructInRegs.m_cFloatReg = 2;
1863 assert(argSize == 8);
1864 m_argLocDescForStructInRegs.m_structFields = STRUCT_FLOAT_FIELD_ONLY_TWO;
1866 m_idxFPReg += cFPRegs;
1872 const int regSlots = ALIGN_UP(cbArg, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE;
1873 if (m_idxGenReg + regSlots <= NUM_ARGUMENT_REGISTERS)
1875 int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1876 m_idxGenReg += regSlots;
1879 else if (m_idxGenReg < NUM_ARGUMENT_REGISTERS)
1881 int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1882 m_ofsStack += (m_idxGenReg + regSlots - NUM_ARGUMENT_REGISTERS)*8;
1883 assert(m_ofsStack == 8);
1884 m_idxGenReg = NUM_ARGUMENT_REGISTERS;
1889 int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
1890 m_ofsStack += ALIGN_UP(cbArg, TARGET_POINTER_SIZE);
1894 PORTABILITY_ASSERT("ArgIteratorTemplate::GetNextOffset");
1895 return TransitionBlock::InvalidOffset;
1899 template<class ARGITERATOR_BASE>
1900 void ArgIteratorTemplate<ARGITERATOR_BASE>::ComputeReturnFlags()
1905 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
1906 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
1907 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
1912 TypeHandle thValueType;
1913 CorElementType type = this->GetReturnType(&thValueType);
1915 DWORD flags = RETURN_FLAGS_COMPUTED;
1918 case ELEMENT_TYPE_TYPEDBYREF:
1919 #ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
1920 if (sizeof(TypedByRef) > ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
1921 flags |= RETURN_HAS_RET_BUFFER;
1923 flags |= RETURN_HAS_RET_BUFFER;
1927 case ELEMENT_TYPE_R4:
1928 #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1929 flags |= STRUCT_FLOAT_FIELD_ONLY_ONE << RETURN_FP_SIZE_SHIFT;
1932 flags |= sizeof(float) << RETURN_FP_SIZE_SHIFT;
1937 case ELEMENT_TYPE_R8:
1938 #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1939 flags |= (STRUCT_FLOAT_FIELD_ONLY_ONE | STRUCT_FIRST_FIELD_SIZE_IS8) << RETURN_FP_SIZE_SHIFT;
1942 flags |= sizeof(double) << RETURN_FP_SIZE_SHIFT;
1947 case ELEMENT_TYPE_VALUETYPE:
1948 #ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
1950 _ASSERTE(!thValueType.IsNull());
1952 #if defined(UNIX_AMD64_ABI)
1953 MethodTable *pMT = thValueType.AsMethodTable();
1954 if (pMT->IsRegPassedStruct())
1956 EEClass* eeClass = pMT->GetClass();
1958 if (eeClass->GetNumberEightBytes() == 1)
1960 // Structs occupying just one eightbyte are treated as int / double
1961 if (eeClass->GetEightByteClassification(0) == SystemVClassificationTypeSSE)
1963 flags |= sizeof(double) << RETURN_FP_SIZE_SHIFT;
1968 // Size of the struct is 16 bytes
1969 flags |= (16 << RETURN_FP_SIZE_SHIFT);
1970 // The lowest two bits of the size encode the order of the int and SSE fields
1971 if (eeClass->GetEightByteClassification(0) == SystemVClassificationTypeSSE)
1973 flags |= (1 << RETURN_FP_SIZE_SHIFT);
1976 if (eeClass->GetEightByteClassification(1) == SystemVClassificationTypeSSE)
1978 flags |= (2 << RETURN_FP_SIZE_SHIFT);
1984 #else // UNIX_AMD64_ABI
1987 if (thValueType.IsHFA() && !this->IsVarArg())
1989 CorInfoHFAElemType hfaType = thValueType.GetHFAType();
1991 int hfaFieldSize = ArgLocDesc::getHFAFieldSize(hfaType);
1992 flags |= ((4 * hfaFieldSize) << RETURN_FP_SIZE_SHIFT);
1997 size_t size = thValueType.GetSize();
1999 #if defined(TARGET_X86) || defined(TARGET_AMD64)
2000 // Return value types of size which are not powers of 2 using a RetBuffArg
2001 if ((size & (size-1)) != 0)
2003 flags |= RETURN_HAS_RET_BUFFER;
2006 #endif // defined(TARGET_X86) || defined(TARGET_AMD64)
2008 #if defined(TARGET_LOONGARCH64)
2009 if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
2011 assert(!thValueType.IsTypeDesc());
2012 flags = (MethodTable::GetLoongArch64PassStructInRegisterFlags(thValueType) & 0xff) << RETURN_FP_SIZE_SHIFT;
2015 #elif defined(TARGET_RISCV64)
2016 if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
2018 assert(!thValueType.IsTypeDesc());
2019 flags = (MethodTable::GetRiscV64PassStructInRegisterFlags(thValueType) & 0xff) << RETURN_FP_SIZE_SHIFT;
2023 if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
2026 #endif // UNIX_AMD64_ABI
2028 #endif // ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
2030 // Value types are returned using return buffer by default
2031 flags |= RETURN_HAS_RET_BUFFER;
2041 template<class ARGITERATOR_BASE>
2042 void ArgIteratorTemplate<ARGITERATOR_BASE>::ForceSigWalk()
2047 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
2048 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
2049 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
2054 // This can be only used before the actual argument iteration started
2055 _ASSERTE((m_dwFlags & ITERATION_STARTED) == 0);
2059 // x86 is special as always
2062 int numRegistersUsed = 0;
2063 int nSizeOfArgStack = 0;
2065 if (this->HasThis())
2068 if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
2071 if (this->IsVarArg())
2073 nSizeOfArgStack += sizeof(void *);
2074 numRegistersUsed = NUM_ARGUMENT_REGISTERS; // Nothing else gets passed in registers for varargs
2077 #ifdef FEATURE_INTERPRETER
2078 BYTE callconv = CallConv();
2081 case IMAGE_CEE_CS_CALLCONV_C:
2082 case IMAGE_CEE_CS_CALLCONV_STDCALL:
2083 numRegistersUsed = NUM_ARGUMENT_REGISTERS;
2084 nSizeOfArgStack = TransitionBlock::GetOffsetOfArgs() + numRegistersUsed * sizeof(void *);
2087 case IMAGE_CEE_CS_CALLCONV_THISCALL:
2088 case IMAGE_CEE_CS_CALLCONV_FASTCALL:
2089 _ASSERTE_MSG(false, "Unsupported calling convention.");
2093 #endif // FEATURE_INTERPRETER
2095 DWORD nArgs = this->NumFixedArgs();
2096 for (DWORD i = 0; i < nArgs; i++)
2098 TypeHandle thValueType;
2099 CorElementType type = this->GetNextArgumentType(i, &thValueType);
2101 if (!IsArgumentInRegister(&numRegistersUsed, type, thValueType))
2103 int structSize = MetaSig::GetElemSize(type, thValueType);
2105 nSizeOfArgStack += StackElemSize(structSize);
2107 #ifndef DACCESS_COMPILE
2108 if (nSizeOfArgStack > MAX_ARG_SIZE)
2111 // We should not ever throw exception in the "FORBIDGC_LOADER_USE_ENABLED" mode.
2112 // The contract violation is required to workaround bug in the static contract analyzer.
2113 _ASSERTE(!FORBIDGC_LOADER_USE_ENABLED());
2114 CONTRACT_VIOLATION(ThrowsViolation);
2116 COMPlusThrow(kNotSupportedException);
2122 if (this->HasParamType())
2124 DWORD paramTypeFlags = 0;
2125 if (numRegistersUsed < NUM_ARGUMENT_REGISTERS)
2128 paramTypeFlags = (numRegistersUsed == 1) ?
2129 PARAM_TYPE_REGISTER_ECX : PARAM_TYPE_REGISTER_EDX;
2133 nSizeOfArgStack += sizeof(void *);
2134 paramTypeFlags = PARAM_TYPE_REGISTER_STACK;
2136 m_dwFlags |= paramTypeFlags;
2141 int maxOffset = TransitionBlock::GetOffsetOfArgs();
2144 while (TransitionBlock::InvalidOffset != (ofs = GetNextOffset()))
2149 #ifdef UNIX_AMD64_ABI
2150 if (m_fArgInRegisters)
2152 // Arguments passed in registers don't consume any stack
2156 stackElemSize = StackElemSize(GetArgSize());
2157 #else // UNIX_AMD64_ABI
2158 // All stack arguments take just one stack slot on AMD64 because of arguments bigger
2159 // than a stack slot are passed by reference.
2160 stackElemSize = TARGET_POINTER_SIZE;
2161 #endif // UNIX_AMD64_ABI
2162 #else // TARGET_AMD64
2164 TypeHandle thValueType;
2165 const CorElementType argType = GetArgType(&thValueType);
2166 const bool isValueType = (argType == ELEMENT_TYPE_VALUETYPE);
2167 stackElemSize = StackElemSize(GetArgSize(), isValueType, thValueType.IsFloatHfa());
2168 #if defined(ENREGISTERED_PARAMTYPE_MAXSIZE)
2169 if (IsArgPassedByRef())
2170 stackElemSize = TARGET_POINTER_SIZE;
2172 #endif // TARGET_AMD64
2174 int endOfs = ofs + stackElemSize;
2175 if (endOfs > maxOffset)
2177 #if !defined(DACCESS_COMPILE)
2178 if (endOfs > MAX_ARG_SIZE)
2181 // We should not ever throw exception in the "FORBIDGC_LOADER_USE_ENABLED" mode.
2182 // The contract violation is required to workaround bug in the static contract analyzer.
2183 _ASSERTE(!FORBIDGC_LOADER_USE_ENABLED());
2184 CONTRACT_VIOLATION(ThrowsViolation);
2186 COMPlusThrow(kNotSupportedException);
2192 // Clear the iterator started flag
2193 m_dwFlags &= ~ITERATION_STARTED;
2195 int nSizeOfArgStack = maxOffset - TransitionBlock::GetOffsetOfArgs();
2197 #if defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI)
2198 nSizeOfArgStack = (nSizeOfArgStack > (int)sizeof(ArgumentRegisters)) ?
2199 (nSizeOfArgStack - sizeof(ArgumentRegisters)) : 0;
2202 #endif // TARGET_X86
2204 // arg stack size is rounded to the pointer size on all platforms.
2205 nSizeOfArgStack = (int)ALIGN_UP(nSizeOfArgStack, TARGET_POINTER_SIZE);
2208 m_nSizeOfArgStack = nSizeOfArgStack;
2209 m_dwFlags |= SIZE_OF_ARG_STACK_COMPUTED;
2214 class ArgIteratorBase
2219 FORCEINLINE CorElementType GetReturnType(TypeHandle * pthValueType)
2221 WRAPPER_NO_CONTRACT;
2222 #ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
2223 return m_pSig->GetReturnTypeNormalized(pthValueType);
2225 return m_pSig->GetReturnTypeNormalized();
2229 FORCEINLINE CorElementType GetNextArgumentType(DWORD iArg, TypeHandle * pthValueType)
2231 WRAPPER_NO_CONTRACT;
2232 _ASSERTE(iArg == m_pSig->GetArgNum());
2233 CorElementType et = m_pSig->PeekArgNormalized(pthValueType);
2238 FORCEINLINE void Reset()
2240 WRAPPER_NO_CONTRACT;
2244 FORCEINLINE BOOL IsRegPassedStruct(MethodTable* pMT)
2246 return pMT->IsRegPassedStruct();
2252 LIMITED_METHOD_CONTRACT;
2253 return m_pSig->HasThis();
2258 LIMITED_METHOD_CONTRACT;
2259 return m_pSig->GetCallingConventionInfo() & CORINFO_CALLCONV_PARAMTYPE;
2264 LIMITED_METHOD_CONTRACT;
2265 return m_pSig->IsVarArg() || m_pSig->IsTreatAsVarArg();
2268 DWORD NumFixedArgs()
2270 LIMITED_METHOD_CONTRACT;
2271 return m_pSig->NumFixedArgs();
2274 #ifdef FEATURE_INTERPRETER
2277 return m_pSig->GetCallingConvention();
2279 #endif // FEATURE_INTERPRETER
2282 // The following is used by the profiler to dig into the iterator for
2283 // discovering if the method has a This pointer or a return buffer.
2284 // Do not use this to re-initialize the signature, use the exposed Init()
2285 // method in this class.
2287 MetaSig *GetSig(void)
2293 class ArgIterator : public ArgIteratorTemplate<ArgIteratorBase>
2296 ArgIterator(MetaSig * pSig)
2301 // This API returns true if we are returning a structure in registers instead of using a byref return buffer
2302 BOOL HasNonStandardByvalReturn()
2304 WRAPPER_NO_CONTRACT;
2306 #ifdef ENREGISTERED_RETURNTYPE_MAXSIZE
2307 CorElementType type = m_pSig->GetReturnTypeNormalized();
2308 return (type == ELEMENT_TYPE_VALUETYPE || type == ELEMENT_TYPE_TYPEDBYREF) && !HasRetBuffArg();
2314 BOOL HasValueTypeReturn()
2316 WRAPPER_NO_CONTRACT;
2318 TypeHandle thValueType;
2319 CorElementType type = m_pSig->GetReturnTypeNormalized(&thValueType);
2320 // Enums are normalized to their underlying type when passing to and from functions.
2321 // This occurs in both managed and native calling conventions.
2322 return type == ELEMENT_TYPE_VALUETYPE && !thValueType.IsEnum();
2326 // Conventience helper
2327 inline BOOL HasRetBuffArg(MetaSig * pSig)
2329 WRAPPER_NO_CONTRACT;
2330 ArgIterator argit(pSig);
2331 return argit.HasRetBuffArg();
2335 // For UNIX_X86_ABI and unmanaged function, we always need RetBuf if the return type is VALUETYPE
2336 inline BOOL HasRetBuffArgUnmanagedFixup(MetaSig * pSig)
2338 WRAPPER_NO_CONTRACT;
2339 // We cannot just pSig->GetReturnType() here since it will return ELEMENT_TYPE_VALUETYPE for enums
2340 CorElementType type = pSig->GetRetTypeHandleThrowing().GetVerifierCorElementType();
2341 return type == ELEMENT_TYPE_VALUETYPE;
2345 inline BOOL IsRetBuffPassedAsFirstArg()
2347 WRAPPER_NO_CONTRACT;
2348 #ifndef TARGET_ARM64
2355 #endif // __CALLING_CONVENTION_INCLUDED