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 // This file is a line by line port of callingconvention.h from the desktop CLR. See reference source in the ReferenceSource directory 8 // 9 #if ARM 10 #define _TARGET_ARM_ 11 #define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter 12 #define CALLDESCR_FPARGREGS // CallDescrWorker has FloatArgumentRegisters parameter 13 #define ENREGISTERED_RETURNTYPE_MAXSIZE 14 #define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE 15 #define FEATURE_HFA 16 #elif ARM64 17 #define _TARGET_ARM64_ 18 #define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter 19 #define CALLDESCR_FPARGREGS // CallDescrWorker has FloatArgumentRegisters parameter 20 #define ENREGISTERED_RETURNTYPE_MAXSIZE 21 #define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE 22 #define ENREGISTERED_PARAMTYPE_MAXSIZE 23 #define FEATURE_HFA 24 #elif X86 25 #define _TARGET_X86_ 26 #define ENREGISTERED_RETURNTYPE_MAXSIZE 27 #define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE 28 #define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter 29 #elif AMD64 30 #if UNIXAMD64 31 #define UNIX_AMD64_ABI 32 #define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter 33 #else 34 #endif 35 #define CALLDESCR_FPARGREGS // CallDescrWorker has FloatArgumentRegisters parameter 36 #define _TARGET_AMD64_ 37 #define ENREGISTERED_RETURNTYPE_MAXSIZE 38 #define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE 39 #define ENREGISTERED_PARAMTYPE_MAXSIZE 40 #elif WASM 41 #define _TARGET_WASM_ 42 #else 43 #error Unknown architecture! 44 #endif 45 46 // Provides an abstraction over platform specific calling conventions (specifically, the calling convention 47 // utilized by the JIT on that platform). The caller enumerates each argument of a signature in turn, and is 48 // provided with information mapping that argument into registers and/or stack locations. 49 50 using System; 51 using System.Collections.Generic; 52 using System.Diagnostics; 53 using Internal.Runtime; 54 using Internal.Runtime.Augments; 55 using Internal.Runtime.TypeLoader; 56 using Internal.NativeFormat; 57 58 namespace Internal.Runtime.CallConverter 59 { 60 public enum CallingConvention 61 { 62 ManagedInstance, 63 ManagedStatic, 64 StdCall, 65 /*FastCall, CDecl */ 66 } 67 68 public static class CallingConventionInfo 69 { TypeUsesReturnBuffer(RuntimeTypeHandle returnType, bool methodWithReturnTypeIsVarArg)70 public static bool TypeUsesReturnBuffer(RuntimeTypeHandle returnType, bool methodWithReturnTypeIsVarArg) 71 { 72 TypeHandle thReturnType = new TypeHandle(false, returnType); 73 CorElementType typeReturnType = thReturnType.GetCorElementType(); 74 75 bool usesReturnBuffer; 76 uint fpReturnSizeIgnored; 77 ArgIterator.ComputeReturnValueTreatment(typeReturnType, thReturnType, methodWithReturnTypeIsVarArg, out usesReturnBuffer, out fpReturnSizeIgnored); 78 79 return usesReturnBuffer; 80 } 81 } 82 83 internal unsafe struct TypeHandle 84 { TypeHandleInternal.Runtime.CallConverter.TypeHandle85 public TypeHandle(bool isByRef, RuntimeTypeHandle eeType) 86 { 87 _eeType = eeType.ToEETypePtr(); 88 _isByRef = isByRef; 89 90 if (_eeType->IsByRefType) 91 { 92 Debug.Assert(_isByRef == false); // ByRef to ByRef isn't valid 93 _isByRef = true; 94 _eeType = _eeType->RelatedParameterType; 95 } 96 } 97 98 private readonly EEType* _eeType; 99 private readonly bool _isByRef; 100 EqualsInternal.Runtime.CallConverter.TypeHandle101 public bool Equals(TypeHandle other) 102 { 103 return _isByRef == other._isByRef && _eeType == other._eeType; 104 } 105 GetHashCodeInternal.Runtime.CallConverter.TypeHandle106 public override int GetHashCode() { return (int)_eeType->HashCode; } 107 IsNullInternal.Runtime.CallConverter.TypeHandle108 public bool IsNull() { return _eeType == null && !_isByRef; } IsValueTypeInternal.Runtime.CallConverter.TypeHandle109 public bool IsValueType() { if (_isByRef) return false; return _eeType->IsValueType; } 110 GetSizeInternal.Runtime.CallConverter.TypeHandle111 public unsafe uint GetSize() 112 { 113 if (IsValueType()) 114 return _eeType->ValueTypeSize; 115 else 116 return (uint)IntPtr.Size; 117 } 118 RequiresAlign8Internal.Runtime.CallConverter.TypeHandle119 public bool RequiresAlign8() 120 { 121 #if !ARM 122 return false; 123 #else 124 if (_isByRef) 125 { 126 return false; 127 } 128 return _eeType->RequiresAlign8; 129 #endif 130 } IsHFAInternal.Runtime.CallConverter.TypeHandle131 public bool IsHFA() 132 { 133 #if !ARM && !ARM64 134 return false; 135 #else 136 if (_isByRef) 137 { 138 return false; 139 } 140 return _eeType->IsHFA; 141 #endif 142 } 143 GetHFATypeInternal.Runtime.CallConverter.TypeHandle144 public CorElementType GetHFAType() 145 { 146 Debug.Assert(IsHFA()); 147 #if ARM 148 if (RequiresAlign8()) 149 { 150 return CorElementType.ELEMENT_TYPE_R8; 151 } 152 #elif ARM64 153 if (_eeType->FieldAlignmentRequirement == IntPtr.Size) 154 { 155 return CorElementType.ELEMENT_TYPE_R8; 156 } 157 #endif 158 return CorElementType.ELEMENT_TYPE_R4; 159 } 160 GetCorElementTypeInternal.Runtime.CallConverter.TypeHandle161 public CorElementType GetCorElementType() 162 { 163 if (_isByRef) 164 { 165 return CorElementType.ELEMENT_TYPE_BYREF; 166 } 167 168 // The core redhawk runtime has a slightly different concept of what CorElementType should be for a type. It matches for primitive and enum types 169 // but for other types, it doesn't match the needs in this file. 170 CorElementType rhCorElementType = _eeType->CorElementType; 171 172 if (((rhCorElementType >= CorElementType.ELEMENT_TYPE_BOOLEAN) && (rhCorElementType <= CorElementType.ELEMENT_TYPE_R8)) || 173 (rhCorElementType == CorElementType.ELEMENT_TYPE_I) || 174 (rhCorElementType == CorElementType.ELEMENT_TYPE_U)) 175 { 176 return rhCorElementType; // If Redhawk thinks the corelementtype is a primitive type, then it agree with the concept of corelement type needed in this codebase. 177 } 178 else if (_eeType == typeof(void).TypeHandle.ToEETypePtr()) 179 { 180 return CorElementType.ELEMENT_TYPE_VOID; 181 } 182 else if (IsValueType()) 183 { 184 return CorElementType.ELEMENT_TYPE_VALUETYPE; 185 } 186 else if (_eeType->IsPointerType) 187 { 188 return CorElementType.ELEMENT_TYPE_PTR; 189 } 190 else 191 { 192 return CorElementType.ELEMENT_TYPE_CLASS; 193 } 194 } 195 196 private static int[] s_elemSizes = new int[] 197 { 198 0, //ELEMENT_TYPE_END 0x0 199 0, //ELEMENT_TYPE_VOID 0x1 200 1, //ELEMENT_TYPE_BOOLEAN 0x2 201 2, //ELEMENT_TYPE_CHAR 0x3 202 1, //ELEMENT_TYPE_I1 0x4 203 1, //ELEMENT_TYPE_U1 0x5 204 2, //ELEMENT_TYPE_I2 0x6 205 2, //ELEMENT_TYPE_U2 0x7 206 4, //ELEMENT_TYPE_I4 0x8 207 4, //ELEMENT_TYPE_U4 0x9 208 8, //ELEMENT_TYPE_I8 0xa 209 8, //ELEMENT_TYPE_U8 0xb 210 4, //ELEMENT_TYPE_R4 0xc 211 8, //ELEMENT_TYPE_R8 0xd 212 -2,//ELEMENT_TYPE_STRING 0xe 213 -2,//ELEMENT_TYPE_PTR 0xf 214 -2,//ELEMENT_TYPE_BYREF 0x10 215 -1,//ELEMENT_TYPE_VALUETYPE 0x11 216 -2,//ELEMENT_TYPE_CLASS 0x12 217 0, //ELEMENT_TYPE_VAR 0x13 218 -2,//ELEMENT_TYPE_ARRAY 0x14 219 0, //ELEMENT_TYPE_GENERICINST 0x15 220 0, //ELEMENT_TYPE_TYPEDBYREF 0x16 221 0, // UNUSED 0x17 222 -2,//ELEMENT_TYPE_I 0x18 223 -2,//ELEMENT_TYPE_U 0x19 224 0, // UNUSED 0x1a 225 -2,//ELEMENT_TYPE_FPTR 0x1b 226 -2,//ELEMENT_TYPE_OBJECT 0x1c 227 -2,//ELEMENT_TYPE_SZARRAY 0x1d 228 }; 229 GetElemSizeInternal.Runtime.CallConverter.TypeHandle230 unsafe public static int GetElemSize(CorElementType t, TypeHandle thValueType) 231 { 232 if (((int)t) <= 0x1d) 233 { 234 int elemSize = s_elemSizes[(int)t]; 235 if (elemSize == -1) 236 { 237 return (int)thValueType.GetSize(); 238 } 239 if (elemSize == -2) 240 { 241 return IntPtr.Size; 242 } 243 return elemSize; 244 } 245 return 0; 246 } 247 GetRuntimeTypeHandleInternal.Runtime.CallConverter.TypeHandle248 public RuntimeTypeHandle GetRuntimeTypeHandle() { return _eeType->ToRuntimeTypeHandle(); } 249 } 250 251 // Describes how a single argument is laid out in registers and/or stack locations when given as an input to a 252 // managed method as part of a larger signature. 253 // 254 // Locations are split into floating point registers, general registers and stack offsets. Registers are 255 // obviously architecture dependent but are represented as a zero-based index into the usual sequence in which 256 // such registers are allocated for input on the platform in question. For instance: 257 // X86: 0 == ecx, 1 == edx 258 // ARM: 0 == r0, 1 == r1, 2 == r2 etc. 259 // 260 // Stack locations are represented as offsets from the stack pointer (at the point of the call). The offset is 261 // given as an index of a pointer sized slot. Similarly the size of data on the stack is given in slot-sized 262 // units. For instance, given an index of 2 and a size of 3: 263 // X86: argument starts at [ESP + 8] and is 12 bytes long 264 // AMD64: argument starts at [RSP + 16] and is 24 bytes long 265 // 266 // The structure is flexible enough to describe an argument that is split over several (consecutive) registers 267 // and possibly on to the stack as well. 268 internal struct ArgLocDesc 269 { 270 public int m_idxFloatReg; // First floating point register used (or -1) 271 public int m_cFloatReg; // Count of floating point registers used (or 0) 272 273 public int m_idxGenReg; // First general register used (or -1) 274 public int m_cGenReg; // Count of general registers used (or 0) 275 276 public int m_idxStack; // First stack slot used (or -1) 277 public int m_cStack; // Count of stack slots used (or 0) 278 279 #if _TARGET_ARM64_ 280 public bool m_isSinglePrecision; // For determining if HFA is single or double precision 281 #endif 282 283 #if _TARGET_ARM_ 284 public bool m_fRequires64BitAlignment; // True if the argument should always be aligned (in registers or on the stack 285 #endif 286 287 // Initialize to represent a non-placed argument (no register or stack slots referenced). InitInternal.Runtime.CallConverter.ArgLocDesc288 public void Init() 289 { 290 m_idxFloatReg = -1; 291 m_cFloatReg = 0; 292 m_idxGenReg = -1; 293 m_cGenReg = 0; 294 m_idxStack = -1; 295 m_cStack = 0; 296 297 #if _TARGET_ARM64_ 298 m_isSinglePrecision = false; 299 #endif 300 301 #if _TARGET_ARM_ 302 m_fRequires64BitAlignment = false; 303 #endif 304 } 305 }; 306 307 internal class ArgIteratorData 308 { ArgIteratorData(bool hasThis, bool isVarArg, TypeHandle[] parameterTypes, TypeHandle returnType)309 public ArgIteratorData(bool hasThis, 310 bool isVarArg, 311 TypeHandle[] parameterTypes, 312 TypeHandle returnType) 313 { 314 _hasThis = hasThis; 315 _isVarArg = isVarArg; 316 _parameterTypes = parameterTypes; 317 _returnType = returnType; 318 } 319 320 private bool _hasThis; 321 private bool _isVarArg; 322 private TypeHandle[] _parameterTypes; 323 private TypeHandle _returnType; 324 Equals(object obj)325 public override bool Equals(object obj) 326 { 327 if (this == obj) return true; 328 329 ArgIteratorData other = obj as ArgIteratorData; 330 if (other == null) return false; 331 332 if (_hasThis != other._hasThis || _isVarArg != other._isVarArg || !_returnType.Equals(other._returnType)) 333 return false; 334 335 if (_parameterTypes == null) 336 return other._parameterTypes == null; 337 338 if (other._parameterTypes == null || _parameterTypes.Length != other._parameterTypes.Length) 339 return false; 340 341 for (int i = 0; i < _parameterTypes.Length; i++) 342 if (!_parameterTypes[i].Equals(other._parameterTypes[i])) 343 return false; 344 345 return true; 346 } 347 GetHashCode()348 public override int GetHashCode() 349 { 350 return 37 + (_parameterTypes == null ? 351 _returnType.GetHashCode() : 352 TypeHashingAlgorithms.ComputeGenericInstanceHashCode(_returnType.GetHashCode(), _parameterTypes)); 353 } 354 HasThis()355 public bool HasThis() { return _hasThis; } IsVarArg()356 public bool IsVarArg() { return _isVarArg; } NumFixedArgs()357 public int NumFixedArgs() { return _parameterTypes != null ? _parameterTypes.Length : 0; } 358 359 // Argument iteration. GetArgumentType(int argNum, out TypeHandle thArgType)360 public CorElementType GetArgumentType(int argNum, out TypeHandle thArgType) 361 { 362 thArgType = _parameterTypes[argNum]; 363 CorElementType returnValue = thArgType.GetCorElementType(); 364 return returnValue; 365 } 366 GetByRefArgumentType(int argNum)367 public TypeHandle GetByRefArgumentType(int argNum) 368 { 369 return (argNum < _parameterTypes.Length && _parameterTypes[argNum].GetCorElementType() == CorElementType.ELEMENT_TYPE_BYREF) ? 370 _parameterTypes[argNum] : 371 default(TypeHandle); 372 } 373 GetReturnType(out TypeHandle thRetType)374 public CorElementType GetReturnType(out TypeHandle thRetType) 375 { 376 thRetType = _returnType; 377 return thRetType.GetCorElementType(); 378 } 379 380 #if CCCONVERTER_TRACE GetEETypeDebugName(int argNum)381 public string GetEETypeDebugName(int argNum) 382 { 383 Internal.TypeSystem.TypeSystemContext context = TypeSystemContextFactory.Create(); 384 var result = context.ResolveRuntimeTypeHandle(_parameterTypes[argNum].GetRuntimeTypeHandle()).ToString(); 385 TypeSystemContextFactory.Recycle(context); 386 return result; 387 } 388 #endif 389 } 390 391 //----------------------------------------------------------------------- 392 // ArgIterator is helper for dealing with calling conventions. 393 // It is tightly coupled with TransitionBlock. It uses offsets into 394 // TransitionBlock to represent argument locations for efficiency 395 // reasons. Alternatively, it can also return ArgLocDesc for less 396 // performance critical code. 397 // 398 // The ARGITERATOR_BASE argument of the template is provider of the parsed 399 // method signature. Typically, the arg iterator works on top of MetaSig. 400 // Reflection invoke uses alternative implementation to save signature parsing 401 // time because of it has the parsed signature available. 402 //----------------------------------------------------------------------- 403 //template<class ARGITERATOR_BASE> 404 internal unsafe struct ArgIterator //: public ARGITERATOR_BASE 405 { 406 private bool _hasThis; 407 private bool _hasParamType; 408 private bool _extraFunctionPointerArg; 409 private ArgIteratorData _argData; 410 private bool[] _forcedByRefParams; 411 private bool _skipFirstArg; 412 private bool _extraObjectFirstArg; 413 private CallingConvention _interpreterCallingConvention; 414 HasThisInternal.Runtime.CallConverter.ArgIterator415 public bool HasThis() { return _hasThis; } IsVarArgInternal.Runtime.CallConverter.ArgIterator416 public bool IsVarArg() { return _argData.IsVarArg(); } HasParamTypeInternal.Runtime.CallConverter.ArgIterator417 public bool HasParamType() { return _hasParamType; } NumFixedArgsInternal.Runtime.CallConverter.ArgIterator418 public int NumFixedArgs() { return _argData.NumFixedArgs() + (_extraFunctionPointerArg ? 1 : 0) + (_extraObjectFirstArg ? 1 : 0); } 419 420 // Argument iteration. GetArgumentTypeInternal.Runtime.CallConverter.ArgIterator421 public CorElementType GetArgumentType(int argNum, out TypeHandle thArgType, out bool forceByRefReturn) 422 { 423 forceByRefReturn = false; 424 425 if (_extraObjectFirstArg && argNum == 0) 426 { 427 thArgType = new TypeHandle(false, typeof(object).TypeHandle); 428 return CorElementType.ELEMENT_TYPE_CLASS; 429 } 430 431 argNum = _extraObjectFirstArg ? argNum - 1 : argNum; 432 Debug.Assert(argNum >= 0); 433 434 if (_forcedByRefParams != null && (argNum + 1) < _forcedByRefParams.Length) 435 forceByRefReturn = _forcedByRefParams[argNum + 1]; 436 437 if (_extraFunctionPointerArg && argNum == _argData.NumFixedArgs()) 438 { 439 thArgType = new TypeHandle(false, typeof(IntPtr).TypeHandle); 440 return CorElementType.ELEMENT_TYPE_I; 441 } 442 443 return _argData.GetArgumentType(argNum, out thArgType); 444 } 445 GetReturnTypeInternal.Runtime.CallConverter.ArgIterator446 public CorElementType GetReturnType(out TypeHandle thRetType, out bool forceByRefReturn) 447 { 448 if (_forcedByRefParams != null && _forcedByRefParams.Length > 0) 449 forceByRefReturn = _forcedByRefParams[0]; 450 else 451 forceByRefReturn = false; 452 453 return _argData.GetReturnType(out thRetType); 454 } 455 456 #if CCCONVERTER_TRACE GetEETypeDebugNameInternal.Runtime.CallConverter.ArgIterator457 public string GetEETypeDebugName(int argNum) 458 { 459 if (_extraObjectFirstArg && argNum == 0) 460 return "System.Object"; 461 return _argData.GetEETypeDebugName(_extraObjectFirstArg ? argNum - 1 : argNum); 462 } 463 #endif 464 ResetInternal.Runtime.CallConverter.ArgIterator465 public void Reset() 466 { 467 _argType = default(CorElementType); 468 _argTypeHandle = default(TypeHandle); 469 _argSize = 0; 470 _argNum = 0; 471 _argForceByRef = false; 472 _ITERATION_STARTED = false; 473 } 474 475 //public: 476 //------------------------------------------------------------ 477 // Constructor 478 //------------------------------------------------------------ ArgIteratorInternal.Runtime.CallConverter.ArgIterator479 public ArgIterator(ArgIteratorData argData, CallingConvention callConv, bool hasParamType, bool extraFunctionPointerArg, bool[] forcedByRefParams, bool skipFirstArg, bool extraObjectFirstArg) 480 { 481 this = default(ArgIterator); 482 _argData = argData; 483 _hasThis = callConv == CallingConvention.ManagedInstance; 484 _hasParamType = hasParamType; 485 _extraFunctionPointerArg = extraFunctionPointerArg; 486 _forcedByRefParams = forcedByRefParams; 487 _skipFirstArg = skipFirstArg; 488 _extraObjectFirstArg = extraObjectFirstArg; 489 _interpreterCallingConvention = callConv; 490 } 491 SetHasParamTypeAndResetInternal.Runtime.CallConverter.ArgIterator492 public void SetHasParamTypeAndReset(bool value) 493 { 494 _hasParamType = value; 495 Reset(); 496 } 497 SetHasThisAndResetInternal.Runtime.CallConverter.ArgIterator498 public void SetHasThisAndReset(bool value) 499 { 500 _hasThis = value; 501 Reset(); 502 } 503 SizeOfArgStackInternal.Runtime.CallConverter.ArgIterator504 private uint SizeOfArgStack() 505 { 506 // WRAPPER_NO_CONTRACT; 507 if (!_SIZE_OF_ARG_STACK_COMPUTED) 508 ForceSigWalk(); 509 Debug.Assert(_SIZE_OF_ARG_STACK_COMPUTED); 510 return (uint)_nSizeOfArgStack; 511 } 512 513 // For use with ArgIterator. This function computes the amount of additional 514 // memory required above the TransitionBlock. The parameter offsets 515 // returned by ArgIterator::GetNextOffset are relative to a 516 // FramedMethodFrame, and may be in either of these regions. SizeOfFrameArgumentArrayInternal.Runtime.CallConverter.ArgIterator517 public int SizeOfFrameArgumentArray() 518 { 519 // WRAPPER_NO_CONTRACT; 520 521 uint size = SizeOfArgStack(); 522 523 #if _TARGET_AMD64_ && !UNIX_AMD64_ABI 524 // The argument registers are not included in the stack size on AMD64 525 size += ArchitectureConstants.ARGUMENTREGISTERS_SIZE; 526 #endif 527 528 return (int)size; 529 } 530 531 //------------------------------------------------------------------------ 532 533 #if _TARGET_X86_ CbStackPopInternal.Runtime.CallConverter.ArgIterator534 public int CbStackPop() 535 { 536 // WRAPPER_NO_CONTRACT; 537 538 if (this.IsVarArg()) 539 return 0; 540 else 541 return (int)SizeOfArgStack(); 542 } 543 #endif 544 545 // Is there a hidden parameter for the return parameter? 546 // HasRetBuffArgInternal.Runtime.CallConverter.ArgIterator547 public bool HasRetBuffArg() 548 { 549 // WRAPPER_NO_CONTRACT; 550 if (!_RETURN_FLAGS_COMPUTED) 551 ComputeReturnFlags(); 552 return _RETURN_HAS_RET_BUFFER; 553 } 554 GetFPReturnSizeInternal.Runtime.CallConverter.ArgIterator555 public uint GetFPReturnSize() 556 { 557 // WRAPPER_NO_CONTRACT; 558 if (!_RETURN_FLAGS_COMPUTED) 559 ComputeReturnFlags(); 560 return _fpReturnSize; 561 } 562 563 #if _TARGET_X86_ 564 //========================================================================= 565 // Indicates whether an argument is to be put in a register using the 566 // default IL calling convention. This should be called on each parameter 567 // in the order it appears in the call signature. For a non-static meethod, 568 // this function should also be called once for the "this" argument, prior 569 // to calling it for the "real" arguments. Pass in a typ of ELEMENT_TYPE_CLASS. 570 // 571 // *pNumRegistersUsed: [in,out]: keeps track of the number of argument 572 // registers assigned previously. The caller should 573 // initialize this variable to 0 - then each call 574 // will update it. 575 // 576 // typ: the signature type 577 //========================================================================= IsArgumentInRegisterInternal.Runtime.CallConverter.ArgIterator578 private static bool IsArgumentInRegister(ref int pNumRegistersUsed, CorElementType typ, TypeHandle thArgType) 579 { 580 // LIMITED_METHOD_CONTRACT; 581 if ((pNumRegistersUsed) < ArchitectureConstants.NUM_ARGUMENT_REGISTERS) 582 { 583 switch (typ) 584 { 585 case CorElementType.ELEMENT_TYPE_BOOLEAN: 586 case CorElementType.ELEMENT_TYPE_CHAR: 587 case CorElementType.ELEMENT_TYPE_I1: 588 case CorElementType.ELEMENT_TYPE_U1: 589 case CorElementType.ELEMENT_TYPE_I2: 590 case CorElementType.ELEMENT_TYPE_U2: 591 case CorElementType.ELEMENT_TYPE_I4: 592 case CorElementType.ELEMENT_TYPE_U4: 593 case CorElementType.ELEMENT_TYPE_STRING: 594 case CorElementType.ELEMENT_TYPE_PTR: 595 case CorElementType.ELEMENT_TYPE_BYREF: 596 case CorElementType.ELEMENT_TYPE_CLASS: 597 case CorElementType.ELEMENT_TYPE_ARRAY: 598 case CorElementType.ELEMENT_TYPE_I: 599 case CorElementType.ELEMENT_TYPE_U: 600 case CorElementType.ELEMENT_TYPE_FNPTR: 601 case CorElementType.ELEMENT_TYPE_OBJECT: 602 case CorElementType.ELEMENT_TYPE_SZARRAY: 603 pNumRegistersUsed++; 604 return true; 605 606 case CorElementType.ELEMENT_TYPE_VALUETYPE: 607 { 608 // On ProjectN valuetypes of integral size are passed enregistered 609 int structSize = TypeHandle.GetElemSize(typ, thArgType); 610 switch (structSize) 611 { 612 case 1: 613 case 2: 614 case 4: 615 pNumRegistersUsed++; 616 return true; 617 } 618 break; 619 } 620 } 621 } 622 623 return (false); 624 } 625 #endif // _TARGET_X86_ 626 627 #if ENREGISTERED_PARAMTYPE_MAXSIZE 628 629 // Note that this overload does not handle varargs IsArgPassedByRefInternal.Runtime.CallConverter.ArgIterator630 private static bool IsArgPassedByRef(TypeHandle th) 631 { 632 // LIMITED_METHOD_CONTRACT; 633 634 Debug.Assert(!th.IsNull()); 635 636 // This method only works for valuetypes. It includes true value types, 637 // primitives, enums and TypedReference. 638 Debug.Assert(th.IsValueType()); 639 640 uint size = th.GetSize(); 641 #if _TARGET_AMD64_ 642 return IsArgPassedByRef((int)size); 643 #elif _TARGET_ARM64_ 644 // Composites greater than 16 bytes are passed by reference 645 return ((size > ArchitectureConstants.ENREGISTERED_PARAMTYPE_MAXSIZE) && !th.IsHFA()); 646 #else 647 #error ArgIterator::IsArgPassedByRef 648 #endif 649 } 650 651 #if _TARGET_AMD64_ 652 // This overload should only be used in AMD64-specific code only. IsArgPassedByRefInternal.Runtime.CallConverter.ArgIterator653 private static bool IsArgPassedByRef(int size) 654 { 655 // LIMITED_METHOD_CONTRACT; 656 657 // If the size is bigger than ENREGISTERED_PARAM_TYPE_MAXSIZE, or if the size is NOT a power of 2, then 658 // the argument is passed by reference. 659 return (size > ArchitectureConstants.ENREGISTERED_PARAMTYPE_MAXSIZE) || ((size & (size - 1)) != 0); 660 } 661 #endif 662 663 // This overload should be used for varargs only. IsVarArgPassedByRefInternal.Runtime.CallConverter.ArgIterator664 private static bool IsVarArgPassedByRef(int size) 665 { 666 // LIMITED_METHOD_CONTRACT; 667 668 #if _TARGET_AMD64_ 669 return IsArgPassedByRef(size); 670 #else 671 return (size > ArchitectureConstants.ENREGISTERED_PARAMTYPE_MAXSIZE); 672 #endif 673 } 674 #endif // ENREGISTERED_PARAMTYPE_MAXSIZE 675 IsArgPassedByRefInternal.Runtime.CallConverter.ArgIterator676 public bool IsArgPassedByRef() 677 { 678 // LIMITED_METHOD_CONTRACT; 679 if (IsArgForcedPassedByRef()) 680 { 681 return true; 682 } 683 684 if (_argType == CorElementType.ELEMENT_TYPE_BYREF) 685 { 686 return true; 687 } 688 #if ENREGISTERED_PARAMTYPE_MAXSIZE 689 #if _TARGET_AMD64_ 690 return IsArgPassedByRef(_argSize); 691 #elif _TARGET_ARM64_ 692 if (_argType == CorElementType.ELEMENT_TYPE_VALUETYPE) 693 { 694 Debug.Assert(!_argTypeHandle.IsNull()); 695 return ((_argSize > ArchitectureConstants.ENREGISTERED_PARAMTYPE_MAXSIZE) && (!_argTypeHandle.IsHFA() || IsVarArg())); 696 } 697 return false; 698 #else 699 #error PORTABILITY_ASSERT("ArgIterator::IsArgPassedByRef"); 700 #endif 701 #else // ENREGISTERED_PARAMTYPE_MAXSIZE 702 return false; 703 #endif // ENREGISTERED_PARAMTYPE_MAXSIZE 704 } 705 IsArgForcedPassedByRefInternal.Runtime.CallConverter.ArgIterator706 private bool IsArgForcedPassedByRef() 707 { 708 // This should be true for valuetypes instantiated over T in a generic signature using universal shared generic calling convention 709 return _argForceByRef; 710 } 711 712 //------------------------------------------------------------ 713 // Return the offsets of the special arguments 714 //------------------------------------------------------------ 715 GetThisOffsetInternal.Runtime.CallConverter.ArgIterator716 public static int GetThisOffset() 717 { 718 return TransitionBlock.GetThisOffset(); 719 } 720 GetRetBuffArgOffsetInternal.Runtime.CallConverter.ArgIterator721 public unsafe int GetRetBuffArgOffset() 722 { 723 // WRAPPER_NO_CONTRACT; 724 725 Debug.Assert(this.HasRetBuffArg()); 726 727 #if _TARGET_X86_ 728 // x86 is special as always 729 // DESKTOP BEHAVIOR ret += this.HasThis() ? ArgumentRegisters.GetOffsetOfEdx() : ArgumentRegisters.GetOffsetOfEcx(); 730 int ret = TransitionBlock.GetOffsetOfArgs(); 731 #else 732 // RetBuf arg is in the first argument register by default 733 int ret = TransitionBlock.GetOffsetOfArgumentRegisters(); 734 735 #if _TARGET_ARM64_ 736 ret += ArgumentRegisters.GetOffsetOfx8(); 737 #else 738 // But if there is a this pointer, push it to the second. 739 if (this.HasThis()) 740 ret += IntPtr.Size; 741 #endif // _TARGET_ARM64_ 742 #endif // _TARGET_X86_ 743 744 return ret; 745 } 746 GetVASigCookieOffsetInternal.Runtime.CallConverter.ArgIterator747 unsafe public int GetVASigCookieOffset() 748 { 749 // WRAPPER_NO_CONTRACT; 750 751 Debug.Assert(this.IsVarArg()); 752 753 #if _TARGET_X86_ 754 // x86 is special as always 755 return sizeof(TransitionBlock); 756 #else 757 // VaSig cookie is after this and retbuf arguments by default. 758 int ret = TransitionBlock.GetOffsetOfArgumentRegisters(); 759 760 if (this.HasThis()) 761 { 762 ret += IntPtr.Size; 763 } 764 765 if (this.HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) 766 { 767 ret += IntPtr.Size; 768 } 769 770 return ret; 771 #endif 772 } 773 GetParamTypeArgOffsetInternal.Runtime.CallConverter.ArgIterator774 unsafe public int GetParamTypeArgOffset() 775 { 776 Debug.Assert(this.HasParamType()); 777 778 #if _TARGET_X86_ 779 // x86 is special as always 780 if (!_SIZE_OF_ARG_STACK_COMPUTED) 781 ForceSigWalk(); 782 783 switch (_paramTypeLoc) 784 { 785 case ParamTypeLocation.Ecx:// PARAM_TYPE_REGISTER_ECX: 786 return TransitionBlock.GetOffsetOfArgumentRegisters() + ArgumentRegisters.GetOffsetOfEcx(); 787 case ParamTypeLocation.Edx: 788 return TransitionBlock.GetOffsetOfArgumentRegisters() + ArgumentRegisters.GetOffsetOfEdx(); 789 default: 790 break; 791 } 792 793 // The param type arg is last stack argument otherwise 794 return sizeof(TransitionBlock); 795 #else 796 // The hidden arg is after this and retbuf arguments by default. 797 int ret = TransitionBlock.GetOffsetOfArgumentRegisters(); 798 799 if (this.HasThis()) 800 { 801 ret += IntPtr.Size; 802 } 803 804 if (this.HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) 805 { 806 ret += IntPtr.Size; 807 } 808 809 return ret; 810 #endif 811 } 812 813 //------------------------------------------------------------ 814 // Each time this is called, this returns a byte offset of the next 815 // argument from the TransitionBlock* pointer. This offset can be positive *or* negative. 816 // 817 // Returns TransitionBlock::InvalidOffset once you've hit the end 818 // of the list. 819 //------------------------------------------------------------ GetNextOffsetInternal.Runtime.CallConverter.ArgIterator820 public unsafe int GetNextOffset() 821 { 822 // WRAPPER_NO_CONTRACT; 823 // SUPPORTS_DAC; 824 825 if (!_ITERATION_STARTED) 826 { 827 int numRegistersUsed = 0; 828 #if _TARGET_X86_ 829 int initialArgOffset = 0; 830 #endif 831 if (this.HasThis()) 832 numRegistersUsed++; 833 834 if (this.HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) 835 { 836 #if !_TARGET_X86_ 837 numRegistersUsed++; 838 #else 839 // DESKTOP BEHAVIOR is to do nothing here, as ret buf is never reached by the scan algortithm that walks backwards 840 // but in .NET Native, the x86 argument scan is a forward scan, so we need to skip the ret buf arg (which is always 841 // on the stack) 842 initialArgOffset = IntPtr.Size; 843 #endif 844 } 845 846 Debug.Assert(!this.IsVarArg() || !this.HasParamType()); 847 848 // DESKTOP BEHAVIOR - This block is disabled for x86 as the param arg is the last argument on desktop x86. 849 if (this.HasParamType()) 850 { 851 numRegistersUsed++; 852 } 853 854 #if !_TARGET_X86_ 855 if (this.IsVarArg()) 856 { 857 numRegistersUsed++; 858 } 859 #endif 860 861 #if _TARGET_X86_ 862 if (this.IsVarArg()) 863 { 864 numRegistersUsed = ArchitectureConstants.NUM_ARGUMENT_REGISTERS; // Nothing else gets passed in registers for varargs 865 } 866 867 #if FEATURE_INTERPRETER 868 switch (_interpreterCallingConvention) 869 { 870 case CallingConvention.StdCall: 871 _numRegistersUsed = ArchitectureConstants.NUM_ARGUMENT_REGISTERS; 872 _curOfs = TransitionBlock.GetOffsetOfArgs() + numRegistersUsed * IntPtr.Size + initialArgOffset; 873 break; 874 875 case CallingConvention.ManagedStatic: 876 case CallingConvention.ManagedInstance: 877 _numRegistersUsed = numRegistersUsed; 878 // DESKTOP BEHAVIOR _curOfs = (int)(TransitionBlock.GetOffsetOfArgs() + SizeOfArgStack()); 879 _curOfs = (int)(TransitionBlock.GetOffsetOfArgs() + initialArgOffset); 880 break; 881 882 default: 883 Environment.FailFast("Unsupported calling convention."); 884 break; 885 } 886 #else 887 _numRegistersUsed = numRegistersUsed; 888 // DESKTOP BEHAVIOR _curOfs = (int)(TransitionBlock.GetOffsetOfArgs() + SizeOfArgStack()); 889 _curOfs = (int)(TransitionBlock.GetOffsetOfArgs() + initialArgOffset); 890 #endif 891 892 #elif _TARGET_AMD64_ 893 #if UNIX_AMD64_ABI 894 _idxGenReg = numRegistersUsed; 895 _idxStack = 0; 896 _idxFPReg = 0; 897 #else 898 _curOfs = TransitionBlock.GetOffsetOfArgs() + numRegistersUsed * IntPtr.Size; 899 #endif 900 #elif _TARGET_ARM_ 901 _idxGenReg = numRegistersUsed; 902 _idxStack = 0; 903 904 _wFPRegs = 0; 905 #elif _TARGET_ARM64_ 906 _idxGenReg = numRegistersUsed; 907 _idxStack = 0; 908 909 _idxFPReg = 0; 910 #elif _TARGET_WASM_ 911 throw new NotImplementedException(); 912 #else 913 PORTABILITY_ASSERT("ArgIterator::GetNextOffset"); 914 #endif 915 916 #if !_TARGET_WASM_ 917 _argNum = (_skipFirstArg ? 1 : 0); 918 919 _ITERATION_STARTED = true; 920 #endif // !_TARGET_WASM_ 921 } 922 923 if (_argNum >= this.NumFixedArgs()) 924 return TransitionBlock.InvalidOffset; 925 926 CorElementType argType = this.GetArgumentType(_argNum, out _argTypeHandle, out _argForceByRef); 927 928 _argTypeHandleOfByRefParam = (argType == CorElementType.ELEMENT_TYPE_BYREF ? _argData.GetByRefArgumentType(_argNum) : default(TypeHandle)); 929 930 _argNum++; 931 932 int argSize = TypeHandle.GetElemSize(argType, _argTypeHandle); 933 934 #if _TARGET_ARM64_ 935 // NOT DESKTOP BEHAVIOR: The S and D registers overlap, and the UniversalTransitionThunk copies D registers to the transition blocks. We'll need 936 // to work with the D registers here as well. 937 if (argType == CorElementType.ELEMENT_TYPE_VALUETYPE && _argTypeHandle.IsHFA() && _argTypeHandle.GetHFAType() == CorElementType.ELEMENT_TYPE_R4) 938 { 939 argSize *= 2; 940 } 941 #endif 942 943 _argType = argType; 944 _argSize = argSize; 945 946 argType = _argForceByRef ? CorElementType.ELEMENT_TYPE_BYREF : argType; 947 argSize = _argForceByRef ? IntPtr.Size : argSize; 948 949 #pragma warning disable 219,168 // Unused local 950 int argOfs; 951 #pragma warning restore 219,168 952 953 #if _TARGET_X86_ 954 #if FEATURE_INTERPRETER 955 if (_interpreterCallingConvention != CallingConvention.ManagedStatic && _interpreterCallingConvention != CallingConvention.ManagedInstance) 956 { 957 argOfs = _curOfs; 958 _curOfs += ArchitectureConstants.StackElemSize(argSize); 959 return argOfs; 960 } 961 #endif 962 if (IsArgumentInRegister(ref _numRegistersUsed, argType, _argTypeHandle)) 963 { 964 return TransitionBlock.GetOffsetOfArgumentRegisters() + (ArchitectureConstants.NUM_ARGUMENT_REGISTERS - _numRegistersUsed) * IntPtr.Size; 965 } 966 967 // DESKTOP BEHAVIOR _curOfs -= ArchitectureConstants.StackElemSize(argSize); 968 // DESKTOP BEHAVIOR return _curOfs; 969 argOfs = _curOfs; 970 _curOfs += ArchitectureConstants.StackElemSize(argSize); 971 Debug.Assert(argOfs >= TransitionBlock.GetOffsetOfArgs()); 972 return argOfs; 973 #elif _TARGET_AMD64_ 974 #if UNIX_AMD64_ABI 975 int cFPRegs = 0; 976 977 switch (argType) 978 { 979 980 case CorElementType.ELEMENT_TYPE_R4: 981 // 32-bit floating point argument. 982 cFPRegs = 1; 983 break; 984 985 case CorElementType.ELEMENT_TYPE_R8: 986 // 64-bit floating point argument. 987 cFPRegs = 1; 988 break; 989 990 case CorElementType.ELEMENT_TYPE_VALUETYPE: 991 { 992 // UNIXTODO: FEATURE_UNIX_AMD64_STRUCT_PASSING: Passing of structs, HFAs. For now, use the Windows convention. 993 argSize = IntPtr.Size; 994 break; 995 } 996 997 default: 998 break; 999 } 1000 1001 int cbArg = ArchitectureConstants.StackElemSize(argSize); 1002 int cArgSlots = cbArg / ArchitectureConstants.STACK_ELEM_SIZE; 1003 1004 if (cFPRegs > 0) 1005 { 1006 if (cFPRegs + m_idxFPReg <= 8) 1007 { 1008 int argOfsInner = TransitionBlock.GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8; 1009 m_idxFPReg += cFPRegs; 1010 return argOfsInner; 1011 } 1012 } 1013 else 1014 { 1015 if (m_idxGenReg + cArgSlots <= 6) 1016 { 1017 int argOfsInner = TransitionBlock.GetOffsetOfArgumentRegisters() + m_idxGenReg * 8; 1018 m_idxGenReg += cArgSlots; 1019 return argOfsInner; 1020 } 1021 } 1022 1023 argOfs = TransitionBlock.GetOffsetOfArgs() + m_idxStack * 8; 1024 m_idxStack += cArgSlots; 1025 return argOfs; 1026 #else 1027 int cFPRegs = 0; 1028 1029 switch (argType) 1030 { 1031 case CorElementType.ELEMENT_TYPE_R4: 1032 // 32-bit floating point argument. 1033 cFPRegs = 1; 1034 break; 1035 1036 case CorElementType.ELEMENT_TYPE_R8: 1037 // 64-bit floating point argument. 1038 cFPRegs = 1; 1039 break; 1040 } 1041 1042 // Each argument takes exactly one slot on AMD64 1043 argOfs = _curOfs - TransitionBlock.GetOffsetOfArgs(); 1044 _curOfs += IntPtr.Size; 1045 1046 if ((cFPRegs == 0) || (argOfs >= sizeof(ArgumentRegisters))) 1047 { 1048 return argOfs + TransitionBlock.GetOffsetOfArgs(); 1049 } 1050 else 1051 { 1052 int idxFpReg = argOfs / IntPtr.Size; 1053 return TransitionBlock.GetOffsetOfFloatArgumentRegisters() + idxFpReg * sizeof(M128A); 1054 } 1055 #endif 1056 #elif _TARGET_ARM_ 1057 // First look at the underlying type of the argument to determine some basic properties: 1058 // 1) The size of the argument in bytes (rounded up to the stack slot size of 4 if necessary). 1059 // 2) Whether the argument represents a floating point primitive (ELEMENT_TYPE_R4 or ELEMENT_TYPE_R8). 1060 // 3) Whether the argument requires 64-bit alignment (anything that contains a Int64/UInt64). 1061 1062 bool fFloatingPoint = false; 1063 bool fRequiresAlign64Bit = false; 1064 1065 switch (argType) 1066 { 1067 case CorElementType.ELEMENT_TYPE_I8: 1068 case CorElementType.ELEMENT_TYPE_U8: 1069 // 64-bit integers require 64-bit alignment on ARM. 1070 fRequiresAlign64Bit = true; 1071 break; 1072 1073 case CorElementType.ELEMENT_TYPE_R4: 1074 // 32-bit floating point argument. 1075 fFloatingPoint = true; 1076 break; 1077 1078 case CorElementType.ELEMENT_TYPE_R8: 1079 // 64-bit floating point argument. 1080 fFloatingPoint = true; 1081 fRequiresAlign64Bit = true; 1082 break; 1083 1084 case CorElementType.ELEMENT_TYPE_VALUETYPE: 1085 { 1086 // Value type case: extract the alignment requirement, note that this has to handle 1087 // the interop "native value types". 1088 fRequiresAlign64Bit = _argTypeHandle.RequiresAlign8(); 1089 1090 // Handle HFAs: packed structures of 1-4 floats or doubles that are passed in FP argument 1091 // registers if possible. 1092 if (_argTypeHandle.IsHFA()) 1093 fFloatingPoint = true; 1094 1095 break; 1096 } 1097 1098 default: 1099 // The default is are 4-byte arguments (or promoted to 4 bytes), non-FP and don't require any 1100 // 64-bit alignment. 1101 break; 1102 } 1103 1104 // Now attempt to place the argument into some combination of floating point or general registers and 1105 // the stack. 1106 1107 // Save the alignment requirement 1108 _fRequires64BitAlignment = fRequiresAlign64Bit; 1109 1110 int cbArg = ArchitectureConstants.StackElemSize(argSize); 1111 int cArgSlots = cbArg / 4; 1112 1113 // Ignore floating point argument placement in registers if we're dealing with a vararg function (the ABI 1114 // specifies this so that vararg processing on the callee side is simplified). 1115 if (fFloatingPoint && !this.IsVarArg()) 1116 { 1117 // Handle floating point (primitive) arguments. 1118 1119 // First determine whether we can place the argument in VFP registers. There are 16 32-bit 1120 // and 8 64-bit argument registers that share the same register space (e.g. D0 overlaps S0 and 1121 // S1). The ABI specifies that VFP values will be passed in the lowest sequence of registers that 1122 // haven't been used yet and have the required alignment. So the sequence (float, double, float) 1123 // would be mapped to (S0, D1, S1) or (S0, S2/S3, S1). 1124 // 1125 // We use a 16-bit bitmap to record which registers have been used so far. 1126 // 1127 // So we can use the same basic loop for each argument type (float, double or HFA struct) we set up 1128 // the following input parameters based on the size and alignment requirements of the arguments: 1129 // wAllocMask : bitmask of the number of 32-bit registers we need (1 for 1, 3 for 2, 7 for 3 etc.) 1130 // cSteps : number of loop iterations it'll take to search the 16 registers 1131 // cShift : how many bits to shift the allocation mask on each attempt 1132 1133 ushort wAllocMask = checked((ushort)((1 << (cbArg / 4)) - 1)); 1134 ushort cSteps = (ushort)(fRequiresAlign64Bit ? 9 - (cbArg / 8) : 17 - (cbArg / 4)); 1135 ushort cShift = fRequiresAlign64Bit ? (ushort)2 : (ushort)1; 1136 1137 // Look through the availability bitmask for a free register or register pair. 1138 for (ushort i = 0; i < cSteps; i++) 1139 { 1140 if ((_wFPRegs & wAllocMask) == 0) 1141 { 1142 // We found one, mark the register or registers as used. 1143 _wFPRegs |= wAllocMask; 1144 1145 // Indicate the registers used to the caller and return. 1146 return TransitionBlock.GetOffsetOfFloatArgumentRegisters() + (i * cShift * 4); 1147 } 1148 wAllocMask <<= cShift; 1149 } 1150 1151 // The FP argument is going to live on the stack. Once this happens the ABI demands we mark all FP 1152 // registers as unavailable. 1153 _wFPRegs = 0xffff; 1154 1155 // Doubles or HFAs containing doubles need the stack aligned appropriately. 1156 if (fRequiresAlign64Bit) 1157 _idxStack = ALIGN_UP(_idxStack, 2); 1158 1159 // Indicate the stack location of the argument to the caller. 1160 int argOfsInner = TransitionBlock.GetOffsetOfArgs() + _idxStack * 4; 1161 1162 // Record the stack usage. 1163 _idxStack += cArgSlots; 1164 1165 return argOfsInner; 1166 } 1167 1168 // 1169 // Handle the non-floating point case. 1170 // 1171 1172 if (_idxGenReg < 4) 1173 { 1174 if (fRequiresAlign64Bit) 1175 { 1176 // The argument requires 64-bit alignment. Align either the next general argument register if 1177 // we have any left. See step C.3 in the algorithm in the ABI spec. 1178 _idxGenReg = ALIGN_UP(_idxGenReg, 2); 1179 } 1180 1181 int argOfsInner = TransitionBlock.GetOffsetOfArgumentRegisters() + _idxGenReg * 4; 1182 1183 int cRemainingRegs = 4 - _idxGenReg; 1184 if (cArgSlots <= cRemainingRegs) 1185 { 1186 // Mark the registers just allocated as used. 1187 _idxGenReg += cArgSlots; 1188 return argOfsInner; 1189 } 1190 1191 // The ABI supports splitting a non-FP argument across registers and the stack. But this is 1192 // disabled if the FP arguments already overflowed onto the stack (i.e. the stack index is not 1193 // zero). The following code marks the general argument registers as exhausted if this condition 1194 // holds. See steps C.5 in the algorithm in the ABI spec. 1195 1196 _idxGenReg = 4; 1197 1198 if (_idxStack == 0) 1199 { 1200 _idxStack += cArgSlots - cRemainingRegs; 1201 return argOfsInner; 1202 } 1203 } 1204 1205 if (fRequiresAlign64Bit) 1206 { 1207 // The argument requires 64-bit alignment. If it is going to be passed on the stack, align 1208 // the next stack slot. See step C.6 in the algorithm in the ABI spec. 1209 _idxStack = ALIGN_UP(_idxStack, 2); 1210 } 1211 1212 argOfs = TransitionBlock.GetOffsetOfArgs() + _idxStack * 4; 1213 1214 // Advance the stack pointer over the argument just placed. 1215 _idxStack += cArgSlots; 1216 1217 return argOfs; 1218 #elif _TARGET_ARM64_ 1219 1220 int cFPRegs = 0; 1221 1222 switch (argType) 1223 { 1224 case CorElementType.ELEMENT_TYPE_R4: 1225 // 32-bit floating point argument. 1226 cFPRegs = 1; 1227 break; 1228 1229 case CorElementType.ELEMENT_TYPE_R8: 1230 // 64-bit floating point argument. 1231 cFPRegs = 1; 1232 break; 1233 1234 case CorElementType.ELEMENT_TYPE_VALUETYPE: 1235 { 1236 // Handle HFAs: packed structures of 2-4 floats or doubles that are passed in FP argument 1237 // registers if possible. 1238 if (_argTypeHandle.IsHFA()) 1239 { 1240 CorElementType type = _argTypeHandle.GetHFAType(); 1241 // DESKTOP BEHAVIOR cFPRegs = (type == CorElementType.ELEMENT_TYPE_R4) ? (argSize / sizeof(float)) : (argSize / sizeof(double)); 1242 cFPRegs = argSize / sizeof(double); 1243 } 1244 else 1245 { 1246 // Composite greater than 16bytes should be passed by reference 1247 if (argSize > ArchitectureConstants.ENREGISTERED_PARAMTYPE_MAXSIZE) 1248 { 1249 argSize = IntPtr.Size; 1250 } 1251 } 1252 1253 break; 1254 } 1255 1256 default: 1257 break; 1258 } 1259 1260 int cbArg = ArchitectureConstants.StackElemSize(argSize); 1261 int cArgSlots = cbArg / ArchitectureConstants.STACK_ELEM_SIZE; 1262 1263 if (cFPRegs > 0 && !this.IsVarArg()) 1264 { 1265 if (cFPRegs + _idxFPReg <= 8) 1266 { 1267 int argOfsInner = TransitionBlock.GetOffsetOfFloatArgumentRegisters() + _idxFPReg * 8; 1268 _idxFPReg += cFPRegs; 1269 return argOfsInner; 1270 } 1271 else 1272 { 1273 _idxFPReg = 8; 1274 } 1275 } 1276 else 1277 { 1278 if (_idxGenReg + cArgSlots <= 8) 1279 { 1280 int argOfsInner = TransitionBlock.GetOffsetOfArgumentRegisters() + _idxGenReg * 8; 1281 _idxGenReg += cArgSlots; 1282 return argOfsInner; 1283 } 1284 else 1285 { 1286 _idxGenReg = 8; 1287 } 1288 } 1289 1290 argOfs = TransitionBlock.GetOffsetOfArgs() + _idxStack * 8; 1291 _idxStack += cArgSlots; 1292 return argOfs; 1293 #elif _TARGET_WASM_ 1294 throw new NotImplementedException(); 1295 #else 1296 #error PORTABILITY_ASSERT("ArgIterator::GetNextOffset"); 1297 #endif 1298 } 1299 1300 GetArgTypeInternal.Runtime.CallConverter.ArgIterator1301 public CorElementType GetArgType(out TypeHandle pTypeHandle) 1302 { 1303 // LIMITED_METHOD_CONTRACT; 1304 pTypeHandle = _argTypeHandle; 1305 return _argType; 1306 } 1307 GetByRefArgTypeInternal.Runtime.CallConverter.ArgIterator1308 public CorElementType GetByRefArgType(out TypeHandle pByRefArgTypeHandle) 1309 { 1310 // LIMITED_METHOD_CONTRACT; 1311 pByRefArgTypeHandle = _argTypeHandleOfByRefParam; 1312 return _argType; 1313 } 1314 GetArgSizeInternal.Runtime.CallConverter.ArgIterator1315 public int GetArgSize() 1316 { 1317 // LIMITED_METHOD_CONTRACT; 1318 return _argSize; 1319 } 1320 ForceSigWalkInternal.Runtime.CallConverter.ArgIterator1321 private unsafe void ForceSigWalk() 1322 { 1323 // This can be only used before the actual argument iteration started 1324 Debug.Assert(!_ITERATION_STARTED); 1325 1326 #if _TARGET_X86_ 1327 // 1328 // x86 is special as always 1329 // 1330 1331 int numRegistersUsed = 0; 1332 int nSizeOfArgStack = 0; 1333 1334 if (this.HasThis()) 1335 numRegistersUsed++; 1336 1337 if (this.HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) 1338 { 1339 // DESKTOP BEHAVIOR numRegistersUsed++; 1340 // On ProjectN ret buff arg is passed on the call stack as the top stack arg 1341 nSizeOfArgStack += IntPtr.Size; 1342 } 1343 1344 // DESKTOP BEHAVIOR - This block is disabled for x86 as the param arg is the last argument on desktop x86. 1345 if (this.HasParamType()) 1346 { 1347 numRegistersUsed++; 1348 _paramTypeLoc = (numRegistersUsed == 1) ? 1349 ParamTypeLocation.Ecx : ParamTypeLocation.Edx; 1350 Debug.Assert(numRegistersUsed <= 2); 1351 } 1352 1353 if (this.IsVarArg()) 1354 { 1355 nSizeOfArgStack += IntPtr.Size; 1356 numRegistersUsed = ArchitectureConstants.NUM_ARGUMENT_REGISTERS; // Nothing else gets passed in registers for varargs 1357 } 1358 1359 #if FEATURE_INTERPRETER 1360 switch (_interpreterCallingConvention) 1361 { 1362 case CallingConvention.StdCall: 1363 numRegistersUsed = ArchitectureConstants.NUM_ARGUMENT_REGISTERS; 1364 break; 1365 1366 case CallingConvention.ManagedStatic: 1367 case CallingConvention.ManagedInstance: 1368 break; 1369 1370 default: 1371 Environment.FailFast("Unsupported calling convention."); 1372 break; 1373 } 1374 #endif // FEATURE_INTERPRETER 1375 1376 int nArgs = this.NumFixedArgs(); 1377 for (int i = (_skipFirstArg ? 1 : 0); i < nArgs; i++) 1378 { 1379 TypeHandle thArgType; 1380 bool argForcedToBeByref; 1381 CorElementType type = this.GetArgumentType(i, out thArgType, out argForcedToBeByref); 1382 if (argForcedToBeByref) 1383 type = CorElementType.ELEMENT_TYPE_BYREF; 1384 1385 if (!IsArgumentInRegister(ref numRegistersUsed, type, thArgType)) 1386 { 1387 int structSize = TypeHandle.GetElemSize(type, thArgType); 1388 1389 nSizeOfArgStack += ArchitectureConstants.StackElemSize(structSize); 1390 1391 if (nSizeOfArgStack > ArchitectureConstants.MAX_ARG_SIZE) 1392 { 1393 throw new NotSupportedException(); 1394 } 1395 } 1396 } 1397 1398 #if DESKTOP // DESKTOP BEHAVIOR 1399 if (this.HasParamType()) 1400 { 1401 if (numRegistersUsed < ArchitectureConstants.NUM_ARGUMENT_REGISTERS) 1402 { 1403 numRegistersUsed++; 1404 paramTypeLoc = (numRegistersUsed == 1) ? 1405 ParamTypeLocation.Ecx : ParamTypeLocation.Edx; 1406 } 1407 else 1408 { 1409 nSizeOfArgStack += IntPtr.Size; 1410 paramTypeLoc = ParamTypeLocation.Stack; 1411 } 1412 } 1413 #endif // DESKTOP BEHAVIOR 1414 1415 #else // _TARGET_X86_ 1416 1417 int maxOffset = TransitionBlock.GetOffsetOfArgs(); 1418 1419 int ofs; 1420 while (TransitionBlock.InvalidOffset != (ofs = GetNextOffset())) 1421 { 1422 int stackElemSize; 1423 1424 #if _TARGET_AMD64_ 1425 // All stack arguments take just one stack slot on AMD64 because of arguments bigger 1426 // than a stack slot are passed by reference. 1427 stackElemSize = ArchitectureConstants.STACK_ELEM_SIZE; 1428 #else 1429 stackElemSize = ArchitectureConstants.StackElemSize(GetArgSize()); 1430 if (IsArgPassedByRef()) 1431 stackElemSize = ArchitectureConstants.STACK_ELEM_SIZE; 1432 #endif 1433 1434 int endOfs = ofs + stackElemSize; 1435 if (endOfs > maxOffset) 1436 { 1437 if (endOfs > ArchitectureConstants.MAX_ARG_SIZE) 1438 { 1439 throw new NotSupportedException(); 1440 } 1441 maxOffset = endOfs; 1442 } 1443 } 1444 // Clear the iterator started flag 1445 _ITERATION_STARTED = false; 1446 1447 int nSizeOfArgStack = maxOffset - TransitionBlock.GetOffsetOfArgs(); 1448 1449 #if _TARGET_AMD64_ && !UNIX_AMD64_ABI 1450 nSizeOfArgStack = (nSizeOfArgStack > (int)sizeof(ArgumentRegisters)) ? 1451 (nSizeOfArgStack - sizeof(ArgumentRegisters)) : 0; 1452 #endif 1453 1454 #endif // _TARGET_X86_ 1455 1456 // Cache the result 1457 _nSizeOfArgStack = nSizeOfArgStack; 1458 _SIZE_OF_ARG_STACK_COMPUTED = true; 1459 1460 this.Reset(); 1461 } 1462 1463 1464 #if !_TARGET_X86_ 1465 // Accessors for built in argument descriptions of the special implicit parameters not mentioned directly 1466 // in signatures (this pointer and the like). Whether or not these can be used successfully before all the 1467 // explicit arguments have been scanned is platform dependent. GetThisLocInternal.Runtime.CallConverter.ArgIterator1468 public unsafe void GetThisLoc(ArgLocDesc* pLoc) { GetSimpleLoc(GetThisOffset(), pLoc); } GetRetBuffArgLocInternal.Runtime.CallConverter.ArgIterator1469 public unsafe void GetRetBuffArgLoc(ArgLocDesc* pLoc) { GetSimpleLoc(GetRetBuffArgOffset(), pLoc); } GetParamTypeLocInternal.Runtime.CallConverter.ArgIterator1470 public unsafe void GetParamTypeLoc(ArgLocDesc* pLoc) { GetSimpleLoc(GetParamTypeArgOffset(), pLoc); } GetVASigCookieLocInternal.Runtime.CallConverter.ArgIterator1471 public unsafe void GetVASigCookieLoc(ArgLocDesc* pLoc) { GetSimpleLoc(GetVASigCookieOffset(), pLoc); } 1472 #endif // !_TARGET_X86_ 1473 1474 #if _TARGET_ARM_ 1475 // Get layout information for the argument that the ArgIterator is currently visiting. GetArgLocInternal.Runtime.CallConverter.ArgIterator1476 private unsafe void GetArgLoc(int argOffset, ArgLocDesc* pLoc) 1477 { 1478 // LIMITED_METHOD_CONTRACT; 1479 1480 pLoc->Init(); 1481 1482 pLoc->m_fRequires64BitAlignment = _fRequires64BitAlignment; 1483 1484 int cSlots = (GetArgSize() + 3) / 4; 1485 1486 if (TransitionBlock.IsFloatArgumentRegisterOffset(argOffset)) 1487 { 1488 pLoc->m_idxFloatReg = (argOffset - TransitionBlock.GetOffsetOfFloatArgumentRegisters()) / 4; 1489 pLoc->m_cFloatReg = cSlots; 1490 return; 1491 } 1492 1493 if (!TransitionBlock.IsStackArgumentOffset(argOffset)) 1494 { 1495 pLoc->m_idxGenReg = TransitionBlock.GetArgumentIndexFromOffset(argOffset); 1496 1497 if (cSlots <= (4 - pLoc->m_idxGenReg)) 1498 { 1499 pLoc->m_cGenReg = cSlots; 1500 } 1501 else 1502 { 1503 pLoc->m_cGenReg = 4 - pLoc->m_idxGenReg; 1504 1505 pLoc->m_idxStack = 0; 1506 pLoc->m_cStack = cSlots - pLoc->m_cGenReg; 1507 } 1508 } 1509 else 1510 { 1511 pLoc->m_idxStack = TransitionBlock.GetArgumentIndexFromOffset(argOffset) - 4; 1512 pLoc->m_cStack = cSlots; 1513 } 1514 } 1515 #endif // _TARGET_ARM_ 1516 1517 #if _TARGET_ARM64_ 1518 // Get layout information for the argument that the ArgIterator is currently visiting. GetArgLocInternal.Runtime.CallConverter.ArgIterator1519 private unsafe void GetArgLoc(int argOffset, ArgLocDesc* pLoc) 1520 { 1521 // LIMITED_METHOD_CONTRACT; 1522 1523 pLoc->Init(); 1524 1525 if (TransitionBlock.IsFloatArgumentRegisterOffset(argOffset)) 1526 { 1527 // Dividing by 8 as size of each register in FloatArgumentRegisters is 8 bytes. 1528 pLoc->m_idxFloatReg = (argOffset - TransitionBlock.GetOffsetOfFloatArgumentRegisters()) / 8; 1529 1530 if (!_argTypeHandle.IsNull() && _argTypeHandle.IsHFA()) 1531 { 1532 CorElementType type = _argTypeHandle.GetHFAType(); 1533 bool isFloatType = (type == CorElementType.ELEMENT_TYPE_R4); 1534 1535 // DESKTOP BEHAVIOR pLoc->m_cFloatReg = isFloatType ? GetArgSize() / sizeof(float) : GetArgSize() / sizeof(double); 1536 pLoc->m_cFloatReg = GetArgSize() / sizeof(double); 1537 pLoc->m_isSinglePrecision = isFloatType; 1538 } 1539 else 1540 { 1541 pLoc->m_cFloatReg = 1; 1542 } 1543 return; 1544 } 1545 1546 int cSlots = (GetArgSize() + 7) / 8; 1547 1548 // Composites greater than 16bytes are passed by reference 1549 TypeHandle dummy; 1550 if (GetArgType(out dummy) == CorElementType.ELEMENT_TYPE_VALUETYPE && GetArgSize() > ArchitectureConstants.ENREGISTERED_PARAMTYPE_MAXSIZE) 1551 { 1552 cSlots = 1; 1553 } 1554 1555 if (!TransitionBlock.IsStackArgumentOffset(argOffset)) 1556 { 1557 pLoc->m_idxGenReg = TransitionBlock.GetArgumentIndexFromOffset(argOffset); 1558 pLoc->m_cGenReg = cSlots; 1559 } 1560 else 1561 { 1562 pLoc->m_idxStack = TransitionBlock.GetStackArgumentIndexFromOffset(argOffset); 1563 pLoc->m_cStack = cSlots; 1564 } 1565 } 1566 #endif // _TARGET_ARM64_ 1567 1568 #if _TARGET_AMD64_ && UNIX_AMD64_ABI 1569 // Get layout information for the argument that the ArgIterator is currently visiting. GetArgLocInternal.Runtime.CallConverter.ArgIterator1570 unsafe void GetArgLoc(int argOffset, ArgLocDesc* pLoc) 1571 { 1572 // LIMITED_METHOD_CONTRACT; 1573 1574 if (argOffset == TransitionBlock.StructInRegsOffset) 1575 { 1576 // We always already have argLocDesc for structs passed in registers, we 1577 // compute it in the GetNextOffset for those since it is always needed. 1578 Debug.Assert(false); 1579 return; 1580 } 1581 1582 pLoc->Init(); 1583 1584 if (TransitionBlock.IsFloatArgumentRegisterOffset(argOffset)) 1585 { 1586 // Dividing by 8 as size of each register in FloatArgumentRegisters is 8 bytes. 1587 pLoc->m_idxFloatReg = (argOffset - TransitionBlock.GetOffsetOfFloatArgumentRegisters()) / 8; 1588 1589 // UNIXTODO: Passing of structs, HFAs. For now, use the Windows convention. 1590 pLoc->m_cFloatReg = 1; 1591 return; 1592 } 1593 1594 // UNIXTODO: Passing of structs, HFAs. For now, use the Windows convention. 1595 int cSlots = 1; 1596 1597 if (!TransitionBlock.IsStackArgumentOffset(argOffset)) 1598 { 1599 pLoc->m_idxGenReg = TransitionBlock.GetArgumentIndexFromOffset(argOffset); 1600 pLoc->m_cGenReg = cSlots; 1601 } 1602 else 1603 { 1604 pLoc->m_idxStack = (argOffset - TransitionBlock.GetOffsetOfArgs()) / 8; 1605 pLoc->m_cStack = cSlots; 1606 } 1607 } 1608 #endif // _TARGET_AMD64_ && UNIX_AMD64_ABI 1609 1610 private int _nSizeOfArgStack; // Cached value of SizeOfArgStack 1611 1612 private int _argNum; 1613 1614 // Cached information about last argument 1615 private CorElementType _argType; 1616 private int _argSize; 1617 private TypeHandle _argTypeHandle; 1618 private TypeHandle _argTypeHandleOfByRefParam; 1619 private bool _argForceByRef; 1620 1621 #if _TARGET_X86_ 1622 private int _curOfs; // Current position of the stack iterator 1623 private int _numRegistersUsed; 1624 #endif 1625 1626 #if _TARGET_AMD64_ 1627 #if UNIX_AMD64_ABI 1628 int _idxGenReg; 1629 int _idxStack; 1630 int _idxFPReg; 1631 #else 1632 private int _curOfs; // Current position of the stack iterator 1633 #endif 1634 #endif 1635 1636 #if _TARGET_ARM_ 1637 private int _idxGenReg; // Next general register to be assigned a value 1638 private int _idxStack; // Next stack slot to be assigned a value 1639 1640 private ushort _wFPRegs; // Bitmask of available floating point argument registers (s0-s15/d0-d7) 1641 private bool _fRequires64BitAlignment; // Cached info about the current arg 1642 #endif 1643 1644 #if _TARGET_ARM64_ 1645 private int _idxGenReg; // Next general register to be assigned a value 1646 private int _idxStack; // Next stack slot to be assigned a value 1647 private int _idxFPReg; // Next FP register to be assigned a value 1648 #endif 1649 1650 // These are enum flags in CallingConvention.h, but that's really ugly in C#, so I've changed them to bools. 1651 private bool _ITERATION_STARTED; // Started iterating over arguments 1652 private bool _SIZE_OF_ARG_STACK_COMPUTED; 1653 private bool _RETURN_FLAGS_COMPUTED; 1654 private bool _RETURN_HAS_RET_BUFFER; // Cached value of HasRetBuffArg 1655 private uint _fpReturnSize; 1656 1657 // enum { 1658 /* ITERATION_STARTED = 0x0001, 1659 SIZE_OF_ARG_STACK_COMPUTED = 0x0002, 1660 RETURN_FLAGS_COMPUTED = 0x0004, 1661 RETURN_HAS_RET_BUFFER = 0x0008, // Cached value of HasRetBuffArg 1662 */ 1663 #if _TARGET_X86_ 1664 private enum ParamTypeLocation 1665 { 1666 Stack, 1667 Ecx, 1668 Edx 1669 } 1670 private ParamTypeLocation _paramTypeLoc; 1671 /* PARAM_TYPE_REGISTER_MASK = 0x0030, 1672 PARAM_TYPE_REGISTER_STACK = 0x0010, 1673 PARAM_TYPE_REGISTER_ECX = 0x0020, 1674 PARAM_TYPE_REGISTER_EDX = 0x0030,*/ 1675 #endif 1676 1677 // METHOD_INVOKE_NEEDS_ACTIVATION = 0x0040, // Flag used by ArgIteratorForMethodInvoke 1678 1679 // RETURN_FP_SIZE_SHIFT = 8, // The rest of the flags is cached value of GetFPReturnSize 1680 // }; 1681 ComputeReturnValueTreatmentInternal.Runtime.CallConverter.ArgIterator1682 internal static void ComputeReturnValueTreatment(CorElementType type, TypeHandle thRetType, bool isVarArgMethod, out bool usesRetBuffer, out uint fpReturnSize) 1683 1684 { 1685 usesRetBuffer = false; 1686 fpReturnSize = 0; 1687 1688 switch (type) 1689 { 1690 case CorElementType.ELEMENT_TYPE_TYPEDBYREF: 1691 throw new NotSupportedException(); 1692 #if ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE 1693 // if (sizeof(TypedByRef) > ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) 1694 // flags |= RETURN_HAS_RET_BUFFER; 1695 #else 1696 // flags |= RETURN_HAS_RET_BUFFER; 1697 #endif 1698 // break; 1699 1700 case CorElementType.ELEMENT_TYPE_R4: 1701 fpReturnSize = sizeof(float); 1702 break; 1703 1704 case CorElementType.ELEMENT_TYPE_R8: 1705 fpReturnSize = sizeof(double); 1706 break; 1707 1708 case CorElementType.ELEMENT_TYPE_VALUETYPE: 1709 #if ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE 1710 { 1711 Debug.Assert(!thRetType.IsNull() && thRetType.IsValueType()); 1712 1713 #if FEATURE_HFA 1714 if (thRetType.IsHFA() && !isVarArgMethod) 1715 { 1716 CorElementType hfaType = thRetType.GetHFAType(); 1717 1718 #if _TARGET_ARM64_ 1719 // DESKTOP BEHAVIOR fpReturnSize = (hfaType == CorElementType.ELEMENT_TYPE_R4) ? (4 * (uint)sizeof(float)) : (4 * (uint)sizeof(double)); 1720 // S and D registers overlap. Since we copy D registers in the UniversalTransitionThunk, we'll 1721 // thread floats like doubles during copying. 1722 fpReturnSize = 4 * (uint)sizeof(double); 1723 #else 1724 fpReturnSize = (hfaType == CorElementType.ELEMENT_TYPE_R4) ? 1725 (4 * (uint)sizeof(float)) : 1726 (4 * (uint)sizeof(double)); 1727 #endif 1728 1729 break; 1730 } 1731 #endif 1732 1733 uint size = thRetType.GetSize(); 1734 1735 #if _TARGET_X86_ || _TARGET_AMD64_ 1736 // Return value types of size which are not powers of 2 using a RetBuffArg 1737 if ((size & (size - 1)) != 0) 1738 { 1739 usesRetBuffer = true; 1740 break; 1741 } 1742 #endif 1743 1744 if (size <= ArchitectureConstants.ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) 1745 break; 1746 } 1747 #endif // ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE 1748 1749 // Value types are returned using return buffer by default 1750 usesRetBuffer = true; 1751 break; 1752 1753 default: 1754 break; 1755 } 1756 } 1757 ComputeReturnFlagsInternal.Runtime.CallConverter.ArgIterator1758 private void ComputeReturnFlags() 1759 { 1760 TypeHandle thRetType; 1761 CorElementType type = this.GetReturnType(out thRetType, out _RETURN_HAS_RET_BUFFER); 1762 1763 if (!_RETURN_HAS_RET_BUFFER) 1764 { 1765 ComputeReturnValueTreatment(type, thRetType, this.IsVarArg(), out _RETURN_HAS_RET_BUFFER, out _fpReturnSize); 1766 } 1767 1768 _RETURN_FLAGS_COMPUTED = true; 1769 } 1770 1771 1772 #if !_TARGET_X86_ GetSimpleLocInternal.Runtime.CallConverter.ArgIterator1773 private unsafe void GetSimpleLoc(int offset, ArgLocDesc* pLoc) 1774 { 1775 // WRAPPER_NO_CONTRACT; 1776 pLoc->Init(); 1777 pLoc->m_idxGenReg = TransitionBlock.GetArgumentIndexFromOffset(offset); 1778 pLoc->m_cGenReg = 1; 1779 } 1780 #endif 1781 ALIGN_UPInternal.Runtime.CallConverter.ArgIterator1782 public static int ALIGN_UP(int input, int align_to) 1783 { 1784 return (input + (align_to - 1)) & ~(align_to - 1); 1785 } 1786 IS_ALIGNEDInternal.Runtime.CallConverter.ArgIterator1787 public static bool IS_ALIGNED(IntPtr val, int alignment) 1788 { 1789 Debug.Assert(0 == (alignment & (alignment - 1))); 1790 return 0 == (val.ToInt64() & (alignment - 1)); 1791 } 1792 IsRetBuffPassedAsFirstArgInternal.Runtime.CallConverter.ArgIterator1793 public static bool IsRetBuffPassedAsFirstArg() 1794 { 1795 // WRAPPER_NO_CONTRACT; 1796 #if !_TARGET_ARM64_ 1797 return true; 1798 #else 1799 return false; 1800 #endif 1801 } 1802 }; 1803 } 1804