| 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 | #ifndef __register_arg_convention__ |
| 6 | #define __register_arg_convention__ |
| 7 | |
| 8 | class LclVarDsc; |
| 9 | |
| 10 | struct InitVarDscInfo |
| 11 | { |
| 12 | LclVarDsc* varDsc; |
| 13 | unsigned varNum; |
| 14 | |
| 15 | unsigned intRegArgNum; |
| 16 | unsigned floatRegArgNum; |
| 17 | unsigned maxIntRegArgNum; |
| 18 | unsigned maxFloatRegArgNum; |
| 19 | |
| 20 | bool hasRetBufArg; |
| 21 | |
| 22 | #ifdef _TARGET_ARM_ |
| 23 | // Support back-filling of FP parameters. This is similar to code in gtMorphArgs() that |
| 24 | // handles arguments. |
| 25 | regMaskTP fltArgSkippedRegMask; |
| 26 | bool anyFloatStackArgs; |
| 27 | #endif // _TARGET_ARM_ |
| 28 | |
| 29 | #if FEATURE_FASTTAILCALL |
| 30 | // It is used to calculate argument stack size information in byte |
| 31 | unsigned stackArgSize; |
| 32 | #endif // FEATURE_FASTTAILCALL |
| 33 | |
| 34 | public: |
| 35 | // set to initial values |
| 36 | void Init(LclVarDsc* lvaTable, bool _hasRetBufArg) |
| 37 | { |
| 38 | hasRetBufArg = _hasRetBufArg; |
| 39 | varDsc = &lvaTable[0]; // the first argument LclVar 0 |
| 40 | varNum = 0; // the first argument varNum 0 |
| 41 | intRegArgNum = 0; |
| 42 | floatRegArgNum = 0; |
| 43 | maxIntRegArgNum = MAX_REG_ARG; |
| 44 | maxFloatRegArgNum = MAX_FLOAT_REG_ARG; |
| 45 | |
| 46 | #ifdef _TARGET_ARM_ |
| 47 | fltArgSkippedRegMask = RBM_NONE; |
| 48 | anyFloatStackArgs = false; |
| 49 | #endif // _TARGET_ARM_ |
| 50 | |
| 51 | #if FEATURE_FASTTAILCALL |
| 52 | stackArgSize = 0; |
| 53 | #endif // FEATURE_FASTTAILCALL |
| 54 | } |
| 55 | |
| 56 | // return ref to current register arg for this type |
| 57 | unsigned& regArgNum(var_types type) |
| 58 | { |
| 59 | return varTypeIsFloating(type) ? floatRegArgNum : intRegArgNum; |
| 60 | } |
| 61 | |
| 62 | // Allocate a set of contiguous argument registers. "type" is either an integer |
| 63 | // type, indicating to use the integer registers, or a floating-point type, indicating |
| 64 | // to use the floating-point registers. The actual type (TYP_FLOAT vs. TYP_DOUBLE) is |
| 65 | // ignored. "numRegs" is the number of registers to allocate. Thus, on ARM, to allocate |
| 66 | // a double-precision floating-point register, you need to pass numRegs=2. For an HFA, |
| 67 | // pass the number of slots/registers needed. |
| 68 | // This routine handles floating-point register back-filling on ARM. |
| 69 | // Returns the first argument register of the allocated set. |
| 70 | unsigned allocRegArg(var_types type, unsigned numRegs = 1); |
| 71 | |
| 72 | // We are aligning the register to an ABI-required boundary, such as putting |
| 73 | // double-precision floats in even-numbered registers, by skipping one register. |
| 74 | // "requiredRegAlignment" is the amount to align to: 1 for no alignment (everything |
| 75 | // is 1-aligned), 2 for "double" alignment. |
| 76 | // Returns the number of registers skipped. |
| 77 | unsigned alignReg(var_types type, unsigned requiredRegAlignment); |
| 78 | |
| 79 | // Return true if it is an enregisterable type and there is room. |
| 80 | // Note that for "type", we only care if it is float or not. In particular, |
| 81 | // "numRegs" must be "2" to allocate an ARM double-precision floating-point register. |
| 82 | bool canEnreg(var_types type, unsigned numRegs = 1); |
| 83 | |
| 84 | // Set the fact that we have used up all remaining registers of 'type' |
| 85 | // |
| 86 | void setAllRegArgUsed(var_types type) |
| 87 | { |
| 88 | regArgNum(type) = maxRegArgNum(type); |
| 89 | } |
| 90 | |
| 91 | #ifdef _TARGET_ARM_ |
| 92 | |
| 93 | void setAnyFloatStackArgs() |
| 94 | { |
| 95 | anyFloatStackArgs = true; |
| 96 | } |
| 97 | |
| 98 | bool existAnyFloatStackArgs() |
| 99 | { |
| 100 | return anyFloatStackArgs; |
| 101 | } |
| 102 | |
| 103 | #endif // _TARGET_ARM_ |
| 104 | |
| 105 | private: |
| 106 | // return max register arg for this type |
| 107 | unsigned maxRegArgNum(var_types type) |
| 108 | { |
| 109 | return varTypeIsFloating(type) ? maxFloatRegArgNum : maxIntRegArgNum; |
| 110 | } |
| 111 | |
| 112 | bool enoughAvailRegs(var_types type, unsigned numRegs = 1); |
| 113 | |
| 114 | void nextReg(var_types type, unsigned numRegs = 1) |
| 115 | { |
| 116 | regArgNum(type) = min(regArgNum(type) + numRegs, maxRegArgNum(type)); |
| 117 | } |
| 118 | }; |
| 119 | |
| 120 | #endif // __register_arg_convention__ |
| 121 | |