Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/CodeGen/include/Luau/IrData.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/DenseHash.h"
6
#include "Luau/IrAnalysis.h"
7
#include "Luau/Label.h"
8
#include "Luau/RegisterX64.h"
9
#include "Luau/RegisterA64.h"
10
#include "Luau/SmallVector.h"
11
12
#include <optional>
13
#include <vector>
14
15
#include <stdint.h>
16
#include <string.h>
17
18
#define OP_A(inst) getOp(inst, 0)
19
#define OP_B(inst) getOp(inst, 1)
20
#define OP_C(inst) getOp(inst, 2)
21
#define OP_D(inst) getOp(inst, 3)
22
#define OP_E(inst) getOp(inst, 4)
23
#define OP_F(inst) getOp(inst, 5)
24
#define OP_G(inst) getOp(inst, 6)
25
26
struct Proto;
27
28
namespace Luau
29
{
30
namespace CodeGen
31
{
32
33
struct LoweringStats;
34
35
constexpr uint8_t kUnknownTag = 0xff;
36
37
// IR extensions to LuauBuiltinFunction enum (these only exist inside IR, and start from 256 to avoid collisions)
38
enum
39
{
40
LBF_IR_MATH_LOG2 = 256,
41
};
42
43
// IR instruction command.
44
// In the command description, following abbreviations are used:
45
// * Rn - VM stack register slot, n in 0..254
46
// * Kn - VM proto constant slot, n in 0..2^23-1
47
// * UPn - VM function upvalue slot, n in 0..199
48
// * A, B, C, D, E, F, G are instruction arguments
49
enum class IrCmd : uint8_t
50
{
51
NOP,
52
53
// Load a tag from TValue
54
// A: Rn or Kn
55
LOAD_TAG,
56
57
// Load a pointer (*) from TValue
58
// A: Rn or Kn
59
LOAD_POINTER,
60
61
// Load a double number from TValue
62
// A: Rn or Kn
63
LOAD_DOUBLE,
64
65
// Load an int from TValue
66
// A: Rn
67
LOAD_INT,
68
69
// Load a float field from vector (use FLOAT_TO_NUM to convert to double)
70
// A: Rn or Kn
71
// B: int (offset from the start of TValue)
72
LOAD_FLOAT,
73
74
// Load a TValue from memory
75
// A: Rn or Kn or pointer (TValue)
76
// B: int/none (optional 'A' pointer offset)
77
// C: tag/none (tag of the value being loaded)
78
LOAD_TVALUE,
79
80
// Load current environment table
81
LOAD_ENV,
82
83
// Get pointer (TValue) to table array at index
84
// A: pointer (LuaTable)
85
// B: int
86
GET_ARR_ADDR,
87
88
// Get pointer (LuaNode) to table node element at the active cached slot index
89
// A: pointer (LuaTable)
90
// B: unsigned int (pcpos)
91
// C: Kn
92
GET_SLOT_NODE_ADDR,
93
94
// Get pointer (LuaNode) to table node element at the main position of the specified key hash
95
// A: pointer (LuaTable)
96
// B: unsigned int (hash)
97
GET_HASH_NODE_ADDR,
98
99
// Get pointer (TValue) to Closure upvalue.
100
// A: pointer or undef (Closure)
101
// B: UPn
102
// When undef is specified, uses current function Closure.
103
GET_CLOSURE_UPVAL_ADDR,
104
105
// Store a tag into TValue
106
// A: Rn
107
// B: tag
108
STORE_TAG,
109
110
// Store an integer into the extra field of the TValue
111
// A: Rn
112
// B: int
113
STORE_EXTRA,
114
115
// Store a pointer (*) into TValue
116
// A: Rn
117
// B: pointer
118
STORE_POINTER,
119
120
// Store a double number into TValue
121
// A: Rn
122
// B: double
123
STORE_DOUBLE,
124
125
// Store an int into TValue
126
// A: Rn
127
// B: int
128
STORE_INT,
129
130
// Store a vector into TValue
131
// When optional 'E' tag is present, it is written out to the TValue as well
132
// A: Rn
133
// B: float (x)
134
// C: float (y)
135
// D: float (z)
136
// E: tag (optional)
137
STORE_VECTOR,
138
139
// Store a TValue into memory
140
// A: Rn or pointer (TValue)
141
// B: TValue
142
// C: int (optional 'A' pointer offset)
143
STORE_TVALUE,
144
145
// Store a pair of tag and value into memory
146
// A: Rn or pointer (TValue)
147
// B: tag (must be a constant)
148
// C: int/double/pointer
149
// D: int (optional 'A' pointer offset)
150
STORE_SPLIT_TVALUE,
151
152
// Add/Sub two integers together
153
// A, B: int
154
ADD_INT,
155
SUB_INT,
156
157
// Sign extend an 8-bit value
158
// A: int
159
SEXTI8_INT,
160
161
// Sign extend a 16-bit value
162
// A: int
163
SEXTI16_INT,
164
165
// Add/Sub/Mul/Div/Idiv/Mod two double numbers
166
// A, B: double
167
// In final x64 lowering, B can also be Rn or Kn
168
ADD_NUM,
169
SUB_NUM,
170
MUL_NUM,
171
DIV_NUM,
172
IDIV_NUM,
173
MOD_NUM,
174
// A * B + C
175
// A, B, C: double
176
MULADD_NUM,
177
178
// Get the minimum/maximum of two numbers
179
// If one of the values is NaN, 'B' is returned as the result
180
// A, B: double
181
// In final x64 lowering, B can also be Rn or Kn
182
MIN_NUM,
183
MAX_NUM,
184
185
// Negate a double number
186
// A: double
187
UNM_NUM,
188
189
// Round number to negative infinity (math.floor)
190
// A: double
191
FLOOR_NUM,
192
193
// Round number to positive infinity (math.ceil)
194
// A: double
195
CEIL_NUM,
196
197
// Round number to nearest integer number, rounding half-way cases away from zero (math.round)
198
// A: double
199
ROUND_NUM,
200
201
// Get square root of the argument (math.sqrt)
202
// A: double
203
SQRT_NUM,
204
205
// Get absolute value of the argument (math.abs)
206
// A: double
207
ABS_NUM,
208
209
// Get the sign of the argument (math.sign)
210
// A: double
211
SIGN_NUM,
212
213
// Add/Sub/Mul/Div/Idiv/Mod two float numbers
214
// A, B: float
215
// In final x64 lowering, B can also be Rn or Kn
216
ADD_FLOAT,
217
SUB_FLOAT,
218
MUL_FLOAT,
219
DIV_FLOAT,
220
221
// Get the minimum/maximum of two numbers
222
// If one of the values is NaN, 'B' is returned as the result
223
// A, B: float
224
MIN_FLOAT,
225
MAX_FLOAT,
226
227
// Negate a float number
228
// A: float
229
UNM_FLOAT,
230
231
// Round number to negative infinity
232
// A: float
233
FLOOR_FLOAT,
234
235
// Round number to positive infinity
236
// A: float
237
CEIL_FLOAT,
238
239
// Get square root of the argument
240
// A: float
241
SQRT_FLOAT,
242
243
// Get absolute value of the argument
244
// A: float
245
ABS_FLOAT,
246
247
// Get the sign of the argument
248
// A: float
249
SIGN_FLOAT,
250
251
// Select B if C == D, otherwise select A
252
// A, B: double (endpoints)
253
// C, D: double (condition arguments)
254
SELECT_NUM,
255
256
// For each lane in the vector, select B if C == D, otherwise select A
257
// A, B: TValue (endpoints)
258
// C, D: TValue (condition arguments)
259
SELECT_VEC,
260
261
// Select one of the TValues based on the truthyness of A
262
// A: TValue
263
// B: TValue (if true)
264
// C: TValue (if false)
265
SELECT_IF_TRUTHY,
266
267
// Add/Sub/Mul/Div/Idiv two vectors
268
// A, B: TValue (vector)
269
ADD_VEC,
270
SUB_VEC,
271
MUL_VEC,
272
DIV_VEC,
273
IDIV_VEC,
274
// Lanewise A * B + C
275
// A, B, C: TValue (vector)
276
MULADD_VEC,
277
278
// Negate a vector
279
// A: TValue (vector)
280
UNM_VEC,
281
282
// Get the minimum/maximum of two vector elements
283
// If one of the element values is NaN, 'B' is returned as the result
284
// A, B: TValue (vector)
285
MIN_VEC,
286
MAX_VEC,
287
288
// Round vector elements to negative infinity
289
// A: TValue (vector)
290
FLOOR_VEC,
291
292
// Round vector elements to positive infinity
293
// A: TValue (vector)
294
CEIL_VEC,
295
296
// Get absolute value of vector elements
297
// A: TValue (vector)
298
ABS_VEC,
299
300
// Compute dot product between two vectors as a float number (use FLOAT_TO_NUM to convert to double)
301
// A, B: TValue (vector)
302
DOT_VEC,
303
304
// Extract a component of a vector (use FLOAT_TO_NUM to convert to double)
305
// A: TValue (vector)
306
// B: int (0-3 index)
307
EXTRACT_VEC,
308
309
// Compute Luau 'not' operation on destructured TValue
310
// A: tag
311
// B: int (value)
312
NOT_ANY,
313
314
// Perform a TValue comparison, supported conditions are LessEqual, Less and Equal
315
// A, B: Rn
316
// C: condition
317
CMP_ANY,
318
319
// Perform a comparison of two integer numbers. Result is an integer register containing 0 or 1
320
// A, B: int
321
// C: condition
322
CMP_INT,
323
324
// Perform a comparison of two tags. Result is an integer register containing 0 or 1
325
CMP_TAG,
326
// A, B: tag
327
// C: condition (eq/not_eq)
328
329
// Perform tag and value comparison. Result is an integer register containing 0 or 1
330
CMP_SPLIT_TVALUE,
331
// A: tag
332
// B: tag (constant: boolean/number/string)
333
// C, D: value
334
// E: condition (eq/not_eq)
335
336
// Unconditional jump
337
// A: block/vmexit/undef
338
JUMP,
339
340
// Jump if TValue is truthy
341
// A: Rn
342
// B: block (if true)
343
// C: block (if false)
344
JUMP_IF_TRUTHY,
345
346
// Jump if TValue is falsy
347
// A: Rn
348
// B: block (if true)
349
// C: block (if false)
350
JUMP_IF_FALSY,
351
352
// Jump if tags are equal
353
// A, B: tag
354
// C: block (if true)
355
// D: block (if false)
356
JUMP_EQ_TAG,
357
358
// Perform a conditional jump based on the result of integer comparison
359
// A, B: int
360
// C: condition
361
// D: block (if true)
362
// E: block (if false)
363
JUMP_CMP_INT,
364
365
// Jump if pointers are equal
366
// A, B: pointer (*)
367
// C: block (if true)
368
// D: block (if false)
369
JUMP_EQ_POINTER,
370
371
// Perform a conditional jump based on the result of double comparison
372
// A, B: double
373
// C: condition
374
// D: block (if true)
375
// E: block (if false)
376
JUMP_CMP_NUM,
377
378
// Perform a conditional jump based on the result of float comparison
379
// A, B: float
380
// C: condition
381
// D: block (if true)
382
// E: block (if false)
383
JUMP_CMP_FLOAT,
384
385
// Perform jump based on a numerical loop condition (step > 0 ? idx <= limit : limit <= idx)
386
// A: double (index)
387
// B: double (limit)
388
// C: double (step)
389
// D: block (if true)
390
// E: block (if false)
391
JUMP_FORN_LOOP_COND,
392
393
// Perform a conditional jump based on cached table node slot matching the actual table node slot for a key
394
// A: pointer (LuaNode)
395
// B: Kn
396
// C: block (if matches)
397
// D: block (if it doesn't)
398
JUMP_SLOT_MATCH,
399
400
// Get table length
401
// A: pointer (LuaTable)
402
TABLE_LEN,
403
404
// Get string length
405
// A: pointer (string)
406
STRING_LEN,
407
408
// Allocate new table
409
// A: unsigned int (array element count)
410
// B: unsigned int (node element count)
411
NEW_TABLE,
412
413
// Duplicate a table
414
// A: pointer (LuaTable)
415
DUP_TABLE,
416
417
// Insert an integer key into a table and return the pointer to inserted value (TValue)
418
// A: pointer (LuaTable)
419
// B: int (key)
420
TABLE_SETNUM,
421
422
// Try to convert a double number into a table index (int) or jump if it's not an integer
423
// A: double
424
// B: block
425
TRY_NUM_TO_INDEX,
426
427
// Try to get pointer to tag method TValue inside the table's metatable or jump if there is no such value or metatable
428
// A: table
429
// B: int (TMS enum)
430
// C: block
431
TRY_CALL_FASTGETTM,
432
433
// Create new tagged userdata
434
// A: int (size)
435
// B: int (tag)
436
NEW_USERDATA,
437
438
// Convert integer into a double number
439
// A: int
440
INT_TO_NUM,
441
442
// Convert unsigned integer into a double number
443
// A: uint
444
UINT_TO_NUM,
445
446
// Convert unsigned integer into a float number
447
// A: uint
448
UINT_TO_FLOAT,
449
450
// Converts a double number to an integer. 'A' may be any representable integer in a double.
451
// A: double
452
NUM_TO_INT,
453
454
// Converts a double number to an unsigned integer. For out-of-range values of 'A', the result is arch-specific.
455
// A: double
456
NUM_TO_UINT,
457
458
// Converts a float number to a double
459
// A: float
460
FLOAT_TO_NUM,
461
462
// Converts a double number to a float
463
// A: double
464
NUM_TO_FLOAT,
465
466
// Converts a float number to a vector with the value in X/Y/Z (use NUM_TO_FLOAT to convert from double)
467
// A: float
468
FLOAT_TO_VEC,
469
470
// Adds VECTOR type tag to a vector, preserving X/Y/Z components
471
// A: TValue
472
TAG_VECTOR,
473
474
// Clear high register bits of an unsigned integer register. Used to sanitize value of 'producesDirtyHighRegisterBits' instructions.
475
// A: uint
476
TRUNCATE_UINT,
477
478
// Adjust stack top (L->top) to point at 'B' TValues *after* the specified register
479
// This is used to return multiple values
480
// A: Rn
481
// B: int (offset)
482
ADJUST_STACK_TO_REG,
483
484
// Restore stack top (L->top) to point to the function stack top (L->ci->top)
485
// This is used to recover after calling a variadic function
486
ADJUST_STACK_TO_TOP,
487
488
// Execute fastcall builtin function with 1 argument in-place
489
// This is used for a few builtins that can have more than 1 result and cannot be represented as a regular instruction
490
// A: unsigned int (builtin id)
491
// B: Rn (result start)
492
// C: Rn (first argument)
493
// D: int (result count)
494
FASTCALL,
495
496
// Call the fastcall builtin function
497
// A: unsigned int (builtin id)
498
// B: Rn (result start)
499
// C: Rn (argument start)
500
// D: Rn or Kn or undef (optional second argument)
501
// E: Rn or Kn or undef (optional third argument)
502
// F: int (argument count or -1 to use all arguments up to stack top)
503
// G: int (result count or -1 to preserve all results and adjust stack top)
504
INVOKE_FASTCALL,
505
506
// Check that fastcall builtin function invocation was successful (negative result count jumps to fallback)
507
// A: int (result count)
508
// B: block (fallback)
509
CHECK_FASTCALL_RES,
510
511
// Fallback functions
512
513
// Perform an arithmetic operation on TValues of any type
514
// A: Rn (where to store the result)
515
// B: Rn (lhs)
516
// C: Rn or Kn (rhs)
517
// D: int (TMS enum with arithmetic type)
518
DO_ARITH,
519
520
// Get length of a TValue of any type
521
// A: Rn (where to store the result)
522
// B: Rn
523
DO_LEN,
524
525
// Lookup a value in TValue of any type using a key of any type
526
// A: Rn (where to store the result)
527
// B: Rn
528
// C: Rn or unsigned int (key)
529
GET_TABLE,
530
531
// Store a value into TValue of any type using a key of any type
532
// A: Rn (value to store)
533
// B: Rn
534
// C: Rn or unsigned int (key)
535
SET_TABLE,
536
537
// Store an import from constant or the import path
538
// A: Rn (where to store the result)
539
// B: Kn
540
// C: unsigned int (import path)
541
// D: unsigned int (pcpos)
542
GET_CACHED_IMPORT,
543
544
// Concatenate multiple TValues into a string
545
// A: Rn (value start)
546
// B: unsigned int (number of registers to go over)
547
// Note: result is stored in the register specified in 'A'
548
// Note: all referenced registers might be modified in the operation
549
CONCAT,
550
551
// Load function upvalue
552
// A: UPn
553
GET_UPVALUE,
554
555
// Store TValue into a function upvalue
556
// A: UPn
557
// B: TValue
558
// C: tag/undef (tag of the value that was written)
559
SET_UPVALUE,
560
561
// Guards and checks (these instructions are not block terminators even though they jump to fallback)
562
563
// Guard against tag mismatch
564
// A, B: tag
565
// C: block/vmexit/undef
566
// In final x64 lowering, A can also be Rn
567
// When DebugLuauAbortingChecks flag is enabled, A can also be Rn
568
// When undef is specified instead of a block, execution is aborted on check failure
569
CHECK_TAG,
570
571
// Guard against a falsy tag+value
572
// A: tag
573
// B: value
574
// C: block/vmexit/undef
575
CHECK_TRUTHY,
576
577
// Guard against readonly table
578
// A: pointer (LuaTable)
579
// B: block/vmexit/undef
580
// When undef is specified instead of a block, execution is aborted on check failure
581
CHECK_READONLY,
582
583
// Guard against table having a metatable
584
// A: pointer (LuaTable)
585
// B: block/vmexit/undef
586
// When undef is specified instead of a block, execution is aborted on check failure
587
CHECK_NO_METATABLE,
588
589
// Guard against executing in unsafe environment, exits to VM on check failure
590
// A: block/vmexit/undef
591
// When undef is specified, execution is aborted on check failure
592
CHECK_SAFE_ENV,
593
594
// Guard against index overflowing the table array size
595
// A: pointer (LuaTable)
596
// B: int (index)
597
// C: block/vmexit/undef
598
// When undef is specified instead of a block, execution is aborted on check failure
599
CHECK_ARRAY_SIZE,
600
601
// Guard against cached table node slot not matching the actual table node slot for a key
602
// A: pointer (LuaNode)
603
// B: Kn
604
// C: block/undef
605
// When undef is specified instead of a block, execution is aborted on check failure
606
CHECK_SLOT_MATCH,
607
608
// Guard against table node with a linked next node to ensure that our lookup hits the main position of the key
609
// A: pointer (LuaNode)
610
// B: block/vmexit/undef
611
// When undef is specified instead of a block, execution is aborted on check failure
612
CHECK_NODE_NO_NEXT,
613
614
// Guard against table node with 'nil' value
615
// A: pointer (LuaNode)
616
// B: block/vmexit/undef
617
// When undef is specified instead of a block, execution is aborted on check failure
618
CHECK_NODE_VALUE,
619
620
// Guard against access at specified offset with [min, max) range of bytes overflowing the buffer length
621
// When base offset source number is provided, instruction will additionally validate that the integer and double versions of base are exact
622
// A: pointer (buffer)
623
// B: int (base offset)
624
// C: int (access range min inclusive)
625
// D: int (access range max exclusive)
626
// E: double/undef (base offset source double)
627
// F: block/vmexit/undef
628
// When undef is specified instead of a block, execution is aborted on check failure
629
CHECK_BUFFER_LEN,
630
631
// Guard against userdata tag mismatch
632
// A: pointer (userdata)
633
// B: int (tag)
634
// C: block/vmexit/undef
635
// When undef is specified instead of a block, execution is aborted on check failure
636
CHECK_USERDATA_TAG,
637
638
// Guard against the result of integer comparison being false
639
// A, B: int
640
// C: condition
641
// D: block/vmexit/undef
642
// When undef is specified instead of a block, execution is aborted on check failure
643
CHECK_CMP_INT,
644
645
// Special operations
646
647
// Check interrupt handler
648
// A: unsigned int (pcpos)
649
INTERRUPT,
650
651
// Check and run GC assist if necessary
652
CHECK_GC,
653
654
// Handle GC write barrier (forward)
655
// A: pointer (GCObject)
656
// B: Rn (TValue that was written to the object)
657
// C: tag/undef (tag of the value that was written)
658
BARRIER_OBJ,
659
660
// Handle GC write barrier (backwards) for a write into a table
661
// A: pointer (LuaTable)
662
BARRIER_TABLE_BACK,
663
664
// Handle GC write barrier (forward) for a write into a table
665
// A: pointer (LuaTable)
666
// B: Rn (TValue that was written to the object)
667
// C: tag/undef (tag of the value that was written)
668
BARRIER_TABLE_FORWARD,
669
670
// Update savedpc value
671
// A: unsigned int (pcpos)
672
SET_SAVEDPC,
673
674
// Close open upvalues for registers at specified index or higher
675
// A: Rn (starting register index)
676
CLOSE_UPVALS,
677
678
// While capture is a no-op right now, it might be useful to track register/upvalue lifetimes
679
// A: Rn or UPn
680
// B: unsigned int (1 for reference capture, 0 for value capture)
681
CAPTURE,
682
683
// Operations that don't have an IR representation yet
684
685
// Set a list of values to table in target register
686
// A: unsigned int (bytecode instruction index)
687
// B: Rn (target)
688
// C: Rn (source start)
689
// D: int (count or -1 to assign values up to stack top)
690
// E: unsigned int (table index to start from)
691
// F: undef/unsigned int (target table known size)
692
SETLIST,
693
694
// Call specified function
695
// A: Rn (function, followed by arguments)
696
// B: int (argument count or -1 to use all arguments up to stack top)
697
// C: int (result count or -1 to preserve all results and adjust stack top)
698
// Note: return values are placed starting from Rn specified in 'A'
699
CALL,
700
701
// Return specified values from the function
702
// A: Rn (value start)
703
// B: int (result count or -1 to return all values up to stack top)
704
RETURN,
705
706
// Adjust loop variables for one iteration of a generic for loop, jump back to the loop header if loop needs to continue
707
// A: Rn (loop variable start, updates Rn+2 and 'B' number of registers starting from Rn+3)
708
// B: int (loop variable count, if more than 2, registers starting from Rn+5 are set to nil)
709
// C: block (repeat)
710
// D: block (exit)
711
FORGLOOP,
712
713
// Handle LOP_FORGLOOP fallback when variable being iterated is not a table
714
// A: Rn (loop state start, updates Rn+2 and 'B' number of registers starting from Rn+3)
715
// B: int (loop variable count and a MSB set when it's an ipairs-like iteration loop)
716
// C: block (repeat)
717
// D: block (exit)
718
FORGLOOP_FALLBACK,
719
720
// Fallback for generic for loop preparation when iterating over builtin pairs/ipairs
721
// It raises an error if 'B' register is not a function
722
// A: unsigned int (bytecode instruction index)
723
// B: Rn
724
// C: block (forgloop location)
725
FORGPREP_XNEXT_FALLBACK,
726
727
// Increment coverage data (saturating 24 bit add)
728
// A: unsigned int (bytecode instruction index)
729
COVERAGE,
730
731
// Operations that have a translation, but use a full instruction fallback
732
733
// Load a value from global table at specified key
734
// A: unsigned int (bytecode instruction index)
735
// B: Rn (dest)
736
// C: Kn (key)
737
FALLBACK_GETGLOBAL,
738
739
// Store a value into global table at specified key
740
// A: unsigned int (bytecode instruction index)
741
// B: Rn (value)
742
// C: Kn (key)
743
FALLBACK_SETGLOBAL,
744
745
// Load a value from table at specified key
746
// A: unsigned int (bytecode instruction index)
747
// B: Rn (dest)
748
// C: Rn (table)
749
// D: Kn (key)
750
FALLBACK_GETTABLEKS,
751
752
// Store a value into a table at specified key
753
// A: unsigned int (bytecode instruction index)
754
// B: Rn (value)
755
// C: Rn (table)
756
// D: Kn (key)
757
FALLBACK_SETTABLEKS,
758
759
// Load function from source register using name into target register and copying source register into target register + 1
760
// A: unsigned int (bytecode instruction index)
761
// B: Rn (target)
762
// C: Rn (source)
763
// D: Kn (name)
764
FALLBACK_NAMECALL,
765
766
// Operations that don't have assembly lowering at all
767
768
// Prepare stack for variadic functions so that GETVARARGS works correctly
769
// A: unsigned int (bytecode instruction index)
770
// B: int (numparams)
771
FALLBACK_PREPVARARGS,
772
773
// Copy variables into the target registers from vararg storage for current function
774
// A: unsigned int (bytecode instruction index)
775
// B: Rn (dest start)
776
// C: int (count)
777
FALLBACK_GETVARARGS,
778
779
// Create closure from a child proto
780
// A: unsigned int (nups)
781
// B: pointer (table)
782
// C: unsigned int (protoid)
783
NEWCLOSURE,
784
785
// Create closure from a pre-created function object (reusing it unless environments diverge)
786
// A: unsigned int (bytecode instruction index)
787
// B: Rn (dest)
788
// C: Kn (prototype)
789
FALLBACK_DUPCLOSURE,
790
791
// Prepare loop variables for a generic for loop, jump to the loop back edge unconditionally
792
// A: unsigned int (bytecode instruction index)
793
// B: Rn (loop state start, updates Rn Rn+1 Rn+2)
794
// C: block
795
FALLBACK_FORGPREP,
796
797
// Instruction that passes value through, it is produced by constant folding and users substitute it with the value
798
// A: operand of any type
799
SUBSTITUTE,
800
801
// Pseudo instruction to mark VM registers as implicitly used at the location
802
// A: Rn (start)
803
// B: int (count, -1 to mark all registers after start)
804
MARK_USED,
805
806
// Pseudo instruction to mark VM registers as dead at the location
807
// A: Rn (start)
808
// B: int (count, -1 to mark all registers after start)
809
MARK_DEAD,
810
811
// Performs bitwise and/xor/or on two unsigned integers
812
// A, B: int
813
BITAND_UINT,
814
BITXOR_UINT,
815
BITOR_UINT,
816
817
// Performs bitwise not on an unsigned integer
818
// A: int
819
BITNOT_UINT,
820
821
// Performs bitwise shift/rotate on an unsigned integer
822
// A: int (source)
823
// B: int (shift amount)
824
BITLSHIFT_UINT,
825
BITRSHIFT_UINT,
826
BITARSHIFT_UINT,
827
BITLROTATE_UINT,
828
BITRROTATE_UINT,
829
830
// Returns the number of consecutive zero bits in A starting from the left-most (most significant) bit.
831
// A: int
832
BITCOUNTLZ_UINT,
833
BITCOUNTRZ_UINT,
834
835
// Swap byte order in A
836
// A: int
837
BYTESWAP_UINT,
838
839
// Calls native libm function with 1 or 2 arguments
840
// A: builtin function ID
841
// B: double
842
// C: double/int (optional, 2nd argument)
843
INVOKE_LIBM,
844
845
// Returns the string name of a type based on tag, alternative for type(x)
846
// A: tag
847
GET_TYPE,
848
849
// Returns the string name of a type either from a __type metatable field or just based on the tag, alternative for typeof(x)
850
// A: Rn
851
GET_TYPEOF,
852
853
// Find or create an upval at the given level
854
// A: Rn (level)
855
FINDUPVAL,
856
857
// Read i8 (sign-extended to int) from buffer storage at specified offset
858
// A: pointer (buffer)
859
// B: int (offset)
860
BUFFER_READI8,
861
862
// Read u8 (zero-extended to int) from buffer storage at specified offset
863
// A: pointer (buffer)
864
// B: int (offset)
865
BUFFER_READU8,
866
867
// Write i8/u8 value (int argument is truncated) to buffer storage at specified offset
868
// A: pointer (buffer)
869
// B: int (offset)
870
// C: int (value)
871
BUFFER_WRITEI8,
872
873
// Read i16 (sign-extended to int) from buffer storage at specified offset
874
// A: pointer (buffer)
875
// B: int (offset)
876
BUFFER_READI16,
877
878
// Read u16 (zero-extended to int) from buffer storage at specified offset
879
// A: pointer (buffer)
880
// B: int (offset)
881
BUFFER_READU16,
882
883
// Write i16/u16 value (int argument is truncated) to buffer storage at specified offset
884
// A: pointer (buffer)
885
// B: int (offset)
886
// C: int (value)
887
BUFFER_WRITEI16,
888
889
// Read i32 value from buffer storage at specified offset
890
// A: pointer (buffer)
891
// B: int (offset)
892
BUFFER_READI32,
893
894
// Write i32/u32 value to buffer storage at specified offset
895
// A: pointer (buffer)
896
// B: int (offset)
897
// C: int (value)
898
BUFFER_WRITEI32,
899
900
// Read float value (use FLOAT_TO_NUM to convert to double) from buffer storage at specified offset
901
// A: pointer (buffer)
902
// B: int (offset)
903
BUFFER_READF32,
904
905
// Write float value (use NUM_TO_FLOAT to convert from double) to buffer storage at specified offset
906
// A: pointer (buffer)
907
// B: int (offset)
908
// C: float (value)
909
BUFFER_WRITEF32,
910
911
// Read double value from buffer storage at specified offset
912
// A: pointer (buffer)
913
// B: int (offset)
914
BUFFER_READF64,
915
916
// Write double value to buffer storage at specified offset
917
// A: pointer (buffer)
918
// B: int (offset)
919
// C: double (value)
920
BUFFER_WRITEF64,
921
};
922
923
enum class IrConstKind : uint8_t
924
{
925
Int,
926
Uint,
927
Double,
928
Tag,
929
Import,
930
};
931
932
struct IrConst
933
{
934
IrConstKind kind;
935
936
union
937
{
938
int valueInt;
939
unsigned valueUint;
940
double valueDouble;
941
uint8_t valueTag;
942
};
943
};
944
945
enum class IrCondition : uint8_t
946
{
947
Equal,
948
NotEqual,
949
Less,
950
NotLess,
951
LessEqual,
952
NotLessEqual,
953
Greater,
954
NotGreater,
955
GreaterEqual,
956
NotGreaterEqual,
957
958
UnsignedLess,
959
UnsignedLessEqual,
960
UnsignedGreater,
961
UnsignedGreaterEqual,
962
963
Count
964
};
965
966
enum class IrOpKind : uint32_t
967
{
968
None,
969
970
Undef,
971
972
// To reference a constant value
973
Constant,
974
975
// To specify a condition code
976
Condition,
977
978
// To reference a result of a previous instruction
979
Inst,
980
981
// To reference a basic block in control flow
982
Block,
983
984
// To reference a VM register
985
VmReg,
986
987
// To reference a VM constant
988
VmConst,
989
990
// To reference a VM upvalue
991
VmUpvalue,
992
993
// To reference an exit to VM at specific PC pos
994
VmExit,
995
};
996
997
// VmExit uses a special value to indicate that pcpos update should be skipped
998
// This is only used during type checking at function entry
999
inline constexpr uint32_t kVmExitEntryGuardPc = (1u << 28) - 1;
1000
1001
struct IrOp
1002
{
1003
IrOpKind kind : 4;
1004
uint32_t index : 28;
1005
1006
IrOp()
1007
: kind(IrOpKind::None)
1008
, index(0)
1009
{
1010
}
1011
1012
IrOp(IrOpKind kind, uint32_t index)
1013
: kind(kind)
1014
, index(index)
1015
{
1016
}
1017
1018
bool operator==(const IrOp& rhs) const
1019
{
1020
return kind == rhs.kind && index == rhs.index;
1021
}
1022
1023
bool operator!=(const IrOp& rhs) const
1024
{
1025
return !(*this == rhs);
1026
}
1027
};
1028
1029
static_assert(sizeof(IrOp) == 4);
1030
1031
enum class IrValueKind : uint8_t
1032
{
1033
Unknown, // Used by SUBSTITUTE, argument has to be checked to get type
1034
None,
1035
Tag,
1036
Int,
1037
Pointer,
1038
Float,
1039
Double,
1040
Tvalue,
1041
1042
Count
1043
};
1044
1045
using IrOps = SmallVector<IrOp, 6>;
1046
1047
struct IrInst
1048
{
1049
IrCmd cmd;
1050
1051
// Operands
1052
// All frequently used instructions use only A-F slots.
1053
IrOps ops;
1054
1055
uint32_t lastUse = 0;
1056
uint16_t useCount = 0;
1057
1058
// Location of the result (optional)
1059
X64::RegisterX64 regX64 = X64::noreg;
1060
A64::RegisterA64 regA64 = A64::noreg;
1061
bool reusedReg = false;
1062
bool spilled = false;
1063
bool needsReload = false;
1064
};
1065
1066
inline IrOp& getOp(IrInst& inst, uint32_t idx)
1067
{
1068
if (LUAU_UNLIKELY(idx >= inst.ops.size()))
1069
{
1070
inst.ops.resize(idx + 1);
1071
}
1072
return inst.ops[idx];
1073
}
1074
1075
inline IrOp& getOp(IrInst* inst, uint32_t idx)
1076
{
1077
return getOp(*inst, idx);
1078
}
1079
1080
inline bool hasOp(IrInst& inst, uint32_t idx)
1081
{
1082
return idx < inst.ops.size();
1083
}
1084
1085
// TODO: once we update kind checks to not use getOp, it will no longer cause a resize and second part can be removed
1086
#define HAS_OP_A(inst) (0 < (inst).ops.size() && (inst).ops[0].kind != IrOpKind::None)
1087
#define HAS_OP_B(inst) (1 < (inst).ops.size() && (inst).ops[1].kind != IrOpKind::None)
1088
#define HAS_OP_C(inst) (2 < (inst).ops.size() && (inst).ops[2].kind != IrOpKind::None)
1089
#define HAS_OP_D(inst) (3 < (inst).ops.size() && (inst).ops[3].kind != IrOpKind::None)
1090
#define HAS_OP_E(inst) (4 < (inst).ops.size() && (inst).ops[4].kind != IrOpKind::None)
1091
#define HAS_OP_F(inst) (5 < (inst).ops.size() && (inst).ops[5].kind != IrOpKind::None)
1092
#define HAS_OP_G(inst) (6 < (inst).ops.size() && (inst).ops[6].kind != IrOpKind::None)
1093
1094
#define OPT_OP_A(inst) (0 < (inst).ops.size() && (inst).ops[0].kind != IrOpKind::None ? (inst).ops[0] : IrOp{})
1095
#define OPT_OP_B(inst) (1 < (inst).ops.size() && (inst).ops[1].kind != IrOpKind::None ? (inst).ops[1] : IrOp{})
1096
#define OPT_OP_C(inst) (2 < (inst).ops.size() && (inst).ops[2].kind != IrOpKind::None ? (inst).ops[2] : IrOp{})
1097
#define OPT_OP_D(inst) (3 < (inst).ops.size() && (inst).ops[3].kind != IrOpKind::None ? (inst).ops[3] : IrOp{})
1098
#define OPT_OP_E(inst) (4 < (inst).ops.size() && (inst).ops[4].kind != IrOpKind::None ? (inst).ops[4] : IrOp{})
1099
#define OPT_OP_F(inst) (5 < (inst).ops.size() && (inst).ops[5].kind != IrOpKind::None ? (inst).ops[5] : IrOp{})
1100
#define OPT_OP_G(inst) (6 < (inst).ops.size() && (inst).ops[6].kind != IrOpKind::None ? (inst).ops[6] : IrOp{})
1101
1102
// When IrInst operands are used, current instruction index is often required to track lifetime
1103
inline constexpr uint32_t kInvalidInstIdx = ~0u;
1104
1105
struct IrInstHash
1106
{
1107
static const uint32_t m = 0x5bd1e995;
1108
static const int r = 24;
1109
1110
static uint32_t mix(uint32_t h, uint32_t k)
1111
{
1112
// MurmurHash2 step
1113
k *= m;
1114
k ^= k >> r;
1115
k *= m;
1116
1117
h *= m;
1118
h ^= k;
1119
1120
return h;
1121
}
1122
1123
static uint32_t mix(uint32_t h, IrOp op)
1124
{
1125
static_assert(sizeof(op) == sizeof(uint32_t));
1126
uint32_t k;
1127
memcpy(&k, &op, sizeof(op));
1128
1129
return mix(h, k);
1130
}
1131
1132
size_t operator()(const IrInst& key) const
1133
{
1134
// MurmurHash2 unrolled
1135
uint32_t h = 25;
1136
1137
h = mix(h, uint32_t(key.cmd));
1138
for (size_t i = 0; i < 7; i++)
1139
h = mix(h, i < uint32_t(key.ops.size()) ? key.ops[i] : IrOp{});
1140
1141
// MurmurHash2 tail
1142
h ^= h >> 13;
1143
h *= m;
1144
h ^= h >> 15;
1145
1146
return h;
1147
}
1148
};
1149
1150
struct IrInstEq
1151
{
1152
bool operator()(const IrInst& a, const IrInst& b) const
1153
{
1154
if (a.cmd != b.cmd)
1155
return false;
1156
if (a.ops.size() == b.ops.size())
1157
{
1158
for (size_t i = 0; i < a.ops.size(); i++)
1159
if (a.ops[i] != b.ops[i])
1160
return false;
1161
}
1162
else if (a.ops.size() < b.ops.size())
1163
{
1164
size_t i = 0;
1165
for (; i < a.ops.size(); i++)
1166
if (a.ops[i] != b.ops[i])
1167
return false;
1168
for (; i < b.ops.size(); i++)
1169
if (b.ops[i].kind != IrOpKind::None)
1170
return false;
1171
}
1172
else
1173
{
1174
size_t i = 0;
1175
for (; i < b.ops.size(); i++)
1176
if (a.ops[i] != b.ops[i])
1177
return false;
1178
for (; i < a.ops.size(); i++)
1179
if (a.ops[i].kind != IrOpKind::None)
1180
return false;
1181
}
1182
return true;
1183
}
1184
};
1185
1186
enum class IrBlockKind : uint8_t
1187
{
1188
Bytecode,
1189
Fallback,
1190
Internal,
1191
Linearized,
1192
Dead,
1193
};
1194
1195
inline constexpr uint32_t kBlockNoStartPc = ~0u;
1196
1197
inline constexpr uint8_t kBlockFlagSafeEnvCheck = 1 << 0;
1198
inline constexpr uint8_t kBlockFlagSafeEnvClear = 1 << 1;
1199
inline constexpr uint8_t kBlockFlagEntryArgCheck = 1 << 2;
1200
1201
struct IrBlock
1202
{
1203
IrBlockKind kind;
1204
uint8_t flags = 0;
1205
uint16_t useCount = 0;
1206
1207
// 'start' and 'finish' define an inclusive range of instructions which belong to this block inside the function
1208
// When block has been constructed, 'finish' always points to the first and only terminating instruction
1209
uint32_t start = ~0u;
1210
uint32_t finish = ~0u;
1211
1212
uint32_t sortkey = ~0u;
1213
uint32_t chainkey = 0;
1214
uint32_t expectedNextBlock = ~0u;
1215
1216
// Bytecode PC position at which the block was generated
1217
uint32_t startpc = kBlockNoStartPc;
1218
1219
Label label;
1220
};
1221
1222
struct BytecodeMapping
1223
{
1224
uint32_t irLocation;
1225
uint32_t asmLocation;
1226
};
1227
1228
struct BytecodeBlock
1229
{
1230
// 'start' and 'finish' define an inclusive range of instructions which belong to the block
1231
int startpc = -1;
1232
int finishpc = -1;
1233
};
1234
1235
struct BytecodeTypes
1236
{
1237
uint8_t result = LBC_TYPE_ANY;
1238
uint8_t a = LBC_TYPE_ANY;
1239
uint8_t b = LBC_TYPE_ANY;
1240
uint8_t c = LBC_TYPE_ANY;
1241
};
1242
1243
struct BytecodeRegTypeInfo
1244
{
1245
uint8_t type = LBC_TYPE_ANY;
1246
uint8_t reg = 0; // Register slot where variable is stored
1247
int startpc = 0; // First point where variable is alive (could be before variable has been assigned a value)
1248
int endpc = 0; // First point where variable is dead
1249
};
1250
1251
struct BytecodeTypeInfo
1252
{
1253
std::vector<uint8_t> argumentTypes;
1254
std::vector<BytecodeRegTypeInfo> regTypes;
1255
std::vector<uint8_t> upvalueTypes;
1256
1257
// Offsets into regTypes for each individual register
1258
// One extra element at the end contains the vector size for easier arr[Rn], arr[Rn + 1] range access
1259
std::vector<uint32_t> regTypeOffsets;
1260
};
1261
1262
struct ValueRestoreLocation
1263
{
1264
IrOp op; // Operand representing the location (Rn/Kn)
1265
IrValueKind kind; // The kind of value at the restore location
1266
IrCmd conversionCmd; // Type conversion instruction that was used to store the value at the restore location
1267
};
1268
1269
struct IrFunction
1270
{
1271
std::vector<IrBlock> blocks;
1272
std::vector<IrInst> instructions;
1273
std::vector<IrConst> constants;
1274
1275
std::vector<BytecodeBlock> bcBlocks;
1276
std::vector<BytecodeTypes> bcTypes;
1277
1278
std::vector<BytecodeMapping> bcMapping;
1279
uint32_t entryBlock = 0;
1280
uint32_t entryLocation = 0;
1281
uint32_t endLocation = 0;
1282
1283
std::vector<uint32_t> extraNativeData;
1284
1285
// For each instruction, an operand that can be used to recompute the value
1286
std::vector<ValueRestoreLocation> valueRestoreOps;
1287
std::vector<uint32_t> validRestoreOpBlocks;
1288
1289
BytecodeTypeInfo bcOriginalTypeInfo; // Bytecode type information as loaded
1290
BytecodeTypeInfo bcTypeInfo; // Bytecode type information with additional inferences
1291
1292
Proto* proto = nullptr;
1293
bool variadic = false;
1294
1295
CfgInfo cfg;
1296
1297
LoweringStats* stats = nullptr;
1298
1299
bool recordCounters = false; // Taken from CompilationOptions for easy access
1300
1301
// Stores register tags that are known after constant propagating through a block, indexed by that block's index
1302
std::vector<std::vector<uint8_t>> blockExitTags; // blockIdx → tag array
1303
1304
IrBlock& blockOp(IrOp op)
1305
{
1306
CODEGEN_ASSERT(op.kind == IrOpKind::Block);
1307
return blocks[op.index];
1308
}
1309
1310
IrInst& instOp(IrOp op)
1311
{
1312
CODEGEN_ASSERT(op.kind == IrOpKind::Inst);
1313
return instructions[op.index];
1314
}
1315
1316
IrInst* asInstOp(IrOp op)
1317
{
1318
if (op.kind == IrOpKind::Inst)
1319
return &instructions[op.index];
1320
1321
return nullptr;
1322
}
1323
1324
IrConst& constOp(IrOp op)
1325
{
1326
CODEGEN_ASSERT(op.kind == IrOpKind::Constant);
1327
return constants[op.index];
1328
}
1329
1330
uint8_t tagOp(IrOp op)
1331
{
1332
IrConst& value = constOp(op);
1333
1334
CODEGEN_ASSERT(value.kind == IrConstKind::Tag);
1335
return value.valueTag;
1336
}
1337
1338
std::optional<uint8_t> asTagOp(IrOp op)
1339
{
1340
if (op.kind != IrOpKind::Constant)
1341
return std::nullopt;
1342
1343
IrConst& value = constOp(op);
1344
1345
if (value.kind != IrConstKind::Tag)
1346
return std::nullopt;
1347
1348
return value.valueTag;
1349
}
1350
1351
int intOp(IrOp op)
1352
{
1353
IrConst& value = constOp(op);
1354
1355
CODEGEN_ASSERT(value.kind == IrConstKind::Int);
1356
return value.valueInt;
1357
}
1358
1359
std::optional<int> asIntOp(IrOp op)
1360
{
1361
if (op.kind != IrOpKind::Constant)
1362
return std::nullopt;
1363
1364
IrConst& value = constOp(op);
1365
1366
if (value.kind != IrConstKind::Int)
1367
return std::nullopt;
1368
1369
return value.valueInt;
1370
}
1371
1372
unsigned uintOp(IrOp op)
1373
{
1374
IrConst& value = constOp(op);
1375
1376
CODEGEN_ASSERT(value.kind == IrConstKind::Uint);
1377
return value.valueUint;
1378
}
1379
1380
unsigned importOp(IrOp op)
1381
{
1382
IrConst& value = constOp(op);
1383
1384
CODEGEN_ASSERT(value.kind == IrConstKind::Import);
1385
return value.valueUint;
1386
}
1387
1388
std::optional<unsigned> asUintOp(IrOp op)
1389
{
1390
if (op.kind != IrOpKind::Constant)
1391
return std::nullopt;
1392
1393
IrConst& value = constOp(op);
1394
1395
if (value.kind != IrConstKind::Uint)
1396
return std::nullopt;
1397
1398
return value.valueUint;
1399
}
1400
1401
double doubleOp(IrOp op)
1402
{
1403
IrConst& value = constOp(op);
1404
1405
CODEGEN_ASSERT(value.kind == IrConstKind::Double);
1406
return value.valueDouble;
1407
}
1408
1409
std::optional<double> asDoubleOp(IrOp op)
1410
{
1411
if (op.kind != IrOpKind::Constant)
1412
return std::nullopt;
1413
1414
IrConst& value = constOp(op);
1415
1416
if (value.kind != IrConstKind::Double)
1417
return std::nullopt;
1418
1419
return value.valueDouble;
1420
}
1421
1422
uint32_t getBlockIndex(const IrBlock& block) const
1423
{
1424
// Can only be called with blocks from our vector
1425
CODEGEN_ASSERT(&block >= blocks.data() && &block <= blocks.data() + blocks.size());
1426
return uint32_t(&block - blocks.data());
1427
}
1428
1429
uint32_t getInstIndex(const IrInst& inst) const
1430
{
1431
// Can only be called with instructions from our vector
1432
CODEGEN_ASSERT(&inst >= instructions.data() && &inst <= instructions.data() + instructions.size());
1433
return uint32_t(&inst - instructions.data());
1434
}
1435
1436
void recordRestoreLocation(uint32_t instIdx, ValueRestoreLocation location)
1437
{
1438
CODEGEN_ASSERT(location.op.kind == IrOpKind::None || location.op.kind == IrOpKind::VmReg || location.op.kind == IrOpKind::VmConst);
1439
1440
if (instIdx >= valueRestoreOps.size())
1441
valueRestoreOps.resize(instIdx + 1);
1442
1443
valueRestoreOps[instIdx] = location;
1444
}
1445
1446
ValueRestoreLocation findRestoreLocation(uint32_t instIdx, bool limitToCurrentBlock) const
1447
{
1448
if (instIdx >= valueRestoreOps.size())
1449
return {};
1450
1451
// When spilled, values can only reference restore operands in the current block chain
1452
if (limitToCurrentBlock)
1453
{
1454
for (uint32_t blockIdx : validRestoreOpBlocks)
1455
{
1456
const IrBlock& block = blocks[blockIdx];
1457
1458
if (instIdx >= block.start && instIdx <= block.finish)
1459
return valueRestoreOps[instIdx];
1460
}
1461
1462
return {};
1463
}
1464
1465
return valueRestoreOps[instIdx];
1466
}
1467
1468
ValueRestoreLocation findRestoreLocation(const IrInst& inst, bool limitToCurrentBlock) const
1469
{
1470
return findRestoreLocation(getInstIndex(inst), limitToCurrentBlock);
1471
}
1472
1473
bool hasRestoreLocation(const IrInst& inst, bool limitToCurrentBlock) const
1474
{
1475
return findRestoreLocation(getInstIndex(inst), limitToCurrentBlock).op.kind != IrOpKind::None;
1476
}
1477
1478
BytecodeTypes getBytecodeTypesAt(int pcpos) const
1479
{
1480
CODEGEN_ASSERT(pcpos >= 0);
1481
1482
if (size_t(pcpos) < bcTypes.size())
1483
return bcTypes[pcpos];
1484
1485
return BytecodeTypes();
1486
}
1487
};
1488
1489
inline IrCondition conditionOp(IrOp op)
1490
{
1491
CODEGEN_ASSERT(op.kind == IrOpKind::Condition);
1492
return IrCondition(op.index);
1493
}
1494
1495
inline int vmRegOp(IrOp op)
1496
{
1497
CODEGEN_ASSERT(op.kind == IrOpKind::VmReg);
1498
return op.index;
1499
}
1500
1501
inline int vmConstOp(IrOp op)
1502
{
1503
CODEGEN_ASSERT(op.kind == IrOpKind::VmConst);
1504
return op.index;
1505
}
1506
1507
inline int vmUpvalueOp(IrOp op)
1508
{
1509
CODEGEN_ASSERT(op.kind == IrOpKind::VmUpvalue);
1510
return op.index;
1511
}
1512
1513
inline uint32_t vmExitOp(IrOp op)
1514
{
1515
CODEGEN_ASSERT(op.kind == IrOpKind::VmExit);
1516
return op.index;
1517
}
1518
1519
} // namespace CodeGen
1520
} // namespace Luau
1521
1522