Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Compiler/src/ConstantFolding.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 "ConstantFolding.h"
3
4
#include "BuiltinFolding.h"
5
#include "Luau/Bytecode.h"
6
#include "Luau/Lexer.h"
7
8
#include <vector>
9
#include <math.h>
10
11
LUAU_FASTFLAG(LuauIntegerType)
12
LUAU_FASTFLAGVARIABLE(LuauCompileFoldStringLimit)
13
14
namespace Luau
15
{
16
namespace Compile
17
{
18
19
constexpr size_t kConstantFoldStringLimit = 4096;
20
21
static bool constantsEqual(const Constant& la, const Constant& ra)
22
{
23
LUAU_ASSERT(la.type != Constant::Type_Unknown && ra.type != Constant::Type_Unknown);
24
25
switch (la.type)
26
{
27
case Constant::Type_Nil:
28
return ra.type == Constant::Type_Nil;
29
30
case Constant::Type_Boolean:
31
return ra.type == Constant::Type_Boolean && la.valueBoolean == ra.valueBoolean;
32
33
case Constant::Type_Number:
34
return ra.type == Constant::Type_Number && la.valueNumber == ra.valueNumber;
35
36
case Constant::Type_Vector:
37
return ra.type == Constant::Type_Vector && la.valueVector[0] == ra.valueVector[0] && la.valueVector[1] == ra.valueVector[1] &&
38
la.valueVector[2] == ra.valueVector[2] && la.valueVector[3] == ra.valueVector[3];
39
40
case Constant::Type_String:
41
return ra.type == Constant::Type_String && la.stringLength == ra.stringLength && memcmp(la.valueString, ra.valueString, la.stringLength) == 0;
42
43
case Constant::Type_Integer:
44
if (FFlag::LuauIntegerType)
45
return ra.type == Constant::Type_Integer && la.valueInteger64 == ra.valueInteger64;
46
[[fallthrough]];
47
48
default:
49
LUAU_ASSERT(!"Unexpected constant type in comparison");
50
return false;
51
}
52
}
53
54
static void foldUnary(Constant& result, AstExprUnary::Op op, const Constant& arg)
55
{
56
switch (op)
57
{
58
case AstExprUnary::Not:
59
if (arg.type != Constant::Type_Unknown)
60
{
61
result.type = Constant::Type_Boolean;
62
result.valueBoolean = !arg.isTruthful();
63
}
64
break;
65
66
case AstExprUnary::Minus:
67
if (arg.type == Constant::Type_Number)
68
{
69
result.type = Constant::Type_Number;
70
result.valueNumber = -arg.valueNumber;
71
}
72
else if (arg.type == Constant::Type_Vector)
73
{
74
result.type = Constant::Type_Vector;
75
result.valueVector[0] = -arg.valueVector[0];
76
result.valueVector[1] = -arg.valueVector[1];
77
result.valueVector[2] = -arg.valueVector[2];
78
result.valueVector[3] = -arg.valueVector[3];
79
}
80
break;
81
82
case AstExprUnary::Len:
83
if (arg.type == Constant::Type_String)
84
{
85
result.type = Constant::Type_Number;
86
result.valueNumber = double(arg.stringLength);
87
}
88
break;
89
90
default:
91
LUAU_ASSERT(!"Unexpected unary operation");
92
}
93
}
94
95
static void foldBinary(Constant& result, AstExprBinary::Op op, const Constant& la, const Constant& ra, AstNameTable& stringTable)
96
{
97
switch (op)
98
{
99
case AstExprBinary::Add:
100
if (la.type == Constant::Type_Number && ra.type == Constant::Type_Number)
101
{
102
result.type = Constant::Type_Number;
103
result.valueNumber = la.valueNumber + ra.valueNumber;
104
}
105
else if (la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
106
{
107
result.type = Constant::Type_Vector;
108
result.valueVector[0] = la.valueVector[0] + ra.valueVector[0];
109
result.valueVector[1] = la.valueVector[1] + ra.valueVector[1];
110
result.valueVector[2] = la.valueVector[2] + ra.valueVector[2];
111
result.valueVector[3] = la.valueVector[3] + ra.valueVector[3];
112
}
113
break;
114
115
case AstExprBinary::Sub:
116
if (la.type == Constant::Type_Number && ra.type == Constant::Type_Number)
117
{
118
result.type = Constant::Type_Number;
119
result.valueNumber = la.valueNumber - ra.valueNumber;
120
}
121
else if (la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
122
{
123
result.type = Constant::Type_Vector;
124
result.valueVector[0] = la.valueVector[0] - ra.valueVector[0];
125
result.valueVector[1] = la.valueVector[1] - ra.valueVector[1];
126
result.valueVector[2] = la.valueVector[2] - ra.valueVector[2];
127
result.valueVector[3] = la.valueVector[3] - ra.valueVector[3];
128
}
129
break;
130
131
case AstExprBinary::Mul:
132
if (la.type == Constant::Type_Number && ra.type == Constant::Type_Number)
133
{
134
result.type = Constant::Type_Number;
135
result.valueNumber = la.valueNumber * ra.valueNumber;
136
}
137
else if (la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
138
{
139
bool hadW = la.valueVector[3] != 0.0f || ra.valueVector[3] != 0.0f;
140
float resultW = la.valueVector[3] * ra.valueVector[3];
141
142
if (resultW == 0.0f || hadW)
143
{
144
result.type = Constant::Type_Vector;
145
result.valueVector[0] = la.valueVector[0] * ra.valueVector[0];
146
result.valueVector[1] = la.valueVector[1] * ra.valueVector[1];
147
result.valueVector[2] = la.valueVector[2] * ra.valueVector[2];
148
result.valueVector[3] = resultW;
149
}
150
}
151
else if (la.type == Constant::Type_Number && ra.type == Constant::Type_Vector)
152
{
153
bool hadW = ra.valueVector[3] != 0.0f;
154
float resultW = float(la.valueNumber) * ra.valueVector[3];
155
156
if (resultW == 0.0f || hadW)
157
{
158
result.type = Constant::Type_Vector;
159
result.valueVector[0] = float(la.valueNumber) * ra.valueVector[0];
160
result.valueVector[1] = float(la.valueNumber) * ra.valueVector[1];
161
result.valueVector[2] = float(la.valueNumber) * ra.valueVector[2];
162
result.valueVector[3] = resultW;
163
}
164
}
165
else if (la.type == Constant::Type_Vector && ra.type == Constant::Type_Number)
166
{
167
bool hadW = la.valueVector[3] != 0.0f;
168
float resultW = la.valueVector[3] * float(ra.valueNumber);
169
170
if (resultW == 0.0f || hadW)
171
{
172
result.type = Constant::Type_Vector;
173
result.valueVector[0] = la.valueVector[0] * float(ra.valueNumber);
174
result.valueVector[1] = la.valueVector[1] * float(ra.valueNumber);
175
result.valueVector[2] = la.valueVector[2] * float(ra.valueNumber);
176
result.valueVector[3] = resultW;
177
}
178
}
179
break;
180
181
case AstExprBinary::Div:
182
if (la.type == Constant::Type_Number && ra.type == Constant::Type_Number)
183
{
184
result.type = Constant::Type_Number;
185
result.valueNumber = la.valueNumber / ra.valueNumber;
186
}
187
else if (la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
188
{
189
bool hadW = la.valueVector[3] != 0.0f || ra.valueVector[3] != 0.0f;
190
float resultW = la.valueVector[3] / ra.valueVector[3];
191
192
if (resultW == 0.0f || hadW)
193
{
194
result.type = Constant::Type_Vector;
195
result.valueVector[0] = la.valueVector[0] / ra.valueVector[0];
196
result.valueVector[1] = la.valueVector[1] / ra.valueVector[1];
197
result.valueVector[2] = la.valueVector[2] / ra.valueVector[2];
198
result.valueVector[3] = resultW;
199
}
200
}
201
else if (la.type == Constant::Type_Number && ra.type == Constant::Type_Vector)
202
{
203
bool hadW = ra.valueVector[3] != 0.0f;
204
float resultW = float(la.valueNumber) / ra.valueVector[3];
205
206
if (resultW == 0.0f || hadW)
207
{
208
result.type = Constant::Type_Vector;
209
result.valueVector[0] = float(la.valueNumber) / ra.valueVector[0];
210
result.valueVector[1] = float(la.valueNumber) / ra.valueVector[1];
211
result.valueVector[2] = float(la.valueNumber) / ra.valueVector[2];
212
result.valueVector[3] = resultW;
213
}
214
}
215
else if (la.type == Constant::Type_Vector && ra.type == Constant::Type_Number)
216
{
217
bool hadW = la.valueVector[3] != 0.0f;
218
float resultW = la.valueVector[3] / float(ra.valueNumber);
219
220
if (resultW == 0.0f || hadW)
221
{
222
result.type = Constant::Type_Vector;
223
result.valueVector[0] = la.valueVector[0] / float(ra.valueNumber);
224
result.valueVector[1] = la.valueVector[1] / float(ra.valueNumber);
225
result.valueVector[2] = la.valueVector[2] / float(ra.valueNumber);
226
result.valueVector[3] = resultW;
227
}
228
}
229
break;
230
231
case AstExprBinary::FloorDiv:
232
if (la.type == Constant::Type_Number && ra.type == Constant::Type_Number)
233
{
234
result.type = Constant::Type_Number;
235
result.valueNumber = floor(la.valueNumber / ra.valueNumber);
236
}
237
else if (la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
238
{
239
bool hadW = la.valueVector[3] != 0.0f || ra.valueVector[3] != 0.0f;
240
float resultW = floor(la.valueVector[3] / ra.valueVector[3]);
241
242
if (resultW == 0.0f || hadW)
243
{
244
result.type = Constant::Type_Vector;
245
result.valueVector[0] = floor(la.valueVector[0] / ra.valueVector[0]);
246
result.valueVector[1] = floor(la.valueVector[1] / ra.valueVector[1]);
247
result.valueVector[2] = floor(la.valueVector[2] / ra.valueVector[2]);
248
result.valueVector[3] = resultW;
249
}
250
}
251
else if (la.type == Constant::Type_Number && ra.type == Constant::Type_Vector)
252
{
253
bool hadW = ra.valueVector[3] != 0.0f;
254
float resultW = floor(float(la.valueNumber) / ra.valueVector[3]);
255
256
if (resultW == 0.0f || hadW)
257
{
258
result.type = Constant::Type_Vector;
259
result.valueVector[0] = floor(float(la.valueNumber) / ra.valueVector[0]);
260
result.valueVector[1] = floor(float(la.valueNumber) / ra.valueVector[1]);
261
result.valueVector[2] = floor(float(la.valueNumber) / ra.valueVector[2]);
262
result.valueVector[3] = resultW;
263
}
264
}
265
else if (la.type == Constant::Type_Vector && ra.type == Constant::Type_Number)
266
{
267
bool hadW = la.valueVector[3] != 0.0f;
268
float resultW = floor(la.valueVector[3] / float(ra.valueNumber));
269
270
if (resultW == 0.0f || hadW)
271
{
272
result.type = Constant::Type_Vector;
273
result.valueVector[0] = floor(la.valueVector[0] / float(ra.valueNumber));
274
result.valueVector[1] = floor(la.valueVector[1] / float(ra.valueNumber));
275
result.valueVector[2] = floor(la.valueVector[2] / float(ra.valueNumber));
276
result.valueVector[3] = floor(la.valueVector[3] / float(ra.valueNumber));
277
}
278
}
279
break;
280
281
case AstExprBinary::Mod:
282
if (la.type == Constant::Type_Number && ra.type == Constant::Type_Number)
283
{
284
result.type = Constant::Type_Number;
285
result.valueNumber = la.valueNumber - floor(la.valueNumber / ra.valueNumber) * ra.valueNumber;
286
}
287
break;
288
289
case AstExprBinary::Pow:
290
if (la.type == Constant::Type_Number && ra.type == Constant::Type_Number)
291
{
292
result.type = Constant::Type_Number;
293
result.valueNumber = pow(la.valueNumber, ra.valueNumber);
294
}
295
break;
296
297
case AstExprBinary::Concat:
298
if (la.type == Constant::Type_String && ra.type == Constant::Type_String &&
299
(!FFlag::LuauCompileFoldStringLimit || la.stringLength + ra.stringLength <= kConstantFoldStringLimit))
300
{
301
result.type = Constant::Type_String;
302
result.stringLength = la.stringLength + ra.stringLength;
303
if (la.stringLength == 0)
304
result.valueString = ra.valueString;
305
else if (ra.stringLength == 0)
306
result.valueString = la.valueString;
307
else
308
{
309
std::string tmp;
310
tmp.reserve(result.stringLength + 1);
311
tmp.append(la.valueString, la.stringLength);
312
tmp.append(ra.valueString, ra.stringLength);
313
AstName name = stringTable.getOrAdd(tmp.c_str(), result.stringLength);
314
result.valueString = name.value;
315
}
316
}
317
break;
318
319
case AstExprBinary::CompareNe:
320
if (la.type != Constant::Type_Unknown && ra.type != Constant::Type_Unknown)
321
{
322
result.type = Constant::Type_Boolean;
323
result.valueBoolean = !constantsEqual(la, ra);
324
}
325
break;
326
327
case AstExprBinary::CompareEq:
328
if (la.type != Constant::Type_Unknown && ra.type != Constant::Type_Unknown)
329
{
330
result.type = Constant::Type_Boolean;
331
result.valueBoolean = constantsEqual(la, ra);
332
}
333
break;
334
335
case AstExprBinary::CompareLt:
336
if (la.type == Constant::Type_Number && ra.type == Constant::Type_Number)
337
{
338
result.type = Constant::Type_Boolean;
339
result.valueBoolean = la.valueNumber < ra.valueNumber;
340
}
341
break;
342
343
case AstExprBinary::CompareLe:
344
if (la.type == Constant::Type_Number && ra.type == Constant::Type_Number)
345
{
346
result.type = Constant::Type_Boolean;
347
result.valueBoolean = la.valueNumber <= ra.valueNumber;
348
}
349
break;
350
351
case AstExprBinary::CompareGt:
352
if (la.type == Constant::Type_Number && ra.type == Constant::Type_Number)
353
{
354
result.type = Constant::Type_Boolean;
355
result.valueBoolean = la.valueNumber > ra.valueNumber;
356
}
357
break;
358
359
case AstExprBinary::CompareGe:
360
if (la.type == Constant::Type_Number && ra.type == Constant::Type_Number)
361
{
362
result.type = Constant::Type_Boolean;
363
result.valueBoolean = la.valueNumber >= ra.valueNumber;
364
}
365
break;
366
367
case AstExprBinary::And:
368
if (la.type != Constant::Type_Unknown)
369
{
370
result = la.isTruthful() ? ra : la;
371
}
372
break;
373
374
case AstExprBinary::Or:
375
if (la.type != Constant::Type_Unknown)
376
{
377
result = la.isTruthful() ? la : ra;
378
}
379
break;
380
381
default:
382
LUAU_ASSERT(!"Unexpected binary operation");
383
}
384
}
385
386
static void foldInterpString(Constant& result, AstExprInterpString* expr, DenseHashMap<AstExpr*, Constant>& constants, AstNameTable& stringTable)
387
{
388
LUAU_ASSERT(expr->strings.size == expr->expressions.size + 1);
389
size_t resultLength = 0;
390
for (size_t index = 0; index < expr->strings.size; ++index)
391
{
392
resultLength += expr->strings.data[index].size;
393
if (index < expr->expressions.size)
394
{
395
const Constant* c = constants.find(expr->expressions.data[index]);
396
LUAU_ASSERT(c != nullptr && c->type == Constant::Type::Type_String);
397
resultLength += c->stringLength;
398
}
399
}
400
401
if (FFlag::LuauCompileFoldStringLimit && resultLength > kConstantFoldStringLimit)
402
return;
403
404
result.type = Constant::Type_String;
405
result.stringLength = unsigned(resultLength);
406
407
if (resultLength == 0)
408
{
409
result.valueString = "";
410
return;
411
}
412
413
std::string tmp;
414
tmp.reserve(resultLength);
415
416
for (size_t index = 0; index < expr->strings.size; ++index)
417
{
418
AstArray<char> string = expr->strings.data[index];
419
tmp.append(string.data, string.size);
420
if (index < expr->expressions.size)
421
{
422
const Constant* c = constants.find(expr->expressions.data[index]);
423
tmp.append(c->valueString, c->stringLength);
424
}
425
}
426
result.type = Constant::Type_String;
427
result.stringLength = unsigned(resultLength);
428
AstName name = stringTable.getOrAdd(tmp.c_str(), resultLength);
429
result.valueString = name.value;
430
}
431
432
struct ConstantVisitor : AstVisitor
433
{
434
DenseHashMap<AstExpr*, Constant>& constants;
435
DenseHashMap<AstLocal*, Variable>& variables;
436
DenseHashMap<AstLocal*, Constant>& locals;
437
438
const DenseHashMap<AstExprCall*, int>* builtins;
439
bool foldLibraryK = false;
440
LibraryMemberConstantCallback libraryMemberConstantCb;
441
AstNameTable& stringTable;
442
443
bool wasEmpty = false;
444
445
std::vector<Constant> builtinArgs;
446
447
ConstantVisitor(
448
DenseHashMap<AstExpr*, Constant>& constants,
449
DenseHashMap<AstLocal*, Variable>& variables,
450
DenseHashMap<AstLocal*, Constant>& locals,
451
const DenseHashMap<AstExprCall*, int>* builtins,
452
bool foldLibraryK,
453
LibraryMemberConstantCallback libraryMemberConstantCb,
454
AstNameTable& stringTable
455
)
456
: constants(constants)
457
, variables(variables)
458
, locals(locals)
459
, builtins(builtins)
460
, foldLibraryK(foldLibraryK)
461
, libraryMemberConstantCb(libraryMemberConstantCb)
462
, stringTable(stringTable)
463
{
464
// since we do a single pass over the tree, if the initial state was empty we don't need to clear out old entries
465
wasEmpty = constants.empty() && locals.empty();
466
}
467
468
Constant analyze(AstExpr* node)
469
{
470
Constant result;
471
result.type = Constant::Type_Unknown;
472
473
if (AstExprGroup* expr = node->as<AstExprGroup>())
474
{
475
result = analyze(expr->expr);
476
}
477
else if (node->is<AstExprConstantNil>())
478
{
479
result.type = Constant::Type_Nil;
480
}
481
else if (AstExprConstantBool* expr = node->as<AstExprConstantBool>())
482
{
483
result.type = Constant::Type_Boolean;
484
result.valueBoolean = expr->value;
485
}
486
else if (AstExprConstantNumber* expr = node->as<AstExprConstantNumber>())
487
{
488
result.type = Constant::Type_Number;
489
result.valueNumber = expr->value;
490
}
491
else if (AstExprConstantInteger* expr = node->as<AstExprConstantInteger>())
492
{
493
result.type = Constant::Type_Integer;
494
result.valueInteger64 = expr->value;
495
}
496
else if (AstExprConstantString* expr = node->as<AstExprConstantString>())
497
{
498
result.type = Constant::Type_String;
499
result.valueString = expr->value.data;
500
result.stringLength = unsigned(expr->value.size);
501
}
502
else if (AstExprLocal* expr = node->as<AstExprLocal>())
503
{
504
const Constant* l = locals.find(expr->local);
505
506
if (l)
507
result = *l;
508
}
509
else if (node->is<AstExprGlobal>())
510
{
511
// nope
512
}
513
else if (node->is<AstExprVarargs>())
514
{
515
// nope
516
}
517
else if (AstExprCall* expr = node->as<AstExprCall>())
518
{
519
analyze(expr->func);
520
521
const int* bfid = builtins ? builtins->find(expr) : nullptr;
522
523
if (bfid && *bfid != LBF_NONE)
524
{
525
// since recursive calls to analyze() may reuse the vector we need to be careful and preserve existing contents
526
size_t offset = builtinArgs.size();
527
bool canFold = true;
528
529
builtinArgs.reserve(offset + expr->args.size);
530
531
for (size_t i = 0; i < expr->args.size; ++i)
532
{
533
Constant ac = analyze(expr->args.data[i]);
534
535
if (ac.type == Constant::Type_Unknown)
536
canFold = false;
537
else
538
builtinArgs.push_back(ac);
539
}
540
541
if (canFold)
542
{
543
LUAU_ASSERT(builtinArgs.size() == offset + expr->args.size);
544
result = foldBuiltin(stringTable, *bfid, builtinArgs.data() + offset, expr->args.size);
545
}
546
547
builtinArgs.resize(offset);
548
}
549
else
550
{
551
for (size_t i = 0; i < expr->args.size; ++i)
552
analyze(expr->args.data[i]);
553
}
554
}
555
else if (AstExprIndexName* expr = node->as<AstExprIndexName>())
556
{
557
Constant value = analyze(expr->expr);
558
559
if (value.type == Constant::Type_Vector)
560
{
561
if (expr->index == "x" || expr->index == "X")
562
{
563
result.type = Constant::Type_Number;
564
result.valueNumber = value.valueVector[0];
565
}
566
else if (expr->index == "y" || expr->index == "Y")
567
{
568
result.type = Constant::Type_Number;
569
result.valueNumber = value.valueVector[1];
570
}
571
else if (expr->index == "z" || expr->index == "Z")
572
{
573
result.type = Constant::Type_Number;
574
result.valueNumber = value.valueVector[2];
575
}
576
577
// Do not handle 'w' component because it isn't known if the runtime will be configured in 3-wide or 4-wide mode
578
// In 3-wide, access to 'w' will call unspecified metamethod or fail
579
}
580
else if (foldLibraryK)
581
{
582
if (AstExprGlobal* eg = expr->expr->as<AstExprGlobal>())
583
{
584
if (eg->name == "math")
585
result = foldBuiltinMath(expr->index);
586
587
// if we have a custom handler and the constant hasn't been resolved
588
if (libraryMemberConstantCb && result.type == Constant::Type_Unknown)
589
libraryMemberConstantCb(eg->name.value, expr->index.value, reinterpret_cast<Luau::CompileConstant*>(&result));
590
}
591
}
592
}
593
else if (AstExprIndexExpr* expr = node->as<AstExprIndexExpr>())
594
{
595
analyze(expr->expr);
596
analyze(expr->index);
597
}
598
else if (AstExprFunction* expr = node->as<AstExprFunction>())
599
{
600
// this is necessary to propagate constant information in all child functions
601
expr->body->visit(this);
602
}
603
else if (AstExprTable* expr = node->as<AstExprTable>())
604
{
605
for (size_t i = 0; i < expr->items.size; ++i)
606
{
607
const AstExprTable::Item& item = expr->items.data[i];
608
609
if (item.key)
610
analyze(item.key);
611
612
analyze(item.value);
613
}
614
}
615
else if (AstExprUnary* expr = node->as<AstExprUnary>())
616
{
617
Constant arg = analyze(expr->expr);
618
619
if (arg.type != Constant::Type_Unknown)
620
foldUnary(result, expr->op, arg);
621
}
622
else if (AstExprBinary* expr = node->as<AstExprBinary>())
623
{
624
Constant la = analyze(expr->left);
625
Constant ra = analyze(expr->right);
626
627
// note: ra doesn't need to be constant to fold and/or
628
if (la.type != Constant::Type_Unknown)
629
foldBinary(result, expr->op, la, ra, stringTable);
630
}
631
else if (AstExprTypeAssertion* expr = node->as<AstExprTypeAssertion>())
632
{
633
Constant arg = analyze(expr->expr);
634
635
result = arg;
636
}
637
else if (AstExprIfElse* expr = node->as<AstExprIfElse>())
638
{
639
Constant cond = analyze(expr->condition);
640
Constant trueExpr = analyze(expr->trueExpr);
641
Constant falseExpr = analyze(expr->falseExpr);
642
643
if (cond.type != Constant::Type_Unknown)
644
result = cond.isTruthful() ? trueExpr : falseExpr;
645
}
646
else if (AstExprInterpString* expr = node->as<AstExprInterpString>())
647
{
648
bool onlyConstantSubExpr = true;
649
for (AstExpr* expression : expr->expressions)
650
if (analyze(expression).type != Constant::Type_String)
651
onlyConstantSubExpr = false;
652
653
if (onlyConstantSubExpr)
654
foldInterpString(result, expr, constants, stringTable);
655
}
656
else if (AstExprInstantiate* expr = node->as<AstExprInstantiate>())
657
{
658
result = analyze(expr->expr);
659
}
660
else
661
{
662
LUAU_ASSERT(!"Unknown expression type");
663
}
664
665
recordConstant(constants, node, result);
666
667
return result;
668
}
669
670
template<typename T>
671
void recordConstant(DenseHashMap<T, Constant>& map, T key, const Constant& value)
672
{
673
if (value.type != Constant::Type_Unknown)
674
map[key] = value;
675
else if (wasEmpty)
676
;
677
else if (Constant* old = map.find(key))
678
old->type = Constant::Type_Unknown;
679
}
680
681
void recordValue(AstLocal* local, const Constant& value)
682
{
683
// note: we rely on trackValues to have been run before us
684
Variable* v = variables.find(local);
685
LUAU_ASSERT(v);
686
687
if (!v->written)
688
{
689
v->constant = (value.type != Constant::Type_Unknown);
690
recordConstant(locals, local, value);
691
}
692
}
693
694
bool visit(AstExpr* node) override
695
{
696
// note: we short-circuit the visitor traversal through any expression trees by returning false
697
// recursive traversal is happening inside analyze() which makes it easier to get the resulting value of the subexpression
698
analyze(node);
699
700
return false;
701
}
702
703
bool visit(AstStatLocal* node) override
704
{
705
// all values that align wrt indexing are simple - we just match them 1-1
706
for (size_t i = 0; i < node->vars.size && i < node->values.size; ++i)
707
{
708
Constant arg = analyze(node->values.data[i]);
709
710
recordValue(node->vars.data[i], arg);
711
}
712
713
if (node->vars.size > node->values.size)
714
{
715
// if we have trailing variables, then depending on whether the last value is capable of returning multiple values
716
// (aka call or varargs), we either don't know anything about these vars, or we know they're nil
717
AstExpr* last = node->values.size ? node->values.data[node->values.size - 1] : nullptr;
718
bool multRet = last && (last->is<AstExprCall>() || last->is<AstExprVarargs>());
719
720
if (!multRet)
721
{
722
for (size_t i = node->values.size; i < node->vars.size; ++i)
723
{
724
Constant nil = {Constant::Type_Nil};
725
recordValue(node->vars.data[i], nil);
726
}
727
}
728
}
729
else
730
{
731
// we can have more values than variables; in this case we still need to analyze them to make sure we do constant propagation inside
732
// them
733
for (size_t i = node->vars.size; i < node->values.size; ++i)
734
analyze(node->values.data[i]);
735
}
736
737
return false;
738
}
739
};
740
741
void foldConstants(
742
DenseHashMap<AstExpr*, Constant>& constants,
743
DenseHashMap<AstLocal*, Variable>& variables,
744
DenseHashMap<AstLocal*, Constant>& locals,
745
const DenseHashMap<AstExprCall*, int>* builtins,
746
bool foldLibraryK,
747
LibraryMemberConstantCallback libraryMemberConstantCb,
748
AstNode* root,
749
AstNameTable& stringTable
750
)
751
{
752
ConstantVisitor visitor{constants, variables, locals, builtins, foldLibraryK, libraryMemberConstantCb, stringTable};
753
root->visit(&visitor);
754
}
755
756
} // namespace Compile
757
} // namespace Luau
758
759