Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Compiler/src/BytecodeBuilder.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/BytecodeBuilder.h"
3
4
#include "Luau/BytecodeUtils.h"
5
#include "Luau/StringUtils.h"
6
7
#include <algorithm>
8
#include <string.h>
9
#include <climits>
10
11
LUAU_FASTFLAG(LuauCompileDuptableConstantPack2)
12
LUAU_FASTFLAG(LuauIntegerType)
13
14
namespace Luau
15
{
16
17
static_assert(LBC_VERSION_TARGET >= LBC_VERSION_MIN && LBC_VERSION_TARGET <= LBC_VERSION_MAX, "Invalid bytecode version setup");
18
static_assert(LBC_VERSION_MAX <= 127, "Bytecode version should be 7-bit so that we can extend the serialization to use varint transparently");
19
20
static const uint32_t kMaxConstantCount = 1 << 23;
21
static const uint32_t kMaxClosureCount = 1 << 15;
22
23
static const int kMaxJumpDistance = 1 << 23;
24
25
static int log2(int v)
26
{
27
LUAU_ASSERT(v);
28
29
int r = 0;
30
31
while (v >= (2 << r))
32
r++;
33
34
return r;
35
}
36
37
static void writeByte(std::string& ss, unsigned char value)
38
{
39
ss.append(reinterpret_cast<const char*>(&value), sizeof(value));
40
}
41
42
static void writeInt(std::string& ss, int value)
43
{
44
ss.append(reinterpret_cast<const char*>(&value), sizeof(value));
45
}
46
47
static void writeFloat(std::string& ss, float value)
48
{
49
ss.append(reinterpret_cast<const char*>(&value), sizeof(value));
50
}
51
52
static void writeDouble(std::string& ss, double value)
53
{
54
ss.append(reinterpret_cast<const char*>(&value), sizeof(value));
55
}
56
57
static void writeVarInt(std::string& ss, uint64_t value)
58
{
59
do
60
{
61
writeByte(ss, (value & 127) | ((value > 127) << 7));
62
value >>= 7;
63
} while (value);
64
}
65
66
inline bool isJumpD(LuauOpcode op)
67
{
68
switch (op)
69
{
70
case LOP_JUMP:
71
case LOP_JUMPIF:
72
case LOP_JUMPIFNOT:
73
case LOP_JUMPIFEQ:
74
case LOP_JUMPIFLE:
75
case LOP_JUMPIFLT:
76
case LOP_JUMPIFNOTEQ:
77
case LOP_JUMPIFNOTLE:
78
case LOP_JUMPIFNOTLT:
79
case LOP_FORNPREP:
80
case LOP_FORNLOOP:
81
case LOP_FORGPREP:
82
case LOP_FORGLOOP:
83
case LOP_FORGPREP_INEXT:
84
case LOP_FORGPREP_NEXT:
85
case LOP_JUMPBACK:
86
case LOP_JUMPXEQKNIL:
87
case LOP_JUMPXEQKB:
88
case LOP_JUMPXEQKN:
89
case LOP_JUMPXEQKS:
90
return true;
91
92
default:
93
return false;
94
}
95
}
96
97
inline bool isSkipC(LuauOpcode op)
98
{
99
switch (op)
100
{
101
case LOP_LOADB:
102
return true;
103
104
default:
105
return false;
106
}
107
}
108
109
inline bool isFastCall(LuauOpcode op)
110
{
111
switch (op)
112
{
113
case LOP_FASTCALL:
114
case LOP_FASTCALL1:
115
case LOP_FASTCALL2:
116
case LOP_FASTCALL2K:
117
case LOP_FASTCALL3:
118
return true;
119
120
default:
121
return false;
122
}
123
}
124
125
static int getJumpTarget(uint32_t insn, uint32_t pc)
126
{
127
LuauOpcode op = LuauOpcode(LUAU_INSN_OP(insn));
128
129
if (isJumpD(op))
130
return int(pc + LUAU_INSN_D(insn) + 1);
131
else if (isFastCall(op))
132
return int(pc + LUAU_INSN_C(insn) + 2);
133
else if (isSkipC(op) && LUAU_INSN_C(insn))
134
return int(pc + LUAU_INSN_C(insn) + 1);
135
else if (op == LOP_JUMPX)
136
return int(pc + LUAU_INSN_E(insn) + 1);
137
else
138
return -1;
139
}
140
141
bool BytecodeBuilder::StringRef::operator==(const StringRef& other) const
142
{
143
return (data && other.data) ? (length == other.length && memcmp(data, other.data, length) == 0) : (data == other.data);
144
}
145
146
bool BytecodeBuilder::TableShape::operator==(const TableShape& other) const
147
{
148
if (!FFlag::LuauCompileDuptableConstantPack2)
149
{
150
151
return length == other.length && memcmp(keys, other.keys, length * sizeof(keys[0])) == 0;
152
}
153
else
154
{
155
bool equal = length == other.length && memcmp(keys, other.keys, length * sizeof(keys[0])) == 0 && hasConstants == other.hasConstants;
156
157
if (hasConstants)
158
{
159
equal = equal && memcmp(constants, other.constants, length * sizeof(constants[0])) == 0;
160
}
161
162
return equal;
163
}
164
}
165
166
size_t BytecodeBuilder::StringRefHash::operator()(const StringRef& v) const
167
{
168
return hashRange(v.data, v.length);
169
}
170
171
size_t BytecodeBuilder::ConstantKeyHash::operator()(const ConstantKey& key) const
172
{
173
if (key.type == Constant::Type_Vector)
174
{
175
uint32_t i[4];
176
static_assert(sizeof(key.value) + sizeof(key.extra) == sizeof(i), "Expecting vector to have four 32-bit components");
177
memcpy(i, &key.value, sizeof(i));
178
179
// scramble bits to make sure that integer coordinates have entropy in lower bits
180
i[0] ^= i[0] >> 17;
181
i[1] ^= i[1] >> 17;
182
i[2] ^= i[2] >> 17;
183
i[3] ^= i[3] >> 17;
184
185
// Optimized Spatial Hashing for Collision Detection of Deformable Objects
186
uint32_t h = (i[0] * 73856093) ^ (i[1] * 19349663) ^ (i[2] * 83492791) ^ (i[3] * 39916801);
187
188
return size_t(h);
189
}
190
else
191
{
192
// finalizer from MurmurHash64B
193
const uint32_t m = 0x5bd1e995;
194
195
uint32_t h1 = uint32_t(key.value);
196
uint32_t h2 = uint32_t(key.value >> 32) ^ (key.type * m);
197
198
h1 ^= h2 >> 18;
199
h1 *= m;
200
h2 ^= h1 >> 22;
201
h2 *= m;
202
h1 ^= h2 >> 17;
203
h1 *= m;
204
h2 ^= h1 >> 19;
205
h2 *= m;
206
207
// ... truncated to 32-bit output (normally hash is equal to (uint64_t(h1) << 32) | h2, but we only really need the lower 32-bit half)
208
return size_t(h2);
209
}
210
}
211
212
size_t BytecodeBuilder::TableShapeHash::operator()(const TableShape& v) const
213
{
214
// FNV-1a inspired hash (note that we feed integers instead of bytes)
215
uint32_t hash = 2166136261;
216
217
for (size_t i = 0; i < v.length; ++i)
218
{
219
hash ^= v.keys[i];
220
hash *= 16777619;
221
222
if (FFlag::LuauCompileDuptableConstantPack2 && v.hasConstants)
223
{
224
hash ^= v.constants[i];
225
hash *= 16777619;
226
}
227
}
228
229
return hash;
230
}
231
232
BytecodeBuilder::BytecodeBuilder(BytecodeEncoder* encoder)
233
: constantMap({Constant::Type_Nil, ~0ull})
234
, tableShapeMap(TableShape())
235
, protoMap(~0u)
236
, stringTable({nullptr, 0})
237
, encoder(encoder)
238
{
239
LUAU_ASSERT(stringTable.find(StringRef{"", 0}) == nullptr);
240
241
// preallocate some buffers that are very likely to grow anyway; this works around std::vector's inefficient growth policy for small arrays
242
insns.reserve(32);
243
lines.reserve(32);
244
constants.reserve(16);
245
protos.reserve(16);
246
functions.reserve(8);
247
}
248
249
uint32_t BytecodeBuilder::beginFunction(uint8_t numparams, bool isvararg)
250
{
251
LUAU_ASSERT(currentFunction == ~0u);
252
253
uint32_t id = uint32_t(functions.size());
254
255
Function func;
256
func.numparams = numparams;
257
func.isvararg = isvararg;
258
259
functions.push_back(func);
260
261
currentFunction = id;
262
263
hasLongJumps = false;
264
debugLine = 0;
265
266
return id;
267
}
268
269
void BytecodeBuilder::endFunction(uint8_t maxstacksize, uint8_t numupvalues, uint8_t flags)
270
{
271
LUAU_ASSERT(currentFunction != ~0u);
272
273
Function& func = functions[currentFunction];
274
275
func.maxstacksize = maxstacksize;
276
func.numupvalues = numupvalues;
277
278
#ifdef LUAU_ASSERTENABLED
279
validate();
280
#endif
281
282
// this call is indirect to make sure we only gain link time dependency on dumpCurrentFunction when needed
283
if (dumpFunctionPtr)
284
func.dump = (this->*dumpFunctionPtr)(func.dumpinstoffs);
285
286
// very approximate: 4 bytes per instruction for code, 1 byte for debug line, and 1-2 bytes for aux data like constants plus overhead
287
func.data.reserve(32 + insns.size() * 7);
288
289
if (encoder)
290
encoder->encode(insns.data(), insns.size());
291
292
writeFunction(func.data, currentFunction, flags);
293
294
currentFunction = ~0u;
295
296
totalInstructionCount += insns.size();
297
insns.clear();
298
lines.clear();
299
constants.clear();
300
protos.clear();
301
jumps.clear();
302
tableShapes.clear();
303
304
debugLocals.clear();
305
debugUpvals.clear();
306
307
typedLocals.clear();
308
typedUpvals.clear();
309
310
constantMap.clear();
311
tableShapeMap.clear();
312
protoMap.clear();
313
314
debugRemarks.clear();
315
debugRemarkBuffer.clear();
316
}
317
318
void BytecodeBuilder::setMainFunction(uint32_t fid)
319
{
320
LUAU_ASSERT(fid < functions.size());
321
322
mainFunction = fid;
323
}
324
325
int32_t BytecodeBuilder::addConstant(const ConstantKey& key, const Constant& value)
326
{
327
if (int32_t* cache = constantMap.find(key))
328
return *cache;
329
330
uint32_t id = uint32_t(constants.size());
331
332
if (id >= kMaxConstantCount)
333
return -1;
334
335
constantMap[key] = int32_t(id);
336
constants.push_back(value);
337
338
return int32_t(id);
339
}
340
341
unsigned int BytecodeBuilder::addStringTableEntry(StringRef value)
342
{
343
unsigned int& index = stringTable[value];
344
345
// note: bytecode serialization format uses 1-based table indices, 0 is reserved to mean nil
346
if (index == 0)
347
{
348
index = uint32_t(stringTable.size());
349
350
if ((dumpFlags & Dump_Code) != 0)
351
debugStrings.push_back(value);
352
}
353
354
return index;
355
}
356
357
const char* BytecodeBuilder::tryGetUserdataTypeName(LuauBytecodeType type) const
358
{
359
unsigned index = unsigned((type & ~LBC_TYPE_OPTIONAL_BIT) - LBC_TYPE_TAGGED_USERDATA_BASE);
360
361
if (index < userdataTypes.size())
362
return userdataTypes[index].name.c_str();
363
364
return nullptr;
365
}
366
367
int32_t BytecodeBuilder::addConstantNil()
368
{
369
Constant c = {Constant::Type_Nil};
370
371
ConstantKey k = {Constant::Type_Nil};
372
return addConstant(k, c);
373
}
374
375
int32_t BytecodeBuilder::addConstantBoolean(bool value)
376
{
377
Constant c = {Constant::Type_Boolean};
378
c.valueBoolean = value;
379
380
ConstantKey k = {Constant::Type_Boolean, value};
381
return addConstant(k, c);
382
}
383
384
int32_t BytecodeBuilder::addConstantNumber(double value)
385
{
386
Constant c = {Constant::Type_Number};
387
c.valueNumber = value;
388
389
ConstantKey k = {Constant::Type_Number};
390
static_assert(sizeof(k.value) == sizeof(value), "Expecting double to be 64-bit");
391
memcpy(&k.value, &value, sizeof(value));
392
393
return addConstant(k, c);
394
}
395
396
int32_t BytecodeBuilder::addConstantInteger(int64_t value)
397
{
398
Constant c = {Constant::Type_Integer};
399
c.valueInteger64 = value;
400
401
ConstantKey k = {Constant::Type_Integer};
402
static_assert(sizeof(k.value) == sizeof(value), "Expecting integer to be 64-bit");
403
memcpy(&k.value, &value, sizeof(value));
404
405
return addConstant(k, c);
406
}
407
408
int32_t BytecodeBuilder::addConstantVector(float x, float y, float z, float w)
409
{
410
Constant c = {Constant::Type_Vector};
411
c.valueVector[0] = x;
412
c.valueVector[1] = y;
413
c.valueVector[2] = z;
414
c.valueVector[3] = w;
415
416
ConstantKey k = {Constant::Type_Vector};
417
static_assert(
418
sizeof(k.value) == sizeof(x) + sizeof(y) && sizeof(k.extra) == sizeof(z) + sizeof(w), "Expecting vector to have four 32-bit components"
419
);
420
memcpy(&k.value, &x, sizeof(x));
421
memcpy((char*)&k.value + sizeof(x), &y, sizeof(y));
422
memcpy(&k.extra, &z, sizeof(z));
423
memcpy((char*)&k.extra + sizeof(z), &w, sizeof(w));
424
425
return addConstant(k, c);
426
}
427
428
int32_t BytecodeBuilder::addConstantString(StringRef value)
429
{
430
unsigned int index = addStringTableEntry(value);
431
432
Constant c = {Constant::Type_String};
433
c.valueString = index;
434
435
ConstantKey k = {Constant::Type_String, index};
436
437
return addConstant(k, c);
438
}
439
440
int32_t BytecodeBuilder::addImport(uint32_t iid)
441
{
442
Constant c = {Constant::Type_Import};
443
c.valueImport = iid;
444
445
ConstantKey k = {Constant::Type_Import, iid};
446
447
return addConstant(k, c);
448
}
449
450
int32_t BytecodeBuilder::addConstantTable(const TableShape& shape)
451
{
452
if (int32_t* cache = tableShapeMap.find(shape))
453
return *cache;
454
455
uint32_t id = uint32_t(constants.size());
456
457
if (id >= kMaxConstantCount)
458
return -1;
459
460
Constant value = {Constant::Type_Table};
461
value.valueTable = uint32_t(tableShapes.size());
462
463
tableShapeMap[shape] = int32_t(id);
464
tableShapes.push_back(shape);
465
constants.push_back(value);
466
467
return int32_t(id);
468
}
469
470
int32_t BytecodeBuilder::addConstantClosure(uint32_t fid)
471
{
472
Constant c = {Constant::Type_Closure};
473
c.valueClosure = fid;
474
475
ConstantKey k = {Constant::Type_Closure, fid};
476
477
return addConstant(k, c);
478
}
479
480
int16_t BytecodeBuilder::addChildFunction(uint32_t fid)
481
{
482
if (int16_t* cache = protoMap.find(fid))
483
return *cache;
484
485
uint32_t id = uint32_t(protos.size());
486
487
if (id >= kMaxClosureCount)
488
return -1;
489
490
protoMap[fid] = int16_t(id);
491
protos.push_back(fid);
492
493
return int16_t(id);
494
}
495
496
void BytecodeBuilder::emitABC(LuauOpcode op, uint8_t a, uint8_t b, uint8_t c)
497
{
498
uint32_t insn = uint32_t(op) | (a << 8) | (b << 16) | (c << 24);
499
500
insns.push_back(insn);
501
lines.push_back(debugLine);
502
}
503
504
void BytecodeBuilder::emitAD(LuauOpcode op, uint8_t a, int16_t d)
505
{
506
uint32_t insn = uint32_t(op) | (a << 8) | (uint16_t(d) << 16);
507
508
insns.push_back(insn);
509
lines.push_back(debugLine);
510
}
511
512
void BytecodeBuilder::emitE(LuauOpcode op, int32_t e)
513
{
514
uint32_t insn = uint32_t(op) | (uint32_t(e) << 8);
515
516
insns.push_back(insn);
517
lines.push_back(debugLine);
518
}
519
520
void BytecodeBuilder::emitAux(uint32_t aux)
521
{
522
insns.push_back(aux);
523
lines.push_back(debugLine);
524
}
525
526
void BytecodeBuilder::undoEmit(LuauOpcode op)
527
{
528
LUAU_ASSERT(!insns.empty());
529
LUAU_ASSERT((insns.back() & 0xff) == op);
530
531
insns.pop_back();
532
lines.pop_back();
533
}
534
535
size_t BytecodeBuilder::emitLabel()
536
{
537
return insns.size();
538
}
539
540
bool BytecodeBuilder::patchJumpD(size_t jumpLabel, size_t targetLabel)
541
{
542
LUAU_ASSERT(jumpLabel < insns.size());
543
544
unsigned int jumpInsn = insns[jumpLabel];
545
(void)jumpInsn;
546
547
LUAU_ASSERT(isJumpD(LuauOpcode(LUAU_INSN_OP(jumpInsn))));
548
LUAU_ASSERT(LUAU_INSN_D(jumpInsn) == 0);
549
550
LUAU_ASSERT(targetLabel <= insns.size());
551
552
int offset = int(targetLabel) - int(jumpLabel) - 1;
553
554
if (int16_t(offset) == offset)
555
{
556
insns[jumpLabel] |= uint16_t(offset) << 16;
557
}
558
else if (abs(offset) < kMaxJumpDistance)
559
{
560
// our jump doesn't fit into 16 bits; we will need to repatch the bytecode sequence with jump trampolines, see expandJumps
561
hasLongJumps = true;
562
}
563
else
564
{
565
return false;
566
}
567
568
jumps.push_back({uint32_t(jumpLabel), uint32_t(targetLabel)});
569
return true;
570
}
571
572
bool BytecodeBuilder::patchSkipC(size_t jumpLabel, size_t targetLabel)
573
{
574
LUAU_ASSERT(jumpLabel < insns.size());
575
576
unsigned int jumpInsn = insns[jumpLabel];
577
(void)jumpInsn;
578
579
LUAU_ASSERT(isSkipC(LuauOpcode(LUAU_INSN_OP(jumpInsn))) || isFastCall(LuauOpcode(LUAU_INSN_OP(jumpInsn))));
580
LUAU_ASSERT(LUAU_INSN_C(jumpInsn) == 0);
581
582
int offset = int(targetLabel) - int(jumpLabel) - 1;
583
584
if (uint8_t(offset) != offset)
585
{
586
return false;
587
}
588
589
insns[jumpLabel] |= offset << 24;
590
return true;
591
}
592
593
void BytecodeBuilder::setFunctionTypeInfo(std::string value)
594
{
595
functions[currentFunction].typeinfo = std::move(value);
596
}
597
598
void BytecodeBuilder::pushLocalTypeInfo(LuauBytecodeType type, uint8_t reg, uint32_t startpc, uint32_t endpc)
599
{
600
TypedLocal local;
601
local.type = type;
602
local.reg = reg;
603
local.startpc = startpc;
604
local.endpc = endpc;
605
606
typedLocals.push_back(local);
607
}
608
609
void BytecodeBuilder::pushUpvalTypeInfo(LuauBytecodeType type)
610
{
611
TypedUpval upval;
612
upval.type = type;
613
614
typedUpvals.push_back(upval);
615
}
616
617
uint32_t BytecodeBuilder::addUserdataType(const char* name)
618
{
619
UserdataType ty;
620
621
ty.name = name;
622
623
userdataTypes.push_back(std::move(ty));
624
return uint32_t(userdataTypes.size() - 1);
625
}
626
627
void BytecodeBuilder::useUserdataType(uint32_t index)
628
{
629
userdataTypes[index].used = true;
630
}
631
632
void BytecodeBuilder::setDebugFunctionName(StringRef name)
633
{
634
unsigned int index = addStringTableEntry(name);
635
636
functions[currentFunction].debugname = index;
637
638
if (dumpFunctionPtr)
639
functions[currentFunction].dumpname = std::string(name.data, name.length);
640
}
641
642
void BytecodeBuilder::setDebugFunctionLineDefined(int line)
643
{
644
functions[currentFunction].debuglinedefined = line;
645
}
646
647
void BytecodeBuilder::setDebugLine(int line)
648
{
649
debugLine = line;
650
}
651
652
void BytecodeBuilder::pushDebugLocal(StringRef name, uint8_t reg, uint32_t startpc, uint32_t endpc)
653
{
654
unsigned int index = addStringTableEntry(name);
655
656
DebugLocal local;
657
local.name = index;
658
local.reg = reg;
659
local.startpc = startpc;
660
local.endpc = endpc;
661
662
debugLocals.push_back(local);
663
}
664
665
void BytecodeBuilder::pushDebugUpval(StringRef name)
666
{
667
unsigned int index = addStringTableEntry(name);
668
669
DebugUpval upval;
670
upval.name = index;
671
672
debugUpvals.push_back(upval);
673
}
674
675
size_t BytecodeBuilder::getInstructionCount() const
676
{
677
return insns.size();
678
}
679
680
size_t BytecodeBuilder::getTotalInstructionCount() const
681
{
682
return totalInstructionCount;
683
}
684
685
uint32_t BytecodeBuilder::getDebugPC() const
686
{
687
return uint32_t(insns.size());
688
}
689
690
void BytecodeBuilder::addDebugRemark(const char* format, ...)
691
{
692
if ((dumpFlags & Dump_Remarks) == 0)
693
return;
694
695
size_t offset = debugRemarkBuffer.size();
696
697
va_list args;
698
va_start(args, format);
699
vformatAppend(debugRemarkBuffer, format, args);
700
va_end(args);
701
702
// we null-terminate all remarks to avoid storing remark length
703
debugRemarkBuffer += '\0';
704
705
debugRemarks.emplace_back(uint32_t(insns.size()), uint32_t(offset));
706
dumpRemarks.emplace_back(debugLine, debugRemarkBuffer.c_str() + offset);
707
}
708
709
void BytecodeBuilder::finalize()
710
{
711
LUAU_ASSERT(bytecode.empty());
712
713
for (auto& ty : userdataTypes)
714
{
715
if (ty.used)
716
ty.nameRef = addStringTableEntry(StringRef({ty.name.c_str(), ty.name.length()}));
717
}
718
719
// preallocate space for bytecode blob
720
size_t capacity = 16;
721
722
for (auto& p : stringTable)
723
capacity += p.first.length + 2;
724
725
for (const Function& func : functions)
726
capacity += func.data.size();
727
728
bytecode.reserve(capacity);
729
730
// assemble final bytecode blob
731
uint8_t version = getVersion();
732
LUAU_ASSERT(version >= LBC_VERSION_MIN && version <= LBC_VERSION_MAX);
733
734
bytecode = char(version);
735
736
uint8_t typesversion = getTypeEncodingVersion();
737
LUAU_ASSERT(typesversion >= LBC_TYPE_VERSION_MIN && typesversion <= LBC_TYPE_VERSION_MAX);
738
writeByte(bytecode, typesversion);
739
740
writeStringTable(bytecode);
741
742
{
743
// Write the mapping between used type name indices and their name
744
for (uint32_t i = 0; i < uint32_t(userdataTypes.size()); i++)
745
{
746
if (userdataTypes[i].used)
747
{
748
writeByte(bytecode, i + 1);
749
writeVarInt(bytecode, userdataTypes[i].nameRef);
750
}
751
}
752
753
// 0 marks the end of the mapping
754
writeByte(bytecode, 0);
755
}
756
757
writeVarInt(bytecode, uint32_t(functions.size()));
758
759
for (const Function& func : functions)
760
bytecode += func.data;
761
762
LUAU_ASSERT(mainFunction < functions.size());
763
writeVarInt(bytecode, mainFunction);
764
}
765
766
void BytecodeBuilder::writeFunction(std::string& ss, uint32_t id, uint8_t flags)
767
{
768
LUAU_ASSERT(id < functions.size());
769
const Function& func = functions[id];
770
771
// header
772
writeByte(ss, func.maxstacksize);
773
writeByte(ss, func.numparams);
774
writeByte(ss, func.numupvalues);
775
writeByte(ss, func.isvararg);
776
777
writeByte(ss, flags);
778
779
if (!func.typeinfo.empty() || !typedUpvals.empty() || !typedLocals.empty())
780
{
781
// collect type info into a temporary string to know the overall size of type data
782
tempTypeInfo.clear();
783
writeVarInt(tempTypeInfo, uint32_t(func.typeinfo.size()));
784
writeVarInt(tempTypeInfo, uint32_t(typedUpvals.size()));
785
writeVarInt(tempTypeInfo, uint32_t(typedLocals.size()));
786
787
tempTypeInfo.append(func.typeinfo);
788
789
for (const TypedUpval& l : typedUpvals)
790
writeByte(tempTypeInfo, l.type);
791
792
for (const TypedLocal& l : typedLocals)
793
{
794
writeByte(tempTypeInfo, l.type);
795
writeByte(tempTypeInfo, l.reg);
796
writeVarInt(tempTypeInfo, l.startpc);
797
LUAU_ASSERT(l.endpc >= l.startpc);
798
writeVarInt(tempTypeInfo, l.endpc - l.startpc);
799
}
800
801
writeVarInt(ss, uint32_t(tempTypeInfo.size()));
802
ss.append(tempTypeInfo);
803
}
804
else
805
{
806
writeVarInt(ss, 0);
807
}
808
809
// instructions
810
writeVarInt(ss, uint32_t(insns.size()));
811
812
for (uint32_t insn : insns)
813
writeInt(ss, insn);
814
815
// constants
816
writeVarInt(ss, uint32_t(constants.size()));
817
818
for (const Constant& c : constants)
819
{
820
switch (c.type)
821
{
822
case Constant::Type_Nil:
823
writeByte(ss, LBC_CONSTANT_NIL);
824
break;
825
826
case Constant::Type_Boolean:
827
writeByte(ss, LBC_CONSTANT_BOOLEAN);
828
writeByte(ss, c.valueBoolean);
829
break;
830
831
case Constant::Type_Number:
832
writeByte(ss, LBC_CONSTANT_NUMBER);
833
writeDouble(ss, c.valueNumber);
834
break;
835
836
case Constant::Type_Integer:
837
writeByte(ss, LBC_CONSTANT_INTEGER);
838
if (c.valueInteger64 < 0)
839
{
840
writeByte(ss, 1);
841
writeVarInt(ss, ~(uint64_t)c.valueInteger64 + 1);
842
}
843
else
844
{
845
writeByte(ss, 0);
846
writeVarInt(ss, c.valueInteger64);
847
}
848
break;
849
850
case Constant::Type_Vector:
851
writeByte(ss, LBC_CONSTANT_VECTOR);
852
writeFloat(ss, c.valueVector[0]);
853
writeFloat(ss, c.valueVector[1]);
854
writeFloat(ss, c.valueVector[2]);
855
writeFloat(ss, c.valueVector[3]);
856
break;
857
858
case Constant::Type_String:
859
writeByte(ss, LBC_CONSTANT_STRING);
860
writeVarInt(ss, c.valueString);
861
break;
862
863
case Constant::Type_Import:
864
writeByte(ss, LBC_CONSTANT_IMPORT);
865
writeInt(ss, c.valueImport);
866
break;
867
868
case Constant::Type_Table:
869
{
870
const TableShape& shape = tableShapes[c.valueTable];
871
if (FFlag::LuauCompileDuptableConstantPack2 && shape.hasConstants)
872
{
873
writeByte(ss, LBC_CONSTANT_TABLE_WITH_CONSTANTS);
874
writeVarInt(ss, uint32_t(shape.length));
875
for (unsigned int i = 0; i < shape.length; ++i)
876
{
877
writeVarInt(ss, shape.keys[i]);
878
writeInt(ss, shape.constants[i]);
879
}
880
}
881
else
882
{
883
writeByte(ss, LBC_CONSTANT_TABLE);
884
writeVarInt(ss, uint32_t(shape.length));
885
for (unsigned int i = 0; i < shape.length; ++i)
886
writeVarInt(ss, shape.keys[i]);
887
}
888
break;
889
}
890
891
case Constant::Type_Closure:
892
writeByte(ss, LBC_CONSTANT_CLOSURE);
893
writeVarInt(ss, c.valueClosure);
894
break;
895
896
default:
897
LUAU_ASSERT(!"Unsupported constant type");
898
}
899
}
900
901
// child protos
902
writeVarInt(ss, uint32_t(protos.size()));
903
904
for (uint32_t child : protos)
905
writeVarInt(ss, child);
906
907
// debug info
908
writeVarInt(ss, func.debuglinedefined);
909
writeVarInt(ss, func.debugname);
910
911
bool hasLines = true;
912
913
for (int line : lines)
914
if (line == 0)
915
{
916
hasLines = false;
917
break;
918
}
919
920
if (hasLines)
921
{
922
writeByte(ss, 1);
923
924
writeLineInfo(ss);
925
}
926
else
927
{
928
writeByte(ss, 0);
929
}
930
931
bool hasDebug = !debugLocals.empty() || !debugUpvals.empty();
932
933
if (hasDebug)
934
{
935
writeByte(ss, 1);
936
937
writeVarInt(ss, uint32_t(debugLocals.size()));
938
939
for (const DebugLocal& l : debugLocals)
940
{
941
writeVarInt(ss, l.name);
942
writeVarInt(ss, l.startpc);
943
writeVarInt(ss, l.endpc);
944
writeByte(ss, l.reg);
945
}
946
947
writeVarInt(ss, uint32_t(debugUpvals.size()));
948
949
for (const DebugUpval& l : debugUpvals)
950
{
951
writeVarInt(ss, l.name);
952
}
953
}
954
else
955
{
956
writeByte(ss, 0);
957
}
958
}
959
960
void BytecodeBuilder::writeLineInfo(std::string& ss) const
961
{
962
LUAU_ASSERT(!lines.empty());
963
964
// this function encodes lines inside each span as a 8-bit delta to span baseline
965
// span is always a power of two; depending on the line info input, it may need to be as low as 1
966
int span = 1 << 24;
967
968
// first pass: determine span length
969
for (size_t offset = 0; offset < lines.size(); offset += span)
970
{
971
size_t next = offset;
972
973
int min = lines[offset];
974
int max = lines[offset];
975
976
for (; next < lines.size() && next < offset + span; ++next)
977
{
978
min = std::min(min, lines[next]);
979
max = std::max(max, lines[next]);
980
981
if (max - min > 255)
982
break;
983
}
984
985
if (next < lines.size() && next - offset < size_t(span))
986
{
987
// since not all lines in the range fit in 8b delta, we need to shrink the span
988
// next iteration will need to reprocess some lines again since span changed
989
span = 1 << log2(int(next - offset));
990
}
991
}
992
993
// second pass: compute span base
994
int baselineOne = 0;
995
std::vector<int> baselineScratch;
996
int* baseline = &baselineOne;
997
size_t baselineSize = (lines.size() - 1) / span + 1;
998
999
if (baselineSize > 1)
1000
{
1001
// avoid heap allocation for single-element baseline which is most functions (<256 lines)
1002
baselineScratch.resize(baselineSize);
1003
baseline = baselineScratch.data();
1004
}
1005
1006
for (size_t offset = 0; offset < lines.size(); offset += span)
1007
{
1008
size_t next = offset;
1009
1010
int min = lines[offset];
1011
1012
for (; next < lines.size() && next < offset + span; ++next)
1013
min = std::min(min, lines[next]);
1014
1015
baseline[offset / span] = min;
1016
}
1017
1018
// third pass: write resulting data
1019
int logspan = log2(span);
1020
1021
writeByte(ss, uint8_t(logspan));
1022
1023
uint8_t lastOffset = 0;
1024
1025
for (size_t i = 0; i < lines.size(); ++i)
1026
{
1027
int delta = lines[i] - baseline[i >> logspan];
1028
LUAU_ASSERT(delta >= 0 && delta <= 255);
1029
1030
writeByte(ss, uint8_t(delta) - lastOffset);
1031
lastOffset = uint8_t(delta);
1032
}
1033
1034
int lastLine = 0;
1035
1036
for (size_t i = 0; i < baselineSize; ++i)
1037
{
1038
writeInt(ss, baseline[i] - lastLine);
1039
lastLine = baseline[i];
1040
}
1041
}
1042
1043
void BytecodeBuilder::writeStringTable(std::string& ss) const
1044
{
1045
std::vector<StringRef> strings(stringTable.size());
1046
1047
for (auto& p : stringTable)
1048
{
1049
LUAU_ASSERT(p.second > 0 && p.second <= strings.size());
1050
strings[p.second - 1] = p.first;
1051
}
1052
1053
writeVarInt(ss, uint32_t(strings.size()));
1054
1055
for (auto& s : strings)
1056
{
1057
writeVarInt(ss, uint32_t(s.length));
1058
ss.append(s.data, s.length);
1059
}
1060
}
1061
1062
uint32_t BytecodeBuilder::getImportId(int32_t id0)
1063
{
1064
LUAU_ASSERT(unsigned(id0) < 1024);
1065
1066
return (1u << 30) | (id0 << 20);
1067
}
1068
1069
uint32_t BytecodeBuilder::getImportId(int32_t id0, int32_t id1)
1070
{
1071
LUAU_ASSERT(unsigned(id0 | id1) < 1024);
1072
1073
return (2u << 30) | (id0 << 20) | (id1 << 10);
1074
}
1075
1076
uint32_t BytecodeBuilder::getImportId(int32_t id0, int32_t id1, int32_t id2)
1077
{
1078
LUAU_ASSERT(unsigned(id0 | id1 | id2) < 1024);
1079
1080
return (3u << 30) | (id0 << 20) | (id1 << 10) | id2;
1081
}
1082
1083
int BytecodeBuilder::decomposeImportId(uint32_t ids, int32_t& id0, int32_t& id1, int32_t& id2)
1084
{
1085
int count = ids >> 30;
1086
id0 = count > 0 ? int(ids >> 20) & 1023 : -1;
1087
id1 = count > 1 ? int(ids >> 10) & 1023 : -1;
1088
id2 = count > 2 ? int(ids) & 1023 : -1;
1089
return count;
1090
}
1091
1092
uint32_t BytecodeBuilder::getStringHash(StringRef key)
1093
{
1094
// This hashing algorithm should match luaS_hash defined in VM/lstring.cpp for short inputs; we can't use that code directly to keep compiler and
1095
// VM independent in terms of compilation/linking. The resulting string hashes are embedded into bytecode binary and result in a better initial
1096
// guess for the field hashes which improves performance during initial code execution. We omit the long string processing here for simplicity, as
1097
// it doesn't really matter on long identifiers.
1098
const char* str = key.data;
1099
size_t len = key.length;
1100
1101
unsigned int h = unsigned(len);
1102
1103
// original Lua 5.1 hash for compatibility (exact match when len<32)
1104
for (size_t i = len; i > 0; --i)
1105
h ^= (h << 5) + (h >> 2) + (uint8_t)str[i - 1];
1106
1107
return h;
1108
}
1109
1110
void BytecodeBuilder::foldJumps()
1111
{
1112
// if our function has long jumps, some processing below can make jump instructions not-jumps (e.g. JUMP->RETURN)
1113
// it's safer to skip this processing
1114
if (hasLongJumps)
1115
return;
1116
1117
for (Jump& jump : jumps)
1118
{
1119
uint32_t jumpLabel = jump.source;
1120
1121
uint32_t jumpInsn = insns[jumpLabel];
1122
1123
// follow jump target through forward unconditional jumps
1124
// we only follow forward jumps to make sure the process terminates
1125
uint32_t targetLabel = jumpLabel + 1 + LUAU_INSN_D(jumpInsn);
1126
LUAU_ASSERT(targetLabel < insns.size());
1127
uint32_t targetInsn = insns[targetLabel];
1128
1129
while (LUAU_INSN_OP(targetInsn) == LOP_JUMP && LUAU_INSN_D(targetInsn) >= 0)
1130
{
1131
targetLabel = targetLabel + 1 + LUAU_INSN_D(targetInsn);
1132
LUAU_ASSERT(targetLabel < insns.size());
1133
targetInsn = insns[targetLabel];
1134
}
1135
1136
int offset = int(targetLabel) - int(jumpLabel) - 1;
1137
1138
// for unconditional jumps to RETURN, we can replace JUMP with RETURN
1139
if (LUAU_INSN_OP(jumpInsn) == LOP_JUMP && LUAU_INSN_OP(targetInsn) == LOP_RETURN)
1140
{
1141
insns[jumpLabel] = targetInsn;
1142
}
1143
else if (int16_t(offset) == offset)
1144
{
1145
insns[jumpLabel] &= 0xffff;
1146
insns[jumpLabel] |= uint16_t(offset) << 16;
1147
}
1148
1149
jump.target = targetLabel;
1150
}
1151
}
1152
1153
void BytecodeBuilder::expandJumps()
1154
{
1155
if (!hasLongJumps)
1156
return;
1157
1158
// we have some jump instructions that couldn't be patched which means their offset didn't fit into 16 bits
1159
// our strategy for replacing instructions is as follows: instead of
1160
// OP jumpoffset
1161
// we will synthesize a jump trampoline before our instruction (note that jump offsets are relative to next instruction):
1162
// JUMP +1
1163
// JUMPX jumpoffset
1164
// OP -2
1165
// the idea is that during forward execution, we will jump over JUMPX into OP; if OP decides to jump, it will jump to JUMPX
1166
// JUMPX can carry a 24-bit jump offset
1167
1168
// jump trampolines expand the code size, which can increase existing jump distances.
1169
// because of this, we may need to expand jumps that previously fit into 16-bit just fine.
1170
// the worst-case expansion is 3x, so to be conservative we will repatch all jumps that have an offset >= 32767/3
1171
const int kMaxJumpDistanceConservative = 32767 / 3;
1172
1173
// we will need to process jumps in order
1174
std::sort(
1175
jumps.begin(),
1176
jumps.end(),
1177
[](const Jump& lhs, const Jump& rhs)
1178
{
1179
return lhs.source < rhs.source;
1180
}
1181
);
1182
1183
// first, let's add jump thunks for every jump with a distance that's too big
1184
// we will create new instruction buffers, with remap table keeping track of the moves: remap[oldpc] = newpc
1185
std::vector<uint32_t> remap(insns.size());
1186
1187
std::vector<uint32_t> newinsns;
1188
std::vector<int> newlines;
1189
1190
LUAU_ASSERT(insns.size() == lines.size());
1191
newinsns.reserve(insns.size());
1192
newlines.reserve(insns.size());
1193
1194
size_t currentJump = 0;
1195
size_t pendingTrampolines = 0;
1196
1197
for (size_t i = 0; i < insns.size();)
1198
{
1199
uint8_t op = LUAU_INSN_OP(insns[i]);
1200
LUAU_ASSERT(op < LOP__COUNT);
1201
1202
if (currentJump < jumps.size() && jumps[currentJump].source == i)
1203
{
1204
int offset = int(jumps[currentJump].target) - int(jumps[currentJump].source) - 1;
1205
1206
if (abs(offset) > kMaxJumpDistanceConservative)
1207
{
1208
// insert jump trampoline as described above; we keep JUMPX offset uninitialized in this pass
1209
newinsns.push_back(LOP_JUMP | (1 << 16));
1210
newinsns.push_back(LOP_JUMPX);
1211
1212
newlines.push_back(lines[i]);
1213
newlines.push_back(lines[i]);
1214
1215
pendingTrampolines++;
1216
}
1217
1218
currentJump++;
1219
}
1220
1221
int oplen = getOpLength(LuauOpcode(op));
1222
1223
// copy instruction and line info to the new stream
1224
for (int j = 0; j < oplen; ++j)
1225
{
1226
remap[i] = uint32_t(newinsns.size());
1227
1228
newinsns.push_back(insns[i]);
1229
newlines.push_back(lines[i]);
1230
1231
i++;
1232
}
1233
}
1234
1235
LUAU_ASSERT(currentJump == jumps.size());
1236
LUAU_ASSERT(pendingTrampolines > 0);
1237
1238
// now we need to recompute offsets for jump instructions - we could not do this in the first pass because the offsets are between *target*
1239
// instructions
1240
for (Jump& jump : jumps)
1241
{
1242
int offset = int(jump.target) - int(jump.source) - 1;
1243
int newoffset = int(remap[jump.target]) - int(remap[jump.source]) - 1;
1244
1245
if (abs(offset) > kMaxJumpDistanceConservative)
1246
{
1247
// fix up jump trampoline
1248
uint32_t& insnt = newinsns[remap[jump.source] - 1];
1249
uint32_t& insnj = newinsns[remap[jump.source]];
1250
1251
LUAU_ASSERT(LUAU_INSN_OP(insnt) == LOP_JUMPX);
1252
1253
// patch JUMPX to JUMPX to target location; note that newoffset is the offset of the jump *relative to OP*, so we need to add 1 to make it
1254
// relative to JUMPX
1255
insnt &= 0xff;
1256
insnt |= uint32_t(newoffset + 1) << 8;
1257
1258
// patch OP to OP -2
1259
insnj &= 0xffff;
1260
insnj |= uint16_t(-2) << 16;
1261
1262
pendingTrampolines--;
1263
}
1264
else
1265
{
1266
uint32_t& insn = newinsns[remap[jump.source]];
1267
1268
// make sure jump instruction had the correct offset before we started
1269
LUAU_ASSERT(LUAU_INSN_D(insn) == offset);
1270
1271
// patch instruction with the new offset
1272
LUAU_ASSERT(int16_t(newoffset) == newoffset);
1273
1274
insn &= 0xffff;
1275
insn |= uint16_t(newoffset) << 16;
1276
}
1277
}
1278
1279
LUAU_ASSERT(pendingTrampolines == 0);
1280
1281
// this was hard, but we're done.
1282
insns.swap(newinsns);
1283
lines.swap(newlines);
1284
1285
for (DebugLocal& debugLocal : debugLocals)
1286
{
1287
// endpc is exclusive, to get the right remapping, we need to remap the location before the end
1288
if (debugLocal.startpc != debugLocal.endpc)
1289
debugLocal.endpc = remap[debugLocal.endpc - 1] + 1;
1290
else
1291
debugLocal.endpc = remap[debugLocal.endpc];
1292
1293
debugLocal.startpc = remap[debugLocal.startpc];
1294
}
1295
1296
for (TypedLocal& typedLocal : typedLocals)
1297
{
1298
// endpc is exclusive, to get the right remapping, we need to remap the location before the end
1299
if (typedLocal.startpc != typedLocal.endpc)
1300
typedLocal.endpc = remap[typedLocal.endpc - 1] + 1;
1301
else
1302
typedLocal.endpc = remap[typedLocal.endpc];
1303
1304
typedLocal.startpc = remap[typedLocal.startpc];
1305
}
1306
}
1307
1308
std::string BytecodeBuilder::getError(const std::string& message)
1309
{
1310
// 0 acts as a special marker for error bytecode (it's equal to LBC_VERSION_TARGET for valid bytecode blobs)
1311
std::string result;
1312
result += char(0);
1313
result += message;
1314
1315
return result;
1316
}
1317
1318
uint8_t BytecodeBuilder::getVersion()
1319
{
1320
// LBC_CONSTANT_TABLE_WITH_CONSTANTS requires version 7
1321
if (FFlag::LuauIntegerType)
1322
return 8;
1323
1324
// LBC_CONSTANT_TABLE_WITH_CONSTANTS requires version 7
1325
if (FFlag::LuauCompileDuptableConstantPack2)
1326
return 7;
1327
1328
return LBC_VERSION_TARGET;
1329
}
1330
1331
uint8_t BytecodeBuilder::getTypeEncodingVersion()
1332
{
1333
return LBC_TYPE_VERSION_TARGET;
1334
}
1335
1336
#ifdef LUAU_ASSERTENABLED
1337
void BytecodeBuilder::validate() const
1338
{
1339
validateInstructions();
1340
validateVariadic();
1341
}
1342
1343
void BytecodeBuilder::validateInstructions() const
1344
{
1345
#define VREG(v) LUAU_ASSERT(unsigned(v) < func.maxstacksize)
1346
#define VREGRANGE(v, count) LUAU_ASSERT(unsigned(v + (count < 0 ? 0 : count)) <= func.maxstacksize)
1347
#define VUPVAL(v) LUAU_ASSERT(unsigned(v) < func.numupvalues)
1348
#define VCONST(v, kind) LUAU_ASSERT(unsigned(v) < constants.size() && constants[v].type == Constant::Type_##kind)
1349
#define VCONSTANY(v) LUAU_ASSERT(unsigned(v) < constants.size())
1350
#define VJUMP(v) LUAU_ASSERT(size_t(i + 1 + v) < insns.size() && insnvalid[i + 1 + v])
1351
1352
LUAU_ASSERT(currentFunction != ~0u);
1353
1354
const Function& func = functions[currentFunction];
1355
1356
// tag instruction offsets so that we can validate jumps
1357
std::vector<uint8_t> insnvalid(insns.size(), 0);
1358
1359
for (size_t i = 0; i < insns.size();)
1360
{
1361
uint32_t insn = insns[i];
1362
LuauOpcode op = LuauOpcode(LUAU_INSN_OP(insn));
1363
1364
insnvalid[i] = true;
1365
1366
i += getOpLength(op);
1367
LUAU_ASSERT(i <= insns.size());
1368
}
1369
1370
std::vector<uint8_t> openCaptures;
1371
1372
// validate individual instructions
1373
for (size_t i = 0; i < insns.size();)
1374
{
1375
uint32_t insn = insns[i];
1376
LuauOpcode op = LuauOpcode(LUAU_INSN_OP(insn));
1377
1378
switch (op)
1379
{
1380
case LOP_LOADNIL:
1381
VREG(LUAU_INSN_A(insn));
1382
break;
1383
1384
case LOP_LOADB:
1385
VREG(LUAU_INSN_A(insn));
1386
LUAU_ASSERT(LUAU_INSN_B(insn) == 0 || LUAU_INSN_B(insn) == 1);
1387
VJUMP(LUAU_INSN_C(insn));
1388
break;
1389
1390
case LOP_LOADN:
1391
VREG(LUAU_INSN_A(insn));
1392
break;
1393
1394
case LOP_LOADK:
1395
VREG(LUAU_INSN_A(insn));
1396
VCONSTANY(LUAU_INSN_D(insn));
1397
break;
1398
1399
case LOP_MOVE:
1400
VREG(LUAU_INSN_A(insn));
1401
VREG(LUAU_INSN_B(insn));
1402
break;
1403
1404
case LOP_GETGLOBAL:
1405
case LOP_SETGLOBAL:
1406
VREG(LUAU_INSN_A(insn));
1407
VCONST(insns[i + 1], String);
1408
break;
1409
1410
case LOP_GETUPVAL:
1411
case LOP_SETUPVAL:
1412
VREG(LUAU_INSN_A(insn));
1413
VUPVAL(LUAU_INSN_B(insn));
1414
break;
1415
1416
case LOP_CLOSEUPVALS:
1417
VREG(LUAU_INSN_A(insn));
1418
while (openCaptures.size() && openCaptures.back() >= LUAU_INSN_A(insn))
1419
openCaptures.pop_back();
1420
break;
1421
1422
case LOP_GETIMPORT:
1423
{
1424
VREG(LUAU_INSN_A(insn));
1425
VCONST(LUAU_INSN_D(insn), Import);
1426
uint32_t id = insns[i + 1];
1427
LUAU_ASSERT((id >> 30) != 0); // import chain with length 1-3
1428
for (unsigned int j = 0; j < (id >> 30); ++j)
1429
VCONST((id >> (20 - 10 * j)) & 1023, String);
1430
}
1431
break;
1432
1433
case LOP_GETTABLE:
1434
case LOP_SETTABLE:
1435
VREG(LUAU_INSN_A(insn));
1436
VREG(LUAU_INSN_B(insn));
1437
VREG(LUAU_INSN_C(insn));
1438
break;
1439
1440
case LOP_GETTABLEKS:
1441
case LOP_SETTABLEKS:
1442
VREG(LUAU_INSN_A(insn));
1443
VREG(LUAU_INSN_B(insn));
1444
VCONST(insns[i + 1], String);
1445
break;
1446
1447
case LOP_GETTABLEN:
1448
case LOP_SETTABLEN:
1449
VREG(LUAU_INSN_A(insn));
1450
VREG(LUAU_INSN_B(insn));
1451
break;
1452
1453
case LOP_NEWCLOSURE:
1454
{
1455
VREG(LUAU_INSN_A(insn));
1456
LUAU_ASSERT(unsigned(LUAU_INSN_D(insn)) < protos.size());
1457
LUAU_ASSERT(protos[LUAU_INSN_D(insn)] < functions.size());
1458
unsigned int numupvalues = functions[protos[LUAU_INSN_D(insn)]].numupvalues;
1459
1460
for (unsigned int j = 0; j < numupvalues; ++j)
1461
{
1462
LUAU_ASSERT(i + 1 + j < insns.size());
1463
uint32_t cinsn = insns[i + 1 + j];
1464
LUAU_ASSERT(LUAU_INSN_OP(cinsn) == LOP_CAPTURE);
1465
}
1466
}
1467
break;
1468
1469
case LOP_NAMECALL:
1470
VREG(LUAU_INSN_A(insn));
1471
VREG(LUAU_INSN_B(insn));
1472
VCONST(insns[i + 1], String);
1473
LUAU_ASSERT(LUAU_INSN_OP(insns[i + 2]) == LOP_CALL);
1474
break;
1475
1476
case LOP_CALL:
1477
{
1478
int nparams = LUAU_INSN_B(insn) - 1;
1479
int nresults = LUAU_INSN_C(insn) - 1;
1480
VREG(LUAU_INSN_A(insn));
1481
VREGRANGE(LUAU_INSN_A(insn) + 1, nparams); // 1..nparams
1482
VREGRANGE(LUAU_INSN_A(insn), nresults); // 1..nresults
1483
}
1484
break;
1485
1486
case LOP_RETURN:
1487
{
1488
int nresults = LUAU_INSN_B(insn) - 1;
1489
VREGRANGE(LUAU_INSN_A(insn), nresults); // 0..nresults-1
1490
}
1491
break;
1492
1493
case LOP_JUMP:
1494
VJUMP(LUAU_INSN_D(insn));
1495
break;
1496
1497
case LOP_JUMPIF:
1498
case LOP_JUMPIFNOT:
1499
VREG(LUAU_INSN_A(insn));
1500
VJUMP(LUAU_INSN_D(insn));
1501
break;
1502
1503
case LOP_JUMPIFEQ:
1504
case LOP_JUMPIFLE:
1505
case LOP_JUMPIFLT:
1506
case LOP_JUMPIFNOTEQ:
1507
case LOP_JUMPIFNOTLE:
1508
case LOP_JUMPIFNOTLT:
1509
VREG(LUAU_INSN_A(insn));
1510
VREG(insns[i + 1]);
1511
VJUMP(LUAU_INSN_D(insn));
1512
break;
1513
1514
case LOP_JUMPXEQKNIL:
1515
case LOP_JUMPXEQKB:
1516
VREG(LUAU_INSN_A(insn));
1517
VJUMP(LUAU_INSN_D(insn));
1518
break;
1519
1520
case LOP_JUMPXEQKN:
1521
VREG(LUAU_INSN_A(insn));
1522
VCONST(insns[i + 1] & 0xffffff, Number);
1523
VJUMP(LUAU_INSN_D(insn));
1524
break;
1525
1526
case LOP_JUMPXEQKS:
1527
VREG(LUAU_INSN_A(insn));
1528
VCONST(insns[i + 1] & 0xffffff, String);
1529
VJUMP(LUAU_INSN_D(insn));
1530
break;
1531
1532
case LOP_ADD:
1533
case LOP_SUB:
1534
case LOP_MUL:
1535
case LOP_DIV:
1536
case LOP_IDIV:
1537
case LOP_MOD:
1538
case LOP_POW:
1539
VREG(LUAU_INSN_A(insn));
1540
VREG(LUAU_INSN_B(insn));
1541
VREG(LUAU_INSN_C(insn));
1542
break;
1543
1544
case LOP_ADDK:
1545
case LOP_SUBK:
1546
case LOP_MULK:
1547
case LOP_DIVK:
1548
case LOP_IDIVK:
1549
case LOP_MODK:
1550
case LOP_POWK:
1551
VREG(LUAU_INSN_A(insn));
1552
VREG(LUAU_INSN_B(insn));
1553
VCONST(LUAU_INSN_C(insn), Number);
1554
break;
1555
1556
case LOP_SUBRK:
1557
case LOP_DIVRK:
1558
VREG(LUAU_INSN_A(insn));
1559
VCONST(LUAU_INSN_B(insn), Number);
1560
VREG(LUAU_INSN_C(insn));
1561
break;
1562
1563
case LOP_AND:
1564
case LOP_OR:
1565
VREG(LUAU_INSN_A(insn));
1566
VREG(LUAU_INSN_B(insn));
1567
VREG(LUAU_INSN_C(insn));
1568
break;
1569
1570
case LOP_ANDK:
1571
case LOP_ORK:
1572
VREG(LUAU_INSN_A(insn));
1573
VREG(LUAU_INSN_B(insn));
1574
VCONSTANY(LUAU_INSN_C(insn));
1575
break;
1576
1577
case LOP_CONCAT:
1578
VREG(LUAU_INSN_A(insn));
1579
VREG(LUAU_INSN_B(insn));
1580
VREG(LUAU_INSN_C(insn));
1581
LUAU_ASSERT(LUAU_INSN_B(insn) <= LUAU_INSN_C(insn));
1582
break;
1583
1584
case LOP_NOT:
1585
case LOP_MINUS:
1586
case LOP_LENGTH:
1587
VREG(LUAU_INSN_A(insn));
1588
VREG(LUAU_INSN_B(insn));
1589
break;
1590
1591
case LOP_NEWTABLE:
1592
VREG(LUAU_INSN_A(insn));
1593
break;
1594
1595
case LOP_DUPTABLE:
1596
VREG(LUAU_INSN_A(insn));
1597
VCONST(LUAU_INSN_D(insn), Table);
1598
break;
1599
1600
case LOP_SETLIST:
1601
{
1602
int count = LUAU_INSN_C(insn) - 1;
1603
VREG(LUAU_INSN_A(insn));
1604
VREGRANGE(LUAU_INSN_B(insn), count);
1605
}
1606
break;
1607
1608
case LOP_FORNPREP:
1609
case LOP_FORNLOOP:
1610
// for loop protocol: A, A+1, A+2 are used for iteration
1611
VREG(LUAU_INSN_A(insn) + 2);
1612
VJUMP(LUAU_INSN_D(insn));
1613
break;
1614
1615
case LOP_FORGPREP:
1616
// forg loop protocol: A, A+1, A+2 are used for iteration protocol; A+3, ... are loop variables
1617
VREG(LUAU_INSN_A(insn) + 2 + 1);
1618
VJUMP(LUAU_INSN_D(insn));
1619
break;
1620
1621
case LOP_FORGLOOP:
1622
// forg loop protocol: A, A+1, A+2 are used for iteration protocol; A+3, ... are loop variables
1623
VREG(LUAU_INSN_A(insn) + 2 + uint8_t(insns[i + 1]));
1624
VJUMP(LUAU_INSN_D(insn));
1625
LUAU_ASSERT(uint8_t(insns[i + 1]) >= 1);
1626
break;
1627
1628
case LOP_FORGPREP_INEXT:
1629
case LOP_FORGPREP_NEXT:
1630
VREG(LUAU_INSN_A(insn) + 4); // forg loop protocol: A, A+1, A+2 are used for iteration protocol; A+3, A+4 are loop variables
1631
VJUMP(LUAU_INSN_D(insn));
1632
break;
1633
1634
case LOP_GETVARARGS:
1635
{
1636
int nresults = LUAU_INSN_B(insn) - 1;
1637
VREGRANGE(LUAU_INSN_A(insn), nresults); // 0..nresults-1
1638
}
1639
break;
1640
1641
case LOP_DUPCLOSURE:
1642
{
1643
VREG(LUAU_INSN_A(insn));
1644
VCONST(LUAU_INSN_D(insn), Closure);
1645
unsigned int proto = constants[LUAU_INSN_D(insn)].valueClosure;
1646
LUAU_ASSERT(proto < functions.size());
1647
unsigned int numupvalues = functions[proto].numupvalues;
1648
1649
for (unsigned int j = 0; j < numupvalues; ++j)
1650
{
1651
LUAU_ASSERT(i + 1 + j < insns.size());
1652
uint32_t cinsn = insns[i + 1 + j];
1653
LUAU_ASSERT(LUAU_INSN_OP(cinsn) == LOP_CAPTURE);
1654
LUAU_ASSERT(LUAU_INSN_A(cinsn) == LCT_VAL || LUAU_INSN_A(cinsn) == LCT_UPVAL);
1655
}
1656
}
1657
break;
1658
1659
case LOP_PREPVARARGS:
1660
LUAU_ASSERT(LUAU_INSN_A(insn) == func.numparams);
1661
LUAU_ASSERT(func.isvararg);
1662
break;
1663
1664
case LOP_BREAK:
1665
break;
1666
1667
case LOP_JUMPBACK:
1668
VJUMP(LUAU_INSN_D(insn));
1669
break;
1670
1671
case LOP_LOADKX:
1672
VREG(LUAU_INSN_A(insn));
1673
VCONSTANY(insns[i + 1]);
1674
break;
1675
1676
case LOP_JUMPX:
1677
VJUMP(LUAU_INSN_E(insn));
1678
break;
1679
1680
case LOP_FASTCALL:
1681
VJUMP(LUAU_INSN_C(insn));
1682
LUAU_ASSERT(LUAU_INSN_OP(insns[i + 1 + LUAU_INSN_C(insn)]) == LOP_CALL);
1683
break;
1684
1685
case LOP_FASTCALL1:
1686
VREG(LUAU_INSN_B(insn));
1687
VJUMP(LUAU_INSN_C(insn));
1688
LUAU_ASSERT(LUAU_INSN_OP(insns[i + 1 + LUAU_INSN_C(insn)]) == LOP_CALL);
1689
break;
1690
1691
case LOP_FASTCALL2:
1692
VREG(LUAU_INSN_B(insn));
1693
VJUMP(LUAU_INSN_C(insn));
1694
LUAU_ASSERT(LUAU_INSN_OP(insns[i + 1 + LUAU_INSN_C(insn)]) == LOP_CALL);
1695
VREG(insns[i + 1]);
1696
break;
1697
1698
case LOP_FASTCALL2K:
1699
VREG(LUAU_INSN_B(insn));
1700
VJUMP(LUAU_INSN_C(insn));
1701
LUAU_ASSERT(LUAU_INSN_OP(insns[i + 1 + LUAU_INSN_C(insn)]) == LOP_CALL);
1702
VCONSTANY(insns[i + 1]);
1703
break;
1704
1705
case LOP_FASTCALL3:
1706
VREG(LUAU_INSN_B(insn));
1707
VJUMP(LUAU_INSN_C(insn));
1708
LUAU_ASSERT(LUAU_INSN_OP(insns[i + 1 + LUAU_INSN_C(insn)]) == LOP_CALL);
1709
VREG(insns[i + 1] & 0xff);
1710
VREG((insns[i + 1] >> 8) & 0xff);
1711
break;
1712
1713
case LOP_COVERAGE:
1714
break;
1715
1716
case LOP_CAPTURE:
1717
switch (LUAU_INSN_A(insn))
1718
{
1719
case LCT_VAL:
1720
VREG(LUAU_INSN_B(insn));
1721
break;
1722
1723
case LCT_REF:
1724
VREG(LUAU_INSN_B(insn));
1725
openCaptures.push_back(LUAU_INSN_B(insn));
1726
break;
1727
1728
case LCT_UPVAL:
1729
VUPVAL(LUAU_INSN_B(insn));
1730
break;
1731
1732
default:
1733
LUAU_ASSERT(!"Unsupported capture type");
1734
}
1735
break;
1736
1737
default:
1738
LUAU_ASSERT(!"Unsupported opcode");
1739
}
1740
1741
i += getOpLength(op);
1742
LUAU_ASSERT(i <= insns.size());
1743
}
1744
1745
// all CAPTURE REF instructions must have a CLOSEUPVALS instruction after them in the bytecode stream
1746
// this doesn't guarantee safety as it doesn't perform basic block based analysis, but if this fails
1747
// then the bytecode is definitely unsafe to run since the compiler won't generate backwards branches
1748
// except for loop edges
1749
LUAU_ASSERT(openCaptures.empty());
1750
1751
#undef VREG
1752
#undef VREGEND
1753
#undef VUPVAL
1754
#undef VCONST
1755
#undef VCONSTANY
1756
#undef VJUMP
1757
}
1758
1759
void BytecodeBuilder::validateVariadic() const
1760
{
1761
// validate MULTRET sequences: instructions that produce a variadic sequence and consume one must come in pairs
1762
// we classify instructions into four groups: producers, consumers, neutral and others
1763
// any producer (an instruction that produces more than one value) must be followed by 0 or more neutral instructions
1764
// and a consumer (that consumes more than one value); these form a variadic sequence.
1765
// except for producer, no instruction in the variadic sequence may be a jump target.
1766
// from the execution perspective, producer adjusts L->top to point to one past the last result, neutral instructions
1767
// leave L->top unmodified, and consumer adjusts L->top back to the stack frame end.
1768
// consumers invalidate all values after L->top after they execute (which we currently don't validate)
1769
bool variadicSeq = false;
1770
1771
std::vector<uint8_t> insntargets(insns.size(), 0);
1772
1773
for (size_t i = 0; i < insns.size();)
1774
{
1775
uint32_t insn = insns[i];
1776
LuauOpcode op = LuauOpcode(LUAU_INSN_OP(insn));
1777
1778
int target = getJumpTarget(insn, uint32_t(i));
1779
1780
if (target >= 0 && !isFastCall(op))
1781
{
1782
LUAU_ASSERT(unsigned(target) < insns.size());
1783
1784
insntargets[target] = true;
1785
}
1786
1787
i += getOpLength(op);
1788
LUAU_ASSERT(i <= insns.size());
1789
}
1790
1791
for (size_t i = 0; i < insns.size();)
1792
{
1793
uint32_t insn = insns[i];
1794
LuauOpcode op = LuauOpcode(LUAU_INSN_OP(insn));
1795
1796
if (variadicSeq)
1797
{
1798
// no instruction inside the sequence, including the consumer, may be a jump target
1799
// this guarantees uninterrupted L->top adjustment flow
1800
LUAU_ASSERT(!insntargets[i]);
1801
}
1802
1803
if (op == LOP_CALL)
1804
{
1805
// note: calls may end one variadic sequence and start a new one
1806
1807
if (LUAU_INSN_B(insn) == 0)
1808
{
1809
// consumer instruction ends a variadic sequence
1810
LUAU_ASSERT(variadicSeq);
1811
variadicSeq = false;
1812
}
1813
else
1814
{
1815
// CALL is not a neutral instruction so it can't be present in a variadic sequence unless it's a consumer
1816
LUAU_ASSERT(!variadicSeq);
1817
}
1818
1819
if (LUAU_INSN_C(insn) == 0)
1820
{
1821
// producer instruction starts a variadic sequence
1822
LUAU_ASSERT(!variadicSeq);
1823
variadicSeq = true;
1824
}
1825
}
1826
else if (op == LOP_GETVARARGS && LUAU_INSN_B(insn) == 0)
1827
{
1828
// producer instruction starts a variadic sequence
1829
LUAU_ASSERT(!variadicSeq);
1830
variadicSeq = true;
1831
}
1832
else if ((op == LOP_RETURN && LUAU_INSN_B(insn) == 0) || (op == LOP_SETLIST && LUAU_INSN_C(insn) == 0))
1833
{
1834
// consumer instruction ends a variadic sequence
1835
LUAU_ASSERT(variadicSeq);
1836
variadicSeq = false;
1837
}
1838
else if (op == LOP_FASTCALL)
1839
{
1840
int callTarget = int(i + LUAU_INSN_C(insn) + 1);
1841
LUAU_ASSERT(unsigned(callTarget) < insns.size() && LUAU_INSN_OP(insns[callTarget]) == LOP_CALL);
1842
1843
if (LUAU_INSN_B(insns[callTarget]) == 0)
1844
{
1845
// consumer instruction ends a variadic sequence; however, we can't terminate it yet because future analysis of CALL will do it
1846
// during FASTCALL fallback, the instructions between this and CALL consumer are going to be executed before L->top so they must
1847
// be neutral; as such, we will defer termination of variadic sequence until CALL analysis
1848
LUAU_ASSERT(variadicSeq);
1849
}
1850
else
1851
{
1852
// FASTCALL is not a neutral instruction so it can't be present in a variadic sequence unless it's linked to CALL consumer
1853
LUAU_ASSERT(!variadicSeq);
1854
}
1855
1856
// note: if FASTCALL is linked to a CALL producer, the instructions between FASTCALL and CALL are technically not part of an executed
1857
// variadic sequence since they are never executed if FASTCALL does anything, so it's okay to skip their validation until CALL
1858
// (we can't simply start a variadic sequence here because that would trigger assertions during linked CALL validation)
1859
}
1860
else if (op == LOP_CLOSEUPVALS || op == LOP_NAMECALL || op == LOP_GETIMPORT || op == LOP_MOVE || op == LOP_GETUPVAL || op == LOP_GETGLOBAL ||
1861
op == LOP_GETTABLEKS || op == LOP_COVERAGE)
1862
{
1863
// instructions inside a variadic sequence must be neutral (can't change L->top)
1864
// while there are many neutral instructions like this, here we check that the instruction is one of the few
1865
// that we'd expect to exist in FASTCALL fallback sequences or between consecutive CALLs for encoding reasons
1866
}
1867
else
1868
{
1869
LUAU_ASSERT(!variadicSeq);
1870
}
1871
1872
i += getOpLength(op);
1873
LUAU_ASSERT(i <= insns.size());
1874
}
1875
1876
LUAU_ASSERT(!variadicSeq);
1877
}
1878
#endif
1879
1880
static bool printableStringConstant(const char* str, size_t len)
1881
{
1882
for (size_t i = 0; i < len; ++i)
1883
{
1884
if (unsigned(str[i]) < ' ')
1885
return false;
1886
}
1887
1888
return true;
1889
}
1890
1891
void BytecodeBuilder::dumpConstant(std::string& result, int k) const
1892
{
1893
LUAU_ASSERT(unsigned(k) < constants.size());
1894
const Constant& data = constants[k];
1895
1896
switch (data.type)
1897
{
1898
case Constant::Type_Nil:
1899
formatAppend(result, "nil");
1900
break;
1901
case Constant::Type_Boolean:
1902
formatAppend(result, "%s", data.valueBoolean ? "true" : "false");
1903
break;
1904
case Constant::Type_Number:
1905
formatAppend(result, "%.17g", data.valueNumber);
1906
break;
1907
case Constant::Type_Integer:
1908
formatAppend(result, "%lld", (long long)(int64_t)data.valueInteger64);
1909
break;
1910
case Constant::Type_Vector:
1911
// 3-vectors is the most common configuration, so truncate to three components if possible
1912
if (data.valueVector[3] == 0.0)
1913
formatAppend(result, "%.9g, %.9g, %.9g", data.valueVector[0], data.valueVector[1], data.valueVector[2]);
1914
else
1915
formatAppend(result, "%.9g, %.9g, %.9g, %.9g", data.valueVector[0], data.valueVector[1], data.valueVector[2], data.valueVector[3]);
1916
break;
1917
case Constant::Type_String:
1918
{
1919
const StringRef& str = debugStrings[data.valueString - 1];
1920
1921
if (printableStringConstant(str.data, str.length))
1922
{
1923
if (str.length < 32)
1924
formatAppend(result, "'%.*s'", int(str.length), str.data);
1925
else
1926
formatAppend(result, "'%.*s'...", 32, str.data);
1927
}
1928
else
1929
{
1930
formatAppend(result, "'");
1931
1932
for (size_t i = 0; i < str.length && i < 32; ++i)
1933
{
1934
if (unsigned(str.data[i]) < ' ')
1935
formatAppend(result, "\\x%02X", uint8_t(str.data[i]));
1936
else
1937
formatAppend(result, "%c", str.data[i]);
1938
}
1939
1940
if (str.length >= 32)
1941
formatAppend(result, "'...");
1942
else
1943
formatAppend(result, "'");
1944
}
1945
break;
1946
}
1947
case Constant::Type_Import:
1948
{
1949
int32_t id0 = -1, id1 = -1, id2 = -1;
1950
if (int count = decomposeImportId(data.valueImport, id0, id1, id2))
1951
{
1952
{
1953
const Constant& id = constants[id0];
1954
LUAU_ASSERT(id.type == Constant::Type_String && id.valueString <= debugStrings.size());
1955
1956
const StringRef& str = debugStrings[id.valueString - 1];
1957
formatAppend(result, "%.*s", int(str.length), str.data);
1958
}
1959
1960
if (count > 1)
1961
{
1962
const Constant& id = constants[id1];
1963
LUAU_ASSERT(id.type == Constant::Type_String && id.valueString <= debugStrings.size());
1964
1965
const StringRef& str = debugStrings[id.valueString - 1];
1966
formatAppend(result, ".%.*s", int(str.length), str.data);
1967
}
1968
1969
if (count > 2)
1970
{
1971
const Constant& id = constants[id2];
1972
LUAU_ASSERT(id.type == Constant::Type_String && id.valueString <= debugStrings.size());
1973
1974
const StringRef& str = debugStrings[id.valueString - 1];
1975
formatAppend(result, ".%.*s", int(str.length), str.data);
1976
}
1977
}
1978
break;
1979
}
1980
case Constant::Type_Table:
1981
formatAppend(result, "{...}");
1982
break;
1983
case Constant::Type_Closure:
1984
{
1985
const Function& func = functions[data.valueClosure];
1986
1987
if (!func.dumpname.empty())
1988
formatAppend(result, "'%s'", func.dumpname.c_str());
1989
break;
1990
}
1991
}
1992
}
1993
1994
void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result, int targetLabel) const
1995
{
1996
uint32_t insn = *code++;
1997
1998
switch (LUAU_INSN_OP(insn))
1999
{
2000
case LOP_LOADNIL:
2001
formatAppend(result, "LOADNIL R%d\n", LUAU_INSN_A(insn));
2002
break;
2003
2004
case LOP_LOADB:
2005
if (LUAU_INSN_C(insn))
2006
formatAppend(result, "LOADB R%d %d +%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2007
else
2008
formatAppend(result, "LOADB R%d %d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn));
2009
break;
2010
2011
case LOP_LOADN:
2012
formatAppend(result, "LOADN R%d %d\n", LUAU_INSN_A(insn), LUAU_INSN_D(insn));
2013
break;
2014
2015
case LOP_LOADK:
2016
formatAppend(result, "LOADK R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_D(insn));
2017
dumpConstant(result, LUAU_INSN_D(insn));
2018
result.append("]\n");
2019
break;
2020
2021
case LOP_MOVE:
2022
formatAppend(result, "MOVE R%d R%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn));
2023
break;
2024
2025
case LOP_GETGLOBAL:
2026
formatAppend(result, "GETGLOBAL R%d K%d [", LUAU_INSN_A(insn), *code);
2027
dumpConstant(result, *code);
2028
result.append("]\n");
2029
code++;
2030
break;
2031
2032
case LOP_SETGLOBAL:
2033
formatAppend(result, "SETGLOBAL R%d K%d [", LUAU_INSN_A(insn), *code);
2034
dumpConstant(result, *code);
2035
result.append("]\n");
2036
code++;
2037
break;
2038
2039
case LOP_GETUPVAL:
2040
formatAppend(result, "GETUPVAL R%d %d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn));
2041
break;
2042
2043
case LOP_SETUPVAL:
2044
formatAppend(result, "SETUPVAL R%d %d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn));
2045
break;
2046
2047
case LOP_CLOSEUPVALS:
2048
formatAppend(result, "CLOSEUPVALS R%d\n", LUAU_INSN_A(insn));
2049
break;
2050
2051
case LOP_GETIMPORT:
2052
formatAppend(result, "GETIMPORT R%d %d [", LUAU_INSN_A(insn), LUAU_INSN_D(insn));
2053
dumpConstant(result, LUAU_INSN_D(insn));
2054
result.append("]\n");
2055
code++; // AUX
2056
break;
2057
2058
case LOP_GETTABLE:
2059
formatAppend(result, "GETTABLE R%d R%d R%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2060
break;
2061
2062
case LOP_SETTABLE:
2063
formatAppend(result, "SETTABLE R%d R%d R%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2064
break;
2065
2066
case LOP_GETTABLEKS:
2067
formatAppend(result, "GETTABLEKS R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), *code);
2068
dumpConstant(result, *code);
2069
result.append("]\n");
2070
code++;
2071
break;
2072
2073
case LOP_SETTABLEKS:
2074
formatAppend(result, "SETTABLEKS R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), *code);
2075
dumpConstant(result, *code);
2076
result.append("]\n");
2077
code++;
2078
break;
2079
2080
case LOP_GETTABLEN:
2081
formatAppend(result, "GETTABLEN R%d R%d %d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn) + 1);
2082
break;
2083
2084
case LOP_SETTABLEN:
2085
formatAppend(result, "SETTABLEN R%d R%d %d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn) + 1);
2086
break;
2087
2088
case LOP_NEWCLOSURE:
2089
formatAppend(result, "NEWCLOSURE R%d P%d\n", LUAU_INSN_A(insn), LUAU_INSN_D(insn));
2090
break;
2091
2092
case LOP_NAMECALL:
2093
formatAppend(result, "NAMECALL R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), *code);
2094
dumpConstant(result, *code);
2095
result.append("]\n");
2096
code++;
2097
break;
2098
2099
case LOP_CALL:
2100
formatAppend(result, "CALL R%d %d %d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn) - 1, LUAU_INSN_C(insn) - 1);
2101
break;
2102
2103
case LOP_RETURN:
2104
formatAppend(result, "RETURN R%d %d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn) - 1);
2105
break;
2106
2107
case LOP_JUMP:
2108
formatAppend(result, "JUMP L%d\n", targetLabel);
2109
break;
2110
2111
case LOP_JUMPIF:
2112
formatAppend(result, "JUMPIF R%d L%d\n", LUAU_INSN_A(insn), targetLabel);
2113
break;
2114
2115
case LOP_JUMPIFNOT:
2116
formatAppend(result, "JUMPIFNOT R%d L%d\n", LUAU_INSN_A(insn), targetLabel);
2117
break;
2118
2119
case LOP_JUMPIFEQ:
2120
formatAppend(result, "JUMPIFEQ R%d R%d L%d\n", LUAU_INSN_A(insn), *code++, targetLabel);
2121
break;
2122
2123
case LOP_JUMPIFLE:
2124
formatAppend(result, "JUMPIFLE R%d R%d L%d\n", LUAU_INSN_A(insn), *code++, targetLabel);
2125
break;
2126
2127
case LOP_JUMPIFLT:
2128
formatAppend(result, "JUMPIFLT R%d R%d L%d\n", LUAU_INSN_A(insn), *code++, targetLabel);
2129
break;
2130
2131
case LOP_JUMPIFNOTEQ:
2132
formatAppend(result, "JUMPIFNOTEQ R%d R%d L%d\n", LUAU_INSN_A(insn), *code++, targetLabel);
2133
break;
2134
2135
case LOP_JUMPIFNOTLE:
2136
formatAppend(result, "JUMPIFNOTLE R%d R%d L%d\n", LUAU_INSN_A(insn), *code++, targetLabel);
2137
break;
2138
2139
case LOP_JUMPIFNOTLT:
2140
formatAppend(result, "JUMPIFNOTLT R%d R%d L%d\n", LUAU_INSN_A(insn), *code++, targetLabel);
2141
break;
2142
2143
case LOP_ADD:
2144
formatAppend(result, "ADD R%d R%d R%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2145
break;
2146
2147
case LOP_SUB:
2148
formatAppend(result, "SUB R%d R%d R%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2149
break;
2150
2151
case LOP_MUL:
2152
formatAppend(result, "MUL R%d R%d R%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2153
break;
2154
2155
case LOP_DIV:
2156
formatAppend(result, "DIV R%d R%d R%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2157
break;
2158
2159
case LOP_IDIV:
2160
formatAppend(result, "IDIV R%d R%d R%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2161
break;
2162
2163
case LOP_MOD:
2164
formatAppend(result, "MOD R%d R%d R%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2165
break;
2166
2167
case LOP_POW:
2168
formatAppend(result, "POW R%d R%d R%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2169
break;
2170
2171
case LOP_ADDK:
2172
formatAppend(result, "ADDK R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2173
dumpConstant(result, LUAU_INSN_C(insn));
2174
result.append("]\n");
2175
break;
2176
2177
case LOP_SUBK:
2178
formatAppend(result, "SUBK R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2179
dumpConstant(result, LUAU_INSN_C(insn));
2180
result.append("]\n");
2181
break;
2182
2183
case LOP_MULK:
2184
formatAppend(result, "MULK R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2185
dumpConstant(result, LUAU_INSN_C(insn));
2186
result.append("]\n");
2187
break;
2188
2189
case LOP_DIVK:
2190
formatAppend(result, "DIVK R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2191
dumpConstant(result, LUAU_INSN_C(insn));
2192
result.append("]\n");
2193
break;
2194
2195
case LOP_IDIVK:
2196
formatAppend(result, "IDIVK R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2197
dumpConstant(result, LUAU_INSN_C(insn));
2198
result.append("]\n");
2199
break;
2200
2201
case LOP_MODK:
2202
formatAppend(result, "MODK R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2203
dumpConstant(result, LUAU_INSN_C(insn));
2204
result.append("]\n");
2205
break;
2206
2207
case LOP_POWK:
2208
formatAppend(result, "POWK R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2209
dumpConstant(result, LUAU_INSN_C(insn));
2210
result.append("]\n");
2211
break;
2212
2213
case LOP_SUBRK:
2214
formatAppend(result, "SUBRK R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn));
2215
dumpConstant(result, LUAU_INSN_B(insn));
2216
formatAppend(result, "] R%d\n", LUAU_INSN_C(insn));
2217
break;
2218
2219
case LOP_DIVRK:
2220
formatAppend(result, "DIVRK R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn));
2221
dumpConstant(result, LUAU_INSN_B(insn));
2222
formatAppend(result, "] R%d\n", LUAU_INSN_C(insn));
2223
break;
2224
2225
case LOP_AND:
2226
formatAppend(result, "AND R%d R%d R%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2227
break;
2228
2229
case LOP_OR:
2230
formatAppend(result, "OR R%d R%d R%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2231
break;
2232
2233
case LOP_ANDK:
2234
formatAppend(result, "ANDK R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2235
dumpConstant(result, LUAU_INSN_C(insn));
2236
result.append("]\n");
2237
break;
2238
2239
case LOP_ORK:
2240
formatAppend(result, "ORK R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2241
dumpConstant(result, LUAU_INSN_C(insn));
2242
result.append("]\n");
2243
break;
2244
2245
case LOP_CONCAT:
2246
formatAppend(result, "CONCAT R%d R%d R%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn));
2247
break;
2248
2249
case LOP_NOT:
2250
formatAppend(result, "NOT R%d R%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn));
2251
break;
2252
2253
case LOP_MINUS:
2254
formatAppend(result, "MINUS R%d R%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn));
2255
break;
2256
2257
case LOP_LENGTH:
2258
formatAppend(result, "LENGTH R%d R%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn));
2259
break;
2260
2261
case LOP_NEWTABLE:
2262
formatAppend(result, "NEWTABLE R%d %d %d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn) == 0 ? 0 : 1 << (LUAU_INSN_B(insn) - 1), *code++);
2263
break;
2264
2265
case LOP_DUPTABLE:
2266
formatAppend(result, "DUPTABLE R%d %d\n", LUAU_INSN_A(insn), LUAU_INSN_D(insn));
2267
break;
2268
2269
case LOP_SETLIST:
2270
formatAppend(result, "SETLIST R%d R%d %d [%d]\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn) - 1, *code++);
2271
break;
2272
2273
case LOP_FORNPREP:
2274
formatAppend(result, "FORNPREP R%d L%d\n", LUAU_INSN_A(insn), targetLabel);
2275
break;
2276
2277
case LOP_FORNLOOP:
2278
formatAppend(result, "FORNLOOP R%d L%d\n", LUAU_INSN_A(insn), targetLabel);
2279
break;
2280
2281
case LOP_FORGPREP:
2282
formatAppend(result, "FORGPREP R%d L%d\n", LUAU_INSN_A(insn), targetLabel);
2283
break;
2284
2285
case LOP_FORGLOOP:
2286
formatAppend(result, "FORGLOOP R%d L%d %d%s\n", LUAU_INSN_A(insn), targetLabel, uint8_t(*code), int(*code) < 0 ? " [inext]" : "");
2287
code++;
2288
break;
2289
2290
case LOP_FORGPREP_INEXT:
2291
formatAppend(result, "FORGPREP_INEXT R%d L%d\n", LUAU_INSN_A(insn), targetLabel);
2292
break;
2293
2294
case LOP_FORGPREP_NEXT:
2295
formatAppend(result, "FORGPREP_NEXT R%d L%d\n", LUAU_INSN_A(insn), targetLabel);
2296
break;
2297
2298
case LOP_GETVARARGS:
2299
formatAppend(result, "GETVARARGS R%d %d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn) - 1);
2300
break;
2301
2302
case LOP_DUPCLOSURE:
2303
formatAppend(result, "DUPCLOSURE R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_D(insn));
2304
dumpConstant(result, LUAU_INSN_D(insn));
2305
result.append("]\n");
2306
break;
2307
2308
case LOP_BREAK:
2309
formatAppend(result, "BREAK\n");
2310
break;
2311
2312
case LOP_JUMPBACK:
2313
formatAppend(result, "JUMPBACK L%d\n", targetLabel);
2314
break;
2315
2316
case LOP_LOADKX:
2317
formatAppend(result, "LOADKX R%d K%d [", LUAU_INSN_A(insn), *code);
2318
dumpConstant(result, *code);
2319
result.append("]\n");
2320
code++;
2321
break;
2322
2323
case LOP_JUMPX:
2324
formatAppend(result, "JUMPX L%d\n", targetLabel);
2325
break;
2326
2327
case LOP_FASTCALL:
2328
formatAppend(result, "FASTCALL %d L%d\n", LUAU_INSN_A(insn), targetLabel);
2329
break;
2330
2331
case LOP_FASTCALL1:
2332
formatAppend(result, "FASTCALL1 %d R%d L%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), targetLabel);
2333
break;
2334
2335
case LOP_FASTCALL2:
2336
formatAppend(result, "FASTCALL2 %d R%d R%d L%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), *code, targetLabel);
2337
code++;
2338
break;
2339
2340
case LOP_FASTCALL2K:
2341
formatAppend(result, "FASTCALL2K %d R%d K%d L%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), *code, targetLabel);
2342
dumpConstant(result, *code);
2343
result.append("]\n");
2344
code++;
2345
break;
2346
2347
case LOP_FASTCALL3:
2348
formatAppend(result, "FASTCALL3 %d R%d R%d R%d L%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), *code & 0xff, (*code >> 8) & 0xff, targetLabel);
2349
code++;
2350
break;
2351
2352
case LOP_COVERAGE:
2353
formatAppend(result, "COVERAGE\n");
2354
break;
2355
2356
case LOP_CAPTURE:
2357
formatAppend(
2358
result,
2359
"CAPTURE %s %c%d\n",
2360
LUAU_INSN_A(insn) == LCT_UPVAL ? "UPVAL"
2361
: LUAU_INSN_A(insn) == LCT_REF ? "REF"
2362
: LUAU_INSN_A(insn) == LCT_VAL ? "VAL"
2363
: "",
2364
LUAU_INSN_A(insn) == LCT_UPVAL ? 'U' : 'R',
2365
LUAU_INSN_B(insn)
2366
);
2367
break;
2368
2369
case LOP_JUMPXEQKNIL:
2370
formatAppend(result, "JUMPXEQKNIL R%d L%d%s\n", LUAU_INSN_A(insn), targetLabel, *code >> 31 ? " NOT" : "");
2371
code++;
2372
break;
2373
2374
case LOP_JUMPXEQKB:
2375
formatAppend(result, "JUMPXEQKB R%d %d L%d%s\n", LUAU_INSN_A(insn), *code & 1, targetLabel, *code >> 31 ? " NOT" : "");
2376
code++;
2377
break;
2378
2379
case LOP_JUMPXEQKN:
2380
formatAppend(result, "JUMPXEQKN R%d K%d L%d%s [", LUAU_INSN_A(insn), *code & 0xffffff, targetLabel, *code >> 31 ? " NOT" : "");
2381
dumpConstant(result, *code & 0xffffff);
2382
result.append("]\n");
2383
code++;
2384
break;
2385
2386
case LOP_JUMPXEQKS:
2387
formatAppend(result, "JUMPXEQKS R%d K%d L%d%s [", LUAU_INSN_A(insn), *code & 0xffffff, targetLabel, *code >> 31 ? " NOT" : "");
2388
dumpConstant(result, *code & 0xffffff);
2389
result.append("]\n");
2390
code++;
2391
break;
2392
2393
default:
2394
LUAU_ASSERT(!"Unsupported opcode");
2395
}
2396
}
2397
2398
static const char* getBaseTypeString(uint8_t type)
2399
{
2400
uint8_t tag = type & ~LBC_TYPE_OPTIONAL_BIT;
2401
switch (tag)
2402
{
2403
case LBC_TYPE_NIL:
2404
return "nil";
2405
case LBC_TYPE_BOOLEAN:
2406
return "boolean";
2407
case LBC_TYPE_NUMBER:
2408
return "number";
2409
case LBC_TYPE_STRING:
2410
return "string";
2411
case LBC_TYPE_TABLE:
2412
return "table";
2413
case LBC_TYPE_FUNCTION:
2414
return "function";
2415
case LBC_TYPE_THREAD:
2416
return "thread";
2417
case LBC_TYPE_USERDATA:
2418
return "userdata";
2419
case LBC_TYPE_VECTOR:
2420
return "vector";
2421
case LBC_TYPE_BUFFER:
2422
return "buffer";
2423
case LBC_TYPE_ANY:
2424
return "any";
2425
}
2426
2427
LUAU_ASSERT(!"Unhandled type in getBaseTypeString");
2428
return nullptr;
2429
}
2430
2431
std::string BytecodeBuilder::dumpCurrentFunction(std::vector<int>& dumpinstoffs) const
2432
{
2433
if ((dumpFlags & (Dump_Code | Dump_Constants)) == 0)
2434
return std::string();
2435
2436
int lastLine = -1;
2437
size_t nextRemark = 0;
2438
2439
std::string result;
2440
2441
if (dumpFlags & Dump_Locals)
2442
{
2443
for (size_t i = 0; i < debugLocals.size(); ++i)
2444
{
2445
const DebugLocal& l = debugLocals[i];
2446
2447
if (l.startpc == l.endpc)
2448
{
2449
LUAU_ASSERT(l.startpc < lines.size());
2450
2451
// it would be nice to emit name as well but it requires reverse lookup through stringtable
2452
formatAppend(result, "local %d: reg %d, start pc %d line %d, no live range\n", int(i), l.reg, l.startpc, lines[l.startpc]);
2453
}
2454
else
2455
{
2456
LUAU_ASSERT(l.startpc < l.endpc);
2457
LUAU_ASSERT(l.startpc < lines.size());
2458
LUAU_ASSERT(l.endpc <= lines.size()); // endpc is exclusive in the debug info, but it's more intuitive to print inclusive data
2459
2460
// it would be nice to emit name as well but it requires reverse lookup through stringtable
2461
formatAppend(
2462
result,
2463
"local %d: reg %d, start pc %d line %d, end pc %d line %d\n",
2464
int(i),
2465
l.reg,
2466
l.startpc,
2467
lines[l.startpc],
2468
l.endpc - 1,
2469
lines[l.endpc - 1]
2470
);
2471
}
2472
}
2473
}
2474
2475
if (dumpFlags & Dump_Types)
2476
{
2477
const std::string& typeinfo = functions.back().typeinfo;
2478
2479
// Arguments start from third byte in function typeinfo string
2480
for (uint8_t i = 2; i < typeinfo.size(); ++i)
2481
{
2482
uint8_t et = typeinfo[i];
2483
2484
const char* userdata = tryGetUserdataTypeName(LuauBytecodeType(et));
2485
const char* name = userdata ? userdata : getBaseTypeString(et);
2486
const char* optional = (et & LBC_TYPE_OPTIONAL_BIT) ? "?" : "";
2487
2488
formatAppend(result, "R%d: %s%s [argument]\n", i - 2, name, optional);
2489
}
2490
2491
for (size_t i = 0; i < typedUpvals.size(); ++i)
2492
{
2493
const TypedUpval& l = typedUpvals[i];
2494
2495
const char* userdata = tryGetUserdataTypeName(l.type);
2496
const char* name = userdata ? userdata : getBaseTypeString(l.type);
2497
const char* optional = (l.type & LBC_TYPE_OPTIONAL_BIT) ? "?" : "";
2498
2499
formatAppend(result, "U%d: %s%s\n", int(i), name, optional);
2500
}
2501
2502
for (size_t i = 0; i < typedLocals.size(); ++i)
2503
{
2504
const TypedLocal& l = typedLocals[i];
2505
2506
const char* userdata = tryGetUserdataTypeName(l.type);
2507
const char* name = userdata ? userdata : getBaseTypeString(l.type);
2508
const char* optional = (l.type & LBC_TYPE_OPTIONAL_BIT) ? "?" : "";
2509
2510
formatAppend(result, "R%d: %s%s from %d to %d\n", l.reg, name, optional, l.startpc, l.endpc);
2511
}
2512
}
2513
2514
if (dumpFlags & Dump_Constants)
2515
{
2516
for (size_t i = 0; i < constants.size(); ++i)
2517
{
2518
formatAppend(result, "K%d: ", int(i));
2519
dumpConstant(result, int(i));
2520
formatAppend(result, "\n");
2521
}
2522
}
2523
2524
if (dumpFlags & Dump_Code)
2525
{
2526
std::vector<int> labels(insns.size(), -1);
2527
2528
// annotate valid jump targets with 0
2529
for (size_t i = 0; i < insns.size();)
2530
{
2531
int target = getJumpTarget(insns[i], uint32_t(i));
2532
2533
if (target >= 0)
2534
{
2535
LUAU_ASSERT(size_t(target) < insns.size());
2536
labels[target] = 0;
2537
}
2538
2539
i += getOpLength(LuauOpcode(LUAU_INSN_OP(insns[i])));
2540
LUAU_ASSERT(i <= insns.size());
2541
}
2542
2543
int nextLabel = 0;
2544
2545
// compute label ids (sequential integers for all jump targets)
2546
for (size_t i = 0; i < labels.size(); ++i)
2547
if (labels[i] == 0)
2548
labels[i] = nextLabel++;
2549
2550
dumpinstoffs.resize(insns.size() + 1, -1);
2551
2552
for (size_t i = 0; i < insns.size();)
2553
{
2554
const uint32_t* code = &insns[i];
2555
uint8_t op = LUAU_INSN_OP(*code);
2556
2557
dumpinstoffs[i] = int(result.size());
2558
2559
if (op == LOP_PREPVARARGS)
2560
{
2561
// Don't emit function header in bytecode - it's used for call dispatching and doesn't contain "interesting" information
2562
i++;
2563
continue;
2564
}
2565
2566
if (dumpFlags & Dump_Remarks)
2567
{
2568
while (nextRemark < debugRemarks.size() && debugRemarks[nextRemark].first == i)
2569
{
2570
formatAppend(result, "REMARK %s\n", debugRemarkBuffer.c_str() + debugRemarks[nextRemark].second);
2571
nextRemark++;
2572
}
2573
}
2574
2575
if (dumpFlags & Dump_Source)
2576
{
2577
int line = lines[i];
2578
2579
if (line > 0 && line != lastLine)
2580
{
2581
LUAU_ASSERT(size_t(line - 1) < dumpSource.size());
2582
formatAppend(result, "%5d: %s\n", line, dumpSource[line - 1].c_str());
2583
lastLine = line;
2584
}
2585
}
2586
2587
if (dumpFlags & Dump_Lines)
2588
formatAppend(result, "%d: ", lines[i]);
2589
2590
if (labels[i] != -1)
2591
formatAppend(result, "L%d: ", labels[i]);
2592
2593
int target = getJumpTarget(*code, uint32_t(i));
2594
2595
dumpInstruction(code, result, target >= 0 ? labels[target] : -1);
2596
2597
i += getOpLength(LuauOpcode(op));
2598
LUAU_ASSERT(i <= insns.size());
2599
}
2600
2601
dumpinstoffs[insns.size()] = int(result.size());
2602
}
2603
2604
return result;
2605
}
2606
2607
void BytecodeBuilder::setDumpSource(const std::string& source)
2608
{
2609
dumpSource.clear();
2610
2611
size_t pos = 0;
2612
2613
while (pos != std::string::npos)
2614
{
2615
size_t next = source.find('\n', pos);
2616
2617
if (next == std::string::npos)
2618
{
2619
dumpSource.push_back(source.substr(pos));
2620
pos = next;
2621
}
2622
else
2623
{
2624
dumpSource.push_back(source.substr(pos, next - pos));
2625
pos = next + 1;
2626
}
2627
2628
if (!dumpSource.back().empty() && dumpSource.back().back() == '\r')
2629
dumpSource.back().pop_back();
2630
}
2631
}
2632
2633
std::string BytecodeBuilder::dumpFunction(uint32_t id) const
2634
{
2635
LUAU_ASSERT(id < functions.size());
2636
2637
return functions[id].dump;
2638
}
2639
2640
std::string BytecodeBuilder::dumpEverything() const
2641
{
2642
std::string result;
2643
2644
for (size_t i = 0; i < functions.size(); ++i)
2645
{
2646
std::string debugname = functions[i].dumpname.empty() ? "??" : functions[i].dumpname;
2647
2648
formatAppend(result, "Function %d (%s):\n", int(i), debugname.c_str());
2649
2650
result += functions[i].dump;
2651
result += "\n";
2652
}
2653
2654
return result;
2655
}
2656
2657
std::string BytecodeBuilder::dumpSourceRemarks() const
2658
{
2659
std::string result;
2660
2661
size_t nextRemark = 0;
2662
2663
std::vector<std::pair<int, std::string>> remarks = dumpRemarks;
2664
std::sort(remarks.begin(), remarks.end());
2665
2666
for (size_t i = 0; i < dumpSource.size(); ++i)
2667
{
2668
const std::string& line = dumpSource[i];
2669
2670
size_t indent = 0;
2671
while (indent < line.length() && (line[indent] == ' ' || line[indent] == '\t'))
2672
indent++;
2673
2674
while (nextRemark < remarks.size() && remarks[nextRemark].first == int(i + 1))
2675
{
2676
formatAppend(result, "%.*s-- remark: %s\n", int(indent), line.c_str(), remarks[nextRemark].second.c_str());
2677
nextRemark++;
2678
2679
// skip duplicate remarks (due to inlining/unrolling)
2680
while (nextRemark < remarks.size() && remarks[nextRemark] == remarks[nextRemark - 1])
2681
nextRemark++;
2682
}
2683
2684
result += line;
2685
2686
if (i + 1 < dumpSource.size())
2687
result += '\n';
2688
}
2689
2690
return result;
2691
}
2692
2693
std::string BytecodeBuilder::dumpTypeInfo() const
2694
{
2695
std::string result;
2696
2697
for (size_t i = 0; i < functions.size(); ++i)
2698
{
2699
const std::string& typeinfo = functions[i].typeinfo;
2700
if (typeinfo.empty())
2701
continue;
2702
2703
uint8_t encodedType = typeinfo[0];
2704
2705
LUAU_ASSERT(encodedType == LBC_TYPE_FUNCTION);
2706
2707
formatAppend(result, "%zu: function(", i);
2708
2709
LUAU_ASSERT(typeinfo.size() >= 2);
2710
2711
uint8_t numparams = typeinfo[1];
2712
2713
LUAU_ASSERT(size_t(1 + numparams - 1) < typeinfo.size());
2714
2715
for (uint8_t i = 0; i < numparams; ++i)
2716
{
2717
uint8_t et = typeinfo[2 + i];
2718
const char* optional = (et & LBC_TYPE_OPTIONAL_BIT) ? "?" : "";
2719
formatAppend(result, "%s%s", getBaseTypeString(et), optional);
2720
2721
if (i + 1 != numparams)
2722
formatAppend(result, ", ");
2723
}
2724
2725
formatAppend(result, ")\n");
2726
}
2727
2728
return result;
2729
}
2730
2731
void BytecodeBuilder::annotateInstruction(std::string& result, uint32_t fid, uint32_t instpos) const
2732
{
2733
if ((dumpFlags & Dump_Code) == 0)
2734
return;
2735
2736
LUAU_ASSERT(fid < functions.size());
2737
2738
const Function& function = functions[fid];
2739
const std::string& dump = function.dump;
2740
const std::vector<int>& dumpinstoffs = function.dumpinstoffs;
2741
2742
uint32_t next = instpos + 1;
2743
2744
LUAU_ASSERT(next < dumpinstoffs.size());
2745
2746
// Skip locations of multi-dword instructions
2747
while (next < dumpinstoffs.size() && dumpinstoffs[next] == -1)
2748
next++;
2749
2750
formatAppend(result, "%.*s", dumpinstoffs[next] - dumpinstoffs[instpos], dump.data() + dumpinstoffs[instpos]);
2751
}
2752
2753
} // namespace Luau
2754
2755