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