Sync may31 release/8.0-tizen (#510)
[platform/upstream/dotnet/runtime.git] / src / coreclr / vm / callingconvention.h
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 //
4
5
6 //
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.
10 //
11
12 #ifndef __CALLING_CONVENTION_INCLUDED
13 #define __CALLING_CONVENTION_INCLUDED
14
15 BOOL IsRetBuffPassedAsFirstArg();
16
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.
19 //
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.
25 //
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
31 //
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.
34 struct ArgLocDesc
35 {
36     int     m_idxFloatReg;        // First floating point register used (or -1)
37     int     m_cFloatReg;          // Count of floating point registers used (or 0)
38
39     int     m_idxGenReg;          // First general register used (or -1)
40     int     m_cGenReg;            // Count of general registers used (or 0)
41
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.
46 #endif
47
48 #if defined(UNIX_AMD64_ABI)
49
50     EEClass* m_eeClass;           // For structs passed in register, it points to the EEClass of the struct
51
52 #endif // UNIX_AMD64_ABI
53
54 #ifdef FEATURE_HFA
55     static unsigned getHFAFieldSize(CorInfoHFAElemType  hfaType)
56     {
57         switch (hfaType)
58         {
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;
64         }
65     }
66 #endif
67 #if defined(TARGET_ARM64)
68     unsigned m_hfaFieldSize;      // Size of HFA field in bytes.
69     void setHFAFieldSize(CorInfoHFAElemType  hfaType)
70     {
71         m_hfaFieldSize = getHFAFieldSize(hfaType);
72     }
73 #endif // defined(TARGET_ARM64)
74
75 #if defined(TARGET_ARM)
76     BOOL    m_fRequires64BitAlignment; // True if the argument should always be aligned (in registers or on the stack
77 #endif
78
79     ArgLocDesc()
80     {
81         Init();
82     }
83
84     // Initialize to represent a non-placed argument (no register or stack slots referenced).
85     void Init()
86     {
87         m_idxFloatReg = -1;
88         m_cFloatReg = 0;
89         m_idxGenReg = -1;
90         m_cGenReg = 0;
91         m_byteStackIndex = -1;
92         m_byteStackSize = 0;
93 #if defined(TARGET_ARM)
94         m_fRequires64BitAlignment = FALSE;
95 #endif
96 #if defined(TARGET_ARM64)
97         m_hfaFieldSize = 0;
98 #endif // defined(TARGET_ARM64)
99 #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
100         m_structFields = STRUCT_NO_FLOAT_FIELD;
101 #endif
102 #if defined(UNIX_AMD64_ABI)
103         m_eeClass = NULL;
104 #endif
105     }
106 };
107
108 //
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.
111 //
112 struct TransitionBlock
113 {
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;
121 #endif
122     CalleeSavedRegisters    m_calleeSavedRegisters;
123     TADDR                   m_ReturnAddress;
124 #elif defined(TARGET_ARM)
125     union {
126         CalleeSavedRegisters m_calleeSavedRegisters;
127         // alias saved link register as m_ReturnAddress
128         struct {
129             INT32 r4, r5, r6, r7, r8, r9, r10;
130             INT32 r11;
131             TADDR m_ReturnAddress;
132         };
133     };
134     ArgumentRegisters       m_argumentRegisters;
135 #elif defined(TARGET_ARM64)
136     union {
137         CalleeSavedRegisters m_calleeSavedRegisters;
138         struct {
139             INT64 x29; // frame pointer
140             TADDR m_ReturnAddress;
141             INT64 x19, x20, x21, x22, x23, x24, x25, x26, x27, x28;
142         };
143     };
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)
148     union {
149         CalleeSavedRegisters m_calleeSavedRegisters;
150         struct {
151             INT64 fp; // frame pointer
152             TADDR m_ReturnAddress;
153             INT64 s0;
154             INT64 s1;
155             INT64 s2;
156             INT64 s3;
157             INT64 s4;
158             INT64 s5;
159             INT64 s6;
160             INT64 s7;
161             INT64 s8;
162             INT64 tp;
163         };
164     };
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)
168     union {
169         CalleeSavedRegisters m_calleeSavedRegisters;
170         struct {
171             INT64 s0; // frame pointer
172             TADDR m_ReturnAddress;
173             INT64 s1;
174             INT64 s2;
175             INT64 s3;
176             INT64 s4;
177             INT64 s5;
178             INT64 s6;
179             INT64 s7;
180             INT64 s8;
181             INT64 s9;
182             INT64 s10;
183             INT64 s11;
184             INT64 tp;
185             INT64 gp;
186         };
187     };
188     //TADDR padding; // Keep size of TransitionBlock as multiple of 16-byte. Simplifies code in PROLOG_WITH_TRANSITION_BLOCK
189     ArgumentRegisters       m_argumentRegisters;
190 #else
191     PORTABILITY_ASSERT("TransitionBlock");
192 #endif
193
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.
196
197     static int GetOffsetOfReturnAddress()
198     {
199         LIMITED_METHOD_CONTRACT;
200         return offsetof(TransitionBlock, m_ReturnAddress);
201     }
202
203 #ifdef TARGET_ARM64
204     static int GetOffsetOfRetBuffArgReg()
205     {
206         LIMITED_METHOD_CONTRACT;
207         return offsetof(TransitionBlock, m_x8RetBuffReg);
208     }
209
210     static int GetOffsetOfFirstGCRefMapSlot()
211     {
212         return GetOffsetOfRetBuffArgReg();
213     }
214 #else
215     static int GetOffsetOfFirstGCRefMapSlot()
216     {
217         return GetOffsetOfArgumentRegisters();
218     }
219 #endif
220
221     static BYTE GetOffsetOfArgs()
222     {
223         LIMITED_METHOD_CONTRACT;
224
225         // Offset of the stack args (which are after the TransitionBlock)
226         return sizeof(TransitionBlock);
227     }
228
229     static int GetOffsetOfArgumentRegisters()
230     {
231         LIMITED_METHOD_CONTRACT;
232         int offs;
233 #if defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI)
234         offs = sizeof(TransitionBlock);
235 #else
236         offs = offsetof(TransitionBlock, m_argumentRegisters);
237 #endif
238         return offs;
239     }
240
241     static BOOL IsStackArgumentOffset(int offset)
242     {
243         LIMITED_METHOD_CONTRACT;
244
245 #if defined(UNIX_AMD64_ABI)
246         return offset >= (int)sizeof(TransitionBlock);
247 #else
248         int ofsArgRegs = GetOffsetOfArgumentRegisters();
249
250         return offset >= (int) (ofsArgRegs + ARGUMENTREGISTERS_SIZE);
251 #endif
252     }
253
254     static BOOL IsArgumentRegisterOffset(int offset)
255     {
256         LIMITED_METHOD_CONTRACT;
257
258         int ofsArgRegs = GetOffsetOfArgumentRegisters();
259
260         return offset >= ofsArgRegs && offset < (int) (ofsArgRegs + ARGUMENTREGISTERS_SIZE);
261     }
262
263     static UINT GetArgumentIndexFromOffset(int offset)
264     {
265         LIMITED_METHOD_CONTRACT;
266
267 #if defined(UNIX_AMD64_ABI)
268         _ASSERTE(offset != TransitionBlock::StructInRegsOffset);
269 #endif
270         offset -= GetOffsetOfArgumentRegisters();
271         _ASSERTE((offset % TARGET_POINTER_SIZE) == 0);
272         return offset / TARGET_POINTER_SIZE;
273     }
274
275     static UINT GetStackArgumentIndexFromOffset(int offset)
276     {
277         LIMITED_METHOD_CONTRACT;
278
279         return (offset - TransitionBlock::GetOffsetOfArgs()) / TARGET_POINTER_SIZE;
280     }
281
282     static UINT GetStackArgumentByteIndexFromOffset(int offset)
283     {
284         LIMITED_METHOD_CONTRACT;
285
286         return (offset - TransitionBlock::GetOffsetOfArgs());
287     }
288
289 #ifdef CALLDESCR_FPARGREGS
290     static BOOL IsFloatArgumentRegisterOffset(int offset)
291     {
292         LIMITED_METHOD_CONTRACT;
293 #if defined(UNIX_AMD64_ABI)
294         return (offset != TransitionBlock::StructInRegsOffset) && (offset < 0);
295 #else
296         return offset < 0;
297 #endif
298     }
299
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)
304     {
305         LIMITED_METHOD_CONTRACT;
306     #if defined(UNIX_AMD64_ABI)
307         if (offset == TransitionBlock::StructInRegsOffset)
308         {
309             return argLocDescForStructInRegs->m_cFloatReg > 0;
310         }
311     #endif
312         return offset < 0;
313     }
314
315     static int GetOffsetOfFloatArgumentRegisters()
316     {
317         LIMITED_METHOD_CONTRACT;
318         return -GetNegSpaceSize();
319     }
320 #endif // CALLDESCR_FPARGREGS
321
322     static int GetOffsetOfCalleeSavedRegisters()
323     {
324         LIMITED_METHOD_CONTRACT;
325         return offsetof(TransitionBlock, m_calleeSavedRegisters);
326     }
327
328     static int GetNegSpaceSize()
329     {
330         LIMITED_METHOD_CONTRACT;
331         int negSpaceSize = 0;
332 #ifdef CALLDESCR_FPARGREGS
333         negSpaceSize += sizeof(FloatArgumentRegisters);
334 #endif
335 #ifdef TARGET_ARM
336         negSpaceSize += TARGET_POINTER_SIZE; // padding to make FloatArgumentRegisters address 8-byte aligned
337 #endif
338         return negSpaceSize;
339     }
340
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;
346 #endif
347 };
348
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.
355 //
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
363 {
364 public:
365     //------------------------------------------------------------
366     // Constructor
367     //------------------------------------------------------------
368     ArgIteratorTemplate()
369     {
370         WRAPPER_NO_CONTRACT;
371         m_dwFlags = 0;
372     }
373
374     UINT SizeOfArgStack()
375     {
376         WRAPPER_NO_CONTRACT;
377         if (!(m_dwFlags & SIZE_OF_ARG_STACK_COMPUTED))
378             ForceSigWalk();
379         _ASSERTE((m_dwFlags & SIZE_OF_ARG_STACK_COMPUTED) != 0);
380         _ASSERTE((m_nSizeOfArgStack % TARGET_POINTER_SIZE) == 0);
381         return m_nSizeOfArgStack;
382     }
383
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()
389     {
390         WRAPPER_NO_CONTRACT;
391
392         UINT size = SizeOfArgStack();
393
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;
397 #endif
398         _ASSERTE((size % TARGET_POINTER_SIZE) == 0);
399         return size;
400     }
401
402     //------------------------------------------------------------------------
403
404 #ifdef TARGET_X86
405     UINT CbStackPop()
406     {
407         WRAPPER_NO_CONTRACT;
408
409         if (this->IsVarArg())
410             return 0;
411         else
412             return SizeOfArgStack();
413     }
414 #endif
415
416     // Is there a hidden parameter for the return parameter?
417     //
418     BOOL HasRetBuffArg()
419     {
420         WRAPPER_NO_CONTRACT;
421         if (!(m_dwFlags & RETURN_FLAGS_COMPUTED))
422             ComputeReturnFlags();
423         return (m_dwFlags & RETURN_HAS_RET_BUFFER);
424     }
425
426     UINT GetFPReturnSize()
427     {
428         WRAPPER_NO_CONTRACT;
429         if (!(m_dwFlags & RETURN_FLAGS_COMPUTED))
430             ComputeReturnFlags();
431         return m_dwFlags >> RETURN_FP_SIZE_SHIFT;
432     }
433
434 #ifdef TARGET_X86
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.
441     //
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
445     //                       will update it.
446     //
447     //  typ:                 the signature type
448     //=========================================================================
449     static BOOL IsArgumentInRegister(int * pNumRegistersUsed, CorElementType typ, TypeHandle hnd)
450     {
451         LIMITED_METHOD_CONTRACT;
452         if ( (*pNumRegistersUsed) < NUM_ARGUMENT_REGISTERS)
453         {
454             if (typ == ELEMENT_TYPE_VALUETYPE)
455             {
456                 // The JIT enables passing trivial pointer sized structs in registers.
457                 MethodTable* pMT = hnd.GetMethodTable();
458
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. 
463                 {       
464                     FieldDesc * pFD = pMT->GetApproxFieldDescListRaw(); 
465                     CorElementType type = pFD->GetFieldType();
466
467                     bool exitLoop = false;
468                     switch (type)       
469                     {
470                         case ELEMENT_TYPE_VALUETYPE:
471                         {
472                             //@todo: Is it more apropos to call LookupApproxFieldTypeHandle() here?     
473                             TypeHandle fldHnd = pFD->GetApproxFieldTypeHandleThrowing();        
474                             CONSISTENCY_CHECK(!fldHnd.IsNull());
475                             pMT = fldHnd.GetMethodTable();
476                             FALLTHROUGH;
477                         }       
478                         case ELEMENT_TYPE_PTR:
479                         case ELEMENT_TYPE_I:
480                         case ELEMENT_TYPE_U:
481                         case ELEMENT_TYPE_I4:   
482                         case ELEMENT_TYPE_U4:
483                         {       
484                             typ = type;
485                             break;      
486                         }
487                         default:
488                             exitLoop = true;
489                             break;
490                     }
491
492                     if (exitLoop)
493                     {
494                         break;
495                     }
496                 }
497             }
498             if (gElementTypeInfo[typ].m_enregister)
499             {
500                 (*pNumRegistersUsed)++;
501                 return(TRUE);
502             }
503         }
504
505         return(FALSE);
506     }
507 #endif // TARGET_X86
508
509 #if defined(ENREGISTERED_PARAMTYPE_MAXSIZE)
510
511     // Note that this overload does not handle varargs
512     static BOOL IsArgPassedByRef(TypeHandle th)
513     {
514         LIMITED_METHOD_CONTRACT;
515
516         _ASSERTE(!th.IsNull());
517
518         // This method only works for valuetypes. It includes true value types,
519         // primitives, enums and TypedReference.
520         _ASSERTE(th.IsValueType());
521
522         size_t size = th.GetSize();
523 #ifdef TARGET_AMD64
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);
533 #else
534         PORTABILITY_ASSERT("ArgIteratorTemplate::IsArgPassedByRef");
535         return FALSE;
536 #endif
537     }
538
539 #ifdef TARGET_AMD64
540     // This overload should only be used in AMD64-specific code only.
541     static BOOL IsArgPassedByRef(size_t size)
542     {
543         LIMITED_METHOD_CONTRACT;
544
545 #ifdef UNIX_AMD64_ABI
546         // No arguments are passed by reference on AMD64 on Unix
547         return FALSE;
548 #else
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);
552 #endif
553     }
554 #endif // TARGET_AMD64
555
556     // This overload should be used for varargs only.
557     static BOOL IsVarArgPassedByRef(size_t size)
558     {
559         LIMITED_METHOD_CONTRACT;
560
561 #ifdef TARGET_AMD64
562 #ifdef UNIX_AMD64_ABI
563         PORTABILITY_ASSERT("ArgIteratorTemplate::IsVarArgPassedByRef");
564         return FALSE;
565 #else // UNIX_AMD64_ABI
566         return IsArgPassedByRef(size);
567 #endif // UNIX_AMD64_ABI
568
569 #else
570         return (size > ENREGISTERED_PARAMTYPE_MAXSIZE);
571 #endif
572     }
573
574     BOOL IsArgPassedByRef()
575     {
576         LIMITED_METHOD_CONTRACT;
577
578 #ifdef TARGET_AMD64
579         return IsArgPassedByRef(m_argSize);
580 #elif defined(TARGET_ARM64)
581         if (m_argType == ELEMENT_TYPE_VALUETYPE)
582         {
583             _ASSERTE(!m_argTypeHandle.IsNull());
584             return ((m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE) && (!m_argTypeHandle.IsHFA() || this->IsVarArg()));
585         }
586         return FALSE;
587 #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
588         if (m_argType == ELEMENT_TYPE_VALUETYPE)
589         {
590             _ASSERTE(!m_argTypeHandle.IsNull());
591             return (m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE);
592         }
593         return FALSE;
594 #else
595         PORTABILITY_ASSERT("ArgIteratorTemplate::IsArgPassedByRef");
596         return FALSE;
597 #endif
598     }
599
600 #endif // ENREGISTERED_PARAMTYPE_MAXSIZE
601
602     //------------------------------------------------------------
603     // Return the offsets of the special arguments
604     //------------------------------------------------------------
605
606     static int GetThisOffset();
607
608     int GetRetBuffArgOffset();
609     int GetVASigCookieOffset();
610     int GetParamTypeArgOffset();
611
612     //------------------------------------------------------------
613     // Each time this is called, this returns a byte offset of the next
614     // argument from the TransitionBlock* pointer.
615     //
616     // Returns TransitionBlock::InvalidOffset once you've hit the end
617     // of the list.
618     //------------------------------------------------------------
619     int GetNextOffset();
620
621     CorElementType GetArgType(TypeHandle *pTypeHandle = NULL)
622     {
623         LIMITED_METHOD_CONTRACT;
624         if (pTypeHandle != NULL)
625         {
626             *pTypeHandle = m_argTypeHandle;
627         }
628         return m_argType;
629     }
630
631     int GetArgSize()
632     {
633         LIMITED_METHOD_CONTRACT;
634         return m_argSize;
635     }
636
637     void ForceSigWalk();
638
639 #ifndef TARGET_X86
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); }
646
647 #ifndef CALLDESCR_RETBUFFARGREG
648     void GetRetBuffArgLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetRetBuffArgOffset(), pLoc); }
649 #endif
650
651 #endif // !TARGET_X86
652
653     ArgLocDesc* GetArgLocDescForStructInRegs()
654     {
655 #if defined(UNIX_AMD64_ABI) || defined (TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined (TARGET_RISCV64)
656         return m_hasArgLocDescForStructInRegs ? &m_argLocDescForStructInRegs : NULL;
657 #else
658         return NULL;
659 #endif
660     }
661
662 #ifdef TARGET_X86
663     // Get layout information for the argument that the ArgIterator is currently visiting.
664     void GetArgLoc(int argOffset, ArgLocDesc *pLoc)
665     {
666         LIMITED_METHOD_CONTRACT;
667
668         pLoc->Init();
669
670         if (!TransitionBlock::IsStackArgumentOffset(argOffset))
671         {
672             pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset);
673             _ASSERTE(GetArgSize() <= TARGET_POINTER_SIZE);
674             pLoc->m_cGenReg = 1;
675         }
676         else
677         {
678             pLoc->m_byteStackSize = GetArgSize();
679             pLoc->m_byteStackIndex = TransitionBlock::GetStackArgumentByteIndexFromOffset(argOffset);
680         }
681     }
682 #endif
683
684 #ifdef TARGET_ARM
685     // Get layout information for the argument that the ArgIterator is currently visiting.
686     void GetArgLoc(int argOffset, ArgLocDesc *pLoc)
687     {
688         LIMITED_METHOD_CONTRACT;
689
690         pLoc->Init();
691
692         pLoc->m_fRequires64BitAlignment = m_fRequires64BitAlignment;
693
694         const int byteArgSize = GetArgSize();
695         if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset))
696         {
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;
701             return;
702         }
703
704         if (!TransitionBlock::IsStackArgumentOffset(argOffset))
705         {
706             pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset);
707
708             if (byteArgSize <= (4 - pLoc->m_idxGenReg) * TARGET_POINTER_SIZE)
709             {
710                 pLoc->m_cGenReg = ALIGN_UP(byteArgSize, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE;
711             }
712             else
713             {
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;
717             }
718         }
719         else
720         {
721             pLoc->m_byteStackIndex = TransitionBlock::GetStackArgumentByteIndexFromOffset(argOffset);
722             pLoc->m_byteStackSize = StackElemSize(byteArgSize);
723         }
724     }
725 #endif // TARGET_ARM
726
727 #ifdef TARGET_ARM64
728     // Get layout information for the argument that the ArgIterator is currently visiting.
729     void GetArgLoc(int argOffset, ArgLocDesc *pLoc)
730     {
731         LIMITED_METHOD_CONTRACT;
732
733         pLoc->Init();
734
735
736         if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset))
737         {
738             const int floatRegOfsInBytes = argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters();
739             _ASSERTE((floatRegOfsInBytes % FLOAT_REGISTER_SIZE) == 0);
740             pLoc->m_idxFloatReg = floatRegOfsInBytes / FLOAT_REGISTER_SIZE;
741
742             if (!m_argTypeHandle.IsNull() && m_argTypeHandle.IsHFA())
743             {
744                 CorInfoHFAElemType type = m_argTypeHandle.GetHFAType();
745                 pLoc->setHFAFieldSize(type);
746                 pLoc->m_cFloatReg = GetArgSize() / pLoc->m_hfaFieldSize;
747
748             }
749             else
750             {
751                 pLoc->m_cFloatReg = 1;
752             }
753             return;
754         }
755
756         unsigned byteArgSize = GetArgSize();
757
758         // On ARM64 some composites are implicitly passed by reference.
759         if (IsArgPassedByRef())
760         {
761             byteArgSize = TARGET_POINTER_SIZE;
762         }
763
764
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());
768
769         if (!TransitionBlock::IsStackArgumentOffset(argOffset))
770         {
771             pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset);
772             pLoc->m_cGenReg = ALIGN_UP(byteArgSize, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE;;
773         }
774         else
775         {
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());
779             if (isFloatHfa)
780             {
781                 CorInfoHFAElemType type = m_argTypeHandle.GetHFAType();
782                 pLoc->setHFAFieldSize(type);
783             }
784             pLoc->m_byteStackSize = StackElemSize(byteArgSize, isValueType, isFloatHfa);
785         }
786     }
787 #endif // TARGET_ARM64
788
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)
792     {
793         LIMITED_METHOD_CONTRACT;
794
795 #if defined(UNIX_AMD64_ABI)
796         if (m_hasArgLocDescForStructInRegs)
797         {
798             *pLoc = m_argLocDescForStructInRegs;
799             return;
800         }
801
802         if (argOffset == TransitionBlock::StructInRegsOffset)
803         {
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.
806             _ASSERTE(false);
807             return;
808         }
809 #endif // UNIX_AMD64_ABI
810
811         pLoc->Init();
812
813 #if defined(UNIX_AMD64_ABI)
814         if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset))
815         {
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;
820         }
821         else 
822 #endif // UNIX_AMD64_ABI
823         if (!TransitionBlock::IsStackArgumentOffset(argOffset))
824         {
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))
828             {
829                 pLoc->m_idxFloatReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset);
830                 pLoc->m_cFloatReg = 1;
831             }
832             else
833 #endif
834             {
835                 pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset);
836                 pLoc->m_cGenReg = 1;
837             }
838         }
839         else
840         {
841             pLoc->m_byteStackIndex = TransitionBlock::GetStackArgumentByteIndexFromOffset(argOffset);
842             int argSizeInBytes;
843             if (IsArgPassedByRef())
844                 argSizeInBytes = TARGET_POINTER_SIZE;
845             else
846                 argSizeInBytes = GetArgSize();
847             pLoc->m_byteStackSize = StackElemSize(argSizeInBytes);
848         }
849     }
850 #endif // TARGET_AMD64
851
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)
856     {
857         LIMITED_METHOD_CONTRACT;
858
859         pLoc->Init();
860
861         if (m_hasArgLocDescForStructInRegs)
862         {
863             *pLoc = m_argLocDescForStructInRegs;
864             return;
865         }
866
867         if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset))
868         {
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);
872
873             pLoc->m_idxFloatReg = floatRegOfsInBytes / FLOAT_REGISTER_SIZE;
874
875             pLoc->m_cFloatReg = 1;
876
877             return;
878         }
879
880         int cSlots = (GetArgSize() + 7)/ 8;
881
882         // Composites greater than 16bytes are passed by reference
883         if (IsArgPassedByRef())
884         {
885             cSlots = 1;
886         }
887
888         if (!TransitionBlock::IsStackArgumentOffset(argOffset))
889         {
890             // At least one used integer register passed.
891             pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset);
892             pLoc->m_cGenReg = cSlots;
893         }
894         else
895         {
896             pLoc->m_byteStackIndex = TransitionBlock::GetStackArgumentByteIndexFromOffset(argOffset);
897             pLoc->m_byteStackSize = cSlots << 3;
898         }
899
900         return;
901     }
902
903 #endif // TARGET_LOONGARCH64 || TARGET_RISCV64
904 protected:
905     DWORD               m_dwFlags;              // Cached flags
906     int                 m_nSizeOfArgStack;      // Cached value of SizeOfArgStack
907
908     DWORD               m_argNum;
909
910     // Cached information about last argument
911     CorElementType      m_argType;
912     int                 m_argSize;
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
918
919     int                 m_ofsStack;           // Current position of the stack iterator, in bytes
920
921 #ifdef TARGET_X86
922     int                 m_numRegistersUsed;
923 #ifdef FEATURE_INTERPRETER
924     bool                m_fUnmanagedCallConv;
925 #endif
926 #endif
927
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
932 #endif
933
934 #ifdef TARGET_ARM
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
938 #endif
939
940 #ifdef TARGET_ARM64
941     int             m_idxGenReg;        // Next general register to be assigned a value
942     int             m_idxFPReg;         // Next FP register to be assigned a value
943 #endif
944
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
949 #endif
950
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
955 #endif
956
957     enum {
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
962
963 #ifdef TARGET_X86
964         PARAM_TYPE_REGISTER_MASK        = 0x0030,
965         PARAM_TYPE_REGISTER_STACK       = 0x0010,
966         PARAM_TYPE_REGISTER_ECX         = 0x0020,
967         PARAM_TYPE_REGISTER_EDX         = 0x0030,
968 #endif
969
970         METHOD_INVOKE_NEEDS_ACTIVATION  = 0x0040,   // Flag used by ArgIteratorForMethodInvoke
971
972         RETURN_FP_SIZE_SHIFT            = 8,        // The rest of the flags is cached value of GetFPReturnSize
973     };
974
975     void ComputeReturnFlags();
976
977 #ifndef TARGET_X86
978     void GetSimpleLoc(int offset, ArgLocDesc * pLoc)
979     {
980         WRAPPER_NO_CONTRACT;
981
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());
986 #endif
987
988         pLoc->Init();
989         pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(offset);
990         pLoc->m_cGenReg = 1;
991     }
992 #endif
993 };
994
995
996 template<class ARGITERATOR_BASE>
997 int ArgIteratorTemplate<ARGITERATOR_BASE>::GetThisOffset()
998 {
999     WRAPPER_NO_CONTRACT;
1000
1001     // This pointer is in the first argument register by default
1002     int ret = TransitionBlock::GetOffsetOfArgumentRegisters();
1003
1004 #ifdef TARGET_X86
1005     // x86 is special as always
1006     ret += offsetof(ArgumentRegisters, ECX);
1007 #endif
1008
1009     return ret;
1010 }
1011
1012 template<class ARGITERATOR_BASE>
1013 int ArgIteratorTemplate<ARGITERATOR_BASE>::GetRetBuffArgOffset()
1014 {
1015     WRAPPER_NO_CONTRACT;
1016
1017     _ASSERTE(this->HasRetBuffArg());
1018
1019     // RetBuf arg is in the second argument register by default
1020     int ret = TransitionBlock::GetOffsetOfArgumentRegisters();
1021
1022 #if TARGET_X86
1023     // x86 is special as always
1024     ret += this->HasThis() ? offsetof(ArgumentRegisters, EDX) : offsetof(ArgumentRegisters, ECX);
1025 #elif TARGET_ARM64
1026     ret = TransitionBlock::GetOffsetOfRetBuffArgReg();
1027 #else
1028     if (this->HasThis())
1029         ret += TARGET_POINTER_SIZE;
1030 #endif
1031
1032     return ret;
1033 }
1034
1035 template<class ARGITERATOR_BASE>
1036 int ArgIteratorTemplate<ARGITERATOR_BASE>::GetVASigCookieOffset()
1037 {
1038     WRAPPER_NO_CONTRACT;
1039
1040     _ASSERTE(this->IsVarArg());
1041
1042 #if defined(TARGET_X86)
1043     // x86 is special as always
1044     return sizeof(TransitionBlock);
1045 #else
1046     // VaSig cookie is after this and retbuf arguments by default.
1047     int ret = TransitionBlock::GetOffsetOfArgumentRegisters();
1048
1049     if (this->HasThis())
1050     {
1051         ret += TARGET_POINTER_SIZE;
1052     }
1053
1054     if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
1055     {
1056         ret += TARGET_POINTER_SIZE;
1057     }
1058
1059     return ret;
1060 #endif
1061 }
1062
1063 //-----------------------------------------------------------
1064 // Get the extra param offset for shared generic code
1065 //-----------------------------------------------------------
1066 template<class ARGITERATOR_BASE>
1067 int ArgIteratorTemplate<ARGITERATOR_BASE>::GetParamTypeArgOffset()
1068 {
1069     CONTRACTL
1070     {
1071         INSTANCE_CHECK;
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()); }
1075         MODE_ANY;
1076     }
1077     CONTRACTL_END
1078
1079     _ASSERTE(this->HasParamType());
1080
1081 #ifdef TARGET_X86
1082     // x86 is special as always
1083     if (!(m_dwFlags & SIZE_OF_ARG_STACK_COMPUTED))
1084         ForceSigWalk();
1085
1086     switch (m_dwFlags & PARAM_TYPE_REGISTER_MASK)
1087     {
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);
1092     default:
1093         break;
1094     }
1095
1096     // The param type arg is last stack argument otherwise
1097     return sizeof(TransitionBlock);
1098 #else
1099     // The hidden arg is after this and retbuf arguments by default.
1100     int ret = TransitionBlock::GetOffsetOfArgumentRegisters();
1101
1102     if (this->HasThis())
1103     {
1104         ret += TARGET_POINTER_SIZE;
1105     }
1106
1107     if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
1108     {
1109         ret += TARGET_POINTER_SIZE;
1110     }
1111
1112     return ret;
1113 #endif
1114 }
1115
1116 // To avoid corner case bugs, limit maximum size of the arguments with sufficient margin
1117 #define MAX_ARG_SIZE 0xFFFFFF
1118
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.
1122 //
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()
1127 {
1128     WRAPPER_NO_CONTRACT;
1129     SUPPORTS_DAC;
1130
1131     if (!(m_dwFlags & ITERATION_STARTED))
1132     {
1133         int numRegistersUsed = 0;
1134
1135         if (this->HasThis())
1136             numRegistersUsed++;
1137
1138         if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
1139             numRegistersUsed++;
1140
1141         _ASSERTE(!this->IsVarArg() || !this->HasParamType());
1142
1143 #ifndef TARGET_X86
1144         if (this->IsVarArg() || this->HasParamType())
1145         {
1146             numRegistersUsed++;
1147         }
1148 #endif
1149
1150 #ifdef TARGET_X86
1151         if (this->IsVarArg())
1152         {
1153             numRegistersUsed = NUM_ARGUMENT_REGISTERS; // Nothing else gets passed in registers for varargs
1154         }
1155
1156 #ifdef FEATURE_INTERPRETER
1157         BYTE callconv = CallConv();
1158         switch (callconv)
1159         {
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;
1165             break;
1166
1167         case IMAGE_CEE_CS_CALLCONV_THISCALL:
1168         case IMAGE_CEE_CS_CALLCONV_FASTCALL:
1169             _ASSERTE_MSG(false, "Unsupported calling convention.");
1170
1171         default:
1172             m_fUnmanagedCallConv = false;
1173             m_numRegistersUsed = numRegistersUsed;
1174             m_ofsStack = TransitionBlock::GetOffsetOfArgs() + SizeOfArgStack();
1175             break;
1176         }
1177 #else
1178         m_numRegistersUsed = numRegistersUsed;
1179         m_ofsStack = TransitionBlock::GetOffsetOfArgs() + SizeOfArgStack();
1180 #endif
1181
1182 #elif defined(TARGET_AMD64)
1183 #ifdef UNIX_AMD64_ABI
1184         m_idxGenReg = numRegistersUsed;
1185         m_ofsStack = 0;
1186         m_idxFPReg = 0;
1187 #else
1188         m_ofsStack = TransitionBlock::GetOffsetOfArgs() + numRegistersUsed * sizeof(void *);
1189 #endif
1190 #elif defined(TARGET_ARM)
1191         m_idxGenReg = numRegistersUsed;
1192         m_ofsStack = 0;
1193
1194         m_wFPRegs = 0;
1195 #elif defined(TARGET_ARM64)
1196         m_idxGenReg = numRegistersUsed;
1197         m_ofsStack = 0;
1198
1199         m_idxFPReg = 0;
1200 #elif defined(TARGET_LOONGARCH64)
1201         m_idxGenReg = numRegistersUsed;
1202         m_ofsStack = 0;
1203         m_idxFPReg = 0;
1204 #elif defined(TARGET_RISCV64)
1205         m_idxGenReg = numRegistersUsed;
1206         m_ofsStack = 0;
1207         m_idxFPReg = 0;
1208 #else
1209         PORTABILITY_ASSERT("ArgIteratorTemplate::GetNextOffset");
1210 #endif
1211
1212         m_argNum = 0;
1213
1214         m_dwFlags |= ITERATION_STARTED;
1215     }
1216
1217     // We're done going through the args for this MetaSig
1218     if (m_argNum == this->NumFixedArgs())
1219         return TransitionBlock::InvalidOffset;
1220
1221     TypeHandle thValueType;
1222     CorElementType argType = this->GetNextArgumentType(m_argNum++, &thValueType);
1223
1224     // TypedReference behaves like a valuetype
1225     if (argType == ELEMENT_TYPE_TYPEDBYREF)
1226     {
1227         argType = ELEMENT_TYPE_VALUETYPE;
1228         thValueType = TypeHandle(g_TypedReferenceMT);
1229     }
1230
1231     int argSize = MetaSig::GetElemSize(argType, thValueType);
1232
1233     m_argType = argType;
1234     m_argSize = argSize;
1235     m_argTypeHandle = thValueType;
1236
1237 #if defined(UNIX_AMD64_ABI) || defined (TARGET_ARM64) || defined (TARGET_LOONGARCH64) || defined (TARGET_RISCV64)
1238     m_hasArgLocDescForStructInRegs = false;
1239 #endif
1240
1241 #ifdef TARGET_X86
1242 #ifdef FEATURE_INTERPRETER
1243     if (m_fUnmanagedCallConv)
1244     {
1245         int argOfs = m_ofsStack;
1246         m_ofsStack += StackElemSize(argSize);
1247         return argOfs;
1248     }
1249 #endif
1250     if (IsArgumentInRegister(&m_numRegistersUsed, argType, thValueType))
1251     {
1252         return TransitionBlock::GetOffsetOfArgumentRegisters() + (NUM_ARGUMENT_REGISTERS - m_numRegistersUsed) * sizeof(void *);
1253     }
1254
1255     m_ofsStack -= StackElemSize(argSize);
1256     _ASSERTE(m_ofsStack >= TransitionBlock::GetOffsetOfArgs());
1257     return m_ofsStack;
1258 #elif defined(TARGET_AMD64)
1259 #ifdef UNIX_AMD64_ABI
1260
1261     m_fArgInRegisters = true;
1262
1263     int cFPRegs = 0;
1264     int cGenRegs = 0;
1265     int cbArg = StackElemSize(argSize);
1266
1267     switch (argType)
1268     {
1269
1270     case ELEMENT_TYPE_R4:
1271         // 32-bit floating point argument.
1272         cFPRegs = 1;
1273         break;
1274
1275     case ELEMENT_TYPE_R8:
1276         // 64-bit floating point argument.
1277         cFPRegs = 1;
1278         break;
1279
1280     case ELEMENT_TYPE_VALUETYPE:
1281     {
1282         MethodTable *pMT = m_argTypeHandle.GetMethodTable();
1283         if (this->IsRegPassedStruct(pMT))
1284         {
1285             EEClass* eeClass = pMT->GetClass();
1286             cGenRegs = 0;
1287             for (int i = 0; i < eeClass->GetNumberEightBytes(); i++)
1288             {
1289                 switch (eeClass->GetEightByteClassification(i))
1290                 {
1291                     case SystemVClassificationTypeInteger:
1292                     case SystemVClassificationTypeIntegerReference:
1293                     case SystemVClassificationTypeIntegerByRef:
1294                         cGenRegs++;
1295                         break;
1296                     case SystemVClassificationTypeSSE:
1297                         cFPRegs++;
1298                         break;
1299                     default:
1300                         _ASSERTE(false);
1301                         break;
1302                 }
1303             }
1304
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)
1307             {
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;
1314
1315                 m_hasArgLocDescForStructInRegs = true;
1316
1317                 m_idxGenReg += cGenRegs;
1318                 m_idxFPReg += cFPRegs;
1319
1320                 return TransitionBlock::StructInRegsOffset;
1321             }
1322         }
1323
1324         // Set the register counts to indicate that this argument will not be passed in registers
1325         cFPRegs = 0;
1326         cGenRegs = 0;
1327         break;
1328     }
1329
1330     default:
1331         cGenRegs = cbArg / 8; // GP reg size
1332         break;
1333     }
1334
1335     if ((cFPRegs > 0) && (cFPRegs + m_idxFPReg <= NUM_FLOAT_ARGUMENT_REGISTERS))
1336     {
1337         int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 16;
1338         m_idxFPReg += cFPRegs;
1339         return argOfs;
1340     }
1341     else if ((cGenRegs > 0) && (m_idxGenReg + cGenRegs <= NUM_ARGUMENT_REGISTERS))
1342     {
1343         int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1344         m_idxGenReg += cGenRegs;
1345         return argOfs;
1346     }
1347
1348     m_fArgInRegisters = false;
1349
1350     int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
1351
1352     m_ofsStack += cbArg;
1353
1354     return argOfs;
1355 #else
1356     // Each argument takes exactly one slot on AMD64 on Windows
1357     int argOfs = m_ofsStack;
1358     m_ofsStack += sizeof(void *);
1359     return argOfs;
1360 #endif
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).
1366
1367     bool fFloatingPoint = false;
1368     bool fRequiresAlign64Bit = false;
1369
1370     switch (argType)
1371     {
1372     case ELEMENT_TYPE_I8:
1373     case ELEMENT_TYPE_U8:
1374         // 64-bit integers require 64-bit alignment on ARM.
1375         fRequiresAlign64Bit = true;
1376         break;
1377
1378     case ELEMENT_TYPE_R4:
1379         // 32-bit floating point argument.
1380         fFloatingPoint = true;
1381         break;
1382
1383     case ELEMENT_TYPE_R8:
1384         // 64-bit floating point argument.
1385         fFloatingPoint = true;
1386         fRequiresAlign64Bit = true;
1387         break;
1388
1389     case ELEMENT_TYPE_VALUETYPE:
1390     {
1391         // Value type case: extract the alignment requirement, note that this has to handle
1392         // the interop "native value types".
1393         fRequiresAlign64Bit = thValueType.RequiresAlign8();
1394
1395 #ifdef FEATURE_HFA
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())
1399         {
1400             fFloatingPoint = true;
1401         }
1402 #endif
1403
1404         break;
1405     }
1406
1407     default:
1408         // The default is are 4-byte arguments (or promoted to 4 bytes), non-FP and don't require any
1409         // 64-bit alignment.
1410         break;
1411     }
1412
1413     // Now attempt to place the argument into some combination of floating point or general registers and
1414     // the stack.
1415
1416     // Save the alignment requirement
1417     m_fRequires64BitAlignment = fRequiresAlign64Bit;
1418
1419     int cbArg = StackElemSize(argSize);
1420     _ASSERTE((cbArg % TARGET_POINTER_SIZE) == 0);
1421
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).
1424 #ifndef ARM_SOFTFP
1425     if (fFloatingPoint && !this->IsVarArg())
1426     {
1427         // Handle floating point (primitive) arguments.
1428
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).
1434         //
1435         // We use a 16-bit bitmap to record which registers have been used so far.
1436         //
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
1442
1443         WORD wAllocMask = (1 << (cbArg / 4)) - 1;
1444         WORD cSteps = (WORD)(fRequiresAlign64Bit ? 9 - (cbArg / 8) : 17 - (cbArg / 4));
1445         WORD cShift = fRequiresAlign64Bit ? 2 : 1;
1446
1447         // Look through the availability bitmask for a free register or register pair.
1448         for (WORD i = 0; i < cSteps; i++)
1449         {
1450             if ((m_wFPRegs & wAllocMask) == 0)
1451             {
1452                 // We found one, mark the register or registers as used.
1453                 m_wFPRegs |= wAllocMask;
1454
1455                 // Indicate the registers used to the caller and return.
1456                 return TransitionBlock::GetOffsetOfFloatArgumentRegisters() + (i * cShift * 4);
1457             }
1458             wAllocMask <<= cShift;
1459         }
1460
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.
1463         m_wFPRegs = 0xffff;
1464
1465         // Doubles or HFAs containing doubles need the stack aligned appropriately.
1466         if (fRequiresAlign64Bit)
1467         {
1468             m_ofsStack = (int)ALIGN_UP(m_ofsStack, TARGET_POINTER_SIZE * 2);
1469         }
1470
1471         // Indicate the stack location of the argument to the caller.
1472         int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
1473
1474         // Record the stack usage.
1475         m_ofsStack += cbArg;
1476
1477         return argOfs;
1478     }
1479 #endif // ARM_SOFTFP
1480
1481     //
1482     // Handle the non-floating point case.
1483     //
1484
1485     if (m_idxGenReg < 4)
1486     {
1487         if (fRequiresAlign64Bit)
1488         {
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);
1492         }
1493
1494         int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 4;
1495
1496         int cRemainingRegs = 4 - m_idxGenReg;
1497         if (cbArg <= cRemainingRegs * TARGET_POINTER_SIZE)
1498         {
1499             // Mark the registers just allocated as used.
1500             m_idxGenReg += ALIGN_UP(cbArg, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE;
1501             return argOfs;
1502         }
1503
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.
1508
1509         m_idxGenReg = 4;
1510
1511         if (m_ofsStack == 0)
1512         {
1513             m_ofsStack += cbArg - cRemainingRegs * TARGET_POINTER_SIZE;
1514             return argOfs;
1515         }
1516     }
1517
1518     if (fRequiresAlign64Bit)
1519     {
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);
1523     }
1524
1525     int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
1526
1527     // Advance the stack pointer over the argument just placed.
1528     m_ofsStack += cbArg;
1529
1530     return argOfs;
1531 #elif defined(TARGET_ARM64)
1532
1533     int cFPRegs = 0;
1534
1535     switch (argType)
1536     {
1537
1538     case ELEMENT_TYPE_R4:
1539         // 32-bit floating point argument.
1540         cFPRegs = 1;
1541         break;
1542
1543     case ELEMENT_TYPE_R8:
1544         // 64-bit floating point argument.
1545         cFPRegs = 1;
1546         break;
1547
1548     case ELEMENT_TYPE_VALUETYPE:
1549     {
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())
1553         {
1554             CorInfoHFAElemType type = thValueType.GetHFAType();
1555
1556             m_argLocDescForStructInRegs.Init();
1557             m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
1558
1559             m_argLocDescForStructInRegs.setHFAFieldSize(type);
1560             cFPRegs = argSize/m_argLocDescForStructInRegs.m_hfaFieldSize;
1561             m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs;
1562
1563             // Check if we have enough registers available for the HFA passing
1564             if ((cFPRegs + m_idxFPReg) <= 8)
1565             {
1566                 m_hasArgLocDescForStructInRegs = true;
1567             }
1568         }
1569         else
1570         {
1571             // Composite greater than 16bytes should be passed by reference
1572             if (argSize > ENREGISTERED_PARAMTYPE_MAXSIZE)
1573             {
1574                 argSize = sizeof(TADDR);
1575             }
1576         }
1577
1578         break;
1579     }
1580
1581     default:
1582         break;
1583     }
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())
1588     {
1589         if (cFPRegs + m_idxFPReg <= 8)
1590         {
1591             // Each floating point register in the argument area is 16 bytes.
1592             int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 16;
1593             m_idxFPReg += cFPRegs;
1594             return argOfs;
1595         }
1596         else
1597         {
1598             m_idxFPReg = 8;
1599         }
1600     }
1601     else
1602     {
1603 #if !defined(OSX_ARM64_ABI)
1604         _ASSERTE((cbArg% TARGET_POINTER_SIZE) == 0);
1605 #endif
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)
1609         {
1610             // The entirety of the arg fits in the register slots.
1611
1612             int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1613             m_idxGenReg += regSlots;
1614             return argOfs;
1615         }
1616         else
1617         {
1618 #ifdef _WIN32
1619             if (this->IsVarArg() && m_idxGenReg < 8)
1620             {
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;
1625
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;
1629
1630                 // We used up the remaining reg slots.
1631                 m_idxGenReg = 8;
1632
1633                 return argOfs;
1634             }
1635             else
1636 #endif
1637             {
1638                 // Don't use reg slots for this. It will be passed purely on the stack arg space.
1639                 m_idxGenReg = 8;
1640             }
1641         }
1642     }
1643
1644 #ifdef OSX_ARM64_ABI
1645     int alignment;
1646     if (!isValueType)
1647     {
1648         _ASSERTE((cbArg & (cbArg - 1)) == 0);
1649         alignment = cbArg;
1650     }
1651     else if (isFloatHfa)
1652     {
1653         alignment = 4;
1654     }
1655     else
1656     {
1657         alignment = 8;
1658     }
1659     m_ofsStack = (int)ALIGN_UP(m_ofsStack, alignment);
1660 #endif // OSX_ARM64_ABI
1661
1662     int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
1663     m_ofsStack += cbArg;
1664     return argOfs;
1665 #elif defined(TARGET_LOONGARCH64)
1666
1667     int cFPRegs = 0;
1668     int flags = 0;
1669
1670     switch (argType)
1671     {
1672
1673     case ELEMENT_TYPE_R4:
1674         // 32-bit floating point argument.
1675         cFPRegs = 1;
1676         break;
1677
1678     case ELEMENT_TYPE_R8:
1679         // 64-bit floating point argument.
1680         cFPRegs = 1;
1681         break;
1682
1683     case ELEMENT_TYPE_VALUETYPE:
1684     {
1685         // Handle struct which containing floats or doubles that can be passed
1686         // in FP registers if possible.
1687
1688         // Composite greater than 16bytes should be passed by reference
1689         if (argSize > ENREGISTERED_PARAMTYPE_MAXSIZE)
1690         {
1691             argSize = sizeof(TADDR);
1692         }
1693         else
1694         {
1695             flags = MethodTable::GetLoongArch64PassStructInRegisterFlags(thValueType);
1696             if (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)
1697             {
1698                 cFPRegs = (flags & STRUCT_FLOAT_FIELD_ONLY_TWO) ? 2 : 1;
1699             }
1700         }
1701
1702         break;
1703     }
1704
1705     default:
1706         break;
1707     }
1708
1709     const bool isValueType = (argType == ELEMENT_TYPE_VALUETYPE);
1710     const bool isFloatHfa = thValueType.IsFloatHfa();
1711     const int cbArg = StackElemSize(argSize, isValueType, isFloatHfa);
1712
1713     if (cFPRegs > 0 && !this->IsVarArg())
1714     {
1715         if (flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND))
1716         {
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)));
1719
1720             if ((1 + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) && (m_idxGenReg + 1 <= NUM_ARGUMENT_REGISTERS))
1721             {
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;
1726                 m_idxFPReg += 1;
1727
1728                 m_argLocDescForStructInRegs.m_structFields = flags;
1729
1730                 m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg;
1731                 m_argLocDescForStructInRegs.m_cGenReg = 1;
1732                 m_idxGenReg += 1;
1733
1734                 m_hasArgLocDescForStructInRegs = true;
1735
1736                 return argOfs;
1737             }
1738         }
1739         else if (cFPRegs + m_idxFPReg <= NUM_ARGUMENT_REGISTERS)
1740         {
1741             int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
1742             if (flags == STRUCT_FLOAT_FIELD_ONLY_TWO) // struct with two float-fields.
1743             {
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;
1751             }
1752             m_idxFPReg += cFPRegs;
1753             return argOfs;
1754         }
1755     }
1756
1757     {
1758         const int regSlots = ALIGN_UP(cbArg, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE;
1759         if (m_idxGenReg + regSlots <= NUM_ARGUMENT_REGISTERS)
1760         {
1761             int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1762             m_idxGenReg += regSlots;
1763             return argOfs;
1764         }
1765         else if (m_idxGenReg < NUM_ARGUMENT_REGISTERS)
1766         {
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;
1771             return argOfs;
1772         }
1773     }
1774
1775     int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
1776     m_ofsStack += ALIGN_UP(cbArg, TARGET_POINTER_SIZE);
1777
1778     return argOfs;
1779 #elif defined(TARGET_RISCV64)
1780
1781     int cFPRegs = 0;
1782     int flags = 0;
1783
1784     switch (argType)
1785     {
1786
1787     case ELEMENT_TYPE_R4:
1788         // 32-bit floating point argument.
1789         cFPRegs = 1;
1790         break;
1791
1792     case ELEMENT_TYPE_R8:
1793         // 64-bit floating point argument.
1794         cFPRegs = 1;
1795         break;
1796
1797     case ELEMENT_TYPE_VALUETYPE:
1798     {
1799         // Handle struct which containing floats or doubles that can be passed
1800         // in FP registers if possible.
1801
1802         // Composite greater than 16bytes should be passed by reference
1803         if (argSize > ENREGISTERED_PARAMTYPE_MAXSIZE)
1804         {
1805             argSize = sizeof(TADDR);
1806         }
1807         else
1808         {
1809             flags = MethodTable::GetRiscV64PassStructInRegisterFlags(thValueType);
1810             if (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)
1811             {
1812                 cFPRegs = (flags & STRUCT_FLOAT_FIELD_ONLY_TWO) ? 2 : 1;
1813             }
1814         }
1815
1816         break;
1817     }
1818
1819     default:
1820         break;
1821     }
1822
1823     const bool isValueType = (argType == ELEMENT_TYPE_VALUETYPE);
1824     const bool isFloatHfa = thValueType.IsFloatHfa();
1825     const int cbArg = StackElemSize(argSize, isValueType, isFloatHfa);
1826
1827     if (cFPRegs > 0 && !this->IsVarArg())
1828     {
1829         if (flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND))
1830         {
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)));
1833
1834             if ((1 + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) && (m_idxGenReg + 1 <= NUM_ARGUMENT_REGISTERS))
1835             {
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;
1840                 m_idxFPReg += 1;
1841
1842                 m_argLocDescForStructInRegs.m_structFields = flags;
1843
1844                 m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg;
1845                 m_argLocDescForStructInRegs.m_cGenReg = 1;
1846                 m_idxGenReg += 1;
1847
1848                 m_hasArgLocDescForStructInRegs = true;
1849
1850                 return argOfs;
1851             }
1852         }
1853         else if (cFPRegs + m_idxFPReg <= NUM_ARGUMENT_REGISTERS)
1854         {
1855             int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
1856             if (flags == STRUCT_FLOAT_FIELD_ONLY_TWO) // struct with two float-fields.
1857             {
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;
1865             }
1866             m_idxFPReg += cFPRegs;
1867             return argOfs;
1868         }
1869     }
1870
1871     {
1872         const int regSlots = ALIGN_UP(cbArg, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE;
1873         if (m_idxGenReg + regSlots <= NUM_ARGUMENT_REGISTERS)
1874         {
1875             int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1876             m_idxGenReg += regSlots;
1877             return argOfs;
1878         }
1879         else if (m_idxGenReg < NUM_ARGUMENT_REGISTERS)
1880         {
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;
1885             return argOfs;
1886         }
1887     }
1888
1889     int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
1890     m_ofsStack += ALIGN_UP(cbArg, TARGET_POINTER_SIZE);
1891
1892     return argOfs;
1893 #else
1894     PORTABILITY_ASSERT("ArgIteratorTemplate::GetNextOffset");
1895     return TransitionBlock::InvalidOffset;
1896 #endif
1897 }
1898
1899 template<class ARGITERATOR_BASE>
1900 void ArgIteratorTemplate<ARGITERATOR_BASE>::ComputeReturnFlags()
1901 {
1902     CONTRACTL
1903     {
1904         INSTANCE_CHECK;
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()); }
1908         MODE_ANY;
1909     }
1910     CONTRACTL_END
1911
1912     TypeHandle thValueType;
1913     CorElementType type = this->GetReturnType(&thValueType);
1914
1915     DWORD flags = RETURN_FLAGS_COMPUTED;
1916     switch (type)
1917     {
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;
1922 #else
1923         flags |= RETURN_HAS_RET_BUFFER;
1924 #endif
1925         break;
1926
1927     case ELEMENT_TYPE_R4:
1928 #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1929         flags |= STRUCT_FLOAT_FIELD_ONLY_ONE << RETURN_FP_SIZE_SHIFT;
1930 #else
1931 #ifndef ARM_SOFTFP
1932         flags |= sizeof(float) << RETURN_FP_SIZE_SHIFT;
1933 #endif
1934 #endif
1935         break;
1936
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;
1940 #else
1941 #ifndef ARM_SOFTFP
1942         flags |= sizeof(double) << RETURN_FP_SIZE_SHIFT;
1943 #endif
1944 #endif
1945         break;
1946
1947     case ELEMENT_TYPE_VALUETYPE:
1948 #ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
1949         {
1950             _ASSERTE(!thValueType.IsNull());
1951
1952 #if defined(UNIX_AMD64_ABI)
1953             MethodTable *pMT = thValueType.AsMethodTable();
1954             if (pMT->IsRegPassedStruct())
1955             {
1956                 EEClass* eeClass = pMT->GetClass();
1957
1958                 if (eeClass->GetNumberEightBytes() == 1)
1959                 {
1960                     // Structs occupying just one eightbyte are treated as int / double
1961                     if (eeClass->GetEightByteClassification(0) == SystemVClassificationTypeSSE)
1962                     {
1963                         flags |= sizeof(double) << RETURN_FP_SIZE_SHIFT;
1964                     }
1965                 }
1966                 else
1967                 {
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)
1972                     {
1973                         flags |= (1 << RETURN_FP_SIZE_SHIFT);
1974                     }
1975
1976                     if (eeClass->GetEightByteClassification(1) == SystemVClassificationTypeSSE)
1977                     {
1978                         flags |= (2 << RETURN_FP_SIZE_SHIFT);
1979                     }
1980                 }
1981
1982                 break;
1983             }
1984 #else // UNIX_AMD64_ABI
1985
1986 #ifdef FEATURE_HFA
1987             if (thValueType.IsHFA() && !this->IsVarArg())
1988             {
1989                 CorInfoHFAElemType hfaType = thValueType.GetHFAType();
1990
1991                 int hfaFieldSize = ArgLocDesc::getHFAFieldSize(hfaType);
1992                 flags |= ((4 * hfaFieldSize) << RETURN_FP_SIZE_SHIFT);
1993                 break;
1994             }
1995 #endif
1996
1997             size_t size = thValueType.GetSize();
1998
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)
2002             {
2003                 flags |= RETURN_HAS_RET_BUFFER;
2004                 break;
2005             }
2006 #endif // defined(TARGET_X86) || defined(TARGET_AMD64)
2007
2008 #if defined(TARGET_LOONGARCH64)
2009             if  (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
2010             {
2011                 assert(!thValueType.IsTypeDesc());
2012                 flags = (MethodTable::GetLoongArch64PassStructInRegisterFlags(thValueType) & 0xff) << RETURN_FP_SIZE_SHIFT;
2013                 break;
2014             }
2015 #elif defined(TARGET_RISCV64)
2016             if  (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
2017             {
2018                 assert(!thValueType.IsTypeDesc());
2019                 flags = (MethodTable::GetRiscV64PassStructInRegisterFlags(thValueType) & 0xff) << RETURN_FP_SIZE_SHIFT;
2020                 break;
2021             }
2022 #else
2023             if  (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
2024                 break;
2025 #endif
2026 #endif // UNIX_AMD64_ABI
2027         }
2028 #endif // ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
2029
2030         // Value types are returned using return buffer by default
2031         flags |= RETURN_HAS_RET_BUFFER;
2032         break;
2033
2034     default:
2035         break;
2036     }
2037
2038     m_dwFlags |= flags;
2039 }
2040
2041 template<class ARGITERATOR_BASE>
2042 void ArgIteratorTemplate<ARGITERATOR_BASE>::ForceSigWalk()
2043 {
2044     CONTRACTL
2045     {
2046         INSTANCE_CHECK;
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()); }
2050         MODE_ANY;
2051     }
2052     CONTRACTL_END
2053
2054     // This can be only used before the actual argument iteration started
2055     _ASSERTE((m_dwFlags & ITERATION_STARTED) == 0);
2056
2057 #ifdef TARGET_X86
2058     //
2059     // x86 is special as always
2060     //
2061
2062     int numRegistersUsed = 0;
2063     int nSizeOfArgStack = 0;
2064
2065     if (this->HasThis())
2066         numRegistersUsed++;
2067
2068     if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
2069         numRegistersUsed++;
2070
2071     if (this->IsVarArg())
2072     {
2073         nSizeOfArgStack += sizeof(void *);
2074         numRegistersUsed = NUM_ARGUMENT_REGISTERS; // Nothing else gets passed in registers for varargs
2075     }
2076
2077 #ifdef FEATURE_INTERPRETER
2078     BYTE callconv = CallConv();
2079     switch (callconv)
2080     {
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 *);
2085         break;
2086
2087     case IMAGE_CEE_CS_CALLCONV_THISCALL:
2088     case IMAGE_CEE_CS_CALLCONV_FASTCALL:
2089         _ASSERTE_MSG(false, "Unsupported calling convention.");
2090     default:
2091         break;
2092     }
2093 #endif // FEATURE_INTERPRETER
2094
2095     DWORD nArgs = this->NumFixedArgs();
2096     for (DWORD i = 0; i < nArgs; i++)
2097     {
2098         TypeHandle thValueType;
2099         CorElementType type = this->GetNextArgumentType(i, &thValueType);
2100
2101         if (!IsArgumentInRegister(&numRegistersUsed, type, thValueType))
2102         {
2103             int structSize = MetaSig::GetElemSize(type, thValueType);
2104
2105             nSizeOfArgStack += StackElemSize(structSize);
2106
2107 #ifndef DACCESS_COMPILE
2108             if (nSizeOfArgStack > MAX_ARG_SIZE)
2109             {
2110 #ifdef _DEBUG
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);
2115 #endif
2116                 COMPlusThrow(kNotSupportedException);
2117             }
2118 #endif
2119         }
2120     }
2121
2122     if (this->HasParamType())
2123     {
2124         DWORD paramTypeFlags = 0;
2125         if (numRegistersUsed < NUM_ARGUMENT_REGISTERS)
2126         {
2127             numRegistersUsed++;
2128             paramTypeFlags = (numRegistersUsed == 1) ?
2129                 PARAM_TYPE_REGISTER_ECX : PARAM_TYPE_REGISTER_EDX;
2130         }
2131         else
2132         {
2133             nSizeOfArgStack += sizeof(void *);
2134             paramTypeFlags = PARAM_TYPE_REGISTER_STACK;
2135         }
2136         m_dwFlags |= paramTypeFlags;
2137     }
2138
2139 #else // TARGET_X86
2140
2141     int maxOffset = TransitionBlock::GetOffsetOfArgs();
2142
2143     int ofs;
2144     while (TransitionBlock::InvalidOffset != (ofs = GetNextOffset()))
2145     {
2146         int stackElemSize;
2147
2148 #ifdef TARGET_AMD64
2149 #ifdef UNIX_AMD64_ABI
2150         if (m_fArgInRegisters)
2151         {
2152             // Arguments passed in registers don't consume any stack
2153             continue;
2154         }
2155
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
2163
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;
2171 #endif
2172 #endif // TARGET_AMD64
2173
2174         int endOfs = ofs + stackElemSize;
2175         if (endOfs > maxOffset)
2176         {
2177 #if !defined(DACCESS_COMPILE)
2178             if (endOfs > MAX_ARG_SIZE)
2179             {
2180 #ifdef _DEBUG
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);
2185 #endif
2186                 COMPlusThrow(kNotSupportedException);
2187             }
2188 #endif
2189             maxOffset = endOfs;
2190         }
2191     }
2192     // Clear the iterator started flag
2193     m_dwFlags &= ~ITERATION_STARTED;
2194
2195     int nSizeOfArgStack = maxOffset - TransitionBlock::GetOffsetOfArgs();
2196
2197 #if defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI)
2198     nSizeOfArgStack = (nSizeOfArgStack > (int)sizeof(ArgumentRegisters)) ?
2199         (nSizeOfArgStack - sizeof(ArgumentRegisters)) : 0;
2200 #endif
2201
2202 #endif // TARGET_X86
2203
2204     // arg stack size is rounded to the pointer size on all platforms.
2205     nSizeOfArgStack = (int)ALIGN_UP(nSizeOfArgStack, TARGET_POINTER_SIZE);
2206
2207     // Cache the result
2208     m_nSizeOfArgStack = nSizeOfArgStack;
2209     m_dwFlags |= SIZE_OF_ARG_STACK_COMPUTED;
2210
2211     this->Reset();
2212 }
2213
2214 class ArgIteratorBase
2215 {
2216 protected:
2217     MetaSig * m_pSig;
2218
2219     FORCEINLINE CorElementType GetReturnType(TypeHandle * pthValueType)
2220     {
2221         WRAPPER_NO_CONTRACT;
2222 #ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
2223         return m_pSig->GetReturnTypeNormalized(pthValueType);
2224 #else
2225         return m_pSig->GetReturnTypeNormalized();
2226 #endif
2227     }
2228
2229     FORCEINLINE CorElementType GetNextArgumentType(DWORD iArg, TypeHandle * pthValueType)
2230     {
2231         WRAPPER_NO_CONTRACT;
2232         _ASSERTE(iArg == m_pSig->GetArgNum());
2233         CorElementType et = m_pSig->PeekArgNormalized(pthValueType);
2234         m_pSig->SkipArg();
2235         return et;
2236     }
2237
2238     FORCEINLINE void Reset()
2239     {
2240         WRAPPER_NO_CONTRACT;
2241         m_pSig->Reset();
2242     }
2243
2244     FORCEINLINE BOOL IsRegPassedStruct(MethodTable* pMT)
2245     {
2246         return pMT->IsRegPassedStruct();
2247     }
2248
2249 public:
2250     BOOL HasThis()
2251     {
2252         LIMITED_METHOD_CONTRACT;
2253         return m_pSig->HasThis();
2254     }
2255
2256     BOOL HasParamType()
2257     {
2258         LIMITED_METHOD_CONTRACT;
2259         return m_pSig->GetCallingConventionInfo() & CORINFO_CALLCONV_PARAMTYPE;
2260     }
2261
2262     BOOL IsVarArg()
2263     {
2264         LIMITED_METHOD_CONTRACT;
2265         return m_pSig->IsVarArg() || m_pSig->IsTreatAsVarArg();
2266     }
2267
2268     DWORD NumFixedArgs()
2269     {
2270         LIMITED_METHOD_CONTRACT;
2271         return m_pSig->NumFixedArgs();
2272     }
2273
2274 #ifdef FEATURE_INTERPRETER
2275     BYTE CallConv()
2276     {
2277         return m_pSig->GetCallingConvention();
2278     }
2279 #endif // FEATURE_INTERPRETER
2280
2281     //
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.
2286     //
2287     MetaSig *GetSig(void)
2288     {
2289         return m_pSig;
2290     }
2291 };
2292
2293 class ArgIterator : public ArgIteratorTemplate<ArgIteratorBase>
2294 {
2295 public:
2296     ArgIterator(MetaSig * pSig)
2297     {
2298         m_pSig = pSig;
2299     }
2300
2301     // This API returns true if we are returning a structure in registers instead of using a byref return buffer
2302     BOOL HasNonStandardByvalReturn()
2303     {
2304         WRAPPER_NO_CONTRACT;
2305
2306 #ifdef ENREGISTERED_RETURNTYPE_MAXSIZE
2307         CorElementType type = m_pSig->GetReturnTypeNormalized();
2308         return (type == ELEMENT_TYPE_VALUETYPE || type == ELEMENT_TYPE_TYPEDBYREF) && !HasRetBuffArg();
2309 #else
2310         return FALSE;
2311 #endif
2312     }
2313
2314     BOOL HasValueTypeReturn()
2315     {
2316         WRAPPER_NO_CONTRACT;
2317
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();
2323     }
2324 };
2325
2326 // Conventience helper
2327 inline BOOL HasRetBuffArg(MetaSig * pSig)
2328 {
2329     WRAPPER_NO_CONTRACT;
2330     ArgIterator argit(pSig);
2331     return argit.HasRetBuffArg();
2332 }
2333
2334 #ifdef UNIX_X86_ABI
2335 // For UNIX_X86_ABI and unmanaged function, we always need RetBuf if the return type is VALUETYPE
2336 inline BOOL HasRetBuffArgUnmanagedFixup(MetaSig * pSig)
2337 {
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;
2342 }
2343 #endif
2344
2345 inline BOOL IsRetBuffPassedAsFirstArg()
2346 {
2347     WRAPPER_NO_CONTRACT;
2348 #ifndef TARGET_ARM64
2349     return TRUE;
2350 #else
2351     return FALSE;
2352 #endif
2353 }
2354
2355 #endif // __CALLING_CONVENTION_INCLUDED