Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/CodeGen/src/IrDump.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/IrDump.h"
3
4
#include "Luau/IrUtils.h"
5
6
#include "lua.h"
7
#include "lobject.h"
8
#include "lstate.h"
9
10
#include <stdarg.h>
11
12
LUAU_FASTFLAG(LuauIntegerType)
13
namespace Luau
14
{
15
namespace CodeGen
16
{
17
18
static const char* textForCondition[] =
19
{"eq", "not_eq", "lt", "not_lt", "le", "not_le", "gt", "not_gt", "ge", "not_ge", "u_lt", "u_le", "u_gt", "u_ge"};
20
static_assert(sizeof(textForCondition) / sizeof(textForCondition[0]) == size_t(IrCondition::Count), "all conditions have to be covered");
21
22
const int kDetailsAlignColumn = 60;
23
const unsigned kMaxStringConstantPrintLength = 16;
24
25
LUAU_PRINTF_ATTR(2, 3)
26
static void append(std::string& result, const char* fmt, ...)
27
{
28
char buf[256];
29
va_list args;
30
va_start(args, fmt);
31
vsnprintf(buf, sizeof(buf), fmt, args);
32
va_end(args);
33
result.append(buf);
34
}
35
36
static void padToDetailColumn(std::string& result, size_t lineStart)
37
{
38
int pad = kDetailsAlignColumn - int(result.size() - lineStart);
39
40
if (pad > 0)
41
result.append(pad, ' ');
42
}
43
44
static bool isPrintableStringConstant(const char* str, size_t len)
45
{
46
for (size_t i = 0; i < len; ++i)
47
{
48
if (unsigned(str[i]) < ' ')
49
return false;
50
}
51
52
return true;
53
}
54
55
static const char* getTagName(uint8_t tag)
56
{
57
switch (tag)
58
{
59
case LUA_TNIL:
60
return "tnil";
61
case LUA_TBOOLEAN:
62
return "tboolean";
63
case LUA_TLIGHTUSERDATA:
64
return "tlightuserdata";
65
case LUA_TNUMBER:
66
return "tnumber";
67
case LUA_TVECTOR:
68
return "tvector";
69
case LUA_TSTRING:
70
return "tstring";
71
case LUA_TTABLE:
72
return "ttable";
73
case LUA_TFUNCTION:
74
return "tfunction";
75
case LUA_TUSERDATA:
76
return "tuserdata";
77
case LUA_TTHREAD:
78
return "tthread";
79
case LUA_TBUFFER:
80
return "tbuffer";
81
case LUA_TPROTO:
82
return "tproto";
83
case LUA_TUPVAL:
84
return "tupval";
85
case LUA_TDEADKEY:
86
return "tdeadkey";
87
case LUA_TINTEGER:
88
if (FFlag::LuauIntegerType)
89
return "tinteger";
90
[[fallthrough]];
91
default:
92
CODEGEN_ASSERT(!"Unknown type tag");
93
LUAU_UNREACHABLE();
94
}
95
}
96
97
const char* getCmdName(IrCmd cmd)
98
{
99
switch (cmd)
100
{
101
case IrCmd::NOP:
102
return "NOP";
103
case IrCmd::LOAD_TAG:
104
return "LOAD_TAG";
105
case IrCmd::LOAD_POINTER:
106
return "LOAD_POINTER";
107
case IrCmd::LOAD_DOUBLE:
108
return "LOAD_DOUBLE";
109
case IrCmd::LOAD_INT:
110
return "LOAD_INT";
111
case IrCmd::LOAD_FLOAT:
112
return "LOAD_FLOAT";
113
case IrCmd::LOAD_TVALUE:
114
return "LOAD_TVALUE";
115
case IrCmd::LOAD_ENV:
116
return "LOAD_ENV";
117
case IrCmd::GET_ARR_ADDR:
118
return "GET_ARR_ADDR";
119
case IrCmd::GET_SLOT_NODE_ADDR:
120
return "GET_SLOT_NODE_ADDR";
121
case IrCmd::GET_HASH_NODE_ADDR:
122
return "GET_HASH_NODE_ADDR";
123
case IrCmd::GET_CLOSURE_UPVAL_ADDR:
124
return "GET_CLOSURE_UPVAL_ADDR";
125
case IrCmd::STORE_TAG:
126
return "STORE_TAG";
127
case IrCmd::STORE_EXTRA:
128
return "STORE_EXTRA";
129
case IrCmd::STORE_POINTER:
130
return "STORE_POINTER";
131
case IrCmd::STORE_DOUBLE:
132
return "STORE_DOUBLE";
133
case IrCmd::STORE_INT:
134
return "STORE_INT";
135
case IrCmd::STORE_VECTOR:
136
return "STORE_VECTOR";
137
case IrCmd::STORE_TVALUE:
138
return "STORE_TVALUE";
139
case IrCmd::STORE_SPLIT_TVALUE:
140
return "STORE_SPLIT_TVALUE";
141
case IrCmd::ADD_INT:
142
return "ADD_INT";
143
case IrCmd::SUB_INT:
144
return "SUB_INT";
145
case IrCmd::SEXTI8_INT:
146
return "SEXTI8_INT";
147
case IrCmd::SEXTI16_INT:
148
return "SEXTI16_INT";
149
case IrCmd::ADD_NUM:
150
return "ADD_NUM";
151
case IrCmd::SUB_NUM:
152
return "SUB_NUM";
153
case IrCmd::MUL_NUM:
154
return "MUL_NUM";
155
case IrCmd::DIV_NUM:
156
return "DIV_NUM";
157
case IrCmd::IDIV_NUM:
158
return "IDIV_NUM";
159
case IrCmd::MOD_NUM:
160
return "MOD_NUM";
161
case IrCmd::MIN_NUM:
162
return "MIN_NUM";
163
case IrCmd::MAX_NUM:
164
return "MAX_NUM";
165
case IrCmd::UNM_NUM:
166
return "UNM_NUM";
167
case IrCmd::FLOOR_NUM:
168
return "FLOOR_NUM";
169
case IrCmd::CEIL_NUM:
170
return "CEIL_NUM";
171
case IrCmd::ROUND_NUM:
172
return "ROUND_NUM";
173
case IrCmd::SQRT_NUM:
174
return "SQRT_NUM";
175
case IrCmd::ABS_NUM:
176
return "ABS_NUM";
177
case IrCmd::SIGN_NUM:
178
return "SIGN_NUM";
179
case IrCmd::ADD_FLOAT:
180
return "ADD_FLOAT";
181
case IrCmd::SUB_FLOAT:
182
return "SUB_FLOAT";
183
case IrCmd::MUL_FLOAT:
184
return "MUL_FLOAT";
185
case IrCmd::DIV_FLOAT:
186
return "DIV_FLOAT";
187
case IrCmd::MIN_FLOAT:
188
return "MIN_FLOAT";
189
case IrCmd::MAX_FLOAT:
190
return "MAX_FLOAT";
191
case IrCmd::UNM_FLOAT:
192
return "UNM_FLOAT";
193
case IrCmd::FLOOR_FLOAT:
194
return "FLOOR_FLOAT";
195
case IrCmd::CEIL_FLOAT:
196
return "CEIL_FLOAT";
197
case IrCmd::SQRT_FLOAT:
198
return "SQRT_FLOAT";
199
case IrCmd::ABS_FLOAT:
200
return "ABS_FLOAT";
201
case IrCmd::SIGN_FLOAT:
202
return "SIGN_FLOAT";
203
case IrCmd::SELECT_NUM:
204
return "SELECT_NUM";
205
case IrCmd::MULADD_NUM:
206
return "MULADD_NUM";
207
case IrCmd::SELECT_VEC:
208
return "SELECT_VEC";
209
case IrCmd::SELECT_IF_TRUTHY:
210
return "SELECT_IF_TRUTHY";
211
case IrCmd::ADD_VEC:
212
return "ADD_VEC";
213
case IrCmd::SUB_VEC:
214
return "SUB_VEC";
215
case IrCmd::MUL_VEC:
216
return "MUL_VEC";
217
case IrCmd::DIV_VEC:
218
return "DIV_VEC";
219
case IrCmd::IDIV_VEC:
220
return "IDIV_VEC";
221
case IrCmd::MULADD_VEC:
222
return "MULADD_VEC";
223
case IrCmd::UNM_VEC:
224
return "UNM_VEC";
225
case IrCmd::MIN_VEC:
226
return "MIN_VEC";
227
case IrCmd::MAX_VEC:
228
return "MAX_VEC";
229
case IrCmd::FLOOR_VEC:
230
return "FLOOR_VEC";
231
case IrCmd::CEIL_VEC:
232
return "CEIL_VEC";
233
case IrCmd::ABS_VEC:
234
return "ABS_VEC";
235
case IrCmd::DOT_VEC:
236
return "DOT_VEC";
237
case IrCmd::EXTRACT_VEC:
238
return "EXTRACT_VEC";
239
case IrCmd::NOT_ANY:
240
return "NOT_ANY";
241
case IrCmd::CMP_ANY:
242
return "CMP_ANY";
243
case IrCmd::CMP_INT:
244
return "CMP_INT";
245
case IrCmd::CMP_TAG:
246
return "CMP_TAG";
247
case IrCmd::CMP_SPLIT_TVALUE:
248
return "CMP_SPLIT_TVALUE";
249
case IrCmd::JUMP:
250
return "JUMP";
251
case IrCmd::JUMP_IF_TRUTHY:
252
return "JUMP_IF_TRUTHY";
253
case IrCmd::JUMP_IF_FALSY:
254
return "JUMP_IF_FALSY";
255
case IrCmd::JUMP_EQ_TAG:
256
return "JUMP_EQ_TAG";
257
case IrCmd::JUMP_CMP_INT:
258
return "JUMP_CMP_INT";
259
case IrCmd::JUMP_EQ_POINTER:
260
return "JUMP_EQ_POINTER";
261
case IrCmd::JUMP_CMP_NUM:
262
return "JUMP_CMP_NUM";
263
case IrCmd::JUMP_CMP_FLOAT:
264
return "JUMP_CMP_FLOAT";
265
case IrCmd::JUMP_FORN_LOOP_COND:
266
return "JUMP_FORN_LOOP_COND";
267
case IrCmd::JUMP_SLOT_MATCH:
268
return "JUMP_SLOT_MATCH";
269
case IrCmd::TABLE_LEN:
270
return "TABLE_LEN";
271
case IrCmd::TABLE_SETNUM:
272
return "TABLE_SETNUM";
273
case IrCmd::STRING_LEN:
274
return "STRING_LEN";
275
case IrCmd::NEW_TABLE:
276
return "NEW_TABLE";
277
case IrCmd::DUP_TABLE:
278
return "DUP_TABLE";
279
case IrCmd::TRY_NUM_TO_INDEX:
280
return "TRY_NUM_TO_INDEX";
281
case IrCmd::TRY_CALL_FASTGETTM:
282
return "TRY_CALL_FASTGETTM";
283
case IrCmd::NEW_USERDATA:
284
return "NEW_USERDATA";
285
case IrCmd::INT_TO_NUM:
286
return "INT_TO_NUM";
287
case IrCmd::UINT_TO_NUM:
288
return "UINT_TO_NUM";
289
case IrCmd::UINT_TO_FLOAT:
290
return "UINT_TO_FLOAT";
291
case IrCmd::NUM_TO_INT:
292
return "NUM_TO_INT";
293
case IrCmd::NUM_TO_UINT:
294
return "NUM_TO_UINT";
295
case IrCmd::FLOAT_TO_NUM:
296
return "FLOAT_TO_NUM";
297
case IrCmd::NUM_TO_FLOAT:
298
return "NUM_TO_FLOAT";
299
case IrCmd::FLOAT_TO_VEC:
300
return "FLOAT_TO_VEC";
301
case IrCmd::TAG_VECTOR:
302
return "TAG_VECTOR";
303
case IrCmd::TRUNCATE_UINT:
304
return "TRUNCATE_UINT";
305
case IrCmd::ADJUST_STACK_TO_REG:
306
return "ADJUST_STACK_TO_REG";
307
case IrCmd::ADJUST_STACK_TO_TOP:
308
return "ADJUST_STACK_TO_TOP";
309
case IrCmd::FASTCALL:
310
return "FASTCALL";
311
case IrCmd::INVOKE_FASTCALL:
312
return "INVOKE_FASTCALL";
313
case IrCmd::CHECK_FASTCALL_RES:
314
return "CHECK_FASTCALL_RES";
315
case IrCmd::DO_ARITH:
316
return "DO_ARITH";
317
case IrCmd::DO_LEN:
318
return "DO_LEN";
319
case IrCmd::GET_TABLE:
320
return "GET_TABLE";
321
case IrCmd::SET_TABLE:
322
return "SET_TABLE";
323
case IrCmd::GET_CACHED_IMPORT:
324
return "GET_CACHED_IMPORT";
325
case IrCmd::CONCAT:
326
return "CONCAT";
327
case IrCmd::GET_UPVALUE:
328
return "GET_UPVALUE";
329
case IrCmd::SET_UPVALUE:
330
return "SET_UPVALUE";
331
case IrCmd::CHECK_TAG:
332
return "CHECK_TAG";
333
case IrCmd::CHECK_TRUTHY:
334
return "CHECK_TRUTHY";
335
case IrCmd::CHECK_READONLY:
336
return "CHECK_READONLY";
337
case IrCmd::CHECK_NO_METATABLE:
338
return "CHECK_NO_METATABLE";
339
case IrCmd::CHECK_SAFE_ENV:
340
return "CHECK_SAFE_ENV";
341
case IrCmd::CHECK_ARRAY_SIZE:
342
return "CHECK_ARRAY_SIZE";
343
case IrCmd::CHECK_SLOT_MATCH:
344
return "CHECK_SLOT_MATCH";
345
case IrCmd::CHECK_NODE_NO_NEXT:
346
return "CHECK_NODE_NO_NEXT";
347
case IrCmd::CHECK_NODE_VALUE:
348
return "CHECK_NODE_VALUE";
349
case IrCmd::CHECK_BUFFER_LEN:
350
return "CHECK_BUFFER_LEN";
351
case IrCmd::CHECK_USERDATA_TAG:
352
return "CHECK_USERDATA_TAG";
353
case IrCmd::CHECK_CMP_INT:
354
return "CHECK_CMP_INT";
355
case IrCmd::INTERRUPT:
356
return "INTERRUPT";
357
case IrCmd::CHECK_GC:
358
return "CHECK_GC";
359
case IrCmd::BARRIER_OBJ:
360
return "BARRIER_OBJ";
361
case IrCmd::BARRIER_TABLE_BACK:
362
return "BARRIER_TABLE_BACK";
363
case IrCmd::BARRIER_TABLE_FORWARD:
364
return "BARRIER_TABLE_FORWARD";
365
case IrCmd::SET_SAVEDPC:
366
return "SET_SAVEDPC";
367
case IrCmd::CLOSE_UPVALS:
368
return "CLOSE_UPVALS";
369
case IrCmd::CAPTURE:
370
return "CAPTURE";
371
case IrCmd::SETLIST:
372
return "SETLIST";
373
case IrCmd::CALL:
374
return "CALL";
375
case IrCmd::RETURN:
376
return "RETURN";
377
case IrCmd::FORGLOOP:
378
return "FORGLOOP";
379
case IrCmd::FORGLOOP_FALLBACK:
380
return "FORGLOOP_FALLBACK";
381
case IrCmd::FORGPREP_XNEXT_FALLBACK:
382
return "FORGPREP_XNEXT_FALLBACK";
383
case IrCmd::COVERAGE:
384
return "COVERAGE";
385
case IrCmd::FALLBACK_GETGLOBAL:
386
return "FALLBACK_GETGLOBAL";
387
case IrCmd::FALLBACK_SETGLOBAL:
388
return "FALLBACK_SETGLOBAL";
389
case IrCmd::FALLBACK_GETTABLEKS:
390
return "FALLBACK_GETTABLEKS";
391
case IrCmd::FALLBACK_SETTABLEKS:
392
return "FALLBACK_SETTABLEKS";
393
case IrCmd::FALLBACK_NAMECALL:
394
return "FALLBACK_NAMECALL";
395
case IrCmd::FALLBACK_PREPVARARGS:
396
return "FALLBACK_PREPVARARGS";
397
case IrCmd::FALLBACK_GETVARARGS:
398
return "FALLBACK_GETVARARGS";
399
case IrCmd::NEWCLOSURE:
400
return "NEWCLOSURE";
401
case IrCmd::FALLBACK_DUPCLOSURE:
402
return "FALLBACK_DUPCLOSURE";
403
case IrCmd::FALLBACK_FORGPREP:
404
return "FALLBACK_FORGPREP";
405
case IrCmd::SUBSTITUTE:
406
return "SUBSTITUTE";
407
case IrCmd::MARK_USED:
408
return "MARK_USED";
409
case IrCmd::MARK_DEAD:
410
return "MARK_DEAD";
411
case IrCmd::BITAND_UINT:
412
return "BITAND_UINT";
413
case IrCmd::BITXOR_UINT:
414
return "BITXOR_UINT";
415
case IrCmd::BITOR_UINT:
416
return "BITOR_UINT";
417
case IrCmd::BITNOT_UINT:
418
return "BITNOT_UINT";
419
case IrCmd::BITLSHIFT_UINT:
420
return "BITLSHIFT_UINT";
421
case IrCmd::BITRSHIFT_UINT:
422
return "BITRSHIFT_UINT";
423
case IrCmd::BITARSHIFT_UINT:
424
return "BITARSHIFT_UINT";
425
case IrCmd::BITLROTATE_UINT:
426
return "BITLROTATE_UINT";
427
case IrCmd::BITRROTATE_UINT:
428
return "BITRROTATE_UINT";
429
case IrCmd::BITCOUNTLZ_UINT:
430
return "BITCOUNTLZ_UINT";
431
case IrCmd::BITCOUNTRZ_UINT:
432
return "BITCOUNTRZ_UINT";
433
case IrCmd::BYTESWAP_UINT:
434
return "BYTESWAP_UINT";
435
case IrCmd::INVOKE_LIBM:
436
return "INVOKE_LIBM";
437
case IrCmd::GET_TYPE:
438
return "GET_TYPE";
439
case IrCmd::GET_TYPEOF:
440
return "GET_TYPEOF";
441
case IrCmd::FINDUPVAL:
442
return "FINDUPVAL";
443
case IrCmd::BUFFER_READI8:
444
return "BUFFER_READI8";
445
case IrCmd::BUFFER_READU8:
446
return "BUFFER_READU8";
447
case IrCmd::BUFFER_WRITEI8:
448
return "BUFFER_WRITEI8";
449
case IrCmd::BUFFER_READI16:
450
return "BUFFER_READI16";
451
case IrCmd::BUFFER_READU16:
452
return "BUFFER_READU16";
453
case IrCmd::BUFFER_WRITEI16:
454
return "BUFFER_WRITEI16";
455
case IrCmd::BUFFER_READI32:
456
return "BUFFER_READI32";
457
case IrCmd::BUFFER_WRITEI32:
458
return "BUFFER_WRITEI32";
459
case IrCmd::BUFFER_READF32:
460
return "BUFFER_READF32";
461
case IrCmd::BUFFER_WRITEF32:
462
return "BUFFER_WRITEF32";
463
case IrCmd::BUFFER_READF64:
464
return "BUFFER_READF64";
465
case IrCmd::BUFFER_WRITEF64:
466
return "BUFFER_WRITEF64";
467
}
468
469
LUAU_UNREACHABLE();
470
}
471
472
const char* getBlockKindName(IrBlockKind kind)
473
{
474
switch (kind)
475
{
476
case IrBlockKind::Bytecode:
477
return "bb_bytecode";
478
case IrBlockKind::Fallback:
479
return "bb_fallback";
480
case IrBlockKind::Internal:
481
return "bb";
482
case IrBlockKind::Linearized:
483
return "bb_linear";
484
case IrBlockKind::Dead:
485
return "dead";
486
}
487
488
LUAU_UNREACHABLE();
489
}
490
491
void toString(IrToStringContext& ctx, const IrInst& inst, uint32_t index)
492
{
493
append(ctx.result, " ");
494
495
// Instructions with a result display target virtual register
496
if (hasResult(inst.cmd))
497
append(ctx.result, "%%%u = ", index);
498
499
ctx.result.append(getCmdName(inst.cmd));
500
501
for (size_t i = 0; i < inst.ops.size(); i++)
502
{
503
if (inst.ops[i].kind == IrOpKind::None)
504
continue;
505
ctx.result.append(i == 0 ? " " : ", ");
506
toString(ctx, inst.ops[i]);
507
}
508
}
509
510
void toString(IrToStringContext& ctx, const IrBlock& block, uint32_t index)
511
{
512
append(ctx.result, "%s_%u", getBlockKindName(block.kind), index);
513
}
514
515
static void appendVmConstant(std::string& result, Proto* proto, int index)
516
{
517
TValue constant = proto->k[index];
518
519
if (constant.tt == LUA_TNIL)
520
{
521
append(result, "nil");
522
}
523
else if (constant.tt == LUA_TBOOLEAN)
524
{
525
append(result, constant.value.b != 0 ? "true" : "false");
526
}
527
else if (constant.tt == LUA_TNUMBER)
528
{
529
if (constant.value.n != constant.value.n)
530
append(result, "nan");
531
else
532
append(result, "%.17g", constant.value.n);
533
}
534
else if (constant.tt == LUA_TINTEGER)
535
{
536
append(result, "%lldi", (long long)constant.value.l);
537
}
538
else if (constant.tt == LUA_TSTRING)
539
{
540
TString* str = gco2ts(constant.value.gc);
541
const char* data = getstr(str);
542
543
if (isPrintableStringConstant(data, str->len))
544
{
545
if (str->len < kMaxStringConstantPrintLength)
546
append(result, "'%.*s'", int(str->len), data);
547
else
548
append(result, "'%.*s'...", int(kMaxStringConstantPrintLength), data);
549
}
550
}
551
else if (constant.tt == LUA_TVECTOR)
552
{
553
const float* v = constant.value.v;
554
555
#if LUA_VECTOR_SIZE == 4
556
if (v[3] != 0)
557
append(result, "%.9g, %.9g, %.9g, %.9g", v[0], v[1], v[2], v[3]);
558
else
559
append(result, "%.9g, %.9g, %.9g", v[0], v[1], v[2]);
560
#else
561
append(result, "%.9g, %.9g, %.9g", v[0], v[1], v[2]);
562
#endif
563
}
564
}
565
566
void toString(IrToStringContext& ctx, IrOp op)
567
{
568
switch (op.kind)
569
{
570
case IrOpKind::None:
571
break;
572
case IrOpKind::Undef:
573
append(ctx.result, "undef");
574
break;
575
case IrOpKind::Constant:
576
toString(ctx.result, ctx.proto, ctx.constants[op.index]);
577
break;
578
case IrOpKind::Condition:
579
CODEGEN_ASSERT(op.index < uint32_t(IrCondition::Count));
580
ctx.result.append(textForCondition[op.index]);
581
break;
582
case IrOpKind::Inst:
583
append(ctx.result, "%%%u", op.index);
584
break;
585
case IrOpKind::Block:
586
append(ctx.result, "%s_%u", getBlockKindName(ctx.blocks[op.index].kind), op.index);
587
break;
588
case IrOpKind::VmReg:
589
append(ctx.result, "R%d", vmRegOp(op));
590
break;
591
case IrOpKind::VmConst:
592
append(ctx.result, "K%d", vmConstOp(op));
593
594
if (ctx.proto)
595
{
596
append(ctx.result, " (");
597
appendVmConstant(ctx.result, ctx.proto, vmConstOp(op));
598
append(ctx.result, ")");
599
}
600
601
break;
602
case IrOpKind::VmUpvalue:
603
append(ctx.result, "U%d", vmUpvalueOp(op));
604
break;
605
case IrOpKind::VmExit:
606
if (vmExitOp(op) == kVmExitEntryGuardPc)
607
append(ctx.result, "exit(entry)");
608
else
609
append(ctx.result, "exit(%d)", vmExitOp(op));
610
break;
611
}
612
}
613
614
void toString(std::string& result, Proto* proto, IrConst constant)
615
{
616
switch (constant.kind)
617
{
618
case IrConstKind::Int:
619
append(result, "%di", constant.valueInt);
620
break;
621
case IrConstKind::Uint:
622
append(result, "%uu", constant.valueUint);
623
break;
624
case IrConstKind::Double:
625
if (constant.valueDouble != constant.valueDouble)
626
append(result, "nan");
627
else
628
append(result, "%.17g", constant.valueDouble);
629
break;
630
case IrConstKind::Tag:
631
result.append(getTagName(constant.valueTag));
632
break;
633
case IrConstKind::Import:
634
append(result, "%uu", constant.valueUint);
635
636
if (proto)
637
{
638
append(result, " (");
639
640
int count = constant.valueUint >> 30;
641
int id0 = count > 0 ? int(constant.valueUint >> 20) & 1023 : -1;
642
int id1 = count > 1 ? int(constant.valueUint >> 10) & 1023 : -1;
643
int id2 = count > 2 ? int(constant.valueUint) & 1023 : -1;
644
645
if (id0 != -1)
646
appendVmConstant(result, proto, id0);
647
648
if (id1 != -1)
649
{
650
append(result, ".");
651
appendVmConstant(result, proto, id1);
652
}
653
654
if (id2 != -1)
655
{
656
append(result, ".");
657
appendVmConstant(result, proto, id2);
658
}
659
660
append(result, ")");
661
}
662
break;
663
}
664
}
665
666
const char* getBytecodeTypeName(uint8_t type, const char* const* userdataTypes)
667
{
668
// Optional bit should be handled externally
669
type = type & ~LBC_TYPE_OPTIONAL_BIT;
670
671
if (type >= LBC_TYPE_TAGGED_USERDATA_BASE && type < LBC_TYPE_TAGGED_USERDATA_END)
672
{
673
if (userdataTypes)
674
return userdataTypes[type - LBC_TYPE_TAGGED_USERDATA_BASE];
675
676
return "userdata";
677
}
678
679
switch (type)
680
{
681
case LBC_TYPE_NIL:
682
return "nil";
683
case LBC_TYPE_BOOLEAN:
684
return "boolean";
685
case LBC_TYPE_NUMBER:
686
return "number";
687
case LBC_TYPE_INTEGER:
688
return "integer";
689
case LBC_TYPE_STRING:
690
return "string";
691
case LBC_TYPE_TABLE:
692
return "table";
693
case LBC_TYPE_FUNCTION:
694
return "function";
695
case LBC_TYPE_THREAD:
696
return "thread";
697
case LBC_TYPE_USERDATA:
698
return "userdata";
699
case LBC_TYPE_VECTOR:
700
return "vector";
701
case LBC_TYPE_BUFFER:
702
return "buffer";
703
case LBC_TYPE_ANY:
704
return "any";
705
}
706
707
CODEGEN_ASSERT(!"Unhandled type in getBytecodeTypeName");
708
return nullptr;
709
}
710
711
void toString(std::string& result, const BytecodeTypes& bcTypes, const char* const* userdataTypes)
712
{
713
append(result, "%s%s", getBytecodeTypeName(bcTypes.result, userdataTypes), (bcTypes.result & LBC_TYPE_OPTIONAL_BIT) != 0 ? "?" : "");
714
append(result, " <- ");
715
append(result, "%s%s", getBytecodeTypeName(bcTypes.a, userdataTypes), (bcTypes.a & LBC_TYPE_OPTIONAL_BIT) != 0 ? "?" : "");
716
append(result, ", ");
717
append(result, "%s%s", getBytecodeTypeName(bcTypes.b, userdataTypes), (bcTypes.b & LBC_TYPE_OPTIONAL_BIT) != 0 ? "?" : "");
718
719
if (bcTypes.c != LBC_TYPE_ANY)
720
{
721
append(result, ", ");
722
append(result, "%s%s", getBytecodeTypeName(bcTypes.c, userdataTypes), (bcTypes.c & LBC_TYPE_OPTIONAL_BIT) != 0 ? "?" : "");
723
}
724
}
725
726
static void appendBlockSet(IrToStringContext& ctx, BlockIteratorWrapper blocks)
727
{
728
bool comma = false;
729
730
for (uint32_t target : blocks)
731
{
732
if (comma)
733
append(ctx.result, ", ");
734
comma = true;
735
736
toString(ctx, ctx.blocks[target], target);
737
}
738
}
739
740
static void appendRegisterSet(IrToStringContext& ctx, const RegisterSet& rs, const char* separator)
741
{
742
bool comma = false;
743
744
for (size_t i = 0; i < rs.regs.size(); i++)
745
{
746
if (rs.regs.test(i))
747
{
748
if (comma)
749
ctx.result.append(separator);
750
comma = true;
751
752
append(ctx.result, "R%d", int(i));
753
}
754
}
755
756
if (rs.varargSeq)
757
{
758
if (comma)
759
ctx.result.append(separator);
760
761
append(ctx.result, "R%d...", rs.varargStart);
762
}
763
}
764
765
static RegisterSet getJumpTargetExtraLiveIn(IrToStringContext& ctx, const IrBlock& block, uint32_t blockIdx, IrInst& inst)
766
{
767
RegisterSet extraRs;
768
769
if (blockIdx >= ctx.cfg.in.size())
770
return extraRs;
771
772
const RegisterSet& defRs = ctx.cfg.in[blockIdx];
773
774
// Find first block argument, for guard instructions (isNonTerminatingJump), that's the first and only one
775
CODEGEN_ASSERT(isNonTerminatingJump(inst.cmd));
776
IrOp op = OP_A(inst);
777
778
for (size_t i = 1; i < inst.ops.size(); i++)
779
if (inst.ops[i].kind == IrOpKind::Block)
780
{
781
op = inst.ops[i];
782
break;
783
}
784
785
if (op.kind == IrOpKind::Block && op.index < ctx.cfg.in.size())
786
{
787
const RegisterSet& inRs = ctx.cfg.in[op.index];
788
789
extraRs.regs = inRs.regs & ~defRs.regs;
790
791
if (inRs.varargSeq)
792
requireVariadicSequence(extraRs, defRs, inRs.varargStart);
793
}
794
795
return extraRs;
796
}
797
798
void toStringDetailed(IrToStringContext& ctx, const IrBlock& block, uint32_t blockIdx, IrInst& inst, uint32_t instIdx, IncludeUseInfo includeUseInfo)
799
{
800
size_t start = ctx.result.size();
801
802
toString(ctx, inst, instIdx);
803
804
if (includeUseInfo == IncludeUseInfo::Yes)
805
{
806
padToDetailColumn(ctx.result, start);
807
808
if (inst.useCount == 0 && hasSideEffects(inst.cmd))
809
{
810
if (isNonTerminatingJump(inst.cmd))
811
{
812
RegisterSet extraRs = getJumpTargetExtraLiveIn(ctx, block, blockIdx, inst);
813
814
if (extraRs.regs.any() || extraRs.varargSeq)
815
{
816
append(ctx.result, "; %%%u, extra in: ", instIdx);
817
appendRegisterSet(ctx, extraRs, ", ");
818
ctx.result.append("\n");
819
}
820
else
821
{
822
append(ctx.result, "; %%%u\n", instIdx);
823
}
824
}
825
else
826
{
827
append(ctx.result, "; %%%u\n", instIdx);
828
}
829
}
830
else
831
{
832
append(ctx.result, "; useCount: %d, lastUse: %%%u\n", inst.useCount, inst.lastUse);
833
}
834
}
835
else
836
{
837
ctx.result.append("\n");
838
}
839
}
840
841
void toStringDetailed(
842
IrToStringContext& ctx,
843
const IrBlock& block,
844
uint32_t blockIdx,
845
IncludeUseInfo includeUseInfo,
846
IncludeCfgInfo includeCfgInfo,
847
IncludeRegFlowInfo includeRegFlowInfo
848
)
849
{
850
// Report captured registers for entry block
851
if (includeRegFlowInfo == IncludeRegFlowInfo::Yes && isEntryBlock(block) && ctx.cfg.captured.regs.any())
852
{
853
append(ctx.result, "; captured regs: ");
854
appendRegisterSet(ctx, ctx.cfg.captured, ", ");
855
append(ctx.result, "\n\n");
856
}
857
858
size_t start = ctx.result.size();
859
860
toString(ctx, block, blockIdx);
861
append(ctx.result, ":");
862
863
if (includeUseInfo == IncludeUseInfo::Yes)
864
{
865
padToDetailColumn(ctx.result, start);
866
867
append(ctx.result, "; useCount: %d\n", block.useCount);
868
}
869
else
870
{
871
ctx.result.append("\n");
872
}
873
874
// Predecessor list
875
if (includeCfgInfo == IncludeCfgInfo::Yes && blockIdx < ctx.cfg.predecessorsOffsets.size())
876
{
877
BlockIteratorWrapper pred = predecessors(ctx.cfg, blockIdx);
878
879
if (!pred.empty())
880
{
881
append(ctx.result, "; predecessors: ");
882
883
appendBlockSet(ctx, pred);
884
append(ctx.result, "\n");
885
}
886
}
887
888
// Successor list
889
if (includeCfgInfo == IncludeCfgInfo::Yes && blockIdx < ctx.cfg.successorsOffsets.size())
890
{
891
BlockIteratorWrapper succ = successors(ctx.cfg, blockIdx);
892
893
if (!succ.empty())
894
{
895
append(ctx.result, "; successors: ");
896
897
appendBlockSet(ctx, succ);
898
append(ctx.result, "\n");
899
}
900
}
901
902
// Live-in VM regs
903
if (includeRegFlowInfo == IncludeRegFlowInfo::Yes && blockIdx < ctx.cfg.in.size())
904
{
905
const RegisterSet& in = ctx.cfg.in[blockIdx];
906
907
if (in.regs.any() || in.varargSeq)
908
{
909
append(ctx.result, "; in regs: ");
910
appendRegisterSet(ctx, in, ", ");
911
append(ctx.result, "\n");
912
}
913
}
914
915
// Live-out VM regs
916
if (includeRegFlowInfo == IncludeRegFlowInfo::Yes && blockIdx < ctx.cfg.out.size())
917
{
918
const RegisterSet& out = ctx.cfg.out[blockIdx];
919
920
if (out.regs.any() || out.varargSeq)
921
{
922
append(ctx.result, "; out regs: ");
923
appendRegisterSet(ctx, out, ", ");
924
append(ctx.result, "\n");
925
}
926
}
927
}
928
929
std::string toString(IrFunction& function, IncludeUseInfo includeUseInfo)
930
{
931
std::string result;
932
IrToStringContext ctx{result, function.blocks, function.constants, function.cfg, function.proto};
933
934
for (size_t i = 0; i < function.blocks.size(); i++)
935
{
936
const IrBlock& block = function.blocks[i];
937
938
if (block.kind == IrBlockKind::Dead)
939
continue;
940
941
toStringDetailed(ctx, block, uint32_t(i), includeUseInfo, IncludeCfgInfo::Yes, IncludeRegFlowInfo::Yes);
942
943
if (block.start == ~0u)
944
{
945
append(ctx.result, " *empty*\n\n");
946
continue;
947
}
948
949
if ((block.flags & kBlockFlagSafeEnvCheck) != 0)
950
append(ctx.result, " implicit CHECK_SAFE_ENV exit(%u)\n", block.startpc);
951
952
// To allow dumping blocks that are still being constructed, we can't rely on terminator and need a bounds check
953
for (uint32_t index = block.start; index <= block.finish && index < uint32_t(function.instructions.size()); index++)
954
{
955
IrInst& inst = function.instructions[index];
956
957
// Skip pseudo instructions unless they are still referenced
958
if (isPseudo(inst.cmd) && inst.useCount == 0)
959
continue;
960
961
append(ctx.result, " ");
962
toStringDetailed(ctx, block, uint32_t(i), inst, index, includeUseInfo);
963
}
964
965
if (block.expectedNextBlock != ~0u)
966
{
967
append(ctx.result, "; glued to: ");
968
toString(ctx, ctx.blocks[block.expectedNextBlock], block.expectedNextBlock);
969
append(ctx.result, "\n");
970
}
971
972
append(ctx.result, "\n");
973
}
974
975
return result;
976
}
977
978
std::string dump(IrFunction& function)
979
{
980
std::string result = toString(function, IncludeUseInfo::Yes);
981
982
printf("%s\n", result.c_str());
983
984
return result;
985
}
986
987
static void appendLabelRegset(IrToStringContext& ctx, const std::vector<RegisterSet>& regSets, size_t blockIdx, const char* name)
988
{
989
if (blockIdx < regSets.size())
990
{
991
const RegisterSet& rs = regSets[blockIdx];
992
993
if (rs.regs.any() || rs.varargSeq)
994
{
995
append(ctx.result, "|{%s|", name);
996
appendRegisterSet(ctx, rs, "|");
997
append(ctx.result, "}");
998
}
999
}
1000
}
1001
1002
static void appendBlocks(IrToStringContext& ctx, const IrFunction& function, bool includeInst, bool includeIn, bool includeOut, bool includeDef)
1003
{
1004
for (size_t i = 0; i < function.blocks.size(); i++)
1005
{
1006
const IrBlock& block = function.blocks[i];
1007
1008
append(ctx.result, "b%u [", unsigned(i));
1009
1010
if (block.kind == IrBlockKind::Fallback)
1011
append(ctx.result, "style=filled;fillcolor=salmon;");
1012
else if (block.kind == IrBlockKind::Bytecode)
1013
append(ctx.result, "style=filled;fillcolor=palegreen;");
1014
1015
append(ctx.result, "label=\"{");
1016
toString(ctx, block, uint32_t(i));
1017
1018
if (includeIn)
1019
appendLabelRegset(ctx, ctx.cfg.in, i, "in");
1020
1021
if (includeInst && block.start != ~0u)
1022
{
1023
for (uint32_t instIdx = block.start; instIdx <= block.finish; instIdx++)
1024
{
1025
const IrInst& inst = function.instructions[instIdx];
1026
1027
// Skip pseudo instructions unless they are still referenced
1028
if (isPseudo(inst.cmd) && inst.useCount == 0)
1029
continue;
1030
1031
append(ctx.result, "|");
1032
toString(ctx, inst, instIdx);
1033
}
1034
}
1035
1036
if (includeDef)
1037
appendLabelRegset(ctx, ctx.cfg.def, i, "def");
1038
1039
if (includeOut)
1040
appendLabelRegset(ctx, ctx.cfg.out, i, "out");
1041
1042
append(ctx.result, "}\"];\n");
1043
}
1044
}
1045
1046
std::string toDot(const IrFunction& function, bool includeInst)
1047
{
1048
std::string result;
1049
IrToStringContext ctx{result, function.blocks, function.constants, function.cfg, function.proto};
1050
1051
append(ctx.result, "digraph CFG {\n");
1052
append(ctx.result, "node[shape=record]\n");
1053
1054
appendBlocks(ctx, function, includeInst, /* includeIn */ true, /* includeOut */ true, /* includeDef */ true);
1055
1056
for (size_t i = 0; i < function.blocks.size(); i++)
1057
{
1058
const IrBlock& block = function.blocks[i];
1059
1060
if (block.start == ~0u)
1061
continue;
1062
1063
for (uint32_t instIdx = block.start; instIdx != ~0u && instIdx <= block.finish; instIdx++)
1064
{
1065
const IrInst& inst = function.instructions[instIdx];
1066
1067
auto checkOp = [&](IrOp op)
1068
{
1069
if (op.kind == IrOpKind::Block)
1070
{
1071
if (function.blocks[op.index].kind != IrBlockKind::Fallback)
1072
append(ctx.result, "b%u -> b%u [weight=10];\n", unsigned(i), op.index);
1073
else
1074
append(ctx.result, "b%u -> b%u;\n", unsigned(i), op.index);
1075
}
1076
};
1077
1078
for (auto& op : inst.ops)
1079
checkOp(op);
1080
}
1081
}
1082
1083
append(ctx.result, "}\n");
1084
1085
return result;
1086
}
1087
1088
std::string toDotCfg(const IrFunction& function)
1089
{
1090
std::string result;
1091
IrToStringContext ctx{result, function.blocks, function.constants, function.cfg, function.proto};
1092
1093
append(ctx.result, "digraph CFG {\n");
1094
append(ctx.result, "node[shape=record]\n");
1095
1096
appendBlocks(ctx, function, /* includeInst */ false, /* includeIn */ false, /* includeOut */ false, /* includeDef */ true);
1097
1098
for (size_t i = 0; i < function.blocks.size() && i < ctx.cfg.successorsOffsets.size(); i++)
1099
{
1100
BlockIteratorWrapper succ = successors(ctx.cfg, unsigned(i));
1101
1102
for (uint32_t target : succ)
1103
append(ctx.result, "b%u -> b%u;\n", unsigned(i), target);
1104
}
1105
1106
append(ctx.result, "}\n");
1107
1108
return result;
1109
}
1110
1111
std::string toDotDjGraph(const IrFunction& function)
1112
{
1113
std::string result;
1114
IrToStringContext ctx{result, function.blocks, function.constants, function.cfg, function.proto};
1115
1116
append(ctx.result, "digraph CFG {\n");
1117
1118
for (size_t i = 0; i < ctx.blocks.size(); i++)
1119
{
1120
const IrBlock& block = ctx.blocks[i];
1121
1122
append(ctx.result, "b%u [", unsigned(i));
1123
1124
if (block.kind == IrBlockKind::Fallback)
1125
append(ctx.result, "style=filled;fillcolor=salmon;");
1126
else if (block.kind == IrBlockKind::Bytecode)
1127
append(ctx.result, "style=filled;fillcolor=palegreen;");
1128
1129
append(ctx.result, "label=\"");
1130
toString(ctx, block, uint32_t(i));
1131
append(ctx.result, "\"];\n");
1132
}
1133
1134
// Layer by depth in tree
1135
uint32_t depth = 0;
1136
bool found = true;
1137
1138
while (found)
1139
{
1140
found = false;
1141
1142
append(ctx.result, "{rank = same;");
1143
for (size_t i = 0; i < ctx.cfg.domOrdering.size(); i++)
1144
{
1145
if (ctx.cfg.domOrdering[i].depth == depth)
1146
{
1147
append(ctx.result, "b%u;", unsigned(i));
1148
found = true;
1149
}
1150
}
1151
append(ctx.result, "}\n");
1152
1153
depth++;
1154
}
1155
1156
for (size_t i = 0; i < ctx.cfg.domChildrenOffsets.size(); i++)
1157
{
1158
BlockIteratorWrapper dom = domChildren(ctx.cfg, unsigned(i));
1159
1160
for (uint32_t target : dom)
1161
append(ctx.result, "b%u -> b%u;\n", unsigned(i), target);
1162
1163
// Join edges are all successor edges that do not strongly dominate
1164
BlockIteratorWrapper succ = successors(ctx.cfg, unsigned(i));
1165
1166
for (uint32_t successor : succ)
1167
{
1168
bool found = false;
1169
1170
for (uint32_t target : dom)
1171
{
1172
if (target == successor)
1173
{
1174
found = true;
1175
break;
1176
}
1177
}
1178
1179
if (!found)
1180
append(ctx.result, "b%u -> b%u [style=dotted];\n", unsigned(i), successor);
1181
}
1182
}
1183
1184
append(ctx.result, "}\n");
1185
1186
return result;
1187
}
1188
1189
std::string dumpDot(const IrFunction& function, bool includeInst)
1190
{
1191
std::string result = toDot(function, includeInst);
1192
1193
printf("%s\n", result.c_str());
1194
1195
return result;
1196
}
1197
1198
} // namespace CodeGen
1199
} // namespace Luau
1200
1201