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
16BOOL 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.
35struct 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//
91struct 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//-----------------------------------------------------------------------
281template<class ARGITERATOR_BASE>
282class ArgIteratorTemplate : public ARGITERATOR_BASE
283{
284public:
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
664protected:
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
748template<class ARGITERATOR_BASE>
749int 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
764template<class ARGITERATOR_BASE>
765int 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
787template<class ARGITERATOR_BASE>
788int 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//-----------------------------------------------------------
818template<class ARGITERATOR_BASE>
819int 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//------------------------------------------------------------
877template<class ARGITERATOR_BASE>
878int 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) || defined (_TARGET_ARM64_)
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 MethodTable *pMT = m_argTypeHandle.GetMethodTable();
1019 if (this->IsRegPassedStruct(pMT))
1020 {
1021 EEClass* eeClass = pMT->GetClass();
1022 cGenRegs = 0;
1023 for (int i = 0; i < eeClass->GetNumberEightBytes(); i++)
1024 {
1025 switch (eeClass->GetEightByteClassification(i))
1026 {
1027 case SystemVClassificationTypeInteger:
1028 case SystemVClassificationTypeIntegerReference:
1029 case SystemVClassificationTypeIntegerByRef:
1030 cGenRegs++;
1031 break;
1032 case SystemVClassificationTypeSSE:
1033 cFPRegs++;
1034 break;
1035 default:
1036 _ASSERTE(false);
1037 break;
1038 }
1039 }
1040
1041 // Check if we have enough registers available for the struct passing
1042 if ((cFPRegs + m_idxFPReg <= NUM_FLOAT_ARGUMENT_REGISTERS) && (cGenRegs + m_idxGenReg) <= NUM_ARGUMENT_REGISTERS)
1043 {
1044 m_argLocDescForStructInRegs.Init();
1045 m_argLocDescForStructInRegs.m_cGenReg = cGenRegs;
1046 m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs;
1047 m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg;
1048 m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
1049 m_argLocDescForStructInRegs.m_eeClass = eeClass;
1050
1051 m_hasArgLocDescForStructInRegs = true;
1052
1053 m_idxGenReg += cGenRegs;
1054 m_idxFPReg += cFPRegs;
1055
1056 return TransitionBlock::StructInRegsOffset;
1057 }
1058 }
1059
1060 // Set the register counts to indicate that this argument will not be passed in registers
1061 cFPRegs = 0;
1062 cGenRegs = 0;
1063 break;
1064 }
1065
1066 default:
1067 cGenRegs = cbArg / 8; // GP reg size
1068 break;
1069 }
1070
1071 if ((cFPRegs > 0) && (cFPRegs + m_idxFPReg <= NUM_FLOAT_ARGUMENT_REGISTERS))
1072 {
1073 int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 16;
1074 m_idxFPReg += cFPRegs;
1075 return argOfs;
1076 }
1077 else if ((cGenRegs > 0) && (m_idxGenReg + cGenRegs <= NUM_ARGUMENT_REGISTERS))
1078 {
1079 int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1080 m_idxGenReg += cGenRegs;
1081 return argOfs;
1082 }
1083
1084 m_fArgInRegisters = false;
1085
1086 int argOfs = TransitionBlock::GetOffsetOfArgs() + m_idxStack * STACK_ELEM_SIZE;
1087
1088 int cArgSlots = cbArg / STACK_ELEM_SIZE;
1089 m_idxStack += cArgSlots;
1090
1091 return argOfs;
1092#else
1093 // Each argument takes exactly one slot on AMD64 on Windows
1094 int argOfs = m_curOfs;
1095 m_curOfs += sizeof(void *);
1096 return argOfs;
1097#endif
1098#elif defined(_TARGET_ARM_)
1099 // First look at the underlying type of the argument to determine some basic properties:
1100 // 1) The size of the argument in bytes (rounded up to the stack slot size of 4 if necessary).
1101 // 2) Whether the argument represents a floating point primitive (ELEMENT_TYPE_R4 or ELEMENT_TYPE_R8).
1102 // 3) Whether the argument requires 64-bit alignment (anything that contains a Int64/UInt64).
1103
1104 bool fFloatingPoint = false;
1105 bool fRequiresAlign64Bit = false;
1106
1107 switch (argType)
1108 {
1109 case ELEMENT_TYPE_I8:
1110 case ELEMENT_TYPE_U8:
1111 // 64-bit integers require 64-bit alignment on ARM.
1112 fRequiresAlign64Bit = true;
1113 break;
1114
1115 case ELEMENT_TYPE_R4:
1116 // 32-bit floating point argument.
1117 fFloatingPoint = true;
1118 break;
1119
1120 case ELEMENT_TYPE_R8:
1121 // 64-bit floating point argument.
1122 fFloatingPoint = true;
1123 fRequiresAlign64Bit = true;
1124 break;
1125
1126 case ELEMENT_TYPE_VALUETYPE:
1127 {
1128 // Value type case: extract the alignment requirement, note that this has to handle
1129 // the interop "native value types".
1130 fRequiresAlign64Bit = thValueType.RequiresAlign8();
1131
1132#ifdef FEATURE_HFA
1133 // Handle HFAs: packed structures of 1-4 floats or doubles that are passed in FP argument
1134 // registers if possible.
1135 if (thValueType.IsHFA())
1136 {
1137 fFloatingPoint = true;
1138 }
1139#endif
1140
1141 break;
1142 }
1143
1144 default:
1145 // The default is are 4-byte arguments (or promoted to 4 bytes), non-FP and don't require any
1146 // 64-bit alignment.
1147 break;
1148 }
1149
1150 // Now attempt to place the argument into some combination of floating point or general registers and
1151 // the stack.
1152
1153 // Save the alignment requirement
1154 m_fRequires64BitAlignment = fRequiresAlign64Bit;
1155
1156 int cbArg = StackElemSize(argSize);
1157 int cArgSlots = cbArg / 4;
1158
1159 // Ignore floating point argument placement in registers if we're dealing with a vararg function (the ABI
1160 // specifies this so that vararg processing on the callee side is simplified).
1161#ifndef ARM_SOFTFP
1162 if (fFloatingPoint && !this->IsVarArg())
1163 {
1164 // Handle floating point (primitive) arguments.
1165
1166 // First determine whether we can place the argument in VFP registers. There are 16 32-bit
1167 // and 8 64-bit argument registers that share the same register space (e.g. D0 overlaps S0 and
1168 // S1). The ABI specifies that VFP values will be passed in the lowest sequence of registers that
1169 // haven't been used yet and have the required alignment. So the sequence (float, double, float)
1170 // would be mapped to (S0, D1, S1) or (S0, S2/S3, S1).
1171 //
1172 // We use a 16-bit bitmap to record which registers have been used so far.
1173 //
1174 // So we can use the same basic loop for each argument type (float, double or HFA struct) we set up
1175 // the following input parameters based on the size and alignment requirements of the arguments:
1176 // wAllocMask : bitmask of the number of 32-bit registers we need (1 for 1, 3 for 2, 7 for 3 etc.)
1177 // cSteps : number of loop iterations it'll take to search the 16 registers
1178 // cShift : how many bits to shift the allocation mask on each attempt
1179
1180 WORD wAllocMask = (1 << (cbArg / 4)) - 1;
1181 WORD cSteps = (WORD)(fRequiresAlign64Bit ? 9 - (cbArg / 8) : 17 - (cbArg / 4));
1182 WORD cShift = fRequiresAlign64Bit ? 2 : 1;
1183
1184 // Look through the availability bitmask for a free register or register pair.
1185 for (WORD i = 0; i < cSteps; i++)
1186 {
1187 if ((m_wFPRegs & wAllocMask) == 0)
1188 {
1189 // We found one, mark the register or registers as used.
1190 m_wFPRegs |= wAllocMask;
1191
1192 // Indicate the registers used to the caller and return.
1193 return TransitionBlock::GetOffsetOfFloatArgumentRegisters() + (i * cShift * 4);
1194 }
1195 wAllocMask <<= cShift;
1196 }
1197
1198 // The FP argument is going to live on the stack. Once this happens the ABI demands we mark all FP
1199 // registers as unavailable.
1200 m_wFPRegs = 0xffff;
1201
1202 // Doubles or HFAs containing doubles need the stack aligned appropriately.
1203 if (fRequiresAlign64Bit)
1204 m_idxStack = (int)ALIGN_UP(m_idxStack, 2);
1205
1206 // Indicate the stack location of the argument to the caller.
1207 int argOfs = TransitionBlock::GetOffsetOfArgs() + m_idxStack * 4;
1208
1209 // Record the stack usage.
1210 m_idxStack += cArgSlots;
1211
1212 return argOfs;
1213 }
1214#endif // ARM_SOFTFP
1215
1216 //
1217 // Handle the non-floating point case.
1218 //
1219
1220 if (m_idxGenReg < 4)
1221 {
1222 if (fRequiresAlign64Bit)
1223 {
1224 // The argument requires 64-bit alignment. Align either the next general argument register if
1225 // we have any left. See step C.3 in the algorithm in the ABI spec.
1226 m_idxGenReg = (int)ALIGN_UP(m_idxGenReg, 2);
1227 }
1228
1229 int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 4;
1230
1231 int cRemainingRegs = 4 - m_idxGenReg;
1232 if (cArgSlots <= cRemainingRegs)
1233 {
1234 // Mark the registers just allocated as used.
1235 m_idxGenReg += cArgSlots;
1236 return argOfs;
1237 }
1238
1239 // The ABI supports splitting a non-FP argument across registers and the stack. But this is
1240 // disabled if the FP arguments already overflowed onto the stack (i.e. the stack index is not
1241 // zero). The following code marks the general argument registers as exhausted if this condition
1242 // holds. See steps C.5 in the algorithm in the ABI spec.
1243
1244 m_idxGenReg = 4;
1245
1246 if (m_idxStack == 0)
1247 {
1248 m_idxStack += cArgSlots - cRemainingRegs;
1249 return argOfs;
1250 }
1251 }
1252
1253 if (fRequiresAlign64Bit)
1254 {
1255 // The argument requires 64-bit alignment. If it is going to be passed on the stack, align
1256 // the next stack slot. See step C.6 in the algorithm in the ABI spec.
1257 m_idxStack = (int)ALIGN_UP(m_idxStack, 2);
1258 }
1259
1260 int argOfs = TransitionBlock::GetOffsetOfArgs() + m_idxStack * 4;
1261
1262 // Advance the stack pointer over the argument just placed.
1263 m_idxStack += cArgSlots;
1264
1265 return argOfs;
1266#elif defined(_TARGET_ARM64_)
1267
1268 int cFPRegs = 0;
1269
1270 switch (argType)
1271 {
1272
1273 case ELEMENT_TYPE_R4:
1274 // 32-bit floating point argument.
1275 cFPRegs = 1;
1276 break;
1277
1278 case ELEMENT_TYPE_R8:
1279 // 64-bit floating point argument.
1280 cFPRegs = 1;
1281 break;
1282
1283 case ELEMENT_TYPE_VALUETYPE:
1284 {
1285 // Handle HFAs: packed structures of 2-4 floats or doubles that are passed in FP argument
1286 // registers if possible.
1287 if (thValueType.IsHFA())
1288 {
1289 CorElementType type = thValueType.GetHFAType();
1290 bool isFloatType = (type == ELEMENT_TYPE_R4);
1291
1292 cFPRegs = (type == ELEMENT_TYPE_R4)? (argSize/sizeof(float)): (argSize/sizeof(double));
1293
1294 m_argLocDescForStructInRegs.Init();
1295 m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs;
1296 m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
1297
1298 m_argLocDescForStructInRegs.m_isSinglePrecision = isFloatType;
1299
1300 m_hasArgLocDescForStructInRegs = true;
1301 }
1302 else
1303 {
1304 // Composite greater than 16bytes should be passed by reference
1305 if (argSize > ENREGISTERED_PARAMTYPE_MAXSIZE)
1306 {
1307 argSize = sizeof(TADDR);
1308 }
1309 }
1310
1311 break;
1312 }
1313
1314 default:
1315 break;
1316 }
1317
1318 int cbArg = StackElemSize(argSize);
1319 int cArgSlots = cbArg / STACK_ELEM_SIZE;
1320
1321 if (cFPRegs>0 && !this->IsVarArg())
1322 {
1323 if (cFPRegs + m_idxFPReg <= 8)
1324 {
1325 int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
1326 m_idxFPReg += cFPRegs;
1327 return argOfs;
1328 }
1329 else
1330 {
1331 m_idxFPReg = 8;
1332 }
1333 }
1334 else
1335 {
1336 // Only x0-x7 are valid argument registers (x8 is always the return buffer)
1337 if (m_idxGenReg + cArgSlots <= 8)
1338 {
1339 // The entirety of the arg fits in the register slots.
1340
1341 int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1342 m_idxGenReg += cArgSlots;
1343 return argOfs;
1344 }
1345 else
1346 {
1347#ifdef _WIN32
1348 if (this->IsVarArg() && m_idxGenReg < 8)
1349 {
1350 // Address the Windows ARM64 varargs case where an arg is split between regs and stack.
1351 // This can happen in the varargs case because the first 64 bytes of the stack are loaded
1352 // into x0-x7, and any remaining stack arguments are placed normally.
1353 int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
1354
1355 // Increase m_idxStack to account for the space used for the remainder of the arg after
1356 // register slots are filled.
1357 m_idxStack += (m_idxGenReg + cArgSlots - 8);
1358
1359 // We used up the remaining reg slots.
1360 m_idxGenReg = 8;
1361
1362 return argOfs;
1363 }
1364 else
1365#endif
1366 {
1367 // Don't use reg slots for this. It will be passed purely on the stack arg space.
1368 m_idxGenReg = 8;
1369 }
1370 }
1371 }
1372
1373 int argOfs = TransitionBlock::GetOffsetOfArgs() + m_idxStack * 8;
1374 m_idxStack += cArgSlots;
1375 return argOfs;
1376#else
1377 PORTABILITY_ASSERT("ArgIteratorTemplate::GetNextOffset");
1378 return TransitionBlock::InvalidOffset;
1379#endif
1380}
1381
1382template<class ARGITERATOR_BASE>
1383void ArgIteratorTemplate<ARGITERATOR_BASE>::ComputeReturnFlags()
1384{
1385 CONTRACTL
1386 {
1387 INSTANCE_CHECK;
1388 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
1389 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
1390 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
1391 MODE_ANY;
1392 }
1393 CONTRACTL_END
1394
1395 TypeHandle thValueType;
1396 CorElementType type = this->GetReturnType(&thValueType);
1397
1398 DWORD flags = RETURN_FLAGS_COMPUTED;
1399 switch (type)
1400 {
1401 case ELEMENT_TYPE_TYPEDBYREF:
1402#ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
1403 if (sizeof(TypedByRef) > ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
1404 flags |= RETURN_HAS_RET_BUFFER;
1405#else
1406 flags |= RETURN_HAS_RET_BUFFER;
1407#endif
1408 break;
1409
1410 case ELEMENT_TYPE_R4:
1411#ifndef ARM_SOFTFP
1412 flags |= sizeof(float) << RETURN_FP_SIZE_SHIFT;
1413#endif
1414 break;
1415
1416 case ELEMENT_TYPE_R8:
1417#ifndef ARM_SOFTFP
1418 flags |= sizeof(double) << RETURN_FP_SIZE_SHIFT;
1419#endif
1420 break;
1421
1422 case ELEMENT_TYPE_VALUETYPE:
1423#ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
1424 {
1425 _ASSERTE(!thValueType.IsNull());
1426
1427#if defined(UNIX_AMD64_ABI)
1428 MethodTable *pMT = thValueType.AsMethodTable();
1429 if (pMT->IsRegPassedStruct())
1430 {
1431 EEClass* eeClass = pMT->GetClass();
1432
1433 if (eeClass->GetNumberEightBytes() == 1)
1434 {
1435 // Structs occupying just one eightbyte are treated as int / double
1436 if (eeClass->GetEightByteClassification(0) == SystemVClassificationTypeSSE)
1437 {
1438 flags |= sizeof(double) << RETURN_FP_SIZE_SHIFT;
1439 }
1440 }
1441 else
1442 {
1443 // Size of the struct is 16 bytes
1444 flags |= (16 << RETURN_FP_SIZE_SHIFT);
1445 // The lowest two bits of the size encode the order of the int and SSE fields
1446 if (eeClass->GetEightByteClassification(0) == SystemVClassificationTypeSSE)
1447 {
1448 flags |= (1 << RETURN_FP_SIZE_SHIFT);
1449 }
1450
1451 if (eeClass->GetEightByteClassification(1) == SystemVClassificationTypeSSE)
1452 {
1453 flags |= (2 << RETURN_FP_SIZE_SHIFT);
1454 }
1455 }
1456
1457 break;
1458 }
1459#else // UNIX_AMD64_ABI
1460
1461#ifdef FEATURE_HFA
1462 if (thValueType.IsHFA() && !this->IsVarArg())
1463 {
1464 CorElementType hfaType = thValueType.GetHFAType();
1465
1466 flags |= (hfaType == ELEMENT_TYPE_R4) ?
1467 ((4 * sizeof(float)) << RETURN_FP_SIZE_SHIFT) :
1468 ((4 * sizeof(double)) << RETURN_FP_SIZE_SHIFT);
1469
1470 break;
1471 }
1472#endif
1473
1474 size_t size = thValueType.GetSize();
1475
1476#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_)
1477 // Return value types of size which are not powers of 2 using a RetBuffArg
1478 if ((size & (size-1)) != 0)
1479 {
1480 flags |= RETURN_HAS_RET_BUFFER;
1481 break;
1482 }
1483#endif
1484
1485 if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
1486 break;
1487#endif // UNIX_AMD64_ABI
1488 }
1489#endif // ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
1490
1491 // Value types are returned using return buffer by default
1492 flags |= RETURN_HAS_RET_BUFFER;
1493 break;
1494
1495 default:
1496 break;
1497 }
1498
1499 m_dwFlags |= flags;
1500}
1501
1502template<class ARGITERATOR_BASE>
1503void ArgIteratorTemplate<ARGITERATOR_BASE>::ForceSigWalk()
1504{
1505 CONTRACTL
1506 {
1507 INSTANCE_CHECK;
1508 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
1509 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
1510 if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
1511 MODE_ANY;
1512 }
1513 CONTRACTL_END
1514
1515 // This can be only used before the actual argument iteration started
1516 _ASSERTE((m_dwFlags & ITERATION_STARTED) == 0);
1517
1518#ifdef _TARGET_X86_
1519 //
1520 // x86 is special as always
1521 //
1522
1523 int numRegistersUsed = 0;
1524 int nSizeOfArgStack = 0;
1525
1526 if (this->HasThis())
1527 numRegistersUsed++;
1528
1529 if (this->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
1530 numRegistersUsed++;
1531
1532 if (this->IsVarArg())
1533 {
1534 nSizeOfArgStack += sizeof(void *);
1535 numRegistersUsed = NUM_ARGUMENT_REGISTERS; // Nothing else gets passed in registers for varargs
1536 }
1537
1538#ifdef FEATURE_INTERPRETER
1539 BYTE callconv = CallConv();
1540 switch (callconv)
1541 {
1542 case IMAGE_CEE_CS_CALLCONV_C:
1543 case IMAGE_CEE_CS_CALLCONV_STDCALL:
1544 numRegistersUsed = NUM_ARGUMENT_REGISTERS;
1545 nSizeOfArgStack = TransitionBlock::GetOffsetOfArgs() + numRegistersUsed * sizeof(void *);
1546 break;
1547
1548 case IMAGE_CEE_CS_CALLCONV_THISCALL:
1549 case IMAGE_CEE_CS_CALLCONV_FASTCALL:
1550 _ASSERTE_MSG(false, "Unsupported calling convention.");
1551 default:
1552 }
1553#endif // FEATURE_INTERPRETER
1554
1555 DWORD nArgs = this->NumFixedArgs();
1556 for (DWORD i = 0; i < nArgs; i++)
1557 {
1558 TypeHandle thValueType;
1559 CorElementType type = this->GetNextArgumentType(i, &thValueType);
1560
1561 if (!IsArgumentInRegister(&numRegistersUsed, type))
1562 {
1563 int structSize = MetaSig::GetElemSize(type, thValueType);
1564
1565 nSizeOfArgStack += StackElemSize(structSize);
1566
1567#ifndef DACCESS_COMPILE
1568 if (nSizeOfArgStack > MAX_ARG_SIZE)
1569 {
1570#ifdef _DEBUG
1571 // We should not ever throw exception in the "FORBIDGC_LOADER_USE_ENABLED" mode.
1572 // The contract violation is required to workaround bug in the static contract analyzer.
1573 _ASSERTE(!FORBIDGC_LOADER_USE_ENABLED());
1574 CONTRACT_VIOLATION(ThrowsViolation);
1575#endif
1576 COMPlusThrow(kNotSupportedException);
1577 }
1578#endif
1579 }
1580 }
1581
1582 if (this->HasParamType())
1583 {
1584 DWORD paramTypeFlags = 0;
1585 if (numRegistersUsed < NUM_ARGUMENT_REGISTERS)
1586 {
1587 numRegistersUsed++;
1588 paramTypeFlags = (numRegistersUsed == 1) ?
1589 PARAM_TYPE_REGISTER_ECX : PARAM_TYPE_REGISTER_EDX;
1590 }
1591 else
1592 {
1593 nSizeOfArgStack += sizeof(void *);
1594 paramTypeFlags = PARAM_TYPE_REGISTER_STACK;
1595 }
1596 m_dwFlags |= paramTypeFlags;
1597 }
1598
1599#else // _TARGET_X86_
1600
1601 int maxOffset = TransitionBlock::GetOffsetOfArgs();
1602
1603 int ofs;
1604 while (TransitionBlock::InvalidOffset != (ofs = GetNextOffset()))
1605 {
1606 int stackElemSize;
1607
1608#ifdef _TARGET_AMD64_
1609#ifdef UNIX_AMD64_ABI
1610 if (m_fArgInRegisters)
1611 {
1612 // Arguments passed in registers don't consume any stack
1613 continue;
1614 }
1615
1616 stackElemSize = StackElemSize(GetArgSize());
1617#else // UNIX_AMD64_ABI
1618 // All stack arguments take just one stack slot on AMD64 because of arguments bigger
1619 // than a stack slot are passed by reference.
1620 stackElemSize = STACK_ELEM_SIZE;
1621#endif // UNIX_AMD64_ABI
1622#else // _TARGET_AMD64_
1623 stackElemSize = StackElemSize(GetArgSize());
1624#if defined(ENREGISTERED_PARAMTYPE_MAXSIZE)
1625 if (IsArgPassedByRef())
1626 stackElemSize = STACK_ELEM_SIZE;
1627#endif
1628#endif // _TARGET_AMD64_
1629
1630 int endOfs = ofs + stackElemSize;
1631 if (endOfs > maxOffset)
1632 {
1633#if !defined(DACCESS_COMPILE)
1634 if (endOfs > MAX_ARG_SIZE)
1635 {
1636#ifdef _DEBUG
1637 // We should not ever throw exception in the "FORBIDGC_LOADER_USE_ENABLED" mode.
1638 // The contract violation is required to workaround bug in the static contract analyzer.
1639 _ASSERTE(!FORBIDGC_LOADER_USE_ENABLED());
1640 CONTRACT_VIOLATION(ThrowsViolation);
1641#endif
1642 COMPlusThrow(kNotSupportedException);
1643 }
1644#endif
1645 maxOffset = endOfs;
1646 }
1647 }
1648 // Clear the iterator started flag
1649 m_dwFlags &= ~ITERATION_STARTED;
1650
1651 int nSizeOfArgStack = maxOffset - TransitionBlock::GetOffsetOfArgs();
1652
1653#if defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI)
1654 nSizeOfArgStack = (nSizeOfArgStack > (int)sizeof(ArgumentRegisters)) ?
1655 (nSizeOfArgStack - sizeof(ArgumentRegisters)) : 0;
1656#endif
1657
1658#endif // _TARGET_X86_
1659
1660 // Cache the result
1661 m_nSizeOfArgStack = nSizeOfArgStack;
1662 m_dwFlags |= SIZE_OF_ARG_STACK_COMPUTED;
1663
1664 this->Reset();
1665}
1666
1667class ArgIteratorBase
1668{
1669protected:
1670 MetaSig * m_pSig;
1671
1672 FORCEINLINE CorElementType GetReturnType(TypeHandle * pthValueType)
1673 {
1674 WRAPPER_NO_CONTRACT;
1675#ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
1676 return m_pSig->GetReturnTypeNormalized(pthValueType);
1677#else
1678 return m_pSig->GetReturnTypeNormalized();
1679#endif
1680 }
1681
1682 FORCEINLINE CorElementType GetNextArgumentType(DWORD iArg, TypeHandle * pthValueType)
1683 {
1684 WRAPPER_NO_CONTRACT;
1685 _ASSERTE(iArg == m_pSig->GetArgNum());
1686 CorElementType et = m_pSig->PeekArgNormalized(pthValueType);
1687 m_pSig->SkipArg();
1688 return et;
1689 }
1690
1691 FORCEINLINE void Reset()
1692 {
1693 WRAPPER_NO_CONTRACT;
1694 m_pSig->Reset();
1695 }
1696
1697 FORCEINLINE BOOL IsRegPassedStruct(MethodTable* pMT)
1698 {
1699 return pMT->IsRegPassedStruct();
1700 }
1701
1702public:
1703 BOOL HasThis()
1704 {
1705 LIMITED_METHOD_CONTRACT;
1706 return m_pSig->HasThis();
1707 }
1708
1709 BOOL HasParamType()
1710 {
1711 LIMITED_METHOD_CONTRACT;
1712 return m_pSig->GetCallingConventionInfo() & CORINFO_CALLCONV_PARAMTYPE;
1713 }
1714
1715 BOOL IsVarArg()
1716 {
1717 LIMITED_METHOD_CONTRACT;
1718 return m_pSig->IsVarArg() || m_pSig->IsTreatAsVarArg();
1719 }
1720
1721 DWORD NumFixedArgs()
1722 {
1723 LIMITED_METHOD_CONTRACT;
1724 return m_pSig->NumFixedArgs();
1725 }
1726
1727#ifdef FEATURE_INTERPRETER
1728 BYTE CallConv()
1729 {
1730 return m_pSig->GetCallingConvention();
1731 }
1732#endif // FEATURE_INTERPRETER
1733
1734 //
1735 // The following is used by the profiler to dig into the iterator for
1736 // discovering if the method has a This pointer or a return buffer.
1737 // Do not use this to re-initialize the signature, use the exposed Init()
1738 // method in this class.
1739 //
1740 MetaSig *GetSig(void)
1741 {
1742 return m_pSig;
1743 }
1744};
1745
1746class ArgIterator : public ArgIteratorTemplate<ArgIteratorBase>
1747{
1748public:
1749 ArgIterator(MetaSig * pSig)
1750 {
1751 m_pSig = pSig;
1752 }
1753
1754 // This API returns true if we are returning a structure in registers instead of using a byref return buffer
1755 BOOL HasNonStandardByvalReturn()
1756 {
1757 WRAPPER_NO_CONTRACT;
1758
1759#ifdef ENREGISTERED_RETURNTYPE_MAXSIZE
1760 CorElementType type = m_pSig->GetReturnTypeNormalized();
1761 return (type == ELEMENT_TYPE_VALUETYPE || type == ELEMENT_TYPE_TYPEDBYREF) && !HasRetBuffArg();
1762#else
1763 return FALSE;
1764#endif
1765 }
1766};
1767
1768// Conventience helper
1769inline BOOL HasRetBuffArg(MetaSig * pSig)
1770{
1771 WRAPPER_NO_CONTRACT;
1772 ArgIterator argit(pSig);
1773 return argit.HasRetBuffArg();
1774}
1775
1776#ifdef UNIX_X86_ABI
1777// For UNIX_X86_ABI and unmanaged function, we always need RetBuf if the return type is VALUETYPE
1778inline BOOL HasRetBuffArgUnmanagedFixup(MetaSig * pSig)
1779{
1780 WRAPPER_NO_CONTRACT;
1781 // We cannot just pSig->GetReturnType() here since it will return ELEMENT_TYPE_VALUETYPE for enums
1782 CorElementType type = pSig->GetRetTypeHandleThrowing().GetVerifierCorElementType();
1783 return type == ELEMENT_TYPE_VALUETYPE;
1784}
1785#endif
1786
1787inline BOOL IsRetBuffPassedAsFirstArg()
1788{
1789 WRAPPER_NO_CONTRACT;
1790#ifndef _TARGET_ARM64_
1791 return TRUE;
1792#else
1793 return FALSE;
1794#endif
1795}
1796
1797#endif // __CALLING_CONVENTION_INCLUDED
1798