-
Notifications
You must be signed in to change notification settings - Fork 4.6k
/
abi.h
255 lines (210 loc) · 7.7 KB
/
abi.h
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#pragma once
class ABIPassingSegment
{
regNumber m_register = REG_NA;
unsigned m_stackOffset = 0;
public:
bool IsPassedInRegister() const;
bool IsPassedOnStack() const;
// Start offset of the segment within the parameter/argument. For example, a struct like { int32_t x; uint64_t y }
// may have two segments
// 1. Register(Offset=0, Type=TYP_INT, Size=4, Register=REG_ESI)
// 2. Register(Offset=8, Type=TYP_LONG, Size=8, Register=REG_EDI)
// on some ABIs, where the size of the first segment is not sufficient to
// compute the offset of the second.
unsigned Offset = 0;
// Size of the segment being passed.
unsigned Size = 0;
// If this segment is passed in a register, return the particular register.
regNumber GetRegister() const;
regMaskTP GetRegisterMask() const;
// If this segment is passed on the stack then return the particular stack
// offset, relative to the base of stack arguments.
unsigned GetStackOffset() const;
var_types GetRegisterType() const;
static ABIPassingSegment InRegister(regNumber reg, unsigned offset, unsigned size);
static ABIPassingSegment OnStack(unsigned stackOffset, unsigned offset, unsigned size);
};
struct ABIPassingInformation
{
// The number of segments used to pass the value. Examples:
// - On SysV x64, structs can be passed in two registers, resulting in two
// register segments
// - On arm64/arm32, HFAs can be passed in up to four registers, giving
// four register segments
// - On arm32, structs can be split out over register and stack, giving
// multiple register segments and a struct segment.
// - On Windows x64, all parameters always fit into one stack slot or
// register, and thus always have NumSegments == 1
// - On loongarch64/riscv64, structs can be passed in two registers or
// can be split out over register and stack, giving
// multiple register segments and a struct segment.
unsigned NumSegments = 0;
ABIPassingSegment* Segments = nullptr;
ABIPassingInformation(unsigned numSegments = 0, ABIPassingSegment* segments = nullptr)
: NumSegments(numSegments)
, Segments(segments)
{
}
bool HasAnyRegisterSegment() const;
bool HasAnyStackSegment() const;
bool HasExactlyOneRegisterSegment() const;
bool HasExactlyOneStackSegment() const;
bool IsSplitAcrossRegistersAndStack() const;
static ABIPassingInformation FromSegment(Compiler* comp, const ABIPassingSegment& segment);
#ifdef DEBUG
void Dump() const;
#endif
};
class RegisterQueue
{
const regNumber* m_regs;
unsigned int m_numRegs;
unsigned int m_index = 0;
public:
RegisterQueue(const regNumber* regs, unsigned int numRegs)
: m_regs(regs)
, m_numRegs(numRegs)
{
}
unsigned Count() const
{
return m_numRegs - m_index;
}
regNumber Dequeue();
regNumber Peek();
void Clear();
};
struct ClassifierInfo
{
CorInfoCallConvExtension CallConv = CorInfoCallConvExtension::Managed;
bool IsVarArgs = false;
bool HasThis = false;
bool HasRetBuff = false;
};
class X86Classifier
{
RegisterQueue m_regs;
unsigned m_stackArgSize = 0;
public:
X86Classifier(const ClassifierInfo& info);
ABIPassingInformation Classify(Compiler* comp,
var_types type,
ClassLayout* structLayout,
WellKnownArg wellKnownParam);
};
class WinX64Classifier
{
RegisterQueue m_intRegs;
RegisterQueue m_floatRegs;
unsigned m_stackArgSize = 0;
public:
WinX64Classifier(const ClassifierInfo& info);
ABIPassingInformation Classify(Compiler* comp,
var_types type,
ClassLayout* structLayout,
WellKnownArg wellKnownParam);
};
class SysVX64Classifier
{
RegisterQueue m_intRegs;
RegisterQueue m_floatRegs;
unsigned m_stackArgSize = 0;
public:
SysVX64Classifier(const ClassifierInfo& info);
ABIPassingInformation Classify(Compiler* comp,
var_types type,
ClassLayout* structLayout,
WellKnownArg wellKnownParam);
};
class Arm64Classifier
{
const ClassifierInfo& m_info;
RegisterQueue m_intRegs;
RegisterQueue m_floatRegs;
unsigned m_stackArgSize = 0;
public:
Arm64Classifier(const ClassifierInfo& info);
ABIPassingInformation Classify(Compiler* comp,
var_types type,
ClassLayout* structLayout,
WellKnownArg wellKnownParam);
};
class Arm32Classifier
{
const ClassifierInfo& m_info;
// 4 int regs are available for parameters. This gives the index of the
// next one.
// A.k.a. "NCRN": Next Core Register Number
unsigned m_nextIntReg = 0;
// 16 float regs are available for parameters. We keep them as a mask as
// they can be backfilled.
unsigned m_floatRegs = 0xFFFF;
// A.k.a. "NSAA": Next Stack Argument Address
unsigned m_stackArgSize = 0;
ABIPassingInformation ClassifyFloat(Compiler* comp, var_types type, unsigned elems);
public:
Arm32Classifier(const ClassifierInfo& info);
ABIPassingInformation Classify(Compiler* comp,
var_types type,
ClassLayout* structLayout,
WellKnownArg wellKnownParam);
};
class RiscV64Classifier
{
const ClassifierInfo& m_info;
RegisterQueue m_intRegs;
RegisterQueue m_floatRegs;
unsigned m_stackArgSize = 0;
public:
RiscV64Classifier(const ClassifierInfo& info);
ABIPassingInformation Classify(Compiler* comp,
var_types type,
ClassLayout* structLayout,
WellKnownArg wellKnownParam);
};
class LoongArch64Classifier
{
const ClassifierInfo& m_info;
RegisterQueue m_intRegs;
RegisterQueue m_floatRegs;
unsigned m_stackArgSize = 0;
public:
LoongArch64Classifier(const ClassifierInfo& info);
ABIPassingInformation Classify(Compiler* comp,
var_types type,
ClassLayout* structLayout,
WellKnownArg wellKnownParam);
};
#if defined(TARGET_X86)
typedef X86Classifier PlatformClassifier;
#elif defined(WINDOWS_AMD64_ABI)
typedef WinX64Classifier PlatformClassifier;
#elif defined(UNIX_AMD64_ABI)
typedef SysVX64Classifier PlatformClassifier;
#elif defined(TARGET_ARM64)
typedef Arm64Classifier PlatformClassifier;
#elif defined(TARGET_ARM)
typedef Arm32Classifier PlatformClassifier;
#elif defined(TARGET_RISCV64)
typedef RiscV64Classifier PlatformClassifier;
#elif defined(TARGET_LOONGARCH64)
typedef LoongArch64Classifier PlatformClassifier;
#endif
#ifdef SWIFT_SUPPORT
class SwiftABIClassifier
{
PlatformClassifier m_classifier;
public:
SwiftABIClassifier(const ClassifierInfo& info)
: m_classifier(info)
{
}
ABIPassingInformation Classify(Compiler* comp,
var_types type,
ClassLayout* structLayout,
WellKnownArg wellKnownParam);
};
#endif