Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/CodeGen/include/Luau/IrUtils.h
2727 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/Bytecode.h"
5
#include "Luau/Common.h"
6
#include "Luau/IrData.h"
7
8
#include <optional>
9
10
LUAU_FASTFLAG(LuauCodegenMarkDeadRegisters2)
11
LUAU_FASTFLAG(LuauCodegenDseOnCondJump)
12
13
namespace Luau
14
{
15
namespace CodeGen
16
{
17
18
struct IrBuilder;
19
enum class HostMetamethod;
20
21
int getOpLength(LuauOpcode op);
22
bool isJumpD(LuauOpcode op);
23
bool isSkipC(LuauOpcode op);
24
bool isFastCall(LuauOpcode op);
25
int getJumpTarget(uint32_t insn, uint32_t pc);
26
27
inline bool isBlockTerminator(IrCmd cmd)
28
{
29
switch (cmd)
30
{
31
case IrCmd::JUMP:
32
case IrCmd::JUMP_IF_TRUTHY:
33
case IrCmd::JUMP_IF_FALSY:
34
case IrCmd::JUMP_EQ_TAG:
35
case IrCmd::JUMP_CMP_INT:
36
case IrCmd::JUMP_EQ_POINTER:
37
case IrCmd::JUMP_CMP_NUM:
38
case IrCmd::JUMP_CMP_FLOAT:
39
case IrCmd::JUMP_FORN_LOOP_COND:
40
case IrCmd::JUMP_SLOT_MATCH:
41
case IrCmd::RETURN:
42
case IrCmd::FORGLOOP:
43
case IrCmd::FORGLOOP_FALLBACK:
44
case IrCmd::FORGPREP_XNEXT_FALLBACK:
45
case IrCmd::FALLBACK_FORGPREP:
46
return true;
47
default:
48
break;
49
}
50
51
return false;
52
}
53
54
inline bool isNonTerminatingJump(IrCmd cmd)
55
{
56
switch (cmd)
57
{
58
case IrCmd::TRY_NUM_TO_INDEX:
59
case IrCmd::TRY_CALL_FASTGETTM:
60
case IrCmd::CHECK_FASTCALL_RES:
61
case IrCmd::CHECK_TAG:
62
case IrCmd::CHECK_TRUTHY:
63
case IrCmd::CHECK_READONLY:
64
case IrCmd::CHECK_NO_METATABLE:
65
case IrCmd::CHECK_SAFE_ENV:
66
case IrCmd::CHECK_ARRAY_SIZE:
67
case IrCmd::CHECK_SLOT_MATCH:
68
case IrCmd::CHECK_NODE_NO_NEXT:
69
case IrCmd::CHECK_NODE_VALUE:
70
case IrCmd::CHECK_BUFFER_LEN:
71
case IrCmd::CHECK_USERDATA_TAG:
72
case IrCmd::CHECK_CMP_INT:
73
return true;
74
default:
75
break;
76
}
77
78
return false;
79
}
80
81
inline bool hasResult(IrCmd cmd)
82
{
83
switch (cmd)
84
{
85
case IrCmd::LOAD_TAG:
86
case IrCmd::LOAD_POINTER:
87
case IrCmd::LOAD_DOUBLE:
88
case IrCmd::LOAD_INT:
89
case IrCmd::LOAD_FLOAT:
90
case IrCmd::LOAD_TVALUE:
91
case IrCmd::LOAD_ENV:
92
case IrCmd::GET_ARR_ADDR:
93
case IrCmd::GET_SLOT_NODE_ADDR:
94
case IrCmd::GET_HASH_NODE_ADDR:
95
case IrCmd::GET_CLOSURE_UPVAL_ADDR:
96
case IrCmd::ADD_INT:
97
case IrCmd::SUB_INT:
98
case IrCmd::SEXTI8_INT:
99
case IrCmd::SEXTI16_INT:
100
case IrCmd::ADD_NUM:
101
case IrCmd::SUB_NUM:
102
case IrCmd::MUL_NUM:
103
case IrCmd::DIV_NUM:
104
case IrCmd::IDIV_NUM:
105
case IrCmd::MOD_NUM:
106
case IrCmd::MIN_NUM:
107
case IrCmd::MAX_NUM:
108
case IrCmd::UNM_NUM:
109
case IrCmd::FLOOR_NUM:
110
case IrCmd::CEIL_NUM:
111
case IrCmd::ROUND_NUM:
112
case IrCmd::SQRT_NUM:
113
case IrCmd::ABS_NUM:
114
case IrCmd::SIGN_NUM:
115
case IrCmd::ADD_FLOAT:
116
case IrCmd::SUB_FLOAT:
117
case IrCmd::MUL_FLOAT:
118
case IrCmd::DIV_FLOAT:
119
case IrCmd::MIN_FLOAT:
120
case IrCmd::MAX_FLOAT:
121
case IrCmd::UNM_FLOAT:
122
case IrCmd::FLOOR_FLOAT:
123
case IrCmd::CEIL_FLOAT:
124
case IrCmd::SQRT_FLOAT:
125
case IrCmd::ABS_FLOAT:
126
case IrCmd::SIGN_FLOAT:
127
case IrCmd::SELECT_NUM:
128
case IrCmd::SELECT_IF_TRUTHY:
129
case IrCmd::ADD_VEC:
130
case IrCmd::SUB_VEC:
131
case IrCmd::MUL_VEC:
132
case IrCmd::DIV_VEC:
133
case IrCmd::IDIV_VEC:
134
case IrCmd::UNM_VEC:
135
case IrCmd::MIN_VEC:
136
case IrCmd::MAX_VEC:
137
case IrCmd::FLOOR_VEC:
138
case IrCmd::CEIL_VEC:
139
case IrCmd::ABS_VEC:
140
case IrCmd::DOT_VEC:
141
case IrCmd::EXTRACT_VEC:
142
case IrCmd::NOT_ANY:
143
case IrCmd::CMP_ANY:
144
case IrCmd::CMP_INT:
145
case IrCmd::CMP_TAG:
146
case IrCmd::CMP_SPLIT_TVALUE:
147
case IrCmd::TABLE_LEN:
148
case IrCmd::TABLE_SETNUM:
149
case IrCmd::STRING_LEN:
150
case IrCmd::NEW_TABLE:
151
case IrCmd::DUP_TABLE:
152
case IrCmd::TRY_NUM_TO_INDEX:
153
case IrCmd::TRY_CALL_FASTGETTM:
154
case IrCmd::NEW_USERDATA:
155
case IrCmd::INT_TO_NUM:
156
case IrCmd::UINT_TO_NUM:
157
case IrCmd::UINT_TO_FLOAT:
158
case IrCmd::NUM_TO_INT:
159
case IrCmd::NUM_TO_UINT:
160
case IrCmd::FLOAT_TO_NUM:
161
case IrCmd::NUM_TO_FLOAT:
162
case IrCmd::FLOAT_TO_VEC:
163
case IrCmd::TAG_VECTOR:
164
case IrCmd::TRUNCATE_UINT:
165
case IrCmd::SUBSTITUTE:
166
case IrCmd::INVOKE_FASTCALL:
167
case IrCmd::BITAND_UINT:
168
case IrCmd::BITXOR_UINT:
169
case IrCmd::BITOR_UINT:
170
case IrCmd::BITNOT_UINT:
171
case IrCmd::BITLSHIFT_UINT:
172
case IrCmd::BITRSHIFT_UINT:
173
case IrCmd::BITARSHIFT_UINT:
174
case IrCmd::BITLROTATE_UINT:
175
case IrCmd::BITRROTATE_UINT:
176
case IrCmd::BITCOUNTLZ_UINT:
177
case IrCmd::BITCOUNTRZ_UINT:
178
case IrCmd::INVOKE_LIBM:
179
case IrCmd::GET_TYPE:
180
case IrCmd::GET_TYPEOF:
181
case IrCmd::NEWCLOSURE:
182
case IrCmd::FINDUPVAL:
183
case IrCmd::BUFFER_READI8:
184
case IrCmd::BUFFER_READU8:
185
case IrCmd::BUFFER_READI16:
186
case IrCmd::BUFFER_READU16:
187
case IrCmd::BUFFER_READI32:
188
case IrCmd::BUFFER_READF32:
189
case IrCmd::BUFFER_READF64:
190
case IrCmd::GET_UPVALUE:
191
return true;
192
default:
193
break;
194
}
195
196
return false;
197
}
198
199
inline bool canInvalidateSafeEnv(IrCmd cmd)
200
{
201
switch (cmd)
202
{
203
case IrCmd::CMP_ANY:
204
case IrCmd::DO_ARITH:
205
case IrCmd::DO_LEN:
206
case IrCmd::GET_TABLE:
207
case IrCmd::SET_TABLE:
208
case IrCmd::CONCAT: // TODO: if only strings and numbers are concatenated, there will be no user calls
209
case IrCmd::CALL:
210
case IrCmd::FORGLOOP_FALLBACK:
211
case IrCmd::FALLBACK_GETGLOBAL:
212
case IrCmd::FALLBACK_SETGLOBAL:
213
case IrCmd::FALLBACK_GETTABLEKS:
214
case IrCmd::FALLBACK_SETTABLEKS:
215
case IrCmd::FALLBACK_NAMECALL:
216
case IrCmd::FALLBACK_FORGPREP:
217
return true;
218
default:
219
break;
220
}
221
222
return false;
223
}
224
225
inline bool isPseudo(IrCmd cmd)
226
{
227
// Instructions that are used for internal needs and are not a part of final lowering
228
if (FFlag::LuauCodegenMarkDeadRegisters2 || FFlag::LuauCodegenDseOnCondJump)
229
return cmd == IrCmd::NOP || cmd == IrCmd::SUBSTITUTE || cmd == IrCmd::MARK_USED || cmd == IrCmd::MARK_DEAD;
230
else
231
return cmd == IrCmd::NOP || cmd == IrCmd::SUBSTITUTE;
232
}
233
234
inline bool hasSideEffects(IrCmd cmd)
235
{
236
if (cmd == IrCmd::INVOKE_FASTCALL)
237
return true;
238
239
if (isPseudo(cmd))
240
return false;
241
242
// Instructions that don't produce a result most likely have other side-effects to make them useful
243
// Right now, a full switch would mirror the 'hasResult' function, so we use this simple condition
244
return !hasResult(cmd);
245
}
246
247
inline bool producesDirtyHighRegisterBits(IrCmd cmd)
248
{
249
return cmd == IrCmd::NUM_TO_UINT || cmd == IrCmd::INVOKE_FASTCALL || cmd == IrCmd::CMP_ANY;
250
}
251
252
// Returns a condition that for 'a op b' will result in '!(a op b)'
253
inline IrCondition getNegatedCondition(IrCondition cond)
254
{
255
switch (cond)
256
{
257
case IrCondition::Equal:
258
return IrCondition::NotEqual;
259
case IrCondition::NotEqual:
260
return IrCondition::Equal;
261
case IrCondition::Less:
262
return IrCondition::NotLess;
263
case IrCondition::NotLess:
264
return IrCondition::Less;
265
case IrCondition::LessEqual:
266
return IrCondition::NotLessEqual;
267
case IrCondition::NotLessEqual:
268
return IrCondition::LessEqual;
269
case IrCondition::Greater:
270
return IrCondition::NotGreater;
271
case IrCondition::NotGreater:
272
return IrCondition::Greater;
273
case IrCondition::GreaterEqual:
274
return IrCondition::NotGreaterEqual;
275
case IrCondition::NotGreaterEqual:
276
return IrCondition::GreaterEqual;
277
case IrCondition::UnsignedLess:
278
return IrCondition::UnsignedGreaterEqual;
279
case IrCondition::UnsignedLessEqual:
280
return IrCondition::UnsignedGreater;
281
case IrCondition::UnsignedGreater:
282
return IrCondition::UnsignedLessEqual;
283
case IrCondition::UnsignedGreaterEqual:
284
return IrCondition::UnsignedLess;
285
default:
286
CODEGEN_ASSERT(!"Unsupported condition");
287
return IrCondition::Count;
288
}
289
}
290
291
IrValueKind getCmdValueKind(IrCmd cmd);
292
IrValueKind getConstValueKind(const IrConst& constant);
293
294
template<typename F>
295
void visitArguments(IrInst& inst, F&& func)
296
{
297
if (isPseudo(inst.cmd))
298
return;
299
300
for (auto& op : inst.ops)
301
func(op);
302
}
303
template<typename F>
304
bool anyArgumentMatch(IrInst& inst, F&& func)
305
{
306
if (isPseudo(inst.cmd))
307
return false;
308
309
for (auto& op : inst.ops)
310
if (func(op))
311
return true;
312
return false;
313
}
314
315
bool isGCO(uint8_t tag);
316
317
// Optional bit has to be cleared at call site, otherwise, this will return 'false' for 'userdata?'
318
bool isUserdataBytecodeType(uint8_t ty);
319
bool isCustomUserdataBytecodeType(uint8_t ty);
320
321
// Check that 'ty' is 'expected' or 'any'
322
bool isExpectedOrUnknownBytecodeType(uint8_t ty, LuauBytecodeType expected);
323
324
HostMetamethod tmToHostMetamethod(int tm);
325
326
// Manually add or remove use of an operand
327
void addUse(IrFunction& function, IrOp op);
328
void removeUse(IrFunction& function, IrOp op);
329
330
// Remove a single instruction
331
void kill(IrFunction& function, IrInst& inst);
332
333
// Remove a range of instructions
334
void kill(IrFunction& function, uint32_t start, uint32_t end);
335
336
// Remove a block, including all instructions inside
337
void kill(IrFunction& function, IrBlock& block);
338
339
// Replace a single operand and update use counts (can cause chain removal of dead code)
340
void replace(IrFunction& function, IrOp& original, IrOp replacement);
341
342
// Replace a single instruction
343
// Target instruction index instead of reference is used to handle introduction of a new block terminator
344
void replace(IrFunction& function, IrBlock& block, uint32_t instIdx, IrInst replacement);
345
346
// Replace instruction with a different value (using IrCmd::SUBSTITUTE)
347
void substitute(IrFunction& function, IrInst& inst, IrOp replacement);
348
349
// Replace instruction arguments that point to substitutions with target values
350
void applySubstitutions(IrFunction& function, IrOp& op);
351
void applySubstitutions(IrFunction& function, IrInst& inst);
352
353
// Compare numbers using IR condition value
354
bool compare(double a, double b, IrCondition cond);
355
356
// Perform constant folding on instruction at index
357
// For most instructions, successful folding results in a IrCmd::SUBSTITUTE
358
// But it can also be successful on conditional control-flow, replacing it with an unconditional IrCmd::JUMP
359
void foldConstants(IrBuilder& build, IrFunction& function, IrBlock& block, uint32_t instIdx);
360
361
uint32_t getNativeContextOffset(int bfid);
362
363
// Cleans up blocks that were created with no users
364
void killUnusedBlocks(IrFunction& function);
365
366
// Get blocks in order that tries to maximize fallthrough between them during lowering
367
// We want to mostly preserve build order with fallbacks outlined
368
// But we also use hints from optimization passes that chain blocks together where there's only one out-in edge between them
369
std::vector<uint32_t> getSortedBlockOrder(IrFunction& function);
370
371
// Returns first non-dead block that comes after block at index 'i' in the sorted blocks array
372
// 'dummy' block is returned if the end of array was reached
373
IrBlock& getNextBlock(IrFunction& function, const std::vector<uint32_t>& sortedBlocks, IrBlock& dummy, size_t i);
374
375
// Returns next block in a chain, marked by 'constPropInBlockChains' optimization pass
376
IrBlock* tryGetNextBlockInChain(IrFunction& function, IrBlock& block);
377
378
bool isEntryBlock(const IrBlock& block);
379
380
// When an operand is an instruction, try to extract the tag which is contained inside that value
381
std::optional<uint8_t> tryGetOperandTag(IrFunction& function, IrOp op);
382
383
// Propagates register tags from predecessor blocks' exit states into the current block's entry state for live in registers
384
// Calls getTag for each register slot to read current tag value (kUnknownTag if unknown)
385
// Calls setTag to update the tag value (kUnknownTag if it cannot be determined)
386
// Assigns the tag directly for the first predecessor
387
// For subsequent predecessors, intersects and sets kUnknownTag when predecessors disagree
388
void propagateTagsFromPredecessors(
389
const IrFunction& function,
390
const IrBlock& block,
391
std::function<uint8_t(size_t)> getTag,
392
std::function<void(size_t, uint8_t)> setTag
393
);
394
395
// If optional part is not ignored, types like 'number?' will fail to convert
396
std::optional<uint8_t> tryGetLuauTagForBcType(uint8_t bcType, bool ignoreOptionalPart);
397
398
} // namespace CodeGen
399
} // namespace Luau
400
401