Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/CodeGen/src/CodeGenX64.cpp
2725 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
#include "CodeGenX64.h"
3
4
#include "Luau/AssemblyBuilderX64.h"
5
#include "Luau/UnwindBuilder.h"
6
7
#include "CodeGenContext.h"
8
#include "NativeState.h"
9
#include "EmitCommonX64.h"
10
11
#include "lstate.h"
12
13
LUAU_FASTFLAG(LuauCodegenFreeBlocks)
14
15
/* An overview of native environment stack setup that we are making in the entry function:
16
* Each line is 8 bytes, stack grows downwards.
17
*
18
* | ... previous frames ...
19
* | rdx home space | (unused)
20
* | rcx home space | (unused)
21
* | return address |
22
* | ... saved non-volatile registers ... <-- rsp + kStackSizeFull
23
* | alignment |
24
* | xmm9 non-vol |
25
* | xmm9 cont. |
26
* | xmm8 non-vol |
27
* | xmm8 cont. |
28
* | xmm7 non-vol |
29
* | xmm7 cont. |
30
* | xmm6 non-vol |
31
* | xmm6 cont. |
32
* | spill slot 5 |
33
* | spill slot 4 |
34
* | spill slot 3 |
35
* | spill slot 2 |
36
* | spill slot 1 | <-- rsp + kStackOffsetToSpillSlots
37
* | sTemporarySlot |
38
* | sCode |
39
* | sClosure | <-- rsp + kStackOffsetToLocals
40
* | argument 6 | <-- rsp + 40
41
* | argument 5 | <-- rsp + 32
42
* | r9 home space |
43
* | r8 home space |
44
* | rdx home space |
45
* | rcx home space | <-- rsp points here
46
*
47
* Arguments to our entry function are saved to home space only on Windows.
48
* Space for arguments to function we call is always reserved, but used only on Windows.
49
*
50
* Right now we use a frame pointer, but because of a fixed layout we can omit it in the future
51
*/
52
53
namespace Luau
54
{
55
namespace CodeGen
56
{
57
namespace X64
58
{
59
60
struct EntryLocations
61
{
62
Label start;
63
Label prologueEnd;
64
Label epilogueStart;
65
};
66
67
static EntryLocations buildEntryFunction(AssemblyBuilderX64& build, UnwindBuilder& unwind)
68
{
69
EntryLocations locations;
70
71
build.align(kFunctionAlignment, X64::AlignmentDataX64::Ud2);
72
73
locations.start = build.setLabel();
74
unwind.startFunction();
75
76
RegisterX64 rArg1 = (build.abi == ABIX64::Windows) ? rcx : rdi;
77
RegisterX64 rArg2 = (build.abi == ABIX64::Windows) ? rdx : rsi;
78
RegisterX64 rArg3 = (build.abi == ABIX64::Windows) ? r8 : rdx;
79
RegisterX64 rArg4 = (build.abi == ABIX64::Windows) ? r9 : rcx;
80
81
// Save common non-volatile registers
82
if (build.abi == ABIX64::SystemV)
83
{
84
// We need to use a standard rbp-based frame setup for debuggers to work with JIT code
85
build.push(rbp);
86
build.mov(rbp, rsp);
87
}
88
89
build.push(rbx);
90
build.push(r12);
91
build.push(r13);
92
build.push(r14);
93
build.push(r15);
94
95
if (build.abi == ABIX64::Windows)
96
{
97
// Save non-volatile registers that are specific to Windows x64 ABI
98
build.push(rdi);
99
build.push(rsi);
100
101
// On Windows, rbp is available as a general-purpose non-volatile register and this might be freed up
102
build.push(rbp);
103
}
104
105
// Allocate stack space
106
uint8_t usableXmmRegCount = getXmmRegisterCount(build.abi);
107
unsigned xmmStorageSize = getNonVolXmmStorageSize(build.abi, usableXmmRegCount);
108
unsigned fullStackSize = getFullStackSize(build.abi, usableXmmRegCount);
109
110
build.sub(rsp, fullStackSize);
111
112
OperandX64 xmmStorageOffset = rsp + (fullStackSize - (kStackAlign + xmmStorageSize));
113
114
// On Windows, we have to save non-volatile xmm registers
115
std::vector<RegisterX64> savedXmmRegs;
116
117
if (build.abi == ABIX64::Windows)
118
{
119
if (usableXmmRegCount > kWindowsFirstNonVolXmmReg)
120
savedXmmRegs.reserve(usableXmmRegCount - kWindowsFirstNonVolXmmReg);
121
122
for (uint8_t i = kWindowsFirstNonVolXmmReg, offset = 0; i < usableXmmRegCount; i++, offset += 16)
123
{
124
RegisterX64 xmmReg = RegisterX64{SizeX64::xmmword, i};
125
build.vmovaps(xmmword[xmmStorageOffset + offset], xmmReg);
126
savedXmmRegs.push_back(xmmReg);
127
}
128
}
129
130
locations.prologueEnd = build.setLabel();
131
132
uint32_t prologueSize = build.getLabelOffset(locations.prologueEnd) - build.getLabelOffset(locations.start);
133
134
if (build.abi == ABIX64::SystemV)
135
unwind.prologueX64(prologueSize, fullStackSize, /* setupFrame= */ true, {rbx, r12, r13, r14, r15}, {});
136
else if (build.abi == ABIX64::Windows)
137
unwind.prologueX64(prologueSize, fullStackSize, /* setupFrame= */ false, {rbx, r12, r13, r14, r15, rdi, rsi, rbp}, savedXmmRegs);
138
139
// Setup native execution environment
140
build.mov(rState, rArg1);
141
build.mov(rNativeContext, rArg4);
142
build.mov(rBase, qword[rState + offsetof(lua_State, base)]); // L->base
143
build.mov(rax, qword[rState + offsetof(lua_State, ci)]); // L->ci
144
build.mov(rax, qword[rax + offsetof(CallInfo, func)]); // L->ci->func
145
build.mov(rax, qword[rax + offsetof(TValue, value.gc)]); // L->ci->func->value.gc aka cl
146
build.mov(sClosure, rax);
147
build.mov(rConstants, qword[rArg2 + offsetof(Proto, k)]); // proto->k
148
build.mov(rax, qword[rArg2 + offsetof(Proto, code)]); // proto->code
149
build.mov(sCode, rax);
150
151
// Jump to the specified instruction; further control flow will be handled with custom ABI with register setup from EmitCommonX64.h
152
build.jmp(rArg3);
153
154
// Even though we jumped away, we will return here in the end
155
locations.epilogueStart = build.setLabel();
156
157
// Epilogue and exit
158
if (build.abi == ABIX64::Windows)
159
{
160
// xmm registers are restored before the official epilogue that has to start with 'add rsp/lea rsp'
161
for (uint8_t i = kWindowsFirstNonVolXmmReg, offset = 0; i < usableXmmRegCount; i++, offset += 16)
162
build.vmovaps(RegisterX64{SizeX64::xmmword, i}, xmmword[xmmStorageOffset + offset]);
163
}
164
165
build.add(rsp, fullStackSize);
166
167
if (build.abi == ABIX64::Windows)
168
{
169
build.pop(rbp);
170
build.pop(rsi);
171
build.pop(rdi);
172
}
173
174
build.pop(r15);
175
build.pop(r14);
176
build.pop(r13);
177
build.pop(r12);
178
build.pop(rbx);
179
180
if (build.abi == ABIX64::SystemV)
181
build.pop(rbp);
182
183
build.ret();
184
185
// Our entry function is special, it spans the whole remaining code area
186
unwind.finishFunction(build.getLabelOffset(locations.start), kFullBlockFunction);
187
188
return locations;
189
}
190
191
bool initHeaderFunctions(BaseCodeGenContext& codeGenContext)
192
{
193
AssemblyBuilderX64 build(/* logText= */ false);
194
UnwindBuilder& unwind = *codeGenContext.unwindBuilder.get();
195
196
unwind.startInfo(UnwindBuilder::X64);
197
198
EntryLocations entryLocations = buildEntryFunction(build, unwind);
199
200
build.finalize();
201
202
unwind.finishInfo();
203
204
CODEGEN_ASSERT(build.data.empty());
205
206
uint8_t* codeStart = nullptr;
207
208
if (FFlag::LuauCodegenFreeBlocks)
209
{
210
codeGenContext.gateAllocationData =
211
codeGenContext.codeAllocator.allocate(build.data.data(), int(build.data.size()), build.code.data(), int(build.code.size()));
212
213
if (!codeGenContext.gateAllocationData.start)
214
return false;
215
216
codeStart = codeGenContext.gateAllocationData.codeStart;
217
}
218
else
219
{
220
if (!codeGenContext.codeAllocator.allocate_DEPRECATED(
221
build.data.data(),
222
int(build.data.size()),
223
build.code.data(),
224
int(build.code.size()),
225
codeGenContext.gateData_DEPRECATED,
226
codeGenContext.gateDataSize_DEPRECATED,
227
codeStart
228
))
229
{
230
return false;
231
}
232
}
233
234
// Set the offset at the beginning so that functions in new blocks will not overlay the locations
235
// specified by the unwind information of the entry function
236
unwind.setBeginOffset(build.getLabelOffset(entryLocations.prologueEnd));
237
238
codeGenContext.context.gateEntry = codeStart + build.getLabelOffset(entryLocations.start);
239
codeGenContext.context.gateExit = codeStart + build.getLabelOffset(entryLocations.epilogueStart);
240
241
return true;
242
}
243
244
void assembleHelpers(X64::AssemblyBuilderX64& build, ModuleHelpers& helpers)
245
{
246
if (build.logText)
247
build.logAppend("; updatePcAndContinueInVm\n");
248
build.setLabel(helpers.updatePcAndContinueInVm);
249
emitUpdatePcForExit(build);
250
251
if (build.logText)
252
build.logAppend("; exitContinueVmClearNativeFlag\n");
253
build.setLabel(helpers.exitContinueVmClearNativeFlag);
254
emitClearNativeFlag(build);
255
256
if (build.logText)
257
build.logAppend("; exitContinueVm\n");
258
build.setLabel(helpers.exitContinueVm);
259
emitExit(build, /* continueInVm */ true);
260
261
if (build.logText)
262
build.logAppend("; exitNoContinueVm\n");
263
build.setLabel(helpers.exitNoContinueVm);
264
emitExit(build, /* continueInVm */ false);
265
266
if (build.logText)
267
build.logAppend("; interrupt\n");
268
build.setLabel(helpers.interrupt);
269
emitInterrupt(build);
270
271
if (build.logText)
272
build.logAppend("; return\n");
273
build.setLabel(helpers.return_);
274
emitReturn(build, helpers);
275
}
276
277
} // namespace X64
278
} // namespace CodeGen
279
} // namespace Luau
280
281