Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/CodeGen/src/EmitCommonX64.h
2725 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
#pragma once
3
4
#include "Luau/AssemblyBuilderX64.h"
5
6
#include "EmitCommon.h"
7
8
#include "lobject.h"
9
#include "ltm.h"
10
11
// MS x64 ABI reminder:
12
// Arguments: rcx, rdx, r8, r9 ('overlapped' with xmm0-xmm3)
13
// Return: rax, xmm0
14
// Nonvolatile: r12-r15, rdi, rsi, rbx, rbp
15
// SIMD: only xmm6-xmm15 are non-volatile, all ymm upper parts are volatile
16
17
// AMD64 ABI reminder:
18
// Arguments: rdi, rsi, rdx, rcx, r8, r9 (xmm0-xmm7)
19
// Return: rax, rdx, xmm0, xmm1
20
// Nonvolatile: r12-r15, rbx, rbp
21
// SIMD: all volatile
22
23
namespace Luau
24
{
25
namespace CodeGen
26
{
27
28
enum class IrCondition : uint8_t;
29
struct IrOp;
30
31
namespace X64
32
{
33
34
struct IrRegAllocX64;
35
36
inline constexpr uint32_t kFunctionAlignment = 32;
37
38
// Data that is very common to access is placed in non-volatile registers
39
inline constexpr RegisterX64 rState = r15; // lua_State* L
40
inline constexpr RegisterX64 rBase = r14; // StkId base
41
inline constexpr RegisterX64 rNativeContext = r13; // NativeContext* context
42
inline constexpr RegisterX64 rConstants = r12; // TValue* k
43
44
inline constexpr unsigned kExtraLocals = 3; // Number of 8 byte slots available for specialized local variables specified below
45
inline constexpr unsigned kSpillSlots_DEPRECATED = 13; // Number of 8 byte slots available for register allocator to spill data into
46
inline constexpr unsigned kSpillSlots_NEW = 12; // TODO: re-adjust kExtraLocals/kSpillSlots to the new value
47
static_assert((kExtraLocals + kSpillSlots_DEPRECATED) * 8 % 16 == 0, "locals have to preserve 16 byte alignment");
48
static_assert(kSpillSlots_NEW <= kSpillSlots_DEPRECATED, "new spill slot allocation cannot exceed deprecated one");
49
static_assert(kSpillSlots_NEW % 2 == 0, "spill slots have to be sized in 16 byte TValue chunks, for valid extra register spill-over");
50
inline constexpr unsigned kExtraSpillSlots = 64;
51
static_assert(kExtraSpillSlots * 8 <= LUA_EXECUTION_CALLBACK_STORAGE, "can't use more extra slots than Luau global state provides");
52
53
inline constexpr uint8_t kWindowsFirstNonVolXmmReg = 6;
54
55
inline constexpr uint8_t kWindowsUsableXmmRegs = 10; // Some xmm regs are non-volatile, we have to balance how many we want to use/preserve
56
inline constexpr uint8_t kSystemVUsableXmmRegs = 16; // All xmm regs are volatile
57
58
inline uint8_t getXmmRegisterCount(ABIX64 abi)
59
{
60
return abi == ABIX64::SystemV ? kSystemVUsableXmmRegs : kWindowsUsableXmmRegs;
61
}
62
63
// Native code is as stackless as the interpreter, so we can place some data on the stack once and have it accessible at any point
64
// Stack is separated into sections for different data. See CodeGenX64.cpp for layout overview
65
inline constexpr unsigned kStackAlign = 8; // Bytes we need to align the stack for non-vol xmm register storage
66
inline constexpr unsigned kStackLocalStorage = 8 * kExtraLocals;
67
inline constexpr unsigned kStackSpillStorage = 8 * kSpillSlots_DEPRECATED;
68
inline constexpr unsigned kStackExtraArgumentStorage = 2 * 8; // Bytes for 5th and 6th function call arguments used under Windows ABI
69
inline constexpr unsigned kStackRegHomeStorage = 4 * 8; // Register 'home' locations that can be used by callees under Windows ABI
70
71
inline unsigned getNonVolXmmStorageSize(ABIX64 abi, uint8_t xmmRegCount)
72
{
73
if (abi == ABIX64::SystemV)
74
return 0;
75
76
// First 6 are volatile
77
if (xmmRegCount <= kWindowsFirstNonVolXmmReg)
78
return 0;
79
80
CODEGEN_ASSERT(xmmRegCount <= 16);
81
return (xmmRegCount - kWindowsFirstNonVolXmmReg) * 16;
82
}
83
84
// Useful offsets to specific parts
85
inline constexpr unsigned kStackOffsetToLocals = kStackExtraArgumentStorage + kStackRegHomeStorage;
86
inline constexpr unsigned kStackOffsetToSpillSlots = kStackOffsetToLocals + kStackLocalStorage;
87
88
inline unsigned getFullStackSize(ABIX64 abi, uint8_t xmmRegCount)
89
{
90
return kStackOffsetToSpillSlots + kStackSpillStorage + getNonVolXmmStorageSize(abi, xmmRegCount) + kStackAlign;
91
}
92
93
inline constexpr OperandX64 sClosure = qword[rsp + kStackOffsetToLocals + 0]; // Closure* cl
94
inline constexpr OperandX64 sCode = qword[rsp + kStackOffsetToLocals + 8]; // Instruction* code
95
inline constexpr OperandX64 sTemporarySlot = addr[rsp + kStackOffsetToLocals + 16];
96
97
inline constexpr OperandX64 sSpillArea = addr[rsp + kStackOffsetToSpillSlots];
98
99
inline OperandX64 luauReg(int ri)
100
{
101
return xmmword[rBase + ri * sizeof(TValue)];
102
}
103
104
inline OperandX64 luauRegAddress(int ri)
105
{
106
return addr[rBase + ri * sizeof(TValue)];
107
}
108
109
inline OperandX64 luauRegValue(int ri)
110
{
111
return qword[rBase + ri * sizeof(TValue) + offsetof(TValue, value)];
112
}
113
114
inline OperandX64 luauRegTag(int ri)
115
{
116
return dword[rBase + ri * sizeof(TValue) + offsetof(TValue, tt)];
117
}
118
119
inline OperandX64 luauRegExtra(int ri)
120
{
121
return dword[rBase + ri * sizeof(TValue) + offsetof(TValue, extra)];
122
}
123
124
inline OperandX64 luauRegValueInt(int ri)
125
{
126
return dword[rBase + ri * sizeof(TValue) + offsetof(TValue, value)];
127
}
128
129
inline OperandX64 luauRegValueVector(int ri, int index)
130
{
131
return dword[rBase + ri * sizeof(TValue) + offsetof(TValue, value) + (sizeof(float) * index)];
132
}
133
134
inline OperandX64 luauConstant(int ki)
135
{
136
return xmmword[rConstants + ki * sizeof(TValue)];
137
}
138
139
inline OperandX64 luauConstantAddress(int ki)
140
{
141
return addr[rConstants + ki * sizeof(TValue)];
142
}
143
144
inline OperandX64 luauConstantTag(int ki)
145
{
146
return dword[rConstants + ki * sizeof(TValue) + offsetof(TValue, tt)];
147
}
148
149
inline OperandX64 luauConstantValue(int ki)
150
{
151
return qword[rConstants + ki * sizeof(TValue) + offsetof(TValue, value)];
152
}
153
154
inline OperandX64 luauNodeKeyValue(RegisterX64 node)
155
{
156
return qword[node + offsetof(LuaNode, key) + offsetof(TKey, value)];
157
}
158
159
// Note: tag has dirty upper bits
160
inline OperandX64 luauNodeKeyTag(RegisterX64 node)
161
{
162
return dword[node + offsetof(LuaNode, key) + kOffsetOfTKeyTagNext];
163
}
164
165
inline void setLuauReg(AssemblyBuilderX64& build, RegisterX64 tmp, int ri, OperandX64 op)
166
{
167
CODEGEN_ASSERT(op.cat == CategoryX64::mem);
168
169
build.vmovups(tmp, op);
170
build.vmovups(luauReg(ri), tmp);
171
}
172
173
inline void jumpIfTagIs(AssemblyBuilderX64& build, int ri, lua_Type tag, Label& label)
174
{
175
build.cmp(luauRegTag(ri), tag);
176
build.jcc(ConditionX64::Equal, label);
177
}
178
179
inline void jumpIfTagIsNot(AssemblyBuilderX64& build, int ri, lua_Type tag, Label& label)
180
{
181
build.cmp(luauRegTag(ri), tag);
182
build.jcc(ConditionX64::NotEqual, label);
183
}
184
185
// Note: fallthrough label should be placed after this condition
186
inline void jumpIfFalsy(AssemblyBuilderX64& build, int ri, Label& target, Label& fallthrough)
187
{
188
jumpIfTagIs(build, ri, LUA_TNIL, target); // false if nil
189
jumpIfTagIsNot(build, ri, LUA_TBOOLEAN, fallthrough); // true if not nil or boolean
190
191
build.cmp(luauRegValueInt(ri), 0);
192
build.jcc(ConditionX64::Equal, target); // true if boolean value is 'true'
193
}
194
195
// Note: fallthrough label should be placed after this condition
196
inline void jumpIfTruthy(AssemblyBuilderX64& build, int ri, Label& target, Label& fallthrough)
197
{
198
jumpIfTagIs(build, ri, LUA_TNIL, fallthrough); // false if nil
199
jumpIfTagIsNot(build, ri, LUA_TBOOLEAN, target); // true if not nil or boolean
200
201
build.cmp(luauRegValueInt(ri), 0);
202
build.jcc(ConditionX64::NotEqual, target); // true if boolean value is 'true'
203
}
204
205
void jumpOnNumberCmp(AssemblyBuilderX64& build, RegisterX64 tmp, OperandX64 lhs, OperandX64 rhs, IrCondition cond, Label& label, bool floatPrecision);
206
207
ConditionX64 getConditionInt(IrCondition cond);
208
209
void getTableNodeAtCachedSlot(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 node, RegisterX64 table, int pcpos);
210
void convertNumberToIndexOrJump(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 numd, RegisterX64 numi, Label& label);
211
212
void callArithHelper(IrRegAllocX64& regs, AssemblyBuilderX64& build, int ra, OperandX64 b, OperandX64 c, TMS tm);
213
void callLengthHelper(IrRegAllocX64& regs, AssemblyBuilderX64& build, int ra, int rb);
214
void callGetTable(IrRegAllocX64& regs, AssemblyBuilderX64& build, int rb, OperandX64 c, int ra);
215
void callSetTable(IrRegAllocX64& regs, AssemblyBuilderX64& build, int rb, OperandX64 c, int ra);
216
void checkObjectBarrierConditions(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 object, RegisterX64 ra, IrOp raOp, int ratag, Label& skip);
217
void checkObjectBarrierConditions_DEPRECATED(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 object, IrOp ra, int ratag, Label& skip);
218
void callBarrierObject(IrRegAllocX64& regs, AssemblyBuilderX64& build, RegisterX64 object, IrOp objectOp, RegisterX64 ra, IrOp raOp, int ratag);
219
void callBarrierObject_DEPRECATED(IrRegAllocX64& regs, AssemblyBuilderX64& build, RegisterX64 object, IrOp objectOp, IrOp ra, int ratag);
220
void callBarrierTableFast(IrRegAllocX64& regs, AssemblyBuilderX64& build, RegisterX64 table, IrOp tableOp);
221
void callStepGc(IrRegAllocX64& regs, AssemblyBuilderX64& build);
222
223
void emitClearNativeFlag(AssemblyBuilderX64& build);
224
void emitExit(AssemblyBuilderX64& build, bool continueInVm);
225
void emitUpdateBase(AssemblyBuilderX64& build);
226
void emitInterrupt(AssemblyBuilderX64& build);
227
void emitFallback(IrRegAllocX64& regs, AssemblyBuilderX64& build, int offset, int pcpos);
228
229
void emitUpdatePcForExit(AssemblyBuilderX64& build);
230
231
void emitReturn(AssemblyBuilderX64& build, ModuleHelpers& helpers);
232
233
} // namespace X64
234
} // namespace CodeGen
235
} // namespace Luau
236
237