Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Compiler/src/BuiltinFolding.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 "BuiltinFolding.h"
3
4
#include "Luau/Bytecode.h"
5
#include "Luau/Lexer.h"
6
7
#include <array>
8
#include <limits>
9
#include <math.h>
10
11
LUAU_FASTFLAGVARIABLE(LuauCompileNewMathConstantsFolded)
12
13
namespace Luau
14
{
15
namespace Compile
16
{
17
18
const double kPi = 3.14159265358979323846;
19
const double kRadDeg = kPi / 180.0;
20
const double kNan = std::numeric_limits<double>::quiet_NaN();
21
const double kE = 2.71828182845904523536;
22
const double kPhi = 1.61803398874989484820;
23
const double kSqrt2 = 1.41421356237309504880;
24
const double kTau = 6.28318530717958647692;
25
26
constexpr size_t kStringCharFoldLimit = 128;
27
28
static Constant cvar()
29
{
30
return Constant();
31
}
32
33
static Constant cbool(bool v)
34
{
35
Constant res = {Constant::Type_Boolean};
36
res.valueBoolean = v;
37
return res;
38
}
39
40
static Constant cnum(double v)
41
{
42
Constant res = {Constant::Type_Number};
43
res.valueNumber = v;
44
return res;
45
}
46
47
static Constant cvector(double x, double y, double z, double w)
48
{
49
Constant res = {Constant::Type_Vector};
50
res.valueVector[0] = (float)x;
51
res.valueVector[1] = (float)y;
52
res.valueVector[2] = (float)z;
53
res.valueVector[3] = (float)w;
54
return res;
55
}
56
57
static Constant cstring(const char* v)
58
{
59
Constant res = {Constant::Type_String};
60
res.stringLength = unsigned(strlen(v));
61
res.valueString = v;
62
return res;
63
}
64
65
static Constant cstring(const char* v, size_t len)
66
{
67
Constant res = {Constant::Type_String};
68
res.stringLength = unsigned(len);
69
res.valueString = v;
70
return res;
71
}
72
73
static Constant ctype(const Constant& c)
74
{
75
LUAU_ASSERT(c.type != Constant::Type_Unknown);
76
77
switch (c.type)
78
{
79
case Constant::Type_Nil:
80
return cstring("nil");
81
82
case Constant::Type_Boolean:
83
return cstring("boolean");
84
85
case Constant::Type_Number:
86
return cstring("number");
87
88
case Constant::Type_Integer:
89
return cstring("integer");
90
91
case Constant::Type_Vector:
92
return cstring("vector");
93
94
case Constant::Type_String:
95
return cstring("string");
96
97
default:
98
LUAU_ASSERT(!"Unsupported constant type");
99
return cvar();
100
}
101
}
102
103
static Constant ctypeof(const Constant& c)
104
{
105
LUAU_ASSERT(c.type != Constant::Type_Unknown);
106
107
switch (c.type)
108
{
109
case Constant::Type_Nil:
110
return cstring("nil");
111
112
case Constant::Type_Boolean:
113
return cstring("boolean");
114
115
case Constant::Type_Number:
116
return cstring("number");
117
118
case Constant::Type_Integer:
119
return cstring("integer");
120
121
case Constant::Type_Vector:
122
return cvar(); // vector can have a custom typeof name at runtime
123
124
case Constant::Type_String:
125
return cstring("string");
126
127
default:
128
LUAU_ASSERT(!"Unsupported constant type");
129
return cvar();
130
}
131
}
132
133
static uint32_t bit32(double v)
134
{
135
// convert through signed 64-bit integer to match runtime behavior and gracefully truncate negative integers
136
return uint32_t(int64_t(v));
137
}
138
139
Constant foldBuiltin(AstNameTable& stringTable, int bfid, const Constant* args, size_t count)
140
{
141
switch (bfid)
142
{
143
case LBF_MATH_ABS:
144
if (count == 1 && args[0].type == Constant::Type_Number)
145
return cnum(fabs(args[0].valueNumber));
146
break;
147
148
case LBF_MATH_ACOS:
149
if (count == 1 && args[0].type == Constant::Type_Number)
150
return cnum(acos(args[0].valueNumber));
151
break;
152
153
case LBF_MATH_ASIN:
154
if (count == 1 && args[0].type == Constant::Type_Number)
155
return cnum(asin(args[0].valueNumber));
156
break;
157
158
case LBF_MATH_ATAN2:
159
if (count == 2 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number)
160
return cnum(atan2(args[0].valueNumber, args[1].valueNumber));
161
break;
162
163
case LBF_MATH_ATAN:
164
if (count == 1 && args[0].type == Constant::Type_Number)
165
return cnum(atan(args[0].valueNumber));
166
break;
167
168
case LBF_MATH_CEIL:
169
if (count == 1 && args[0].type == Constant::Type_Number)
170
return cnum(ceil(args[0].valueNumber));
171
break;
172
173
case LBF_MATH_COSH:
174
if (count == 1 && args[0].type == Constant::Type_Number)
175
return cnum(cosh(args[0].valueNumber));
176
break;
177
178
case LBF_MATH_COS:
179
if (count == 1 && args[0].type == Constant::Type_Number)
180
return cnum(cos(args[0].valueNumber));
181
break;
182
183
case LBF_MATH_DEG:
184
if (count == 1 && args[0].type == Constant::Type_Number)
185
return cnum(args[0].valueNumber / kRadDeg);
186
break;
187
188
case LBF_MATH_EXP:
189
if (count == 1 && args[0].type == Constant::Type_Number)
190
return cnum(exp(args[0].valueNumber));
191
break;
192
193
case LBF_MATH_FLOOR:
194
if (count == 1 && args[0].type == Constant::Type_Number)
195
return cnum(floor(args[0].valueNumber));
196
break;
197
198
case LBF_MATH_FMOD:
199
if (count == 2 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number)
200
return cnum(fmod(args[0].valueNumber, args[1].valueNumber));
201
break;
202
203
// Note: FREXP isn't folded since it returns multiple values
204
205
case LBF_MATH_LDEXP:
206
if (count == 2 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number)
207
return cnum(ldexp(args[0].valueNumber, int(args[1].valueNumber)));
208
break;
209
210
case LBF_MATH_LOG10:
211
if (count == 1 && args[0].type == Constant::Type_Number)
212
return cnum(log10(args[0].valueNumber));
213
break;
214
215
case LBF_MATH_LOG:
216
if (count == 1 && args[0].type == Constant::Type_Number)
217
return cnum(log(args[0].valueNumber));
218
else if (count == 2 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number)
219
{
220
if (args[1].valueNumber == 2.0)
221
return cnum(log2(args[0].valueNumber));
222
else if (args[1].valueNumber == 10.0)
223
return cnum(log10(args[0].valueNumber));
224
else
225
return cnum(log(args[0].valueNumber) / log(args[1].valueNumber));
226
}
227
break;
228
229
case LBF_MATH_MAX:
230
if (count >= 1 && args[0].type == Constant::Type_Number)
231
{
232
double r = args[0].valueNumber;
233
234
for (size_t i = 1; i < count; ++i)
235
{
236
if (args[i].type != Constant::Type_Number)
237
return cvar();
238
239
double a = args[i].valueNumber;
240
241
r = (a > r) ? a : r;
242
}
243
244
return cnum(r);
245
}
246
break;
247
248
case LBF_MATH_MIN:
249
if (count >= 1 && args[0].type == Constant::Type_Number)
250
{
251
double r = args[0].valueNumber;
252
253
for (size_t i = 1; i < count; ++i)
254
{
255
if (args[i].type != Constant::Type_Number)
256
return cvar();
257
258
double a = args[i].valueNumber;
259
260
r = (a < r) ? a : r;
261
}
262
263
return cnum(r);
264
}
265
break;
266
267
// Note: MODF isn't folded since it returns multiple values
268
269
case LBF_MATH_POW:
270
if (count == 2 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number)
271
return cnum(pow(args[0].valueNumber, args[1].valueNumber));
272
break;
273
274
case LBF_MATH_RAD:
275
if (count == 1 && args[0].type == Constant::Type_Number)
276
return cnum(args[0].valueNumber * kRadDeg);
277
break;
278
279
case LBF_MATH_SINH:
280
if (count == 1 && args[0].type == Constant::Type_Number)
281
return cnum(sinh(args[0].valueNumber));
282
break;
283
284
case LBF_MATH_SIN:
285
if (count == 1 && args[0].type == Constant::Type_Number)
286
return cnum(sin(args[0].valueNumber));
287
break;
288
289
case LBF_MATH_SQRT:
290
if (count == 1 && args[0].type == Constant::Type_Number)
291
return cnum(sqrt(args[0].valueNumber));
292
break;
293
294
case LBF_MATH_TANH:
295
if (count == 1 && args[0].type == Constant::Type_Number)
296
return cnum(tanh(args[0].valueNumber));
297
break;
298
299
case LBF_MATH_TAN:
300
if (count == 1 && args[0].type == Constant::Type_Number)
301
return cnum(tan(args[0].valueNumber));
302
break;
303
304
case LBF_BIT32_ARSHIFT:
305
if (count == 2 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number)
306
{
307
uint32_t u = bit32(args[0].valueNumber);
308
int s = int(args[1].valueNumber);
309
310
if (unsigned(s) < 32)
311
return cnum(double(uint32_t(int32_t(u) >> s)));
312
}
313
break;
314
315
case LBF_BIT32_BAND:
316
if (count >= 1 && args[0].type == Constant::Type_Number)
317
{
318
uint32_t r = bit32(args[0].valueNumber);
319
320
for (size_t i = 1; i < count; ++i)
321
{
322
if (args[i].type != Constant::Type_Number)
323
return cvar();
324
325
r &= bit32(args[i].valueNumber);
326
}
327
328
return cnum(double(r));
329
}
330
break;
331
332
case LBF_BIT32_BNOT:
333
if (count == 1 && args[0].type == Constant::Type_Number)
334
return cnum(double(uint32_t(~bit32(args[0].valueNumber))));
335
break;
336
337
case LBF_BIT32_BOR:
338
if (count >= 1 && args[0].type == Constant::Type_Number)
339
{
340
uint32_t r = bit32(args[0].valueNumber);
341
342
for (size_t i = 1; i < count; ++i)
343
{
344
if (args[i].type != Constant::Type_Number)
345
return cvar();
346
347
r |= bit32(args[i].valueNumber);
348
}
349
350
return cnum(double(r));
351
}
352
break;
353
354
case LBF_BIT32_BXOR:
355
if (count >= 1 && args[0].type == Constant::Type_Number)
356
{
357
uint32_t r = bit32(args[0].valueNumber);
358
359
for (size_t i = 1; i < count; ++i)
360
{
361
if (args[i].type != Constant::Type_Number)
362
return cvar();
363
364
r ^= bit32(args[i].valueNumber);
365
}
366
367
return cnum(double(r));
368
}
369
break;
370
371
case LBF_BIT32_BTEST:
372
if (count >= 1 && args[0].type == Constant::Type_Number)
373
{
374
uint32_t r = bit32(args[0].valueNumber);
375
376
for (size_t i = 1; i < count; ++i)
377
{
378
if (args[i].type != Constant::Type_Number)
379
return cvar();
380
381
r &= bit32(args[i].valueNumber);
382
}
383
384
return cbool(r != 0);
385
}
386
break;
387
388
case LBF_BIT32_EXTRACT:
389
if (count >= 2 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number &&
390
(count == 2 || args[2].type == Constant::Type_Number))
391
{
392
uint32_t u = bit32(args[0].valueNumber);
393
int f = int(args[1].valueNumber);
394
int w = count == 2 ? 1 : int(args[2].valueNumber);
395
396
if (f >= 0 && w > 0 && f + w <= 32)
397
{
398
uint32_t m = ~(0xfffffffeu << (w - 1));
399
400
return cnum(double((u >> f) & m));
401
}
402
}
403
break;
404
405
case LBF_BIT32_LROTATE:
406
if (count == 2 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number)
407
{
408
uint32_t u = bit32(args[0].valueNumber);
409
int s = int(args[1].valueNumber);
410
411
return cnum(double((u << (s & 31)) | (u >> ((32 - s) & 31))));
412
}
413
break;
414
415
case LBF_BIT32_LSHIFT:
416
if (count == 2 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number)
417
{
418
uint32_t u = bit32(args[0].valueNumber);
419
int s = int(args[1].valueNumber);
420
421
if (unsigned(s) < 32)
422
return cnum(double(u << s));
423
}
424
break;
425
426
case LBF_BIT32_REPLACE:
427
if (count >= 3 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number && args[2].type == Constant::Type_Number &&
428
(count == 3 || args[3].type == Constant::Type_Number))
429
{
430
uint32_t n = bit32(args[0].valueNumber);
431
uint32_t v = bit32(args[1].valueNumber);
432
int f = int(args[2].valueNumber);
433
int w = count == 3 ? 1 : int(args[3].valueNumber);
434
435
if (f >= 0 && w > 0 && f + w <= 32)
436
{
437
uint32_t m = ~(0xfffffffeu << (w - 1));
438
439
return cnum(double((n & ~(m << f)) | ((v & m) << f)));
440
}
441
}
442
break;
443
444
case LBF_BIT32_RROTATE:
445
if (count == 2 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number)
446
{
447
uint32_t u = bit32(args[0].valueNumber);
448
int s = int(args[1].valueNumber);
449
450
return cnum(double((u >> (s & 31)) | (u << ((32 - s) & 31))));
451
}
452
break;
453
454
case LBF_BIT32_RSHIFT:
455
if (count == 2 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number)
456
{
457
uint32_t u = bit32(args[0].valueNumber);
458
int s = int(args[1].valueNumber);
459
460
if (unsigned(s) < 32)
461
return cnum(double(u >> s));
462
}
463
break;
464
465
case LBF_TYPE:
466
if (count == 1 && args[0].type != Constant::Type_Unknown)
467
return ctype(args[0]);
468
break;
469
470
case LBF_STRING_BYTE:
471
if (count == 1 && args[0].type == Constant::Type_String)
472
{
473
if (args[0].stringLength > 0)
474
return cnum(double(uint8_t(args[0].valueString[0])));
475
}
476
else if (count == 2 && args[0].type == Constant::Type_String && args[1].type == Constant::Type_Number)
477
{
478
int i = int(args[1].valueNumber);
479
480
if (i > 0 && unsigned(i) <= args[0].stringLength)
481
return cnum(double(uint8_t(args[0].valueString[i - 1])));
482
}
483
break;
484
485
case LBF_STRING_CHAR:
486
if (count < kStringCharFoldLimit)
487
{
488
std::array<char, kStringCharFoldLimit> buf{};
489
490
for (size_t i = 0; i < count; i++)
491
{
492
if (args[i].type != Constant::Type_Number)
493
return cvar();
494
495
int ch = int(args[i].valueNumber);
496
497
if ((unsigned char)(ch) != ch)
498
return cvar();
499
500
buf[i] = ch;
501
}
502
503
if (count == 0)
504
return cstring("");
505
506
AstName name = stringTable.getOrAdd(buf.data(), count);
507
return cstring(name.value, count);
508
}
509
break;
510
511
case LBF_STRING_LEN:
512
if (count == 1 && args[0].type == Constant::Type_String)
513
return cnum(double(args[0].stringLength));
514
break;
515
516
case LBF_TYPEOF:
517
if (count == 1 && args[0].type != Constant::Type_Unknown)
518
return ctypeof(args[0]);
519
break;
520
521
case LBF_STRING_SUB:
522
if (count >= 2 && args[0].type == Constant::Type_String && args[1].type == Constant::Type_Number)
523
{
524
if (count >= 3 && args[2].type != Constant::Type_Number)
525
return cvar();
526
527
const char* str = args[0].valueString;
528
unsigned len = args[0].stringLength;
529
530
int start = int(args[1].valueNumber);
531
int end = count >= 3 ? int(args[2].valueNumber) : int(len);
532
533
// Relative string position: negative means back from end
534
if (start < 0)
535
start += int(len) + 1;
536
if (end < 0)
537
end += int(len) + 1;
538
539
// If end is before the start of the string, substring is empty
540
if (end < 1)
541
return cstring("");
542
543
// Start clamped to the start of the string, end is clamped to the end
544
start = start < 1 ? 1 : start;
545
end = end > int(len) ? int(len) : end;
546
547
if (start <= end)
548
{
549
AstName name = stringTable.getOrAdd(str + (start - 1), end - start + 1);
550
return cstring(name.value, end - start + 1);
551
}
552
553
return cstring("");
554
}
555
break;
556
557
case LBF_MATH_CLAMP:
558
if (count == 3 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number && args[2].type == Constant::Type_Number)
559
{
560
double min = args[1].valueNumber;
561
double max = args[2].valueNumber;
562
563
if (min <= max)
564
{
565
double v = args[0].valueNumber;
566
v = v < min ? min : v;
567
v = v > max ? max : v;
568
569
return cnum(v);
570
}
571
}
572
break;
573
574
case LBF_MATH_SIGN:
575
if (count == 1 && args[0].type == Constant::Type_Number)
576
{
577
double v = args[0].valueNumber;
578
579
return cnum(v > 0.0 ? 1.0 : v < 0.0 ? -1.0 : 0.0);
580
}
581
break;
582
583
case LBF_MATH_ROUND:
584
if (count == 1 && args[0].type == Constant::Type_Number)
585
return cnum(round(args[0].valueNumber));
586
break;
587
588
case LBF_VECTOR:
589
if (count >= 2 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number)
590
{
591
if (count == 2)
592
return cvector(args[0].valueNumber, args[1].valueNumber, 0.0, 0.0);
593
else if (count == 3 && args[2].type == Constant::Type_Number)
594
return cvector(args[0].valueNumber, args[1].valueNumber, args[2].valueNumber, 0.0);
595
else if (count == 4 && args[2].type == Constant::Type_Number && args[3].type == Constant::Type_Number)
596
return cvector(args[0].valueNumber, args[1].valueNumber, args[2].valueNumber, args[3].valueNumber);
597
}
598
break;
599
600
case LBF_MATH_LERP:
601
if (count == 3 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number && args[2].type == Constant::Type_Number)
602
{
603
double a = args[0].valueNumber;
604
double b = args[1].valueNumber;
605
double t = args[2].valueNumber;
606
607
double v = (t == 1.0) ? b : a + (b - a) * t;
608
return cnum(v);
609
}
610
break;
611
612
case LBF_MATH_ISNAN:
613
if (count == 1 && args[0].type == Constant::Type_Number)
614
{
615
double x = args[0].valueNumber;
616
617
return cbool(isnan(x));
618
}
619
break;
620
621
case LBF_MATH_ISINF:
622
if (count == 1 && args[0].type == Constant::Type_Number)
623
{
624
double x = args[0].valueNumber;
625
626
return cbool(isinf(x));
627
}
628
break;
629
630
case LBF_MATH_ISFINITE:
631
if (count == 1 && args[0].type == Constant::Type_Number)
632
{
633
double x = args[0].valueNumber;
634
635
return cbool(isfinite(x));
636
}
637
break;
638
}
639
640
return cvar();
641
}
642
643
Constant foldBuiltinMath(AstName index)
644
{
645
if (index == "pi")
646
return cnum(kPi);
647
648
if (index == "huge")
649
return cnum(HUGE_VAL);
650
651
if (FFlag::LuauCompileNewMathConstantsFolded)
652
{
653
if (index == "nan")
654
return cnum(kNan);
655
656
if (index == "e")
657
return cnum(kE);
658
659
if (index == "phi")
660
return cnum(kPhi);
661
662
if (index == "sqrt2")
663
return cnum(kSqrt2);
664
665
if (index == "tau")
666
return cnum(kTau);
667
}
668
669
return cvar();
670
}
671
672
} // namespace Compile
673
} // namespace Luau
674
675