Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/CodeGen/src/IrUtils.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 "Luau/IrUtils.h"
3
4
#include "Luau/CodeGenOptions.h"
5
#include "Luau/IrBuilder.h"
6
7
#include "BitUtils.h"
8
#include "NativeState.h"
9
10
#include "lua.h"
11
#include "lnumutils.h"
12
13
#include <algorithm>
14
#include <vector>
15
16
#include <limits.h>
17
#include <math.h>
18
19
LUAU_FASTFLAG(LuauCodegenBufferRangeMerge4)
20
LUAU_FASTFLAGVARIABLE(LuauCodegenTruncatedSubsts)
21
LUAU_FASTFLAG(LuauCodegenPropagateTagsAcrossChains2)
22
23
namespace Luau
24
{
25
namespace CodeGen
26
{
27
28
constexpr double kDoubleMaxExactInteger = 9007199254740992.0;
29
30
int getOpLength(LuauOpcode op)
31
{
32
switch (int(op))
33
{
34
case LOP_GETGLOBAL:
35
case LOP_SETGLOBAL:
36
case LOP_GETIMPORT:
37
case LOP_GETTABLEKS:
38
case LOP_SETTABLEKS:
39
case LOP_NAMECALL:
40
case LOP_JUMPIFEQ:
41
case LOP_JUMPIFLE:
42
case LOP_JUMPIFLT:
43
case LOP_JUMPIFNOTEQ:
44
case LOP_JUMPIFNOTLE:
45
case LOP_JUMPIFNOTLT:
46
case LOP_NEWTABLE:
47
case LOP_SETLIST:
48
case LOP_FORGLOOP:
49
case LOP_LOADKX:
50
case LOP_FASTCALL2:
51
case LOP_FASTCALL2K:
52
case LOP_FASTCALL3:
53
case LOP_JUMPXEQKNIL:
54
case LOP_JUMPXEQKB:
55
case LOP_JUMPXEQKN:
56
case LOP_JUMPXEQKS:
57
return 2;
58
59
default:
60
return 1;
61
}
62
}
63
64
bool isJumpD(LuauOpcode op)
65
{
66
switch (int(op))
67
{
68
case LOP_JUMP:
69
case LOP_JUMPIF:
70
case LOP_JUMPIFNOT:
71
case LOP_JUMPIFEQ:
72
case LOP_JUMPIFLE:
73
case LOP_JUMPIFLT:
74
case LOP_JUMPIFNOTEQ:
75
case LOP_JUMPIFNOTLE:
76
case LOP_JUMPIFNOTLT:
77
case LOP_FORNPREP:
78
case LOP_FORNLOOP:
79
case LOP_FORGPREP:
80
case LOP_FORGLOOP:
81
case LOP_FORGPREP_INEXT:
82
case LOP_FORGPREP_NEXT:
83
case LOP_JUMPBACK:
84
case LOP_JUMPXEQKNIL:
85
case LOP_JUMPXEQKB:
86
case LOP_JUMPXEQKN:
87
case LOP_JUMPXEQKS:
88
return true;
89
90
default:
91
return false;
92
}
93
}
94
95
bool isSkipC(LuauOpcode op)
96
{
97
switch (int(op))
98
{
99
case LOP_LOADB:
100
return true;
101
102
default:
103
return false;
104
}
105
}
106
107
bool isFastCall(LuauOpcode op)
108
{
109
switch (int(op))
110
{
111
case LOP_FASTCALL:
112
case LOP_FASTCALL1:
113
case LOP_FASTCALL2:
114
case LOP_FASTCALL2K:
115
case LOP_FASTCALL3:
116
return true;
117
118
default:
119
return false;
120
}
121
}
122
123
int getJumpTarget(uint32_t insn, uint32_t pc)
124
{
125
LuauOpcode op = LuauOpcode(LUAU_INSN_OP(insn));
126
127
if (isJumpD(op))
128
return int(pc + LUAU_INSN_D(insn) + 1);
129
else if (isFastCall(op))
130
return int(pc + LUAU_INSN_C(insn) + 2);
131
else if (isSkipC(op) && LUAU_INSN_C(insn))
132
return int(pc + LUAU_INSN_C(insn) + 1);
133
else if (int(op) == LOP_JUMPX)
134
return int(pc + LUAU_INSN_E(insn) + 1);
135
else
136
return -1;
137
}
138
139
IrValueKind getCmdValueKind(IrCmd cmd)
140
{
141
switch (cmd)
142
{
143
case IrCmd::NOP:
144
return IrValueKind::None;
145
case IrCmd::LOAD_TAG:
146
return IrValueKind::Tag;
147
case IrCmd::LOAD_POINTER:
148
return IrValueKind::Pointer;
149
case IrCmd::LOAD_DOUBLE:
150
return IrValueKind::Double;
151
case IrCmd::LOAD_INT:
152
return IrValueKind::Int;
153
case IrCmd::LOAD_FLOAT:
154
return IrValueKind::Float;
155
case IrCmd::LOAD_TVALUE:
156
return IrValueKind::Tvalue;
157
case IrCmd::LOAD_ENV:
158
case IrCmd::GET_ARR_ADDR:
159
case IrCmd::GET_SLOT_NODE_ADDR:
160
case IrCmd::GET_HASH_NODE_ADDR:
161
case IrCmd::GET_CLOSURE_UPVAL_ADDR:
162
return IrValueKind::Pointer;
163
case IrCmd::STORE_TAG:
164
case IrCmd::STORE_EXTRA:
165
case IrCmd::STORE_POINTER:
166
case IrCmd::STORE_DOUBLE:
167
case IrCmd::STORE_INT:
168
case IrCmd::STORE_VECTOR:
169
case IrCmd::STORE_TVALUE:
170
case IrCmd::STORE_SPLIT_TVALUE:
171
return IrValueKind::None;
172
case IrCmd::ADD_INT:
173
case IrCmd::SUB_INT:
174
case IrCmd::SEXTI8_INT:
175
case IrCmd::SEXTI16_INT:
176
return IrValueKind::Int;
177
case IrCmd::ADD_NUM:
178
case IrCmd::SUB_NUM:
179
case IrCmd::MUL_NUM:
180
case IrCmd::DIV_NUM:
181
case IrCmd::IDIV_NUM:
182
case IrCmd::MOD_NUM:
183
case IrCmd::MIN_NUM:
184
case IrCmd::MAX_NUM:
185
case IrCmd::UNM_NUM:
186
case IrCmd::FLOOR_NUM:
187
case IrCmd::CEIL_NUM:
188
case IrCmd::ROUND_NUM:
189
case IrCmd::SQRT_NUM:
190
case IrCmd::ABS_NUM:
191
case IrCmd::SIGN_NUM:
192
case IrCmd::SELECT_NUM:
193
case IrCmd::MULADD_NUM:
194
return IrValueKind::Double;
195
case IrCmd::ADD_FLOAT:
196
case IrCmd::SUB_FLOAT:
197
case IrCmd::MUL_FLOAT:
198
case IrCmd::DIV_FLOAT:
199
case IrCmd::MIN_FLOAT:
200
case IrCmd::MAX_FLOAT:
201
case IrCmd::UNM_FLOAT:
202
case IrCmd::FLOOR_FLOAT:
203
case IrCmd::CEIL_FLOAT:
204
case IrCmd::SQRT_FLOAT:
205
case IrCmd::ABS_FLOAT:
206
case IrCmd::SIGN_FLOAT:
207
return IrValueKind::Float;
208
case IrCmd::ADD_VEC:
209
case IrCmd::SUB_VEC:
210
case IrCmd::MUL_VEC:
211
case IrCmd::DIV_VEC:
212
case IrCmd::IDIV_VEC:
213
case IrCmd::UNM_VEC:
214
case IrCmd::MIN_VEC:
215
case IrCmd::MAX_VEC:
216
case IrCmd::FLOOR_VEC:
217
case IrCmd::CEIL_VEC:
218
case IrCmd::ABS_VEC:
219
case IrCmd::SELECT_VEC:
220
case IrCmd::SELECT_IF_TRUTHY:
221
case IrCmd::MULADD_VEC:
222
return IrValueKind::Tvalue;
223
case IrCmd::DOT_VEC:
224
case IrCmd::EXTRACT_VEC:
225
return IrValueKind::Float;
226
case IrCmd::NOT_ANY:
227
case IrCmd::CMP_ANY:
228
case IrCmd::CMP_INT:
229
case IrCmd::CMP_TAG:
230
case IrCmd::CMP_SPLIT_TVALUE:
231
return IrValueKind::Int;
232
case IrCmd::JUMP:
233
case IrCmd::JUMP_IF_TRUTHY:
234
case IrCmd::JUMP_IF_FALSY:
235
case IrCmd::JUMP_EQ_TAG:
236
case IrCmd::JUMP_CMP_INT:
237
case IrCmd::JUMP_EQ_POINTER:
238
case IrCmd::JUMP_CMP_NUM:
239
case IrCmd::JUMP_CMP_FLOAT:
240
case IrCmd::JUMP_FORN_LOOP_COND:
241
case IrCmd::JUMP_SLOT_MATCH:
242
return IrValueKind::None;
243
case IrCmd::TABLE_LEN:
244
return IrValueKind::Int;
245
case IrCmd::TABLE_SETNUM:
246
return IrValueKind::Pointer;
247
case IrCmd::STRING_LEN:
248
return IrValueKind::Int;
249
case IrCmd::NEW_TABLE:
250
case IrCmd::DUP_TABLE:
251
return IrValueKind::Pointer;
252
case IrCmd::TRY_NUM_TO_INDEX:
253
return IrValueKind::Int;
254
case IrCmd::TRY_CALL_FASTGETTM:
255
case IrCmd::NEW_USERDATA:
256
return IrValueKind::Pointer;
257
case IrCmd::INT_TO_NUM:
258
case IrCmd::UINT_TO_NUM:
259
return IrValueKind::Double;
260
case IrCmd::UINT_TO_FLOAT:
261
return IrValueKind::Float;
262
case IrCmd::NUM_TO_INT:
263
case IrCmd::NUM_TO_UINT:
264
return IrValueKind::Int;
265
case IrCmd::FLOAT_TO_NUM:
266
return IrValueKind::Double;
267
case IrCmd::NUM_TO_FLOAT:
268
return IrValueKind::Float;
269
case IrCmd::FLOAT_TO_VEC:
270
case IrCmd::TAG_VECTOR:
271
return IrValueKind::Tvalue;
272
case IrCmd::TRUNCATE_UINT:
273
return IrValueKind::Int;
274
case IrCmd::ADJUST_STACK_TO_REG:
275
case IrCmd::ADJUST_STACK_TO_TOP:
276
return IrValueKind::None;
277
case IrCmd::FASTCALL:
278
return IrValueKind::None;
279
case IrCmd::INVOKE_FASTCALL:
280
return IrValueKind::Int;
281
case IrCmd::CHECK_FASTCALL_RES:
282
case IrCmd::DO_ARITH:
283
case IrCmd::DO_LEN:
284
case IrCmd::GET_TABLE:
285
case IrCmd::SET_TABLE:
286
case IrCmd::GET_CACHED_IMPORT:
287
case IrCmd::CONCAT:
288
return IrValueKind::None;
289
case IrCmd::GET_UPVALUE:
290
return IrValueKind::Tvalue;
291
case IrCmd::SET_UPVALUE:
292
case IrCmd::CHECK_TAG:
293
case IrCmd::CHECK_TRUTHY:
294
case IrCmd::CHECK_READONLY:
295
case IrCmd::CHECK_NO_METATABLE:
296
case IrCmd::CHECK_SAFE_ENV:
297
case IrCmd::CHECK_ARRAY_SIZE:
298
case IrCmd::CHECK_SLOT_MATCH:
299
case IrCmd::CHECK_NODE_NO_NEXT:
300
case IrCmd::CHECK_NODE_VALUE:
301
case IrCmd::CHECK_BUFFER_LEN:
302
case IrCmd::CHECK_USERDATA_TAG:
303
case IrCmd::CHECK_CMP_INT:
304
case IrCmd::INTERRUPT:
305
case IrCmd::CHECK_GC:
306
case IrCmd::BARRIER_OBJ:
307
case IrCmd::BARRIER_TABLE_BACK:
308
case IrCmd::BARRIER_TABLE_FORWARD:
309
case IrCmd::SET_SAVEDPC:
310
case IrCmd::CLOSE_UPVALS:
311
case IrCmd::CAPTURE:
312
case IrCmd::SETLIST:
313
case IrCmd::CALL:
314
case IrCmd::RETURN:
315
case IrCmd::FORGLOOP:
316
case IrCmd::FORGLOOP_FALLBACK:
317
case IrCmd::FORGPREP_XNEXT_FALLBACK:
318
case IrCmd::COVERAGE:
319
case IrCmd::FALLBACK_GETGLOBAL:
320
case IrCmd::FALLBACK_SETGLOBAL:
321
case IrCmd::FALLBACK_GETTABLEKS:
322
case IrCmd::FALLBACK_SETTABLEKS:
323
case IrCmd::FALLBACK_NAMECALL:
324
case IrCmd::FALLBACK_PREPVARARGS:
325
case IrCmd::FALLBACK_GETVARARGS:
326
return IrValueKind::None;
327
case IrCmd::NEWCLOSURE:
328
return IrValueKind::Pointer;
329
case IrCmd::FALLBACK_DUPCLOSURE:
330
case IrCmd::FALLBACK_FORGPREP:
331
return IrValueKind::None;
332
case IrCmd::SUBSTITUTE:
333
return IrValueKind::Unknown;
334
case IrCmd::MARK_USED:
335
case IrCmd::MARK_DEAD:
336
return IrValueKind::None;
337
case IrCmd::BITAND_UINT:
338
case IrCmd::BITXOR_UINT:
339
case IrCmd::BITOR_UINT:
340
case IrCmd::BITNOT_UINT:
341
case IrCmd::BITLSHIFT_UINT:
342
case IrCmd::BITRSHIFT_UINT:
343
case IrCmd::BITARSHIFT_UINT:
344
case IrCmd::BITLROTATE_UINT:
345
case IrCmd::BITRROTATE_UINT:
346
case IrCmd::BITCOUNTLZ_UINT:
347
case IrCmd::BITCOUNTRZ_UINT:
348
case IrCmd::BYTESWAP_UINT:
349
return IrValueKind::Int;
350
case IrCmd::INVOKE_LIBM:
351
return IrValueKind::Double;
352
case IrCmd::GET_TYPE:
353
case IrCmd::GET_TYPEOF:
354
return IrValueKind::Pointer;
355
case IrCmd::FINDUPVAL:
356
return IrValueKind::Pointer;
357
case IrCmd::BUFFER_READI8:
358
case IrCmd::BUFFER_READU8:
359
case IrCmd::BUFFER_READI16:
360
case IrCmd::BUFFER_READU16:
361
case IrCmd::BUFFER_READI32:
362
return IrValueKind::Int;
363
case IrCmd::BUFFER_WRITEI8:
364
case IrCmd::BUFFER_WRITEI16:
365
case IrCmd::BUFFER_WRITEI32:
366
case IrCmd::BUFFER_WRITEF32:
367
case IrCmd::BUFFER_WRITEF64:
368
return IrValueKind::None;
369
case IrCmd::BUFFER_READF32:
370
return IrValueKind::Float;
371
case IrCmd::BUFFER_READF64:
372
return IrValueKind::Double;
373
}
374
375
LUAU_UNREACHABLE();
376
}
377
378
IrValueKind getConstValueKind(const IrConst& constant)
379
{
380
switch (constant.kind)
381
{
382
case IrConstKind::Int:
383
return IrValueKind::Int;
384
case IrConstKind::Uint:
385
return IrValueKind::Int;
386
case IrConstKind::Double:
387
return IrValueKind::Double;
388
case IrConstKind::Tag:
389
return IrValueKind::Tag;
390
case IrConstKind::Import:
391
CODEGEN_ASSERT(!"Import constants cannot be used as IR values");
392
return IrValueKind::Unknown;
393
}
394
395
LUAU_UNREACHABLE();
396
}
397
398
static void removeInstUse(IrFunction& function, uint32_t instIdx)
399
{
400
IrInst& inst = function.instructions[instIdx];
401
402
CODEGEN_ASSERT(inst.useCount);
403
inst.useCount--;
404
405
if (inst.useCount == 0)
406
kill(function, inst);
407
}
408
409
static void removeBlockUse(IrFunction& function, uint32_t blockIdx)
410
{
411
IrBlock& block = function.blocks[blockIdx];
412
413
CODEGEN_ASSERT(block.useCount);
414
block.useCount--;
415
416
// Entry block is never removed because is has an implicit use
417
if (block.useCount == 0 && blockIdx != 0)
418
kill(function, block);
419
}
420
421
void addUse(IrFunction& function, IrOp op)
422
{
423
if (op.kind == IrOpKind::Inst)
424
function.instructions[op.index].useCount++;
425
else if (op.kind == IrOpKind::Block)
426
function.blocks[op.index].useCount++;
427
}
428
429
void removeUse(IrFunction& function, IrOp op)
430
{
431
if (op.kind == IrOpKind::Inst)
432
removeInstUse(function, op.index);
433
else if (op.kind == IrOpKind::Block)
434
removeBlockUse(function, op.index);
435
}
436
437
bool isGCO(uint8_t tag)
438
{
439
CODEGEN_ASSERT(tag < LUA_T_COUNT);
440
441
// mirrors iscollectable(o) from VM/lobject.h
442
return tag >= LUA_TSTRING;
443
}
444
445
bool isUserdataBytecodeType(uint8_t ty)
446
{
447
return ty == LBC_TYPE_USERDATA || isCustomUserdataBytecodeType(ty);
448
}
449
450
bool isCustomUserdataBytecodeType(uint8_t ty)
451
{
452
return ty >= LBC_TYPE_TAGGED_USERDATA_BASE && ty < LBC_TYPE_TAGGED_USERDATA_END;
453
}
454
455
bool isExpectedOrUnknownBytecodeType(uint8_t ty, LuauBytecodeType expected)
456
{
457
return ty == LBC_TYPE_ANY || ty == expected;
458
}
459
460
HostMetamethod tmToHostMetamethod(int tm)
461
{
462
switch (TMS(tm))
463
{
464
case TM_ADD:
465
return HostMetamethod::Add;
466
case TM_SUB:
467
return HostMetamethod::Sub;
468
case TM_MUL:
469
return HostMetamethod::Mul;
470
case TM_DIV:
471
return HostMetamethod::Div;
472
case TM_IDIV:
473
return HostMetamethod::Idiv;
474
case TM_MOD:
475
return HostMetamethod::Mod;
476
case TM_POW:
477
return HostMetamethod::Pow;
478
case TM_UNM:
479
return HostMetamethod::Minus;
480
case TM_EQ:
481
return HostMetamethod::Equal;
482
case TM_LT:
483
return HostMetamethod::LessThan;
484
case TM_LE:
485
return HostMetamethod::LessEqual;
486
case TM_LEN:
487
return HostMetamethod::Length;
488
case TM_CONCAT:
489
return HostMetamethod::Concat;
490
default:
491
CODEGEN_ASSERT(!"invalid tag method for host");
492
break;
493
}
494
495
return HostMetamethod::Add;
496
}
497
498
void kill(IrFunction& function, IrInst& inst)
499
{
500
CODEGEN_ASSERT(inst.useCount == 0);
501
502
inst.cmd = IrCmd::NOP;
503
504
for (auto& op : inst.ops)
505
removeUse(function, op);
506
inst.ops.clear();
507
}
508
509
void kill(IrFunction& function, uint32_t start, uint32_t end)
510
{
511
// Kill instructions in reverse order to avoid killing instructions that are still marked as used
512
for (int i = int(end); i >= int(start); i--)
513
{
514
CODEGEN_ASSERT(unsigned(i) < function.instructions.size());
515
IrInst& curr = function.instructions[i];
516
517
if (curr.cmd == IrCmd::NOP)
518
continue;
519
520
// Do not force destruction of instructions that are still in use
521
// When the operands are released, the instruction will be released automatically
522
if (curr.useCount != 0)
523
continue;
524
525
kill(function, curr);
526
}
527
}
528
529
void kill(IrFunction& function, IrBlock& block)
530
{
531
CODEGEN_ASSERT(block.useCount == 0);
532
533
block.kind = IrBlockKind::Dead;
534
535
kill(function, block.start, block.finish);
536
block.start = ~0u;
537
block.finish = ~0u;
538
}
539
540
void replace(IrFunction& function, IrOp& original, IrOp replacement)
541
{
542
// Add use before removing new one if that's the last one keeping target operand alive
543
addUse(function, replacement);
544
removeUse(function, original);
545
546
original = replacement;
547
}
548
549
void replace(IrFunction& function, IrBlock& block, uint32_t instIdx, IrInst replacement)
550
{
551
IrInst& inst = function.instructions[instIdx];
552
553
// Add uses before removing new ones if those are the last ones keeping target operand alive
554
for (auto& op : replacement.ops)
555
addUse(function, op);
556
557
// If we introduced an earlier terminating instruction, all following instructions become dead
558
if (!isBlockTerminator(inst.cmd) && isBlockTerminator(replacement.cmd))
559
{
560
// Block has has to be fully constructed before replacement is performed
561
CODEGEN_ASSERT(block.finish != ~0u);
562
CODEGEN_ASSERT(instIdx + 1 <= block.finish);
563
564
kill(function, instIdx + 1, block.finish);
565
566
// If killing that range killed the current block we have to undo replacement instruction uses and exit
567
if (block.kind == IrBlockKind::Dead)
568
{
569
for (auto& op : replacement.ops)
570
removeUse(function, op);
571
return;
572
}
573
574
block.finish = instIdx;
575
}
576
577
// Before we remove old argument uses, we have to place our new instruction
578
IrInst copy = inst;
579
580
// Inherit existing use count (last use is skipped as it will be defined later)
581
replacement.useCount = inst.useCount;
582
583
inst = replacement;
584
585
for (auto& op : copy.ops)
586
removeUse(function, op);
587
}
588
589
void substitute(IrFunction& function, IrInst& inst, IrOp replacement)
590
{
591
CODEGEN_ASSERT(!isBlockTerminator(inst.cmd));
592
593
inst.cmd = IrCmd::SUBSTITUTE;
594
595
addUse(function, replacement);
596
597
for (auto& op : inst.ops)
598
removeUse(function, op);
599
600
inst.ops.resize(1);
601
OP_A(inst) = replacement;
602
}
603
604
void applySubstitutions(IrFunction& function, IrOp& op)
605
{
606
if (op.kind == IrOpKind::Inst)
607
{
608
IrInst& src = function.instructions[op.index];
609
610
if (src.cmd == IrCmd::SUBSTITUTE)
611
{
612
op.kind = OP_A(src).kind;
613
op.index = OP_A(src).index;
614
615
// If we substitute with the result of a different instruction, update the use count
616
if (op.kind == IrOpKind::Inst)
617
{
618
IrInst& dst = function.instructions[op.index];
619
CODEGEN_ASSERT(dst.cmd != IrCmd::SUBSTITUTE && "chained substitutions are not allowed");
620
621
dst.useCount++;
622
}
623
624
CODEGEN_ASSERT(src.useCount > 0);
625
src.useCount--;
626
627
if (src.useCount == 0)
628
{
629
src.cmd = IrCmd::NOP;
630
removeUse(function, OP_A(src));
631
src.ops.clear();
632
}
633
}
634
}
635
}
636
637
void applySubstitutions(IrFunction& function, IrInst& inst)
638
{
639
for (auto& op : inst.ops)
640
applySubstitutions(function, op);
641
}
642
643
bool compare(double a, double b, IrCondition cond)
644
{
645
// Note: redundant bool() casts work around invalid MSVC optimization that merges cases in this switch, violating IEEE754 comparison semantics
646
switch (cond)
647
{
648
case IrCondition::Equal:
649
return a == b;
650
case IrCondition::NotEqual:
651
return a != b;
652
case IrCondition::Less:
653
return a < b;
654
case IrCondition::NotLess:
655
return !bool(a < b);
656
case IrCondition::LessEqual:
657
return a <= b;
658
case IrCondition::NotLessEqual:
659
return !bool(a <= b);
660
case IrCondition::Greater:
661
return a > b;
662
case IrCondition::NotGreater:
663
return !bool(a > b);
664
case IrCondition::GreaterEqual:
665
return a >= b;
666
case IrCondition::NotGreaterEqual:
667
return !bool(a >= b);
668
default:
669
CODEGEN_ASSERT(!"Unsupported condition");
670
}
671
672
return false;
673
}
674
675
bool compare(int a, int b, IrCondition cond)
676
{
677
switch (cond)
678
{
679
case IrCondition::Equal:
680
return a == b;
681
case IrCondition::NotEqual:
682
return a != b;
683
case IrCondition::Less:
684
return a < b;
685
case IrCondition::NotLess:
686
return !(a < b);
687
case IrCondition::LessEqual:
688
return a <= b;
689
case IrCondition::NotLessEqual:
690
return !(a <= b);
691
case IrCondition::Greater:
692
return a > b;
693
case IrCondition::NotGreater:
694
return !(a > b);
695
case IrCondition::GreaterEqual:
696
return a >= b;
697
case IrCondition::NotGreaterEqual:
698
return !(a >= b);
699
case IrCondition::UnsignedLess:
700
return unsigned(a) < unsigned(b);
701
case IrCondition::UnsignedLessEqual:
702
return unsigned(a) <= unsigned(b);
703
case IrCondition::UnsignedGreater:
704
return unsigned(a) > unsigned(b);
705
case IrCondition::UnsignedGreaterEqual:
706
return unsigned(a) >= unsigned(b);
707
default:
708
CODEGEN_ASSERT(!"Unsupported condition");
709
}
710
711
return false;
712
}
713
714
static void substituteWithTruncatedUint(IrFunction& function, IrBlock& block, IrInst& inst, IrOp op)
715
{
716
CODEGEN_ASSERT(FFlag::LuauCodegenTruncatedSubsts);
717
718
if (IrInst* srcOfSrc = function.asInstOp(op); srcOfSrc && producesDirtyHighRegisterBits(srcOfSrc->cmd))
719
replace(function, block, function.getInstIndex(inst), IrInst{IrCmd::TRUNCATE_UINT, {op}});
720
else
721
substitute(function, inst, op);
722
}
723
724
void foldConstants(IrBuilder& build, IrFunction& function, IrBlock& block, uint32_t index)
725
{
726
IrInst& inst = function.instructions[index];
727
728
switch (inst.cmd)
729
{
730
case IrCmd::ADD_INT:
731
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
732
{
733
// We need to avoid signed integer overflow, but we also have to produce a result
734
// So we add numbers as unsigned and use fixed-width integer types to force a two's complement evaluation
735
int32_t lhs = function.intOp(OP_A(inst));
736
int32_t rhs = function.intOp(OP_B(inst));
737
int sum = int32_t(uint32_t(lhs) + uint32_t(rhs));
738
739
substitute(function, inst, build.constInt(sum));
740
}
741
break;
742
case IrCmd::SUB_INT:
743
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
744
{
745
// We need to avoid signed integer overflow, but we also have to produce a result
746
// So we subtract numbers as unsigned and use fixed-width integer types to force a two's complement evaluation
747
int32_t lhs = function.intOp(OP_A(inst));
748
int32_t rhs = function.intOp(OP_B(inst));
749
int sum = int32_t(uint32_t(lhs) - uint32_t(rhs));
750
751
substitute(function, inst, build.constInt(sum));
752
}
753
break;
754
case IrCmd::SEXTI8_INT:
755
if (OP_A(inst).kind == IrOpKind::Constant)
756
{
757
int32_t value = int8_t(function.intOp(OP_A(inst)));
758
substitute(function, inst, build.constInt(value));
759
}
760
break;
761
case IrCmd::SEXTI16_INT:
762
if (OP_A(inst).kind == IrOpKind::Constant)
763
{
764
int32_t value = int16_t(function.intOp(OP_A(inst)));
765
substitute(function, inst, build.constInt(value));
766
}
767
break;
768
case IrCmd::ADD_NUM:
769
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
770
substitute(function, inst, build.constDouble(function.doubleOp(OP_A(inst)) + function.doubleOp(OP_B(inst))));
771
break;
772
case IrCmd::SUB_NUM:
773
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
774
substitute(function, inst, build.constDouble(function.doubleOp(OP_A(inst)) - function.doubleOp(OP_B(inst))));
775
break;
776
case IrCmd::MUL_NUM:
777
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
778
substitute(function, inst, build.constDouble(function.doubleOp(OP_A(inst)) * function.doubleOp(OP_B(inst))));
779
break;
780
case IrCmd::DIV_NUM:
781
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
782
substitute(function, inst, build.constDouble(function.doubleOp(OP_A(inst)) / function.doubleOp(OP_B(inst))));
783
break;
784
case IrCmd::IDIV_NUM:
785
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
786
substitute(function, inst, build.constDouble(luai_numidiv(function.doubleOp(OP_A(inst)), function.doubleOp(OP_B(inst)))));
787
break;
788
case IrCmd::MOD_NUM:
789
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
790
substitute(function, inst, build.constDouble(luai_nummod(function.doubleOp(OP_A(inst)), function.doubleOp(OP_B(inst)))));
791
break;
792
case IrCmd::MIN_NUM:
793
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
794
{
795
double a1 = function.doubleOp(OP_A(inst));
796
double a2 = function.doubleOp(OP_B(inst));
797
798
substitute(function, inst, build.constDouble(a1 < a2 ? a1 : a2));
799
}
800
break;
801
case IrCmd::MAX_NUM:
802
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
803
{
804
double a1 = function.doubleOp(OP_A(inst));
805
double a2 = function.doubleOp(OP_B(inst));
806
807
substitute(function, inst, build.constDouble(a1 > a2 ? a1 : a2));
808
}
809
break;
810
case IrCmd::UNM_NUM:
811
if (OP_A(inst).kind == IrOpKind::Constant)
812
substitute(function, inst, build.constDouble(-function.doubleOp(OP_A(inst))));
813
break;
814
case IrCmd::FLOOR_NUM:
815
if (OP_A(inst).kind == IrOpKind::Constant)
816
substitute(function, inst, build.constDouble(floor(function.doubleOp(OP_A(inst)))));
817
break;
818
case IrCmd::CEIL_NUM:
819
if (OP_A(inst).kind == IrOpKind::Constant)
820
substitute(function, inst, build.constDouble(ceil(function.doubleOp(OP_A(inst)))));
821
break;
822
case IrCmd::ROUND_NUM:
823
if (OP_A(inst).kind == IrOpKind::Constant)
824
substitute(function, inst, build.constDouble(round(function.doubleOp(OP_A(inst)))));
825
break;
826
case IrCmd::SQRT_NUM:
827
if (OP_A(inst).kind == IrOpKind::Constant)
828
substitute(function, inst, build.constDouble(sqrt(function.doubleOp(OP_A(inst)))));
829
break;
830
case IrCmd::ABS_NUM:
831
if (OP_A(inst).kind == IrOpKind::Constant)
832
substitute(function, inst, build.constDouble(fabs(function.doubleOp(OP_A(inst)))));
833
break;
834
case IrCmd::SIGN_NUM:
835
if (OP_A(inst).kind == IrOpKind::Constant)
836
{
837
double v = function.doubleOp(OP_A(inst));
838
839
substitute(function, inst, build.constDouble(v > 0.0 ? 1.0 : v < 0.0 ? -1.0 : 0.0));
840
}
841
break;
842
case IrCmd::ADD_FLOAT:
843
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
844
substitute(function, inst, build.constDouble(float(function.doubleOp(OP_A(inst))) + float(function.doubleOp(OP_B(inst)))));
845
break;
846
case IrCmd::SUB_FLOAT:
847
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
848
substitute(function, inst, build.constDouble(float(function.doubleOp(OP_A(inst))) - float(function.doubleOp(OP_B(inst)))));
849
break;
850
case IrCmd::MUL_FLOAT:
851
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
852
substitute(function, inst, build.constDouble(float(function.doubleOp(OP_A(inst))) * float(function.doubleOp(OP_B(inst)))));
853
break;
854
case IrCmd::DIV_FLOAT:
855
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
856
substitute(function, inst, build.constDouble(float(function.doubleOp(OP_A(inst))) / float(function.doubleOp(OP_B(inst)))));
857
break;
858
case IrCmd::MIN_FLOAT:
859
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
860
{
861
float a1 = float(function.doubleOp(OP_A(inst)));
862
float a2 = float(function.doubleOp(OP_B(inst)));
863
864
substitute(function, inst, build.constDouble(a1 < a2 ? a1 : a2));
865
}
866
break;
867
case IrCmd::MAX_FLOAT:
868
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
869
{
870
float a1 = float(function.doubleOp(OP_A(inst)));
871
float a2 = float(function.doubleOp(OP_B(inst)));
872
873
substitute(function, inst, build.constDouble(a1 > a2 ? a1 : a2));
874
}
875
break;
876
case IrCmd::UNM_FLOAT:
877
if (OP_A(inst).kind == IrOpKind::Constant)
878
substitute(function, inst, build.constDouble(-float(function.doubleOp(OP_A(inst)))));
879
break;
880
case IrCmd::FLOOR_FLOAT:
881
if (OP_A(inst).kind == IrOpKind::Constant)
882
substitute(function, inst, build.constDouble(floorf(float(function.doubleOp(OP_A(inst))))));
883
break;
884
case IrCmd::CEIL_FLOAT:
885
if (OP_A(inst).kind == IrOpKind::Constant)
886
substitute(function, inst, build.constDouble(ceilf(float(function.doubleOp(OP_A(inst))))));
887
break;
888
case IrCmd::SQRT_FLOAT:
889
if (OP_A(inst).kind == IrOpKind::Constant)
890
substitute(function, inst, build.constDouble(sqrtf(float(function.doubleOp(OP_A(inst))))));
891
break;
892
case IrCmd::ABS_FLOAT:
893
if (OP_A(inst).kind == IrOpKind::Constant)
894
substitute(function, inst, build.constDouble(fabsf(float(function.doubleOp(OP_A(inst))))));
895
break;
896
case IrCmd::SIGN_FLOAT:
897
if (OP_A(inst).kind == IrOpKind::Constant)
898
{
899
float v = float(function.doubleOp(OP_A(inst)));
900
901
substitute(function, inst, build.constDouble(v > 0.0f ? 1.0f : v < 0.0f ? -1.0f : 0.0f));
902
}
903
break;
904
case IrCmd::SELECT_NUM:
905
if (OP_C(inst).kind == IrOpKind::Constant && OP_D(inst).kind == IrOpKind::Constant)
906
{
907
double c = function.doubleOp(OP_C(inst));
908
double d = function.doubleOp(OP_D(inst));
909
910
substitute(function, inst, c == d ? OP_B(inst) : OP_A(inst));
911
}
912
else if (OP_A(inst) == OP_B(inst))
913
{
914
// If the values are the same, no need to worry about the condition check
915
substitute(function, inst, OP_A(inst));
916
}
917
break;
918
case IrCmd::SELECT_VEC:
919
if (OP_A(inst) == OP_B(inst))
920
{
921
// If the values are the same, no need to worry about the condition check
922
substitute(function, inst, OP_A(inst));
923
}
924
break;
925
case IrCmd::SELECT_IF_TRUTHY:
926
if (OP_B(inst) == OP_C(inst))
927
{
928
// If the values are the same, no need to worry about the condition check
929
substitute(function, inst, OP_B(inst));
930
}
931
break;
932
case IrCmd::NOT_ANY:
933
if (OP_A(inst).kind == IrOpKind::Constant)
934
{
935
uint8_t a = function.tagOp(OP_A(inst));
936
937
if (a == LUA_TNIL)
938
substitute(function, inst, build.constInt(1));
939
else if (a != LUA_TBOOLEAN)
940
substitute(function, inst, build.constInt(0));
941
else if (OP_B(inst).kind == IrOpKind::Constant)
942
substitute(function, inst, build.constInt(function.intOp(OP_B(inst)) == 1 ? 0 : 1));
943
}
944
break;
945
case IrCmd::CMP_INT:
946
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
947
{
948
if (compare(function.intOp(OP_A(inst)), function.intOp(OP_B(inst)), conditionOp(OP_C(inst))))
949
substitute(function, inst, build.constInt(1));
950
else
951
substitute(function, inst, build.constInt(0));
952
}
953
break;
954
case IrCmd::CMP_TAG:
955
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
956
{
957
IrCondition cond = conditionOp(OP_C(inst));
958
CODEGEN_ASSERT(cond == IrCondition::Equal || cond == IrCondition::NotEqual);
959
960
if (cond == IrCondition::Equal)
961
substitute(function, inst, build.constInt(function.tagOp(OP_A(inst)) == function.tagOp(OP_B(inst)) ? 1 : 0));
962
else
963
substitute(function, inst, build.constInt(function.tagOp(OP_A(inst)) != function.tagOp(OP_B(inst)) ? 1 : 0));
964
}
965
break;
966
case IrCmd::CMP_SPLIT_TVALUE:
967
{
968
CODEGEN_ASSERT(OP_B(inst).kind == IrOpKind::Constant);
969
970
IrCondition cond = conditionOp(OP_E(inst));
971
CODEGEN_ASSERT(cond == IrCondition::Equal || cond == IrCondition::NotEqual);
972
973
if (cond == IrCondition::Equal)
974
{
975
if (OP_A(inst).kind == IrOpKind::Constant && function.tagOp(OP_A(inst)) != function.tagOp(OP_B(inst)))
976
{
977
substitute(function, inst, build.constInt(0));
978
}
979
else if (OP_C(inst).kind == IrOpKind::Constant && OP_D(inst).kind == IrOpKind::Constant)
980
{
981
// If the tag is a constant, this means previous condition has failed because tags are the same
982
bool knownSameTag = OP_A(inst).kind == IrOpKind::Constant;
983
bool sameValue = false;
984
985
if (function.tagOp(OP_B(inst)) == LUA_TBOOLEAN)
986
sameValue = compare(function.intOp(OP_C(inst)), function.intOp(OP_D(inst)), IrCondition::Equal);
987
else if (function.tagOp(OP_B(inst)) == LUA_TNUMBER)
988
sameValue = compare(function.doubleOp(OP_C(inst)), function.doubleOp(OP_D(inst)), IrCondition::Equal);
989
else
990
CODEGEN_ASSERT(!"unsupported type");
991
992
if (knownSameTag && sameValue)
993
substitute(function, inst, build.constInt(1));
994
else if (sameValue)
995
replace(function, block, index, {IrCmd::CMP_TAG, {OP_A(inst), OP_B(inst), OP_E(inst)}});
996
else
997
substitute(function, inst, build.constInt(0));
998
}
999
}
1000
else
1001
{
1002
if (OP_A(inst).kind == IrOpKind::Constant && function.tagOp(OP_A(inst)) != function.tagOp(OP_B(inst)))
1003
{
1004
substitute(function, inst, build.constInt(1));
1005
}
1006
else if (OP_C(inst).kind == IrOpKind::Constant && OP_D(inst).kind == IrOpKind::Constant)
1007
{
1008
// If the tag is a constant, this means previous condition has failed because tags are the same
1009
bool knownSameTag = OP_A(inst).kind == IrOpKind::Constant;
1010
bool differentValue = false;
1011
1012
if (function.tagOp(OP_B(inst)) == LUA_TBOOLEAN)
1013
differentValue = compare(function.intOp(OP_C(inst)), function.intOp(OP_D(inst)), IrCondition::NotEqual);
1014
else if (function.tagOp(OP_B(inst)) == LUA_TNUMBER)
1015
differentValue = compare(function.doubleOp(OP_C(inst)), function.doubleOp(OP_D(inst)), IrCondition::NotEqual);
1016
else
1017
CODEGEN_ASSERT(!"unsupported type");
1018
1019
if (differentValue)
1020
substitute(function, inst, build.constInt(1));
1021
else if (knownSameTag)
1022
substitute(function, inst, build.constInt(0));
1023
else
1024
replace(function, block, index, {IrCmd::CMP_TAG, {OP_A(inst), OP_B(inst), OP_E(inst)}});
1025
}
1026
}
1027
}
1028
break;
1029
case IrCmd::JUMP_EQ_TAG:
1030
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
1031
{
1032
if (function.tagOp(OP_A(inst)) == function.tagOp(OP_B(inst)))
1033
replace(function, block, index, {IrCmd::JUMP, {OP_C(inst)}});
1034
else
1035
replace(function, block, index, {IrCmd::JUMP, {OP_D(inst)}});
1036
}
1037
break;
1038
case IrCmd::JUMP_CMP_INT:
1039
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
1040
{
1041
if (compare(function.intOp(OP_A(inst)), function.intOp(OP_B(inst)), conditionOp(OP_C(inst))))
1042
replace(function, block, index, {IrCmd::JUMP, {OP_D(inst)}});
1043
else
1044
replace(function, block, index, {IrCmd::JUMP, {OP_E(inst)}});
1045
}
1046
break;
1047
case IrCmd::JUMP_CMP_NUM:
1048
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
1049
{
1050
if (compare(function.doubleOp(OP_A(inst)), function.doubleOp(OP_B(inst)), conditionOp(OP_C(inst))))
1051
replace(function, block, index, {IrCmd::JUMP, {OP_D(inst)}});
1052
else
1053
replace(function, block, index, {IrCmd::JUMP, {OP_E(inst)}});
1054
}
1055
break;
1056
case IrCmd::JUMP_CMP_FLOAT:
1057
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
1058
{
1059
if (compare(float(function.doubleOp(OP_A(inst))), float(function.doubleOp(OP_B(inst))), conditionOp(OP_C(inst))))
1060
replace(function, block, index, {IrCmd::JUMP, {OP_D(inst)}});
1061
else
1062
replace(function, block, index, {IrCmd::JUMP, {OP_E(inst)}});
1063
}
1064
break;
1065
case IrCmd::TRY_NUM_TO_INDEX:
1066
if (OP_A(inst).kind == IrOpKind::Constant)
1067
{
1068
double value = function.doubleOp(OP_A(inst));
1069
1070
// To avoid undefined behavior of casting a value not representable in the target type, we check the range
1071
if (value >= INT_MIN && value <= INT_MAX)
1072
{
1073
int arrIndex = int(value);
1074
1075
if (double(arrIndex) == value)
1076
substitute(function, inst, build.constInt(arrIndex));
1077
else
1078
replace(function, block, index, {IrCmd::JUMP, {OP_B(inst)}});
1079
}
1080
else
1081
{
1082
replace(function, block, index, {IrCmd::JUMP, {OP_B(inst)}});
1083
}
1084
}
1085
break;
1086
case IrCmd::INT_TO_NUM:
1087
if (OP_A(inst).kind == IrOpKind::Constant)
1088
substitute(function, inst, build.constDouble(double(function.intOp(OP_A(inst)))));
1089
break;
1090
case IrCmd::UINT_TO_NUM:
1091
if (OP_A(inst).kind == IrOpKind::Constant)
1092
substitute(function, inst, build.constDouble(double(unsigned(function.intOp(OP_A(inst))))));
1093
break;
1094
case IrCmd::UINT_TO_FLOAT:
1095
if (OP_A(inst).kind == IrOpKind::Constant)
1096
substitute(function, inst, build.constDouble(float(unsigned(function.intOp(OP_A(inst))))));
1097
break;
1098
case IrCmd::NUM_TO_INT:
1099
if (OP_A(inst).kind == IrOpKind::Constant)
1100
{
1101
double value = function.doubleOp(OP_A(inst));
1102
1103
// To avoid undefined behavior of casting a value not representable in the target type, check the range (matches luai_num2int)
1104
if (value >= INT_MIN && value <= INT_MAX)
1105
substitute(function, inst, build.constInt(int(value)));
1106
}
1107
break;
1108
case IrCmd::NUM_TO_UINT:
1109
if (OP_A(inst).kind == IrOpKind::Constant)
1110
{
1111
double value = function.doubleOp(OP_A(inst));
1112
1113
// To avoid undefined behavior of casting a value not representable in the target type, check the range (matches luai_num2unsigned)
1114
if (value >= -kDoubleMaxExactInteger && value <= kDoubleMaxExactInteger)
1115
substitute(function, inst, build.constInt(unsigned((long long)function.doubleOp(OP_A(inst)))));
1116
}
1117
break;
1118
case IrCmd::FLOAT_TO_NUM:
1119
// float -> double for a constant is a no-op
1120
if (OP_A(inst).kind == IrOpKind::Constant)
1121
substitute(function, inst, build.constDouble(function.doubleOp(OP_A(inst))));
1122
break;
1123
case IrCmd::NUM_TO_FLOAT:
1124
// double -> float for a constant just needs to lower precision
1125
if (OP_A(inst).kind == IrOpKind::Constant)
1126
substitute(function, inst, build.constDouble(float(function.doubleOp(OP_A(inst)))));
1127
break;
1128
case IrCmd::TRUNCATE_UINT:
1129
// Truncating a constant integer is a no-op as constant integers only store 32 bits
1130
if (OP_A(inst).kind == IrOpKind::Constant)
1131
substitute(function, inst, OP_A(inst));
1132
break;
1133
case IrCmd::CHECK_TAG:
1134
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
1135
{
1136
if (function.tagOp(OP_A(inst)) == function.tagOp(OP_B(inst)))
1137
kill(function, inst);
1138
else
1139
replace(function, block, index, {IrCmd::JUMP, {OP_C(inst)}}); // Shows a conflict in assumptions on this path
1140
}
1141
break;
1142
case IrCmd::CHECK_TRUTHY:
1143
if (OP_A(inst).kind == IrOpKind::Constant)
1144
{
1145
if (function.tagOp(OP_A(inst)) == LUA_TNIL)
1146
{
1147
replace(function, block, index, {IrCmd::JUMP, {OP_C(inst)}}); // Shows a conflict in assumptions on this path
1148
}
1149
else if (function.tagOp(OP_A(inst)) == LUA_TBOOLEAN)
1150
{
1151
if (OP_B(inst).kind == IrOpKind::Constant)
1152
{
1153
if (function.intOp(OP_B(inst)) == 0)
1154
replace(function, block, index, {IrCmd::JUMP, {OP_C(inst)}}); // Shows a conflict in assumptions on this path
1155
else
1156
kill(function, inst);
1157
}
1158
}
1159
else
1160
{
1161
kill(function, inst);
1162
}
1163
}
1164
break;
1165
case IrCmd::CHECK_CMP_INT:
1166
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
1167
{
1168
if (compare(function.intOp(OP_A(inst)), function.intOp(OP_B(inst)), conditionOp(OP_C(inst))))
1169
kill(function, inst);
1170
else
1171
replace(function, block, index, {IrCmd::JUMP, {OP_D(inst)}}); // Shows a conflict in assumptions on this path
1172
}
1173
break;
1174
case IrCmd::BITAND_UINT:
1175
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
1176
{
1177
unsigned op1 = unsigned(function.intOp(OP_A(inst)));
1178
unsigned op2 = unsigned(function.intOp(OP_B(inst)));
1179
substitute(function, inst, build.constInt(op1 & op2));
1180
}
1181
else
1182
{
1183
if (OP_A(inst).kind == IrOpKind::Constant && function.intOp(OP_A(inst)) == 0) // (0 & b) -> 0
1184
{
1185
substitute(function, inst, build.constInt(0));
1186
}
1187
else if (OP_A(inst).kind == IrOpKind::Constant && function.intOp(OP_A(inst)) == -1) // (-1 & b) -> b
1188
{
1189
if (FFlag::LuauCodegenTruncatedSubsts)
1190
substituteWithTruncatedUint(function, block, inst, OP_B(inst));
1191
else
1192
substitute(function, inst, OP_B(inst));
1193
}
1194
else if (OP_B(inst).kind == IrOpKind::Constant && function.intOp(OP_B(inst)) == 0) // (a & 0) -> 0
1195
{
1196
substitute(function, inst, build.constInt(0));
1197
}
1198
else if (OP_B(inst).kind == IrOpKind::Constant && function.intOp(OP_B(inst)) == -1) // (a & -1) -> a
1199
{
1200
if (FFlag::LuauCodegenTruncatedSubsts)
1201
substituteWithTruncatedUint(function, block, inst, OP_A(inst));
1202
else
1203
substitute(function, inst, OP_A(inst));
1204
}
1205
}
1206
break;
1207
case IrCmd::BITXOR_UINT:
1208
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
1209
{
1210
unsigned op1 = unsigned(function.intOp(OP_A(inst)));
1211
unsigned op2 = unsigned(function.intOp(OP_B(inst)));
1212
substitute(function, inst, build.constInt(op1 ^ op2));
1213
}
1214
else
1215
{
1216
if (OP_A(inst).kind == IrOpKind::Constant && function.intOp(OP_A(inst)) == 0) // (0 ^ b) -> b
1217
{
1218
if (FFlag::LuauCodegenTruncatedSubsts)
1219
substituteWithTruncatedUint(function, block, inst, OP_B(inst));
1220
else
1221
substitute(function, inst, OP_B(inst));
1222
}
1223
else if (OP_A(inst).kind == IrOpKind::Constant && function.intOp(OP_A(inst)) == -1) // (-1 ^ b) -> ~b
1224
{
1225
replace(function, block, index, {IrCmd::BITNOT_UINT, {OP_B(inst)}});
1226
}
1227
else if (OP_B(inst).kind == IrOpKind::Constant && function.intOp(OP_B(inst)) == 0) // (a ^ 0) -> a
1228
{
1229
if (FFlag::LuauCodegenTruncatedSubsts)
1230
substituteWithTruncatedUint(function, block, inst, OP_A(inst));
1231
else
1232
substitute(function, inst, OP_A(inst));
1233
}
1234
else if (OP_B(inst).kind == IrOpKind::Constant && function.intOp(OP_B(inst)) == -1) // (a ^ -1) -> ~a
1235
{
1236
replace(function, block, index, {IrCmd::BITNOT_UINT, {OP_A(inst)}});
1237
}
1238
}
1239
break;
1240
case IrCmd::BITOR_UINT:
1241
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
1242
{
1243
unsigned op1 = unsigned(function.intOp(OP_A(inst)));
1244
unsigned op2 = unsigned(function.intOp(OP_B(inst)));
1245
substitute(function, inst, build.constInt(op1 | op2));
1246
}
1247
else
1248
{
1249
if (OP_A(inst).kind == IrOpKind::Constant && function.intOp(OP_A(inst)) == 0) // (0 | b) -> b
1250
{
1251
if (FFlag::LuauCodegenTruncatedSubsts)
1252
substituteWithTruncatedUint(function, block, inst, OP_B(inst));
1253
else
1254
substitute(function, inst, OP_B(inst));
1255
}
1256
else if (OP_A(inst).kind == IrOpKind::Constant && function.intOp(OP_A(inst)) == -1) // (-1 | b) -> -1
1257
{
1258
substitute(function, inst, build.constInt(-1));
1259
}
1260
else if (OP_B(inst).kind == IrOpKind::Constant && function.intOp(OP_B(inst)) == 0) // (a | 0) -> a
1261
{
1262
if (FFlag::LuauCodegenTruncatedSubsts)
1263
substituteWithTruncatedUint(function, block, inst, OP_A(inst));
1264
else
1265
substitute(function, inst, OP_A(inst));
1266
}
1267
else if (OP_B(inst).kind == IrOpKind::Constant && function.intOp(OP_B(inst)) == -1) // (a | -1) -> -1
1268
{
1269
substitute(function, inst, build.constInt(-1));
1270
}
1271
}
1272
break;
1273
case IrCmd::BITNOT_UINT:
1274
if (OP_A(inst).kind == IrOpKind::Constant)
1275
substitute(function, inst, build.constInt(~unsigned(function.intOp(OP_A(inst)))));
1276
break;
1277
case IrCmd::BITLSHIFT_UINT:
1278
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
1279
{
1280
unsigned op1 = unsigned(function.intOp(OP_A(inst)));
1281
int op2 = function.intOp(OP_B(inst));
1282
1283
substitute(function, inst, build.constInt(op1 << (op2 & 31)));
1284
}
1285
else if (OP_B(inst).kind == IrOpKind::Constant && function.intOp(OP_B(inst)) == 0)
1286
{
1287
if (FFlag::LuauCodegenTruncatedSubsts)
1288
substituteWithTruncatedUint(function, block, inst, OP_A(inst));
1289
else
1290
substitute(function, inst, OP_A(inst));
1291
}
1292
break;
1293
case IrCmd::BITRSHIFT_UINT:
1294
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
1295
{
1296
unsigned op1 = unsigned(function.intOp(OP_A(inst)));
1297
int op2 = function.intOp(OP_B(inst));
1298
1299
substitute(function, inst, build.constInt(op1 >> (op2 & 31)));
1300
}
1301
else if (OP_B(inst).kind == IrOpKind::Constant && function.intOp(OP_B(inst)) == 0)
1302
{
1303
if (FFlag::LuauCodegenTruncatedSubsts)
1304
substituteWithTruncatedUint(function, block, inst, OP_A(inst));
1305
else
1306
substitute(function, inst, OP_A(inst));
1307
}
1308
break;
1309
case IrCmd::BITARSHIFT_UINT:
1310
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
1311
{
1312
int op1 = function.intOp(OP_A(inst));
1313
int op2 = function.intOp(OP_B(inst));
1314
1315
// note: technically right shift of negative values is UB, but this behavior is getting defined in C++20 and all compilers do the
1316
// right (shift) thing.
1317
substitute(function, inst, build.constInt(op1 >> (op2 & 31)));
1318
}
1319
else if (OP_B(inst).kind == IrOpKind::Constant && function.intOp(OP_B(inst)) == 0)
1320
{
1321
if (FFlag::LuauCodegenTruncatedSubsts)
1322
substituteWithTruncatedUint(function, block, inst, OP_A(inst));
1323
else
1324
substitute(function, inst, OP_A(inst));
1325
}
1326
break;
1327
case IrCmd::BITLROTATE_UINT:
1328
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
1329
{
1330
substitute(function, inst, build.constInt(lrotate(unsigned(function.intOp(OP_A(inst))), function.intOp(OP_B(inst)))));
1331
}
1332
else if (OP_B(inst).kind == IrOpKind::Constant && function.intOp(OP_B(inst)) == 0)
1333
{
1334
if (FFlag::LuauCodegenTruncatedSubsts)
1335
substituteWithTruncatedUint(function, block, inst, OP_A(inst));
1336
else
1337
substitute(function, inst, OP_A(inst));
1338
}
1339
break;
1340
case IrCmd::BITRROTATE_UINT:
1341
if (OP_A(inst).kind == IrOpKind::Constant && OP_B(inst).kind == IrOpKind::Constant)
1342
{
1343
substitute(function, inst, build.constInt(rrotate(unsigned(function.intOp(OP_A(inst))), function.intOp(OP_B(inst)))));
1344
}
1345
else if (OP_B(inst).kind == IrOpKind::Constant && function.intOp(OP_B(inst)) == 0)
1346
{
1347
if (FFlag::LuauCodegenTruncatedSubsts)
1348
substituteWithTruncatedUint(function, block, inst, OP_A(inst));
1349
else
1350
substitute(function, inst, OP_A(inst));
1351
}
1352
break;
1353
case IrCmd::BITCOUNTLZ_UINT:
1354
if (OP_A(inst).kind == IrOpKind::Constant)
1355
substitute(function, inst, build.constInt(countlz(unsigned(function.intOp(OP_A(inst))))));
1356
break;
1357
case IrCmd::BITCOUNTRZ_UINT:
1358
if (OP_A(inst).kind == IrOpKind::Constant)
1359
substitute(function, inst, build.constInt(countrz(unsigned(function.intOp(OP_A(inst))))));
1360
break;
1361
case IrCmd::CHECK_BUFFER_LEN:
1362
if (FFlag::LuauCodegenBufferRangeMerge4)
1363
{
1364
if (OP_B(inst).kind == IrOpKind::Constant && OP_E(inst).kind == IrOpKind::Constant)
1365
{
1366
// If base offset and base offset source double value are both constants, we can get rid of that check or fallback
1367
if (double(function.intOp(OP_B(inst))) == function.doubleOp(OP_E(inst)))
1368
replace(function, OP_E(inst), build.undef()); // This disables equality check at runtime
1369
else
1370
replace(function, block, index, {IrCmd::JUMP, {OP_F(inst)}}); // Shows a conflict in assumptions on this path
1371
}
1372
else if (OP_B(inst).kind == IrOpKind::Inst && OP_E(inst).kind == IrOpKind::Constant)
1373
{
1374
// If only the base offset source double value is a constant, it means we couldn't constant-fold NUM_TO_INT
1375
CODEGEN_ASSERT(function.instOp(OP_B(inst)).cmd == IrCmd::NUM_TO_INT && OP_A(function.instOp(OP_B(inst))) == OP_E(inst));
1376
1377
replace(function, block, index, {IrCmd::JUMP, {OP_F(inst)}}); // Shows a conflict in assumptions on this path
1378
}
1379
}
1380
break;
1381
default:
1382
break;
1383
}
1384
}
1385
1386
uint32_t getNativeContextOffset(int bfid)
1387
{
1388
switch (bfid)
1389
{
1390
case LBF_MATH_ACOS:
1391
return offsetof(NativeContext, libm_acos);
1392
case LBF_MATH_ASIN:
1393
return offsetof(NativeContext, libm_asin);
1394
case LBF_MATH_ATAN2:
1395
return offsetof(NativeContext, libm_atan2);
1396
case LBF_MATH_ATAN:
1397
return offsetof(NativeContext, libm_atan);
1398
case LBF_MATH_COSH:
1399
return offsetof(NativeContext, libm_cosh);
1400
case LBF_MATH_COS:
1401
return offsetof(NativeContext, libm_cos);
1402
case LBF_MATH_EXP:
1403
return offsetof(NativeContext, libm_exp);
1404
case LBF_MATH_LOG10:
1405
return offsetof(NativeContext, libm_log10);
1406
case LBF_MATH_LOG:
1407
return offsetof(NativeContext, libm_log);
1408
case LBF_MATH_SINH:
1409
return offsetof(NativeContext, libm_sinh);
1410
case LBF_MATH_SIN:
1411
return offsetof(NativeContext, libm_sin);
1412
case LBF_MATH_TANH:
1413
return offsetof(NativeContext, libm_tanh);
1414
case LBF_MATH_TAN:
1415
return offsetof(NativeContext, libm_tan);
1416
case LBF_MATH_FMOD:
1417
return offsetof(NativeContext, libm_fmod);
1418
case LBF_MATH_POW:
1419
return offsetof(NativeContext, libm_pow);
1420
case LBF_IR_MATH_LOG2:
1421
return offsetof(NativeContext, libm_log2);
1422
case LBF_MATH_LDEXP:
1423
return offsetof(NativeContext, libm_ldexp);
1424
default:
1425
CODEGEN_ASSERT(!"Unsupported bfid");
1426
}
1427
1428
return 0;
1429
}
1430
1431
void killUnusedBlocks(IrFunction& function)
1432
{
1433
// Start from 1 as the first block is the entry block
1434
for (unsigned i = 1; i < function.blocks.size(); i++)
1435
{
1436
IrBlock& block = function.blocks[i];
1437
1438
if (block.kind != IrBlockKind::Dead && block.useCount == 0)
1439
kill(function, block);
1440
}
1441
}
1442
1443
std::vector<uint32_t> getSortedBlockOrder(IrFunction& function)
1444
{
1445
std::vector<uint32_t> sortedBlocks;
1446
sortedBlocks.reserve(function.blocks.size());
1447
for (uint32_t i = 0; i < function.blocks.size(); i++)
1448
sortedBlocks.push_back(i);
1449
1450
std::sort(
1451
sortedBlocks.begin(),
1452
sortedBlocks.end(),
1453
[&](uint32_t idxA, uint32_t idxB)
1454
{
1455
const IrBlock& a = function.blocks[idxA];
1456
const IrBlock& b = function.blocks[idxB];
1457
1458
// Place fallback blocks at the end
1459
if ((a.kind == IrBlockKind::Fallback) != (b.kind == IrBlockKind::Fallback))
1460
return (a.kind == IrBlockKind::Fallback) < (b.kind == IrBlockKind::Fallback);
1461
1462
// Try to order by instruction order
1463
if (a.sortkey != b.sortkey)
1464
return a.sortkey < b.sortkey;
1465
1466
// Chains of blocks are merged together by having the same sort key and consecutive chain key
1467
return a.chainkey < b.chainkey;
1468
}
1469
);
1470
1471
return sortedBlocks;
1472
}
1473
1474
IrBlock& getNextBlock(IrFunction& function, const std::vector<uint32_t>& sortedBlocks, IrBlock& dummy, size_t i)
1475
{
1476
for (size_t j = i + 1; j < sortedBlocks.size(); ++j)
1477
{
1478
IrBlock& block = function.blocks[sortedBlocks[j]];
1479
if (block.kind != IrBlockKind::Dead)
1480
return block;
1481
}
1482
1483
return dummy;
1484
}
1485
1486
IrBlock* tryGetNextBlockInChain(IrFunction& function, IrBlock& block)
1487
{
1488
IrInst& termInst = function.instructions[block.finish];
1489
1490
// Follow the strict block chain
1491
if (termInst.cmd == IrCmd::JUMP && OP_A(termInst).kind == IrOpKind::Block)
1492
{
1493
IrBlock& target = function.blockOp(OP_A(termInst));
1494
1495
// Has to have the same sorting key and a consecutive chain key
1496
if (target.sortkey == block.sortkey && target.chainkey == block.chainkey + 1)
1497
return &target;
1498
}
1499
1500
return nullptr;
1501
}
1502
1503
bool isEntryBlock(const IrBlock& block)
1504
{
1505
return block.useCount == 0 && block.kind != IrBlockKind::Dead;
1506
}
1507
1508
std::optional<uint8_t> tryGetOperandTag(IrFunction& function, IrOp op)
1509
{
1510
if (IrInst* arg = function.asInstOp(op))
1511
{
1512
if (arg->cmd == IrCmd::TAG_VECTOR)
1513
return LUA_TVECTOR;
1514
1515
if (arg->cmd == IrCmd::LOAD_TVALUE && HAS_OP_C(*arg))
1516
return function.tagOp(OP_C(*arg));
1517
}
1518
1519
return std::nullopt;
1520
}
1521
1522
void propagateTagsFromPredecessors(
1523
const IrFunction& function,
1524
const IrBlock& block,
1525
std::function<uint8_t(size_t)> getTag,
1526
std::function<void(size_t, uint8_t)> setTag
1527
)
1528
{
1529
CODEGEN_ASSERT(FFlag::LuauCodegenPropagateTagsAcrossChains2);
1530
1531
uint32_t blockIdx = function.getBlockIndex(block);
1532
1533
if (blockIdx >= function.cfg.predecessorsOffsets.size())
1534
return;
1535
1536
BlockIteratorWrapper preds = predecessors(function.cfg, blockIdx);
1537
1538
if (preds.empty())
1539
return;
1540
1541
size_t minRegsKnown = std::numeric_limits<size_t>::max();
1542
1543
const size_t numBlockExitTags = function.blockExitTags.size();
1544
1545
for (uint32_t predIdx : preds)
1546
{
1547
if (predIdx >= numBlockExitTags)
1548
return;
1549
1550
minRegsKnown = std::min(minRegsKnown, function.blockExitTags[predIdx].size());
1551
}
1552
1553
const RegisterSet& in = function.cfg.in[blockIdx];
1554
1555
bool firstPredecessor = true;
1556
1557
for (uint32_t predIdx : preds)
1558
{
1559
const std::vector<uint8_t>& predTags = function.blockExitTags[predIdx];
1560
1561
CODEGEN_ASSERT(minRegsKnown <= predTags.size());
1562
1563
for (size_t i = 0; i < minRegsKnown; ++i)
1564
{
1565
// Only registers that are live in can receive information from the predecessors
1566
if (in.regs.test(i) || (in.varargSeq && i >= in.varargStart))
1567
{
1568
uint8_t currentTag = getTag(i);
1569
1570
if (firstPredecessor)
1571
setTag(i, predTags[i]);
1572
else if (currentTag != kUnknownTag && currentTag != predTags[i])
1573
setTag(i, kUnknownTag);
1574
}
1575
}
1576
1577
firstPredecessor = false;
1578
}
1579
}
1580
1581
std::optional<uint8_t> tryGetLuauTagForBcType(uint8_t bcType, bool ignoreOptionalPart)
1582
{
1583
if (ignoreOptionalPart)
1584
bcType = bcType & ~LBC_TYPE_OPTIONAL_BIT;
1585
1586
switch (bcType)
1587
{
1588
case LBC_TYPE_NIL:
1589
return LUA_TNIL;
1590
case LBC_TYPE_BOOLEAN:
1591
return LUA_TBOOLEAN;
1592
case LBC_TYPE_NUMBER:
1593
return LUA_TNUMBER;
1594
case LBC_TYPE_INTEGER:
1595
return LUA_TINTEGER;
1596
case LBC_TYPE_STRING:
1597
return LUA_TSTRING;
1598
case LBC_TYPE_TABLE:
1599
return LUA_TTABLE;
1600
case LBC_TYPE_FUNCTION:
1601
return LUA_TFUNCTION;
1602
case LBC_TYPE_THREAD:
1603
return LUA_TTHREAD;
1604
case LBC_TYPE_USERDATA:
1605
return LUA_TUSERDATA;
1606
case LBC_TYPE_VECTOR:
1607
return LUA_TVECTOR;
1608
case LBC_TYPE_BUFFER:
1609
return LUA_TBUFFER;
1610
default:
1611
if (bcType >= LBC_TYPE_TAGGED_USERDATA_BASE && bcType < LBC_TYPE_TAGGED_USERDATA_END)
1612
return LUA_TUSERDATA;
1613
1614
break;
1615
}
1616
1617
return std::nullopt;
1618
}
1619
1620
} // namespace CodeGen
1621
} // namespace Luau
1622
1623