Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/CodeGen/src/IrTranslateBuiltins.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 "IrTranslateBuiltins.h"
3
4
#include "Luau/Bytecode.h"
5
#include "Luau/IrBuilder.h"
6
7
#include "lstate.h"
8
9
#include <math.h>
10
11
LUAU_FASTFLAG(LuauCodegenBufferRangeMerge4)
12
LUAU_FASTFLAGVARIABLE(LuauCodegenBufNoDefTag)
13
14
// TODO: when nresults is less than our actual result count, we can skip computing/writing unused results
15
16
static const int kMinMaxUnrolledParams = 5;
17
static const int kBit32BinaryOpUnrolledParams = 5;
18
19
namespace Luau
20
{
21
namespace CodeGen
22
{
23
24
static void builtinCheckDouble(IrBuilder& build, IrOp arg, int pcpos)
25
{
26
if (arg.kind == IrOpKind::Constant)
27
CODEGEN_ASSERT(build.function.constOp(arg).kind == IrConstKind::Double);
28
else
29
build.loadAndCheckTag(arg, LUA_TNUMBER, build.vmExit(pcpos));
30
}
31
32
static IrOp builtinLoadDouble(IrBuilder& build, IrOp arg)
33
{
34
if (arg.kind == IrOpKind::Constant)
35
return arg;
36
37
return build.inst(IrCmd::LOAD_DOUBLE, arg);
38
}
39
40
// Wrapper code for all builtins with a fixed signature and manual assembly lowering of the body
41
42
static BuiltinImplResult translateBuiltinNumberToNumberLibm(
43
IrBuilder& build,
44
LuauBuiltinFunction bfid,
45
int nparams,
46
int ra,
47
int arg,
48
int nresults,
49
int pcpos
50
)
51
{
52
if (nparams < 1 || nresults > 1)
53
return {BuiltinImplType::None, -1};
54
55
builtinCheckDouble(build, build.vmReg(arg), pcpos);
56
IrOp va = builtinLoadDouble(build, build.vmReg(arg));
57
58
IrOp res = build.inst(IrCmd::INVOKE_LIBM, build.constUint(bfid), va);
59
60
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), res);
61
62
if (ra != arg)
63
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
64
65
return {BuiltinImplType::Full, 1};
66
}
67
68
static BuiltinImplResult translateBuiltin2NumberToNumberLibm(
69
IrBuilder& build,
70
LuauBuiltinFunction bfid,
71
int nparams,
72
int ra,
73
int arg,
74
IrOp args,
75
int nresults,
76
int pcpos
77
)
78
{
79
if (nparams < 2 || nresults > 1)
80
return {BuiltinImplType::None, -1};
81
82
builtinCheckDouble(build, build.vmReg(arg), pcpos);
83
builtinCheckDouble(build, args, pcpos);
84
85
IrOp va = builtinLoadDouble(build, build.vmReg(arg));
86
IrOp vb = builtinLoadDouble(build, args);
87
88
if (bfid == LBF_MATH_LDEXP)
89
vb = build.inst(IrCmd::NUM_TO_INT, vb);
90
91
IrOp res = build.inst(IrCmd::INVOKE_LIBM, build.constUint(bfid), va, vb);
92
93
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), res);
94
95
if (ra != arg)
96
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
97
98
return {BuiltinImplType::Full, 1};
99
}
100
101
// (number, ...) -> (number, number)
102
static BuiltinImplResult translateBuiltinNumberTo2Number(
103
IrBuilder& build,
104
LuauBuiltinFunction bfid,
105
int nparams,
106
int ra,
107
int arg,
108
IrOp args,
109
int nresults,
110
int pcpos
111
)
112
{
113
if (nparams < 1 || nresults > 2)
114
return {BuiltinImplType::None, -1};
115
116
builtinCheckDouble(build, build.vmReg(arg), pcpos);
117
118
build.inst(IrCmd::FASTCALL, build.constUint(bfid), build.vmReg(ra), build.vmReg(arg), build.constInt(nresults == 1 ? 1 : 2));
119
120
return {BuiltinImplType::Full, 2};
121
}
122
123
static BuiltinImplResult translateBuiltinAssert(IrBuilder& build, int nparams, int ra, int arg, IrOp args, int nresults, int pcpos)
124
{
125
if (nparams < 1 || nresults != 0)
126
return {BuiltinImplType::None, -1};
127
128
IrOp tag = build.inst(IrCmd::LOAD_TAG, build.vmReg(arg));
129
130
// We don't know if it's really a boolean at this point, but we will only check this value if it is
131
IrOp value = build.inst(IrCmd::LOAD_INT, build.vmReg(arg));
132
133
build.inst(IrCmd::CHECK_TRUTHY, tag, value, build.vmExit(pcpos));
134
135
return {BuiltinImplType::UsesFallback, 0};
136
}
137
138
static BuiltinImplResult translateBuiltinMathDegRad(IrBuilder& build, IrCmd cmd, int nparams, int ra, int arg, IrOp args, int nresults, int pcpos)
139
{
140
if (nparams < 1 || nresults > 1)
141
return {BuiltinImplType::None, -1};
142
143
builtinCheckDouble(build, build.vmReg(arg), pcpos);
144
145
const double rpd = (3.14159265358979323846 / 180.0);
146
147
IrOp varg = builtinLoadDouble(build, build.vmReg(arg));
148
IrOp value = build.inst(cmd, varg, build.constDouble(rpd));
149
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), value);
150
151
if (ra != arg)
152
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
153
154
return {BuiltinImplType::Full, 1};
155
}
156
157
static BuiltinImplResult translateBuiltinMathLog(IrBuilder& build, int nparams, int ra, int arg, IrOp args, int nresults, int pcpos)
158
{
159
if (nparams < 1 || nresults > 1)
160
return {BuiltinImplType::None, -1};
161
162
int libmId = LBF_MATH_LOG;
163
std::optional<double> denom;
164
165
if (nparams != 1)
166
{
167
std::optional<double> y = build.function.asDoubleOp(args);
168
169
if (!y)
170
return {BuiltinImplType::None, -1};
171
172
if (*y == 2.0)
173
libmId = LBF_IR_MATH_LOG2;
174
else if (*y == 10.0)
175
libmId = LBF_MATH_LOG10;
176
else
177
denom = log(*y);
178
}
179
180
builtinCheckDouble(build, build.vmReg(arg), pcpos);
181
182
IrOp va = builtinLoadDouble(build, build.vmReg(arg));
183
184
IrOp res = build.inst(IrCmd::INVOKE_LIBM, build.constUint(libmId), va);
185
186
if (denom)
187
res = build.inst(IrCmd::DIV_NUM, res, build.constDouble(*denom));
188
189
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), res);
190
191
if (ra != arg)
192
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
193
194
return {BuiltinImplType::Full, 1};
195
}
196
197
static BuiltinImplResult translateBuiltinMathMinMax(
198
IrBuilder& build,
199
IrCmd cmd,
200
int nparams,
201
int ra,
202
int arg,
203
IrOp args,
204
IrOp arg3,
205
int nresults,
206
int pcpos
207
)
208
{
209
if (nparams < 2 || nparams > kMinMaxUnrolledParams || nresults > 1)
210
return {BuiltinImplType::None, -1};
211
212
builtinCheckDouble(build, build.vmReg(arg), pcpos);
213
builtinCheckDouble(build, args, pcpos);
214
215
if (nparams >= 3)
216
builtinCheckDouble(build, arg3, pcpos);
217
218
for (int i = 4; i <= nparams; ++i)
219
builtinCheckDouble(build, build.vmReg(vmRegOp(args) + (i - 2)), pcpos);
220
221
IrOp varg1 = builtinLoadDouble(build, build.vmReg(arg));
222
IrOp varg2 = builtinLoadDouble(build, args);
223
224
IrOp res = build.inst(cmd, varg2, varg1); // Swapped arguments are required for consistency with VM builtins
225
226
if (nparams >= 3)
227
{
228
IrOp arg = builtinLoadDouble(build, arg3);
229
res = build.inst(cmd, arg, res);
230
}
231
232
for (int i = 4; i <= nparams; ++i)
233
{
234
IrOp arg = builtinLoadDouble(build, build.vmReg(vmRegOp(args) + (i - 2)));
235
res = build.inst(cmd, arg, res);
236
}
237
238
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), res);
239
240
if (ra != arg)
241
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
242
243
return {BuiltinImplType::Full, 1};
244
}
245
246
static BuiltinImplResult translateBuiltinMathClamp(
247
IrBuilder& build,
248
int nparams,
249
int ra,
250
int arg,
251
IrOp args,
252
IrOp arg3,
253
int nresults,
254
IrOp fallback,
255
int pcpos
256
)
257
{
258
if (nparams < 3 || nresults > 1)
259
return {BuiltinImplType::None, -1};
260
261
IrOp block = build.block(IrBlockKind::Internal);
262
263
CODEGEN_ASSERT(args.kind == IrOpKind::VmReg);
264
265
builtinCheckDouble(build, build.vmReg(arg), pcpos);
266
builtinCheckDouble(build, args, pcpos);
267
builtinCheckDouble(build, arg3, pcpos);
268
269
IrOp min = builtinLoadDouble(build, args);
270
IrOp max = builtinLoadDouble(build, arg3);
271
272
build.inst(IrCmd::JUMP_CMP_NUM, min, max, build.cond(IrCondition::NotLessEqual), fallback, block);
273
build.beginBlock(block);
274
275
IrOp v = builtinLoadDouble(build, build.vmReg(arg));
276
IrOp r = build.inst(IrCmd::MAX_NUM, min, v);
277
IrOp clamped = build.inst(IrCmd::MIN_NUM, max, r);
278
279
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), clamped);
280
281
if (ra != arg)
282
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
283
284
return {BuiltinImplType::UsesFallback, 1};
285
}
286
287
static BuiltinImplResult translateBuiltinVectorLerp(IrBuilder& build, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, int pcpos)
288
{
289
if (nparams < 3 || nresults > 1)
290
return {BuiltinImplType::None, -1};
291
292
IrOp arg1 = build.vmReg(arg);
293
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
294
build.loadAndCheckTag(args, LUA_TVECTOR, build.vmExit(pcpos));
295
builtinCheckDouble(build, arg3, pcpos);
296
297
IrOp a = build.inst(IrCmd::LOAD_TVALUE, arg1);
298
IrOp b = build.inst(IrCmd::LOAD_TVALUE, args);
299
IrOp t = builtinLoadDouble(build, arg3);
300
301
IrOp tvec = build.inst(IrCmd::FLOAT_TO_VEC, build.inst(IrCmd::NUM_TO_FLOAT, t));
302
IrOp one = build.inst(IrCmd::FLOAT_TO_VEC, build.constDouble(1.0));
303
IrOp diff = build.inst(IrCmd::SUB_VEC, b, a);
304
305
IrOp res = build.inst(IrCmd::MULADD_VEC, diff, tvec, a);
306
IrOp ret = build.inst(IrCmd::SELECT_VEC, res, b, tvec, one);
307
build.inst(IrCmd::STORE_TVALUE, build.vmReg(ra), build.inst(IrCmd::TAG_VECTOR, ret));
308
309
return {BuiltinImplType::Full, 1};
310
}
311
312
static BuiltinImplResult translateBuiltinMathLerp(
313
IrBuilder& build,
314
int nparams,
315
int ra,
316
int arg,
317
IrOp args,
318
IrOp arg3,
319
int nresults,
320
IrOp fallback,
321
int pcpos
322
)
323
{
324
if (nparams < 3 || nresults > 1)
325
return {BuiltinImplType::None, -1};
326
327
builtinCheckDouble(build, build.vmReg(arg), pcpos);
328
builtinCheckDouble(build, args, pcpos);
329
builtinCheckDouble(build, arg3, pcpos);
330
331
IrOp a = builtinLoadDouble(build, build.vmReg(arg));
332
IrOp b = builtinLoadDouble(build, args);
333
IrOp t = builtinLoadDouble(build, arg3);
334
335
IrOp l = build.inst(IrCmd::MULADD_NUM, build.inst(IrCmd::SUB_NUM, b, a), t, a);
336
IrOp r = build.inst(IrCmd::SELECT_NUM, l, b, t, build.constDouble(1.0)); // select on t==1.0
337
338
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), r);
339
340
if (ra != arg)
341
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
342
343
return {BuiltinImplType::Full, 1};
344
}
345
346
static BuiltinImplResult translateBuiltinMathUnary(IrBuilder& build, IrCmd cmd, int nparams, int ra, int arg, int nresults, int pcpos)
347
{
348
if (nparams < 1 || nresults > 1)
349
return {BuiltinImplType::None, -1};
350
351
builtinCheckDouble(build, build.vmReg(arg), pcpos);
352
353
IrOp varg = builtinLoadDouble(build, build.vmReg(arg));
354
IrOp result = build.inst(cmd, varg);
355
356
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), result);
357
358
if (ra != arg)
359
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
360
361
return {BuiltinImplType::Full, 1};
362
}
363
364
static BuiltinImplResult translateBuiltinMathIsNan(IrBuilder& build, int nparams, int ra, int arg, IrOp args, int nresults, int pcpos)
365
{
366
if (nparams < 1 || nresults > 1)
367
return {BuiltinImplType::None, -1};
368
369
builtinCheckDouble(build, build.vmReg(arg), pcpos);
370
371
IrOp varg = builtinLoadDouble(build, build.vmReg(arg));
372
373
IrOp result =
374
build.inst(IrCmd::CMP_SPLIT_TVALUE, build.constTag(LUA_TNUMBER), build.constTag(LUA_TNUMBER), varg, varg, build.cond(IrCondition::NotEqual));
375
376
build.inst(IrCmd::STORE_INT, build.vmReg(ra), result);
377
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TBOOLEAN));
378
379
return {BuiltinImplType::Full, 1};
380
}
381
382
static BuiltinImplResult translateBuiltinType(IrBuilder& build, int nparams, int ra, int arg, IrOp args, int nresults)
383
{
384
if (nparams < 1 || nresults > 1)
385
return {BuiltinImplType::None, -1};
386
387
IrOp tag = build.inst(IrCmd::LOAD_TAG, build.vmReg(arg));
388
IrOp name = build.inst(IrCmd::GET_TYPE, tag);
389
390
build.inst(IrCmd::STORE_POINTER, build.vmReg(ra), name);
391
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TSTRING));
392
393
return {BuiltinImplType::Full, 1};
394
}
395
396
static BuiltinImplResult translateBuiltinTypeof(IrBuilder& build, int nparams, int ra, int arg, IrOp args, int nresults)
397
{
398
if (nparams < 1 || nresults > 1)
399
return {BuiltinImplType::None, -1};
400
401
IrOp name = build.inst(IrCmd::GET_TYPEOF, build.vmReg(arg));
402
403
build.inst(IrCmd::STORE_POINTER, build.vmReg(ra), name);
404
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TSTRING));
405
406
return {BuiltinImplType::Full, 1};
407
}
408
409
static BuiltinImplResult translateBuiltinBit32MultiargOp(
410
IrBuilder& build,
411
IrCmd cmd,
412
bool btest,
413
int nparams,
414
int ra,
415
int arg,
416
IrOp args,
417
IrOp arg3,
418
int nresults,
419
int pcpos
420
)
421
{
422
if (nparams < 1 || nparams > kBit32BinaryOpUnrolledParams || nresults > 1)
423
return {BuiltinImplType::None, -1};
424
425
builtinCheckDouble(build, build.vmReg(arg), pcpos);
426
427
if (nparams >= 2)
428
builtinCheckDouble(build, args, pcpos);
429
430
if (nparams >= 3)
431
builtinCheckDouble(build, arg3, pcpos);
432
433
for (int i = 4; i <= nparams; ++i)
434
builtinCheckDouble(build, build.vmReg(vmRegOp(args) + (i - 2)), pcpos);
435
436
IrOp va = builtinLoadDouble(build, build.vmReg(arg));
437
IrOp res = build.inst(IrCmd::NUM_TO_UINT, va);
438
439
if (nparams >= 2)
440
{
441
IrOp vb = builtinLoadDouble(build, args);
442
IrOp arg = build.inst(IrCmd::NUM_TO_UINT, vb);
443
444
res = build.inst(cmd, res, arg);
445
}
446
447
if (nparams >= 3)
448
{
449
IrOp vc = builtinLoadDouble(build, arg3);
450
IrOp arg = build.inst(IrCmd::NUM_TO_UINT, vc);
451
452
res = build.inst(cmd, res, arg);
453
}
454
455
for (int i = 4; i <= nparams; ++i)
456
{
457
IrOp vc = builtinLoadDouble(build, build.vmReg(vmRegOp(args) + (i - 2)));
458
IrOp arg = build.inst(IrCmd::NUM_TO_UINT, vc);
459
460
res = build.inst(cmd, res, arg);
461
}
462
463
if (btest)
464
{
465
IrOp value = build.inst(IrCmd::CMP_INT, res, build.constInt(0), build.cond(IrCondition::NotEqual));
466
build.inst(IrCmd::STORE_INT, build.vmReg(ra), value);
467
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TBOOLEAN));
468
}
469
else
470
{
471
IrOp value = build.inst(IrCmd::UINT_TO_NUM, res);
472
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), value);
473
474
if (ra != arg)
475
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
476
}
477
478
return {BuiltinImplType::Full, 1};
479
}
480
481
static BuiltinImplResult translateBuiltinBit32Bnot(IrBuilder& build, int nparams, int ra, int arg, IrOp args, int nresults, int pcpos)
482
{
483
if (nparams < 1 || nresults > 1)
484
return {BuiltinImplType::None, -1};
485
486
builtinCheckDouble(build, build.vmReg(arg), pcpos);
487
IrOp va = builtinLoadDouble(build, build.vmReg(arg));
488
489
IrOp vaui = build.inst(IrCmd::NUM_TO_UINT, va);
490
IrOp not_ = build.inst(IrCmd::BITNOT_UINT, vaui);
491
IrOp value = build.inst(IrCmd::UINT_TO_NUM, not_);
492
493
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), value);
494
495
if (ra != arg)
496
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
497
498
return {BuiltinImplType::Full, 1};
499
}
500
501
static BuiltinImplResult translateBuiltinBit32Shift(
502
IrBuilder& build,
503
IrCmd cmd,
504
int nparams,
505
int ra,
506
int arg,
507
IrOp args,
508
int nresults,
509
int pcpos
510
)
511
{
512
if (nparams < 2 || nresults > 1)
513
return {BuiltinImplType::None, -1};
514
515
builtinCheckDouble(build, build.vmReg(arg), pcpos);
516
builtinCheckDouble(build, args, pcpos);
517
518
IrOp va = builtinLoadDouble(build, build.vmReg(arg));
519
IrOp vb = builtinLoadDouble(build, args);
520
521
IrOp vaui = build.inst(IrCmd::NUM_TO_UINT, va);
522
523
IrOp vbi;
524
525
if (std::optional<double> vbd = build.function.asDoubleOp(vb); vbd && *vbd >= INT_MIN && *vbd <= INT_MAX)
526
vbi = build.constInt(int(*vbd));
527
else
528
vbi = build.inst(IrCmd::NUM_TO_INT, vb);
529
530
bool knownGoodShift = unsigned(build.function.asIntOp(vbi).value_or(-1)) < 32u;
531
532
if (!knownGoodShift)
533
{
534
// unsigned(s) < 32
535
build.inst(IrCmd::CHECK_CMP_INT, vbi, build.constInt(32), build.cond(IrCondition::UnsignedLess), build.vmExit(pcpos));
536
}
537
538
IrOp shift = build.inst(cmd, vaui, vbi);
539
540
IrOp value = build.inst(IrCmd::UINT_TO_NUM, shift);
541
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), value);
542
543
if (ra != arg)
544
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
545
546
return {BuiltinImplType::Full, 1};
547
}
548
549
static BuiltinImplResult translateBuiltinBit32Rotate(IrBuilder& build, IrCmd cmd, int nparams, int ra, int arg, IrOp args, int nresults, int pcpos)
550
{
551
if (nparams < 2 || nresults > 1)
552
return {BuiltinImplType::None, -1};
553
554
builtinCheckDouble(build, build.vmReg(arg), pcpos);
555
builtinCheckDouble(build, args, pcpos);
556
557
IrOp va = builtinLoadDouble(build, build.vmReg(arg));
558
IrOp vb = builtinLoadDouble(build, args);
559
560
IrOp vaui = build.inst(IrCmd::NUM_TO_UINT, va);
561
IrOp vbi = build.inst(IrCmd::NUM_TO_INT, vb);
562
563
IrOp shift = build.inst(cmd, vaui, vbi);
564
565
IrOp value = build.inst(IrCmd::UINT_TO_NUM, shift);
566
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), value);
567
568
if (ra != arg)
569
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
570
571
return {BuiltinImplType::Full, 1};
572
}
573
574
static BuiltinImplResult translateBuiltinBit32Extract(
575
IrBuilder& build,
576
int nparams,
577
int ra,
578
int arg,
579
IrOp args,
580
IrOp arg3,
581
int nresults,
582
int pcpos
583
)
584
{
585
if (nparams < 2 || nresults > 1)
586
return {BuiltinImplType::None, -1};
587
588
if (nparams == 2 && args.kind == IrOpKind::Constant && unsigned(int(build.function.doubleOp(args))) >= 32)
589
return {BuiltinImplType::None, -1};
590
591
builtinCheckDouble(build, build.vmReg(arg), pcpos);
592
builtinCheckDouble(build, args, pcpos);
593
594
IrOp va = builtinLoadDouble(build, build.vmReg(arg));
595
IrOp vb = builtinLoadDouble(build, args);
596
597
IrOp n = build.inst(IrCmd::NUM_TO_UINT, va);
598
599
IrOp value;
600
if (nparams == 2)
601
{
602
if (vb.kind == IrOpKind::Constant)
603
{
604
int f = int(build.function.doubleOp(vb));
605
CODEGEN_ASSERT(unsigned(f) < 32); // checked above
606
607
value = n;
608
609
if (f)
610
value = build.inst(IrCmd::BITRSHIFT_UINT, value, build.constInt(f));
611
612
if (f + 1 < 32)
613
value = build.inst(IrCmd::BITAND_UINT, value, build.constInt(1));
614
}
615
else
616
{
617
IrOp f = build.inst(IrCmd::NUM_TO_INT, vb);
618
619
// unsigned(f) < 32
620
build.inst(IrCmd::CHECK_CMP_INT, f, build.constInt(32), build.cond(IrCondition::UnsignedLess), build.vmExit(pcpos));
621
622
IrOp shift = build.inst(IrCmd::BITRSHIFT_UINT, n, f);
623
value = build.inst(IrCmd::BITAND_UINT, shift, build.constInt(1));
624
}
625
}
626
else
627
{
628
IrOp f = build.inst(IrCmd::NUM_TO_INT, vb);
629
630
builtinCheckDouble(build, arg3, pcpos);
631
IrOp vc = builtinLoadDouble(build, arg3);
632
633
IrOp w = build.inst(IrCmd::NUM_TO_INT, vc);
634
IrOp fw = build.inst(IrCmd::ADD_INT, f, w);
635
636
// f >= 0 && w > 0 && f + w <= 32
637
build.inst(IrCmd::CHECK_CMP_INT, f, build.constInt(0), build.cond(IrCondition::GreaterEqual), build.vmExit(pcpos));
638
build.inst(IrCmd::CHECK_CMP_INT, w, build.constInt(0), build.cond(IrCondition::Greater), build.vmExit(pcpos));
639
build.inst(IrCmd::CHECK_CMP_INT, fw, build.constInt(32), build.cond(IrCondition::LessEqual), build.vmExit(pcpos));
640
641
IrOp shift = build.inst(IrCmd::BITLSHIFT_UINT, build.constInt(0xfffffffe), build.inst(IrCmd::SUB_INT, w, build.constInt(1)));
642
IrOp m = build.inst(IrCmd::BITNOT_UINT, shift);
643
644
IrOp nf = build.inst(IrCmd::BITRSHIFT_UINT, n, f);
645
value = build.inst(IrCmd::BITAND_UINT, nf, m);
646
}
647
648
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), build.inst(IrCmd::UINT_TO_NUM, value));
649
650
if (ra != arg)
651
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
652
653
return {BuiltinImplType::Full, 1};
654
}
655
656
static BuiltinImplResult translateBuiltinBit32ExtractK(IrBuilder& build, int nparams, int ra, int arg, IrOp args, int nresults, int pcpos)
657
{
658
if (nparams < 2 || nresults > 1)
659
return {BuiltinImplType::None, -1};
660
661
builtinCheckDouble(build, build.vmReg(arg), pcpos);
662
663
IrOp va = builtinLoadDouble(build, build.vmReg(arg));
664
IrOp n = build.inst(IrCmd::NUM_TO_UINT, va);
665
666
double a2 = build.function.doubleOp(args);
667
int fw = int(a2);
668
669
int f = fw & 31;
670
int w1 = fw >> 5;
671
672
uint32_t m = ~(0xfffffffeu << w1);
673
674
IrOp result = n;
675
676
if (f)
677
result = build.inst(IrCmd::BITRSHIFT_UINT, result, build.constInt(f));
678
679
if ((f + w1 + 1) < 32)
680
result = build.inst(IrCmd::BITAND_UINT, result, build.constInt(m));
681
682
IrOp value = build.inst(IrCmd::UINT_TO_NUM, result);
683
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), value);
684
685
if (ra != arg)
686
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
687
688
return {BuiltinImplType::Full, 1};
689
}
690
691
static BuiltinImplResult translateBuiltinBit32Unary(IrBuilder& build, IrCmd cmd, int nparams, int ra, int arg, IrOp args, int nresults, int pcpos)
692
{
693
if (nparams < 1 || nresults > 1)
694
return {BuiltinImplType::None, -1};
695
696
builtinCheckDouble(build, build.vmReg(arg), pcpos);
697
IrOp va = builtinLoadDouble(build, build.vmReg(arg));
698
699
IrOp vaui = build.inst(IrCmd::NUM_TO_UINT, va);
700
701
IrOp bin = build.inst(cmd, vaui);
702
703
IrOp value = build.inst(IrCmd::UINT_TO_NUM, bin);
704
705
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), value);
706
707
if (ra != arg)
708
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
709
710
return {BuiltinImplType::Full, 1};
711
}
712
713
static BuiltinImplResult translateBuiltinBit32Replace(
714
IrBuilder& build,
715
int nparams,
716
int ra,
717
int arg,
718
IrOp args,
719
IrOp arg3,
720
int nresults,
721
int pcpos
722
)
723
{
724
if (nparams < 3 || nresults > 1)
725
return {BuiltinImplType::None, -1};
726
727
builtinCheckDouble(build, build.vmReg(arg), pcpos);
728
builtinCheckDouble(build, args, pcpos);
729
builtinCheckDouble(build, arg3, pcpos);
730
731
IrOp va = builtinLoadDouble(build, build.vmReg(arg));
732
IrOp vb = builtinLoadDouble(build, args);
733
IrOp vc = builtinLoadDouble(build, arg3);
734
735
IrOp n = build.inst(IrCmd::NUM_TO_UINT, va);
736
IrOp v = build.inst(IrCmd::NUM_TO_UINT, vb);
737
IrOp f = build.inst(IrCmd::NUM_TO_INT, vc);
738
739
IrOp value;
740
if (nparams == 3)
741
{
742
// unsigned(f) < 32
743
build.inst(IrCmd::CHECK_CMP_INT, f, build.constInt(32), build.cond(IrCondition::UnsignedLess), build.vmExit(pcpos));
744
745
IrOp m = build.constInt(1);
746
IrOp shift = build.inst(IrCmd::BITLSHIFT_UINT, m, f);
747
IrOp not_ = build.inst(IrCmd::BITNOT_UINT, shift);
748
IrOp lhs = build.inst(IrCmd::BITAND_UINT, n, not_);
749
750
IrOp vm = build.inst(IrCmd::BITAND_UINT, v, m);
751
IrOp rhs = build.inst(IrCmd::BITLSHIFT_UINT, vm, f);
752
753
value = build.inst(IrCmd::BITOR_UINT, lhs, rhs);
754
}
755
else
756
{
757
builtinCheckDouble(build, build.vmReg(vmRegOp(args) + 2), pcpos);
758
IrOp vd = builtinLoadDouble(build, build.vmReg(vmRegOp(args) + 2));
759
760
IrOp w = build.inst(IrCmd::NUM_TO_INT, vd);
761
IrOp fw = build.inst(IrCmd::ADD_INT, f, w);
762
763
// f >= 0 && w > 0 && f + w <= 32
764
build.inst(IrCmd::CHECK_CMP_INT, f, build.constInt(0), build.cond(IrCondition::GreaterEqual), build.vmExit(pcpos));
765
build.inst(IrCmd::CHECK_CMP_INT, w, build.constInt(0), build.cond(IrCondition::Greater), build.vmExit(pcpos));
766
build.inst(IrCmd::CHECK_CMP_INT, fw, build.constInt(32), build.cond(IrCondition::LessEqual), build.vmExit(pcpos));
767
768
IrOp shift1 = build.inst(IrCmd::BITLSHIFT_UINT, build.constInt(0xfffffffe), build.inst(IrCmd::SUB_INT, w, build.constInt(1)));
769
IrOp m = build.inst(IrCmd::BITNOT_UINT, shift1);
770
771
IrOp shift2 = build.inst(IrCmd::BITLSHIFT_UINT, m, f);
772
IrOp not_ = build.inst(IrCmd::BITNOT_UINT, shift2);
773
IrOp lhs = build.inst(IrCmd::BITAND_UINT, n, not_);
774
775
IrOp vm = build.inst(IrCmd::BITAND_UINT, v, m);
776
IrOp rhs = build.inst(IrCmd::BITLSHIFT_UINT, vm, f);
777
778
value = build.inst(IrCmd::BITOR_UINT, lhs, rhs);
779
}
780
781
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), build.inst(IrCmd::UINT_TO_NUM, value));
782
783
if (ra != arg)
784
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
785
786
return {BuiltinImplType::Full, 1};
787
}
788
789
static BuiltinImplResult translateBuiltinVector(IrBuilder& build, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, int pcpos)
790
{
791
if (nparams < 2 || nresults > 1)
792
return {BuiltinImplType::None, -1};
793
794
CODEGEN_ASSERT(LUA_VECTOR_SIZE == 3);
795
796
if (nparams == 2)
797
{
798
builtinCheckDouble(build, build.vmReg(arg), pcpos);
799
builtinCheckDouble(build, args, pcpos);
800
801
IrOp x = builtinLoadDouble(build, build.vmReg(arg));
802
IrOp y = builtinLoadDouble(build, args);
803
804
IrOp xf = build.inst(IrCmd::NUM_TO_FLOAT, x);
805
IrOp yf = build.inst(IrCmd::NUM_TO_FLOAT, y);
806
807
build.inst(IrCmd::STORE_VECTOR, build.vmReg(ra), xf, yf, build.constDouble(0.0));
808
}
809
else
810
{
811
builtinCheckDouble(build, build.vmReg(arg), pcpos);
812
builtinCheckDouble(build, args, pcpos);
813
builtinCheckDouble(build, arg3, pcpos);
814
815
IrOp x = builtinLoadDouble(build, build.vmReg(arg));
816
IrOp y = builtinLoadDouble(build, args);
817
IrOp z = builtinLoadDouble(build, arg3);
818
819
IrOp xf = build.inst(IrCmd::NUM_TO_FLOAT, x);
820
IrOp yf = build.inst(IrCmd::NUM_TO_FLOAT, y);
821
IrOp zf = build.inst(IrCmd::NUM_TO_FLOAT, z);
822
823
build.inst(IrCmd::STORE_VECTOR, build.vmReg(ra), xf, yf, zf);
824
}
825
826
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TVECTOR));
827
828
return {BuiltinImplType::Full, 1};
829
}
830
831
static BuiltinImplResult translateBuiltinTableInsert(IrBuilder& build, int nparams, int ra, int arg, IrOp args, int nresults, int pcpos)
832
{
833
if (nparams != 2 || nresults > 0)
834
return {BuiltinImplType::None, -1};
835
836
build.loadAndCheckTag(build.vmReg(arg), LUA_TTABLE, build.vmExit(pcpos));
837
838
IrOp table = build.inst(IrCmd::LOAD_POINTER, build.vmReg(arg));
839
build.inst(IrCmd::CHECK_READONLY, table, build.vmExit(pcpos));
840
841
IrOp pos = build.inst(IrCmd::ADD_INT, build.inst(IrCmd::TABLE_LEN, table), build.constInt(1));
842
843
IrOp setnum = build.inst(IrCmd::TABLE_SETNUM, table, pos);
844
845
if (args.kind == IrOpKind::Constant)
846
{
847
CODEGEN_ASSERT(build.function.constOp(args).kind == IrConstKind::Double);
848
849
// No barrier necessary since numbers aren't collectable
850
build.inst(IrCmd::STORE_DOUBLE, setnum, args);
851
build.inst(IrCmd::STORE_TAG, setnum, build.constTag(LUA_TNUMBER));
852
}
853
else
854
{
855
IrOp va = build.inst(IrCmd::LOAD_TVALUE, args);
856
build.inst(IrCmd::STORE_TVALUE, setnum, va);
857
858
// Compiler only generates FASTCALL*K for source-level constants, so dynamic imports are not affected
859
CODEGEN_ASSERT(build.function.proto);
860
IrOp argstag = args.kind == IrOpKind::VmConst ? build.constTag(build.function.proto->k[vmConstOp(args)].tt) : build.undef();
861
862
build.inst(IrCmd::BARRIER_TABLE_FORWARD, table, args, argstag);
863
}
864
865
return {BuiltinImplType::Full, 0};
866
}
867
868
static BuiltinImplResult translateBuiltinStringLen(IrBuilder& build, int nparams, int ra, int arg, IrOp args, int nresults, int pcpos)
869
{
870
if (nparams < 1 || nresults > 1)
871
return {BuiltinImplType::None, -1};
872
873
build.loadAndCheckTag(build.vmReg(arg), LUA_TSTRING, build.vmExit(pcpos));
874
875
IrOp ts = build.inst(IrCmd::LOAD_POINTER, build.vmReg(arg));
876
877
IrOp len = build.inst(IrCmd::STRING_LEN, ts);
878
879
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), build.inst(IrCmd::INT_TO_NUM, len));
880
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
881
882
return {BuiltinImplType::Full, 1};
883
}
884
885
static void translateBufferArgsAndCheckBounds(
886
IrBuilder& build,
887
int nparams,
888
int arg,
889
IrOp args,
890
IrOp arg3,
891
int size,
892
int pcpos,
893
IrOp& buf,
894
IrOp& intIndex
895
)
896
{
897
build.loadAndCheckTag(build.vmReg(arg), LUA_TBUFFER, build.vmExit(pcpos));
898
builtinCheckDouble(build, args, pcpos);
899
900
if (nparams == 3)
901
builtinCheckDouble(build, arg3, pcpos);
902
903
buf = build.inst(IrCmd::LOAD_POINTER, build.vmReg(arg));
904
905
IrOp numIndex = builtinLoadDouble(build, args);
906
intIndex = build.inst(IrCmd::NUM_TO_INT, numIndex);
907
908
if (FFlag::LuauCodegenBufferRangeMerge4)
909
build.inst(IrCmd::CHECK_BUFFER_LEN, buf, intIndex, build.constInt(0), build.constInt(size), build.undef(), build.vmExit(pcpos));
910
else
911
build.inst(IrCmd::CHECK_BUFFER_LEN, buf, intIndex, build.constInt(size), build.vmExit(pcpos));
912
}
913
914
static BuiltinImplResult translateBuiltinBufferRead(
915
IrBuilder& build,
916
int nparams,
917
int ra,
918
int arg,
919
IrOp args,
920
IrOp arg3,
921
int nresults,
922
int pcpos,
923
IrCmd readCmd,
924
int size,
925
IrCmd convCmd
926
)
927
{
928
if (nparams < 2 || nresults > 1)
929
return {BuiltinImplType::None, -1};
930
931
IrOp buf, intIndex;
932
translateBufferArgsAndCheckBounds(build, nparams, arg, args, arg3, size, pcpos, buf, intIndex);
933
934
IrOp result =
935
FFlag::LuauCodegenBufNoDefTag ? build.inst(readCmd, buf, intIndex, build.constTag(LUA_TBUFFER)) : build.inst(readCmd, buf, intIndex);
936
937
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), convCmd == IrCmd::NOP ? result : build.inst(convCmd, result));
938
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
939
940
return {BuiltinImplType::Full, 1};
941
}
942
943
static BuiltinImplResult translateBuiltinBufferWrite(
944
IrBuilder& build,
945
int nparams,
946
int ra,
947
int arg,
948
IrOp args,
949
IrOp arg3,
950
int nresults,
951
int pcpos,
952
IrCmd writeCmd,
953
int size,
954
IrCmd convCmd
955
)
956
{
957
if (nparams < 3 || nresults > 0)
958
return {BuiltinImplType::None, -1};
959
960
IrOp buf, intIndex;
961
translateBufferArgsAndCheckBounds(build, nparams, arg, args, arg3, size, pcpos, buf, intIndex);
962
963
IrOp numValue = builtinLoadDouble(build, arg3);
964
965
if (FFlag::LuauCodegenBufNoDefTag)
966
build.inst(writeCmd, buf, intIndex, convCmd == IrCmd::NOP ? numValue : build.inst(convCmd, numValue), build.constTag(LUA_TBUFFER));
967
else
968
build.inst(writeCmd, buf, intIndex, convCmd == IrCmd::NOP ? numValue : build.inst(convCmd, numValue));
969
970
return {BuiltinImplType::Full, 0};
971
}
972
973
static BuiltinImplResult translateBuiltinVectorMagnitude(
974
IrBuilder& build,
975
int nparams,
976
int ra,
977
int arg,
978
IrOp args,
979
IrOp arg3,
980
int nresults,
981
int pcpos
982
)
983
{
984
IrOp arg1 = build.vmReg(arg);
985
986
if (nparams != 1 || nresults > 1 || arg1.kind == IrOpKind::Constant)
987
return {BuiltinImplType::None, -1};
988
989
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
990
991
IrOp a = build.inst(IrCmd::LOAD_TVALUE, arg1, build.constInt(0));
992
993
IrOp sum = build.inst(IrCmd::DOT_VEC, a, a);
994
IrOp mag = build.inst(IrCmd::SQRT_FLOAT, sum);
995
996
mag = build.inst(IrCmd::FLOAT_TO_NUM, mag);
997
998
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), mag);
999
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
1000
1001
return {BuiltinImplType::Full, 1};
1002
}
1003
1004
static BuiltinImplResult translateBuiltinVectorNormalize(
1005
IrBuilder& build,
1006
int nparams,
1007
int ra,
1008
int arg,
1009
IrOp args,
1010
IrOp arg3,
1011
int nresults,
1012
int pcpos
1013
)
1014
{
1015
IrOp arg1 = build.vmReg(arg);
1016
1017
if (nparams != 1 || nresults > 1 || arg1.kind == IrOpKind::Constant)
1018
return {BuiltinImplType::None, -1};
1019
1020
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
1021
1022
IrOp a = build.inst(IrCmd::LOAD_TVALUE, arg1, build.constInt(0));
1023
IrOp sum = build.inst(IrCmd::DOT_VEC, a, a);
1024
1025
IrOp mag = build.inst(IrCmd::SQRT_FLOAT, sum);
1026
IrOp inv = build.inst(IrCmd::DIV_FLOAT, build.constDouble(1.0f), mag);
1027
IrOp invvec = build.inst(IrCmd::FLOAT_TO_VEC, inv);
1028
1029
IrOp result = build.inst(IrCmd::MUL_VEC, a, invvec);
1030
1031
result = build.inst(IrCmd::TAG_VECTOR, result);
1032
1033
build.inst(IrCmd::STORE_TVALUE, build.vmReg(ra), result);
1034
1035
return {BuiltinImplType::Full, 1};
1036
}
1037
1038
static BuiltinImplResult translateBuiltinVectorCross(IrBuilder& build, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, int pcpos)
1039
{
1040
IrOp arg1 = build.vmReg(arg);
1041
1042
if (nparams != 2 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant)
1043
return {BuiltinImplType::None, -1};
1044
1045
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
1046
build.loadAndCheckTag(args, LUA_TVECTOR, build.vmExit(pcpos));
1047
1048
IrOp x1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(0));
1049
IrOp x2 = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(0));
1050
1051
IrOp y1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(4));
1052
IrOp y2 = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(4));
1053
1054
IrOp z1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(8));
1055
IrOp z2 = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(8));
1056
1057
IrOp y1z2 = build.inst(IrCmd::MUL_FLOAT, y1, z2);
1058
IrOp z1y2 = build.inst(IrCmd::MUL_FLOAT, z1, y2);
1059
IrOp xr = build.inst(IrCmd::SUB_FLOAT, y1z2, z1y2);
1060
1061
IrOp z1x2 = build.inst(IrCmd::MUL_FLOAT, z1, x2);
1062
IrOp x1z2 = build.inst(IrCmd::MUL_FLOAT, x1, z2);
1063
IrOp yr = build.inst(IrCmd::SUB_FLOAT, z1x2, x1z2);
1064
1065
IrOp x1y2 = build.inst(IrCmd::MUL_FLOAT, x1, y2);
1066
IrOp y1x2 = build.inst(IrCmd::MUL_FLOAT, y1, x2);
1067
IrOp zr = build.inst(IrCmd::SUB_FLOAT, x1y2, y1x2);
1068
1069
build.inst(IrCmd::STORE_VECTOR, build.vmReg(ra), xr, yr, zr);
1070
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TVECTOR));
1071
1072
return {BuiltinImplType::Full, 1};
1073
}
1074
1075
static BuiltinImplResult translateBuiltinVectorDot(IrBuilder& build, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, int pcpos)
1076
{
1077
IrOp arg1 = build.vmReg(arg);
1078
1079
if (nparams != 2 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant)
1080
return {BuiltinImplType::None, -1};
1081
1082
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
1083
build.loadAndCheckTag(args, LUA_TVECTOR, build.vmExit(pcpos));
1084
1085
IrOp a = build.inst(IrCmd::LOAD_TVALUE, arg1, build.constInt(0));
1086
IrOp b = build.inst(IrCmd::LOAD_TVALUE, args, build.constInt(0));
1087
1088
IrOp sum = build.inst(IrCmd::DOT_VEC, a, b);
1089
1090
sum = build.inst(IrCmd::FLOAT_TO_NUM, sum);
1091
1092
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), sum);
1093
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
1094
1095
return {BuiltinImplType::Full, 1};
1096
}
1097
1098
static BuiltinImplResult translateBuiltinVectorMap1x4(
1099
IrBuilder& build,
1100
IrCmd cmd,
1101
int nparams,
1102
int ra,
1103
int arg,
1104
IrOp args,
1105
IrOp arg3,
1106
int nresults,
1107
int pcpos
1108
)
1109
{
1110
IrOp arg1 = build.vmReg(arg);
1111
1112
if (nparams != 1 || nresults > 1 || arg1.kind == IrOpKind::Constant)
1113
return {BuiltinImplType::None, -1};
1114
1115
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
1116
1117
IrOp value = build.inst(IrCmd::LOAD_TVALUE, arg1);
1118
IrOp ret = build.inst(cmd, value);
1119
1120
build.inst(IrCmd::STORE_TVALUE, build.vmReg(ra), build.inst(IrCmd::TAG_VECTOR, ret));
1121
1122
return {BuiltinImplType::Full, 1};
1123
}
1124
1125
static BuiltinImplResult translateBuiltinVectorMap1(
1126
IrBuilder& build,
1127
IrCmd cmd,
1128
int nparams,
1129
int ra,
1130
int arg,
1131
IrOp args,
1132
IrOp arg3,
1133
int nresults,
1134
int pcpos
1135
)
1136
{
1137
IrOp arg1 = build.vmReg(arg);
1138
1139
if (nparams != 1 || nresults > 1 || arg1.kind == IrOpKind::Constant)
1140
return {BuiltinImplType::None, -1};
1141
1142
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
1143
1144
IrOp x1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(0));
1145
IrOp y1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(4));
1146
IrOp z1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(8));
1147
1148
IrOp xr = build.inst(cmd, x1);
1149
IrOp yr = build.inst(cmd, y1);
1150
IrOp zr = build.inst(cmd, z1);
1151
1152
build.inst(IrCmd::STORE_VECTOR, build.vmReg(ra), xr, yr, zr);
1153
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TVECTOR));
1154
1155
return {BuiltinImplType::Full, 1};
1156
}
1157
1158
static BuiltinImplResult translateBuiltinVectorClamp(
1159
IrBuilder& build,
1160
int nparams,
1161
int ra,
1162
int arg,
1163
IrOp args,
1164
IrOp arg3,
1165
int nresults,
1166
IrOp fallback,
1167
int pcpos
1168
)
1169
{
1170
IrOp arg1 = build.vmReg(arg);
1171
1172
if (nparams != 3 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant || arg3.kind == IrOpKind::Constant)
1173
return {BuiltinImplType::None, -1};
1174
1175
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
1176
build.loadAndCheckTag(args, LUA_TVECTOR, build.vmExit(pcpos));
1177
build.loadAndCheckTag(arg3, LUA_TVECTOR, build.vmExit(pcpos));
1178
1179
IrOp block1 = build.block(IrBlockKind::Internal);
1180
IrOp block2 = build.block(IrBlockKind::Internal);
1181
IrOp block3 = build.block(IrBlockKind::Internal);
1182
1183
IrOp x = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(0));
1184
IrOp xmin = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(0));
1185
IrOp xmax = build.inst(IrCmd::LOAD_FLOAT, arg3, build.constInt(0));
1186
1187
build.inst(IrCmd::JUMP_CMP_FLOAT, xmin, xmax, build.cond(IrCondition::NotLessEqual), fallback, block1);
1188
1189
build.beginBlock(block1);
1190
1191
IrOp y = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(4));
1192
IrOp ymin = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(4));
1193
IrOp ymax = build.inst(IrCmd::LOAD_FLOAT, arg3, build.constInt(4));
1194
1195
build.inst(IrCmd::JUMP_CMP_FLOAT, ymin, ymax, build.cond(IrCondition::NotLessEqual), fallback, block2);
1196
1197
build.beginBlock(block2);
1198
1199
IrOp z = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(8));
1200
IrOp zmin = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(8));
1201
IrOp zmax = build.inst(IrCmd::LOAD_FLOAT, arg3, build.constInt(8));
1202
1203
build.inst(IrCmd::JUMP_CMP_FLOAT, zmin, zmax, build.cond(IrCondition::NotLessEqual), fallback, block3);
1204
1205
build.beginBlock(block3);
1206
1207
IrOp xtemp = build.inst(IrCmd::MAX_FLOAT, xmin, x);
1208
IrOp xclamped = build.inst(IrCmd::MIN_FLOAT, xmax, xtemp);
1209
1210
IrOp ytemp = build.inst(IrCmd::MAX_FLOAT, ymin, y);
1211
IrOp yclamped = build.inst(IrCmd::MIN_FLOAT, ymax, ytemp);
1212
1213
IrOp ztemp = build.inst(IrCmd::MAX_FLOAT, zmin, z);
1214
IrOp zclamped = build.inst(IrCmd::MIN_FLOAT, zmax, ztemp);
1215
1216
build.inst(IrCmd::STORE_VECTOR, build.vmReg(ra), xclamped, yclamped, zclamped);
1217
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TVECTOR));
1218
1219
return {BuiltinImplType::UsesFallback, 1};
1220
}
1221
1222
static BuiltinImplResult translateBuiltinVectorMinMax(
1223
IrBuilder& build,
1224
IrCmd cmd,
1225
int nparams,
1226
int ra,
1227
int arg,
1228
IrOp args,
1229
IrOp arg3,
1230
int nresults,
1231
int pcpos
1232
)
1233
{
1234
IrOp arg1 = build.vmReg(arg);
1235
1236
if (nparams != 2 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant)
1237
return {BuiltinImplType::None, -1};
1238
1239
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
1240
build.loadAndCheckTag(args, LUA_TVECTOR, build.vmExit(pcpos));
1241
1242
IrOp value1 = build.inst(IrCmd::LOAD_TVALUE, arg1);
1243
IrOp value2 = build.inst(IrCmd::LOAD_TVALUE, args);
1244
1245
IrOp ret = build.inst(cmd, value2, value1); // Swapped arguments are required for consistency with VM builtins
1246
1247
build.inst(IrCmd::STORE_TVALUE, build.vmReg(ra), build.inst(IrCmd::TAG_VECTOR, ret));
1248
1249
return {BuiltinImplType::Full, 1};
1250
}
1251
1252
BuiltinImplResult translateBuiltin(
1253
IrBuilder& build,
1254
int bfid,
1255
int ra,
1256
int arg,
1257
IrOp args,
1258
IrOp arg3,
1259
int nparams,
1260
int nresults,
1261
IrOp fallback,
1262
int pcpos
1263
)
1264
{
1265
// Builtins are not allowed to handle variadic arguments
1266
if (nparams == LUA_MULTRET)
1267
return {BuiltinImplType::None, -1};
1268
1269
switch (bfid)
1270
{
1271
case LBF_ASSERT:
1272
return translateBuiltinAssert(build, nparams, ra, arg, args, nresults, pcpos);
1273
case LBF_MATH_DEG:
1274
return translateBuiltinMathDegRad(build, IrCmd::DIV_NUM, nparams, ra, arg, args, nresults, pcpos);
1275
case LBF_MATH_RAD:
1276
return translateBuiltinMathDegRad(build, IrCmd::MUL_NUM, nparams, ra, arg, args, nresults, pcpos);
1277
case LBF_MATH_LOG:
1278
return translateBuiltinMathLog(build, nparams, ra, arg, args, nresults, pcpos);
1279
case LBF_MATH_MIN:
1280
return translateBuiltinMathMinMax(build, IrCmd::MIN_NUM, nparams, ra, arg, args, arg3, nresults, pcpos);
1281
case LBF_MATH_MAX:
1282
return translateBuiltinMathMinMax(build, IrCmd::MAX_NUM, nparams, ra, arg, args, arg3, nresults, pcpos);
1283
case LBF_MATH_CLAMP:
1284
return translateBuiltinMathClamp(build, nparams, ra, arg, args, arg3, nresults, fallback, pcpos);
1285
case LBF_MATH_FLOOR:
1286
return translateBuiltinMathUnary(build, IrCmd::FLOOR_NUM, nparams, ra, arg, nresults, pcpos);
1287
case LBF_MATH_CEIL:
1288
return translateBuiltinMathUnary(build, IrCmd::CEIL_NUM, nparams, ra, arg, nresults, pcpos);
1289
case LBF_MATH_SQRT:
1290
return translateBuiltinMathUnary(build, IrCmd::SQRT_NUM, nparams, ra, arg, nresults, pcpos);
1291
case LBF_MATH_ABS:
1292
return translateBuiltinMathUnary(build, IrCmd::ABS_NUM, nparams, ra, arg, nresults, pcpos);
1293
case LBF_MATH_ROUND:
1294
return translateBuiltinMathUnary(build, IrCmd::ROUND_NUM, nparams, ra, arg, nresults, pcpos);
1295
case LBF_MATH_EXP:
1296
case LBF_MATH_ASIN:
1297
case LBF_MATH_SIN:
1298
case LBF_MATH_SINH:
1299
case LBF_MATH_ACOS:
1300
case LBF_MATH_COS:
1301
case LBF_MATH_COSH:
1302
case LBF_MATH_ATAN:
1303
case LBF_MATH_TAN:
1304
case LBF_MATH_TANH:
1305
case LBF_MATH_LOG10:
1306
return translateBuiltinNumberToNumberLibm(build, LuauBuiltinFunction(bfid), nparams, ra, arg, nresults, pcpos);
1307
case LBF_MATH_SIGN:
1308
return translateBuiltinMathUnary(build, IrCmd::SIGN_NUM, nparams, ra, arg, nresults, pcpos);
1309
case LBF_MATH_POW:
1310
case LBF_MATH_FMOD:
1311
case LBF_MATH_ATAN2:
1312
case LBF_MATH_LDEXP:
1313
return translateBuiltin2NumberToNumberLibm(build, LuauBuiltinFunction(bfid), nparams, ra, arg, args, nresults, pcpos);
1314
case LBF_MATH_FREXP:
1315
case LBF_MATH_MODF:
1316
return translateBuiltinNumberTo2Number(build, LuauBuiltinFunction(bfid), nparams, ra, arg, args, nresults, pcpos);
1317
case LBF_BIT32_BAND:
1318
return translateBuiltinBit32MultiargOp(build, IrCmd::BITAND_UINT, /* btest= */ false, nparams, ra, arg, args, arg3, nresults, pcpos);
1319
case LBF_BIT32_BOR:
1320
return translateBuiltinBit32MultiargOp(build, IrCmd::BITOR_UINT, /* btest= */ false, nparams, ra, arg, args, arg3, nresults, pcpos);
1321
case LBF_BIT32_BXOR:
1322
return translateBuiltinBit32MultiargOp(build, IrCmd::BITXOR_UINT, /* btest= */ false, nparams, ra, arg, args, arg3, nresults, pcpos);
1323
case LBF_BIT32_BTEST:
1324
return translateBuiltinBit32MultiargOp(build, IrCmd::BITAND_UINT, /* btest= */ true, nparams, ra, arg, args, arg3, nresults, pcpos);
1325
case LBF_BIT32_BNOT:
1326
return translateBuiltinBit32Bnot(build, nparams, ra, arg, args, nresults, pcpos);
1327
case LBF_BIT32_LSHIFT:
1328
return translateBuiltinBit32Shift(build, IrCmd::BITLSHIFT_UINT, nparams, ra, arg, args, nresults, pcpos);
1329
case LBF_BIT32_RSHIFT:
1330
return translateBuiltinBit32Shift(build, IrCmd::BITRSHIFT_UINT, nparams, ra, arg, args, nresults, pcpos);
1331
case LBF_BIT32_ARSHIFT:
1332
return translateBuiltinBit32Shift(build, IrCmd::BITARSHIFT_UINT, nparams, ra, arg, args, nresults, pcpos);
1333
case LBF_BIT32_LROTATE:
1334
return translateBuiltinBit32Rotate(build, IrCmd::BITLROTATE_UINT, nparams, ra, arg, args, nresults, pcpos);
1335
case LBF_BIT32_RROTATE:
1336
return translateBuiltinBit32Rotate(build, IrCmd::BITRROTATE_UINT, nparams, ra, arg, args, nresults, pcpos);
1337
case LBF_BIT32_EXTRACT:
1338
return translateBuiltinBit32Extract(build, nparams, ra, arg, args, arg3, nresults, pcpos);
1339
case LBF_BIT32_EXTRACTK:
1340
return translateBuiltinBit32ExtractK(build, nparams, ra, arg, args, nresults, pcpos);
1341
case LBF_BIT32_COUNTLZ:
1342
return translateBuiltinBit32Unary(build, IrCmd::BITCOUNTLZ_UINT, nparams, ra, arg, args, nresults, pcpos);
1343
case LBF_BIT32_COUNTRZ:
1344
return translateBuiltinBit32Unary(build, IrCmd::BITCOUNTRZ_UINT, nparams, ra, arg, args, nresults, pcpos);
1345
case LBF_BIT32_REPLACE:
1346
return translateBuiltinBit32Replace(build, nparams, ra, arg, args, arg3, nresults, pcpos);
1347
case LBF_TYPE:
1348
return translateBuiltinType(build, nparams, ra, arg, args, nresults);
1349
case LBF_TYPEOF:
1350
return translateBuiltinTypeof(build, nparams, ra, arg, args, nresults);
1351
case LBF_VECTOR:
1352
return translateBuiltinVector(build, nparams, ra, arg, args, arg3, nresults, pcpos);
1353
case LBF_TABLE_INSERT:
1354
return translateBuiltinTableInsert(build, nparams, ra, arg, args, nresults, pcpos);
1355
case LBF_STRING_LEN:
1356
return translateBuiltinStringLen(build, nparams, ra, arg, args, nresults, pcpos);
1357
case LBF_BIT32_BYTESWAP:
1358
return translateBuiltinBit32Unary(build, IrCmd::BYTESWAP_UINT, nparams, ra, arg, args, nresults, pcpos);
1359
case LBF_BUFFER_READI8:
1360
return translateBuiltinBufferRead(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_READI8, 1, IrCmd::INT_TO_NUM);
1361
case LBF_BUFFER_READU8:
1362
return translateBuiltinBufferRead(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_READU8, 1, IrCmd::INT_TO_NUM);
1363
case LBF_BUFFER_WRITEU8:
1364
return translateBuiltinBufferWrite(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_WRITEI8, 1, IrCmd::NUM_TO_UINT);
1365
case LBF_BUFFER_READI16:
1366
return translateBuiltinBufferRead(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_READI16, 2, IrCmd::INT_TO_NUM);
1367
case LBF_BUFFER_READU16:
1368
return translateBuiltinBufferRead(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_READU16, 2, IrCmd::INT_TO_NUM);
1369
case LBF_BUFFER_WRITEU16:
1370
return translateBuiltinBufferWrite(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_WRITEI16, 2, IrCmd::NUM_TO_UINT);
1371
case LBF_BUFFER_READI32:
1372
return translateBuiltinBufferRead(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_READI32, 4, IrCmd::INT_TO_NUM);
1373
case LBF_BUFFER_READU32:
1374
return translateBuiltinBufferRead(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_READI32, 4, IrCmd::UINT_TO_NUM);
1375
case LBF_BUFFER_WRITEU32:
1376
return translateBuiltinBufferWrite(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_WRITEI32, 4, IrCmd::NUM_TO_UINT);
1377
case LBF_BUFFER_READF32:
1378
return translateBuiltinBufferRead(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_READF32, 4, IrCmd::FLOAT_TO_NUM);
1379
case LBF_BUFFER_WRITEF32:
1380
return translateBuiltinBufferWrite(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_WRITEF32, 4, IrCmd::NUM_TO_FLOAT);
1381
case LBF_BUFFER_READF64:
1382
return translateBuiltinBufferRead(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_READF64, 8, IrCmd::NOP);
1383
case LBF_BUFFER_WRITEF64:
1384
return translateBuiltinBufferWrite(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_WRITEF64, 8, IrCmd::NOP);
1385
case LBF_VECTOR_MAGNITUDE:
1386
return translateBuiltinVectorMagnitude(build, nparams, ra, arg, args, arg3, nresults, pcpos);
1387
case LBF_VECTOR_NORMALIZE:
1388
return translateBuiltinVectorNormalize(build, nparams, ra, arg, args, arg3, nresults, pcpos);
1389
case LBF_VECTOR_CROSS:
1390
return translateBuiltinVectorCross(build, nparams, ra, arg, args, arg3, nresults, pcpos);
1391
case LBF_VECTOR_DOT:
1392
return translateBuiltinVectorDot(build, nparams, ra, arg, args, arg3, nresults, pcpos);
1393
case LBF_VECTOR_FLOOR:
1394
return translateBuiltinVectorMap1x4(build, IrCmd::FLOOR_VEC, nparams, ra, arg, args, arg3, nresults, pcpos);
1395
case LBF_VECTOR_CEIL:
1396
return translateBuiltinVectorMap1x4(build, IrCmd::CEIL_VEC, nparams, ra, arg, args, arg3, nresults, pcpos);
1397
case LBF_VECTOR_ABS:
1398
return translateBuiltinVectorMap1x4(build, IrCmd::ABS_VEC, nparams, ra, arg, args, arg3, nresults, pcpos);
1399
case LBF_VECTOR_SIGN:
1400
return translateBuiltinVectorMap1(build, IrCmd::SIGN_FLOAT, nparams, ra, arg, args, arg3, nresults, pcpos);
1401
case LBF_VECTOR_CLAMP:
1402
return translateBuiltinVectorClamp(build, nparams, ra, arg, args, arg3, nresults, fallback, pcpos);
1403
case LBF_VECTOR_MIN:
1404
return translateBuiltinVectorMinMax(build, IrCmd::MIN_VEC, nparams, ra, arg, args, arg3, nresults, pcpos);
1405
case LBF_VECTOR_MAX:
1406
return translateBuiltinVectorMinMax(build, IrCmd::MAX_VEC, nparams, ra, arg, args, arg3, nresults, pcpos);
1407
case LBF_VECTOR_LERP:
1408
return translateBuiltinVectorLerp(build, nparams, ra, arg, args, arg3, nresults, pcpos);
1409
case LBF_MATH_LERP:
1410
return translateBuiltinMathLerp(build, nparams, ra, arg, args, arg3, nresults, fallback, pcpos);
1411
case LBF_MATH_ISNAN:
1412
return translateBuiltinMathIsNan(build, nparams, ra, arg, args, nresults, pcpos);
1413
default:
1414
return {BuiltinImplType::None, -1};
1415
}
1416
}
1417
1418
} // namespace CodeGen
1419
} // namespace Luau
1420
1421