Deal with cross-bitness compilation warnings Pt.2 (#19781)
[platform/upstream/coreclr.git] / src / 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 // See the LICENSE file in the project root for more information.
4 //
5
6
7 //
8 // Provides an abstraction over platform specific calling conventions (specifically, the calling convention
9 // utilized by the JIT on that platform). The caller enumerates each argument of a signature in turn, and is 
10 // provided with information mapping that argument into registers and/or stack locations.
11 //
12
13 #ifndef __CALLING_CONVENTION_INCLUDED
14 #define __CALLING_CONVENTION_INCLUDED
15
16 BOOL IsRetBuffPassedAsFirstArg();
17
18 // Describes how a single argument is laid out in registers and/or stack locations when given as an input to a
19 // managed method as part of a larger signature.
20 //
21 // Locations are split into floating point registers, general registers and stack offsets. Registers are
22 // obviously architecture dependent but are represented as a zero-based index into the usual sequence in which
23 // such registers are allocated for input on the platform in question. For instance:
24 //      X86: 0 == ecx, 1 == edx
25 //      ARM: 0 == r0, 1 == r1, 2 == r2 etc.
26 //
27 // Stack locations are represented as offsets from the stack pointer (at the point of the call). The offset is
28 // given as an index of a pointer sized slot. Similarly the size of data on the stack is given in slot-sized
29 // units. For instance, given an index of 2 and a size of 3:
30 //      X86:   argument starts at [ESP + 8] and is 12 bytes long
31 //      AMD64: argument starts at [RSP + 16] and is 24 bytes long
32 //
33 // The structure is flexible enough to describe an argument that is split over several (consecutive) registers
34 // and possibly on to the stack as well.
35 struct ArgLocDesc
36 {
37     int     m_idxFloatReg;        // First floating point register used (or -1)
38     int     m_cFloatReg;          // Count of floating point registers used (or 0)
39
40     int     m_idxGenReg;          // First general register used (or -1)
41     int     m_cGenReg;            // Count of general registers used (or 0)
42
43     int     m_idxStack;           // First stack slot used (or -1)
44     int     m_cStack;             // Count of stack slots used (or 0)
45
46 #if defined(UNIX_AMD64_ABI)
47
48     EEClass* m_eeClass;           // For structs passed in register, it points to the EEClass of the struct
49
50 #endif // UNIX_AMD64_ABI
51
52 #if defined(_TARGET_ARM64_)
53     bool    m_isSinglePrecision;  // For determining if HFA is single or double
54                                   // precision
55 #endif // defined(_TARGET_ARM64_)
56
57 #if defined(_TARGET_ARM_)
58     BOOL    m_fRequires64BitAlignment; // True if the argument should always be aligned (in registers or on the stack
59 #endif
60
61     ArgLocDesc()
62     {
63         Init();
64     }
65
66     // Initialize to represent a non-placed argument (no register or stack slots referenced).
67     void Init()
68     {
69         m_idxFloatReg = -1;
70         m_cFloatReg = 0;
71         m_idxGenReg = -1;
72         m_cGenReg = 0;
73         m_idxStack = -1;
74         m_cStack = 0;
75 #if defined(_TARGET_ARM_)
76         m_fRequires64BitAlignment = FALSE;
77 #endif
78 #if defined(_TARGET_ARM64_)
79         m_isSinglePrecision = FALSE;
80 #endif // defined(_TARGET_ARM64_)
81 #if defined(UNIX_AMD64_ABI)
82         m_eeClass = NULL;
83 #endif
84     }
85 };
86
87 //
88 // TransitionBlock is layout of stack frame of method call, saved argument registers and saved callee saved registers. Even though not 
89 // all fields are used all the time, we use uniform form for simplicity.
90 //
91 struct TransitionBlock
92 {
93 #if defined(_TARGET_X86_)
94     ArgumentRegisters       m_argumentRegisters;
95     CalleeSavedRegisters    m_calleeSavedRegisters;
96     TADDR                   m_ReturnAddress;
97 #elif defined(_TARGET_AMD64_)
98 #ifdef UNIX_AMD64_ABI
99     ArgumentRegisters       m_argumentRegisters;
100 #endif
101     CalleeSavedRegisters    m_calleeSavedRegisters;
102     TADDR                   m_ReturnAddress;
103 #elif defined(_TARGET_ARM_)
104     union {
105         CalleeSavedRegisters m_calleeSavedRegisters;
106         // alias saved link register as m_ReturnAddress
107         struct {
108             INT32 r4, r5, r6, r7, r8, r9, r10;
109             INT32 r11;
110             TADDR m_ReturnAddress;
111         };
112     };
113     ArgumentRegisters       m_argumentRegisters;
114 #elif defined(_TARGET_ARM64_)
115     union {
116         CalleeSavedRegisters m_calleeSavedRegisters;
117         struct {
118             INT64 x29; // frame pointer
119             TADDR m_ReturnAddress;
120             INT64 x19, x20, x21, x22, x23, x24, x25, x26, x27, x28;
121         };
122     };
123     TADDR padding; // Keep size of TransitionBlock as multiple of 16-byte. Simplifies code in PROLOG_WITH_TRANSITION_BLOCK
124     INT64 m_x8RetBuffReg;
125     ArgumentRegisters       m_argumentRegisters;
126 #else
127     PORTABILITY_ASSERT("TransitionBlock");
128 #endif
129
130     // The transition block should define everything pushed by callee. The code assumes in number of places that
131     // end of the transition block is caller's stack pointer.
132
133     static int GetOffsetOfReturnAddress()
134     {
135         LIMITED_METHOD_CONTRACT;
136         return offsetof(TransitionBlock, m_ReturnAddress);
137     }
138
139 #ifdef _TARGET_ARM64_
140     static int GetOffsetOfRetBuffArgReg()
141     {
142         LIMITED_METHOD_CONTRACT;
143         return offsetof(TransitionBlock, m_x8RetBuffReg);
144     }
145 #endif
146
147     static BYTE GetOffsetOfArgs()
148     {
149         LIMITED_METHOD_CONTRACT;
150
151         // Offset of the stack args (which are after the TransitionBlock)
152         return sizeof(TransitionBlock);
153     }
154
155     static int GetOffsetOfArgumentRegisters()
156     {
157         LIMITED_METHOD_CONTRACT;
158         int offs;
159 #if defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI)
160         offs = sizeof(TransitionBlock);
161 #else
162         offs = offsetof(TransitionBlock, m_argumentRegisters);
163 #endif
164         return offs;
165     }
166
167     static BOOL IsStackArgumentOffset(int offset)
168     {
169         LIMITED_METHOD_CONTRACT;
170
171 #if defined(UNIX_AMD64_ABI)
172         return offset >= sizeof(TransitionBlock);
173 #else        
174         int ofsArgRegs = GetOffsetOfArgumentRegisters();
175
176         return offset >= (int) (ofsArgRegs + ARGUMENTREGISTERS_SIZE);
177 #endif        
178     }
179
180     static BOOL IsArgumentRegisterOffset(int offset)
181     {
182         LIMITED_METHOD_CONTRACT;
183
184         int ofsArgRegs = GetOffsetOfArgumentRegisters();
185
186         return offset >= ofsArgRegs && offset < (int) (ofsArgRegs + ARGUMENTREGISTERS_SIZE);
187     }
188
189 #ifndef _TARGET_X86_
190     static UINT GetArgumentIndexFromOffset(int offset)
191     {
192         LIMITED_METHOD_CONTRACT;
193
194 #if defined(UNIX_AMD64_ABI)
195         _ASSERTE(offset != TransitionBlock::StructInRegsOffset);
196 #endif        
197         return (offset - GetOffsetOfArgumentRegisters()) / TARGET_POINTER_SIZE;
198     }
199
200     static UINT GetStackArgumentIndexFromOffset(int offset)
201     {
202         LIMITED_METHOD_CONTRACT;
203
204         return (offset - TransitionBlock::GetOffsetOfArgs()) / STACK_ELEM_SIZE;
205     }
206
207 #endif
208
209 #ifdef CALLDESCR_FPARGREGS
210     static BOOL IsFloatArgumentRegisterOffset(int offset)
211     {
212         LIMITED_METHOD_CONTRACT;
213 #if defined(UNIX_AMD64_ABI)
214         return (offset != TransitionBlock::StructInRegsOffset) && (offset < 0);
215 #else        
216         return offset < 0;
217 #endif        
218     }
219
220     // Check if an argument has floating point register, that means that it is
221     // either a floating point argument or a struct passed in registers that
222     // has a floating point member.
223     static BOOL HasFloatRegister(int offset, ArgLocDesc* argLocDescForStructInRegs)
224     {
225         LIMITED_METHOD_CONTRACT;
226     #if defined(UNIX_AMD64_ABI)
227         if (offset == TransitionBlock::StructInRegsOffset)
228         {
229             return argLocDescForStructInRegs->m_cFloatReg > 0;
230         }
231     #endif        
232         return offset < 0;
233     }
234
235     static int GetOffsetOfFloatArgumentRegisters()
236     {
237         LIMITED_METHOD_CONTRACT;
238         return -GetNegSpaceSize();
239     }
240 #endif // CALLDESCR_FPARGREGS
241
242     static int GetOffsetOfCalleeSavedRegisters()
243     {
244         LIMITED_METHOD_CONTRACT;
245         return offsetof(TransitionBlock, m_calleeSavedRegisters);
246     }
247
248     static int GetNegSpaceSize()
249     {
250         LIMITED_METHOD_CONTRACT;
251         int negSpaceSize = 0;
252 #ifdef CALLDESCR_FPARGREGS
253         negSpaceSize += sizeof(FloatArgumentRegisters);
254 #endif
255 #ifdef _TARGET_ARM_
256         negSpaceSize += TARGET_POINTER_SIZE; // padding to make FloatArgumentRegisters address 8-byte aligned
257 #endif
258         return negSpaceSize;
259     }
260
261     static const int InvalidOffset = -1;
262 #if defined(UNIX_AMD64_ABI)
263     // Special offset value to represent  struct passed in registers. Such a struct can span both
264     // general purpose and floating point registers, so it can have two different offsets.
265     static const int StructInRegsOffset = -2;
266 #endif    
267 };
268
269 //-----------------------------------------------------------------------
270 // ArgIterator is helper for dealing with calling conventions.
271 // It is tightly coupled with TransitionBlock. It uses offsets into
272 // TransitionBlock to represent argument locations for efficiency
273 // reasons. Alternatively, it can also return ArgLocDesc for less
274 // performance critical code.
275 //
276 // The ARGITERATOR_BASE argument of the template is provider of the parsed
277 // method signature. Typically, the arg iterator works on top of MetaSig. 
278 // Reflection invoke uses alternative implementation to save signature parsing
279 // time because of it has the parsed signature available.
280 //-----------------------------------------------------------------------
281 template<class ARGITERATOR_BASE>
282 class ArgIteratorTemplate : public ARGITERATOR_BASE
283 {
284 public:
285     //------------------------------------------------------------
286     // Constructor
287     //------------------------------------------------------------
288     ArgIteratorTemplate()
289     {
290         WRAPPER_NO_CONTRACT;
291         m_dwFlags = 0;
292     }
293
294     UINT SizeOfArgStack()
295     {
296         WRAPPER_NO_CONTRACT;
297         if (!(m_dwFlags & SIZE_OF_ARG_STACK_COMPUTED))
298             ForceSigWalk();
299         _ASSERTE((m_dwFlags & SIZE_OF_ARG_STACK_COMPUTED) != 0);
300         return m_nSizeOfArgStack;
301     }
302
303     // For use with ArgIterator. This function computes the amount of additional
304     // memory required above the TransitionBlock.  The parameter offsets
305     // returned by ArgIteratorTemplate::GetNextOffset are relative to a
306     // FramedMethodFrame, and may be in either of these regions.
307     UINT SizeOfFrameArgumentArray()
308     {
309         WRAPPER_NO_CONTRACT;
310
311         UINT size = SizeOfArgStack();
312
313 #if defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI)
314         // The argument registers are not included in the stack size on AMD64
315         size += ARGUMENTREGISTERS_SIZE;
316 #endif
317
318         return size;
319     }
320
321     //------------------------------------------------------------------------
322
323 #ifdef _TARGET_X86_
324     UINT CbStackPop()
325     {
326         WRAPPER_NO_CONTRACT;
327
328         if (this->IsVarArg())
329             return 0;
330         else
331             return SizeOfArgStack();
332     }
333 #endif
334
335     // Is there a hidden parameter for the return parameter? 
336     //
337     BOOL HasRetBuffArg()
338     {
339         WRAPPER_NO_CONTRACT;
340         if (!(m_dwFlags & RETURN_FLAGS_COMPUTED))
341             ComputeReturnFlags();
342         return (m_dwFlags & RETURN_HAS_RET_BUFFER);
343     }
344
345     UINT GetFPReturnSize()
346     {
347         WRAPPER_NO_CONTRACT;
348         if (!(m_dwFlags & RETURN_FLAGS_COMPUTED))
349             ComputeReturnFlags();
350         return m_dwFlags >> RETURN_FP_SIZE_SHIFT;
351     }
352
353 #ifdef _TARGET_X86_
354     //=========================================================================
355     // Indicates whether an argument is to be put in a register using the
356     // default IL calling convention. This should be called on each parameter
357     // in the order it appears in the call signature. For a non-static method,
358     // this function should also be called once for the "this" argument, prior
359     // to calling it for the "real" arguments. Pass in a typ of ELEMENT_TYPE_CLASS.
360     //
361     //  *pNumRegistersUsed:  [in,out]: keeps track of the number of argument
362     //                       registers assigned previously. The caller should
363     //                       initialize this variable to 0 - then each call
364     //                       will update it.
365     //
366     //  typ:                 the signature type
367     //=========================================================================
368     static BOOL IsArgumentInRegister(int * pNumRegistersUsed, CorElementType typ)
369     {
370         LIMITED_METHOD_CONTRACT;
371         if ( (*pNumRegistersUsed) < NUM_ARGUMENT_REGISTERS) {
372             if (gElementTypeInfo[typ].m_enregister) {
373                 (*pNumRegistersUsed)++;
374                 return(TRUE);
375             }
376         }
377
378         return(FALSE);
379     }
380 #endif // _TARGET_X86_
381
382 #if defined(ENREGISTERED_PARAMTYPE_MAXSIZE)
383
384     // Note that this overload does not handle varargs
385     static BOOL IsArgPassedByRef(TypeHandle th)
386     {
387         LIMITED_METHOD_CONTRACT;
388
389         _ASSERTE(!th.IsNull());
390
391         // This method only works for valuetypes. It includes true value types, 
392         // primitives, enums and TypedReference.
393         _ASSERTE(th.IsValueType());
394
395         size_t size = th.GetSize();
396 #ifdef _TARGET_AMD64_
397         return IsArgPassedByRef(size);
398 #elif defined(_TARGET_ARM64_)
399         // Composites greater than 16 bytes are passed by reference
400         return ((size > ENREGISTERED_PARAMTYPE_MAXSIZE) && !th.IsHFA());
401 #else
402         PORTABILITY_ASSERT("ArgIteratorTemplate::IsArgPassedByRef");
403         return FALSE;
404 #endif
405     }
406
407 #ifdef _TARGET_AMD64_
408     // This overload should only be used in AMD64-specific code only.
409     static BOOL IsArgPassedByRef(size_t size)
410     {
411         LIMITED_METHOD_CONTRACT;
412
413 #ifdef UNIX_AMD64_ABI
414         // No arguments are passed by reference on AMD64 on Unix
415         return FALSE;
416 #else
417         // If the size is bigger than ENREGISTERED_PARAM_TYPE_MAXSIZE, or if the size is NOT a power of 2, then
418         // the argument is passed by reference.
419         return (size > ENREGISTERED_PARAMTYPE_MAXSIZE) || ((size & (size-1)) != 0);
420 #endif        
421     }
422 #endif // _TARGET_AMD64_
423
424     // This overload should be used for varargs only.
425     static BOOL IsVarArgPassedByRef(size_t size)
426     {
427         LIMITED_METHOD_CONTRACT;
428
429 #ifdef _TARGET_AMD64_
430 #ifdef UNIX_AMD64_ABI
431         PORTABILITY_ASSERT("ArgIteratorTemplate::IsVarArgPassedByRef");                
432         return FALSE;
433 #else // UNIX_AMD64_ABI
434         return IsArgPassedByRef(size);
435 #endif // UNIX_AMD64_ABI
436
437 #else
438         return (size > ENREGISTERED_PARAMTYPE_MAXSIZE);
439 #endif
440     }
441
442     BOOL IsArgPassedByRef()
443     {
444         LIMITED_METHOD_CONTRACT;
445
446 #ifdef _TARGET_AMD64_
447         return IsArgPassedByRef(m_argSize);
448 #elif defined(_TARGET_ARM64_)
449         if (m_argType == ELEMENT_TYPE_VALUETYPE)
450         {
451             _ASSERTE(!m_argTypeHandle.IsNull());
452             return ((m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE) && (!m_argTypeHandle.IsHFA() || this->IsVarArg()));
453         }
454         return FALSE;
455 #else
456         PORTABILITY_ASSERT("ArgIteratorTemplate::IsArgPassedByRef");
457         return FALSE;
458 #endif
459     }
460
461 #endif // ENREGISTERED_PARAMTYPE_MAXSIZE
462
463     //------------------------------------------------------------
464     // Return the offsets of the special arguments
465     //------------------------------------------------------------
466
467     static int GetThisOffset();
468
469     int GetRetBuffArgOffset();
470     int GetVASigCookieOffset();
471     int GetParamTypeArgOffset();
472
473     //------------------------------------------------------------
474     // Each time this is called, this returns a byte offset of the next
475     // argument from the TransitionBlock* pointer.
476     //
477     // Returns TransitionBlock::InvalidOffset once you've hit the end 
478     // of the list.
479     //------------------------------------------------------------
480     int GetNextOffset();
481
482     CorElementType GetArgType(TypeHandle *pTypeHandle = NULL)
483     {
484         LIMITED_METHOD_CONTRACT;
485         if (pTypeHandle != NULL)
486         {
487             *pTypeHandle = m_argTypeHandle;
488         }
489         return m_argType;
490     }
491
492     int GetArgSize()
493     {
494         LIMITED_METHOD_CONTRACT;
495         return m_argSize;
496     }
497
498     void ForceSigWalk();
499
500 #ifndef _TARGET_X86_
501     // Accessors for built in argument descriptions of the special implicit parameters not mentioned directly
502     // in signatures (this pointer and the like). Whether or not these can be used successfully before all the
503     // explicit arguments have been scanned is platform dependent.
504     void GetThisLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetThisOffset(), pLoc); }
505     void GetParamTypeLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetParamTypeArgOffset(), pLoc); }
506     void GetVASigCookieLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetVASigCookieOffset(), pLoc); }
507
508 #ifndef CALLDESCR_RETBUFFARGREG
509     void GetRetBuffArgLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetRetBuffArgOffset(), pLoc); }
510 #endif
511
512 #endif // !_TARGET_X86_
513
514     ArgLocDesc* GetArgLocDescForStructInRegs()
515     {
516 #if defined(UNIX_AMD64_ABI) || defined (_TARGET_ARM64_)
517         return m_hasArgLocDescForStructInRegs ? &m_argLocDescForStructInRegs : NULL;
518 #else
519         return NULL;
520 #endif
521     }
522
523 #ifdef _TARGET_ARM_
524     // Get layout information for the argument that the ArgIterator is currently visiting.
525     void GetArgLoc(int argOffset, ArgLocDesc *pLoc)
526     {
527         LIMITED_METHOD_CONTRACT;
528
529         pLoc->Init();
530
531         pLoc->m_fRequires64BitAlignment = m_fRequires64BitAlignment;
532
533         int cSlots = (GetArgSize() + 3) / 4;
534
535         if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset))
536         {
537             pLoc->m_idxFloatReg = (argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters()) / 4;
538             pLoc->m_cFloatReg = cSlots;
539             return;
540         }
541
542         if (!TransitionBlock::IsStackArgumentOffset(argOffset))
543         {
544             pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset);
545
546             if (cSlots <= (4 - pLoc->m_idxGenReg))
547             {
548                 pLoc->m_cGenReg = cSlots;
549             }
550             else
551             {
552                 pLoc->m_cGenReg = 4 - pLoc->m_idxGenReg;
553
554                 pLoc->m_idxStack = 0;
555                 pLoc->m_cStack = cSlots - pLoc->m_cGenReg;
556             }
557         }
558         else
559         {
560             pLoc->m_idxStack = TransitionBlock::GetStackArgumentIndexFromOffset(argOffset);
561             pLoc->m_cStack = cSlots;
562         }
563     }
564 #endif // _TARGET_ARM_
565
566 #ifdef _TARGET_ARM64_
567     // Get layout information for the argument that the ArgIterator is currently visiting.
568     void GetArgLoc(int argOffset, ArgLocDesc *pLoc)
569     {
570         LIMITED_METHOD_CONTRACT;
571
572         pLoc->Init();
573
574         if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset))
575         {
576             // Dividing by 8 as size of each register in FloatArgumentRegisters is 8 bytes.
577             pLoc->m_idxFloatReg = (argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters()) / 8;
578
579             if (!m_argTypeHandle.IsNull() && m_argTypeHandle.IsHFA())
580             {
581                 CorElementType type = m_argTypeHandle.GetHFAType();
582                 bool isFloatType = (type == ELEMENT_TYPE_R4);
583
584                 pLoc->m_cFloatReg = isFloatType ? GetArgSize()/sizeof(float): GetArgSize()/sizeof(double);
585                 pLoc->m_isSinglePrecision = isFloatType;
586             }
587             else
588             {
589                 pLoc->m_cFloatReg = 1;
590             }
591             return;
592         }
593
594         int cSlots = (GetArgSize() + 7)/ 8;
595
596         // Composites greater than 16bytes are passed by reference
597         if (GetArgType() == ELEMENT_TYPE_VALUETYPE && GetArgSize() > ENREGISTERED_PARAMTYPE_MAXSIZE)
598         {
599             cSlots = 1;
600         }
601
602 #ifdef _TARGET_ARM64_
603         // Sanity check to make sure no caller is trying to get an ArgLocDesc that
604         // describes the return buffer reg field that's in the TransitionBlock.
605         _ASSERTE(argOffset != TransitionBlock::GetOffsetOfRetBuffArgReg());
606 #endif
607
608         if (!TransitionBlock::IsStackArgumentOffset(argOffset))
609         {
610             pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset);
611             pLoc->m_cGenReg = cSlots;
612         }
613         else
614         {
615             pLoc->m_idxStack = TransitionBlock::GetStackArgumentIndexFromOffset(argOffset);
616             pLoc->m_cStack = cSlots;
617         }
618     }
619 #endif // _TARGET_ARM64_
620
621 #if defined(_TARGET_AMD64_) && defined(UNIX_AMD64_ABI)
622     // Get layout information for the argument that the ArgIterator is currently visiting.
623     void GetArgLoc(int argOffset, ArgLocDesc* pLoc)
624     {
625         LIMITED_METHOD_CONTRACT;
626
627 #if defined(UNIX_AMD64_ABI)
628         if (m_hasArgLocDescForStructInRegs)
629         {
630             *pLoc = m_argLocDescForStructInRegs;
631             return;
632         }
633 #endif // UNIX_AMD64_ABI
634
635         if (argOffset == TransitionBlock::StructInRegsOffset)
636         {
637             // We always already have argLocDesc for structs passed in registers, we 
638             // compute it in the GetNextOffset for those since it is always needed.
639             _ASSERTE(false);
640             return;
641         }
642
643         pLoc->Init();
644
645         if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset))
646         {
647             // Dividing by 16 as size of each register in FloatArgumentRegisters is 16 bytes.
648             pLoc->m_idxFloatReg = (argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters()) / 16;
649             pLoc->m_cFloatReg = 1;
650         }
651         else if (!TransitionBlock::IsStackArgumentOffset(argOffset))
652         {
653             pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset);
654             pLoc->m_cGenReg = 1;
655         }
656         else
657         {
658             pLoc->m_idxStack = TransitionBlock::GetStackArgumentIndexFromOffset(argOffset);
659             pLoc->m_cStack = (GetArgSize() + STACK_ELEM_SIZE - 1) / STACK_ELEM_SIZE;
660         }
661     }
662 #endif // _TARGET_AMD64_ && UNIX_AMD64_ABI
663
664 protected:
665     DWORD               m_dwFlags;              // Cached flags
666     int                 m_nSizeOfArgStack;      // Cached value of SizeOfArgStack
667
668     DWORD               m_argNum;
669
670     // Cached information about last argument
671     CorElementType      m_argType;
672     int                 m_argSize;
673     TypeHandle          m_argTypeHandle;
674 #if (defined(_TARGET_AMD64_) && defined(UNIX_AMD64_ABI)) || defined(_TARGET_ARM64_)
675     ArgLocDesc          m_argLocDescForStructInRegs;
676     bool                m_hasArgLocDescForStructInRegs;
677 #endif // (_TARGET_AMD64_ && UNIX_AMD64_ABI) || _TARGET_ARM64_
678
679 #ifdef _TARGET_X86_
680     int                 m_curOfs;           // Current position of the stack iterator
681     int                 m_numRegistersUsed;
682 #endif
683
684 #ifdef _TARGET_AMD64_
685 #ifdef UNIX_AMD64_ABI
686     int                 m_idxGenReg;        // Next general register to be assigned a value
687     int                 m_idxStack;         // Next stack slot to be assigned a value
688     int                 m_idxFPReg;         // Next floating point register to be assigned a value
689     bool                m_fArgInRegisters;  // Indicates that the current argument is stored in registers
690 #else
691     int                 m_curOfs;           // Current position of the stack iterator
692 #endif
693 #endif
694
695 #ifdef _TARGET_ARM_
696     int                 m_idxGenReg;        // Next general register to be assigned a value
697     int                 m_idxStack;         // Next stack slot to be assigned a value
698
699     WORD                m_wFPRegs;          // Bitmask of available floating point argument registers (s0-s15/d0-d7)
700     bool                m_fRequires64BitAlignment; // Cached info about the current arg
701 #endif
702
703 #ifdef _TARGET_ARM64_
704     int             m_idxGenReg;        // Next general register to be assigned a value
705     int             m_idxStack;         // Next stack slot to be assigned a value
706     int             m_idxFPReg;         // Next FP register to be assigned a value
707 #endif
708
709     enum {
710         ITERATION_STARTED               = 0x0001,   // Started iterating over arguments
711         SIZE_OF_ARG_STACK_COMPUTED      = 0x0002,
712         RETURN_FLAGS_COMPUTED           = 0x0004,
713         RETURN_HAS_RET_BUFFER           = 0x0008,   // Cached value of HasRetBuffArg
714
715 #ifdef _TARGET_X86_
716         PARAM_TYPE_REGISTER_MASK        = 0x0030,
717         PARAM_TYPE_REGISTER_STACK       = 0x0010,
718         PARAM_TYPE_REGISTER_ECX         = 0x0020,
719         PARAM_TYPE_REGISTER_EDX         = 0x0030,
720 #endif
721
722         METHOD_INVOKE_NEEDS_ACTIVATION  = 0x0040,   // Flag used by ArgIteratorForMethodInvoke
723
724         RETURN_FP_SIZE_SHIFT            = 8,        // The rest of the flags is cached value of GetFPReturnSize
725     };
726
727     void ComputeReturnFlags();
728
729 #ifndef _TARGET_X86_
730     void GetSimpleLoc(int offset, ArgLocDesc * pLoc)
731     { 
732         WRAPPER_NO_CONTRACT; 
733
734 #ifdef CALLDESCR_RETBUFFARGREG
735         // Codepaths where this could happen have been removed. If this occurs, something
736         // has been missed and this needs another look.
737         _ASSERTE(offset != TransitionBlock::GetOffsetOfRetBuffArgReg());
738 #endif
739
740         pLoc->Init();
741         pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(offset);
742         pLoc->m_cGenReg = 1;
743     }
744 #endif
745 };
746
747
748 template<class ARGITERATOR_BASE>
749 int ArgIteratorTemplate<ARGITERATOR_BASE>::GetThisOffset()
750 {
751     WRAPPER_NO_CONTRACT;
752
753     // This pointer is in the first argument register by default
754     int ret = TransitionBlock::GetOffsetOfArgumentRegisters();
755
756 #ifdef _TARGET_X86_
757     // x86 is special as always
758     ret += offsetof(ArgumentRegisters, ECX);
759 #endif
760
761     return ret;
762 }
763
764 template<class ARGITERATOR_BASE>
765 int ArgIteratorTemplate<ARGITERATOR_BASE>::GetRetBuffArgOffset()
766 {
767     WRAPPER_NO_CONTRACT;
768
769     _ASSERTE(this->HasRetBuffArg());
770
771     // RetBuf arg is in the second argument register by default
772     int ret = TransitionBlock::GetOffsetOfArgumentRegisters();
773
774 #if _TARGET_X86_
775     // x86 is special as always
776     ret += this->HasThis() ? offsetof(ArgumentRegisters, EDX) : offsetof(ArgumentRegisters, ECX);
777 #elif _TARGET_ARM64_
778     ret = TransitionBlock::GetOffsetOfRetBuffArgReg();
779 #else
780     if (this->HasThis())
781         ret += TARGET_POINTER_SIZE;
782 #endif
783
784     return ret;
785 }
786
787 template<class ARGITERATOR_BASE>
788 int ArgIteratorTemplate<ARGITERATOR_BASE>::GetVASigCookieOffset()
789 {
790     WRAPPER_NO_CONTRACT;
791
792     _ASSERTE(this->IsVarArg());
793
794 #if defined(_TARGET_X86_)
795     // x86 is special as always
796     return sizeof(TransitionBlock);
797 #else
798     // VaSig cookie is after this and retbuf arguments by default.
799     int ret = TransitionBlock::GetOffsetOfArgumentRegisters();
800
801     if (this->HasThis())
802     {
803         ret += TARGET_POINTER_SIZE;
804     }
805
806     if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
807     {
808         ret += TARGET_POINTER_SIZE;
809     }
810
811     return ret;
812 #endif
813 }
814
815 //-----------------------------------------------------------
816 // Get the extra param offset for shared generic code
817 //-----------------------------------------------------------
818 template<class ARGITERATOR_BASE>
819 int ArgIteratorTemplate<ARGITERATOR_BASE>::GetParamTypeArgOffset()
820 {
821     CONTRACTL
822     {
823         INSTANCE_CHECK;
824         if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
825         if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
826         if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
827         MODE_ANY;
828     }
829     CONTRACTL_END
830
831     _ASSERTE(this->HasParamType());
832
833 #ifdef _TARGET_X86_
834     // x86 is special as always
835     if (!(m_dwFlags & SIZE_OF_ARG_STACK_COMPUTED))
836         ForceSigWalk();
837
838     switch (m_dwFlags & PARAM_TYPE_REGISTER_MASK)
839     {
840     case PARAM_TYPE_REGISTER_ECX:
841         return TransitionBlock::GetOffsetOfArgumentRegisters() + offsetof(ArgumentRegisters, ECX);
842     case PARAM_TYPE_REGISTER_EDX:
843         return TransitionBlock::GetOffsetOfArgumentRegisters() + offsetof(ArgumentRegisters, EDX);
844     default:
845         break;
846     }
847
848     // The param type arg is last stack argument otherwise
849     return sizeof(TransitionBlock);
850 #else
851     // The hidden arg is after this and retbuf arguments by default.
852     int ret = TransitionBlock::GetOffsetOfArgumentRegisters();
853
854     if (this->HasThis())
855     {
856         ret += TARGET_POINTER_SIZE;
857     }
858
859     if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
860     {
861         ret += TARGET_POINTER_SIZE;
862     }
863
864     return ret;
865 #endif
866 }
867
868 // To avoid corner case bugs, limit maximum size of the arguments with sufficient margin
869 #define MAX_ARG_SIZE 0xFFFFFF
870
871 //------------------------------------------------------------
872 // Each time this is called, this returns a byte offset of the next
873 // argument from the Frame* pointer. This offset can be positive *or* negative.
874 //
875 // Returns TransitionBlock::InvalidOffset once you've hit the end of the list.
876 //------------------------------------------------------------
877 template<class ARGITERATOR_BASE>
878 int ArgIteratorTemplate<ARGITERATOR_BASE>::GetNextOffset()
879 {
880     WRAPPER_NO_CONTRACT;
881     SUPPORTS_DAC;
882
883     if (!(m_dwFlags & ITERATION_STARTED))
884     {
885         int numRegistersUsed = 0;
886
887         if (this->HasThis())
888             numRegistersUsed++;
889
890         if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
891             numRegistersUsed++;
892
893         _ASSERTE(!this->IsVarArg() || !this->HasParamType());
894
895 #ifndef _TARGET_X86_
896         if (this->IsVarArg() || this->HasParamType())
897         {
898             numRegistersUsed++;
899         }
900 #endif
901
902 #ifdef _TARGET_X86_
903         if (this->IsVarArg())
904         {
905             numRegistersUsed = NUM_ARGUMENT_REGISTERS; // Nothing else gets passed in registers for varargs
906         }
907
908 #ifdef FEATURE_INTERPRETER
909         BYTE callconv = CallConv();
910         switch (callconv)
911         {
912         case IMAGE_CEE_CS_CALLCONV_C:
913         case IMAGE_CEE_CS_CALLCONV_STDCALL:
914             m_numRegistersUsed = NUM_ARGUMENT_REGISTERS;
915             m_curOfs = TransitionBlock::GetOffsetOfArgs() + numRegistersUsed * sizeof(void *); 
916             m_fUnmanagedCallConv = true;
917             break;
918
919         case IMAGE_CEE_CS_CALLCONV_THISCALL:
920         case IMAGE_CEE_CS_CALLCONV_FASTCALL:
921             _ASSERTE_MSG(false, "Unsupported calling convention.");
922
923         default:
924             m_fUnmanagedCallConv = false;
925             m_numRegistersUsed = numRegistersUsed;
926             m_curOfs = TransitionBlock::GetOffsetOfArgs() + SizeOfArgStack();
927         }
928 #else
929         m_numRegistersUsed = numRegistersUsed;
930         m_curOfs = TransitionBlock::GetOffsetOfArgs() + SizeOfArgStack();
931 #endif
932
933 #elif defined(_TARGET_AMD64_)
934 #ifdef UNIX_AMD64_ABI
935         m_idxGenReg = numRegistersUsed;
936         m_idxStack = 0;
937         m_idxFPReg = 0;
938 #else
939         m_curOfs = TransitionBlock::GetOffsetOfArgs() + numRegistersUsed * sizeof(void *);
940 #endif
941 #elif defined(_TARGET_ARM_)
942         m_idxGenReg = numRegistersUsed;
943         m_idxStack = 0;
944
945         m_wFPRegs = 0;
946 #elif defined(_TARGET_ARM64_)
947         m_idxGenReg = numRegistersUsed;
948         m_idxStack = 0;
949
950         m_idxFPReg = 0;
951 #else
952         PORTABILITY_ASSERT("ArgIteratorTemplate::GetNextOffset");
953 #endif
954
955         m_argNum = 0;
956
957         m_dwFlags |= ITERATION_STARTED;
958     }
959
960     // We're done going through the args for this MetaSig
961     if (m_argNum == this->NumFixedArgs())
962         return TransitionBlock::InvalidOffset;
963
964     TypeHandle thValueType;
965     CorElementType argType = this->GetNextArgumentType(m_argNum++, &thValueType);
966
967     int argSize = MetaSig::GetElemSize(argType, thValueType);
968
969     m_argType = argType;
970     m_argSize = argSize;
971     m_argTypeHandle = thValueType;
972
973 #if defined(UNIX_AMD64_ABI)
974     m_hasArgLocDescForStructInRegs = false;
975 #endif
976
977 #ifdef _TARGET_X86_
978 #ifdef FEATURE_INTERPRETER
979     if (m_fUnmanagedCallConv)
980     {
981         int argOfs = m_curOfs;
982         m_curOfs += StackElemSize(argSize);
983         return argOfs;
984     }
985 #endif
986     if (IsArgumentInRegister(&m_numRegistersUsed, argType))
987     {
988         return TransitionBlock::GetOffsetOfArgumentRegisters() + (NUM_ARGUMENT_REGISTERS - m_numRegistersUsed) * sizeof(void *);
989     }
990
991     m_curOfs -= StackElemSize(argSize);
992     _ASSERTE(m_curOfs >= TransitionBlock::GetOffsetOfArgs());
993     return m_curOfs;
994 #elif defined(_TARGET_AMD64_)
995 #ifdef UNIX_AMD64_ABI
996
997     m_fArgInRegisters = true;
998
999     int cFPRegs = 0;
1000     int cGenRegs = 0;
1001     int cbArg = StackElemSize(argSize);
1002
1003     switch (argType)
1004     {
1005
1006     case ELEMENT_TYPE_R4:
1007         // 32-bit floating point argument.
1008         cFPRegs = 1;
1009         break;
1010
1011     case ELEMENT_TYPE_R8:
1012         // 64-bit floating point argument.
1013         cFPRegs = 1;
1014         break;
1015
1016     case ELEMENT_TYPE_VALUETYPE:
1017     {
1018 #ifdef UNIX_AMD64_ABI
1019         MethodTable *pMT = m_argTypeHandle.AsMethodTable();
1020         if (pMT->IsRegPassedStruct())
1021         {
1022             EEClass* eeClass = pMT->GetClass();
1023             cGenRegs = 0;
1024             for (int i = 0; i < eeClass->GetNumberEightBytes(); i++)
1025             {
1026                 switch (eeClass->GetEightByteClassification(i))
1027                 {
1028                     case SystemVClassificationTypeInteger:
1029                     case SystemVClassificationTypeIntegerReference:
1030                     case SystemVClassificationTypeIntegerByRef:
1031                         cGenRegs++;
1032                         break;
1033                     case SystemVClassificationTypeSSE:
1034                         cFPRegs++;
1035                         break;
1036                     default:
1037                         _ASSERTE(false);
1038                         break;
1039                 }
1040             }
1041
1042             // Check if we have enough registers available for the struct passing
1043             if ((cFPRegs + m_idxFPReg <= NUM_FLOAT_ARGUMENT_REGISTERS) && (cGenRegs + m_idxGenReg) <= NUM_ARGUMENT_REGISTERS)
1044             {
1045                 m_argLocDescForStructInRegs.Init();
1046                 m_argLocDescForStructInRegs.m_cGenReg = cGenRegs;
1047                 m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs;
1048                 m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg;
1049                 m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
1050                 m_argLocDescForStructInRegs.m_eeClass = eeClass;
1051                 
1052                 m_hasArgLocDescForStructInRegs = true;
1053
1054                 m_idxGenReg += cGenRegs;
1055                 m_idxFPReg += cFPRegs;
1056
1057                 return TransitionBlock::StructInRegsOffset;
1058             }
1059         }
1060
1061         // Set the register counts to indicate that this argument will not be passed in registers
1062         cFPRegs = 0;
1063         cGenRegs = 0;
1064
1065 #else // UNIX_AMD64_ABI
1066         argSize = sizeof(TADDR);        
1067 #endif // UNIX_AMD64_ABI
1068
1069         break;
1070     }
1071
1072     default:
1073         cGenRegs = cbArg / 8; // GP reg size
1074         break;
1075     }
1076
1077     if ((cFPRegs > 0) && (cFPRegs + m_idxFPReg <= NUM_FLOAT_ARGUMENT_REGISTERS))
1078     {
1079         int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 16;
1080         m_idxFPReg += cFPRegs;
1081         return argOfs;
1082     }
1083     else if ((cGenRegs > 0) && (m_idxGenReg + cGenRegs <= NUM_ARGUMENT_REGISTERS))
1084     {
1085         int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1086         m_idxGenReg += cGenRegs;
1087         return argOfs;
1088     }
1089
1090 #if defined(UNIX_AMD64_ABI)
1091     m_fArgInRegisters = false;
1092 #endif        
1093
1094     int argOfs = TransitionBlock::GetOffsetOfArgs() + m_idxStack * STACK_ELEM_SIZE;
1095
1096     int cArgSlots = cbArg / STACK_ELEM_SIZE;
1097     m_idxStack += cArgSlots;
1098
1099     return argOfs;
1100 #else
1101     // Each argument takes exactly one slot on AMD64 on Windows
1102     int argOfs = m_curOfs;
1103     m_curOfs += sizeof(void *);
1104     return argOfs;
1105 #endif
1106 #elif defined(_TARGET_ARM_)
1107     // First look at the underlying type of the argument to determine some basic properties:
1108     //  1) The size of the argument in bytes (rounded up to the stack slot size of 4 if necessary).
1109     //  2) Whether the argument represents a floating point primitive (ELEMENT_TYPE_R4 or ELEMENT_TYPE_R8).
1110     //  3) Whether the argument requires 64-bit alignment (anything that contains a Int64/UInt64).
1111
1112     bool fFloatingPoint = false;
1113     bool fRequiresAlign64Bit = false;
1114
1115     switch (argType)
1116     {
1117     case ELEMENT_TYPE_I8:
1118     case ELEMENT_TYPE_U8:
1119         // 64-bit integers require 64-bit alignment on ARM.
1120         fRequiresAlign64Bit = true;
1121         break;
1122
1123     case ELEMENT_TYPE_R4:
1124         // 32-bit floating point argument.
1125         fFloatingPoint = true;
1126         break;
1127
1128     case ELEMENT_TYPE_R8:
1129         // 64-bit floating point argument.
1130         fFloatingPoint = true;
1131         fRequiresAlign64Bit = true;
1132         break;
1133
1134     case ELEMENT_TYPE_VALUETYPE:
1135     {
1136         // Value type case: extract the alignment requirement, note that this has to handle 
1137         // the interop "native value types".
1138         fRequiresAlign64Bit = thValueType.RequiresAlign8();
1139
1140 #ifdef FEATURE_HFA
1141         // Handle HFAs: packed structures of 1-4 floats or doubles that are passed in FP argument
1142         // registers if possible.
1143         if (thValueType.IsHFA())
1144         {
1145             fFloatingPoint = true;
1146         }
1147 #endif
1148
1149         break;
1150     }
1151
1152     default:
1153         // The default is are 4-byte arguments (or promoted to 4 bytes), non-FP and don't require any
1154         // 64-bit alignment.
1155         break;
1156     }
1157
1158     // Now attempt to place the argument into some combination of floating point or general registers and
1159     // the stack.
1160
1161     // Save the alignment requirement
1162     m_fRequires64BitAlignment = fRequiresAlign64Bit;
1163
1164     int cbArg = StackElemSize(argSize);
1165     int cArgSlots = cbArg / 4;
1166
1167     // Ignore floating point argument placement in registers if we're dealing with a vararg function (the ABI
1168     // specifies this so that vararg processing on the callee side is simplified).
1169 #ifndef ARM_SOFTFP
1170     if (fFloatingPoint && !this->IsVarArg())
1171     {
1172         // Handle floating point (primitive) arguments.
1173
1174         // First determine whether we can place the argument in VFP registers. There are 16 32-bit
1175         // and 8 64-bit argument registers that share the same register space (e.g. D0 overlaps S0 and
1176         // S1). The ABI specifies that VFP values will be passed in the lowest sequence of registers that
1177         // haven't been used yet and have the required alignment. So the sequence (float, double, float)
1178         // would be mapped to (S0, D1, S1) or (S0, S2/S3, S1).
1179         //
1180         // We use a 16-bit bitmap to record which registers have been used so far.
1181         //
1182         // So we can use the same basic loop for each argument type (float, double or HFA struct) we set up
1183         // the following input parameters based on the size and alignment requirements of the arguments:
1184         //   wAllocMask : bitmask of the number of 32-bit registers we need (1 for 1, 3 for 2, 7 for 3 etc.)
1185         //   cSteps     : number of loop iterations it'll take to search the 16 registers
1186         //   cShift     : how many bits to shift the allocation mask on each attempt
1187
1188         WORD wAllocMask = (1 << (cbArg / 4)) - 1;
1189         WORD cSteps = (WORD)(fRequiresAlign64Bit ? 9 - (cbArg / 8) : 17 - (cbArg / 4));
1190         WORD cShift = fRequiresAlign64Bit ? 2 : 1;
1191
1192         // Look through the availability bitmask for a free register or register pair.
1193         for (WORD i = 0; i < cSteps; i++)
1194         {
1195             if ((m_wFPRegs & wAllocMask) == 0)
1196             {
1197                 // We found one, mark the register or registers as used. 
1198                 m_wFPRegs |= wAllocMask;
1199
1200                 // Indicate the registers used to the caller and return.
1201                 return TransitionBlock::GetOffsetOfFloatArgumentRegisters() + (i * cShift * 4);
1202             }
1203             wAllocMask <<= cShift;
1204         }
1205
1206         // The FP argument is going to live on the stack. Once this happens the ABI demands we mark all FP
1207         // registers as unavailable.
1208         m_wFPRegs = 0xffff;
1209
1210         // Doubles or HFAs containing doubles need the stack aligned appropriately.
1211         if (fRequiresAlign64Bit)
1212             m_idxStack = (int)ALIGN_UP(m_idxStack, 2);
1213
1214         // Indicate the stack location of the argument to the caller.
1215         int argOfs = TransitionBlock::GetOffsetOfArgs() + m_idxStack * 4;
1216
1217         // Record the stack usage.
1218         m_idxStack += cArgSlots;
1219
1220         return argOfs;
1221     }
1222 #endif // ARM_SOFTFP
1223
1224     //
1225     // Handle the non-floating point case.
1226     //
1227
1228     if (m_idxGenReg < 4)
1229     {
1230         if (fRequiresAlign64Bit)
1231         {
1232             // The argument requires 64-bit alignment. Align either the next general argument register if
1233             // we have any left.  See step C.3 in the algorithm in the ABI spec.       
1234             m_idxGenReg = (int)ALIGN_UP(m_idxGenReg, 2);
1235         }
1236
1237         int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 4;
1238
1239         int cRemainingRegs = 4 - m_idxGenReg;
1240         if (cArgSlots <= cRemainingRegs)
1241         {
1242             // Mark the registers just allocated as used.
1243             m_idxGenReg += cArgSlots;
1244             return argOfs;
1245         }
1246
1247         // The ABI supports splitting a non-FP argument across registers and the stack. But this is
1248         // disabled if the FP arguments already overflowed onto the stack (i.e. the stack index is not
1249         // zero). The following code marks the general argument registers as exhausted if this condition
1250         // holds.  See steps C.5 in the algorithm in the ABI spec.
1251
1252         m_idxGenReg = 4;
1253
1254         if (m_idxStack == 0)
1255         {
1256             m_idxStack += cArgSlots - cRemainingRegs;
1257             return argOfs;
1258         }
1259     }
1260
1261     if (fRequiresAlign64Bit)
1262     {
1263         // The argument requires 64-bit alignment. If it is going to be passed on the stack, align
1264         // the next stack slot.  See step C.6 in the algorithm in the ABI spec.  
1265         m_idxStack = (int)ALIGN_UP(m_idxStack, 2);
1266     }
1267
1268     int argOfs = TransitionBlock::GetOffsetOfArgs() + m_idxStack * 4;
1269
1270     // Advance the stack pointer over the argument just placed.
1271     m_idxStack += cArgSlots;
1272
1273     return argOfs;
1274 #elif defined(_TARGET_ARM64_)
1275
1276     int cFPRegs = 0;
1277
1278     switch (argType)
1279     {
1280
1281     case ELEMENT_TYPE_R4:
1282         // 32-bit floating point argument.
1283         cFPRegs = 1;
1284         break;
1285
1286     case ELEMENT_TYPE_R8:
1287         // 64-bit floating point argument.
1288         cFPRegs = 1;
1289         break;
1290
1291     case ELEMENT_TYPE_VALUETYPE:
1292     {
1293         // Handle HFAs: packed structures of 2-4 floats or doubles that are passed in FP argument
1294         // registers if possible.
1295         if (thValueType.IsHFA())
1296         {
1297             CorElementType type = thValueType.GetHFAType();
1298             bool isFloatType = (type == ELEMENT_TYPE_R4);
1299
1300             cFPRegs = (type == ELEMENT_TYPE_R4)? (argSize/sizeof(float)): (argSize/sizeof(double));
1301
1302             m_argLocDescForStructInRegs.Init();
1303             m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs;
1304             m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
1305
1306             m_argLocDescForStructInRegs.m_isSinglePrecision = isFloatType;
1307                 
1308             m_hasArgLocDescForStructInRegs = true;
1309         }
1310         else 
1311         {
1312             // Composite greater than 16bytes should be passed by reference
1313             if (argSize > ENREGISTERED_PARAMTYPE_MAXSIZE)
1314             {
1315                 argSize = sizeof(TADDR);
1316             }
1317         }
1318
1319         break;
1320     }
1321
1322     default:
1323         break;
1324     }
1325
1326     int cbArg = StackElemSize(argSize);
1327     int cArgSlots = cbArg / STACK_ELEM_SIZE;
1328
1329     if (cFPRegs>0 && !this->IsVarArg())
1330     {
1331         if (cFPRegs + m_idxFPReg <= 8)
1332         {
1333             int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
1334             m_idxFPReg += cFPRegs;
1335             return argOfs;
1336         }
1337         else
1338         {
1339             m_idxFPReg = 8;
1340         }
1341     }
1342     else
1343     {
1344         // Only x0-x7 are valid argument registers (x8 is always the return buffer)
1345         if (m_idxGenReg + cArgSlots <= 8)
1346         {
1347             // The entirety of the arg fits in the register slots.
1348
1349             int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1350             m_idxGenReg += cArgSlots;
1351             return argOfs;
1352         }
1353         else
1354         {
1355 #ifdef _WIN32
1356             if (this->IsVarArg() && m_idxGenReg < 8)
1357             {
1358                 // Address the Windows ARM64 varargs case where an arg is split between regs and stack.
1359                 // This can happen in the varargs case because the first 64 bytes of the stack are loaded
1360                 // into x0-x7, and any remaining stack arguments are placed normally.
1361                 int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1362
1363                 // Increase m_idxStack to account for the space used for the remainder of the arg after
1364                 // register slots are filled.
1365                 m_idxStack += (m_idxGenReg + cArgSlots - 8);
1366
1367                 // We used up the remaining reg slots.
1368                 m_idxGenReg = 8; 
1369
1370                 return argOfs;
1371             }
1372             else
1373 #endif
1374             {
1375                 // Don't use reg slots for this. It will be passed purely on the stack arg space.
1376                 m_idxGenReg = 8;
1377             }
1378         }
1379     }
1380
1381     int argOfs = TransitionBlock::GetOffsetOfArgs() + m_idxStack * 8;
1382     m_idxStack += cArgSlots;
1383     return argOfs;
1384 #else
1385     PORTABILITY_ASSERT("ArgIteratorTemplate::GetNextOffset");
1386     return TransitionBlock::InvalidOffset;
1387 #endif
1388 }
1389
1390 template<class ARGITERATOR_BASE>
1391 void ArgIteratorTemplate<ARGITERATOR_BASE>::ComputeReturnFlags()
1392 {
1393     CONTRACTL
1394     {
1395         INSTANCE_CHECK;
1396         if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
1397         if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
1398         if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
1399         MODE_ANY;
1400     }
1401     CONTRACTL_END
1402
1403     TypeHandle thValueType;
1404     CorElementType type = this->GetReturnType(&thValueType);
1405
1406     DWORD flags = RETURN_FLAGS_COMPUTED;
1407     switch (type)
1408     {
1409     case ELEMENT_TYPE_TYPEDBYREF:
1410 #ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
1411         if (sizeof(TypedByRef) > ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
1412             flags |= RETURN_HAS_RET_BUFFER;
1413 #else
1414         flags |= RETURN_HAS_RET_BUFFER;
1415 #endif
1416         break;
1417
1418     case ELEMENT_TYPE_R4:
1419 #ifndef ARM_SOFTFP
1420         flags |= sizeof(float) << RETURN_FP_SIZE_SHIFT;
1421 #endif
1422         break;
1423
1424     case ELEMENT_TYPE_R8:
1425 #ifndef ARM_SOFTFP
1426         flags |= sizeof(double) << RETURN_FP_SIZE_SHIFT;
1427 #endif
1428         break;
1429
1430     case ELEMENT_TYPE_VALUETYPE:
1431 #ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
1432         {
1433             _ASSERTE(!thValueType.IsNull());
1434
1435 #if defined(UNIX_AMD64_ABI)
1436             MethodTable *pMT = thValueType.AsMethodTable();
1437             if (pMT->IsRegPassedStruct())
1438             {
1439                 EEClass* eeClass = pMT->GetClass();
1440
1441                 if (eeClass->GetNumberEightBytes() == 1)
1442                 {
1443                     // Structs occupying just one eightbyte are treated as int / double
1444                     if (eeClass->GetEightByteClassification(0) == SystemVClassificationTypeSSE)
1445                     {
1446                         flags |= sizeof(double) << RETURN_FP_SIZE_SHIFT;
1447                     }
1448                 }
1449                 else
1450                 {
1451                     // Size of the struct is 16 bytes
1452                     flags |= (16 << RETURN_FP_SIZE_SHIFT);
1453                     // The lowest two bits of the size encode the order of the int and SSE fields
1454                     if (eeClass->GetEightByteClassification(0) == SystemVClassificationTypeSSE)
1455                     {
1456                         flags |= (1 << RETURN_FP_SIZE_SHIFT);
1457                     }
1458
1459                     if (eeClass->GetEightByteClassification(1) == SystemVClassificationTypeSSE)
1460                     {
1461                         flags |= (2 << RETURN_FP_SIZE_SHIFT);                    
1462                     }
1463                 }
1464
1465                 break;
1466             }
1467 #else // UNIX_AMD64_ABI
1468
1469 #ifdef FEATURE_HFA
1470             if (thValueType.IsHFA() && !this->IsVarArg())
1471             {
1472                 CorElementType hfaType = thValueType.GetHFAType();
1473
1474                 flags |= (hfaType == ELEMENT_TYPE_R4) ? 
1475                     ((4 * sizeof(float)) << RETURN_FP_SIZE_SHIFT) : 
1476                     ((4 * sizeof(double)) << RETURN_FP_SIZE_SHIFT);
1477
1478                 break;
1479             }
1480 #endif
1481
1482             size_t size = thValueType.GetSize();
1483
1484 #if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
1485             // Return value types of size which are not powers of 2 using a RetBuffArg
1486             if ((size & (size-1)) != 0)
1487             {
1488                 flags |= RETURN_HAS_RET_BUFFER;
1489                 break;
1490             }
1491 #endif
1492
1493             if  (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
1494                 break;
1495 #endif // UNIX_AMD64_ABI
1496         }
1497 #endif // ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
1498
1499         // Value types are returned using return buffer by default
1500         flags |= RETURN_HAS_RET_BUFFER;
1501         break;
1502
1503     default:
1504         break;
1505     }
1506
1507     m_dwFlags |= flags;
1508 }
1509
1510 template<class ARGITERATOR_BASE>
1511 void ArgIteratorTemplate<ARGITERATOR_BASE>::ForceSigWalk()
1512 {
1513     CONTRACTL
1514     {
1515         INSTANCE_CHECK;
1516         if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
1517         if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
1518         if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
1519         MODE_ANY;
1520     }
1521     CONTRACTL_END
1522
1523     // This can be only used before the actual argument iteration started
1524     _ASSERTE((m_dwFlags & ITERATION_STARTED) == 0);
1525
1526 #ifdef _TARGET_X86_
1527     //
1528     // x86 is special as always
1529     //
1530
1531     int numRegistersUsed = 0;
1532     int nSizeOfArgStack = 0;
1533
1534     if (this->HasThis())
1535         numRegistersUsed++;
1536
1537     if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
1538         numRegistersUsed++;
1539
1540     if (this->IsVarArg())
1541     {
1542         nSizeOfArgStack += sizeof(void *);
1543         numRegistersUsed = NUM_ARGUMENT_REGISTERS; // Nothing else gets passed in registers for varargs
1544     }
1545
1546 #ifdef FEATURE_INTERPRETER
1547      BYTE callconv = CallConv();
1548      switch (callconv)
1549      {
1550      case IMAGE_CEE_CS_CALLCONV_C:
1551      case IMAGE_CEE_CS_CALLCONV_STDCALL:
1552            numRegistersUsed = NUM_ARGUMENT_REGISTERS;
1553            nSizeOfArgStack = TransitionBlock::GetOffsetOfArgs() + numRegistersUsed * sizeof(void *); 
1554            break;
1555
1556      case IMAGE_CEE_CS_CALLCONV_THISCALL:
1557      case IMAGE_CEE_CS_CALLCONV_FASTCALL:
1558           _ASSERTE_MSG(false, "Unsupported calling convention.");
1559      default:
1560      }
1561 #endif // FEATURE_INTERPRETER
1562
1563     DWORD nArgs = this->NumFixedArgs();
1564     for (DWORD i = 0; i < nArgs; i++)
1565     {        
1566         TypeHandle thValueType;        
1567         CorElementType type = this->GetNextArgumentType(i, &thValueType);
1568
1569         if (!IsArgumentInRegister(&numRegistersUsed, type))
1570         {
1571             int structSize = MetaSig::GetElemSize(type, thValueType);
1572
1573             nSizeOfArgStack += StackElemSize(structSize);
1574
1575 #ifndef DACCESS_COMPILE
1576             if (nSizeOfArgStack > MAX_ARG_SIZE)
1577             {
1578 #ifdef _DEBUG
1579                 // We should not ever throw exception in the "FORBIDGC_LOADER_USE_ENABLED" mode.
1580                 // The contract violation is required to workaround bug in the static contract analyzer.                 
1581                 _ASSERTE(!FORBIDGC_LOADER_USE_ENABLED());
1582                 CONTRACT_VIOLATION(ThrowsViolation);
1583 #endif
1584                 COMPlusThrow(kNotSupportedException);
1585             }
1586 #endif
1587         }
1588     }
1589
1590     if (this->HasParamType())
1591     {
1592         DWORD paramTypeFlags = 0;
1593         if (numRegistersUsed < NUM_ARGUMENT_REGISTERS)
1594         {
1595             numRegistersUsed++;
1596             paramTypeFlags = (numRegistersUsed == 1) ? 
1597                 PARAM_TYPE_REGISTER_ECX : PARAM_TYPE_REGISTER_EDX;
1598         }
1599         else
1600         {
1601             nSizeOfArgStack += sizeof(void *);
1602             paramTypeFlags = PARAM_TYPE_REGISTER_STACK;
1603         }
1604         m_dwFlags |= paramTypeFlags;
1605     }
1606
1607 #else // _TARGET_X86_
1608
1609     int maxOffset = TransitionBlock::GetOffsetOfArgs();
1610
1611     int ofs;
1612     while (TransitionBlock::InvalidOffset != (ofs = GetNextOffset()))
1613     {
1614         int stackElemSize;
1615
1616 #ifdef _TARGET_AMD64_
1617 #ifdef UNIX_AMD64_ABI
1618         if (m_fArgInRegisters)
1619         {
1620             // Arguments passed in registers don't consume any stack 
1621             continue;
1622         }
1623
1624         stackElemSize = StackElemSize(GetArgSize());
1625 #else // UNIX_AMD64_ABI
1626         // All stack arguments take just one stack slot on AMD64 because of arguments bigger 
1627         // than a stack slot are passed by reference. 
1628         stackElemSize = STACK_ELEM_SIZE;
1629 #endif // UNIX_AMD64_ABI
1630 #else // _TARGET_AMD64_
1631         stackElemSize = StackElemSize(GetArgSize());
1632 #if defined(ENREGISTERED_PARAMTYPE_MAXSIZE)
1633         if (IsArgPassedByRef())
1634             stackElemSize = STACK_ELEM_SIZE;
1635 #endif
1636 #endif // _TARGET_AMD64_
1637
1638         int endOfs = ofs + stackElemSize;
1639         if (endOfs > maxOffset)
1640         {
1641 #if !defined(DACCESS_COMPILE)
1642             if (endOfs > MAX_ARG_SIZE)
1643             {
1644 #ifdef _DEBUG
1645                 // We should not ever throw exception in the "FORBIDGC_LOADER_USE_ENABLED" mode.
1646                 // The contract violation is required to workaround bug in the static contract analyzer.                 
1647                 _ASSERTE(!FORBIDGC_LOADER_USE_ENABLED());
1648                 CONTRACT_VIOLATION(ThrowsViolation);
1649 #endif
1650                 COMPlusThrow(kNotSupportedException);
1651             }
1652 #endif
1653             maxOffset = endOfs;
1654         }        
1655     }
1656     // Clear the iterator started flag
1657     m_dwFlags &= ~ITERATION_STARTED;
1658
1659     int nSizeOfArgStack = maxOffset - TransitionBlock::GetOffsetOfArgs();
1660
1661 #if defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI)
1662     nSizeOfArgStack = (nSizeOfArgStack > (int)sizeof(ArgumentRegisters)) ?
1663         (nSizeOfArgStack - sizeof(ArgumentRegisters)) : 0;
1664 #endif
1665
1666 #endif // _TARGET_X86_
1667
1668     // Cache the result
1669     m_nSizeOfArgStack = nSizeOfArgStack;
1670     m_dwFlags |= SIZE_OF_ARG_STACK_COMPUTED;
1671
1672     this->Reset();
1673 }
1674
1675 class ArgIteratorBase
1676 {
1677 protected:
1678     MetaSig * m_pSig;
1679
1680     FORCEINLINE CorElementType GetReturnType(TypeHandle * pthValueType)
1681     {
1682         WRAPPER_NO_CONTRACT;
1683 #ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
1684         return m_pSig->GetReturnTypeNormalized(pthValueType);
1685 #else
1686         return m_pSig->GetReturnTypeNormalized();
1687 #endif
1688     }
1689
1690     FORCEINLINE CorElementType GetNextArgumentType(DWORD iArg, TypeHandle * pthValueType)
1691     {
1692         WRAPPER_NO_CONTRACT;
1693         _ASSERTE(iArg == m_pSig->GetArgNum());
1694         CorElementType et = m_pSig->PeekArgNormalized(pthValueType);
1695         m_pSig->SkipArg();
1696         return et;
1697     }
1698
1699     FORCEINLINE void Reset()
1700     {
1701         WRAPPER_NO_CONTRACT;
1702         m_pSig->Reset();
1703     }
1704
1705 public:
1706     BOOL HasThis()
1707     {
1708         LIMITED_METHOD_CONTRACT;
1709         return m_pSig->HasThis();
1710     }
1711
1712     BOOL HasParamType()
1713     {
1714         LIMITED_METHOD_CONTRACT;
1715         return m_pSig->GetCallingConventionInfo() & CORINFO_CALLCONV_PARAMTYPE;
1716     }
1717
1718     BOOL IsVarArg()
1719     {
1720         LIMITED_METHOD_CONTRACT;
1721         return m_pSig->IsVarArg() || m_pSig->IsTreatAsVarArg();
1722     }
1723
1724     DWORD NumFixedArgs()
1725     {
1726         LIMITED_METHOD_CONTRACT;
1727         return m_pSig->NumFixedArgs();
1728     }
1729
1730 #ifdef FEATURE_INTERPRETER
1731     BYTE CallConv()
1732     {
1733         return m_pSig->GetCallingConvention();
1734     }
1735 #endif // FEATURE_INTERPRETER
1736
1737     //
1738     // The following is used by the profiler to dig into the iterator for
1739     // discovering if the method has a This pointer or a return buffer.
1740     // Do not use this to re-initialize the signature, use the exposed Init()
1741     // method in this class.
1742     //
1743     MetaSig *GetSig(void)
1744     {
1745         return m_pSig;
1746     }
1747 };
1748
1749 class ArgIterator : public ArgIteratorTemplate<ArgIteratorBase>
1750 {
1751 public:
1752     ArgIterator(MetaSig * pSig)
1753     {
1754         m_pSig = pSig;
1755     }
1756
1757     // This API returns true if we are returning a structure in registers instead of using a byref return buffer
1758     BOOL HasNonStandardByvalReturn()
1759     {
1760         WRAPPER_NO_CONTRACT;
1761
1762 #ifdef ENREGISTERED_RETURNTYPE_MAXSIZE
1763         CorElementType type = m_pSig->GetReturnTypeNormalized();
1764         return (type == ELEMENT_TYPE_VALUETYPE || type == ELEMENT_TYPE_TYPEDBYREF) && !HasRetBuffArg();
1765 #else
1766         return FALSE;
1767 #endif
1768     }
1769 };
1770
1771 // Conventience helper
1772 inline BOOL HasRetBuffArg(MetaSig * pSig)
1773 {
1774     WRAPPER_NO_CONTRACT;
1775     ArgIterator argit(pSig);
1776     return argit.HasRetBuffArg();
1777 }
1778
1779 #ifdef UNIX_X86_ABI
1780 // For UNIX_X86_ABI and unmanaged function, we always need RetBuf if the return type is VALUETYPE
1781 inline BOOL HasRetBuffArgUnmanagedFixup(MetaSig * pSig)
1782 {
1783     WRAPPER_NO_CONTRACT;
1784     // We cannot just pSig->GetReturnType() here since it will return ELEMENT_TYPE_VALUETYPE for enums
1785     CorElementType type = pSig->GetRetTypeHandleThrowing().GetVerifierCorElementType();
1786     return type == ELEMENT_TYPE_VALUETYPE;
1787 }
1788 #endif
1789
1790 inline BOOL IsRetBuffPassedAsFirstArg()
1791 {
1792     WRAPPER_NO_CONTRACT;
1793 #ifndef _TARGET_ARM64_
1794     return TRUE;
1795 #else
1796     return FALSE;
1797 #endif        
1798 }
1799
1800 #endif // __CALLING_CONVENTION_INCLUDED