Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/CodeGen/src/IrValueLocationTracking.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 "IrValueLocationTracking.h"
3
4
#include "Luau/IrUtils.h"
5
6
namespace Luau
7
{
8
namespace CodeGen
9
{
10
11
IrValueLocationTracking::IrValueLocationTracking(IrFunction& function)
12
: function(function)
13
{
14
vmRegValue.fill(kInvalidInstIdx);
15
}
16
17
void IrValueLocationTracking::setRestoreCallback(void* context, void (*callback)(void* context, IrInst& inst))
18
{
19
restoreCallbackCtx = context;
20
restoreCallback = callback;
21
}
22
23
bool IrValueLocationTracking::canBeRematerialized(IrCmd cmd)
24
{
25
return cmd == IrCmd::UINT_TO_NUM || cmd == IrCmd::INT_TO_NUM;
26
}
27
28
bool IrValueLocationTracking::canRematerializeArguments(IrInst& inst)
29
{
30
if (canBeRematerialized(inst.cmd) && OP_A(inst).kind == IrOpKind::Inst)
31
{
32
IrInst& depInst = function.instOp(OP_A(inst));
33
34
// If this argument is last used in the current instructions, there's no point in preserving it
35
if (depInst.lastUse != function.getInstIndex(inst))
36
return true;
37
}
38
39
return false;
40
}
41
42
void IrValueLocationTracking::beforeInstLowering(IrInst& inst)
43
{
44
switch (inst.cmd)
45
{
46
case IrCmd::STORE_TAG:
47
// Tag update is a bit tricky, restore operations of values are not affected
48
invalidateRestoreOp(OP_A(inst), /*skipValueInvalidation*/ true);
49
break;
50
case IrCmd::STORE_EXTRA:
51
// While extra field update doesn't invalidate some of the values, it can invalidate a vector type field
52
invalidateRestoreOp(OP_A(inst), /*skipValueInvalidation*/ false);
53
break;
54
case IrCmd::STORE_POINTER:
55
case IrCmd::STORE_DOUBLE:
56
case IrCmd::STORE_INT:
57
case IrCmd::STORE_VECTOR:
58
case IrCmd::STORE_TVALUE:
59
case IrCmd::STORE_SPLIT_TVALUE:
60
invalidateRestoreOp(OP_A(inst), /*skipValueInvalidation*/ false);
61
break;
62
case IrCmd::ADJUST_STACK_TO_REG:
63
invalidateRestoreVmRegs(vmRegOp(OP_A(inst)), -1);
64
break;
65
case IrCmd::FASTCALL:
66
invalidateRestoreVmRegs(vmRegOp(OP_B(inst)), function.intOp(OP_D(inst)));
67
break;
68
case IrCmd::INVOKE_FASTCALL:
69
// Multiple return sequences (count == -1) are defined by ADJUST_STACK_TO_REG
70
if (int count = function.intOp(OP_G(inst)); count != -1)
71
invalidateRestoreVmRegs(vmRegOp(OP_B(inst)), count);
72
break;
73
case IrCmd::DO_ARITH:
74
case IrCmd::DO_LEN:
75
case IrCmd::GET_TABLE:
76
case IrCmd::GET_CACHED_IMPORT:
77
invalidateRestoreOp(OP_A(inst), /*skipValueInvalidation*/ false);
78
break;
79
case IrCmd::CONCAT:
80
invalidateRestoreVmRegs(vmRegOp(OP_A(inst)), function.uintOp(OP_B(inst)));
81
break;
82
case IrCmd::GET_UPVALUE:
83
break;
84
case IrCmd::CALL:
85
// Even if result count is limited, all registers starting from function (ra) might be modified
86
invalidateRestoreVmRegs(vmRegOp(OP_A(inst)), -1);
87
break;
88
case IrCmd::FORGLOOP:
89
case IrCmd::FORGLOOP_FALLBACK:
90
// Even if result count is limited, all registers starting from iteration index (ra+2) might be modified
91
invalidateRestoreVmRegs(vmRegOp(OP_A(inst)) + 2, -1);
92
break;
93
case IrCmd::FALLBACK_GETGLOBAL:
94
case IrCmd::FALLBACK_GETTABLEKS:
95
invalidateRestoreOp(OP_B(inst), /*skipValueInvalidation*/ false);
96
break;
97
case IrCmd::FALLBACK_NAMECALL:
98
invalidateRestoreVmRegs(vmRegOp(OP_B(inst)), 2);
99
break;
100
case IrCmd::FALLBACK_GETVARARGS:
101
invalidateRestoreVmRegs(vmRegOp(OP_B(inst)), function.intOp(OP_C(inst)));
102
break;
103
case IrCmd::FALLBACK_DUPCLOSURE:
104
invalidateRestoreOp(OP_B(inst), /*skipValueInvalidation*/ false);
105
break;
106
case IrCmd::FALLBACK_FORGPREP:
107
invalidateRestoreVmRegs(vmRegOp(OP_B(inst)), 3);
108
break;
109
110
// Make sure all VmReg referencing instructions are handled explicitly (only register reads here)
111
case IrCmd::LOAD_TAG:
112
case IrCmd::LOAD_POINTER:
113
case IrCmd::LOAD_DOUBLE:
114
case IrCmd::LOAD_INT:
115
case IrCmd::LOAD_FLOAT:
116
case IrCmd::LOAD_TVALUE:
117
case IrCmd::CMP_ANY:
118
case IrCmd::CMP_TAG:
119
case IrCmd::JUMP_IF_TRUTHY:
120
case IrCmd::JUMP_IF_FALSY:
121
case IrCmd::JUMP_EQ_TAG:
122
case IrCmd::SET_TABLE:
123
case IrCmd::SET_UPVALUE:
124
case IrCmd::INTERRUPT:
125
case IrCmd::BARRIER_OBJ:
126
case IrCmd::BARRIER_TABLE_FORWARD:
127
case IrCmd::CLOSE_UPVALS:
128
case IrCmd::CAPTURE:
129
case IrCmd::SETLIST:
130
case IrCmd::RETURN:
131
case IrCmd::FORGPREP_XNEXT_FALLBACK:
132
case IrCmd::FALLBACK_SETGLOBAL:
133
case IrCmd::FALLBACK_SETTABLEKS:
134
case IrCmd::FALLBACK_PREPVARARGS:
135
case IrCmd::ADJUST_STACK_TO_TOP:
136
case IrCmd::GET_TYPEOF:
137
case IrCmd::NEWCLOSURE:
138
case IrCmd::FINDUPVAL:
139
break;
140
141
// These instructions read VmReg only after optimizeMemoryOperandsX64
142
case IrCmd::CHECK_TAG:
143
case IrCmd::CHECK_TRUTHY:
144
case IrCmd::ADD_NUM:
145
case IrCmd::SUB_NUM:
146
case IrCmd::MUL_NUM:
147
case IrCmd::DIV_NUM:
148
case IrCmd::IDIV_NUM:
149
case IrCmd::MOD_NUM:
150
case IrCmd::MIN_NUM:
151
case IrCmd::MAX_NUM:
152
case IrCmd::JUMP_CMP_NUM:
153
case IrCmd::FLOOR_NUM:
154
case IrCmd::CEIL_NUM:
155
case IrCmd::ROUND_NUM:
156
case IrCmd::SQRT_NUM:
157
case IrCmd::ABS_NUM:
158
break;
159
160
default:
161
// All instructions which reference registers have to be handled explicitly
162
for (auto& op : inst.ops)
163
CODEGEN_ASSERT(op.kind != IrOpKind::VmReg);
164
break;
165
}
166
}
167
168
void IrValueLocationTracking::afterInstLowering(IrInst& inst, uint32_t instIdx)
169
{
170
switch (inst.cmd)
171
{
172
case IrCmd::LOAD_TAG:
173
case IrCmd::LOAD_POINTER:
174
case IrCmd::LOAD_DOUBLE:
175
case IrCmd::LOAD_INT:
176
case IrCmd::LOAD_TVALUE:
177
if (OP_A(inst).kind == IrOpKind::VmReg)
178
invalidateRestoreOp(OP_A(inst), /*skipValueInvalidation*/ false);
179
180
recordRestoreOp(instIdx, OP_A(inst));
181
break;
182
case IrCmd::STORE_POINTER:
183
case IrCmd::STORE_DOUBLE:
184
case IrCmd::STORE_INT:
185
case IrCmd::STORE_TVALUE:
186
// If this is not the last use of the stored value, we can restore it from this new location
187
// Additionally, even if it's a last use, it might allow its argument to be restored
188
if (OP_B(inst).kind == IrOpKind::Inst)
189
{
190
IrInst& source = function.instOp(OP_B(inst));
191
192
if (source.lastUse != instIdx || canRematerializeArguments(source))
193
recordRestoreOp(OP_B(inst).index, OP_A(inst));
194
}
195
break;
196
case IrCmd::STORE_SPLIT_TVALUE:
197
// If this is not the last use of the stored value, we can restore it from this new location
198
// Additionally, even if it's a last use, it might allow its argument to be restored
199
if (OP_C(inst).kind == IrOpKind::Inst)
200
{
201
IrInst& source = function.instOp(OP_C(inst));
202
203
if (source.lastUse != instIdx || canRematerializeArguments(source))
204
recordRestoreOp(OP_C(inst).index, OP_A(inst));
205
}
206
break;
207
default:
208
break;
209
}
210
}
211
212
void IrValueLocationTracking::recordRestoreOp(uint32_t instIdx, IrOp location)
213
{
214
IrInst& inst = function.instructions[instIdx];
215
216
if (location.kind == IrOpKind::VmReg)
217
{
218
int reg = vmRegOp(location);
219
220
if (reg > maxReg)
221
maxReg = reg;
222
223
// Record location in register memory only if register is not captured
224
bool captured = function.cfg.captured.regs.test(reg);
225
226
if (!captured)
227
function.recordRestoreLocation(instIdx, {location, getCmdValueKind(inst.cmd), IrCmd::NOP});
228
229
vmRegValue[reg] = instIdx;
230
231
if (canBeRematerialized(inst.cmd) && OP_A(inst).kind == IrOpKind::Inst)
232
{
233
uint32_t depInstIdx = OP_A(inst).index;
234
235
if (!captured)
236
function.recordRestoreLocation(depInstIdx, {location, getCmdValueKind(inst.cmd), inst.cmd});
237
}
238
}
239
else if (location.kind == IrOpKind::VmConst)
240
{
241
function.recordRestoreLocation(instIdx, {location, getCmdValueKind(inst.cmd)});
242
}
243
}
244
245
void IrValueLocationTracking::invalidateRestoreOp(IrOp location, bool skipValueInvalidation)
246
{
247
if (location.kind == IrOpKind::VmReg)
248
{
249
uint32_t& instIdx = vmRegValue[vmRegOp(location)];
250
251
if (instIdx != kInvalidInstIdx)
252
{
253
IrInst& inst = function.instructions[instIdx];
254
255
// If we are only modifying the tag, we can avoid invalidating tracked location of values
256
if (skipValueInvalidation)
257
{
258
switch (getCmdValueKind(inst.cmd))
259
{
260
case IrValueKind::Double:
261
case IrValueKind::Pointer:
262
case IrValueKind::Int:
263
return;
264
default:
265
break;
266
}
267
}
268
269
// If instruction value is spilled and memory location is about to be lost, it has to be restored immediately
270
if (inst.needsReload)
271
restoreCallback(restoreCallbackCtx, inst);
272
273
// Get the current restore location of the instruction
274
ValueRestoreLocation currRestoreLocation = function.findRestoreLocation(instIdx, /* limitToCurrentBlock */ false);
275
276
// If the current location is the one that is being invalidated, we can no longer restore from it
277
if (location == currRestoreLocation.op)
278
function.recordRestoreLocation(instIdx, {});
279
280
// Register loses link with instruction
281
instIdx = kInvalidInstIdx;
282
283
// Chained instruction special case
284
if (canBeRematerialized(inst.cmd) && OP_A(inst).kind == IrOpKind::Inst)
285
{
286
uint32_t depInstIdx = OP_A(inst).index;
287
IrInst& depInst = function.instructions[depInstIdx];
288
289
if (depInst.needsReload)
290
restoreCallback(restoreCallbackCtx, depInst);
291
292
if (location == currRestoreLocation.op)
293
function.recordRestoreLocation(depInstIdx, {});
294
}
295
}
296
}
297
else if (location.kind == IrOpKind::VmConst)
298
{
299
CODEGEN_ASSERT(!"VM constants are immutable");
300
}
301
}
302
303
void IrValueLocationTracking::invalidateRestoreVmRegs(int start, int count)
304
{
305
int end = count == -1 ? 255 : start + count;
306
307
if (end > maxReg)
308
end = maxReg;
309
310
for (int reg = start; reg <= end; reg++)
311
invalidateRestoreOp(IrOp{IrOpKind::VmReg, uint8_t(reg)}, /*skipValueInvalidation*/ false);
312
}
313
314
} // namespace CodeGen
315
} // namespace Luau
316
317