Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Compiler/src/Compiler.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/Compiler.h"
3
4
#include "Luau/Parser.h"
5
#include "Luau/BytecodeBuilder.h"
6
#include "Luau/Common.h"
7
#include "Luau/InsertionOrderedMap.h"
8
#include "Luau/StringUtils.h"
9
#include "Luau/TimeTrace.h"
10
11
#include "Builtins.h"
12
#include "ConstantFolding.h"
13
#include "CostModel.h"
14
#include "TableShape.h"
15
#include "Types.h"
16
#include "Utils.h"
17
#include "ValueTracking.h"
18
19
#include <algorithm>
20
#include <bitset>
21
#include <memory>
22
23
#include <math.h>
24
25
LUAU_FASTINTVARIABLE(LuauCompileLoopUnrollThreshold, 25)
26
LUAU_FASTINTVARIABLE(LuauCompileLoopUnrollThresholdMaxBoost, 300)
27
28
LUAU_FASTINTVARIABLE(LuauCompileInlineThreshold, 25)
29
LUAU_FASTINTVARIABLE(LuauCompileInlineThresholdMaxBoost, 300)
30
LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5)
31
32
LUAU_FASTFLAGVARIABLE(LuauCompileDuptableConstantPack2)
33
LUAU_FASTFLAGVARIABLE(LuauCompileVectorReveseMul)
34
LUAU_FASTFLAG(LuauIntegerType)
35
LUAU_FASTFLAGVARIABLE(LuauCompileStringInterpWithZero)
36
LUAU_FASTFLAG(DebugLuauNoInline)
37
38
namespace Luau
39
{
40
41
using namespace Luau::Compile;
42
43
static const uint32_t kMaxRegisterCount = 255;
44
static const uint32_t kMaxUpvalueCount = 200;
45
static const uint32_t kMaxLocalCount = 200;
46
static const uint32_t kMaxInstructionCount = 1'000'000'000;
47
48
static const uint8_t kInvalidReg = 255;
49
50
static const uint32_t kDefaultAllocPc = ~0u;
51
52
void escapeAndAppend(std::string& buffer, const char* str, size_t len)
53
{
54
if (memchr(str, '%', len))
55
{
56
for (size_t characterIndex = 0; characterIndex < len; ++characterIndex)
57
{
58
char character = str[characterIndex];
59
buffer.push_back(character);
60
61
if (character == '%')
62
buffer.push_back('%');
63
}
64
}
65
else
66
buffer.append(str, len);
67
}
68
69
CompileError::CompileError(const Location& location, std::string message)
70
: location(location)
71
, message(std::move(message))
72
{
73
}
74
75
CompileError::~CompileError() throw() {}
76
77
const char* CompileError::what() const throw()
78
{
79
return message.c_str();
80
}
81
82
const Location& CompileError::getLocation() const
83
{
84
return location;
85
}
86
87
// NOINLINE is used to limit the stack cost of this function due to std::string object / exception plumbing
88
LUAU_NOINLINE void CompileError::raise(const Location& location, const char* format, ...)
89
{
90
va_list args;
91
va_start(args, format);
92
std::string message = vformat(format, args);
93
va_end(args);
94
95
throw CompileError(location, message);
96
}
97
98
static BytecodeBuilder::StringRef sref(AstName name)
99
{
100
LUAU_ASSERT(name.value);
101
return {name.value, strlen(name.value)};
102
}
103
104
static BytecodeBuilder::StringRef sref(AstArray<char> data)
105
{
106
LUAU_ASSERT(data.data);
107
return {data.data, data.size};
108
}
109
110
static BytecodeBuilder::StringRef sref(AstArray<const char> data)
111
{
112
LUAU_ASSERT(data.data);
113
return {data.data, data.size};
114
}
115
116
struct Compiler
117
{
118
struct RegScope;
119
120
Compiler(BytecodeBuilder& bytecode, const CompileOptions& options, AstNameTable& names)
121
: bytecode(bytecode)
122
, options(options)
123
, functions(nullptr)
124
, locals(nullptr)
125
, globals(AstName())
126
, variables(nullptr)
127
, constants(nullptr)
128
, locstants(nullptr)
129
, tableShapes(nullptr)
130
, builtins(nullptr)
131
, userdataTypes(AstName())
132
, functionTypes(nullptr)
133
, localTypes(nullptr)
134
, exprTypes(nullptr)
135
, builtinTypes(options.vectorType)
136
, names(names)
137
{
138
// preallocate some buffers that are very likely to grow anyway; this works around std::vector's inefficient growth policy for small arrays
139
localStack.reserve(16);
140
upvals.reserve(16);
141
}
142
143
int getLocalReg(AstLocal* local)
144
{
145
Local* l = locals.find(local);
146
147
return l && l->allocated ? l->reg : -1;
148
}
149
150
uint8_t getUpval(AstLocal* local)
151
{
152
for (size_t uid = 0; uid < upvals.size(); ++uid)
153
if (upvals[uid] == local)
154
return uint8_t(uid);
155
156
if (upvals.size() >= kMaxUpvalueCount)
157
CompileError::raise(
158
local->location, "Out of upvalue registers when trying to allocate %s: exceeded limit %d", local->name.value, kMaxUpvalueCount
159
);
160
161
// mark local as captured so that closeLocals emits LOP_CLOSEUPVALS accordingly
162
Variable* v = variables.find(local);
163
164
if (v && v->written)
165
locals[local].captured = true;
166
167
upvals.push_back(local);
168
169
return uint8_t(upvals.size() - 1);
170
}
171
172
bool alwaysTerminates(AstStat* node)
173
{
174
return Compile::alwaysTerminates(constants, node);
175
}
176
177
void emitLoadK(uint8_t target, int32_t cid)
178
{
179
LUAU_ASSERT(cid >= 0);
180
181
if (cid < 32768)
182
{
183
bytecode.emitAD(LOP_LOADK, target, int16_t(cid));
184
}
185
else
186
{
187
bytecode.emitAD(LOP_LOADKX, target, 0);
188
bytecode.emitAux(cid);
189
}
190
}
191
192
AstExprFunction* getFunctionExpr(AstExpr* node)
193
{
194
if (AstExprLocal* expr = node->as<AstExprLocal>())
195
{
196
Variable* lv = variables.find(expr->local);
197
198
if (!lv || lv->written || !lv->init)
199
return nullptr;
200
201
return getFunctionExpr(lv->init);
202
}
203
else if (AstExprGroup* expr = node->as<AstExprGroup>())
204
return getFunctionExpr(expr->expr);
205
else if (AstExprTypeAssertion* expr = node->as<AstExprTypeAssertion>())
206
return getFunctionExpr(expr->expr);
207
else
208
return node->as<AstExprFunction>();
209
}
210
211
uint32_t compileFunction(AstExprFunction* func, uint8_t& protoflags)
212
{
213
LUAU_TIMETRACE_SCOPE("Compiler::compileFunction", "Compiler");
214
215
if (func->debugname.value)
216
LUAU_TIMETRACE_ARGUMENT("name", func->debugname.value);
217
218
LUAU_ASSERT(!functions.contains(func));
219
LUAU_ASSERT(regTop == 0 && stackSize == 0 && localStack.empty() && upvals.empty());
220
221
RegScope rs(this);
222
223
bool self = func->self != 0;
224
uint32_t fid = bytecode.beginFunction(uint8_t(self + func->args.size), func->vararg);
225
226
setDebugLine(func);
227
228
if (func->vararg)
229
bytecode.emitABC(LOP_PREPVARARGS, uint8_t(self + func->args.size), 0, 0);
230
231
uint8_t args = allocReg(func, self + unsigned(func->args.size));
232
233
if (func->self)
234
pushLocal(func->self, args, kDefaultAllocPc);
235
236
for (size_t i = 0; i < func->args.size; ++i)
237
pushLocal(func->args.data[i], uint8_t(args + self + i), kDefaultAllocPc);
238
239
argCount = localStack.size();
240
241
AstStatBlock* stat = func->body;
242
243
bool terminatesEarly = false;
244
245
for (size_t i = 0; i < stat->body.size; ++i)
246
{
247
AstStat* bodyStat = stat->body.data[i];
248
compileStat(bodyStat);
249
250
if (alwaysTerminates(bodyStat))
251
{
252
terminatesEarly = true;
253
break;
254
}
255
}
256
257
// valid function bytecode must always end with RETURN
258
// we elide this if we're guaranteed to hit a RETURN statement regardless of the control flow
259
if (!terminatesEarly)
260
{
261
setDebugLineEnd(stat);
262
closeLocals(0);
263
264
bytecode.emitABC(LOP_RETURN, 0, 1, 0);
265
}
266
267
// constant folding may remove some upvalue refs from bytecode, so this puts them back
268
if (options.optimizationLevel >= 1 && options.debugLevel >= 2)
269
gatherConstUpvals(func);
270
271
bytecode.setDebugFunctionLineDefined(func->location.begin.line + 1);
272
273
if (options.debugLevel >= 1 && func->debugname.value)
274
bytecode.setDebugFunctionName(sref(func->debugname));
275
276
if (options.debugLevel >= 2 && !upvals.empty())
277
{
278
for (AstLocal* l : upvals)
279
bytecode.pushDebugUpval(sref(l->name));
280
}
281
282
if (options.typeInfoLevel >= 1)
283
{
284
for (AstLocal* l : upvals)
285
{
286
LuauBytecodeType ty = LBC_TYPE_ANY;
287
288
if (LuauBytecodeType* recordedTy = localTypes.find(l))
289
ty = *recordedTy;
290
291
bytecode.pushUpvalTypeInfo(ty);
292
}
293
}
294
295
if (options.optimizationLevel >= 1)
296
bytecode.foldJumps();
297
298
bytecode.expandJumps();
299
300
popLocals(0);
301
302
if (bytecode.getInstructionCount() > kMaxInstructionCount)
303
CompileError::raise(func->location, "Exceeded function instruction limit; split the function into parts to compile");
304
305
// note: we move types out of typeMap which is safe because compileFunction is only called once per function
306
if (std::string* funcType = functionTypes.find(func))
307
bytecode.setFunctionTypeInfo(std::move(*funcType));
308
309
// top-level code only executes once so it can be marked as cold if it has no loops; code with loops might be profitable to compile natively
310
if (func->functionDepth == 0 && !hasLoops)
311
protoflags |= LPF_NATIVE_COLD;
312
313
if (func->hasNativeAttribute())
314
protoflags |= LPF_NATIVE_FUNCTION;
315
316
bytecode.endFunction(uint8_t(stackSize), uint8_t(upvals.size()), protoflags);
317
318
Function& f = functions[func];
319
f.id = fid;
320
f.upvals = upvals;
321
322
// record information for inlining
323
if (options.optimizationLevel >= 2 && !func->vararg && !func->self && !getfenvUsed && !setfenvUsed)
324
{
325
if (FFlag::DebugLuauNoInline && func->hasAttribute(AstAttr::Type::DebugNoinline))
326
{
327
f.canInline = false;
328
}
329
else
330
{
331
f.canInline = true;
332
}
333
f.stackSize = stackSize;
334
f.costModel = modelCost(func->body, func->args.data, func->args.size, builtins, constants);
335
336
// track functions that only ever return a single value so that we can convert multret calls to fixedret calls
337
if (alwaysTerminates(func->body))
338
{
339
ReturnVisitor returnVisitor(this);
340
stat->visit(&returnVisitor);
341
f.returnsOne = returnVisitor.returnsOne;
342
}
343
}
344
345
upvals.clear(); // note: instead of std::move above, we copy & clear to preserve capacity for future pushes
346
stackSize = 0;
347
348
argCount = 0;
349
350
hasLoops = false;
351
352
return fid;
353
}
354
355
// returns true if node can return multiple values; may conservatively return true even if expr is known to return just a single value
356
bool isExprMultRet(AstExpr* node)
357
{
358
AstExprCall* expr = node->as<AstExprCall>();
359
if (!expr)
360
return node->is<AstExprVarargs>();
361
362
// conservative version, optimized for compilation throughput
363
if (options.optimizationLevel <= 1)
364
return true;
365
366
// handles builtin calls that can be constant-folded
367
// without this we may omit some optimizations eg compiling fast calls without use of FASTCALL2K
368
if (isConstant(expr))
369
return false;
370
371
// handles builtin calls that can't be constant-folded but are known to return one value
372
// note: optimizationLevel check is technically redundant but it's important that we never optimize based on builtins in O1
373
if (options.optimizationLevel >= 2)
374
{
375
if (int* bfid = builtins.find(expr); bfid && *bfid != LBF_NONE)
376
return getBuiltinInfo(*bfid).results != 1;
377
}
378
379
// handles local function calls where we know only one argument is returned
380
AstExprFunction* func = getFunctionExpr(expr->func);
381
Function* fi = func ? functions.find(func) : nullptr;
382
383
if (fi && fi->returnsOne)
384
return false;
385
386
// unrecognized call, so we conservatively assume multret
387
return true;
388
}
389
390
// note: this doesn't just clobber target (assuming it's temp), but also clobbers *all* allocated registers >= target!
391
// this is important to be able to support "multret" semantics due to Lua call frame structure
392
bool compileExprTempMultRet(AstExpr* node, uint8_t target)
393
{
394
if (AstExprCall* expr = node->as<AstExprCall>())
395
{
396
// Optimization: convert multret calls that always return one value to fixedret calls; this facilitates inlining/constant folding
397
if (options.optimizationLevel >= 2 && !isExprMultRet(node))
398
{
399
compileExprTemp(node, target);
400
return false;
401
}
402
403
// We temporarily swap out regTop to have targetTop work correctly...
404
// This is a crude hack but it's necessary for correctness :(
405
RegScope rs(this, target);
406
compileExprCall(expr, target, /* targetCount= */ 0, /* targetTop= */ true, /* multRet= */ true);
407
return true;
408
}
409
else if (AstExprVarargs* expr = node->as<AstExprVarargs>())
410
{
411
// We temporarily swap out regTop to have targetTop work correctly...
412
// This is a crude hack but it's necessary for correctness :(
413
RegScope rs(this, target);
414
compileExprVarargs(expr, target, /* targetCount= */ 0, /* multRet= */ true);
415
return true;
416
}
417
else
418
{
419
compileExprTemp(node, target);
420
return false;
421
}
422
}
423
424
// note: this doesn't just clobber target (assuming it's temp), but also clobbers *all* allocated registers >= target!
425
// this is important to be able to emit code that takes fewer registers and runs faster
426
void compileExprTempTop(AstExpr* node, uint8_t target)
427
{
428
// We temporarily swap out regTop to have targetTop work correctly...
429
// This is a crude hack but it's necessary for performance :(
430
// It makes sure that nested call expressions can use targetTop optimization and don't need to have too many registers
431
RegScope rs(this, target + 1);
432
compileExprTemp(node, target);
433
}
434
435
void compileExprVarargs(AstExprVarargs* expr, uint8_t target, uint8_t targetCount, bool multRet = false)
436
{
437
LUAU_ASSERT(targetCount < 255);
438
LUAU_ASSERT(!multRet || unsigned(target + targetCount) == regTop);
439
440
setDebugLine(expr); // normally compileExpr sets up line info, but compileExprVarargs can be called directly
441
442
bytecode.emitABC(LOP_GETVARARGS, target, multRet ? 0 : uint8_t(targetCount + 1), 0);
443
}
444
445
void compileExprSelectVararg(AstExprCall* expr, uint8_t target, uint8_t targetCount, bool targetTop, bool multRet, uint8_t regs)
446
{
447
LUAU_ASSERT(targetCount == 1);
448
LUAU_ASSERT(!expr->self);
449
LUAU_ASSERT(expr->args.size == 2 && expr->args.data[1]->is<AstExprVarargs>());
450
451
AstExpr* arg = expr->args.data[0];
452
453
uint8_t argreg;
454
455
if (int reg = getExprLocalReg(arg); reg >= 0)
456
argreg = uint8_t(reg);
457
else
458
{
459
argreg = uint8_t(regs + 1);
460
compileExprTempTop(arg, argreg);
461
}
462
463
size_t fastcallLabel = bytecode.emitLabel();
464
465
bytecode.emitABC(LOP_FASTCALL1, LBF_SELECT_VARARG, argreg, 0);
466
467
// note, these instructions are normally not executed and are used as a fallback for FASTCALL
468
// we can't use TempTop variant here because we need to make sure the arguments we already computed aren't overwritten
469
compileExprTemp(expr->func, regs);
470
471
if (argreg != regs + 1)
472
bytecode.emitABC(LOP_MOVE, uint8_t(regs + 1), argreg, 0);
473
474
bytecode.emitABC(LOP_GETVARARGS, uint8_t(regs + 2), 0, 0);
475
476
size_t callLabel = bytecode.emitLabel();
477
if (!bytecode.patchSkipC(fastcallLabel, callLabel))
478
CompileError::raise(expr->func->location, "Exceeded jump distance limit; simplify the code to compile");
479
480
// note, this is always multCall (last argument is variadic)
481
bytecode.emitABC(LOP_CALL, regs, 0, multRet ? 0 : uint8_t(targetCount + 1));
482
483
// if we didn't output results directly to target, we need to move them
484
if (!targetTop)
485
{
486
for (size_t i = 0; i < targetCount; ++i)
487
bytecode.emitABC(LOP_MOVE, uint8_t(target + i), uint8_t(regs + i), 0);
488
}
489
}
490
491
void compileExprFastcallN(
492
AstExprCall* expr,
493
uint8_t target,
494
uint8_t targetCount,
495
bool targetTop,
496
bool multRet,
497
uint8_t regs,
498
int bfid,
499
int bfK = -1
500
)
501
{
502
LUAU_ASSERT(!expr->self);
503
LUAU_ASSERT(expr->args.size >= 1);
504
LUAU_ASSERT(expr->args.size <= 3);
505
LUAU_ASSERT(bfid == LBF_BIT32_EXTRACTK ? bfK >= 0 : bfK < 0);
506
LUAU_ASSERT(targetCount < 255);
507
508
LuauOpcode opc = LOP_NOP;
509
510
if (expr->args.size == 1)
511
opc = LOP_FASTCALL1;
512
else if (bfK >= 0 || (expr->args.size == 2 && isConstant(expr->args.data[1])))
513
opc = LOP_FASTCALL2K;
514
else if (expr->args.size == 2)
515
opc = LOP_FASTCALL2;
516
else
517
opc = LOP_FASTCALL3;
518
519
uint32_t args[3] = {};
520
521
for (size_t i = 0; i < expr->args.size; ++i)
522
{
523
if (i > 0 && opc == LOP_FASTCALL2K)
524
{
525
int32_t cid = getConstantIndex(expr->args.data[i]);
526
if (cid < 0)
527
CompileError::raise(expr->location, "Exceeded constant limit; simplify the code to compile");
528
529
args[i] = cid;
530
}
531
else if (int reg = getExprLocalReg(expr->args.data[i]); reg >= 0)
532
{
533
args[i] = uint8_t(reg);
534
}
535
else
536
{
537
args[i] = uint8_t(regs + 1 + i);
538
compileExprTempTop(expr->args.data[i], uint8_t(args[i]));
539
}
540
}
541
542
size_t fastcallLabel = bytecode.emitLabel();
543
544
bytecode.emitABC(opc, uint8_t(bfid), uint8_t(args[0]), 0);
545
546
if (opc == LOP_FASTCALL3)
547
{
548
LUAU_ASSERT(bfK < 0);
549
bytecode.emitAux(args[1] | (args[2] << 8));
550
}
551
else if (opc != LOP_FASTCALL1)
552
{
553
bytecode.emitAux(bfK >= 0 ? bfK : args[1]);
554
}
555
556
// Set up a traditional Lua stack for the subsequent LOP_CALL.
557
// Note, as with other instructions that immediately follow FASTCALL, these are normally not executed and are used as a fallback for
558
// these FASTCALL variants.
559
for (size_t i = 0; i < expr->args.size; ++i)
560
{
561
if (i > 0 && opc == LOP_FASTCALL2K)
562
emitLoadK(uint8_t(regs + 1 + i), args[i]);
563
else if (args[i] != regs + 1 + i)
564
bytecode.emitABC(LOP_MOVE, uint8_t(regs + 1 + i), uint8_t(args[i]), 0);
565
}
566
567
// note, these instructions are normally not executed and are used as a fallback for FASTCALL
568
// we can't use TempTop variant here because we need to make sure the arguments we already computed aren't overwritten
569
compileExprTemp(expr->func, regs);
570
571
size_t callLabel = bytecode.emitLabel();
572
573
// FASTCALL will skip over the instructions needed to compute function and jump over CALL which must immediately follow the instruction
574
// sequence after FASTCALL
575
if (!bytecode.patchSkipC(fastcallLabel, callLabel))
576
CompileError::raise(expr->func->location, "Exceeded jump distance limit; simplify the code to compile");
577
578
bytecode.emitABC(LOP_CALL, regs, uint8_t(expr->args.size + 1), multRet ? 0 : uint8_t(targetCount + 1));
579
580
// if we didn't output results directly to target, we need to move them
581
if (!targetTop)
582
{
583
for (size_t i = 0; i < targetCount; ++i)
584
bytecode.emitABC(LOP_MOVE, uint8_t(target + i), uint8_t(regs + i), 0);
585
}
586
}
587
588
bool tryCompileInlinedCall(
589
AstExprCall* expr,
590
AstExprFunction* func,
591
uint8_t target,
592
uint8_t targetCount,
593
bool multRet,
594
int thresholdBase,
595
int thresholdMaxBoost,
596
int depthLimit
597
)
598
{
599
Function* fi = functions.find(func);
600
LUAU_ASSERT(fi);
601
602
// make sure we have enough register space
603
if (regTop > 128 || fi->stackSize > 32)
604
{
605
bytecode.addDebugRemark("inlining failed: high register pressure");
606
return false;
607
}
608
609
// we should ideally aggregate the costs during recursive inlining, but for now simply limit the depth
610
if (int(inlineFrames.size()) >= depthLimit)
611
{
612
bytecode.addDebugRemark("inlining failed: too many inlined frames");
613
return false;
614
}
615
616
// compiling recursive inlining is difficult because we share constant/variable state but need to bind variables to different registers
617
for (InlineFrame& frame : inlineFrames)
618
if (frame.func == func)
619
{
620
bytecode.addDebugRemark("inlining failed: can't inline recursive calls");
621
return false;
622
}
623
624
// we can't inline multret functions because the caller expects L->top to be adjusted:
625
// - inlined return compiles to a JUMP, and we don't have an instruction that adjusts L->top arbitrarily
626
// - even if we did, right now all L->top adjustments are immediately consumed by the next instruction, and for now we want to preserve that
627
// - additionally, we can't easily compile multret expressions into designated target as computed call arguments will get clobbered
628
if (multRet)
629
{
630
bytecode.addDebugRemark("inlining failed: can't convert fixed returns to multret");
631
return false;
632
}
633
634
// compute constant bitvector for all arguments to feed the cost model
635
bool varc[8] = {};
636
bool hasConstant = false;
637
for (size_t i = 0; i < func->args.size && i < expr->args.size && i < 8; ++i)
638
{
639
if (isConstant(expr->args.data[i]))
640
{
641
varc[i] = true;
642
hasConstant = true;
643
}
644
}
645
646
// if the last argument only returns a single value, all following arguments are nil
647
if (expr->args.size != 0 && !isExprMultRet(expr->args.data[expr->args.size - 1]))
648
{
649
for (size_t i = expr->args.size; i < func->args.size && i < 8; ++i)
650
{
651
varc[i] = true;
652
hasConstant = true;
653
}
654
}
655
656
// If we had constant arguments that can affect the cost model of this specific call in non-trivial ways
657
uint64_t callCostModel = fi->costModel;
658
659
if (hasConstant)
660
callCostModel = costModelInlinedCall(expr, func);
661
662
// we use a dynamic cost threshold that's based on the fixed limit boosted by the cost advantage we gain due to inlining
663
int inlinedCost = computeCost(callCostModel, varc, std::min(int(func->args.size), 8));
664
int baselineCost = computeCost(fi->costModel, nullptr, 0) + 3;
665
int inlineProfit = (inlinedCost == 0) ? thresholdMaxBoost : std::min(thresholdMaxBoost, 100 * baselineCost / inlinedCost);
666
667
int threshold = thresholdBase * inlineProfit / 100;
668
669
if (inlinedCost > threshold)
670
{
671
bytecode.addDebugRemark("inlining failed: too expensive (cost %d, profit %.2fx)", inlinedCost, double(inlineProfit) / 100);
672
return false;
673
}
674
675
bytecode.addDebugRemark(
676
"inlining succeeded (cost %d, profit %.2fx, depth %d)", inlinedCost, double(inlineProfit) / 100, int(inlineFrames.size())
677
);
678
679
compileInlinedCall(expr, func, target, targetCount);
680
return true;
681
}
682
683
uint64_t costModelInlinedCall(AstExprCall* expr, AstExprFunction* func)
684
{
685
for (size_t i = 0; i < func->args.size; ++i)
686
{
687
AstLocal* var = func->args.data[i];
688
AstExpr* arg = i < expr->args.size ? expr->args.data[i] : nullptr;
689
690
// last expression is a multret, there are no constant for it and it will fill all values
691
if (i + 1 == expr->args.size && func->args.size > expr->args.size && isExprMultRet(arg))
692
break;
693
694
// variable gets mutated at some point, so we do not have a constant for it
695
if (Variable* vv = variables.find(var); vv && vv->written)
696
continue;
697
698
if (arg == nullptr)
699
locstants[var] = {Constant::Type_Nil};
700
else if (const Constant* cv = constants.find(arg); cv && cv->type != Constant::Type_Unknown)
701
locstants[var] = *cv;
702
}
703
704
// fold constant values updated above into expressions in the function body
705
foldConstants(constants, variables, locstants, builtinsFold, builtinsFoldLibraryK, options.libraryMemberConstantCb, func->body, names);
706
707
// model the cost of the function evaluated with current constants
708
uint64_t cost = modelCost(func->body, func->args.data, func->args.size, builtins, constants);
709
710
// clean up constant state for future inlining attempts
711
for (size_t i = 0; i < func->args.size; ++i)
712
{
713
if (Constant* var = locstants.find(func->args.data[i]))
714
var->type = Constant::Type_Unknown;
715
}
716
717
foldConstants(constants, variables, locstants, builtinsFold, builtinsFoldLibraryK, options.libraryMemberConstantCb, func->body, names);
718
719
return cost;
720
}
721
722
void compileInlinedCall(AstExprCall* expr, AstExprFunction* func, uint8_t target, uint8_t targetCount)
723
{
724
RegScope rs(this);
725
726
size_t oldLocals = localStack.size();
727
728
std::vector<InlineArg> args;
729
args.reserve(func->args.size);
730
731
// evaluate all arguments; note that we don't emit code for constant arguments (relying on constant folding)
732
// note that compiler state (variable registers/values) does not change here - we defer that to a separate loop below to handle nested calls
733
for (size_t i = 0; i < func->args.size; ++i)
734
{
735
AstLocal* var = func->args.data[i];
736
AstExpr* arg = i < expr->args.size ? expr->args.data[i] : nullptr;
737
738
if (i + 1 == expr->args.size && func->args.size > expr->args.size && isExprMultRet(arg))
739
{
740
// if the last argument can return multiple values, we need to compute all of them into the remaining arguments
741
unsigned int tail = unsigned(func->args.size - expr->args.size) + 1;
742
uint8_t reg = allocReg(arg, tail);
743
uint32_t allocpc = bytecode.getDebugPC();
744
745
if (AstExprCall* expr = arg->as<AstExprCall>())
746
compileExprCall(expr, reg, tail, /* targetTop= */ true);
747
else if (AstExprVarargs* expr = arg->as<AstExprVarargs>())
748
compileExprVarargs(expr, reg, tail);
749
else
750
LUAU_ASSERT(!"Unexpected expression type");
751
752
for (size_t j = i; j < func->args.size; ++j)
753
args.push_back({func->args.data[j], uint8_t(reg + (j - i)), {Constant::Type_Unknown}, allocpc});
754
755
// all remaining function arguments have been allocated and assigned to
756
break;
757
}
758
else if (Variable* vv = variables.find(var); vv && vv->written)
759
{
760
// if the argument is mutated, we need to allocate a fresh register even if it's a constant
761
uint8_t reg = allocReg(arg, 1u);
762
uint32_t allocpc = bytecode.getDebugPC();
763
764
if (arg)
765
compileExprTemp(arg, reg);
766
else
767
bytecode.emitABC(LOP_LOADNIL, reg, 0, 0);
768
769
args.push_back({var, reg, {Constant::Type_Unknown}, allocpc});
770
}
771
else if (arg == nullptr)
772
{
773
// since the argument is not mutated, we can simply fold the value into the expressions that need it
774
args.push_back({var, kInvalidReg, {Constant::Type_Nil}});
775
}
776
else if (const Constant* cv = constants.find(arg); cv && cv->type != Constant::Type_Unknown)
777
{
778
// since the argument is not mutated, we can simply fold the value into the expressions that need it
779
args.push_back({var, kInvalidReg, *cv});
780
}
781
else
782
{
783
AstExprLocal* le = getExprLocal(arg);
784
Variable* lv = le ? variables.find(le->local) : nullptr;
785
786
// if the argument is a local that isn't mutated, we will simply reuse the existing register
787
if (int reg = le ? getExprLocalReg(le) : -1; reg >= 0 && (!lv || !lv->written))
788
{
789
args.push_back({var, uint8_t(reg), {Constant::Type_Unknown}, kDefaultAllocPc, lv ? lv->init : nullptr});
790
}
791
else
792
{
793
uint8_t temp = allocReg(arg, 1u);
794
uint32_t allocpc = bytecode.getDebugPC();
795
796
compileExprTemp(arg, temp);
797
798
args.push_back({var, temp, {Constant::Type_Unknown}, allocpc, arg});
799
}
800
}
801
}
802
803
// evaluate extra expressions for side effects
804
for (size_t i = func->args.size; i < expr->args.size; ++i)
805
compileExprSide(expr->args.data[i]);
806
807
// apply all evaluated arguments to the compiler state
808
// note: locals use current startpc for debug info, although some of them have been computed earlier; this is similar to compileStatLocal
809
for (InlineArg& arg : args)
810
{
811
if (arg.value.type == Constant::Type_Unknown)
812
{
813
pushLocal(arg.local, arg.reg, arg.allocpc);
814
815
if (arg.init)
816
{
817
if (Variable* lv = variables.find(arg.local))
818
lv->init = arg.init;
819
}
820
}
821
else
822
{
823
locstants[arg.local] = arg.value;
824
}
825
}
826
827
// the inline frame will be used to compile return statements as well as to reject recursive inlining attempts
828
inlineFrames.push_back({func, oldLocals, target, targetCount});
829
830
// this pass tracks which calls are builtins and can be compiled more efficiently
831
analyzeBuiltins(inlineBuiltins, globals, variables, options, func->body, names);
832
833
// If we found new builtins, apply them, but record which expressions we changed so we can undo later
834
if (!inlineBuiltins.empty())
835
{
836
for (auto [callExpr, bfid] : inlineBuiltins)
837
{
838
int& builtin = builtins[callExpr]; // If there was no builtin previously, we will get LBF_NONE
839
840
if (bfid != builtin)
841
{
842
inlineBuiltinsBackup[callExpr] = builtin;
843
builtin = bfid;
844
}
845
}
846
847
inlineBuiltins.clear();
848
}
849
850
// fold constant values updated above into expressions in the function body
851
foldConstants(constants, variables, locstants, builtinsFold, builtinsFoldLibraryK, options.libraryMemberConstantCb, func->body, names);
852
853
bool terminatesEarly = false;
854
855
for (size_t i = 0; i < func->body->body.size; ++i)
856
{
857
AstStat* stat = func->body->body.data[i];
858
compileStat(stat);
859
860
if (alwaysTerminates(stat))
861
{
862
terminatesEarly = true;
863
864
// Remove the last jump which jumps directly to the next instruction
865
InlineFrame& currFrame = inlineFrames.back();
866
if (!currFrame.returnJumps.empty() && currFrame.returnJumps.back() == bytecode.emitLabel() - 1)
867
{
868
bytecode.undoEmit(LOP_JUMP);
869
currFrame.returnJumps.pop_back();
870
}
871
break;
872
}
873
}
874
875
// for the fallthrough path we need to ensure we clear out target registers
876
if (!terminatesEarly)
877
{
878
for (size_t i = 0; i < targetCount; ++i)
879
bytecode.emitABC(LOP_LOADNIL, uint8_t(target + i), 0, 0);
880
881
closeLocals(oldLocals);
882
}
883
884
popLocals(oldLocals);
885
886
size_t returnLabel = bytecode.emitLabel();
887
patchJumps(expr, inlineFrames.back().returnJumps, returnLabel);
888
889
inlineFrames.pop_back();
890
891
// clean up constant state for future inlining attempts
892
for (size_t i = 0; i < func->args.size; ++i)
893
{
894
AstLocal* local = func->args.data[i];
895
896
if (Constant* var = locstants.find(local))
897
var->type = Constant::Type_Unknown;
898
899
if (Variable* lv = variables.find(local))
900
lv->init = nullptr;
901
}
902
903
if (!inlineBuiltinsBackup.empty())
904
{
905
for (auto [callExpr, bfid] : inlineBuiltinsBackup)
906
builtins[callExpr] = bfid;
907
908
inlineBuiltinsBackup.clear();
909
}
910
911
foldConstants(constants, variables, locstants, builtinsFold, builtinsFoldLibraryK, options.libraryMemberConstantCb, func->body, names);
912
}
913
914
void compileExprCall(AstExprCall* expr, uint8_t target, uint8_t targetCount, bool targetTop = false, bool multRet = false)
915
{
916
LUAU_ASSERT(targetCount < 255);
917
LUAU_ASSERT(!targetTop || unsigned(target + targetCount) == regTop);
918
919
setDebugLine(expr); // normally compileExpr sets up line info, but compileExprCall can be called directly
920
921
// try inlining the function
922
if (options.optimizationLevel >= 2 && !expr->self)
923
{
924
AstExprFunction* func = getFunctionExpr(expr->func);
925
Function* fi = func ? functions.find(func) : nullptr;
926
927
if (fi && fi->canInline &&
928
tryCompileInlinedCall(
929
expr,
930
func,
931
target,
932
targetCount,
933
multRet,
934
FInt::LuauCompileInlineThreshold,
935
FInt::LuauCompileInlineThresholdMaxBoost,
936
FInt::LuauCompileInlineDepth
937
))
938
return;
939
940
// add a debug remark for cases when we didn't even call tryCompileInlinedCall
941
if (func && !(fi && fi->canInline))
942
{
943
if (func->vararg)
944
bytecode.addDebugRemark("inlining failed: function is variadic");
945
else if (!fi)
946
bytecode.addDebugRemark("inlining failed: can't inline recursive calls");
947
else if (getfenvUsed || setfenvUsed)
948
bytecode.addDebugRemark("inlining failed: module uses getfenv/setfenv");
949
}
950
}
951
952
RegScope rs(this);
953
954
unsigned int regCount = std::max(unsigned(1 + expr->self + expr->args.size), unsigned(targetCount));
955
956
// Optimization: if target points to the top of the stack, we can start the call at oldTop - 1 and won't need MOVE at the end
957
uint8_t regs = targetTop ? allocReg(expr, regCount - targetCount) - targetCount : allocReg(expr, regCount);
958
959
uint8_t selfreg = 0;
960
961
int bfid = -1;
962
963
if (options.optimizationLevel >= 1 && !expr->self)
964
{
965
if (const int* id = builtins.find(expr); id && *id != LBF_NONE)
966
bfid = *id;
967
}
968
969
if (bfid >= 0 && bytecode.needsDebugRemarks())
970
{
971
Builtin builtin = getBuiltin(expr->func, globals, variables);
972
bool lastMult = expr->args.size > 0 && isExprMultRet(expr->args.data[expr->args.size - 1]);
973
974
if (builtin.object.value)
975
bytecode.addDebugRemark("builtin %s.%s/%d%s", builtin.object.value, builtin.method.value, int(expr->args.size), lastMult ? "+" : "");
976
else if (builtin.method.value)
977
bytecode.addDebugRemark("builtin %s/%d%s", builtin.method.value, int(expr->args.size), lastMult ? "+" : "");
978
}
979
980
if (bfid == LBF_SELECT_VARARG)
981
{
982
// Optimization: compile select(_, ...) as FASTCALL1; the builtin will read variadic arguments directly
983
// note: for now we restrict this to single-return expressions since our runtime code doesn't deal with general cases
984
if (multRet == false && targetCount == 1)
985
return compileExprSelectVararg(expr, target, targetCount, targetTop, multRet, regs);
986
else
987
bfid = -1;
988
}
989
990
// Optimization: for bit32.extract with constant in-range f/w we compile using FASTCALL2K and a special builtin
991
if (bfid == LBF_BIT32_EXTRACT && expr->args.size == 3 && isConstant(expr->args.data[1]) && isConstant(expr->args.data[2]))
992
{
993
Constant fc = getConstant(expr->args.data[1]);
994
Constant wc = getConstant(expr->args.data[2]);
995
996
int fi = fc.type == Constant::Type_Number ? int(fc.valueNumber) : -1;
997
int wi = wc.type == Constant::Type_Number ? int(wc.valueNumber) : -1;
998
999
if (fi >= 0 && wi > 0 && fi + wi <= 32)
1000
{
1001
int fwp = fi | ((wi - 1) << 5);
1002
int32_t cid = bytecode.addConstantNumber(fwp);
1003
if (cid < 0)
1004
CompileError::raise(expr->location, "Exceeded constant limit; simplify the code to compile");
1005
1006
return compileExprFastcallN(expr, target, targetCount, targetTop, multRet, regs, LBF_BIT32_EXTRACTK, cid);
1007
}
1008
}
1009
1010
unsigned maxFastcallArgs = 2;
1011
1012
// Fastcall with 3 arguments is only used if it can help save one or more move instructions
1013
if (bfid >= 0 && expr->args.size == 3)
1014
{
1015
for (size_t i = 0; i < expr->args.size; ++i)
1016
{
1017
if (int reg = getExprLocalReg(expr->args.data[i]); reg >= 0)
1018
{
1019
maxFastcallArgs = 3;
1020
break;
1021
}
1022
}
1023
}
1024
1025
// Optimization: for 1/2/3 argument fast calls use specialized opcodes
1026
if (bfid >= 0 && expr->args.size >= 1 && expr->args.size <= maxFastcallArgs)
1027
{
1028
if (!isExprMultRet(expr->args.data[expr->args.size - 1]))
1029
{
1030
return compileExprFastcallN(expr, target, targetCount, targetTop, multRet, regs, bfid);
1031
}
1032
else if (options.optimizationLevel >= 2)
1033
{
1034
// when a builtin is none-safe with matching arity, even if the last expression returns 0 or >1 arguments,
1035
// we can rely on the behavior of the function being the same (none-safe means nil and none are interchangeable)
1036
BuiltinInfo info = getBuiltinInfo(bfid);
1037
if (int(expr->args.size) == info.params && (info.flags & BuiltinInfo::Flag_NoneSafe) != 0)
1038
return compileExprFastcallN(expr, target, targetCount, targetTop, multRet, regs, bfid);
1039
}
1040
}
1041
1042
if (expr->self)
1043
{
1044
AstExprIndexName* fi = expr->func->as<AstExprIndexName>();
1045
LUAU_ASSERT(fi);
1046
1047
// Optimization: use local register directly in NAMECALL if possible
1048
if (int reg = getExprLocalReg(fi->expr); reg >= 0)
1049
{
1050
selfreg = uint8_t(reg);
1051
}
1052
else
1053
{
1054
// Note: to be able to compile very deeply nested self call chains (obj:method1():method2():...), we need to be able to do this in
1055
// finite stack space NAMECALL will happily move object from regs to regs+1 but we need to compute it into regs so that
1056
// compileExprTempTop doesn't increase stack usage for every recursive call
1057
selfreg = regs;
1058
1059
compileExprTempTop(fi->expr, selfreg);
1060
}
1061
}
1062
else if (bfid < 0)
1063
{
1064
compileExprTempTop(expr->func, regs);
1065
}
1066
1067
bool multCall = false;
1068
1069
for (size_t i = 0; i < expr->args.size; ++i)
1070
if (i + 1 == expr->args.size)
1071
multCall = compileExprTempMultRet(expr->args.data[i], uint8_t(regs + 1 + expr->self + i));
1072
else
1073
compileExprTempTop(expr->args.data[i], uint8_t(regs + 1 + expr->self + i));
1074
1075
setDebugLineEnd(expr->func);
1076
1077
if (expr->self)
1078
{
1079
AstExprIndexName* fi = expr->func->as<AstExprIndexName>();
1080
LUAU_ASSERT(fi);
1081
1082
setDebugLine(fi->indexLocation);
1083
1084
BytecodeBuilder::StringRef iname = sref(fi->index);
1085
int32_t cid = bytecode.addConstantString(iname);
1086
if (cid < 0)
1087
CompileError::raise(fi->location, "Exceeded constant limit; simplify the code to compile");
1088
1089
bytecode.emitABC(LOP_NAMECALL, regs, selfreg, uint8_t(BytecodeBuilder::getStringHash(iname)));
1090
bytecode.emitAux(cid);
1091
1092
hintTemporaryExprRegType(fi->expr, selfreg, LBC_TYPE_TABLE, /* instLength */ 2);
1093
}
1094
else if (bfid >= 0)
1095
{
1096
size_t fastcallLabel = bytecode.emitLabel();
1097
bytecode.emitABC(LOP_FASTCALL, uint8_t(bfid), 0, 0);
1098
1099
// note, these instructions are normally not executed and are used as a fallback for FASTCALL
1100
// we can't use TempTop variant here because we need to make sure the arguments we already computed aren't overwritten
1101
compileExprTemp(expr->func, regs);
1102
1103
size_t callLabel = bytecode.emitLabel();
1104
1105
// FASTCALL will skip over the instructions needed to compute function and jump over CALL which must immediately follow the instruction
1106
// sequence after FASTCALL
1107
if (!bytecode.patchSkipC(fastcallLabel, callLabel))
1108
CompileError::raise(expr->func->location, "Exceeded jump distance limit; simplify the code to compile");
1109
}
1110
1111
bytecode.emitABC(LOP_CALL, regs, multCall ? 0 : uint8_t(expr->self + expr->args.size + 1), multRet ? 0 : uint8_t(targetCount + 1));
1112
1113
// if we didn't output results directly to target, we need to move them
1114
if (!targetTop)
1115
{
1116
for (size_t i = 0; i < targetCount; ++i)
1117
bytecode.emitABC(LOP_MOVE, uint8_t(target + i), uint8_t(regs + i), 0);
1118
}
1119
}
1120
1121
bool shouldShareClosure(AstExprFunction* func)
1122
{
1123
const Function* f = functions.find(func);
1124
if (!f)
1125
return false;
1126
1127
for (AstLocal* uv : f->upvals)
1128
{
1129
Variable* ul = variables.find(uv);
1130
1131
if (!ul)
1132
return false;
1133
1134
if (ul->written)
1135
return false;
1136
1137
// it's technically safe to share closures whenever all upvalues are immutable
1138
// this is because of a runtime equality check in DUPCLOSURE.
1139
// however, this results in frequent de-optimization and increases the set of reachable objects, making some temporary objects permanent
1140
// instead we apply a heuristic: we share closures if they refer to top-level upvalues, or closures that refer to top-level upvalues
1141
// this will only de-optimize (outside of fenv changes) if top level code is executed twice with different results.
1142
if (uv->functionDepth != 0 || uv->loopDepth != 0)
1143
{
1144
AstExprFunction* uf = ul->init ? ul->init->as<AstExprFunction>() : nullptr;
1145
if (!uf)
1146
return false;
1147
1148
if (uf != func && !shouldShareClosure(uf))
1149
return false;
1150
}
1151
}
1152
1153
return true;
1154
}
1155
1156
void compileExprFunction(AstExprFunction* expr, uint8_t target)
1157
{
1158
RegScope rs(this);
1159
1160
const Function* f = functions.find(expr);
1161
LUAU_ASSERT(f);
1162
1163
// when the closure has upvalues we'll use this to create the closure at runtime
1164
// when the closure has no upvalues, we use constant closures that technically don't rely on the child function list
1165
// however, it's still important to add the child function because debugger relies on the function hierarchy when setting breakpoints
1166
int16_t pid = bytecode.addChildFunction(f->id);
1167
if (pid < 0)
1168
CompileError::raise(expr->location, "Exceeded closure limit; simplify the code to compile");
1169
1170
// we use a scratch vector to reduce allocations; this is safe since compileExprFunction is not reentrant
1171
captures.clear();
1172
captures.reserve(f->upvals.size());
1173
1174
for (AstLocal* uv : f->upvals)
1175
{
1176
LUAU_ASSERT(uv->functionDepth < expr->functionDepth);
1177
1178
if (int reg = getLocalReg(uv); reg >= 0)
1179
{
1180
// note: we can't check if uv is an upvalue in the current frame because inlining can migrate from upvalues to locals
1181
Variable* ul = variables.find(uv);
1182
bool immutable = !ul || !ul->written;
1183
1184
captures.push_back({immutable ? LCT_VAL : LCT_REF, uint8_t(reg)});
1185
}
1186
else if (const Constant* uc = locstants.find(uv); uc && uc->type != Constant::Type_Unknown)
1187
{
1188
// inlining can result in an upvalue capture of a constant, in which case we can't capture without a temporary register
1189
uint8_t reg = allocReg(expr, 1u);
1190
compileExprConstant(expr, uc, reg);
1191
1192
captures.push_back({LCT_VAL, reg});
1193
}
1194
else
1195
{
1196
LUAU_ASSERT(uv->functionDepth < expr->functionDepth - 1);
1197
1198
// get upvalue from parent frame
1199
// note: this will add uv to the current upvalue list if necessary
1200
uint8_t uid = getUpval(uv);
1201
1202
captures.push_back({LCT_UPVAL, uid});
1203
}
1204
}
1205
1206
// Optimization: when closure has no upvalues, or upvalues are safe to share, instead of allocating it every time we can share closure
1207
// objects (this breaks assumptions about function identity which can lead to setfenv not working as expected, so we disable this when it
1208
// is used)
1209
int16_t shared = -1;
1210
1211
if (options.optimizationLevel >= 1 && shouldShareClosure(expr) && !setfenvUsed)
1212
{
1213
int32_t cid = bytecode.addConstantClosure(f->id);
1214
1215
if (cid >= 0 && cid < 32768)
1216
shared = int16_t(cid);
1217
}
1218
1219
if (shared < 0)
1220
bytecode.addDebugRemark("allocation: closure with %d upvalues", int(captures.size()));
1221
1222
if (shared >= 0)
1223
bytecode.emitAD(LOP_DUPCLOSURE, target, shared);
1224
else
1225
bytecode.emitAD(LOP_NEWCLOSURE, target, pid);
1226
1227
for (const Capture& c : captures)
1228
{
1229
bytecode.emitABC(LOP_CAPTURE, uint8_t(c.type), c.data, 0);
1230
}
1231
}
1232
1233
LuauOpcode getUnaryOp(AstExprUnary::Op op)
1234
{
1235
switch (op)
1236
{
1237
case AstExprUnary::Not:
1238
return LOP_NOT;
1239
1240
case AstExprUnary::Minus:
1241
return LOP_MINUS;
1242
1243
case AstExprUnary::Len:
1244
return LOP_LENGTH;
1245
1246
default:
1247
LUAU_ASSERT(!"Unexpected unary operation");
1248
return LOP_NOP;
1249
}
1250
}
1251
1252
LuauOpcode getBinaryOpArith(AstExprBinary::Op op, bool k = false)
1253
{
1254
switch (op)
1255
{
1256
case AstExprBinary::Add:
1257
return k ? LOP_ADDK : LOP_ADD;
1258
1259
case AstExprBinary::Sub:
1260
return k ? LOP_SUBK : LOP_SUB;
1261
1262
case AstExprBinary::Mul:
1263
return k ? LOP_MULK : LOP_MUL;
1264
1265
case AstExprBinary::Div:
1266
return k ? LOP_DIVK : LOP_DIV;
1267
1268
case AstExprBinary::FloorDiv:
1269
return k ? LOP_IDIVK : LOP_IDIV;
1270
1271
case AstExprBinary::Mod:
1272
return k ? LOP_MODK : LOP_MOD;
1273
1274
case AstExprBinary::Pow:
1275
return k ? LOP_POWK : LOP_POW;
1276
1277
default:
1278
LUAU_ASSERT(!"Unexpected binary operation");
1279
return LOP_NOP;
1280
}
1281
}
1282
1283
LuauOpcode getJumpOpCompare(AstExprBinary::Op op, bool not_ = false)
1284
{
1285
switch (op)
1286
{
1287
case AstExprBinary::CompareNe:
1288
return not_ ? LOP_JUMPIFEQ : LOP_JUMPIFNOTEQ;
1289
1290
case AstExprBinary::CompareEq:
1291
return not_ ? LOP_JUMPIFNOTEQ : LOP_JUMPIFEQ;
1292
1293
case AstExprBinary::CompareLt:
1294
case AstExprBinary::CompareGt:
1295
return not_ ? LOP_JUMPIFNOTLT : LOP_JUMPIFLT;
1296
1297
case AstExprBinary::CompareLe:
1298
case AstExprBinary::CompareGe:
1299
return not_ ? LOP_JUMPIFNOTLE : LOP_JUMPIFLE;
1300
1301
default:
1302
LUAU_ASSERT(!"Unexpected binary operation");
1303
return LOP_NOP;
1304
}
1305
}
1306
1307
bool isConstant(AstExpr* node)
1308
{
1309
const Constant* cv = constants.find(node);
1310
1311
return (cv != nullptr) && cv->type != Constant::Type_Unknown;
1312
}
1313
1314
bool isConstantTrue(AstExpr* node)
1315
{
1316
return Compile::isConstantTrue(constants, node);
1317
}
1318
1319
bool isConstantFalse(AstExpr* node)
1320
{
1321
return Compile::isConstantFalse(constants, node);
1322
}
1323
1324
bool isConstantVector(AstExpr* node)
1325
{
1326
const Constant* cv = constants.find(node);
1327
1328
return (cv != nullptr) && cv->type == Constant::Type_Vector;
1329
}
1330
1331
bool isConstantInteger(AstExpr* node)
1332
{
1333
const Constant* cv = constants.find(node);
1334
1335
return cv && cv->type == Constant::Type_Integer;
1336
}
1337
1338
Constant getConstant(AstExpr* node)
1339
{
1340
const Constant* cv = constants.find(node);
1341
1342
return cv ? *cv : Constant{Constant::Type_Unknown};
1343
}
1344
1345
size_t compileCompareJump(AstExprBinary* expr, bool not_ = false)
1346
{
1347
RegScope rs(this);
1348
1349
bool isEq = (expr->op == AstExprBinary::CompareEq || expr->op == AstExprBinary::CompareNe);
1350
AstExpr* left = expr->left;
1351
AstExpr* right = expr->right;
1352
1353
bool operandIsConstant = isConstant(right);
1354
if (isEq && !operandIsConstant)
1355
{
1356
operandIsConstant = isConstant(left);
1357
if (operandIsConstant)
1358
std::swap(left, right);
1359
}
1360
1361
// disable fast path for vectors and integers because supporting it would require a new opcode
1362
if (operandIsConstant && (isConstantVector(right) || (FFlag::LuauIntegerType && isConstantInteger(right))))
1363
operandIsConstant = false;
1364
1365
uint8_t rl = compileExprAuto(left, rs);
1366
1367
if (isEq && operandIsConstant)
1368
{
1369
const Constant* cv = constants.find(right);
1370
LUAU_ASSERT(cv && cv->type != Constant::Type_Unknown);
1371
1372
LuauOpcode opc = LOP_NOP;
1373
int32_t cid = -1;
1374
uint32_t flip = (expr->op == AstExprBinary::CompareEq) == not_ ? 0x80000000 : 0;
1375
1376
switch (cv->type)
1377
{
1378
case Constant::Type_Nil:
1379
opc = LOP_JUMPXEQKNIL;
1380
cid = 0;
1381
break;
1382
1383
case Constant::Type_Boolean:
1384
opc = LOP_JUMPXEQKB;
1385
cid = cv->valueBoolean;
1386
break;
1387
1388
case Constant::Type_Number:
1389
opc = LOP_JUMPXEQKN;
1390
cid = getConstantIndex(right);
1391
break;
1392
1393
case Constant::Type_String:
1394
opc = LOP_JUMPXEQKS;
1395
cid = getConstantIndex(right);
1396
break;
1397
1398
default:
1399
LUAU_ASSERT(!"Unexpected constant type");
1400
}
1401
1402
if (cid < 0)
1403
CompileError::raise(expr->location, "Exceeded constant limit; simplify the code to compile");
1404
1405
size_t jumpLabel = bytecode.emitLabel();
1406
1407
bytecode.emitAD(opc, rl, 0);
1408
bytecode.emitAux(cid | flip);
1409
1410
return jumpLabel;
1411
}
1412
else
1413
{
1414
LuauOpcode opc = getJumpOpCompare(expr->op, not_);
1415
1416
uint8_t rr = compileExprAuto(right, rs);
1417
1418
size_t jumpLabel = bytecode.emitLabel();
1419
1420
if (expr->op == AstExprBinary::CompareGt || expr->op == AstExprBinary::CompareGe)
1421
{
1422
bytecode.emitAD(opc, rr, 0);
1423
bytecode.emitAux(rl);
1424
}
1425
else
1426
{
1427
bytecode.emitAD(opc, rl, 0);
1428
bytecode.emitAux(rr);
1429
}
1430
1431
return jumpLabel;
1432
}
1433
}
1434
1435
int32_t getConstantNumber(AstExpr* node)
1436
{
1437
const Constant* c = constants.find(node);
1438
1439
if (c && c->type == Constant::Type_Number)
1440
{
1441
int cid = bytecode.addConstantNumber(c->valueNumber);
1442
if (cid < 0)
1443
CompileError::raise(node->location, "Exceeded constant limit; simplify the code to compile");
1444
1445
return cid;
1446
}
1447
1448
return -1;
1449
}
1450
1451
int32_t getConstantIndex(AstExpr* node)
1452
{
1453
const Constant* c = constants.find(node);
1454
1455
if (!c || c->type == Constant::Type_Unknown)
1456
return -1;
1457
1458
int cid = -1;
1459
1460
switch (c->type)
1461
{
1462
case Constant::Type_Nil:
1463
cid = bytecode.addConstantNil();
1464
break;
1465
1466
case Constant::Type_Boolean:
1467
cid = bytecode.addConstantBoolean(c->valueBoolean);
1468
break;
1469
1470
case Constant::Type_Number:
1471
cid = bytecode.addConstantNumber(c->valueNumber);
1472
break;
1473
1474
case Constant::Type_Integer:
1475
cid = bytecode.addConstantInteger(c->valueInteger64);
1476
break;
1477
1478
case Constant::Type_Vector:
1479
cid = bytecode.addConstantVector(c->valueVector[0], c->valueVector[1], c->valueVector[2], c->valueVector[3]);
1480
break;
1481
1482
case Constant::Type_String:
1483
cid = bytecode.addConstantString(sref(c->getString()));
1484
break;
1485
1486
default:
1487
LUAU_ASSERT(!"Unexpected constant type");
1488
return -1;
1489
}
1490
1491
if (cid < 0)
1492
CompileError::raise(node->location, "Exceeded constant limit; simplify the code to compile");
1493
1494
return cid;
1495
}
1496
1497
// compile expr to target temp register
1498
// if the expr (or not expr if onlyTruth is false) is truthy, jump via skipJump
1499
// if the expr (or not expr if onlyTruth is false) is falsy, fall through (target isn't guaranteed to be updated in this case)
1500
// if target is omitted, then the jump behavior is the same - skipJump or fallthrough depending on the truthiness of the expression
1501
void compileConditionValue(AstExpr* node, const uint8_t* target, std::vector<size_t>& skipJump, bool onlyTruth)
1502
{
1503
// Optimization: we don't need to compute constant values
1504
if (const Constant* cv = constants.find(node); cv && cv->type != Constant::Type_Unknown)
1505
{
1506
// note that we only need to compute the value if it's truthy; otherwise we cal fall through
1507
if (cv->isTruthful() == onlyTruth)
1508
{
1509
if (target)
1510
compileExprTemp(node, *target);
1511
1512
skipJump.push_back(bytecode.emitLabel());
1513
bytecode.emitAD(LOP_JUMP, 0, 0);
1514
}
1515
return;
1516
}
1517
1518
if (AstExprBinary* expr = node->as<AstExprBinary>())
1519
{
1520
switch (expr->op)
1521
{
1522
case AstExprBinary::And:
1523
case AstExprBinary::Or:
1524
{
1525
// disambiguation: there's 4 cases (we only need truthy or falsy results based on onlyTruth)
1526
// onlyTruth = 1: a and b transforms to a ? b : dontcare
1527
// onlyTruth = 1: a or b transforms to a ? a : b
1528
// onlyTruth = 0: a and b transforms to !a ? a : b
1529
// onlyTruth = 0: a or b transforms to !a ? b : dontcare
1530
if (onlyTruth == (expr->op == AstExprBinary::And))
1531
{
1532
// we need to compile the left hand side, and skip to "dontcare" (aka fallthrough of the entire statement) if it's not the same as
1533
// onlyTruth if it's the same then the result of the expression is the right hand side because of this, we *never* care about the
1534
// result of the left hand side
1535
std::vector<size_t> elseJump;
1536
compileConditionValue(expr->left, nullptr, elseJump, !onlyTruth);
1537
1538
// fallthrough indicates that we need to compute & return the right hand side
1539
// we use compileConditionValue again to process any extra and/or statements directly
1540
compileConditionValue(expr->right, target, skipJump, onlyTruth);
1541
1542
size_t elseLabel = bytecode.emitLabel();
1543
1544
patchJumps(expr, elseJump, elseLabel);
1545
}
1546
else
1547
{
1548
// we need to compute the left hand side first; note that we will jump to skipJump if we know the answer
1549
compileConditionValue(expr->left, target, skipJump, onlyTruth);
1550
1551
// we will fall through if computing the left hand didn't give us an "interesting" result
1552
// we still use compileConditionValue to recursively optimize any and/or/compare statements
1553
compileConditionValue(expr->right, target, skipJump, onlyTruth);
1554
}
1555
return;
1556
}
1557
break;
1558
1559
case AstExprBinary::CompareNe:
1560
case AstExprBinary::CompareEq:
1561
case AstExprBinary::CompareLt:
1562
case AstExprBinary::CompareLe:
1563
case AstExprBinary::CompareGt:
1564
case AstExprBinary::CompareGe:
1565
{
1566
if (target)
1567
{
1568
// since target is a temp register, we'll initialize it to 1, and then jump if the comparison is true
1569
// if the comparison is false, we'll fallthrough and target will still be 1 but target has unspecified value for falsy results
1570
// when we only care about falsy values instead of truthy values, the process is the same but with flipped conditionals
1571
bytecode.emitABC(LOP_LOADB, *target, onlyTruth ? 1 : 0, 0);
1572
}
1573
1574
size_t jumpLabel = compileCompareJump(expr, /* not= */ !onlyTruth);
1575
1576
skipJump.push_back(jumpLabel);
1577
return;
1578
}
1579
break;
1580
1581
// fall-through to default path below
1582
default:;
1583
}
1584
}
1585
1586
if (AstExprUnary* expr = node->as<AstExprUnary>())
1587
{
1588
// if we *do* need to compute the target, we'd have to inject "not" ops on every return path
1589
// this is possible but cumbersome; so for now we only optimize not expression when we *don't* need the value
1590
if (!target && expr->op == AstExprUnary::Not)
1591
{
1592
compileConditionValue(expr->expr, target, skipJump, !onlyTruth);
1593
return;
1594
}
1595
}
1596
1597
if (AstExprGroup* expr = node->as<AstExprGroup>())
1598
{
1599
compileConditionValue(expr->expr, target, skipJump, onlyTruth);
1600
return;
1601
}
1602
1603
RegScope rs(this);
1604
uint8_t reg;
1605
1606
if (target)
1607
{
1608
reg = *target;
1609
compileExprTemp(node, reg);
1610
}
1611
else
1612
{
1613
reg = compileExprAuto(node, rs);
1614
}
1615
1616
skipJump.push_back(bytecode.emitLabel());
1617
bytecode.emitAD(onlyTruth ? LOP_JUMPIF : LOP_JUMPIFNOT, reg, 0);
1618
}
1619
1620
// checks if compiling the expression as a condition value generates code that's faster than using compileExpr
1621
bool isConditionFast(AstExpr* node)
1622
{
1623
const Constant* cv = constants.find(node);
1624
1625
if (cv && cv->type != Constant::Type_Unknown)
1626
return true;
1627
1628
if (AstExprBinary* expr = node->as<AstExprBinary>())
1629
{
1630
switch (expr->op)
1631
{
1632
case AstExprBinary::And:
1633
case AstExprBinary::Or:
1634
return true;
1635
1636
case AstExprBinary::CompareNe:
1637
case AstExprBinary::CompareEq:
1638
case AstExprBinary::CompareLt:
1639
case AstExprBinary::CompareLe:
1640
case AstExprBinary::CompareGt:
1641
case AstExprBinary::CompareGe:
1642
return true;
1643
1644
default:
1645
return false;
1646
}
1647
}
1648
1649
if (AstExprGroup* expr = node->as<AstExprGroup>())
1650
return isConditionFast(expr->expr);
1651
1652
return false;
1653
}
1654
1655
void compileExprAndOr(AstExprBinary* expr, uint8_t target, bool targetTemp)
1656
{
1657
bool and_ = (expr->op == AstExprBinary::And);
1658
1659
RegScope rs(this);
1660
1661
// Optimization: when left hand side is a constant, we can emit left hand side or right hand side
1662
if (const Constant* cl = constants.find(expr->left); cl && cl->type != Constant::Type_Unknown)
1663
{
1664
compileExpr(and_ == cl->isTruthful() ? expr->right : expr->left, target, targetTemp);
1665
return;
1666
}
1667
1668
// Note: two optimizations below can lead to inefficient codegen when the left hand side is a condition
1669
if (!isConditionFast(expr->left))
1670
{
1671
// Optimization: when right hand side is a local variable, we can use AND/OR
1672
if (int reg = getExprLocalReg(expr->right); reg >= 0)
1673
{
1674
uint8_t lr = compileExprAuto(expr->left, rs);
1675
uint8_t rr = uint8_t(reg);
1676
1677
bytecode.emitABC(and_ ? LOP_AND : LOP_OR, target, lr, rr);
1678
return;
1679
}
1680
1681
// Optimization: when right hand side is a constant, we can use ANDK/ORK
1682
int32_t cid = getConstantIndex(expr->right);
1683
1684
if (cid >= 0 && cid <= 255)
1685
{
1686
uint8_t lr = compileExprAuto(expr->left, rs);
1687
1688
bytecode.emitABC(and_ ? LOP_ANDK : LOP_ORK, target, lr, uint8_t(cid));
1689
return;
1690
}
1691
}
1692
1693
// Optimization: if target is a temp register, we can clobber it which allows us to compute the result directly into it
1694
// If it's not a temp register, then something like `a = a > 1 or a + 2` may clobber `a` while evaluating left hand side, and `a+2` will break
1695
uint8_t reg = targetTemp ? target : allocReg(expr, 1u);
1696
1697
std::vector<size_t> skipJump;
1698
compileConditionValue(expr->left, &reg, skipJump, /* onlyTruth= */ !and_);
1699
1700
compileExprTemp(expr->right, reg);
1701
1702
size_t moveLabel = bytecode.emitLabel();
1703
1704
patchJumps(expr, skipJump, moveLabel);
1705
1706
if (target != reg)
1707
bytecode.emitABC(LOP_MOVE, target, reg, 0);
1708
}
1709
1710
void compileExprUnary(AstExprUnary* expr, uint8_t target)
1711
{
1712
RegScope rs(this);
1713
1714
// Special case for integer constants, like -1000000000i
1715
AstExprConstantInteger* cint = expr->expr->as<AstExprConstantInteger>();
1716
if (FFlag::LuauIntegerType && (expr->op == AstExprUnary::Minus) && (cint != nullptr))
1717
{
1718
int32_t cid = bytecode.addConstantInteger((int64_t)(~(uint64_t)cint->value + 1));
1719
if (cid < 0)
1720
CompileError::raise(expr->location, "Exceeded constant limit; simplify the code to compile");
1721
1722
emitLoadK(target, cid);
1723
return;
1724
}
1725
1726
uint8_t re = compileExprAuto(expr->expr, rs);
1727
1728
bytecode.emitABC(getUnaryOp(expr->op), target, re, 0);
1729
}
1730
1731
static void unrollConcats(std::vector<AstExpr*>& args)
1732
{
1733
for (;;)
1734
{
1735
AstExprBinary* be = args.back()->as<AstExprBinary>();
1736
1737
if (!be || be->op != AstExprBinary::Concat)
1738
break;
1739
1740
args.back() = be->left;
1741
args.push_back(be->right);
1742
}
1743
}
1744
1745
void compileExprBinary(AstExprBinary* expr, uint8_t target, bool targetTemp)
1746
{
1747
RegScope rs(this);
1748
1749
switch (expr->op)
1750
{
1751
case AstExprBinary::Add:
1752
case AstExprBinary::Sub:
1753
case AstExprBinary::Mul:
1754
case AstExprBinary::Div:
1755
case AstExprBinary::FloorDiv:
1756
case AstExprBinary::Mod:
1757
case AstExprBinary::Pow:
1758
{
1759
int32_t rc = getConstantNumber(expr->right);
1760
1761
if (rc >= 0 && rc <= 255)
1762
{
1763
uint8_t rl = compileExprAuto(expr->left, rs);
1764
1765
bytecode.emitABC(getBinaryOpArith(expr->op, /* k= */ true), target, rl, uint8_t(rc));
1766
1767
hintTemporaryExprRegType(expr->left, rl, LBC_TYPE_NUMBER, /* instLength */ 1);
1768
}
1769
else
1770
{
1771
if (expr->op == AstExprBinary::Sub || expr->op == AstExprBinary::Div)
1772
{
1773
int32_t lc = getConstantNumber(expr->left);
1774
1775
if (lc >= 0 && lc <= 255)
1776
{
1777
uint8_t rr = compileExprAuto(expr->right, rs);
1778
LuauOpcode op = (expr->op == AstExprBinary::Sub) ? LOP_SUBRK : LOP_DIVRK;
1779
1780
bytecode.emitABC(op, target, uint8_t(lc), uint8_t(rr));
1781
1782
hintTemporaryExprRegType(expr->right, rr, LBC_TYPE_NUMBER, /* instLength */ 1);
1783
return;
1784
}
1785
}
1786
else if (options.optimizationLevel >= 2 && (expr->op == AstExprBinary::Add || expr->op == AstExprBinary::Mul))
1787
{
1788
// Optimization: replace k*r with r*k when r is known to be a number (otherwise metamethods may be called)
1789
if (FFlag::LuauCompileVectorReveseMul)
1790
{
1791
if (LuauBytecodeType* ty = exprTypes.find(expr))
1792
{
1793
// Note: for vectors, it only makes sense to do for a multiplication as number+vector is an error
1794
if (*ty == LBC_TYPE_NUMBER ||
1795
(FFlag::LuauCompileVectorReveseMul && *ty == LBC_TYPE_VECTOR && expr->op == AstExprBinary::Mul))
1796
{
1797
int32_t lc = getConstantNumber(expr->left);
1798
1799
if (lc >= 0 && lc <= 255)
1800
{
1801
uint8_t rr = compileExprAuto(expr->right, rs);
1802
1803
bytecode.emitABC(getBinaryOpArith(expr->op, /* k= */ true), target, rr, uint8_t(lc));
1804
1805
hintTemporaryExprRegType(expr->right, rr, LBC_TYPE_NUMBER, /* instLength */ 1);
1806
return;
1807
}
1808
}
1809
}
1810
}
1811
else
1812
{
1813
if (LuauBytecodeType* ty = exprTypes.find(expr); ty && *ty == LBC_TYPE_NUMBER)
1814
{
1815
int32_t lc = getConstantNumber(expr->left);
1816
1817
if (lc >= 0 && lc <= 255)
1818
{
1819
uint8_t rr = compileExprAuto(expr->right, rs);
1820
1821
bytecode.emitABC(getBinaryOpArith(expr->op, /* k= */ true), target, rr, uint8_t(lc));
1822
1823
hintTemporaryExprRegType(expr->right, rr, LBC_TYPE_NUMBER, /* instLength */ 1);
1824
return;
1825
}
1826
}
1827
}
1828
}
1829
1830
uint8_t rl = compileExprAuto(expr->left, rs);
1831
uint8_t rr = compileExprAuto(expr->right, rs);
1832
1833
bytecode.emitABC(getBinaryOpArith(expr->op), target, rl, rr);
1834
1835
hintTemporaryExprRegType(expr->left, rl, LBC_TYPE_NUMBER, /* instLength */ 1);
1836
hintTemporaryExprRegType(expr->right, rr, LBC_TYPE_NUMBER, /* instLength */ 1);
1837
}
1838
}
1839
break;
1840
1841
case AstExprBinary::Concat:
1842
{
1843
std::vector<AstExpr*> args = {expr->left, expr->right};
1844
1845
// unroll the tree of concats down the right hand side to be able to do multiple ops
1846
unrollConcats(args);
1847
1848
uint8_t regs = allocReg(expr, unsigned(args.size()));
1849
1850
for (size_t i = 0; i < args.size(); ++i)
1851
compileExprTemp(args[i], uint8_t(regs + i));
1852
1853
bytecode.emitABC(LOP_CONCAT, target, regs, uint8_t(regs + args.size() - 1));
1854
}
1855
break;
1856
1857
case AstExprBinary::CompareNe:
1858
case AstExprBinary::CompareEq:
1859
case AstExprBinary::CompareLt:
1860
case AstExprBinary::CompareLe:
1861
case AstExprBinary::CompareGt:
1862
case AstExprBinary::CompareGe:
1863
{
1864
size_t jumpLabel = compileCompareJump(expr);
1865
1866
// note: this skips over the next LOADB instruction because of "1" in the C slot
1867
bytecode.emitABC(LOP_LOADB, target, 0, 1);
1868
1869
size_t thenLabel = bytecode.emitLabel();
1870
1871
bytecode.emitABC(LOP_LOADB, target, 1, 0);
1872
1873
patchJump(expr, jumpLabel, thenLabel);
1874
}
1875
break;
1876
1877
case AstExprBinary::And:
1878
case AstExprBinary::Or:
1879
{
1880
compileExprAndOr(expr, target, targetTemp);
1881
}
1882
break;
1883
1884
default:
1885
LUAU_ASSERT(!"Unexpected binary operation");
1886
}
1887
}
1888
1889
void compileExprIfElseAndOr(bool and_, uint8_t creg, AstExpr* other, uint8_t target)
1890
{
1891
int32_t cid = getConstantIndex(other);
1892
1893
if (cid >= 0 && cid <= 255)
1894
{
1895
bytecode.emitABC(and_ ? LOP_ANDK : LOP_ORK, target, creg, uint8_t(cid));
1896
}
1897
else
1898
{
1899
RegScope rs(this);
1900
uint8_t oreg = compileExprAuto(other, rs);
1901
1902
bytecode.emitABC(and_ ? LOP_AND : LOP_OR, target, creg, oreg);
1903
}
1904
}
1905
1906
void compileExprIfElse(AstExprIfElse* expr, uint8_t target, bool targetTemp)
1907
{
1908
if (isConstant(expr->condition))
1909
{
1910
if (isConstantTrue(expr->condition))
1911
{
1912
compileExpr(expr->trueExpr, target, targetTemp);
1913
}
1914
else
1915
{
1916
compileExpr(expr->falseExpr, target, targetTemp);
1917
}
1918
}
1919
else
1920
{
1921
// Optimization: convert some if..then..else expressions into and/or when the other side has no side effects and is very cheap to compute
1922
// if v then v else e => v or e
1923
// if v then e else v => v and e
1924
if (int creg = getExprLocalReg(expr->condition); creg >= 0)
1925
{
1926
if (creg == getExprLocalReg(expr->trueExpr) && (getExprLocalReg(expr->falseExpr) >= 0 || isConstant(expr->falseExpr)))
1927
return compileExprIfElseAndOr(/* and_= */ false, uint8_t(creg), expr->falseExpr, target);
1928
else if (creg == getExprLocalReg(expr->falseExpr) && (getExprLocalReg(expr->trueExpr) >= 0 || isConstant(expr->trueExpr)))
1929
return compileExprIfElseAndOr(/* and_= */ true, uint8_t(creg), expr->trueExpr, target);
1930
}
1931
1932
std::vector<size_t> elseJump;
1933
compileConditionValue(expr->condition, nullptr, elseJump, false);
1934
compileExpr(expr->trueExpr, target, targetTemp);
1935
1936
// Jump over else expression evaluation
1937
size_t thenLabel = bytecode.emitLabel();
1938
bytecode.emitAD(LOP_JUMP, 0, 0);
1939
1940
size_t elseLabel = bytecode.emitLabel();
1941
compileExpr(expr->falseExpr, target, targetTemp);
1942
size_t endLabel = bytecode.emitLabel();
1943
1944
patchJumps(expr, elseJump, elseLabel);
1945
patchJump(expr, thenLabel, endLabel);
1946
}
1947
}
1948
1949
void compileExprInterpString(AstExprInterpString* expr, uint8_t target, bool targetTemp)
1950
{
1951
size_t formatCapacity = 0;
1952
for (AstArray<char> string : expr->strings)
1953
{
1954
formatCapacity += string.size + std::count(string.data, string.data + string.size, '%');
1955
}
1956
1957
size_t skippedSubExpr = 0;
1958
for (size_t index = 0; index < expr->expressions.size; ++index)
1959
{
1960
const Constant* c = constants.find(expr->expressions.data[index]);
1961
if (c && c->type == Constant::Type::Type_String)
1962
{
1963
formatCapacity += c->stringLength + std::count(c->valueString, c->valueString + c->stringLength, '%');
1964
skippedSubExpr++;
1965
}
1966
else
1967
formatCapacity += 2; // "%*"
1968
}
1969
1970
std::string formatString;
1971
formatString.reserve(formatCapacity);
1972
1973
LUAU_ASSERT(expr->strings.size == expr->expressions.size + 1);
1974
for (size_t idx = 0; idx < expr->strings.size; idx++)
1975
{
1976
AstArray<char> string = expr->strings.data[idx];
1977
escapeAndAppend(formatString, string.data, string.size);
1978
1979
if (idx < expr->expressions.size)
1980
{
1981
const Constant* c = constants.find(expr->expressions.data[idx]);
1982
if (c && c->type == Constant::Type::Type_String)
1983
escapeAndAppend(formatString, c->valueString, c->stringLength);
1984
else
1985
formatString += "%*";
1986
}
1987
}
1988
1989
int32_t formatStringIndex = -1;
1990
1991
if (formatString.empty())
1992
{
1993
formatStringIndex = bytecode.addConstantString({"", 0});
1994
}
1995
else if (FFlag::LuauCompileStringInterpWithZero)
1996
{
1997
AstName interned = names.getOrAdd(formatString.c_str(), formatString.size());
1998
AstArray<const char> formatStringArray{interned.value, formatString.size()};
1999
formatStringIndex = bytecode.addConstantString(sref(formatStringArray));
2000
}
2001
else
2002
{
2003
formatStringIndex = bytecode.addConstantString(sref(names.getOrAdd(formatString.c_str(), formatString.size())));
2004
}
2005
2006
if (formatStringIndex < 0)
2007
CompileError::raise(expr->location, "Exceeded constant limit; simplify the code to compile");
2008
2009
RegScope rs(this);
2010
2011
uint8_t baseReg = allocReg(expr, unsigned(2 + expr->expressions.size - skippedSubExpr));
2012
2013
emitLoadK(baseReg, formatStringIndex);
2014
2015
size_t skipped = 0;
2016
for (size_t index = 0; index < expr->expressions.size; ++index)
2017
{
2018
AstExpr* subExpr = expr->expressions.data[index];
2019
const Constant* c = constants.find(subExpr);
2020
if (!c || c->type != Constant::Type::Type_String)
2021
compileExprTempTop(subExpr, uint8_t(baseReg + 2 + index - skipped));
2022
else
2023
skipped++;
2024
}
2025
2026
BytecodeBuilder::StringRef formatMethod = sref(AstName("format"));
2027
2028
int32_t formatMethodIndex = bytecode.addConstantString(formatMethod);
2029
if (formatMethodIndex < 0)
2030
CompileError::raise(expr->location, "Exceeded constant limit; simplify the code to compile");
2031
2032
bytecode.emitABC(LOP_NAMECALL, baseReg, baseReg, uint8_t(BytecodeBuilder::getStringHash(formatMethod)));
2033
bytecode.emitAux(formatMethodIndex);
2034
bytecode.emitABC(LOP_CALL, baseReg, uint8_t(expr->expressions.size + 2 - skippedSubExpr), 2);
2035
bytecode.emitABC(LOP_MOVE, target, baseReg, 0);
2036
}
2037
2038
static uint8_t encodeHashSize(unsigned int hashSize)
2039
{
2040
size_t hashSizeLog2 = 0;
2041
while ((1u << hashSizeLog2) < hashSize)
2042
hashSizeLog2++;
2043
2044
return hashSize == 0 ? 0 : uint8_t(hashSizeLog2 + 1);
2045
}
2046
2047
void compileExprTable(AstExprTable* expr, uint8_t target, bool targetTemp)
2048
{
2049
// Optimization: if the table is empty, we can compute it directly into the target
2050
if (expr->items.size == 0)
2051
{
2052
TableShape shape = tableShapes[expr];
2053
2054
bytecode.addDebugRemark("allocation: table hash %d", shape.hashSize);
2055
2056
bytecode.emitABC(LOP_NEWTABLE, target, encodeHashSize(shape.hashSize), 0);
2057
bytecode.emitAux(shape.arraySize);
2058
return;
2059
}
2060
2061
unsigned int arraySize = 0;
2062
unsigned int hashSize = 0;
2063
unsigned int recordSize = 0;
2064
unsigned int indexSize = 0;
2065
2066
for (size_t i = 0; i < expr->items.size; ++i)
2067
{
2068
const AstExprTable::Item& item = expr->items.data[i];
2069
2070
arraySize += (item.kind == AstExprTable::Item::List);
2071
hashSize += (item.kind != AstExprTable::Item::List);
2072
recordSize += (item.kind == AstExprTable::Item::Record);
2073
}
2074
2075
// Optimization: allocate sequential explicitly specified numeric indices ([1]) as arrays
2076
if (arraySize == 0 && hashSize > 0)
2077
{
2078
for (size_t i = 0; i < expr->items.size; ++i)
2079
{
2080
const AstExprTable::Item& item = expr->items.data[i];
2081
LUAU_ASSERT(item.key); // no list portion => all items have keys
2082
2083
const Constant* ckey = constants.find(item.key);
2084
2085
indexSize += (ckey && ckey->type == Constant::Type_Number && ckey->valueNumber == double(indexSize + 1));
2086
}
2087
2088
// we only perform the optimization if we don't have any other []-keys
2089
// technically it's "safe" to do this even if we have other keys, but doing so changes iteration order and may break existing code
2090
if (hashSize == recordSize + indexSize)
2091
hashSize = recordSize;
2092
else
2093
indexSize = 0;
2094
}
2095
2096
int encodedHashSize = encodeHashSize(hashSize);
2097
2098
RegScope rs(this);
2099
2100
// Optimization: if target is a temp register, we can clobber it which allows us to compute the result directly into it
2101
uint8_t reg = targetTemp ? target : allocReg(expr, 1u);
2102
2103
// flattening operation where we only load the last element
2104
// this optimizes for tables like: { data = 43, data = "true", data = 9 }
2105
// this does not optimize for tables such as: { data = 43, data = function() end, data = 9}
2106
// in this case, we know that data = 9 should be the element, so we can just skip the rest
2107
InsertionOrderedMap<int32_t, int32_t> lastKeyVal;
2108
// Optimization: when all items are record fields, use template tables to compile expression
2109
if (arraySize == 0 && indexSize == 0 && hashSize == recordSize && recordSize >= 1 && recordSize <= BytecodeBuilder::TableShape::kMaxLength)
2110
{
2111
BytecodeBuilder::TableShape shape;
2112
2113
if (FFlag::LuauCompileDuptableConstantPack2)
2114
{
2115
for (size_t i = 0; i < expr->items.size; ++i)
2116
{
2117
const AstExprTable::Item& item = expr->items.data[i];
2118
LUAU_ASSERT(item.kind == AstExprTable::Item::Record);
2119
2120
AstExprConstantString* ckey = item.key->as<AstExprConstantString>();
2121
LUAU_ASSERT(ckey);
2122
2123
int keyCid = bytecode.addConstantString(sref(ckey->value));
2124
if (keyCid < 0)
2125
CompileError::raise(ckey->location, "Exceeded constant limit; simplify the code to compile");
2126
2127
int32_t valueCid = getConstantIndex(item.value);
2128
if (lastKeyVal.contains(keyCid) && lastKeyVal[keyCid] == -1)
2129
continue;
2130
2131
lastKeyVal[keyCid] = valueCid;
2132
}
2133
2134
for (auto& [keyCid, valueCid] : lastKeyVal)
2135
{
2136
LUAU_ASSERT(shape.length < BytecodeBuilder::TableShape::kMaxLength);
2137
2138
size_t idx = shape.length;
2139
shape.keys[idx] = keyCid;
2140
2141
shape.constants[idx] = valueCid;
2142
if (valueCid >= 0)
2143
{
2144
shape.hasConstants = true;
2145
}
2146
2147
shape.length++;
2148
}
2149
}
2150
else
2151
{
2152
for (size_t i = 0; i < expr->items.size; ++i)
2153
{
2154
const AstExprTable::Item& item = expr->items.data[i];
2155
LUAU_ASSERT(item.kind == AstExprTable::Item::Record);
2156
2157
AstExprConstantString* ckey = item.key->as<AstExprConstantString>();
2158
LUAU_ASSERT(ckey);
2159
2160
int cid = bytecode.addConstantString(sref(ckey->value));
2161
if (cid < 0)
2162
CompileError::raise(ckey->location, "Exceeded constant limit; simplify the code to compile");
2163
2164
LUAU_ASSERT(shape.length < BytecodeBuilder::TableShape::kMaxLength);
2165
2166
shape.keys[shape.length++] = cid;
2167
}
2168
}
2169
2170
int32_t tid = bytecode.addConstantTable(shape);
2171
if (tid < 0)
2172
CompileError::raise(expr->location, "Exceeded constant limit; simplify the code to compile");
2173
2174
bytecode.addDebugRemark("allocation: table template %d", hashSize);
2175
2176
if (tid < 32768)
2177
{
2178
bytecode.emitAD(LOP_DUPTABLE, reg, int16_t(tid));
2179
}
2180
else
2181
{
2182
// must disable duptable constant optimization here, as we're defaulting back to new table
2183
if (FFlag::LuauCompileDuptableConstantPack2)
2184
{
2185
shape.hasConstants = false;
2186
lastKeyVal.clear();
2187
}
2188
2189
bytecode.emitABC(LOP_NEWTABLE, reg, uint8_t(encodedHashSize), 0);
2190
bytecode.emitAux(0);
2191
}
2192
}
2193
else
2194
{
2195
// Optimization: instead of allocating one extra element when the last element of the table literal is ..., let SETLIST allocate the
2196
// correct amount of storage
2197
const AstExprTable::Item* last = expr->items.size > 0 ? &expr->items.data[expr->items.size - 1] : nullptr;
2198
2199
bool trailingVarargs = last && last->kind == AstExprTable::Item::List && last->value->is<AstExprVarargs>();
2200
LUAU_ASSERT(!trailingVarargs || arraySize > 0);
2201
2202
unsigned int arrayAllocation = arraySize - trailingVarargs + indexSize;
2203
2204
if (hashSize == 0)
2205
bytecode.addDebugRemark("allocation: table array %d", arrayAllocation);
2206
else if (arrayAllocation == 0)
2207
bytecode.addDebugRemark("allocation: table hash %d", hashSize);
2208
else
2209
bytecode.addDebugRemark("allocation: table hash %d array %d", hashSize, arrayAllocation);
2210
2211
bytecode.emitABC(LOP_NEWTABLE, reg, uint8_t(encodedHashSize), 0);
2212
bytecode.emitAux(arrayAllocation);
2213
}
2214
2215
unsigned int arrayChunkSize = std::min(16u, arraySize);
2216
uint8_t arrayChunkReg = allocReg(expr, arrayChunkSize);
2217
unsigned int arrayChunkCurrent = 0;
2218
2219
unsigned int arrayIndex = 1;
2220
bool multRet = false;
2221
2222
for (size_t i = 0; i < expr->items.size; ++i)
2223
{
2224
const AstExprTable::Item& item = expr->items.data[i];
2225
2226
AstExpr* key = item.key;
2227
AstExpr* value = item.value;
2228
2229
if (FFlag::LuauCompileDuptableConstantPack2 && lastKeyVal.size() > 0 && key && key->is<AstExprConstantString>())
2230
{
2231
AstExprConstantString* ckey = item.key->as<AstExprConstantString>();
2232
LUAU_ASSERT(ckey);
2233
2234
int keyCid = bytecode.addConstantString(sref(ckey->value));
2235
if (const int32_t* valueCid = lastKeyVal.get(keyCid))
2236
{
2237
// do not generate assignments for constants
2238
if (*valueCid >= 0)
2239
{
2240
continue;
2241
}
2242
}
2243
}
2244
2245
2246
// some key/value pairs don't require us to compile the expressions, so we need to setup the line info here
2247
setDebugLine(value);
2248
2249
if (options.coverageLevel >= 2)
2250
{
2251
bytecode.emitABC(LOP_COVERAGE, 0, 0, 0);
2252
}
2253
2254
// flush array chunk on overflow or before hash keys to maintain insertion order
2255
if (arrayChunkCurrent > 0 && (key || arrayChunkCurrent == arrayChunkSize))
2256
{
2257
bytecode.emitABC(LOP_SETLIST, reg, arrayChunkReg, uint8_t(arrayChunkCurrent + 1));
2258
bytecode.emitAux(arrayIndex);
2259
arrayIndex += arrayChunkCurrent;
2260
arrayChunkCurrent = 0;
2261
}
2262
2263
// items with a key are set one by one via SETTABLE/SETTABLEKS/SETTABLEN
2264
if (key)
2265
{
2266
RegScope rsi(this);
2267
2268
LValue lv = compileLValueIndex(reg, key, rsi);
2269
uint8_t rv = compileExprAuto(value, rsi);
2270
2271
compileAssign(lv, rv, nullptr);
2272
}
2273
// items without a key are set using SETLIST so that we can initialize large arrays quickly
2274
else
2275
{
2276
uint8_t temp = uint8_t(arrayChunkReg + arrayChunkCurrent);
2277
2278
if (i + 1 == expr->items.size)
2279
multRet = compileExprTempMultRet(value, temp);
2280
else
2281
compileExprTempTop(value, temp);
2282
2283
arrayChunkCurrent++;
2284
}
2285
}
2286
2287
// flush last array chunk; note that this needs multret handling if the last expression was multret
2288
if (arrayChunkCurrent)
2289
{
2290
bytecode.emitABC(LOP_SETLIST, reg, arrayChunkReg, multRet ? 0 : uint8_t(arrayChunkCurrent + 1));
2291
bytecode.emitAux(arrayIndex);
2292
}
2293
2294
if (target != reg)
2295
bytecode.emitABC(LOP_MOVE, target, reg, 0);
2296
}
2297
2298
bool canImport(AstExprGlobal* expr)
2299
{
2300
return options.optimizationLevel >= 1 && getGlobalState(globals, expr->name) != Global::Written;
2301
}
2302
2303
bool canImportChain(AstExprGlobal* expr)
2304
{
2305
return options.optimizationLevel >= 1 && getGlobalState(globals, expr->name) == Global::Default;
2306
}
2307
2308
void compileExprIndexName(AstExprIndexName* expr, uint8_t target, bool targetTemp = false)
2309
{
2310
setDebugLine(expr); // normally compileExpr sets up line info, but compileExprIndexName can be called directly
2311
2312
// Optimization: index chains that start from global variables can be compiled into GETIMPORT statement
2313
AstExprGlobal* importRoot = 0;
2314
AstExprIndexName* import1 = 0;
2315
AstExprIndexName* import2 = 0;
2316
2317
if (AstExprIndexName* index = expr->expr->as<AstExprIndexName>())
2318
{
2319
importRoot = index->expr->as<AstExprGlobal>();
2320
import1 = index;
2321
import2 = expr;
2322
}
2323
else
2324
{
2325
importRoot = expr->expr->as<AstExprGlobal>();
2326
import1 = expr;
2327
}
2328
2329
if (importRoot && canImportChain(importRoot))
2330
{
2331
int32_t id0 = bytecode.addConstantString(sref(importRoot->name));
2332
int32_t id1 = bytecode.addConstantString(sref(import1->index));
2333
int32_t id2 = import2 ? bytecode.addConstantString(sref(import2->index)) : -1;
2334
2335
if (id0 < 0 || id1 < 0 || (import2 && id2 < 0))
2336
CompileError::raise(expr->location, "Exceeded constant limit; simplify the code to compile");
2337
2338
// Note: GETIMPORT encoding is limited to 10 bits per object id component
2339
if (id0 < 1024 && id1 < 1024 && id2 < 1024)
2340
{
2341
uint32_t iid = import2 ? BytecodeBuilder::getImportId(id0, id1, id2) : BytecodeBuilder::getImportId(id0, id1);
2342
int32_t cid = bytecode.addImport(iid);
2343
2344
if (cid >= 0 && cid < 32768)
2345
{
2346
bytecode.emitAD(LOP_GETIMPORT, target, int16_t(cid));
2347
bytecode.emitAux(iid);
2348
return;
2349
}
2350
}
2351
}
2352
2353
RegScope rs(this);
2354
2355
uint8_t reg = target;
2356
2357
if (int localReg = getExprLocalReg(expr->expr); localReg >= 0) // Locals can be indexed directly
2358
reg = uint8_t(localReg);
2359
else if (targetTemp) // If target is a temp register, we can clobber it which allows us to compute the result directly into it
2360
compileExprTemp(expr->expr, target);
2361
else
2362
reg = compileExprAuto(expr->expr, rs);
2363
2364
setDebugLine(expr->indexLocation);
2365
2366
BytecodeBuilder::StringRef iname = sref(expr->index);
2367
int32_t cid = bytecode.addConstantString(iname);
2368
if (cid < 0)
2369
CompileError::raise(expr->location, "Exceeded constant limit; simplify the code to compile");
2370
2371
bytecode.emitABC(LOP_GETTABLEKS, target, reg, uint8_t(BytecodeBuilder::getStringHash(iname)));
2372
bytecode.emitAux(cid);
2373
2374
hintTemporaryExprRegType(expr->expr, reg, LBC_TYPE_TABLE, /* instLength */ 2);
2375
}
2376
2377
void compileExprIndexExpr(AstExprIndexExpr* expr, uint8_t target)
2378
{
2379
RegScope rs(this);
2380
2381
Constant cv = getConstant(expr->index);
2382
2383
if (cv.type == Constant::Type_Number && cv.valueNumber >= 1 && cv.valueNumber <= 256 && double(int(cv.valueNumber)) == cv.valueNumber)
2384
{
2385
uint8_t i = uint8_t(int(cv.valueNumber) - 1);
2386
2387
uint8_t rt = compileExprAuto(expr->expr, rs);
2388
2389
setDebugLine(expr->index);
2390
2391
bytecode.emitABC(LOP_GETTABLEN, target, rt, i);
2392
2393
hintTemporaryExprRegType(expr->expr, rt, LBC_TYPE_TABLE, /* instLength */ 1);
2394
}
2395
else if (cv.type == Constant::Type_String)
2396
{
2397
BytecodeBuilder::StringRef iname = sref(cv.getString());
2398
int32_t cid = bytecode.addConstantString(iname);
2399
if (cid < 0)
2400
CompileError::raise(expr->location, "Exceeded constant limit; simplify the code to compile");
2401
2402
uint8_t rt = compileExprAuto(expr->expr, rs);
2403
2404
setDebugLine(expr->index);
2405
2406
bytecode.emitABC(LOP_GETTABLEKS, target, rt, uint8_t(BytecodeBuilder::getStringHash(iname)));
2407
bytecode.emitAux(cid);
2408
2409
hintTemporaryExprRegType(expr->expr, rt, LBC_TYPE_TABLE, /* instLength */ 2);
2410
}
2411
else
2412
{
2413
uint8_t rt = compileExprAuto(expr->expr, rs);
2414
uint8_t ri = compileExprAuto(expr->index, rs);
2415
2416
bytecode.emitABC(LOP_GETTABLE, target, rt, ri);
2417
2418
hintTemporaryExprRegType(expr->expr, rt, LBC_TYPE_TABLE, /* instLength */ 1);
2419
hintTemporaryExprRegType(expr->index, ri, LBC_TYPE_NUMBER, /* instLength */ 1);
2420
}
2421
}
2422
2423
void compileExprGlobal(AstExprGlobal* expr, uint8_t target)
2424
{
2425
// Optimization: builtin globals can be retrieved using GETIMPORT
2426
if (canImport(expr))
2427
{
2428
int32_t id0 = bytecode.addConstantString(sref(expr->name));
2429
if (id0 < 0)
2430
CompileError::raise(expr->location, "Exceeded constant limit; simplify the code to compile");
2431
2432
// Note: GETIMPORT encoding is limited to 10 bits per object id component
2433
if (id0 < 1024)
2434
{
2435
uint32_t iid = BytecodeBuilder::getImportId(id0);
2436
int32_t cid = bytecode.addImport(iid);
2437
2438
if (cid >= 0 && cid < 32768)
2439
{
2440
bytecode.emitAD(LOP_GETIMPORT, target, int16_t(cid));
2441
bytecode.emitAux(iid);
2442
return;
2443
}
2444
}
2445
}
2446
2447
BytecodeBuilder::StringRef gname = sref(expr->name);
2448
int32_t cid = bytecode.addConstantString(gname);
2449
if (cid < 0)
2450
CompileError::raise(expr->location, "Exceeded constant limit; simplify the code to compile");
2451
2452
bytecode.emitABC(LOP_GETGLOBAL, target, 0, uint8_t(BytecodeBuilder::getStringHash(gname)));
2453
bytecode.emitAux(cid);
2454
}
2455
2456
void compileExprConstant(AstExpr* node, const Constant* cv, uint8_t target)
2457
{
2458
switch (cv->type)
2459
{
2460
case Constant::Type_Nil:
2461
bytecode.emitABC(LOP_LOADNIL, target, 0, 0);
2462
break;
2463
2464
case Constant::Type_Boolean:
2465
bytecode.emitABC(LOP_LOADB, target, cv->valueBoolean, 0);
2466
break;
2467
2468
case Constant::Type_Number:
2469
{
2470
double d = cv->valueNumber;
2471
2472
if (d >= std::numeric_limits<int16_t>::min() && d <= std::numeric_limits<int16_t>::max() && double(int16_t(d)) == d &&
2473
!(d == 0.0 && signbit(d)))
2474
{
2475
// short number encoding: doesn't require a table entry lookup
2476
bytecode.emitAD(LOP_LOADN, target, int16_t(d));
2477
}
2478
else
2479
{
2480
// long number encoding: use generic constant path
2481
int32_t cid = bytecode.addConstantNumber(d);
2482
if (cid < 0)
2483
CompileError::raise(node->location, "Exceeded constant limit; simplify the code to compile");
2484
2485
emitLoadK(target, cid);
2486
}
2487
}
2488
break;
2489
2490
case Constant::Type_Integer:
2491
{
2492
int64_t l = cv->valueInteger64;
2493
2494
int32_t cid = bytecode.addConstantInteger(l);
2495
if (cid < 0)
2496
CompileError::raise(node->location, "Exceeded constant limit; simplify the code to compile");
2497
2498
emitLoadK(target, cid);
2499
}
2500
break;
2501
2502
case Constant::Type_Vector:
2503
{
2504
int32_t cid = bytecode.addConstantVector(cv->valueVector[0], cv->valueVector[1], cv->valueVector[2], cv->valueVector[3]);
2505
if (cid < 0)
2506
CompileError::raise(node->location, "Exceeded constant limit; simplify the code to compile");
2507
2508
emitLoadK(target, cid);
2509
}
2510
break;
2511
2512
case Constant::Type_String:
2513
{
2514
int32_t cid = bytecode.addConstantString(sref(cv->getString()));
2515
if (cid < 0)
2516
CompileError::raise(node->location, "Exceeded constant limit; simplify the code to compile");
2517
2518
emitLoadK(target, cid);
2519
}
2520
break;
2521
2522
default:
2523
LUAU_ASSERT(!"Unexpected constant type");
2524
}
2525
}
2526
2527
void compileExpr(AstExpr* node, uint8_t target, bool targetTemp = false)
2528
{
2529
setDebugLine(node);
2530
2531
if (options.coverageLevel >= 2 && needsCoverage(node))
2532
{
2533
bytecode.emitABC(LOP_COVERAGE, 0, 0, 0);
2534
}
2535
2536
// Optimization: if expression has a constant value, we can emit it directly
2537
if (const Constant* cv = constants.find(node); cv && cv->type != Constant::Type_Unknown)
2538
{
2539
compileExprConstant(node, cv, target);
2540
return;
2541
}
2542
2543
if (AstExprGroup* expr = node->as<AstExprGroup>())
2544
{
2545
compileExpr(expr->expr, target, targetTemp);
2546
}
2547
else if (node->is<AstExprConstantNil>())
2548
{
2549
bytecode.emitABC(LOP_LOADNIL, target, 0, 0);
2550
}
2551
else if (AstExprConstantBool* expr = node->as<AstExprConstantBool>())
2552
{
2553
bytecode.emitABC(LOP_LOADB, target, expr->value, 0);
2554
}
2555
else if (AstExprConstantNumber* expr = node->as<AstExprConstantNumber>())
2556
{
2557
int32_t cid = bytecode.addConstantNumber(expr->value);
2558
if (cid < 0)
2559
CompileError::raise(expr->location, "Exceeded constant limit; simplify the code to compile");
2560
2561
emitLoadK(target, cid);
2562
}
2563
else if (AstExprConstantString* expr = node->as<AstExprConstantString>())
2564
{
2565
int32_t cid = bytecode.addConstantString(sref(expr->value));
2566
if (cid < 0)
2567
CompileError::raise(expr->location, "Exceeded constant limit; simplify the code to compile");
2568
2569
emitLoadK(target, cid);
2570
}
2571
else if (AstExprLocal* expr = node->as<AstExprLocal>())
2572
{
2573
// note: this can't check expr->upvalue because upvalues may be upgraded to locals during inlining
2574
if (int reg = getExprLocalReg(expr); reg >= 0)
2575
{
2576
// Optimization: we don't need to move if target happens to be in the same register
2577
if (options.optimizationLevel == 0 || target != reg)
2578
bytecode.emitABC(LOP_MOVE, target, uint8_t(reg), 0);
2579
}
2580
else
2581
{
2582
LUAU_ASSERT(expr->upvalue);
2583
uint8_t uid = getUpval(expr->local);
2584
2585
bytecode.emitABC(LOP_GETUPVAL, target, uid, 0);
2586
}
2587
}
2588
else if (AstExprGlobal* expr = node->as<AstExprGlobal>())
2589
{
2590
compileExprGlobal(expr, target);
2591
}
2592
else if (AstExprVarargs* expr = node->as<AstExprVarargs>())
2593
{
2594
compileExprVarargs(expr, target, /* targetCount= */ 1);
2595
}
2596
else if (AstExprCall* expr = node->as<AstExprCall>())
2597
{
2598
// Optimization: when targeting temporary registers, we can compile call in a special mode that doesn't require extra register moves
2599
if (targetTemp && target == regTop - 1)
2600
compileExprCall(expr, target, 1, /* targetTop= */ true);
2601
else
2602
compileExprCall(expr, target, /* targetCount= */ 1);
2603
}
2604
else if (AstExprIndexName* expr = node->as<AstExprIndexName>())
2605
{
2606
compileExprIndexName(expr, target, targetTemp);
2607
}
2608
else if (AstExprIndexExpr* expr = node->as<AstExprIndexExpr>())
2609
{
2610
compileExprIndexExpr(expr, target);
2611
}
2612
else if (AstExprFunction* expr = node->as<AstExprFunction>())
2613
{
2614
compileExprFunction(expr, target);
2615
}
2616
else if (AstExprTable* expr = node->as<AstExprTable>())
2617
{
2618
compileExprTable(expr, target, targetTemp);
2619
}
2620
else if (AstExprUnary* expr = node->as<AstExprUnary>())
2621
{
2622
compileExprUnary(expr, target);
2623
}
2624
else if (AstExprBinary* expr = node->as<AstExprBinary>())
2625
{
2626
compileExprBinary(expr, target, targetTemp);
2627
}
2628
else if (AstExprTypeAssertion* expr = node->as<AstExprTypeAssertion>())
2629
{
2630
compileExpr(expr->expr, target, targetTemp);
2631
}
2632
else if (AstExprIfElse* expr = node->as<AstExprIfElse>())
2633
{
2634
compileExprIfElse(expr, target, targetTemp);
2635
}
2636
else if (AstExprInterpString* interpString = node->as<AstExprInterpString>())
2637
{
2638
compileExprInterpString(interpString, target, targetTemp);
2639
}
2640
else if (AstExprInstantiate* expr = node->as<AstExprInstantiate>())
2641
{
2642
compileExpr(expr->expr, target, targetTemp);
2643
}
2644
else
2645
{
2646
LUAU_ASSERT(!"Unknown expression type");
2647
}
2648
}
2649
2650
void compileExprTemp(AstExpr* node, uint8_t target)
2651
{
2652
return compileExpr(node, target, /* targetTemp= */ true);
2653
}
2654
2655
uint8_t compileExprAuto(AstExpr* node, RegScope&)
2656
{
2657
// Optimization: directly return locals instead of copying them to a temporary
2658
if (int reg = getExprLocalReg(node); reg >= 0)
2659
return uint8_t(reg);
2660
2661
// note: the register is owned by the parent scope
2662
uint8_t reg = allocReg(node, 1u);
2663
2664
compileExprTemp(node, reg);
2665
2666
return reg;
2667
}
2668
2669
void compileExprSide(AstExpr* node)
2670
{
2671
// Optimization: some expressions never carry side effects so we don't need to emit any code
2672
if (node->is<AstExprLocal>() || node->is<AstExprGlobal>() || node->is<AstExprVarargs>() || node->is<AstExprFunction>() || isConstant(node))
2673
return;
2674
2675
// note: the remark is omitted for calls as it's fairly noisy due to inlining
2676
if (!node->is<AstExprCall>())
2677
bytecode.addDebugRemark("expression only compiled for side effects");
2678
2679
RegScope rsi(this);
2680
compileExprAuto(node, rsi);
2681
}
2682
2683
// initializes target..target+targetCount-1 range using expression
2684
// if expression is a call/vararg, we assume it returns all values, otherwise we fill the rest with nil
2685
// assumes target register range can be clobbered and is at the top of the register space if targetTop = true
2686
void compileExprTempN(AstExpr* node, uint8_t target, uint8_t targetCount, bool targetTop)
2687
{
2688
// we assume that target range is at the top of the register space and can be clobbered
2689
// this is what allows us to compile the last call expression - if it's a call - using targetTop=true
2690
LUAU_ASSERT(!targetTop || unsigned(target + targetCount) == regTop);
2691
2692
// LOP_CALL/LOP_GETVARARGS encoding uses 255 to signal a multret
2693
if (targetCount == 255)
2694
CompileError::raise(node->location, "Exceeded result count limit; simplify the code to compile");
2695
2696
if (AstExprCall* expr = node->as<AstExprCall>())
2697
{
2698
compileExprCall(expr, target, targetCount, targetTop);
2699
}
2700
else if (AstExprVarargs* expr = node->as<AstExprVarargs>())
2701
{
2702
compileExprVarargs(expr, target, targetCount);
2703
}
2704
else
2705
{
2706
compileExprTemp(node, target);
2707
2708
for (size_t i = 1; i < targetCount; ++i)
2709
bytecode.emitABC(LOP_LOADNIL, uint8_t(target + i), 0, 0);
2710
}
2711
}
2712
2713
// initializes target..target+targetCount-1 range using expressions from the list
2714
// if list has fewer expressions, and last expression is multret, we assume it returns the rest of the values
2715
// if list has fewer expressions, and last expression isn't multret, we fill the rest with nil
2716
// assumes target register range can be clobbered and is at the top of the register space if targetTop = true
2717
void compileExprListTemp(const AstArray<AstExpr*>& list, uint8_t target, uint8_t targetCount, bool targetTop)
2718
{
2719
// we assume that target range is at the top of the register space and can be clobbered
2720
// this is what allows us to compile the last call expression - if it's a call - using targetTop=true
2721
LUAU_ASSERT(!targetTop || unsigned(target + targetCount) == regTop);
2722
2723
if (list.size == targetCount)
2724
{
2725
for (size_t i = 0; i < list.size; ++i)
2726
compileExprTemp(list.data[i], uint8_t(target + i));
2727
}
2728
else if (list.size > targetCount)
2729
{
2730
for (size_t i = 0; i < targetCount; ++i)
2731
compileExprTemp(list.data[i], uint8_t(target + i));
2732
2733
// evaluate extra expressions for side effects
2734
for (size_t i = targetCount; i < list.size; ++i)
2735
compileExprSide(list.data[i]);
2736
}
2737
else if (list.size > 0)
2738
{
2739
for (size_t i = 0; i < list.size - 1; ++i)
2740
compileExprTemp(list.data[i], uint8_t(target + i));
2741
2742
compileExprTempN(list.data[list.size - 1], uint8_t(target + list.size - 1), uint8_t(targetCount - (list.size - 1)), targetTop);
2743
}
2744
else
2745
{
2746
for (size_t i = 0; i < targetCount; ++i)
2747
bytecode.emitABC(LOP_LOADNIL, uint8_t(target + i), 0, 0);
2748
}
2749
}
2750
2751
struct LValue
2752
{
2753
enum Kind
2754
{
2755
Kind_Local,
2756
Kind_Upvalue,
2757
Kind_Global,
2758
Kind_IndexName,
2759
Kind_IndexNumber,
2760
Kind_IndexExpr,
2761
};
2762
2763
Kind kind;
2764
uint8_t reg; // register for local (Local) or table (Index*)
2765
uint8_t upval;
2766
uint8_t index; // register for index in IndexExpr
2767
uint8_t number; // index-1 (0-255) in IndexNumber
2768
BytecodeBuilder::StringRef name;
2769
Location location;
2770
};
2771
2772
LValue compileLValueIndex(uint8_t reg, AstExpr* index, RegScope& rs)
2773
{
2774
Constant cv = getConstant(index);
2775
2776
if (cv.type == Constant::Type_Number && cv.valueNumber >= 1 && cv.valueNumber <= 256 && double(int(cv.valueNumber)) == cv.valueNumber)
2777
{
2778
LValue result = {LValue::Kind_IndexNumber};
2779
result.reg = reg;
2780
result.number = uint8_t(int(cv.valueNumber) - 1);
2781
result.location = index->location;
2782
2783
return result;
2784
}
2785
else if (cv.type == Constant::Type_String)
2786
{
2787
LValue result = {LValue::Kind_IndexName};
2788
result.reg = reg;
2789
result.name = sref(cv.getString());
2790
result.location = index->location;
2791
2792
return result;
2793
}
2794
else
2795
{
2796
LValue result = {LValue::Kind_IndexExpr};
2797
result.reg = reg;
2798
result.index = compileExprAuto(index, rs);
2799
result.location = index->location;
2800
2801
return result;
2802
}
2803
}
2804
2805
LValue compileLValue(AstExpr* node, RegScope& rs)
2806
{
2807
setDebugLine(node);
2808
2809
if (AstExprLocal* expr = node->as<AstExprLocal>())
2810
{
2811
// note: this can't check expr->upvalue because upvalues may be upgraded to locals during inlining
2812
if (int reg = getExprLocalReg(expr); reg >= 0)
2813
{
2814
LValue result = {LValue::Kind_Local};
2815
result.reg = uint8_t(reg);
2816
result.location = node->location;
2817
2818
return result;
2819
}
2820
else
2821
{
2822
LUAU_ASSERT(expr->upvalue);
2823
2824
LValue result = {LValue::Kind_Upvalue};
2825
result.upval = getUpval(expr->local);
2826
result.location = node->location;
2827
2828
return result;
2829
}
2830
}
2831
else if (AstExprGlobal* expr = node->as<AstExprGlobal>())
2832
{
2833
LValue result = {LValue::Kind_Global};
2834
result.name = sref(expr->name);
2835
result.location = node->location;
2836
2837
return result;
2838
}
2839
else if (AstExprIndexName* expr = node->as<AstExprIndexName>())
2840
{
2841
LValue result = {LValue::Kind_IndexName};
2842
result.reg = compileExprAuto(expr->expr, rs);
2843
result.name = sref(expr->index);
2844
result.location = node->location;
2845
2846
return result;
2847
}
2848
else if (AstExprIndexExpr* expr = node->as<AstExprIndexExpr>())
2849
{
2850
uint8_t reg = compileExprAuto(expr->expr, rs);
2851
2852
return compileLValueIndex(reg, expr->index, rs);
2853
}
2854
else
2855
{
2856
LUAU_ASSERT(!"Unknown assignment expression");
2857
2858
return LValue();
2859
}
2860
}
2861
2862
void compileLValueUse(const LValue& lv, uint8_t reg, bool set, AstExpr* targetExpr)
2863
{
2864
setDebugLine(lv.location);
2865
2866
switch (lv.kind)
2867
{
2868
case LValue::Kind_Local:
2869
if (set)
2870
bytecode.emitABC(LOP_MOVE, lv.reg, reg, 0);
2871
else
2872
bytecode.emitABC(LOP_MOVE, reg, lv.reg, 0);
2873
break;
2874
2875
case LValue::Kind_Upvalue:
2876
bytecode.emitABC(set ? LOP_SETUPVAL : LOP_GETUPVAL, reg, lv.upval, 0);
2877
break;
2878
2879
case LValue::Kind_Global:
2880
{
2881
int32_t cid = bytecode.addConstantString(lv.name);
2882
if (cid < 0)
2883
CompileError::raise(lv.location, "Exceeded constant limit; simplify the code to compile");
2884
2885
bytecode.emitABC(set ? LOP_SETGLOBAL : LOP_GETGLOBAL, reg, 0, uint8_t(BytecodeBuilder::getStringHash(lv.name)));
2886
bytecode.emitAux(cid);
2887
}
2888
break;
2889
2890
case LValue::Kind_IndexName:
2891
{
2892
int32_t cid = bytecode.addConstantString(lv.name);
2893
if (cid < 0)
2894
CompileError::raise(lv.location, "Exceeded constant limit; simplify the code to compile");
2895
2896
bytecode.emitABC(set ? LOP_SETTABLEKS : LOP_GETTABLEKS, reg, lv.reg, uint8_t(BytecodeBuilder::getStringHash(lv.name)));
2897
bytecode.emitAux(cid);
2898
2899
if (targetExpr)
2900
{
2901
if (AstExprIndexName* targetExprIndexName = targetExpr->as<AstExprIndexName>())
2902
hintTemporaryExprRegType(targetExprIndexName->expr, lv.reg, LBC_TYPE_TABLE, /* instLength */ 2);
2903
}
2904
}
2905
break;
2906
2907
case LValue::Kind_IndexNumber:
2908
bytecode.emitABC(set ? LOP_SETTABLEN : LOP_GETTABLEN, reg, lv.reg, lv.number);
2909
2910
if (targetExpr)
2911
{
2912
if (AstExprIndexExpr* targetExprIndexExpr = targetExpr->as<AstExprIndexExpr>())
2913
hintTemporaryExprRegType(targetExprIndexExpr->expr, lv.reg, LBC_TYPE_TABLE, /* instLength */ 1);
2914
}
2915
break;
2916
2917
case LValue::Kind_IndexExpr:
2918
bytecode.emitABC(set ? LOP_SETTABLE : LOP_GETTABLE, reg, lv.reg, lv.index);
2919
2920
if (targetExpr)
2921
{
2922
if (AstExprIndexExpr* targetExprIndexExpr = targetExpr->as<AstExprIndexExpr>())
2923
{
2924
hintTemporaryExprRegType(targetExprIndexExpr->expr, lv.reg, LBC_TYPE_TABLE, /* instLength */ 1);
2925
hintTemporaryExprRegType(targetExprIndexExpr->index, lv.index, LBC_TYPE_NUMBER, /* instLength */ 1);
2926
}
2927
}
2928
break;
2929
2930
default:
2931
LUAU_ASSERT(!"Unknown lvalue kind");
2932
}
2933
}
2934
2935
void compileAssign(const LValue& lv, uint8_t source, AstExpr* targetExpr)
2936
{
2937
compileLValueUse(lv, source, /* set= */ true, targetExpr);
2938
}
2939
2940
AstExprLocal* getExprLocal(AstExpr* node)
2941
{
2942
if (AstExprLocal* expr = node->as<AstExprLocal>())
2943
return expr;
2944
else if (AstExprGroup* expr = node->as<AstExprGroup>())
2945
return getExprLocal(expr->expr);
2946
else if (AstExprTypeAssertion* expr = node->as<AstExprTypeAssertion>())
2947
return getExprLocal(expr->expr);
2948
else
2949
return nullptr;
2950
}
2951
2952
int getExprLocalReg(AstExpr* node)
2953
{
2954
if (AstExprLocal* expr = getExprLocal(node))
2955
{
2956
// note: this can't check expr->upvalue because upvalues may be upgraded to locals during inlining
2957
Local* l = locals.find(expr->local);
2958
2959
return l && l->allocated ? l->reg : -1;
2960
}
2961
else
2962
return -1;
2963
}
2964
2965
bool isStatBreak(AstStat* node)
2966
{
2967
if (AstStatBlock* stat = node->as<AstStatBlock>())
2968
return stat->body.size == 1 && stat->body.data[0]->is<AstStatBreak>();
2969
2970
return node->is<AstStatBreak>();
2971
}
2972
2973
AstStatContinue* extractStatContinue(AstStatBlock* block)
2974
{
2975
if (block->body.size == 1)
2976
return block->body.data[0]->as<AstStatContinue>();
2977
else
2978
return nullptr;
2979
}
2980
2981
void compileStatIf(AstStatIf* stat)
2982
{
2983
// Optimization: condition is always false => we only need the else body
2984
if (isConstantFalse(stat->condition))
2985
{
2986
if (stat->elsebody)
2987
compileStat(stat->elsebody);
2988
return;
2989
}
2990
2991
// Optimization: condition is always false but isn't a constant => we only need the else body and condition's side effects
2992
if (AstExprBinary* cand = stat->condition->as<AstExprBinary>(); cand && cand->op == AstExprBinary::And && isConstantFalse(cand->right))
2993
{
2994
compileExprSide(cand->left);
2995
if (stat->elsebody)
2996
compileStat(stat->elsebody);
2997
return;
2998
}
2999
3000
// Optimization: body is a "break" statement with no "else" => we can directly break out of the loop in "then" case
3001
if (!stat->elsebody && isStatBreak(stat->thenbody) && !areLocalsCaptured(loops.back().localOffset))
3002
{
3003
// fallthrough = continue with the loop as usual
3004
std::vector<size_t> elseJump;
3005
compileConditionValue(stat->condition, nullptr, elseJump, true);
3006
3007
for (size_t jump : elseJump)
3008
loopJumps.push_back({LoopJump::Break, jump});
3009
return;
3010
}
3011
3012
AstStatContinue* continueStatement = extractStatContinue(stat->thenbody);
3013
3014
// Optimization: body is a "continue" statement with no "else" => we can directly continue in "then" case
3015
if (!stat->elsebody && continueStatement != nullptr && !areLocalsCaptured(loops.back().localOffsetContinue))
3016
{
3017
// track continue statement for repeat..until validation (validateContinueUntil)
3018
if (!loops.back().continueUsed)
3019
loops.back().continueUsed = continueStatement;
3020
3021
// fallthrough = proceed with the loop body as usual
3022
std::vector<size_t> elseJump;
3023
compileConditionValue(stat->condition, nullptr, elseJump, true);
3024
3025
for (size_t jump : elseJump)
3026
loopJumps.push_back({LoopJump::Continue, jump});
3027
return;
3028
}
3029
3030
std::vector<size_t> elseJump;
3031
compileConditionValue(stat->condition, nullptr, elseJump, false);
3032
3033
compileStat(stat->thenbody);
3034
3035
if (stat->elsebody && elseJump.size() > 0)
3036
{
3037
// we don't need to skip past "else" body if "then" ends with return/break/continue
3038
// this is important because, if "else" also ends with return, we may *not* have any statement to skip to!
3039
if (alwaysTerminates(stat->thenbody))
3040
{
3041
size_t elseLabel = bytecode.emitLabel();
3042
3043
compileStat(stat->elsebody);
3044
3045
patchJumps(stat, elseJump, elseLabel);
3046
}
3047
else
3048
{
3049
size_t thenLabel = bytecode.emitLabel();
3050
3051
bytecode.emitAD(LOP_JUMP, 0, 0);
3052
3053
size_t elseLabel = bytecode.emitLabel();
3054
3055
compileStat(stat->elsebody);
3056
3057
size_t endLabel = bytecode.emitLabel();
3058
3059
patchJumps(stat, elseJump, elseLabel);
3060
patchJump(stat, thenLabel, endLabel);
3061
}
3062
}
3063
else
3064
{
3065
size_t endLabel = bytecode.emitLabel();
3066
3067
patchJumps(stat, elseJump, endLabel);
3068
}
3069
}
3070
3071
void compileStatWhile(AstStatWhile* stat)
3072
{
3073
// Optimization: condition is always false => there's no loop!
3074
if (isConstantFalse(stat->condition))
3075
return;
3076
3077
size_t oldJumps = loopJumps.size();
3078
size_t oldLocals = localStack.size();
3079
3080
loops.push_back({oldLocals, oldLocals, nullptr});
3081
hasLoops = true;
3082
3083
size_t loopLabel = bytecode.emitLabel();
3084
3085
std::vector<size_t> elseJump;
3086
compileConditionValue(stat->condition, nullptr, elseJump, false);
3087
3088
compileStat(stat->body);
3089
3090
size_t contLabel = bytecode.emitLabel();
3091
3092
size_t backLabel = bytecode.emitLabel();
3093
3094
setDebugLine(stat->condition);
3095
3096
// Note: this is using JUMPBACK, not JUMP, since JUMPBACK is interruptable and we want all loops to have at least one interruptable
3097
// instruction
3098
bytecode.emitAD(LOP_JUMPBACK, 0, 0);
3099
3100
size_t endLabel = bytecode.emitLabel();
3101
3102
patchJump(stat, backLabel, loopLabel);
3103
patchJumps(stat, elseJump, endLabel);
3104
3105
patchLoopJumps(stat, oldJumps, endLabel, contLabel);
3106
loopJumps.resize(oldJumps);
3107
3108
loops.pop_back();
3109
}
3110
3111
void compileStatRepeat(AstStatRepeat* stat)
3112
{
3113
size_t oldJumps = loopJumps.size();
3114
size_t oldLocals = localStack.size();
3115
3116
loops.push_back({oldLocals, oldLocals, nullptr});
3117
hasLoops = true;
3118
3119
size_t loopLabel = bytecode.emitLabel();
3120
3121
// note: we "inline" compileStatBlock here so that we can close/pop locals after evaluating condition
3122
// this is necessary because condition can access locals declared inside the repeat..until body
3123
AstStatBlock* body = stat->body;
3124
3125
RegScope rs(this);
3126
3127
bool continueValidated = false;
3128
size_t conditionLocals = 0;
3129
3130
for (size_t i = 0; i < body->body.size; ++i)
3131
{
3132
compileStat(body->body.data[i]);
3133
3134
// continue statement inside the repeat..until loop should not close upvalues defined directly in the loop body
3135
// (but it must still close upvalues defined in more nested blocks)
3136
// this is because the upvalues defined inside the loop body may be captured by a closure defined in the until
3137
// expression that continue will jump to.
3138
loops.back().localOffsetContinue = localStack.size();
3139
3140
// if continue was called from this statement, any local defined after this in the loop body should not be accessed by until condition
3141
// it is sufficient to check this condition once, as if this holds for the first continue, it must hold for all subsequent continues.
3142
if (loops.back().continueUsed && !continueValidated)
3143
{
3144
validateContinueUntil(loops.back().continueUsed, stat->condition, body, i + 1);
3145
continueValidated = true;
3146
conditionLocals = localStack.size();
3147
}
3148
}
3149
3150
// if continue was used, some locals might not have had their initialization completed
3151
// the lifetime of these locals has to end before the condition is executed
3152
// because referencing skipped locals is not possible from the condition, this earlier closure doesn't affect upvalues
3153
if (continueValidated)
3154
{
3155
// if continueValidated is set, it means we have visited at least one body node and size > 0
3156
setDebugLineEnd(body->body.data[body->body.size - 1]);
3157
3158
closeLocals(conditionLocals);
3159
3160
popLocals(conditionLocals);
3161
}
3162
3163
size_t contLabel = bytecode.emitLabel();
3164
3165
size_t endLabel;
3166
3167
setDebugLine(stat->condition);
3168
3169
if (isConstantTrue(stat->condition))
3170
{
3171
closeLocals(oldLocals);
3172
3173
endLabel = bytecode.emitLabel();
3174
}
3175
else
3176
{
3177
std::vector<size_t> skipJump;
3178
compileConditionValue(stat->condition, nullptr, skipJump, true);
3179
3180
// we close locals *after* we compute loop conditionals because during computation of condition it's (in theory) possible that user code
3181
// mutates them
3182
closeLocals(oldLocals);
3183
3184
size_t backLabel = bytecode.emitLabel();
3185
3186
// Note: this is using JUMPBACK, not JUMP, since JUMPBACK is interruptable and we want all loops to have at least one interruptable
3187
// instruction
3188
bytecode.emitAD(LOP_JUMPBACK, 0, 0);
3189
3190
size_t skipLabel = bytecode.emitLabel();
3191
3192
// we need to close locals *again* after the loop ends because the first closeLocals would be jumped over on the last iteration
3193
closeLocals(oldLocals);
3194
3195
endLabel = bytecode.emitLabel();
3196
3197
patchJump(stat, backLabel, loopLabel);
3198
patchJumps(stat, skipJump, skipLabel);
3199
}
3200
3201
popLocals(oldLocals);
3202
3203
patchLoopJumps(stat, oldJumps, endLabel, contLabel);
3204
loopJumps.resize(oldJumps);
3205
3206
loops.pop_back();
3207
}
3208
3209
void compileInlineReturn(AstStatReturn* stat, bool fallthrough)
3210
{
3211
setDebugLine(stat); // normally compileStat sets up line info, but compileInlineReturn can be called directly
3212
3213
InlineFrame frame = inlineFrames.back();
3214
3215
compileExprListTemp(stat->list, frame.target, frame.targetCount, /* targetTop= */ false);
3216
3217
closeLocals(frame.localOffset);
3218
3219
size_t jumpLabel = bytecode.emitLabel();
3220
bytecode.emitAD(LOP_JUMP, 0, 0);
3221
3222
inlineFrames.back().returnJumps.push_back(jumpLabel);
3223
}
3224
3225
void compileStatReturn(AstStatReturn* stat)
3226
{
3227
// LOP_RETURN encoding uses 255 to signal a multret
3228
if (stat->list.size >= 255)
3229
CompileError::raise(stat->location, "Exceeded return count limit; simplify the code to compile");
3230
3231
RegScope rs(this);
3232
3233
uint8_t temp = 0;
3234
bool consecutive = false;
3235
bool multRet = false;
3236
3237
// Optimization: return locals directly instead of copying them into a temporary
3238
// this is very important for a single return value and occasionally effective for multiple values
3239
if (int reg = stat->list.size > 0 ? getExprLocalReg(stat->list.data[0]) : -1; reg >= 0)
3240
{
3241
temp = uint8_t(reg);
3242
consecutive = true;
3243
3244
for (size_t i = 1; i < stat->list.size; ++i)
3245
if (getExprLocalReg(stat->list.data[i]) != int(temp + i))
3246
{
3247
consecutive = false;
3248
break;
3249
}
3250
}
3251
3252
if (!consecutive && stat->list.size > 0)
3253
{
3254
temp = allocReg(stat, unsigned(stat->list.size));
3255
3256
// Note: if the last element is a function call or a vararg specifier, then we need to somehow return all values that that call returned
3257
for (size_t i = 0; i < stat->list.size; ++i)
3258
if (i + 1 == stat->list.size)
3259
multRet = compileExprTempMultRet(stat->list.data[i], uint8_t(temp + i));
3260
else
3261
compileExprTempTop(stat->list.data[i], uint8_t(temp + i));
3262
}
3263
3264
closeLocals(0);
3265
3266
bytecode.emitABC(LOP_RETURN, uint8_t(temp), multRet ? 0 : uint8_t(stat->list.size + 1), 0);
3267
}
3268
3269
bool areLocalsRedundant(AstStatLocal* stat)
3270
{
3271
// Extra expressions may have side effects
3272
if (stat->values.size > stat->vars.size)
3273
return false;
3274
3275
for (AstLocal* local : stat->vars)
3276
{
3277
Variable* v = variables.find(local);
3278
3279
if (!v || !v->constant)
3280
return false;
3281
}
3282
3283
return true;
3284
}
3285
3286
void compileStatLocal(AstStatLocal* stat)
3287
{
3288
// Optimization: we don't need to allocate and assign const locals, since their uses will be constant-folded
3289
if (options.optimizationLevel >= 1 && options.debugLevel <= 1 && areLocalsRedundant(stat))
3290
return;
3291
3292
// Optimization: for 1-1 local assignments, we can reuse the register *if* neither local is mutated
3293
if (options.optimizationLevel >= 1 && stat->vars.size == 1 && stat->values.size == 1)
3294
{
3295
if (AstExprLocal* re = getExprLocal(stat->values.data[0]))
3296
{
3297
Variable* lv = variables.find(stat->vars.data[0]);
3298
Variable* rv = variables.find(re->local);
3299
3300
if (int reg = getExprLocalReg(re); reg >= 0 && (!lv || !lv->written) && (!rv || !rv->written))
3301
{
3302
pushLocal(stat->vars.data[0], uint8_t(reg), kDefaultAllocPc);
3303
return;
3304
}
3305
}
3306
}
3307
3308
// note: allocReg in this case allocates into parent block register - note that we don't have RegScope here
3309
uint8_t vars = allocReg(stat, unsigned(stat->vars.size));
3310
uint32_t allocpc = bytecode.getDebugPC();
3311
3312
compileExprListTemp(stat->values, vars, uint8_t(stat->vars.size), /* targetTop= */ true);
3313
3314
for (size_t i = 0; i < stat->vars.size; ++i)
3315
pushLocal(stat->vars.data[i], uint8_t(vars + i), allocpc);
3316
}
3317
3318
bool tryCompileUnrolledFor(AstStatFor* stat, int thresholdBase, int thresholdMaxBoost)
3319
{
3320
Constant one = {Constant::Type_Number};
3321
one.valueNumber = 1.0;
3322
3323
Constant fromc = getConstant(stat->from);
3324
Constant toc = getConstant(stat->to);
3325
Constant stepc = stat->step ? getConstant(stat->step) : one;
3326
3327
int tripCount = (fromc.type == Constant::Type_Number && toc.type == Constant::Type_Number && stepc.type == Constant::Type_Number)
3328
? getTripCount(fromc.valueNumber, toc.valueNumber, stepc.valueNumber)
3329
: -1;
3330
3331
if (tripCount < 0)
3332
{
3333
bytecode.addDebugRemark("loop unroll failed: invalid iteration count");
3334
return false;
3335
}
3336
3337
if (tripCount > thresholdBase)
3338
{
3339
bytecode.addDebugRemark("loop unroll failed: too many iterations (%d)", tripCount);
3340
return false;
3341
}
3342
3343
if (Variable* lv = variables.find(stat->var); lv && lv->written)
3344
{
3345
bytecode.addDebugRemark("loop unroll failed: mutable loop variable");
3346
return false;
3347
}
3348
3349
AstLocal* var = stat->var;
3350
uint64_t costModel = modelCost(stat->body, &var, 1, builtins, constants);
3351
3352
// we use a dynamic cost threshold that's based on the fixed limit boosted by the cost advantage we gain due to unrolling
3353
bool varc = true;
3354
int unrolledCost = computeCost(costModel, &varc, 1) * tripCount;
3355
int baselineCost = (computeCost(costModel, nullptr, 0) + 1) * tripCount;
3356
int unrollProfit = (unrolledCost == 0) ? thresholdMaxBoost : std::min(thresholdMaxBoost, 100 * baselineCost / unrolledCost);
3357
3358
int threshold = thresholdBase * unrollProfit / 100;
3359
3360
if (unrolledCost > threshold)
3361
{
3362
bytecode.addDebugRemark(
3363
"loop unroll failed: too expensive (iterations %d, cost %d, profit %.2fx)", tripCount, unrolledCost, double(unrollProfit) / 100
3364
);
3365
return false;
3366
}
3367
3368
bytecode.addDebugRemark("loop unroll succeeded (iterations %d, cost %d, profit %.2fx)", tripCount, unrolledCost, double(unrollProfit) / 100);
3369
3370
compileUnrolledFor(stat, tripCount, fromc.valueNumber, stepc.valueNumber);
3371
return true;
3372
}
3373
3374
void compileUnrolledFor(AstStatFor* stat, int tripCount, double from, double step)
3375
{
3376
AstLocal* var = stat->var;
3377
3378
size_t oldLocals = localStack.size();
3379
size_t oldJumps = loopJumps.size();
3380
3381
loops.push_back({oldLocals, oldLocals, nullptr});
3382
3383
for (int iv = 0; iv < tripCount; ++iv)
3384
{
3385
// we need to re-fold constants in the loop body with the new value; this reuses computed constant values elsewhere in the tree
3386
locstants[var].type = Constant::Type_Number;
3387
locstants[var].valueNumber = from + iv * step;
3388
3389
foldConstants(constants, variables, locstants, builtinsFold, builtinsFoldLibraryK, options.libraryMemberConstantCb, stat, names);
3390
3391
size_t iterJumps = loopJumps.size();
3392
3393
compileStat(stat->body);
3394
3395
// all continue jumps need to go to the next iteration
3396
size_t contLabel = bytecode.emitLabel();
3397
3398
for (size_t i = iterJumps; i < loopJumps.size(); ++i)
3399
if (loopJumps[i].type == LoopJump::Continue)
3400
patchJump(stat, loopJumps[i].label, contLabel);
3401
}
3402
3403
// all break jumps need to go past the loop
3404
size_t endLabel = bytecode.emitLabel();
3405
3406
for (size_t i = oldJumps; i < loopJumps.size(); ++i)
3407
if (loopJumps[i].type == LoopJump::Break)
3408
patchJump(stat, loopJumps[i].label, endLabel);
3409
3410
loopJumps.resize(oldJumps);
3411
3412
loops.pop_back();
3413
3414
// clean up fold state in case we need to recompile - normally we compile the loop body once, but due to inlining we may need to do it again
3415
locstants[var].type = Constant::Type_Unknown;
3416
3417
foldConstants(constants, variables, locstants, builtinsFold, builtinsFoldLibraryK, options.libraryMemberConstantCb, stat, names);
3418
}
3419
3420
void compileStatFor(AstStatFor* stat)
3421
{
3422
RegScope rs(this);
3423
3424
// Optimization: small loops can be unrolled when it is profitable
3425
if (options.optimizationLevel >= 2 && isConstant(stat->to) && isConstant(stat->from) && (!stat->step || isConstant(stat->step)))
3426
if (tryCompileUnrolledFor(stat, FInt::LuauCompileLoopUnrollThreshold, FInt::LuauCompileLoopUnrollThresholdMaxBoost))
3427
return;
3428
3429
size_t oldLocals = localStack.size();
3430
size_t oldJumps = loopJumps.size();
3431
3432
loops.push_back({oldLocals, oldLocals, nullptr});
3433
hasLoops = true;
3434
3435
// register layout: limit, step, index
3436
uint8_t regs = allocReg(stat, 3u);
3437
3438
// if the iteration index is assigned from within the loop, we need to protect the internal index from the assignment
3439
// to do that, we will copy the index into an actual local variable on each iteration
3440
// this makes sure the code inside the loop can't interfere with the iteration process (other than modifying the table we're iterating
3441
// through)
3442
uint8_t varreg = regs + 2;
3443
uint32_t varregallocpc = bytecode.getDebugPC();
3444
3445
if (Variable* il = variables.find(stat->var); il && il->written)
3446
varreg = allocReg(stat, 1u);
3447
3448
compileExprTemp(stat->from, uint8_t(regs + 2));
3449
compileExprTemp(stat->to, uint8_t(regs + 0));
3450
3451
if (stat->step)
3452
compileExprTemp(stat->step, uint8_t(regs + 1));
3453
else
3454
bytecode.emitABC(LOP_LOADN, uint8_t(regs + 1), 1, 0);
3455
3456
size_t forLabel = bytecode.emitLabel();
3457
3458
bytecode.emitAD(LOP_FORNPREP, regs, 0);
3459
3460
size_t loopLabel = bytecode.emitLabel();
3461
3462
if (varreg != regs + 2)
3463
bytecode.emitABC(LOP_MOVE, varreg, regs + 2, 0);
3464
3465
pushLocal(stat->var, varreg, varregallocpc);
3466
3467
compileStat(stat->body);
3468
3469
closeLocals(oldLocals);
3470
popLocals(oldLocals);
3471
3472
setDebugLine(stat);
3473
3474
size_t contLabel = bytecode.emitLabel();
3475
3476
size_t backLabel = bytecode.emitLabel();
3477
3478
bytecode.emitAD(LOP_FORNLOOP, regs, 0);
3479
3480
size_t endLabel = bytecode.emitLabel();
3481
3482
patchJump(stat, forLabel, endLabel);
3483
patchJump(stat, backLabel, loopLabel);
3484
3485
patchLoopJumps(stat, oldJumps, endLabel, contLabel);
3486
loopJumps.resize(oldJumps);
3487
3488
loops.pop_back();
3489
}
3490
3491
void compileStatForIn(AstStatForIn* stat)
3492
{
3493
RegScope rs(this);
3494
3495
size_t oldLocals = localStack.size();
3496
size_t oldJumps = loopJumps.size();
3497
3498
loops.push_back({oldLocals, oldLocals, nullptr});
3499
hasLoops = true;
3500
3501
// register layout: generator, state, index, variables...
3502
uint8_t regs = allocReg(stat, 3u);
3503
3504
// this puts initial values of (generator, state, index) into the loop registers
3505
compileExprListTemp(stat->values, regs, 3, /* targetTop= */ true);
3506
3507
// note that we reserve at least 2 variables; this allows our fast path to assume that we need 2 variables instead of 1 or 2
3508
uint8_t vars = allocReg(stat, std::max(unsigned(stat->vars.size), 2u));
3509
LUAU_ASSERT(vars == regs + 3);
3510
uint32_t varsallocpc = bytecode.getDebugPC();
3511
3512
LuauOpcode skipOp = LOP_FORGPREP;
3513
3514
// Optimization: when we iterate via pairs/ipairs, we generate special bytecode that optimizes the traversal using internal iteration index
3515
// These instructions dynamically check if generator is equal to next/inext and bail out
3516
// They assume that the generator produces 2 variables, which is why we allocate at least 2 above (see vars assignment)
3517
if (options.optimizationLevel >= 1 && stat->vars.size <= 2)
3518
{
3519
if (stat->values.size == 1 && stat->values.data[0]->is<AstExprCall>())
3520
{
3521
Builtin builtin = getBuiltin(stat->values.data[0]->as<AstExprCall>()->func, globals, variables);
3522
3523
if (builtin.isGlobal("ipairs")) // for .. in ipairs(t)
3524
skipOp = LOP_FORGPREP_INEXT;
3525
else if (builtin.isGlobal("pairs")) // for .. in pairs(t)
3526
skipOp = LOP_FORGPREP_NEXT;
3527
}
3528
else if (stat->values.size == 2)
3529
{
3530
Builtin builtin = getBuiltin(stat->values.data[0], globals, variables);
3531
3532
if (builtin.isGlobal("next")) // for .. in next,t
3533
skipOp = LOP_FORGPREP_NEXT;
3534
}
3535
}
3536
3537
// first iteration jumps into FORGLOOP instruction, but for ipairs/pairs it does extra preparation that makes the cost of an extra instruction
3538
// worthwhile
3539
size_t skipLabel = bytecode.emitLabel();
3540
3541
bytecode.emitAD(skipOp, regs, 0);
3542
3543
size_t loopLabel = bytecode.emitLabel();
3544
3545
for (size_t i = 0; i < stat->vars.size; ++i)
3546
pushLocal(stat->vars.data[i], uint8_t(vars + i), varsallocpc);
3547
3548
compileStat(stat->body);
3549
3550
closeLocals(oldLocals);
3551
popLocals(oldLocals);
3552
3553
setDebugLine(stat);
3554
3555
size_t contLabel = bytecode.emitLabel();
3556
3557
size_t backLabel = bytecode.emitLabel();
3558
3559
// FORGLOOP uses aux to encode variable count and fast path flag for ipairs traversal in the high bit
3560
bytecode.emitAD(LOP_FORGLOOP, regs, 0);
3561
bytecode.emitAux((skipOp == LOP_FORGPREP_INEXT ? 0x80000000 : 0) | uint32_t(stat->vars.size));
3562
3563
size_t endLabel = bytecode.emitLabel();
3564
3565
patchJump(stat, skipLabel, backLabel);
3566
patchJump(stat, backLabel, loopLabel);
3567
3568
patchLoopJumps(stat, oldJumps, endLabel, contLabel);
3569
loopJumps.resize(oldJumps);
3570
3571
loops.pop_back();
3572
}
3573
3574
struct Assignment
3575
{
3576
LValue lvalue;
3577
3578
uint8_t conflictReg = kInvalidReg;
3579
uint8_t valueReg = kInvalidReg;
3580
};
3581
3582
// This function analyzes assignments and marks assignment conflicts: cases when a variable is assigned on lhs
3583
// but subsequently used on the rhs, assuming assignments are performed in order. Note that it's also possible
3584
// for a variable to conflict on the lhs, if it's used in an lvalue expression after it's assigned.
3585
// When conflicts are found, Assignment::conflictReg is allocated and that's where assignment is performed instead,
3586
// until the final fixup in compileStatAssign. Assignment::valueReg is allocated by compileStatAssign as well.
3587
//
3588
// Per Lua manual, section 3.3.3 (Assignments), the proper assignment order is only guaranteed to hold for syntactic access:
3589
//
3590
// Note that this guarantee covers only accesses syntactically inside the assignment statement. If a function or a metamethod called
3591
// during the assignment changes the value of a variable, Lua gives no guarantees about the order of that access.
3592
//
3593
// As such, we currently don't check if an assigned local is captured, which may mean it gets reassigned during a function call.
3594
void resolveAssignConflicts(AstStat* stat, std::vector<Assignment>& vars, const AstArray<AstExpr*>& values)
3595
{
3596
struct Visitor : AstVisitor
3597
{
3598
Compiler* self;
3599
3600
std::bitset<256> conflict;
3601
std::bitset<256> assigned;
3602
3603
Visitor(Compiler* self)
3604
: self(self)
3605
{
3606
}
3607
3608
bool visit(AstExprLocal* node) override
3609
{
3610
int reg = self->getLocalReg(node->local);
3611
3612
if (reg >= 0 && assigned[reg])
3613
conflict[reg] = true;
3614
3615
return true;
3616
}
3617
};
3618
3619
Visitor visitor(this);
3620
3621
// mark any registers that are used *after* assignment as conflicting
3622
3623
// first we go through assignments to locals, since they are performed before assignments to other l-values
3624
for (size_t i = 0; i < vars.size(); ++i)
3625
{
3626
const LValue& li = vars[i].lvalue;
3627
3628
if (li.kind == LValue::Kind_Local)
3629
{
3630
if (i < values.size)
3631
values.data[i]->visit(&visitor);
3632
3633
visitor.assigned[li.reg] = true;
3634
}
3635
}
3636
3637
// and now we handle all other l-values
3638
for (size_t i = 0; i < vars.size(); ++i)
3639
{
3640
const LValue& li = vars[i].lvalue;
3641
3642
if (li.kind != LValue::Kind_Local && i < values.size)
3643
values.data[i]->visit(&visitor);
3644
}
3645
3646
// mark any registers used in trailing expressions as conflicting as well
3647
for (size_t i = vars.size(); i < values.size; ++i)
3648
values.data[i]->visit(&visitor);
3649
3650
// mark any registers used on left hand side that are also assigned anywhere as conflicting
3651
// this is order-independent because we evaluate all right hand side arguments into registers before doing table assignments
3652
for (const Assignment& var : vars)
3653
{
3654
const LValue& li = var.lvalue;
3655
3656
if ((li.kind == LValue::Kind_IndexName || li.kind == LValue::Kind_IndexNumber || li.kind == LValue::Kind_IndexExpr) &&
3657
visitor.assigned[li.reg])
3658
visitor.conflict[li.reg] = true;
3659
3660
if (li.kind == LValue::Kind_IndexExpr && visitor.assigned[li.index])
3661
visitor.conflict[li.index] = true;
3662
}
3663
3664
// for any conflicting var, we need to allocate a temporary register where the assignment is performed, so that we can move the value later
3665
for (Assignment& var : vars)
3666
{
3667
const LValue& li = var.lvalue;
3668
3669
if (li.kind == LValue::Kind_Local && visitor.conflict[li.reg])
3670
var.conflictReg = allocReg(stat, 1u);
3671
}
3672
}
3673
3674
void compileStatAssign(AstStatAssign* stat)
3675
{
3676
RegScope rs(this);
3677
3678
// Optimization: one to one assignments don't require complex conflict resolution machinery
3679
if (stat->vars.size == 1 && stat->values.size == 1)
3680
{
3681
LValue var = compileLValue(stat->vars.data[0], rs);
3682
3683
// Optimization: assign to locals directly
3684
if (var.kind == LValue::Kind_Local)
3685
{
3686
compileExpr(stat->values.data[0], var.reg);
3687
}
3688
else
3689
{
3690
uint8_t reg = compileExprAuto(stat->values.data[0], rs);
3691
3692
setDebugLine(stat->vars.data[0]);
3693
3694
compileAssign(var, reg, stat->vars.data[0]);
3695
}
3696
return;
3697
}
3698
3699
// compute all l-values: note that this doesn't assign anything yet but it allocates registers and computes complex expressions on the
3700
// left hand side - for example, in "a[expr] = foo" expr will get evaluated here
3701
std::vector<Assignment> vars(stat->vars.size);
3702
3703
for (size_t i = 0; i < stat->vars.size; ++i)
3704
vars[i].lvalue = compileLValue(stat->vars.data[i], rs);
3705
3706
// perform conflict resolution: if any expression refers to a local that is assigned before evaluating it, we assign to a temporary
3707
// register after this, vars[i].conflictReg is set for locals that need to be assigned in the second pass
3708
resolveAssignConflicts(stat, vars, stat->values);
3709
3710
// compute rhs into (mostly) fresh registers
3711
// note that when the lhs assignment is a local, we evaluate directly into that register
3712
// this is possible because resolveAssignConflicts renamed conflicting locals into temporaries
3713
// after this, vars[i].valueReg is set to a register with the value for *all* vars, but some have already been assigned
3714
for (size_t i = 0; i < stat->vars.size && i < stat->values.size; ++i)
3715
{
3716
AstExpr* value = stat->values.data[i];
3717
3718
if (i + 1 == stat->values.size && stat->vars.size > stat->values.size)
3719
{
3720
// allocate a consecutive range of regs for all remaining vars and compute everything into temps
3721
// note, this also handles trailing nils
3722
unsigned rest = unsigned(stat->vars.size - stat->values.size + 1);
3723
uint8_t temp = allocReg(stat, rest);
3724
3725
compileExprTempN(value, temp, uint8_t(rest), /* targetTop= */ true);
3726
3727
for (size_t j = i; j < stat->vars.size; ++j)
3728
vars[j].valueReg = uint8_t(temp + (j - i));
3729
}
3730
else
3731
{
3732
Assignment& var = vars[i];
3733
3734
// if target is a local, use compileExpr directly to target
3735
if (var.lvalue.kind == LValue::Kind_Local)
3736
{
3737
var.valueReg = (var.conflictReg == kInvalidReg) ? var.lvalue.reg : var.conflictReg;
3738
3739
compileExpr(stat->values.data[i], var.valueReg);
3740
}
3741
else
3742
{
3743
var.valueReg = compileExprAuto(stat->values.data[i], rs);
3744
}
3745
}
3746
}
3747
3748
// compute expressions with side effects
3749
for (size_t i = stat->vars.size; i < stat->values.size; ++i)
3750
compileExprSide(stat->values.data[i]);
3751
3752
// almost done... let's assign everything left to right, noting that locals were either written-to directly, or will be written-to in a
3753
// separate pass to avoid conflicts
3754
size_t varPos = 0;
3755
for (const Assignment& var : vars)
3756
{
3757
LUAU_ASSERT(var.valueReg != kInvalidReg);
3758
3759
if (var.lvalue.kind != LValue::Kind_Local)
3760
{
3761
setDebugLine(var.lvalue.location);
3762
3763
if (varPos < stat->vars.size)
3764
compileAssign(var.lvalue, var.valueReg, stat->vars.data[varPos]);
3765
else
3766
compileAssign(var.lvalue, var.valueReg, nullptr);
3767
}
3768
3769
varPos++;
3770
}
3771
3772
// all regular local writes are done by the prior loops by computing result directly into target, so this just handles conflicts OR
3773
// local copies from temporary registers in multret context, since in that case we have to allocate consecutive temporaries
3774
for (const Assignment& var : vars)
3775
{
3776
if (var.lvalue.kind == LValue::Kind_Local && var.valueReg != var.lvalue.reg)
3777
bytecode.emitABC(LOP_MOVE, var.lvalue.reg, var.valueReg, 0);
3778
}
3779
}
3780
3781
void compileStatCompoundAssign(AstStatCompoundAssign* stat)
3782
{
3783
RegScope rs(this);
3784
3785
LValue var = compileLValue(stat->var, rs);
3786
3787
// Optimization: assign to locals directly
3788
uint8_t target = (var.kind == LValue::Kind_Local) ? var.reg : allocReg(stat, 1u);
3789
3790
switch (stat->op)
3791
{
3792
case AstExprBinary::Add:
3793
case AstExprBinary::Sub:
3794
case AstExprBinary::Mul:
3795
case AstExprBinary::Div:
3796
case AstExprBinary::FloorDiv:
3797
case AstExprBinary::Mod:
3798
case AstExprBinary::Pow:
3799
{
3800
if (var.kind != LValue::Kind_Local)
3801
compileLValueUse(var, target, /* set= */ false, stat->var);
3802
3803
int32_t rc = getConstantNumber(stat->value);
3804
3805
if (rc >= 0 && rc <= 255)
3806
{
3807
bytecode.emitABC(getBinaryOpArith(stat->op, /* k= */ true), target, target, uint8_t(rc));
3808
}
3809
else
3810
{
3811
uint8_t rr = compileExprAuto(stat->value, rs);
3812
3813
bytecode.emitABC(getBinaryOpArith(stat->op), target, target, rr);
3814
3815
if (var.kind != LValue::Kind_Local)
3816
hintTemporaryRegType(stat->var, target, LBC_TYPE_NUMBER, /* instLength */ 1);
3817
3818
hintTemporaryExprRegType(stat->value, rr, LBC_TYPE_NUMBER, /* instLength */ 1);
3819
}
3820
}
3821
break;
3822
3823
case AstExprBinary::Concat:
3824
{
3825
std::vector<AstExpr*> args = {stat->value};
3826
3827
// unroll the tree of concats down the right hand side to be able to do multiple ops
3828
unrollConcats(args);
3829
3830
uint8_t regs = allocReg(stat, unsigned(1 + args.size()));
3831
3832
compileLValueUse(var, regs, /* set= */ false, stat->var);
3833
3834
for (size_t i = 0; i < args.size(); ++i)
3835
compileExprTemp(args[i], uint8_t(regs + 1 + i));
3836
3837
bytecode.emitABC(LOP_CONCAT, target, regs, uint8_t(regs + args.size()));
3838
}
3839
break;
3840
3841
default:
3842
LUAU_ASSERT(!"Unexpected compound assignment operation");
3843
}
3844
3845
if (var.kind != LValue::Kind_Local)
3846
compileAssign(var, target, stat->var);
3847
}
3848
3849
void compileStatFunction(AstStatFunction* stat)
3850
{
3851
// Optimization: compile value expresion directly into target local register
3852
if (int reg = getExprLocalReg(stat->name); reg >= 0)
3853
{
3854
compileExpr(stat->func, uint8_t(reg));
3855
return;
3856
}
3857
3858
RegScope rs(this);
3859
uint8_t reg = allocReg(stat, 1u);
3860
3861
compileExprTemp(stat->func, reg);
3862
3863
LValue var = compileLValue(stat->name, rs);
3864
compileAssign(var, reg, stat->name);
3865
}
3866
3867
void compileStat(AstStat* node)
3868
{
3869
setDebugLine(node);
3870
3871
if (options.coverageLevel >= 1 && needsCoverage(node))
3872
{
3873
bytecode.emitABC(LOP_COVERAGE, 0, 0, 0);
3874
}
3875
3876
if (AstStatBlock* stat = node->as<AstStatBlock>())
3877
{
3878
RegScope rs(this);
3879
3880
size_t oldLocals = localStack.size();
3881
3882
for (size_t i = 0; i < stat->body.size; ++i)
3883
{
3884
AstStat* bodyStat = stat->body.data[i];
3885
compileStat(bodyStat);
3886
3887
if (alwaysTerminates(bodyStat))
3888
break;
3889
}
3890
3891
closeLocals(oldLocals);
3892
3893
popLocals(oldLocals);
3894
}
3895
else if (AstStatIf* stat = node->as<AstStatIf>())
3896
{
3897
compileStatIf(stat);
3898
}
3899
else if (AstStatWhile* stat = node->as<AstStatWhile>())
3900
{
3901
compileStatWhile(stat);
3902
}
3903
else if (AstStatRepeat* stat = node->as<AstStatRepeat>())
3904
{
3905
compileStatRepeat(stat);
3906
}
3907
else if (node->is<AstStatBreak>())
3908
{
3909
LUAU_ASSERT(!loops.empty());
3910
3911
// before exiting out of the loop, we need to close all local variables that were captured in closures since loop start
3912
// normally they are closed by the enclosing blocks, including the loop block, but we're skipping that here
3913
closeLocals(loops.back().localOffset);
3914
3915
size_t label = bytecode.emitLabel();
3916
3917
bytecode.emitAD(LOP_JUMP, 0, 0);
3918
3919
loopJumps.push_back({LoopJump::Break, label});
3920
}
3921
else if (AstStatContinue* stat = node->as<AstStatContinue>())
3922
{
3923
LUAU_ASSERT(!loops.empty());
3924
3925
// track continue statement for repeat..until validation (validateContinueUntil)
3926
if (!loops.back().continueUsed)
3927
loops.back().continueUsed = stat;
3928
3929
// before continuing, we need to close all local variables that were captured in closures since loop start
3930
// normally they are closed by the enclosing blocks, including the loop block, but we're skipping that here
3931
closeLocals(loops.back().localOffsetContinue);
3932
3933
size_t label = bytecode.emitLabel();
3934
3935
bytecode.emitAD(LOP_JUMP, 0, 0);
3936
3937
loopJumps.push_back({LoopJump::Continue, label});
3938
}
3939
else if (AstStatReturn* stat = node->as<AstStatReturn>())
3940
{
3941
if (options.optimizationLevel >= 2 && !inlineFrames.empty())
3942
compileInlineReturn(stat, /* fallthrough= */ false);
3943
else
3944
compileStatReturn(stat);
3945
}
3946
else if (AstStatExpr* stat = node->as<AstStatExpr>())
3947
{
3948
// Optimization: since we don't need to read anything from the stack, we can compile the call to not return anything which saves register
3949
// moves
3950
if (AstExprCall* expr = stat->expr->as<AstExprCall>())
3951
{
3952
uint8_t target = uint8_t(regTop);
3953
3954
compileExprCall(expr, target, /* targetCount= */ 0);
3955
}
3956
else
3957
{
3958
compileExprSide(stat->expr);
3959
}
3960
}
3961
else if (AstStatLocal* stat = node->as<AstStatLocal>())
3962
{
3963
compileStatLocal(stat);
3964
}
3965
else if (AstStatFor* stat = node->as<AstStatFor>())
3966
{
3967
compileStatFor(stat);
3968
}
3969
else if (AstStatForIn* stat = node->as<AstStatForIn>())
3970
{
3971
compileStatForIn(stat);
3972
}
3973
else if (AstStatAssign* stat = node->as<AstStatAssign>())
3974
{
3975
compileStatAssign(stat);
3976
}
3977
else if (AstStatCompoundAssign* stat = node->as<AstStatCompoundAssign>())
3978
{
3979
compileStatCompoundAssign(stat);
3980
}
3981
else if (AstStatFunction* stat = node->as<AstStatFunction>())
3982
{
3983
compileStatFunction(stat);
3984
}
3985
else if (AstStatLocalFunction* stat = node->as<AstStatLocalFunction>())
3986
{
3987
uint8_t var = allocReg(stat, 1u);
3988
3989
pushLocal(stat->name, var, kDefaultAllocPc);
3990
compileExprFunction(stat->func, var);
3991
3992
Local& l = locals[stat->name];
3993
3994
// we *have* to pushLocal before we compile the function, since the function may refer to the local as an upvalue
3995
// however, this means the debugpc for the local is at an instruction where the local value hasn't been computed yet
3996
// to fix this we just move the debugpc after the local value is established
3997
l.debugpc = bytecode.getDebugPC();
3998
}
3999
else if (node->is<AstStatTypeAlias>())
4000
{
4001
// do nothing
4002
}
4003
else if (node->is<AstStatTypeFunction>())
4004
{
4005
// do nothing
4006
}
4007
else
4008
{
4009
LUAU_ASSERT(!"Unknown statement type");
4010
}
4011
}
4012
4013
void validateContinueUntil(AstStat* cont, AstExpr* condition, AstStatBlock* body, size_t start)
4014
{
4015
UndefinedLocalVisitor visitor(this);
4016
4017
for (size_t i = start; i < body->body.size; ++i)
4018
{
4019
if (AstStatLocal* stat = body->body.data[i]->as<AstStatLocal>())
4020
{
4021
for (AstLocal* local : stat->vars)
4022
visitor.locals.insert(local);
4023
}
4024
else if (AstStatLocalFunction* stat = body->body.data[i]->as<AstStatLocalFunction>())
4025
{
4026
visitor.locals.insert(stat->name);
4027
}
4028
}
4029
4030
condition->visit(&visitor);
4031
4032
if (visitor.undef)
4033
CompileError::raise(
4034
condition->location,
4035
"Local %s used in the repeat..until condition is undefined because continue statement on line %d jumps over it",
4036
visitor.undef->name.value,
4037
cont->location.begin.line + 1
4038
);
4039
}
4040
4041
void gatherConstUpvals(AstExprFunction* func)
4042
{
4043
ConstUpvalueVisitor visitor(this);
4044
func->body->visit(&visitor);
4045
4046
for (AstLocal* local : visitor.upvals)
4047
getUpval(local);
4048
}
4049
4050
void pushLocal(AstLocal* local, uint8_t reg, uint32_t allocpc)
4051
{
4052
if (localStack.size() >= kMaxLocalCount)
4053
CompileError::raise(
4054
local->location, "Out of local registers when trying to allocate %s: exceeded limit %d", local->name.value, kMaxLocalCount
4055
);
4056
4057
localStack.push_back(local);
4058
4059
Local& l = locals[local];
4060
4061
LUAU_ASSERT(!l.allocated);
4062
4063
l.reg = reg;
4064
l.allocated = true;
4065
l.debugpc = bytecode.getDebugPC();
4066
l.allocpc = allocpc == kDefaultAllocPc ? l.debugpc : allocpc;
4067
}
4068
4069
bool areLocalsCaptured(size_t start)
4070
{
4071
LUAU_ASSERT(start <= localStack.size());
4072
4073
for (size_t i = start; i < localStack.size(); ++i)
4074
{
4075
Local* l = locals.find(localStack[i]);
4076
LUAU_ASSERT(l);
4077
4078
if (l->captured)
4079
return true;
4080
}
4081
4082
return false;
4083
}
4084
4085
void closeLocals(size_t start)
4086
{
4087
LUAU_ASSERT(start <= localStack.size());
4088
4089
bool captured = false;
4090
uint8_t captureReg = 255;
4091
4092
for (size_t i = start; i < localStack.size(); ++i)
4093
{
4094
Local* l = locals.find(localStack[i]);
4095
LUAU_ASSERT(l);
4096
4097
if (l->captured)
4098
{
4099
captured = true;
4100
captureReg = std::min(captureReg, l->reg);
4101
}
4102
}
4103
4104
if (captured)
4105
{
4106
bytecode.emitABC(LOP_CLOSEUPVALS, captureReg, 0, 0);
4107
}
4108
}
4109
4110
void popLocals(size_t start)
4111
{
4112
LUAU_ASSERT(start <= localStack.size());
4113
4114
for (size_t i = start; i < localStack.size(); ++i)
4115
{
4116
Local* l = locals.find(localStack[i]);
4117
LUAU_ASSERT(l);
4118
LUAU_ASSERT(l->allocated);
4119
4120
l->allocated = false;
4121
4122
if (options.debugLevel >= 2)
4123
{
4124
uint32_t debugpc = bytecode.getDebugPC();
4125
4126
bytecode.pushDebugLocal(sref(localStack[i]->name), l->reg, l->debugpc, debugpc);
4127
}
4128
4129
if (options.typeInfoLevel >= 1 && i >= argCount)
4130
{
4131
uint32_t debugpc = bytecode.getDebugPC();
4132
LuauBytecodeType ty = LBC_TYPE_ANY;
4133
4134
if (LuauBytecodeType* recordedTy = localTypes.find(localStack[i]))
4135
ty = *recordedTy;
4136
4137
bytecode.pushLocalTypeInfo(ty, l->reg, l->allocpc, debugpc);
4138
}
4139
}
4140
4141
localStack.resize(start);
4142
}
4143
4144
void patchJump(AstNode* node, size_t label, size_t target)
4145
{
4146
if (!bytecode.patchJumpD(label, target))
4147
CompileError::raise(node->location, "Exceeded jump distance limit; simplify the code to compile");
4148
}
4149
4150
void patchJumps(AstNode* node, std::vector<size_t>& labels, size_t target)
4151
{
4152
for (size_t l : labels)
4153
patchJump(node, l, target);
4154
}
4155
4156
void patchLoopJumps(AstNode* node, size_t oldJumps, size_t endLabel, size_t contLabel)
4157
{
4158
LUAU_ASSERT(oldJumps <= loopJumps.size());
4159
4160
for (size_t i = oldJumps; i < loopJumps.size(); ++i)
4161
{
4162
const LoopJump& lj = loopJumps[i];
4163
4164
switch (lj.type)
4165
{
4166
case LoopJump::Break:
4167
patchJump(node, lj.label, endLabel);
4168
break;
4169
4170
case LoopJump::Continue:
4171
patchJump(node, lj.label, contLabel);
4172
break;
4173
4174
default:
4175
LUAU_ASSERT(!"Unknown loop jump type");
4176
}
4177
}
4178
}
4179
4180
uint8_t allocReg(AstNode* node, unsigned int count)
4181
{
4182
unsigned int top = regTop;
4183
if (top + count > kMaxRegisterCount)
4184
CompileError::raise(node->location, "Out of registers when trying to allocate %d registers: exceeded limit %d", count, kMaxRegisterCount);
4185
4186
regTop += count;
4187
stackSize = std::max(stackSize, regTop);
4188
4189
return uint8_t(top);
4190
}
4191
4192
template<typename T>
4193
uint8_t allocReg(AstNode* node, T count) = delete;
4194
4195
void setDebugLine(AstNode* node)
4196
{
4197
if (options.debugLevel >= 1)
4198
bytecode.setDebugLine(node->location.begin.line + 1);
4199
}
4200
4201
void setDebugLine(const Location& location)
4202
{
4203
if (options.debugLevel >= 1)
4204
bytecode.setDebugLine(location.begin.line + 1);
4205
}
4206
4207
void setDebugLineEnd(AstNode* node)
4208
{
4209
if (options.debugLevel >= 1)
4210
bytecode.setDebugLine(node->location.end.line + 1);
4211
}
4212
4213
bool needsCoverage(AstNode* node)
4214
{
4215
return !node->is<AstStatBlock>() && !node->is<AstStatTypeAlias>();
4216
}
4217
4218
void hintTemporaryRegType(AstExpr* expr, int reg, LuauBytecodeType expectedType, int instLength)
4219
{
4220
// If we know the type of a temporary and it's not the type that would be expected by codegen, provide a hint
4221
if (LuauBytecodeType* ty = exprTypes.find(expr))
4222
{
4223
if (*ty != expectedType)
4224
bytecode.pushLocalTypeInfo(*ty, reg, bytecode.getDebugPC() - instLength, bytecode.getDebugPC());
4225
}
4226
}
4227
4228
void hintTemporaryExprRegType(AstExpr* expr, int reg, LuauBytecodeType expectedType, int instLength)
4229
{
4230
// If we allocated a temporary register for the operation argument, try hinting its type
4231
if (!getExprLocal(expr))
4232
hintTemporaryRegType(expr, reg, expectedType, instLength);
4233
}
4234
4235
struct FenvVisitor : AstVisitor
4236
{
4237
bool& getfenvUsed;
4238
bool& setfenvUsed;
4239
4240
FenvVisitor(bool& getfenvUsed, bool& setfenvUsed)
4241
: getfenvUsed(getfenvUsed)
4242
, setfenvUsed(setfenvUsed)
4243
{
4244
}
4245
4246
bool visit(AstExprGlobal* node) override
4247
{
4248
if (node->name == "getfenv")
4249
getfenvUsed = true;
4250
if (node->name == "setfenv")
4251
setfenvUsed = true;
4252
4253
return false;
4254
}
4255
};
4256
4257
struct FunctionVisitor : AstVisitor
4258
{
4259
std::vector<AstExprFunction*>& functions;
4260
bool hasTypes = false;
4261
bool hasNativeFunction = false;
4262
4263
FunctionVisitor(std::vector<AstExprFunction*>& functions)
4264
: functions(functions)
4265
{
4266
// preallocate the result; this works around std::vector's inefficient growth policy for small arrays
4267
functions.reserve(16);
4268
}
4269
4270
bool visit(AstExprFunction* node) override
4271
{
4272
node->body->visit(this);
4273
4274
for (AstLocal* arg : node->args)
4275
hasTypes |= arg->annotation != nullptr;
4276
4277
// this makes sure all functions that are used when compiling this one have been already added to the vector
4278
functions.push_back(node);
4279
4280
if (!hasNativeFunction && node->hasNativeAttribute())
4281
hasNativeFunction = true;
4282
4283
return false;
4284
}
4285
4286
bool visit(AstStatTypeFunction* node) override
4287
{
4288
return false;
4289
}
4290
};
4291
4292
struct UndefinedLocalVisitor : AstVisitor
4293
{
4294
UndefinedLocalVisitor(Compiler* self)
4295
: self(self)
4296
, undef(nullptr)
4297
, locals(nullptr)
4298
{
4299
}
4300
4301
void check(AstLocal* local)
4302
{
4303
if (!undef && locals.contains(local))
4304
undef = local;
4305
}
4306
4307
bool visit(AstExprLocal* node) override
4308
{
4309
if (!node->upvalue)
4310
check(node->local);
4311
4312
return false;
4313
}
4314
4315
bool visit(AstExprFunction* node) override
4316
{
4317
const Function* f = self->functions.find(node);
4318
LUAU_ASSERT(f);
4319
4320
for (AstLocal* uv : f->upvals)
4321
{
4322
LUAU_ASSERT(uv->functionDepth < node->functionDepth);
4323
4324
if (uv->functionDepth == node->functionDepth - 1)
4325
check(uv);
4326
}
4327
4328
return false;
4329
}
4330
4331
Compiler* self;
4332
AstLocal* undef;
4333
DenseHashSet<AstLocal*> locals;
4334
};
4335
4336
struct ConstUpvalueVisitor : AstVisitor
4337
{
4338
ConstUpvalueVisitor(Compiler* self)
4339
: self(self)
4340
{
4341
}
4342
4343
bool visit(AstExprLocal* node) override
4344
{
4345
if (node->upvalue && self->isConstant(node))
4346
{
4347
upvals.push_back(node->local);
4348
}
4349
4350
return false;
4351
}
4352
4353
bool visit(AstExprFunction* node) override
4354
{
4355
// short-circuits the traversal to make it faster
4356
return false;
4357
}
4358
4359
Compiler* self;
4360
std::vector<AstLocal*> upvals;
4361
};
4362
4363
struct ReturnVisitor : AstVisitor
4364
{
4365
Compiler* self;
4366
bool returnsOne = true;
4367
4368
ReturnVisitor(Compiler* self)
4369
: self(self)
4370
{
4371
}
4372
4373
bool visit(AstExpr* expr) override
4374
{
4375
return false;
4376
}
4377
4378
bool visit(AstStatReturn* stat) override
4379
{
4380
returnsOne &= stat->list.size == 1 && !self->isExprMultRet(stat->list.data[0]);
4381
4382
return false;
4383
}
4384
};
4385
4386
struct RegScope
4387
{
4388
RegScope(Compiler* self)
4389
: self(self)
4390
, oldTop(self->regTop)
4391
{
4392
}
4393
4394
// This ctor is useful to forcefully adjust the stack frame in case we know that registers after a certain point are scratch and can be
4395
// discarded
4396
RegScope(Compiler* self, unsigned int top)
4397
: self(self)
4398
, oldTop(self->regTop)
4399
{
4400
LUAU_ASSERT(top <= self->regTop);
4401
self->regTop = top;
4402
}
4403
4404
~RegScope()
4405
{
4406
self->regTop = oldTop;
4407
}
4408
4409
Compiler* self;
4410
unsigned int oldTop;
4411
};
4412
4413
struct Function
4414
{
4415
uint32_t id;
4416
std::vector<AstLocal*> upvals;
4417
4418
uint64_t costModel = 0;
4419
unsigned int stackSize = 0;
4420
bool canInline = false;
4421
bool returnsOne = false;
4422
};
4423
4424
struct Local
4425
{
4426
uint8_t reg = 0;
4427
bool allocated = false;
4428
bool captured = false;
4429
uint32_t debugpc = 0;
4430
uint32_t allocpc = 0;
4431
};
4432
4433
struct LoopJump
4434
{
4435
enum Type
4436
{
4437
Break,
4438
Continue
4439
};
4440
4441
Type type;
4442
size_t label;
4443
};
4444
4445
struct Loop
4446
{
4447
size_t localOffset;
4448
size_t localOffsetContinue;
4449
4450
AstStatContinue* continueUsed;
4451
};
4452
4453
struct InlineArg
4454
{
4455
AstLocal* local;
4456
4457
uint8_t reg;
4458
Constant value;
4459
uint32_t allocpc;
4460
4461
AstExpr* init;
4462
};
4463
4464
struct InlineFrame
4465
{
4466
AstExprFunction* func;
4467
4468
size_t localOffset;
4469
4470
uint8_t target;
4471
uint8_t targetCount;
4472
4473
std::vector<size_t> returnJumps;
4474
};
4475
4476
struct Capture
4477
{
4478
LuauCaptureType type;
4479
uint8_t data;
4480
};
4481
4482
BytecodeBuilder& bytecode;
4483
4484
CompileOptions options;
4485
4486
DenseHashMap<AstExprFunction*, Function> functions;
4487
DenseHashMap<AstLocal*, Local> locals;
4488
DenseHashMap<AstName, Global> globals;
4489
DenseHashMap<AstLocal*, Variable> variables;
4490
DenseHashMap<AstExpr*, Constant> constants;
4491
DenseHashMap<AstLocal*, Constant> locstants;
4492
DenseHashMap<AstExprTable*, TableShape> tableShapes;
4493
DenseHashMap<AstExprCall*, int> builtins;
4494
DenseHashMap<AstName, uint8_t> userdataTypes;
4495
DenseHashMap<AstExprFunction*, std::string> functionTypes;
4496
DenseHashMap<AstLocal*, LuauBytecodeType> localTypes;
4497
DenseHashMap<AstExpr*, LuauBytecodeType> exprTypes;
4498
4499
DenseHashMap<AstExprCall*, int> inlineBuiltins{nullptr};
4500
DenseHashMap<AstExprCall*, int> inlineBuiltinsBackup{nullptr};
4501
4502
BuiltinAstTypes builtinTypes;
4503
AstNameTable& names;
4504
4505
const DenseHashMap<AstExprCall*, int>* builtinsFold = nullptr;
4506
bool builtinsFoldLibraryK = false;
4507
4508
// compileFunction state, gets reset for every function
4509
unsigned int regTop = 0;
4510
unsigned int stackSize = 0;
4511
size_t argCount = 0;
4512
bool hasLoops = false;
4513
4514
bool getfenvUsed = false;
4515
bool setfenvUsed = false;
4516
4517
std::vector<AstLocal*> localStack;
4518
std::vector<AstLocal*> upvals;
4519
std::vector<LoopJump> loopJumps;
4520
std::vector<Loop> loops;
4521
std::vector<InlineFrame> inlineFrames;
4522
std::vector<Capture> captures;
4523
};
4524
4525
static void setCompileOptionsForNativeCompilation(CompileOptions& options)
4526
{
4527
options.optimizationLevel = 2; // note: this might be removed in the future in favor of --!optimize
4528
options.typeInfoLevel = 1;
4529
}
4530
4531
void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, AstNameTable& names, const CompileOptions& inputOptions)
4532
{
4533
LUAU_TIMETRACE_SCOPE("compileOrThrow", "Compiler");
4534
4535
LUAU_ASSERT(parseResult.root);
4536
LUAU_ASSERT(parseResult.errors.empty());
4537
4538
CompileOptions options = inputOptions;
4539
uint8_t mainFlags = 0;
4540
4541
for (const HotComment& hc : parseResult.hotcomments)
4542
{
4543
if (hc.header && hc.content.compare(0, 9, "optimize ") == 0)
4544
options.optimizationLevel = std::max(0, std::min(2, atoi(hc.content.c_str() + 9)));
4545
4546
if (hc.header && hc.content == "native")
4547
{
4548
mainFlags |= LPF_NATIVE_MODULE;
4549
setCompileOptionsForNativeCompilation(options);
4550
}
4551
}
4552
4553
AstStatBlock* root = parseResult.root;
4554
4555
// gathers all functions with the invariant that all function references are to functions earlier in the list
4556
// for example, function foo() return function() end end will result in two vector entries, [0] = anonymous and [1] = foo
4557
std::vector<AstExprFunction*> functions;
4558
Compiler::FunctionVisitor functionVisitor(functions);
4559
root->visit(&functionVisitor);
4560
4561
if (functionVisitor.hasNativeFunction)
4562
setCompileOptionsForNativeCompilation(options);
4563
4564
Compiler compiler(bytecode, options, names);
4565
4566
// since access to some global objects may result in values that change over time, we block imports from non-readonly tables
4567
assignMutable(compiler.globals, names, options.mutableGlobals);
4568
4569
// this pass analyzes mutability of locals/globals and associates locals with their initial values
4570
trackValues(compiler.globals, compiler.variables, root);
4571
4572
// this visitor tracks calls to getfenv/setfenv and disables some optimizations when they are found
4573
if (options.optimizationLevel >= 1 && (names.get("getfenv").value || names.get("setfenv").value))
4574
{
4575
Compiler::FenvVisitor fenvVisitor(compiler.getfenvUsed, compiler.setfenvUsed);
4576
root->visit(&fenvVisitor);
4577
}
4578
4579
// builtin folding is enabled on optimization level 2 since we can't de-optimize folding at runtime
4580
if (options.optimizationLevel >= 2 && (!compiler.getfenvUsed && !compiler.setfenvUsed))
4581
{
4582
compiler.builtinsFold = &compiler.builtins;
4583
4584
if (AstName math = names.get("math"); math.value && getGlobalState(compiler.globals, math) == Global::Default)
4585
{
4586
compiler.builtinsFoldLibraryK = true;
4587
}
4588
else if (const char* const* ptr = options.librariesWithKnownMembers)
4589
{
4590
for (; *ptr; ++ptr)
4591
{
4592
if (AstName name = names.get(*ptr); name.value && getGlobalState(compiler.globals, name) == Global::Default)
4593
{
4594
compiler.builtinsFoldLibraryK = true;
4595
break;
4596
}
4597
}
4598
}
4599
}
4600
4601
if (options.optimizationLevel >= 1)
4602
{
4603
// this pass tracks which calls are builtins and can be compiled more efficiently
4604
analyzeBuiltins(compiler.builtins, compiler.globals, compiler.variables, options, root, names);
4605
4606
// this pass analyzes constantness of expressions
4607
foldConstants(
4608
compiler.constants,
4609
compiler.variables,
4610
compiler.locstants,
4611
compiler.builtinsFold,
4612
compiler.builtinsFoldLibraryK,
4613
options.libraryMemberConstantCb,
4614
root,
4615
names
4616
);
4617
4618
// this pass analyzes table assignments to estimate table shapes for initially empty tables
4619
predictTableShapes(compiler.tableShapes, root);
4620
}
4621
4622
if (const char* const* ptr = options.userdataTypes)
4623
{
4624
for (; *ptr; ++ptr)
4625
{
4626
// Type will only resolve to an AstName if it is actually mentioned in the source
4627
if (AstName name = names.get(*ptr); name.value)
4628
compiler.userdataTypes[name] = bytecode.addUserdataType(name.value);
4629
}
4630
4631
if (uintptr_t(ptr - options.userdataTypes) > (LBC_TYPE_TAGGED_USERDATA_END - LBC_TYPE_TAGGED_USERDATA_BASE))
4632
CompileError::raise(root->location, "Exceeded userdata type limit in the compilation options");
4633
}
4634
4635
// computes type information for all functions based on type annotations
4636
if (options.typeInfoLevel >= 1 || options.optimizationLevel >= 2)
4637
buildTypeMap(
4638
compiler.functionTypes,
4639
compiler.localTypes,
4640
compiler.exprTypes,
4641
root,
4642
options.vectorType,
4643
compiler.userdataTypes,
4644
compiler.builtinTypes,
4645
compiler.builtins,
4646
compiler.globals,
4647
options.libraryMemberTypeCb,
4648
bytecode
4649
);
4650
4651
for (AstExprFunction* expr : functions)
4652
{
4653
uint8_t protoflags = 0;
4654
compiler.compileFunction(expr, protoflags);
4655
4656
// If a function has native attribute and the whole module is not native, we set LPF_NATIVE_FUNCTION flag
4657
// This ensures that LPF_NATIVE_MODULE and LPF_NATIVE_FUNCTION are exclusive.
4658
if ((protoflags & LPF_NATIVE_FUNCTION) && !(mainFlags & LPF_NATIVE_MODULE))
4659
mainFlags |= LPF_NATIVE_FUNCTION;
4660
}
4661
4662
AstExprFunction main(
4663
root->location,
4664
/* attributes= */ AstArray<AstAttr*>({nullptr, 0}),
4665
/* generics= */ AstArray<AstGenericType*>(),
4666
/* genericPacks= */ AstArray<AstGenericTypePack*>(),
4667
/* self= */ nullptr,
4668
AstArray<AstLocal*>(),
4669
/* vararg= */ true,
4670
/* varargLocation= */ Luau::Location(),
4671
root,
4672
/* functionDepth= */ 0,
4673
/* debugname= */ AstName(),
4674
/* returnAnnotation= */ nullptr
4675
);
4676
uint32_t mainid = compiler.compileFunction(&main, mainFlags);
4677
4678
const Compiler::Function* mainf = compiler.functions.find(&main);
4679
LUAU_ASSERT(mainf && mainf->upvals.empty());
4680
4681
bytecode.setMainFunction(mainid);
4682
bytecode.finalize();
4683
}
4684
4685
void compileOrThrow(BytecodeBuilder& bytecode, const std::string& source, const CompileOptions& options, const ParseOptions& parseOptions)
4686
{
4687
Allocator allocator;
4688
AstNameTable names(allocator);
4689
ParseResult result = Parser::parse(source.c_str(), source.size(), names, allocator, parseOptions);
4690
4691
if (!result.errors.empty())
4692
throw ParseErrors(result.errors);
4693
4694
compileOrThrow(bytecode, result, names, options);
4695
}
4696
4697
std::string compile(const std::string& source, const CompileOptions& options, const ParseOptions& parseOptions, BytecodeEncoder* encoder)
4698
{
4699
LUAU_TIMETRACE_SCOPE("compile", "Compiler");
4700
4701
Allocator allocator;
4702
AstNameTable names(allocator);
4703
ParseResult result = Parser::parse(source.c_str(), source.size(), names, allocator, parseOptions);
4704
4705
if (!result.errors.empty())
4706
{
4707
// Users of this function expect only a single error message
4708
const Luau::ParseError& parseError = result.errors.front();
4709
std::string error = format(":%d: %s", parseError.getLocation().begin.line + 1, parseError.what());
4710
4711
return BytecodeBuilder::getError(error);
4712
}
4713
4714
try
4715
{
4716
BytecodeBuilder bcb(encoder);
4717
compileOrThrow(bcb, result, names, options);
4718
4719
return bcb.getBytecode();
4720
}
4721
catch (CompileError& e)
4722
{
4723
std::string error = format(":%d: %s", e.getLocation().begin.line + 1, e.what());
4724
return BytecodeBuilder::getError(error);
4725
}
4726
}
4727
4728
void setCompileConstantNil(CompileConstant* constant)
4729
{
4730
Compile::Constant* target = reinterpret_cast<Compile::Constant*>(constant);
4731
4732
target->type = Compile::Constant::Type_Nil;
4733
}
4734
4735
void setCompileConstantBoolean(CompileConstant* constant, bool b)
4736
{
4737
Compile::Constant* target = reinterpret_cast<Compile::Constant*>(constant);
4738
4739
target->type = Compile::Constant::Type_Boolean;
4740
target->valueBoolean = b;
4741
}
4742
4743
void setCompileConstantNumber(CompileConstant* constant, double n)
4744
{
4745
Compile::Constant* target = reinterpret_cast<Compile::Constant*>(constant);
4746
4747
target->type = Compile::Constant::Type_Number;
4748
target->valueNumber = n;
4749
}
4750
4751
void setCompileConstantInteger64(CompileConstant* constant, int64_t l)
4752
{
4753
Compile::Constant* target = reinterpret_cast<Compile::Constant*>(constant);
4754
4755
target->type = Compile::Constant::Type_Integer;
4756
target->valueInteger64 = l;
4757
}
4758
4759
void setCompileConstantVector(CompileConstant* constant, float x, float y, float z, float w)
4760
{
4761
Compile::Constant* target = reinterpret_cast<Compile::Constant*>(constant);
4762
4763
target->type = Compile::Constant::Type_Vector;
4764
target->valueVector[0] = x;
4765
target->valueVector[1] = y;
4766
target->valueVector[2] = z;
4767
target->valueVector[3] = w;
4768
}
4769
4770
void setCompileConstantString(CompileConstant* constant, const char* s, size_t l)
4771
{
4772
Compile::Constant* target = reinterpret_cast<Compile::Constant*>(constant);
4773
4774
if (l > std::numeric_limits<unsigned int>::max())
4775
CompileError::raise({}, "Exceeded custom string constant length limit");
4776
4777
target->type = Compile::Constant::Type_String;
4778
target->stringLength = unsigned(l);
4779
target->valueString = s;
4780
}
4781
4782
} // namespace Luau
4783
4784