Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/tests/IrLowering.test.cpp
2723 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
#include "lua.h"
3
#include "lualib.h"
4
#include "luacode.h"
5
6
#include "Luau/BytecodeBuilder.h"
7
#include "Luau/CodeGen.h"
8
#include "Luau/Compiler.h"
9
#include "Luau/Parser.h"
10
#include "Luau/IrBuilder.h"
11
12
#include "doctest.h"
13
#include "ScopedFlags.h"
14
#include "ConformanceIrHooks.h"
15
16
#include <memory>
17
#include <string_view>
18
19
LUAU_FASTFLAG(LuauCodegenMarkDeadRegisters2)
20
LUAU_FASTFLAG(LuauCodegenDseOnCondJump)
21
LUAU_FASTFLAG(LuauCodegenBlockSafeEnv)
22
LUAU_FASTFLAG(LuauCodegenBufNoDefTag)
23
LUAU_FASTFLAG(LuauCodegenSetBlockEntryState3)
24
LUAU_FASTFLAG(LuauCodegenGcoDse2)
25
LUAU_FASTFLAG(LuauCodegenBufferRangeMerge4)
26
LUAU_FASTFLAG(LuauCodegenRemoveDuplicateDoubleIntValues)
27
LUAU_FASTFLAG(LuauCompileExtraTypes)
28
LUAU_FASTFLAG(LuauCompileVectorReveseMul)
29
LUAU_FASTFLAG(LuauCodegenDsoPairTrackFix)
30
LUAU_FASTFLAG(LuauCodegenDsoTagOverlayFix)
31
LUAU_FASTFLAG(LuauCodegenLengthBaseInst)
32
LUAU_FASTFLAG(LuauCodegenTruncatedSubsts)
33
LUAU_FASTFLAG(LuauCodegenPropagateTagsAcrossChains2)
34
35
static void luauLibraryConstantLookup(const char* library, const char* member, Luau::CompileConstant* constant)
36
{
37
// While 'vector' library constants are a Luau built-in, their constant value depends on the embedder LUA_VECTOR_SIZE value
38
if (strcmp(library, "vector") == 0)
39
{
40
if (strcmp(member, "zero") == 0)
41
return Luau::setCompileConstantVector(constant, 0.0f, 0.0f, 0.0f, 0.0f);
42
43
if (strcmp(member, "one") == 0)
44
return Luau::setCompileConstantVector(constant, 1.0f, 1.0f, 1.0f, 0.0f);
45
}
46
47
if (strcmp(library, "Vector3") == 0)
48
{
49
if (strcmp(member, "xAxis") == 0)
50
return Luau::setCompileConstantVector(constant, 1.0f, 0.0f, 0.0f, 0.0f);
51
52
if (strcmp(member, "yAxis") == 0)
53
return Luau::setCompileConstantVector(constant, 0.0f, 1.0f, 0.0f, 0.0f);
54
}
55
56
if (strcmp(library, "test") == 0)
57
{
58
if (strcmp(member, "some_nil") == 0)
59
return luau_set_compile_constant_nil(constant);
60
61
if (strcmp(member, "some_boolean") == 0)
62
return luau_set_compile_constant_boolean(constant, 1);
63
64
if (strcmp(member, "some_number") == 0)
65
return luau_set_compile_constant_number(constant, 4.75);
66
67
if (strcmp(member, "some_vector") == 0)
68
return luau_set_compile_constant_vector(constant, 1.0f, 2.0f, 4.0f, 8.0f);
69
70
if (strcmp(member, "some_string") == 0)
71
return luau_set_compile_constant_string(constant, "test", 4);
72
}
73
}
74
75
static int luauLibraryTypeLookup(const char* library, const char* member)
76
{
77
if (strcmp(library, "Vector3") == 0)
78
{
79
if (strcmp(member, "xAxis") == 0)
80
return LuauBytecodeType::LBC_TYPE_VECTOR;
81
82
if (strcmp(member, "yAxis") == 0)
83
return LuauBytecodeType::LBC_TYPE_VECTOR;
84
}
85
86
return LuauBytecodeType::LBC_TYPE_ANY;
87
}
88
89
class LoweringFixture
90
{
91
public:
92
LoweringFixture()
93
{
94
static const char* kUserdataCompileTypes[] = {"vec2", "color", "mat3", "vertex", nullptr};
95
static const char* kLibrariesWithConstants[] = {"vector", "Vector3", "test", nullptr};
96
97
compilationOptions.optimizationLevel = 2;
98
compilationOptions.debugLevel = 1;
99
compilationOptions.typeInfoLevel = 1;
100
compilationOptions.vectorCtor = "vector";
101
compilationOptions.vectorType = "vector";
102
compilationOptions.userdataTypes = kUserdataCompileTypes;
103
compilationOptions.librariesWithKnownMembers = kLibrariesWithConstants;
104
compilationOptions.libraryMemberTypeCb = luauLibraryTypeLookup;
105
compilationOptions.libraryMemberConstantCb = luauLibraryConstantLookup;
106
107
compilationOptionsC.optimizationLevel = 2;
108
compilationOptionsC.debugLevel = 1;
109
compilationOptionsC.typeInfoLevel = 1;
110
compilationOptionsC.vectorCtor = "vector";
111
compilationOptionsC.vectorType = "vector";
112
compilationOptionsC.userdataTypes = kUserdataCompileTypes;
113
compilationOptionsC.librariesWithKnownMembers = kLibrariesWithConstants;
114
compilationOptionsC.libraryMemberTypeCb = luauLibraryTypeLookup;
115
compilationOptionsC.libraryMemberConstantCb = luauLibraryConstantLookup;
116
117
assemblyOptions.compilationOptions.hooks.vectorAccessBytecodeType = vectorAccessBytecodeType;
118
assemblyOptions.compilationOptions.hooks.vectorNamecallBytecodeType = vectorNamecallBytecodeType;
119
assemblyOptions.compilationOptions.hooks.vectorAccess = vectorAccess;
120
assemblyOptions.compilationOptions.hooks.vectorNamecall = vectorNamecall;
121
122
assemblyOptions.compilationOptions.hooks.userdataAccessBytecodeType = userdataAccessBytecodeType;
123
assemblyOptions.compilationOptions.hooks.userdataMetamethodBytecodeType = userdataMetamethodBytecodeType;
124
assemblyOptions.compilationOptions.hooks.userdataNamecallBytecodeType = userdataNamecallBytecodeType;
125
assemblyOptions.compilationOptions.hooks.userdataAccess = userdataAccess;
126
assemblyOptions.compilationOptions.hooks.userdataMetamethod = userdataMetamethod;
127
assemblyOptions.compilationOptions.hooks.userdataNamecall = userdataNamecall;
128
129
// Runtime mapping is specifically created to NOT match the compilation mapping
130
assemblyOptions.compilationOptions.userdataTypes = kUserdataRunTypes;
131
132
assemblyOptions.outputBinary = false;
133
assemblyOptions.includeAssembly = false;
134
assemblyOptions.includeIr = true;
135
assemblyOptions.includeOutlinedCode = false;
136
assemblyOptions.includeIrTypes = false;
137
138
assemblyOptions.includeIrPrefix = Luau::CodeGen::IncludeIrPrefix::No;
139
assemblyOptions.includeUseInfo = Luau::CodeGen::IncludeUseInfo::No;
140
assemblyOptions.includeCfgInfo = Luau::CodeGen::IncludeCfgInfo::No;
141
assemblyOptions.includeRegFlowInfo = Luau::CodeGen::IncludeRegFlowInfo::No;
142
}
143
144
Luau::CompileOptions compilationOptions = {};
145
lua_CompileOptions compilationOptionsC = {};
146
147
Luau::CodeGen::AssemblyOptions assemblyOptions = {};
148
149
void initializeCodegen(lua_State* L)
150
{
151
if (Luau::CodeGen::isSupported())
152
{
153
// Type remapper requires the codegen runtime
154
Luau::CodeGen::create(L);
155
156
Luau::CodeGen::setUserdataRemapper(
157
L,
158
kUserdataRunTypes,
159
[](void* context, const char* str, size_t len) -> uint8_t
160
{
161
const char** types = (const char**)context;
162
163
uint8_t index = 0;
164
165
std::string_view sv{str, len};
166
167
for (; *types; ++types)
168
{
169
if (sv == *types)
170
return index;
171
172
index++;
173
}
174
175
return 0xff;
176
}
177
);
178
}
179
}
180
181
std::string getCodegenAssembly(
182
const char* source,
183
bool includeIrTypes = false,
184
int debugLevel = 1,
185
int optimizationLevel = 2,
186
bool clipToFirstReturn = false
187
)
188
{
189
// TODO: move this into a per-test setup
190
compilationOptions.optimizationLevel = optimizationLevel;
191
compilationOptions.debugLevel = debugLevel;
192
assemblyOptions.includeIrTypes = includeIrTypes;
193
194
Luau::Allocator allocator;
195
Luau::AstNameTable names(allocator);
196
Luau::ParseResult result = Luau::Parser::parse(source, strlen(source), names, allocator);
197
198
if (!result.errors.empty())
199
throw Luau::ParseErrors(result.errors);
200
201
Luau::BytecodeBuilder bcb;
202
Luau::compileOrThrow(bcb, result, names, compilationOptions);
203
204
std::string bytecode = bcb.getBytecode();
205
std::unique_ptr<lua_State, void (*)(lua_State*)> globalState(luaL_newstate(), lua_close);
206
lua_State* L = globalState.get();
207
208
initializeCodegen(L);
209
210
if (luau_load(L, "name", bytecode.data(), bytecode.size(), 0) == 0)
211
{
212
// For IR, we don't care about assembly, but we want a stable target
213
assemblyOptions.target = Luau::CodeGen::AssemblyOptions::Target::X64_SystemV;
214
215
std::string result = Luau::CodeGen::getAssembly(L, -1, assemblyOptions, nullptr);
216
217
if (Luau::CodeGen::isSupported())
218
{
219
// Checking that other target lower correctly as well
220
assemblyOptions.target = Luau::CodeGen::AssemblyOptions::Target::A64;
221
222
Luau::CodeGen::getAssembly(L, -1, assemblyOptions, nullptr);
223
}
224
225
if (clipToFirstReturn)
226
{
227
if (auto pos = result.find("RETURN"); pos != std::string::npos)
228
{
229
if (auto newLine = result.find('\n', pos); newLine != std::string::npos)
230
return result.substr(0, newLine + 1);
231
}
232
}
233
234
return result;
235
}
236
237
FAIL("Failed to load bytecode");
238
return "";
239
}
240
241
std::string getCodegenAssemblyUsingCApi(const char* source, bool includeIrTypes = false, int debugLevel = 1)
242
{
243
// TODO: move this into a per-test setup
244
compilationOptionsC.debugLevel = debugLevel;
245
assemblyOptions.includeIrTypes = includeIrTypes;
246
247
size_t bytecodeSize = 0;
248
char* bytecode = luau_compile(source, strlen(source), &compilationOptionsC, &bytecodeSize);
249
REQUIRE(bytecode);
250
251
std::unique_ptr<lua_State, void (*)(lua_State*)> globalState(luaL_newstate(), lua_close);
252
lua_State* L = globalState.get();
253
254
initializeCodegen(L);
255
256
if (luau_load(L, "name", bytecode, bytecodeSize, 0) == 0)
257
{
258
free(bytecode);
259
260
// For IR, we don't care about assembly, but we want a stable target
261
assemblyOptions.target = Luau::CodeGen::AssemblyOptions::Target::X64_SystemV;
262
263
return Luau::CodeGen::getAssembly(L, -1, assemblyOptions, nullptr);
264
}
265
266
free(bytecode);
267
268
FAIL("Failed to load bytecode");
269
return "";
270
}
271
272
std::string getCodegenHeader(const char* source)
273
{
274
std::string assembly = getCodegenAssembly(source, /* includeIrTypes */ true, /* debugLevel */ 2);
275
276
// Skip functions until we get the last one
277
while (assembly.find("; function ", 1) != std::string::npos)
278
assembly = assembly.substr(assembly.find("; function ", 1));
279
280
auto bytecodeStart = assembly.find("bb_bytecode_0:");
281
282
if (bytecodeStart == std::string::npos)
283
bytecodeStart = assembly.find("bb_0:");
284
285
REQUIRE(bytecodeStart != std::string::npos);
286
287
return assembly.substr(0, bytecodeStart);
288
}
289
};
290
291
TEST_SUITE_BEGIN("IrLowering");
292
293
TEST_CASE_FIXTURE(LoweringFixture, "VectorReciprocal")
294
{
295
CHECK_EQ(
296
"\n" + getCodegenAssembly(R"(
297
local function vecrcp(a: vector)
298
return 1 / a
299
end
300
)"),
301
R"(
302
; function vecrcp($arg0) line 2
303
bb_0:
304
CHECK_TAG R0, tvector, exit(entry)
305
JUMP bb_2
306
bb_2:
307
JUMP bb_bytecode_1
308
bb_bytecode_1:
309
%7 = FLOAT_TO_VEC 1
310
%8 = LOAD_TVALUE R0, 0i, tvector
311
%9 = DIV_VEC %7, %8
312
%10 = TAG_VECTOR %9
313
STORE_TVALUE R1, %10
314
INTERRUPT 1u
315
RETURN R1, 1i
316
)"
317
);
318
}
319
320
TEST_CASE_FIXTURE(LoweringFixture, "VectorComponentRead")
321
{
322
CHECK_EQ(
323
"\n" + getCodegenAssembly(R"(
324
local function compsum(a: vector)
325
return a.X + a.Y + a.Z
326
end
327
)"),
328
R"(
329
; function compsum($arg0) line 2
330
bb_0:
331
CHECK_TAG R0, tvector, exit(entry)
332
JUMP bb_2
333
bb_2:
334
JUMP bb_bytecode_1
335
bb_bytecode_1:
336
%6 = LOAD_FLOAT R0, 0i
337
%7 = FLOAT_TO_NUM %6
338
%12 = LOAD_FLOAT R0, 4i
339
%13 = FLOAT_TO_NUM %12
340
%22 = ADD_NUM %7, %13
341
%27 = LOAD_FLOAT R0, 8i
342
%28 = FLOAT_TO_NUM %27
343
%37 = ADD_NUM %22, %28
344
STORE_DOUBLE R1, %37
345
STORE_TAG R1, tnumber
346
INTERRUPT 8u
347
RETURN R1, 1i
348
)"
349
);
350
}
351
352
TEST_CASE_FIXTURE(LoweringFixture, "VectorAdd")
353
{
354
CHECK_EQ(
355
"\n" + getCodegenAssembly(R"(
356
local function vec3add(a: vector, b: vector)
357
return a + b
358
end
359
)"),
360
R"(
361
; function vec3add($arg0, $arg1) line 2
362
bb_0:
363
CHECK_TAG R0, tvector, exit(entry)
364
CHECK_TAG R1, tvector, exit(entry)
365
JUMP bb_2
366
bb_2:
367
JUMP bb_bytecode_1
368
bb_bytecode_1:
369
%10 = LOAD_TVALUE R0, 0i, tvector
370
%11 = LOAD_TVALUE R1, 0i, tvector
371
%12 = ADD_VEC %10, %11
372
%13 = TAG_VECTOR %12
373
STORE_TVALUE R2, %13
374
INTERRUPT 1u
375
RETURN R2, 1i
376
)"
377
);
378
}
379
380
TEST_CASE_FIXTURE(LoweringFixture, "VectorMinus")
381
{
382
CHECK_EQ(
383
"\n" + getCodegenAssembly(R"(
384
local function vec3minus(a: vector)
385
return -a
386
end
387
)"),
388
R"(
389
; function vec3minus($arg0) line 2
390
bb_0:
391
CHECK_TAG R0, tvector, exit(entry)
392
JUMP bb_2
393
bb_2:
394
JUMP bb_bytecode_1
395
bb_bytecode_1:
396
%6 = LOAD_TVALUE R0, 0i, tvector
397
%7 = UNM_VEC %6
398
%8 = TAG_VECTOR %7
399
STORE_TVALUE R1, %8
400
INTERRUPT 1u
401
RETURN R1, 1i
402
)"
403
);
404
}
405
406
TEST_CASE_FIXTURE(LoweringFixture, "VectorSubMulDiv")
407
{
408
CHECK_EQ(
409
"\n" + getCodegenAssembly(R"(
410
local function vec3combo(a: vector, b: vector, c: vector, d: vector)
411
return a * b - c / d
412
end
413
)"),
414
R"(
415
; function vec3combo($arg0, $arg1, $arg2, $arg3) line 2
416
bb_0:
417
CHECK_TAG R0, tvector, exit(entry)
418
CHECK_TAG R1, tvector, exit(entry)
419
CHECK_TAG R2, tvector, exit(entry)
420
CHECK_TAG R3, tvector, exit(entry)
421
JUMP bb_2
422
bb_2:
423
JUMP bb_bytecode_1
424
bb_bytecode_1:
425
%14 = LOAD_TVALUE R0, 0i, tvector
426
%15 = LOAD_TVALUE R1, 0i, tvector
427
%16 = MUL_VEC %14, %15
428
%23 = LOAD_TVALUE R2, 0i, tvector
429
%24 = LOAD_TVALUE R3, 0i, tvector
430
%25 = DIV_VEC %23, %24
431
%34 = SUB_VEC %16, %25
432
%35 = TAG_VECTOR %34
433
STORE_TVALUE R4, %35
434
INTERRUPT 3u
435
RETURN R4, 1i
436
)"
437
);
438
}
439
440
TEST_CASE_FIXTURE(LoweringFixture, "VectorSubMulDiv2")
441
{
442
CHECK_EQ(
443
"\n" + getCodegenAssembly(R"(
444
local function vec3combo(a: vector)
445
local tmp = a * a
446
return (tmp - tmp) / (tmp + tmp)
447
end
448
)"),
449
R"(
450
; function vec3combo($arg0) line 2
451
bb_0:
452
CHECK_TAG R0, tvector, exit(entry)
453
JUMP bb_2
454
bb_2:
455
JUMP bb_bytecode_1
456
bb_bytecode_1:
457
%8 = LOAD_TVALUE R0, 0i, tvector
458
%10 = MUL_VEC %8, %8
459
%19 = SUB_VEC %10, %10
460
%28 = ADD_VEC %10, %10
461
%37 = DIV_VEC %19, %28
462
%38 = TAG_VECTOR %37
463
STORE_TVALUE R2, %38
464
INTERRUPT 4u
465
RETURN R2, 1i
466
)"
467
);
468
}
469
470
TEST_CASE_FIXTURE(LoweringFixture, "VectorMulDivMixed")
471
{
472
ScopedFastFlag luauCompileVectorReveseMul{FFlag::LuauCompileVectorReveseMul, true};
473
474
CHECK_EQ(
475
"\n" + getCodegenAssembly(R"(
476
local function vec3combo(a: vector, b: vector, c: vector, d: vector)
477
return a * 2 + b / 4 + 0.5 * c + 40 / d
478
end
479
)"),
480
R"(
481
; function vec3combo($arg0, $arg1, $arg2, $arg3) line 2
482
bb_0:
483
CHECK_TAG R0, tvector, exit(entry)
484
CHECK_TAG R1, tvector, exit(entry)
485
CHECK_TAG R2, tvector, exit(entry)
486
CHECK_TAG R3, tvector, exit(entry)
487
JUMP bb_2
488
bb_2:
489
JUMP bb_bytecode_1
490
bb_bytecode_1:
491
%12 = LOAD_TVALUE R0, 0i, tvector
492
%14 = FLOAT_TO_VEC 2
493
%15 = MUL_VEC %12, %14
494
%20 = LOAD_TVALUE R1, 0i, tvector
495
%22 = FLOAT_TO_VEC 4
496
%23 = DIV_VEC %20, %22
497
%32 = ADD_VEC %15, %23
498
%37 = LOAD_TVALUE R2, 0i, tvector
499
%39 = FLOAT_TO_VEC 0.5
500
%40 = MUL_VEC %37, %39
501
%49 = ADD_VEC %32, %40
502
%55 = FLOAT_TO_VEC 40
503
%56 = LOAD_TVALUE R3, 0i, tvector
504
%57 = DIV_VEC %55, %56
505
%66 = ADD_VEC %49, %57
506
%67 = TAG_VECTOR %66
507
STORE_TVALUE R4, %67
508
INTERRUPT 7u
509
RETURN R4, 1i
510
)"
511
);
512
}
513
514
TEST_CASE_FIXTURE(LoweringFixture, "VectorLerp")
515
{
516
ScopedFastFlag _[]{
517
{FFlag::LuauCodegenBlockSafeEnv, true},
518
};
519
CHECK_EQ(
520
"\n" + getCodegenAssembly(R"(
521
local function vec3lerp(a: vector, b: vector, t: number)
522
return vector.lerp(a, b, t)
523
end
524
)"),
525
R"(
526
; function vec3lerp($arg0, $arg1, $arg2) line 2
527
bb_0:
528
CHECK_TAG R0, tvector, exit(entry)
529
CHECK_TAG R1, tvector, exit(entry)
530
CHECK_TAG R2, tnumber, exit(entry)
531
JUMP bb_2
532
bb_2:
533
JUMP bb_bytecode_1
534
bb_bytecode_1:
535
implicit CHECK_SAFE_ENV exit(0)
536
%15 = LOAD_TVALUE R0, 0i, tvector
537
%16 = LOAD_TVALUE R1, 0i, tvector
538
%17 = LOAD_DOUBLE R2
539
%18 = NUM_TO_FLOAT %17
540
%19 = FLOAT_TO_VEC %18
541
%20 = FLOAT_TO_VEC 1
542
%21 = SUB_VEC %16, %15
543
MULADD_VEC %21, %19, %15
544
SELECT_VEC %22, %16, %19, %20
545
%24 = TAG_VECTOR %23
546
STORE_TVALUE R3, %24
547
INTERRUPT 8u
548
RETURN R3, 1i
549
)"
550
);
551
}
552
553
TEST_CASE_FIXTURE(LoweringFixture, "VectorMinMax")
554
{
555
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
556
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
557
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
558
559
CHECK_EQ(
560
"\n" + getCodegenAssembly(R"(
561
local function vecops(a: vector, b: vector)
562
return vector.min(a, b), vector.max(a, b)
563
end
564
)"),
565
R"(
566
; function vecops($arg0, $arg1) line 2
567
bb_0:
568
CHECK_TAG R0, tvector, exit(entry)
569
CHECK_TAG R1, tvector, exit(entry)
570
JUMP bb_2
571
bb_2:
572
JUMP bb_bytecode_1
573
bb_bytecode_1:
574
implicit CHECK_SAFE_ENV exit(0)
575
%11 = LOAD_TVALUE R0, 0i, tvector
576
%12 = LOAD_TVALUE R1, 0i, tvector
577
%13 = MIN_VEC %12, %11
578
%14 = TAG_VECTOR %13
579
STORE_TVALUE R2, %14
580
%24 = MAX_VEC %12, %11
581
%25 = TAG_VECTOR %24
582
STORE_TVALUE R3, %25
583
INTERRUPT 14u
584
RETURN R2, 2i
585
)"
586
);
587
}
588
TEST_CASE_FIXTURE(LoweringFixture, "VectorFloorCeilAbs")
589
{
590
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
591
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
592
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
593
594
CHECK_EQ(
595
"\n" + getCodegenAssembly(R"(
596
local function vecops(a: vector)
597
return vector.abs(a), vector.floor(a), vector.ceil(a)
598
end
599
)"),
600
R"(
601
; function vecops($arg0) line 2
602
bb_0:
603
CHECK_TAG R0, tvector, exit(entry)
604
JUMP bb_2
605
bb_2:
606
JUMP bb_bytecode_1
607
bb_bytecode_1:
608
implicit CHECK_SAFE_ENV exit(0)
609
%7 = LOAD_TVALUE R0, 0i, tvector
610
%8 = ABS_VEC %7
611
%9 = TAG_VECTOR %8
612
STORE_TVALUE R1, %9
613
%16 = FLOOR_VEC %7
614
%17 = TAG_VECTOR %16
615
STORE_TVALUE R2, %17
616
%24 = CEIL_VEC %7
617
%25 = TAG_VECTOR %24
618
STORE_TVALUE R3, %25
619
INTERRUPT 15u
620
RETURN R1, 3i
621
)"
622
);
623
}
624
625
TEST_CASE_FIXTURE(LoweringFixture, "ExtraMathMemoryOperands")
626
{
627
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
628
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
629
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
630
631
CHECK_EQ(
632
"\n" + getCodegenAssembly(R"(
633
local function foo(a: number, b: number, c: number, d: number, e: number)
634
return math.floor(a) + math.ceil(b) + math.round(c) + math.sqrt(d) + math.abs(e)
635
end
636
)"),
637
R"(
638
; function foo($arg0, $arg1, $arg2, $arg3, $arg4) line 2
639
bb_0:
640
CHECK_TAG R0, tnumber, exit(entry)
641
CHECK_TAG R1, tnumber, exit(entry)
642
CHECK_TAG R2, tnumber, exit(entry)
643
CHECK_TAG R3, tnumber, exit(entry)
644
CHECK_TAG R4, tnumber, exit(entry)
645
JUMP bb_2
646
bb_2:
647
JUMP bb_bytecode_1
648
bb_bytecode_1:
649
implicit CHECK_SAFE_ENV exit(0)
650
%16 = FLOOR_NUM R0
651
%24 = CEIL_NUM R1
652
%34 = ADD_NUM %16, %24
653
%41 = ROUND_NUM R2
654
%51 = ADD_NUM %34, %41
655
%58 = SQRT_NUM R3
656
%68 = ADD_NUM %51, %58
657
%75 = ABS_NUM R4
658
%85 = ADD_NUM %68, %75
659
STORE_DOUBLE R5, %85
660
STORE_TAG R5, tnumber
661
INTERRUPT 29u
662
RETURN R5, 1i
663
)"
664
);
665
}
666
667
TEST_CASE_FIXTURE(LoweringFixture, "DseInitialStackState")
668
{
669
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
670
671
CHECK_EQ(
672
"\n" + getCodegenAssembly(R"(
673
local function foo()
674
while {} do
675
local _ = not _,{}
676
_ = nil
677
end
678
end
679
)"),
680
R"(
681
; function foo() line 2
682
bb_bytecode_0:
683
SET_SAVEDPC 1u
684
%1 = NEW_TABLE 0u, 0u
685
STORE_POINTER R0, %1
686
STORE_TAG R0, ttable
687
CHECK_GC
688
JUMP bb_2
689
bb_2:
690
implicit CHECK_SAFE_ENV exit(3)
691
GET_CACHED_IMPORT R1, K1 (nil), 1073741824u ('_'), 4u
692
SET_SAVEDPC 7u
693
%14 = NEW_TABLE 0u, 0u
694
STORE_POINTER R1, %14
695
STORE_TAG R1, ttable
696
STORE_TAG R0, tnil
697
INTERRUPT 9u
698
JUMP bb_bytecode_0
699
)"
700
);
701
}
702
703
TEST_CASE_FIXTURE(LoweringFixture, "DseInitialStackState2")
704
{
705
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
706
707
CHECK_EQ(
708
"\n" + getCodegenAssembly(R"(
709
local function foo(a)
710
math.frexp(a)
711
return a
712
end
713
)"),
714
R"(
715
; function foo($arg0) line 2
716
bb_bytecode_0:
717
implicit CHECK_SAFE_ENV exit(0)
718
CHECK_TAG R0, tnumber, exit(1)
719
FASTCALL 14u, R1, R0, 2i
720
INTERRUPT 5u
721
RETURN R0, 1i
722
)"
723
);
724
}
725
726
TEST_CASE_FIXTURE(LoweringFixture, "StringCompare")
727
{
728
CHECK_EQ(
729
"\n" + getCodegenAssembly(
730
R"(
731
local function foo(a)
732
return a == "test"
733
end
734
)"
735
),
736
R"(
737
; function foo($arg0) line 2
738
bb_bytecode_0:
739
%0 = LOAD_TAG R0
740
%1 = LOAD_POINTER R0
741
%2 = LOAD_POINTER K0 ('test')
742
%3 = CMP_SPLIT_TVALUE %0, tstring, %1, %2, eq
743
STORE_TAG R1, tboolean
744
STORE_INT R1, %3
745
JUMP bb_bytecode_2
746
bb_bytecode_2:
747
INTERRUPT 4u
748
RETURN R1, 1i
749
)"
750
);
751
}
752
753
TEST_CASE_FIXTURE(LoweringFixture, "StringCompareAnnotated")
754
{
755
CHECK_EQ(
756
"\n" + getCodegenAssembly(
757
R"(
758
local function foo(a: string)
759
return a == "test"
760
end
761
)"
762
),
763
R"(
764
; function foo($arg0) line 2
765
bb_0:
766
CHECK_TAG R0, tstring, exit(entry)
767
JUMP bb_4
768
bb_4:
769
JUMP bb_bytecode_1
770
bb_bytecode_1:
771
%5 = LOAD_POINTER R0
772
%6 = LOAD_POINTER K0 ('test')
773
%7 = CMP_SPLIT_TVALUE tstring, tstring, %5, %6, eq
774
STORE_TAG R1, tboolean
775
STORE_INT R1, %7
776
JUMP bb_bytecode_3
777
bb_bytecode_3:
778
INTERRUPT 4u
779
RETURN R1, 1i
780
)"
781
);
782
}
783
784
TEST_CASE_FIXTURE(LoweringFixture, "NilCompare")
785
{
786
CHECK_EQ(
787
"\n" + getCodegenAssembly(
788
R"(
789
local function foo(a)
790
return a == nil
791
end
792
)"
793
),
794
R"(
795
; function foo($arg0) line 2
796
bb_bytecode_0:
797
%0 = LOAD_TAG R0
798
%1 = CMP_TAG %0, tnil, eq
799
STORE_TAG R1, tboolean
800
STORE_INT R1, %1
801
JUMP bb_bytecode_2
802
bb_bytecode_2:
803
INTERRUPT 4u
804
RETURN R1, 1i
805
)"
806
);
807
}
808
809
TEST_CASE_FIXTURE(LoweringFixture, "BooleanCompare")
810
{
811
CHECK_EQ(
812
"\n" + getCodegenAssembly(
813
R"(
814
local function foo(a)
815
return { a == true, a == false, a ~= true, a ~= false }
816
end
817
)"
818
),
819
R"(
820
; function foo($arg0) line 2
821
bb_bytecode_0:
822
SET_SAVEDPC 1u
823
%1 = NEW_TABLE 4u, 0u
824
STORE_POINTER R1, %1
825
STORE_TAG R1, ttable
826
CHECK_GC
827
%5 = LOAD_TAG R0
828
%6 = LOAD_INT R0
829
%7 = CMP_SPLIT_TVALUE %5, tboolean, %6, 1i, eq
830
STORE_TAG R2, tboolean
831
STORE_INT R2, %7
832
JUMP bb_bytecode_2
833
bb_bytecode_2:
834
%16 = CMP_SPLIT_TVALUE %5, tboolean, %6, 0i, eq
835
STORE_TAG R3, tboolean
836
STORE_INT R3, %16
837
JUMP bb_bytecode_4
838
bb_bytecode_4:
839
%25 = CMP_SPLIT_TVALUE %5, tboolean, %6, 1i, not_eq
840
STORE_TAG R4, tboolean
841
STORE_INT R4, %25
842
JUMP bb_bytecode_6
843
bb_bytecode_6:
844
%34 = CMP_SPLIT_TVALUE %5, tboolean, %6, 0i, not_eq
845
STORE_TAG R5, tboolean
846
STORE_INT R5, %34
847
JUMP bb_bytecode_8
848
bb_bytecode_8:
849
SETLIST 18u, R1, R2, 4i, 1u, 4u
850
INTERRUPT 20u
851
RETURN R1, 1i
852
)"
853
);
854
}
855
856
TEST_CASE_FIXTURE(LoweringFixture, "NumberCompare")
857
{
858
CHECK_EQ(
859
"\n" + getCodegenAssembly(
860
R"(
861
local function foo(a)
862
return { a == 4.0, a ~= 3.0 }
863
end
864
)"
865
),
866
R"(
867
; function foo($arg0) line 2
868
bb_bytecode_0:
869
SET_SAVEDPC 1u
870
%1 = NEW_TABLE 2u, 0u
871
STORE_POINTER R1, %1
872
STORE_TAG R1, ttable
873
CHECK_GC
874
%5 = LOAD_TAG R0
875
%6 = LOAD_DOUBLE R0
876
%7 = CMP_SPLIT_TVALUE %5, tnumber, %6, 4, eq
877
STORE_TAG R2, tboolean
878
STORE_INT R2, %7
879
JUMP bb_bytecode_2
880
bb_bytecode_2:
881
%16 = CMP_SPLIT_TVALUE %5, tnumber, %6, 3, not_eq
882
STORE_TAG R3, tboolean
883
STORE_INT R3, %16
884
JUMP bb_bytecode_4
885
bb_bytecode_4:
886
SETLIST 10u, R1, R2, 2i, 1u, 2u
887
INTERRUPT 12u
888
RETURN R1, 1i
889
)"
890
);
891
}
892
893
TEST_CASE_FIXTURE(LoweringFixture, "NumberCompare2")
894
{
895
CHECK_EQ(
896
"\n" + getCodegenAssembly(
897
R"(
898
local function foo(a, b, c)
899
return { a == b, a ~= c }
900
end
901
)"
902
),
903
R"(
904
; function foo($arg0, $arg1, $arg2) line 2
905
bb_bytecode_0:
906
SET_SAVEDPC 1u
907
%1 = NEW_TABLE 2u, 0u
908
STORE_POINTER R3, %1
909
STORE_TAG R3, ttable
910
CHECK_GC
911
CHECK_TAG R0, tnumber, bb_fallback_5
912
CHECK_TAG R1, tnumber, bb_fallback_5
913
%9 = LOAD_DOUBLE R0
914
%10 = LOAD_DOUBLE R1
915
%11 = CMP_SPLIT_TVALUE tnumber, tnumber, %9, %10, eq
916
STORE_INT R4, %11
917
STORE_TAG R4, tboolean
918
JUMP bb_bytecode_2
919
bb_bytecode_2:
920
CHECK_TAG R0, tnumber, bb_fallback_6
921
CHECK_TAG R2, tnumber, bb_fallback_6
922
%27 = LOAD_DOUBLE R0
923
%28 = LOAD_DOUBLE R2
924
%29 = CMP_SPLIT_TVALUE tnumber, tnumber, %27, %28, not_eq
925
STORE_INT R5, %29
926
STORE_TAG R5, tboolean
927
JUMP bb_bytecode_4
928
bb_bytecode_4:
929
SETLIST 10u, R3, R4, 2i, 1u, undef
930
INTERRUPT 12u
931
RETURN R3, 1i
932
)"
933
);
934
}
935
936
TEST_CASE_FIXTURE(LoweringFixture, "NumberCompare3")
937
{
938
CHECK_EQ(
939
"\n" + getCodegenAssembly(
940
R"(
941
local function foo(a: number, b: number, c: number)
942
return { a == b, a ~= c }
943
end
944
)"
945
),
946
R"(
947
; function foo($arg0, $arg1, $arg2) line 2
948
bb_0:
949
CHECK_TAG R0, tnumber, exit(entry)
950
CHECK_TAG R1, tnumber, exit(entry)
951
CHECK_TAG R2, tnumber, exit(entry)
952
JUMP bb_6
953
bb_6:
954
JUMP bb_bytecode_1
955
bb_bytecode_1:
956
SET_SAVEDPC 1u
957
%9 = NEW_TABLE 2u, 0u
958
STORE_POINTER R3, %9
959
STORE_TAG R3, ttable
960
CHECK_GC
961
%17 = LOAD_DOUBLE R0
962
%18 = LOAD_DOUBLE R1
963
%19 = CMP_SPLIT_TVALUE tnumber, tnumber, %17, %18, eq
964
STORE_INT R4, %19
965
STORE_TAG R4, tboolean
966
JUMP bb_bytecode_3
967
bb_bytecode_3:
968
%31 = LOAD_DOUBLE R2
969
%32 = CMP_SPLIT_TVALUE tnumber, tnumber, %17, %31, not_eq
970
STORE_INT R5, %32
971
STORE_TAG R5, tboolean
972
JUMP bb_bytecode_5
973
bb_bytecode_5:
974
SETLIST 10u, R3, R4, 2i, 1u, 2u
975
INTERRUPT 12u
976
RETURN R3, 1i
977
)"
978
);
979
}
980
981
TEST_CASE_FIXTURE(LoweringFixture, "TypeCompare")
982
{
983
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
984
ScopedFastFlag luauCodegenGcoDse{FFlag::LuauCodegenGcoDse2, true};
985
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
986
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
987
988
CHECK_EQ(
989
"\n" + getCodegenAssembly(
990
R"(
991
local function foo(a)
992
return type(a) == "number"
993
end
994
)"
995
),
996
R"(
997
; function foo($arg0) line 2
998
bb_bytecode_0:
999
implicit CHECK_SAFE_ENV exit(0)
1000
%1 = LOAD_TAG R0
1001
%9 = CMP_TAG %1, tnumber, eq
1002
STORE_TAG R1, tboolean
1003
STORE_INT R1, %9
1004
JUMP bb_bytecode_2
1005
bb_bytecode_2:
1006
INTERRUPT 9u
1007
RETURN R1, 1i
1008
)"
1009
);
1010
}
1011
1012
TEST_CASE_FIXTURE(LoweringFixture, "TypeofCompare")
1013
{
1014
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
1015
ScopedFastFlag luauCodegenGcoDse{FFlag::LuauCodegenGcoDse2, true};
1016
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
1017
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
1018
1019
CHECK_EQ(
1020
"\n" + getCodegenAssembly(
1021
R"(
1022
local function foo(a)
1023
return typeof(a) == "number"
1024
end
1025
)"
1026
),
1027
R"(
1028
; function foo($arg0) line 2
1029
bb_bytecode_0:
1030
implicit CHECK_SAFE_ENV exit(0)
1031
%8 = CMP_TAG R0, tnumber, eq
1032
STORE_TAG R1, tboolean
1033
STORE_INT R1, %8
1034
JUMP bb_bytecode_2
1035
bb_bytecode_2:
1036
INTERRUPT 9u
1037
RETURN R1, 1i
1038
)"
1039
);
1040
}
1041
1042
TEST_CASE_FIXTURE(LoweringFixture, "TypeofCompareCustom")
1043
{
1044
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
1045
ScopedFastFlag luauCodegenGcoDse{FFlag::LuauCodegenGcoDse2, true};
1046
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
1047
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
1048
1049
CHECK_EQ(
1050
"\n" + getCodegenAssembly(
1051
R"(
1052
local function foo(a)
1053
return typeof(a) == "User"
1054
end
1055
)"
1056
),
1057
R"(
1058
; function foo($arg0) line 2
1059
bb_bytecode_0:
1060
implicit CHECK_SAFE_ENV exit(0)
1061
%1 = GET_TYPEOF R0
1062
%7 = LOAD_POINTER K2 ('User')
1063
%8 = CMP_SPLIT_TVALUE tstring, tstring, %1, %7, eq
1064
STORE_TAG R1, tboolean
1065
STORE_INT R1, %8
1066
JUMP bb_bytecode_2
1067
bb_bytecode_2:
1068
INTERRUPT 9u
1069
RETURN R1, 1i
1070
)"
1071
);
1072
}
1073
1074
TEST_CASE_FIXTURE(LoweringFixture, "TypeCondition")
1075
{
1076
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
1077
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
1078
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
1079
1080
// TODO: opportunity - bb_4 already made sure %1 == R0.tag is a number, check in bb_3 can be removed
1081
CHECK_EQ(
1082
"\n" + getCodegenAssembly(
1083
R"(
1084
local function foo(a, b)
1085
if type(a) == "number" then
1086
return a + b
1087
end
1088
return nil
1089
end
1090
)"
1091
),
1092
R"(
1093
; function foo($arg0, $arg1) line 2
1094
bb_bytecode_0:
1095
implicit CHECK_SAFE_ENV exit(0)
1096
JUMP bb_4
1097
bb_4:
1098
JUMP_EQ_TAG R0, tnumber, bb_3, bb_bytecode_1
1099
bb_3:
1100
CHECK_TAG R0, tnumber, bb_fallback_5
1101
CHECK_TAG R1, tnumber, bb_fallback_5
1102
%15 = LOAD_DOUBLE R0
1103
%17 = ADD_NUM %15, R1
1104
STORE_DOUBLE R2, %17
1105
STORE_TAG R2, tnumber
1106
JUMP bb_6
1107
bb_6:
1108
INTERRUPT 8u
1109
RETURN R2, 1i
1110
bb_bytecode_1:
1111
STORE_TAG R2, tnil
1112
INTERRUPT 10u
1113
RETURN R2, 1i
1114
)"
1115
);
1116
}
1117
1118
TEST_CASE_FIXTURE(LoweringFixture, "TypeCondition2")
1119
{
1120
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
1121
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
1122
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
1123
1124
// TODO: opportunity - bb_4 already made sure env is safe, check in bb_3 can be removed
1125
CHECK_EQ(
1126
"\n" + getCodegenAssembly(
1127
R"(
1128
local function foo(a, b)
1129
if type(a) == "number" and type(b) == "number" then
1130
return a + b
1131
end
1132
return nil
1133
end
1134
)"
1135
),
1136
R"(
1137
; function foo($arg0, $arg1) line 2
1138
bb_bytecode_0:
1139
implicit CHECK_SAFE_ENV exit(0)
1140
JUMP bb_4
1141
bb_4:
1142
JUMP_EQ_TAG R0, tnumber, bb_3, bb_bytecode_1
1143
bb_3:
1144
implicit CHECK_SAFE_ENV exit(7)
1145
JUMP bb_7
1146
bb_7:
1147
JUMP_EQ_TAG R1, tnumber, bb_6, bb_bytecode_1
1148
bb_6:
1149
CHECK_TAG R0, tnumber, bb_fallback_8
1150
CHECK_TAG R1, tnumber, bb_fallback_8
1151
%26 = LOAD_DOUBLE R0
1152
%28 = ADD_NUM %26, R1
1153
STORE_DOUBLE R2, %28
1154
STORE_TAG R2, tnumber
1155
JUMP bb_9
1156
bb_9:
1157
INTERRUPT 15u
1158
RETURN R2, 1i
1159
bb_bytecode_1:
1160
STORE_TAG R2, tnil
1161
INTERRUPT 17u
1162
RETURN R2, 1i
1163
)"
1164
);
1165
}
1166
1167
TEST_CASE_FIXTURE(LoweringFixture, "AssertTypeGuard")
1168
{
1169
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
1170
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
1171
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
1172
1173
// TODO: opportunity - CHECK_TRUTHY indirectly establishes that %1 is a number for CHECK_TAG in bb_5
1174
CHECK_EQ(
1175
"\n" + getCodegenAssembly(
1176
R"(
1177
local function foo(a)
1178
assert(type(a) == "number")
1179
return a * 2
1180
end
1181
)"
1182
),
1183
R"(
1184
; function foo($arg0) line 2
1185
bb_bytecode_0:
1186
implicit CHECK_SAFE_ENV exit(0)
1187
%1 = LOAD_TAG R0
1188
%2 = GET_TYPE %1
1189
STORE_POINTER R3, %2
1190
STORE_TAG R3, tstring
1191
%9 = CMP_TAG %1, tnumber, eq
1192
STORE_TAG R2, tboolean
1193
STORE_INT R2, %9
1194
JUMP bb_bytecode_2
1195
bb_bytecode_2:
1196
CHECK_TRUTHY tboolean, %9, exit(10)
1197
JUMP bb_5
1198
bb_5:
1199
CHECK_TAG %1, tnumber, bb_fallback_6
1200
%30 = LOAD_DOUBLE R0
1201
%31 = ADD_NUM %30, %30
1202
STORE_DOUBLE R1, %31
1203
STORE_TAG R1, tnumber
1204
JUMP bb_7
1205
bb_7:
1206
INTERRUPT 14u
1207
RETURN R1, 1i
1208
)"
1209
);
1210
}
1211
1212
TEST_CASE_FIXTURE(LoweringFixture, "VectorConstantTag")
1213
{
1214
CHECK_EQ(
1215
"\n" + getCodegenAssembly(R"(
1216
local function vecrcp(a: vector)
1217
return vector(1, 2, 3) + a
1218
end
1219
)"),
1220
R"(
1221
; function vecrcp($arg0) line 2
1222
bb_0:
1223
CHECK_TAG R0, tvector, exit(entry)
1224
JUMP bb_2
1225
bb_2:
1226
JUMP bb_bytecode_1
1227
bb_bytecode_1:
1228
%4 = LOAD_TVALUE K0 (1, 2, 3), 0i, tvector
1229
%11 = LOAD_TVALUE R0, 0i, tvector
1230
%12 = ADD_VEC %4, %11
1231
%13 = TAG_VECTOR %12
1232
STORE_TVALUE R1, %13
1233
INTERRUPT 2u
1234
RETURN R1, 1i
1235
)"
1236
);
1237
}
1238
1239
TEST_CASE_FIXTURE(LoweringFixture, "VectorNamecall")
1240
{
1241
CHECK_EQ(
1242
"\n" + getCodegenAssembly(R"(
1243
local function abs(a: vector)
1244
return a:Abs()
1245
end
1246
)"),
1247
R"(
1248
; function abs($arg0) line 2
1249
bb_0:
1250
CHECK_TAG R0, tvector, exit(entry)
1251
JUMP bb_2
1252
bb_2:
1253
JUMP bb_bytecode_1
1254
bb_bytecode_1:
1255
FALLBACK_NAMECALL 0u, R1, R0, K0 ('Abs')
1256
INTERRUPT 2u
1257
SET_SAVEDPC 3u
1258
CALL R1, 1i, -1i
1259
INTERRUPT 3u
1260
RETURN R1, -1i
1261
)"
1262
);
1263
}
1264
1265
TEST_CASE_FIXTURE(LoweringFixture, "VectorRandomProp")
1266
{
1267
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
1268
1269
CHECK_EQ(
1270
"\n" + getCodegenAssembly(R"(
1271
local function foo(a: vector)
1272
return a.XX + a.YY + a.ZZ
1273
end
1274
)"),
1275
R"(
1276
; function foo($arg0) line 2
1277
bb_0:
1278
CHECK_TAG R0, tvector, exit(entry)
1279
JUMP bb_2
1280
bb_2:
1281
JUMP bb_bytecode_1
1282
bb_bytecode_1:
1283
FALLBACK_GETTABLEKS 0u, R3, R0, K0 ('XX')
1284
FALLBACK_GETTABLEKS 2u, R4, R0, K1 ('YY')
1285
CHECK_TAG R3, tnumber, bb_fallback_3
1286
CHECK_TAG R4, tnumber, bb_fallback_3
1287
%14 = LOAD_DOUBLE R3
1288
%16 = ADD_NUM %14, R4
1289
STORE_DOUBLE R2, %16
1290
STORE_TAG R2, tnumber
1291
JUMP bb_4
1292
bb_4:
1293
FALLBACK_GETTABLEKS 5u, R3, R0, K2 ('ZZ')
1294
CHECK_TAG R2, tnumber, bb_fallback_5
1295
CHECK_TAG R3, tnumber, bb_fallback_5
1296
%30 = LOAD_DOUBLE R2
1297
%32 = ADD_NUM %30, R3
1298
STORE_DOUBLE R1, %32
1299
STORE_TAG R1, tnumber
1300
JUMP bb_6
1301
bb_6:
1302
INTERRUPT 8u
1303
RETURN R1, 1i
1304
)"
1305
);
1306
}
1307
1308
TEST_CASE_FIXTURE(LoweringFixture, "VectorCustomAccess")
1309
{
1310
CHECK_EQ(
1311
"\n" + getCodegenAssembly(R"(
1312
local function vec3magn(a: vector)
1313
return a.Magnitude * 3
1314
end
1315
)"),
1316
R"(
1317
; function vec3magn($arg0) line 2
1318
bb_0:
1319
CHECK_TAG R0, tvector, exit(entry)
1320
JUMP bb_2
1321
bb_2:
1322
JUMP bb_bytecode_1
1323
bb_bytecode_1:
1324
%6 = LOAD_FLOAT R0, 0i
1325
%7 = LOAD_FLOAT R0, 4i
1326
%8 = LOAD_FLOAT R0, 8i
1327
%9 = MUL_FLOAT %6, %6
1328
%10 = MUL_FLOAT %7, %7
1329
%11 = MUL_FLOAT %8, %8
1330
%12 = ADD_FLOAT %9, %10
1331
%13 = ADD_FLOAT %12, %11
1332
%14 = SQRT_FLOAT %13
1333
%15 = FLOAT_TO_NUM %14
1334
%21 = MUL_NUM %15, 3
1335
STORE_DOUBLE R1, %21
1336
STORE_TAG R1, tnumber
1337
INTERRUPT 3u
1338
RETURN R1, 1i
1339
)"
1340
);
1341
}
1342
1343
TEST_CASE_FIXTURE(LoweringFixture, "VectorCustomNamecall")
1344
{
1345
CHECK_EQ(
1346
"\n" + getCodegenAssembly(R"(
1347
local function vec3dot(a: vector, b: vector)
1348
return (a:Dot(b))
1349
end
1350
)"),
1351
R"(
1352
; function vec3dot($arg0, $arg1) line 2
1353
bb_0:
1354
CHECK_TAG R0, tvector, exit(entry)
1355
CHECK_TAG R1, tvector, exit(entry)
1356
JUMP bb_2
1357
bb_2:
1358
JUMP bb_bytecode_1
1359
bb_bytecode_1:
1360
%6 = LOAD_TVALUE R1, 0i, tvector
1361
%12 = LOAD_FLOAT R0, 0i
1362
%13 = EXTRACT_VEC %6, 0i
1363
%14 = MUL_FLOAT %12, %13
1364
%15 = LOAD_FLOAT R0, 4i
1365
%16 = EXTRACT_VEC %6, 1i
1366
%17 = MUL_FLOAT %15, %16
1367
%18 = LOAD_FLOAT R0, 8i
1368
%19 = EXTRACT_VEC %6, 2i
1369
%20 = MUL_FLOAT %18, %19
1370
%21 = ADD_FLOAT %14, %17
1371
%22 = ADD_FLOAT %21, %20
1372
%23 = FLOAT_TO_NUM %22
1373
STORE_DOUBLE R2, %23
1374
STORE_TAG R2, tnumber
1375
INTERRUPT 4u
1376
RETURN R2, 1i
1377
)"
1378
);
1379
}
1380
1381
TEST_CASE_FIXTURE(LoweringFixture, "VectorCustomNamecall2")
1382
{
1383
CHECK_EQ(
1384
"\n" + getCodegenAssembly(R"(
1385
local function vec3dot(a: vector)
1386
return (a:Dot(vector.create(1, 2, 3)))
1387
end
1388
)"),
1389
R"(
1390
; function vec3dot($arg0) line 2
1391
bb_0:
1392
CHECK_TAG R0, tvector, exit(entry)
1393
JUMP bb_2
1394
bb_2:
1395
JUMP bb_bytecode_1
1396
bb_bytecode_1:
1397
%10 = LOAD_FLOAT R0, 0i
1398
%13 = LOAD_FLOAT R0, 4i
1399
%15 = ADD_FLOAT %13, %13
1400
%16 = LOAD_FLOAT R0, 8i
1401
%18 = MUL_FLOAT %16, 3
1402
%19 = ADD_FLOAT %10, %15
1403
%20 = ADD_FLOAT %19, %18
1404
%21 = FLOAT_TO_NUM %20
1405
STORE_DOUBLE R1, %21
1406
STORE_TAG R1, tnumber
1407
INTERRUPT 4u
1408
RETURN R1, 1i
1409
)"
1410
);
1411
}
1412
1413
TEST_CASE_FIXTURE(LoweringFixture, "VectorCustomAccessChain")
1414
{
1415
CHECK_EQ(
1416
"\n" + getCodegenAssembly(R"(
1417
local function foo(a: vector, b: vector)
1418
return a.Unit * b.Magnitude
1419
end
1420
)"),
1421
R"(
1422
; function foo($arg0, $arg1) line 2
1423
bb_0:
1424
CHECK_TAG R0, tvector, exit(entry)
1425
CHECK_TAG R1, tvector, exit(entry)
1426
JUMP bb_2
1427
bb_2:
1428
JUMP bb_bytecode_1
1429
bb_bytecode_1:
1430
%8 = LOAD_FLOAT R0, 0i
1431
%9 = LOAD_FLOAT R0, 4i
1432
%10 = LOAD_FLOAT R0, 8i
1433
%11 = MUL_FLOAT %8, %8
1434
%12 = MUL_FLOAT %9, %9
1435
%13 = MUL_FLOAT %10, %10
1436
%14 = ADD_FLOAT %11, %12
1437
%15 = ADD_FLOAT %14, %13
1438
%16 = SQRT_FLOAT %15
1439
%17 = DIV_FLOAT 1, %16
1440
%18 = MUL_FLOAT %8, %17
1441
%19 = MUL_FLOAT %9, %17
1442
%20 = MUL_FLOAT %10, %17
1443
STORE_VECTOR R3, %18, %19, %20
1444
STORE_TAG R3, tvector
1445
%25 = LOAD_FLOAT R1, 0i
1446
%26 = LOAD_FLOAT R1, 4i
1447
%27 = LOAD_FLOAT R1, 8i
1448
%28 = MUL_FLOAT %25, %25
1449
%29 = MUL_FLOAT %26, %26
1450
%30 = MUL_FLOAT %27, %27
1451
%31 = ADD_FLOAT %28, %29
1452
%32 = ADD_FLOAT %31, %30
1453
%33 = SQRT_FLOAT %32
1454
%41 = LOAD_TVALUE R3, 0i, tvector
1455
%44 = FLOAT_TO_VEC %33
1456
%45 = MUL_VEC %41, %44
1457
%46 = TAG_VECTOR %45
1458
STORE_TVALUE R2, %46
1459
INTERRUPT 5u
1460
RETURN R2, 1i
1461
)"
1462
);
1463
}
1464
1465
TEST_CASE_FIXTURE(LoweringFixture, "VectorCustomNamecallChain")
1466
{
1467
CHECK_EQ(
1468
"\n" + getCodegenAssembly(R"(
1469
local function foo(n: vector, b: vector, t: vector)
1470
return n:Cross(t):Dot(b) + 1
1471
end
1472
)"),
1473
R"(
1474
; function foo($arg0, $arg1, $arg2) line 2
1475
bb_0:
1476
CHECK_TAG R0, tvector, exit(entry)
1477
CHECK_TAG R1, tvector, exit(entry)
1478
CHECK_TAG R2, tvector, exit(entry)
1479
JUMP bb_2
1480
bb_2:
1481
JUMP bb_bytecode_1
1482
bb_bytecode_1:
1483
%8 = LOAD_TVALUE R2, 0i, tvector
1484
%14 = LOAD_FLOAT R0, 0i
1485
%15 = EXTRACT_VEC %8, 0i
1486
%16 = LOAD_FLOAT R0, 4i
1487
%17 = EXTRACT_VEC %8, 1i
1488
%18 = LOAD_FLOAT R0, 8i
1489
%19 = EXTRACT_VEC %8, 2i
1490
%20 = MUL_FLOAT %16, %19
1491
%21 = MUL_FLOAT %18, %17
1492
%22 = SUB_FLOAT %20, %21
1493
%23 = MUL_FLOAT %18, %15
1494
%24 = MUL_FLOAT %14, %19
1495
%25 = SUB_FLOAT %23, %24
1496
%26 = MUL_FLOAT %14, %17
1497
%27 = MUL_FLOAT %16, %15
1498
%28 = SUB_FLOAT %26, %27
1499
STORE_VECTOR R4, %22, %25, %28
1500
STORE_TAG R4, tvector
1501
%31 = LOAD_TVALUE R1, 0i, tvector
1502
%37 = LOAD_FLOAT R4, 0i
1503
%38 = EXTRACT_VEC %31, 0i
1504
%39 = MUL_FLOAT %37, %38
1505
%40 = LOAD_FLOAT R4, 4i
1506
%41 = EXTRACT_VEC %31, 1i
1507
%42 = MUL_FLOAT %40, %41
1508
%43 = LOAD_FLOAT R4, 8i
1509
%44 = EXTRACT_VEC %31, 2i
1510
%45 = MUL_FLOAT %43, %44
1511
%46 = ADD_FLOAT %39, %42
1512
%47 = ADD_FLOAT %46, %45
1513
%48 = FLOAT_TO_NUM %47
1514
%54 = ADD_NUM %48, 1
1515
STORE_DOUBLE R3, %54
1516
STORE_TAG R3, tnumber
1517
INTERRUPT 9u
1518
RETURN R3, 1i
1519
)"
1520
);
1521
}
1522
1523
TEST_CASE_FIXTURE(LoweringFixture, "VectorCustomNamecallChain2")
1524
{
1525
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
1526
1527
CHECK_EQ(
1528
"\n" + getCodegenAssembly(R"(
1529
type Vertex = {n: vector, b: vector}
1530
1531
local function foo(v: Vertex, t: vector)
1532
return v.n:Cross(t):Dot(v.b) + 1
1533
end
1534
)"),
1535
R"(
1536
; function foo($arg0, $arg1) line 4
1537
bb_0:
1538
CHECK_TAG R0, ttable, exit(entry)
1539
CHECK_TAG R1, tvector, exit(entry)
1540
JUMP bb_2
1541
bb_2:
1542
JUMP bb_bytecode_1
1543
bb_bytecode_1:
1544
%8 = LOAD_POINTER R0
1545
%9 = GET_SLOT_NODE_ADDR %8, 0u, K1 ('n')
1546
CHECK_SLOT_MATCH %9, K1 ('n'), bb_fallback_3
1547
%11 = LOAD_TVALUE %9, 0i
1548
STORE_TVALUE R3, %11
1549
JUMP bb_4
1550
bb_4:
1551
%16 = LOAD_TVALUE R1, 0i, tvector
1552
STORE_TVALUE R5, %16
1553
CHECK_TAG R3, tvector, exit(3)
1554
%22 = LOAD_FLOAT R3, 0i
1555
%23 = EXTRACT_VEC %16, 0i
1556
%24 = LOAD_FLOAT R3, 4i
1557
%25 = EXTRACT_VEC %16, 1i
1558
%26 = LOAD_FLOAT R3, 8i
1559
%27 = EXTRACT_VEC %16, 2i
1560
%28 = MUL_FLOAT %24, %27
1561
%29 = MUL_FLOAT %26, %25
1562
%30 = SUB_FLOAT %28, %29
1563
%31 = MUL_FLOAT %26, %23
1564
%32 = MUL_FLOAT %22, %27
1565
%33 = SUB_FLOAT %31, %32
1566
%34 = MUL_FLOAT %22, %25
1567
%35 = MUL_FLOAT %24, %23
1568
%36 = SUB_FLOAT %34, %35
1569
STORE_VECTOR R3, %30, %33, %36
1570
%41 = LOAD_POINTER R0
1571
%42 = GET_SLOT_NODE_ADDR %41, 6u, K3 ('b')
1572
CHECK_SLOT_MATCH %42, K3 ('b'), bb_fallback_5
1573
%44 = LOAD_TVALUE %42, 0i
1574
STORE_TVALUE R5, %44
1575
JUMP bb_6
1576
bb_6:
1577
CHECK_TAG R3, tvector, exit(8)
1578
CHECK_TAG R5, tvector, exit(8)
1579
%53 = LOAD_FLOAT R3, 0i
1580
%54 = LOAD_FLOAT R5, 0i
1581
%55 = MUL_FLOAT %53, %54
1582
%56 = LOAD_FLOAT R3, 4i
1583
%57 = LOAD_FLOAT R5, 4i
1584
%58 = MUL_FLOAT %56, %57
1585
%59 = LOAD_FLOAT R3, 8i
1586
%60 = LOAD_FLOAT R5, 8i
1587
%61 = MUL_FLOAT %59, %60
1588
%62 = ADD_FLOAT %55, %58
1589
%63 = ADD_FLOAT %62, %61
1590
%64 = FLOAT_TO_NUM %63
1591
%70 = ADD_NUM %64, 1
1592
STORE_DOUBLE R2, %70
1593
STORE_TAG R2, tnumber
1594
INTERRUPT 12u
1595
RETURN R2, 1i
1596
)"
1597
);
1598
}
1599
1600
TEST_CASE_FIXTURE(LoweringFixture, "VectorLoadFloatPropagation")
1601
{
1602
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
1603
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
1604
1605
CHECK_EQ(
1606
"\n" + getCodegenAssembly(R"(
1607
local function foo(t: vector)
1608
t = t * 2
1609
return vector.create(t.x, t.y, t.x)
1610
end
1611
)"),
1612
R"(
1613
; function foo($arg0) line 2
1614
bb_0:
1615
CHECK_TAG R0, tvector, exit(entry)
1616
JUMP bb_2
1617
bb_2:
1618
JUMP bb_bytecode_1
1619
bb_bytecode_1:
1620
implicit CHECK_SAFE_ENV exit(0)
1621
%6 = LOAD_TVALUE R0, 0i, tvector
1622
%8 = FLOAT_TO_VEC 2
1623
%9 = MUL_VEC %6, %8
1624
%14 = EXTRACT_VEC %9, 0i
1625
%20 = EXTRACT_VEC %9, 1i
1626
STORE_VECTOR R1, %14, %20, %14
1627
STORE_TAG R1, tvector
1628
INTERRUPT 11u
1629
RETURN R1, 1i
1630
)"
1631
);
1632
}
1633
1634
TEST_CASE_FIXTURE(LoweringFixture, "VectorLibraryChain")
1635
{
1636
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
1637
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
1638
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
1639
1640
CHECK_EQ(
1641
"\n" + getCodegenAssembly(R"(
1642
local function foo(a: vector, b: vector)
1643
return vector.normalize(a) * (vector.magnitude(b) + vector.dot(a, b))
1644
end
1645
)"),
1646
R"(
1647
; function foo($arg0, $arg1) line 2
1648
bb_0:
1649
CHECK_TAG R0, tvector, exit(entry)
1650
CHECK_TAG R1, tvector, exit(entry)
1651
JUMP bb_2
1652
bb_2:
1653
JUMP bb_bytecode_1
1654
bb_bytecode_1:
1655
implicit CHECK_SAFE_ENV exit(0)
1656
%9 = LOAD_TVALUE R0, 0i, tvector
1657
%10 = DOT_VEC %9, %9
1658
%11 = SQRT_FLOAT %10
1659
%12 = DIV_FLOAT 1, %11
1660
%13 = FLOAT_TO_VEC %12
1661
%14 = MUL_VEC %9, %13
1662
%21 = LOAD_TVALUE R1, 0i, tvector
1663
%22 = DOT_VEC %21, %21
1664
%23 = SQRT_FLOAT %22
1665
%24 = FLOAT_TO_NUM %23
1666
%35 = DOT_VEC %9, %21
1667
%36 = FLOAT_TO_NUM %35
1668
%46 = ADD_NUM %24, %36
1669
%55 = NUM_TO_FLOAT %46
1670
%56 = FLOAT_TO_VEC %55
1671
%57 = MUL_VEC %14, %56
1672
%58 = TAG_VECTOR %57
1673
STORE_TVALUE R2, %58
1674
INTERRUPT 19u
1675
RETURN R2, 1i
1676
)"
1677
);
1678
}
1679
1680
TEST_CASE_FIXTURE(LoweringFixture, "VectorIdiv")
1681
{
1682
CHECK_EQ(
1683
"\n" + getCodegenAssembly(
1684
R"(
1685
local function foo(x: vector): vector
1686
x *= 1.5
1687
x -= x // 1
1688
x -= vector.create(0.5, 0.5, 0.5)
1689
return x
1690
end
1691
)"
1692
),
1693
R"(
1694
; function foo($arg0) line 2
1695
bb_0:
1696
CHECK_TAG R0, tvector, exit(entry)
1697
JUMP bb_2
1698
bb_2:
1699
JUMP bb_bytecode_1
1700
bb_bytecode_1:
1701
%6 = LOAD_TVALUE R0, 0i, tvector
1702
%8 = FLOAT_TO_VEC 1.5
1703
%9 = MUL_VEC %6, %8
1704
%16 = FLOAT_TO_VEC 1
1705
%17 = IDIV_VEC %9, %16
1706
%26 = SUB_VEC %9, %17
1707
%29 = LOAD_TVALUE K2 (0.5, 0.5, 0.5), 0i, tvector
1708
%37 = SUB_VEC %26, %29
1709
%38 = TAG_VECTOR %37
1710
STORE_TVALUE R0, %38
1711
INTERRUPT 5u
1712
RETURN R0, 1i
1713
)"
1714
);
1715
}
1716
1717
TEST_CASE_FIXTURE(LoweringFixture, "VectorNumberMixed1")
1718
{
1719
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
1720
1721
CHECK_EQ(
1722
"\n" + getCodegenAssembly(
1723
R"(
1724
local function foo(vectors: {vector}, i)
1725
local t = i / 100
1726
return vectors[i] * (1 - t)
1727
end
1728
)",
1729
false,
1730
1,
1731
2,
1732
true
1733
),
1734
R"(
1735
; function foo($arg0, $arg1) line 2
1736
bb_0:
1737
CHECK_TAG R0, ttable, exit(entry)
1738
JUMP bb_2
1739
bb_2:
1740
JUMP bb_bytecode_1
1741
bb_bytecode_1:
1742
CHECK_TAG R1, tnumber, bb_fallback_3
1743
%6 = LOAD_DOUBLE R1
1744
%7 = DIV_NUM %6, 100
1745
STORE_DOUBLE R2, %7
1746
STORE_TAG R2, tnumber
1747
JUMP bb_linear_11
1748
bb_linear_11:
1749
%60 = LOAD_POINTER R0
1750
%62 = TRY_NUM_TO_INDEX %6, bb_fallback_5
1751
%63 = SUB_INT %62, 1i
1752
CHECK_ARRAY_SIZE %60, %63, bb_fallback_5
1753
CHECK_NO_METATABLE %60, bb_fallback_5
1754
%66 = GET_ARR_ADDR %60, %63
1755
%67 = LOAD_TVALUE %66
1756
STORE_TVALUE R4, %67
1757
%73 = SUB_NUM 1, %7
1758
STORE_DOUBLE R5, %73
1759
STORE_TAG R5, tnumber
1760
CHECK_TAG R4, tvector, exit(3)
1761
%83 = NUM_TO_FLOAT %73
1762
%84 = FLOAT_TO_VEC %83
1763
%85 = MUL_VEC %67, %84
1764
%86 = TAG_VECTOR %85
1765
STORE_TVALUE R3, %86
1766
INTERRUPT 4u
1767
RETURN R3, 1i
1768
)"
1769
);
1770
}
1771
1772
TEST_CASE_FIXTURE(LoweringFixture, "VectorNumberMixed2")
1773
{
1774
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
1775
1776
assemblyOptions.includeOutlinedCode = true;
1777
1778
CHECK_EQ(
1779
"\n" + getCodegenAssembly(
1780
R"(
1781
local function foo(vectors: {vector}, i: string, t: {})
1782
return vectors[i] * (1 - t)
1783
end
1784
)"
1785
),
1786
R"(
1787
; function foo($arg0, $arg1, $arg2) line 2
1788
bb_0:
1789
CHECK_TAG R0, ttable, exit(entry)
1790
CHECK_TAG R1, tstring, exit(entry)
1791
CHECK_TAG R2, ttable, exit(entry)
1792
JUMP bb_2
1793
bb_2:
1794
JUMP bb_bytecode_1
1795
bb_bytecode_1:
1796
SET_SAVEDPC 1u
1797
GET_TABLE R4, R0, R1
1798
JUMP bb_fallback_3
1799
bb_4:
1800
CHECK_TAG R4, tvector, exit(2)
1801
CHECK_TAG R5, tnumber, bb_fallback_5
1802
%24 = LOAD_TVALUE R4, 0i, tvector
1803
%25 = LOAD_DOUBLE R5
1804
%26 = NUM_TO_FLOAT %25
1805
%27 = FLOAT_TO_VEC %26
1806
%28 = MUL_VEC %24, %27
1807
%29 = TAG_VECTOR %28
1808
STORE_TVALUE R3, %29
1809
JUMP bb_6
1810
bb_6:
1811
INTERRUPT 3u
1812
RETURN R3, 1i
1813
bb_fallback_3:
1814
SET_SAVEDPC 2u
1815
DO_ARITH R5, K0 (1), R2, 9i
1816
JUMP bb_4
1817
bb_fallback_5:
1818
SET_SAVEDPC 3u
1819
DO_ARITH R3, R4, R5, 10i
1820
JUMP bb_6
1821
)"
1822
);
1823
}
1824
1825
TEST_CASE_FIXTURE(LoweringFixture, "VectorReverseOps")
1826
{
1827
CHECK_EQ(
1828
"\n" + getCodegenAssembly(R"(
1829
local function vecrcp(a: vector)
1830
return vector(1, 2, 3) + a
1831
end
1832
)"),
1833
R"(
1834
; function vecrcp($arg0) line 2
1835
bb_0:
1836
CHECK_TAG R0, tvector, exit(entry)
1837
JUMP bb_2
1838
bb_2:
1839
JUMP bb_bytecode_1
1840
bb_bytecode_1:
1841
%4 = LOAD_TVALUE K0 (1, 2, 3), 0i, tvector
1842
%11 = LOAD_TVALUE R0, 0i, tvector
1843
%12 = ADD_VEC %4, %11
1844
%13 = TAG_VECTOR %12
1845
STORE_TVALUE R1, %13
1846
INTERRUPT 2u
1847
RETURN R1, 1i
1848
)"
1849
);
1850
}
1851
1852
TEST_CASE_FIXTURE(LoweringFixture, "UserDataGetIndex")
1853
{
1854
CHECK_EQ(
1855
"\n" + getCodegenAssembly(R"(
1856
local function getxy(a: Point)
1857
return a.x + a.y
1858
end
1859
)"),
1860
R"(
1861
; function getxy($arg0) line 2
1862
bb_0:
1863
CHECK_TAG R0, tuserdata, exit(entry)
1864
JUMP bb_2
1865
bb_2:
1866
JUMP bb_bytecode_1
1867
bb_bytecode_1:
1868
FALLBACK_GETTABLEKS 0u, R2, R0, K0 ('x')
1869
FALLBACK_GETTABLEKS 2u, R3, R0, K1 ('y')
1870
CHECK_TAG R2, tnumber, bb_fallback_3
1871
CHECK_TAG R3, tnumber, bb_fallback_3
1872
%14 = LOAD_DOUBLE R2
1873
%16 = ADD_NUM %14, R3
1874
STORE_DOUBLE R1, %16
1875
STORE_TAG R1, tnumber
1876
JUMP bb_4
1877
bb_4:
1878
INTERRUPT 5u
1879
RETURN R1, 1i
1880
)"
1881
);
1882
}
1883
1884
TEST_CASE_FIXTURE(LoweringFixture, "UserDataSetIndex")
1885
{
1886
CHECK_EQ(
1887
"\n" + getCodegenAssembly(R"(
1888
local function setxy(a: Point)
1889
a.x = 3
1890
a.y = 4
1891
end
1892
)"),
1893
R"(
1894
; function setxy($arg0) line 2
1895
bb_0:
1896
CHECK_TAG R0, tuserdata, exit(entry)
1897
JUMP bb_2
1898
bb_2:
1899
JUMP bb_bytecode_1
1900
bb_bytecode_1:
1901
STORE_DOUBLE R1, 3
1902
STORE_TAG R1, tnumber
1903
FALLBACK_SETTABLEKS 1u, R1, R0, K0 ('x')
1904
STORE_DOUBLE R1, 4
1905
FALLBACK_SETTABLEKS 4u, R1, R0, K1 ('y')
1906
INTERRUPT 6u
1907
RETURN R0, 0i
1908
)"
1909
);
1910
}
1911
1912
TEST_CASE_FIXTURE(LoweringFixture, "UserDataNamecall")
1913
{
1914
CHECK_EQ(
1915
"\n" + getCodegenAssembly(R"(
1916
local function getxy(a: Point)
1917
return a:GetX() + a:GetY()
1918
end
1919
)"),
1920
R"(
1921
; function getxy($arg0) line 2
1922
bb_0:
1923
CHECK_TAG R0, tuserdata, exit(entry)
1924
JUMP bb_2
1925
bb_2:
1926
JUMP bb_bytecode_1
1927
bb_bytecode_1:
1928
FALLBACK_NAMECALL 0u, R2, R0, K0 ('GetX')
1929
INTERRUPT 2u
1930
SET_SAVEDPC 3u
1931
CALL R2, 1i, 1i
1932
FALLBACK_NAMECALL 3u, R3, R0, K1 ('GetY')
1933
INTERRUPT 5u
1934
SET_SAVEDPC 6u
1935
CALL R3, 1i, 1i
1936
CHECK_TAG R2, tnumber, bb_fallback_3
1937
CHECK_TAG R3, tnumber, bb_fallback_3
1938
%20 = LOAD_DOUBLE R2
1939
%22 = ADD_NUM %20, R3
1940
STORE_DOUBLE R1, %22
1941
STORE_TAG R1, tnumber
1942
JUMP bb_4
1943
bb_4:
1944
INTERRUPT 7u
1945
RETURN R1, 1i
1946
)"
1947
);
1948
}
1949
1950
TEST_CASE_FIXTURE(LoweringFixture, "EntryBlockChecksAreNotInferred")
1951
{
1952
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
1953
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
1954
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
1955
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
1956
1957
CHECK_EQ(
1958
"\n" + getCodegenAssembly(
1959
R"(
1960
function eq(a: number, b: number, limit)
1961
if not limit then limit = 0.125 end
1962
return math.abs(a - b) <= limit
1963
end
1964
)"
1965
),
1966
R"(
1967
; function eq($arg0, $arg1, $arg2) line 2
1968
bb_0:
1969
CHECK_TAG R0, tnumber, exit(entry)
1970
CHECK_TAG R1, tnumber, exit(entry)
1971
JUMP bb_5
1972
bb_5:
1973
JUMP bb_bytecode_1
1974
bb_bytecode_1:
1975
JUMP_IF_TRUTHY R2, bb_bytecode_2, bb_6
1976
bb_6:
1977
STORE_DOUBLE R2, 0.125
1978
STORE_TAG R2, tnumber
1979
JUMP bb_bytecode_2
1980
bb_bytecode_2:
1981
implicit CHECK_SAFE_ENV exit(2)
1982
%14 = LOAD_DOUBLE R0
1983
%16 = SUB_NUM %14, R1
1984
%23 = ABS_NUM %16
1985
STORE_DOUBLE R4, %23
1986
STORE_TAG R4, tnumber
1987
CHECK_TAG R2, tnumber, bb_fallback_9
1988
%32 = LOAD_DOUBLE R2
1989
JUMP_CMP_NUM %23, %32, le, bb_bytecode_3, bb_8
1990
bb_8:
1991
STORE_INT R3, 0i
1992
STORE_TAG R3, tboolean
1993
JUMP bb_bytecode_4
1994
bb_bytecode_3:
1995
STORE_INT R3, 1i
1996
STORE_TAG R3, tboolean
1997
JUMP bb_bytecode_4
1998
bb_bytecode_4:
1999
INTERRUPT 11u
2000
RETURN R3, 1i
2001
)"
2002
);
2003
}
2004
2005
TEST_CASE_FIXTURE(LoweringFixture, "EntryBlockChecksWithOptional1")
2006
{
2007
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
2008
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
2009
2010
CHECK_EQ(
2011
"\n" + getCodegenAssembly(
2012
R"(
2013
function eq(a: number?, b: number)
2014
return if a ~= nil then a + b else b
2015
end
2016
)"
2017
),
2018
R"(
2019
; function eq($arg0, $arg1) line 2
2020
bb_0:
2021
%0 = LOAD_TAG R0
2022
JUMP_EQ_TAG %0, tnil, bb_3, bb_4
2023
bb_4:
2024
CHECK_TAG %0, tnumber, exit(entry)
2025
JUMP bb_3
2026
bb_3:
2027
CHECK_TAG R1, tnumber, exit(entry)
2028
JUMP bb_5
2029
bb_5:
2030
JUMP bb_bytecode_1
2031
bb_bytecode_1:
2032
JUMP_EQ_TAG R0, tnil, bb_bytecode_2, bb_6
2033
bb_6:
2034
CHECK_TAG R0, tnumber, exit(2)
2035
%14 = LOAD_DOUBLE R0
2036
%16 = ADD_NUM %14, R1
2037
STORE_DOUBLE R2, %16
2038
STORE_TAG R2, tnumber
2039
INTERRUPT 3u
2040
RETURN R2, 1i
2041
bb_bytecode_2:
2042
%21 = LOAD_TVALUE R1, 0i, tnumber
2043
STORE_TVALUE R2, %21
2044
INTERRUPT 5u
2045
RETURN R2, 1i
2046
)"
2047
);
2048
}
2049
2050
TEST_CASE_FIXTURE(LoweringFixture, "EntryBlockChecksWithOptional2")
2051
{
2052
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
2053
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
2054
2055
CHECK_EQ(
2056
"\n" + getCodegenAssembly(
2057
R"(
2058
function eq(a: number, b: number?)
2059
return if b ~= nil then a + b else a
2060
end
2061
)"
2062
),
2063
R"(
2064
; function eq($arg0, $arg1) line 2
2065
bb_0:
2066
CHECK_TAG R0, tnumber, exit(entry)
2067
%2 = LOAD_TAG R1
2068
JUMP_EQ_TAG %2, tnil, bb_3, bb_4
2069
bb_4:
2070
CHECK_TAG %2, tnumber, exit(entry)
2071
JUMP bb_3
2072
bb_3:
2073
JUMP bb_bytecode_1
2074
bb_bytecode_1:
2075
JUMP_EQ_TAG R1, tnil, bb_bytecode_2, bb_5
2076
bb_5:
2077
CHECK_TAG R1, tnumber, exit(2)
2078
%13 = LOAD_DOUBLE R0
2079
%15 = ADD_NUM %13, R1
2080
STORE_DOUBLE R2, %15
2081
STORE_TAG R2, tnumber
2082
INTERRUPT 3u
2083
RETURN R2, 1i
2084
bb_bytecode_2:
2085
%20 = LOAD_TVALUE R0, 0i, tnumber
2086
STORE_TVALUE R2, %20
2087
INTERRUPT 5u
2088
RETURN R2, 1i
2089
)"
2090
);
2091
}
2092
2093
// This test captures how R4 check was previously incorrectly removed
2094
TEST_CASE_FIXTURE(LoweringFixture, "EntryBlockChecksWithOptional3")
2095
{
2096
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
2097
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
2098
2099
CHECK_EQ(
2100
"\n" + getCodegenAssembly(
2101
R"(
2102
function eq(a: string, b: string?, c: {string}?, d: number?, e: {x: number}, f: number?)
2103
if b then
2104
return a
2105
else
2106
return c
2107
end
2108
end
2109
)"
2110
),
2111
R"(
2112
; function eq($arg0, $arg1, $arg2, $arg3, $arg4, $arg5) line 2
2113
bb_0:
2114
CHECK_TAG R0, tstring, exit(entry)
2115
%2 = LOAD_TAG R1
2116
JUMP_EQ_TAG %2, tnil, bb_3, bb_4
2117
bb_4:
2118
CHECK_TAG %2, tstring, exit(entry)
2119
JUMP bb_3
2120
bb_3:
2121
%6 = LOAD_TAG R2
2122
JUMP_EQ_TAG %6, tnil, bb_5, bb_6
2123
bb_6:
2124
CHECK_TAG %6, ttable, exit(entry)
2125
JUMP bb_5
2126
bb_5:
2127
%10 = LOAD_TAG R3
2128
JUMP_EQ_TAG %10, tnil, bb_7, bb_8
2129
bb_8:
2130
CHECK_TAG %10, tnumber, exit(entry)
2131
JUMP bb_7
2132
bb_7:
2133
CHECK_TAG R4, ttable, exit(entry)
2134
%16 = LOAD_TAG R5
2135
JUMP_EQ_TAG %16, tnil, bb_9, bb_10
2136
bb_10:
2137
CHECK_TAG %16, tnumber, exit(entry)
2138
JUMP bb_9
2139
bb_9:
2140
JUMP bb_bytecode_1
2141
bb_bytecode_1:
2142
JUMP_IF_FALSY R1, bb_bytecode_2, bb_11
2143
bb_11:
2144
INTERRUPT 1u
2145
RETURN R0, 1i
2146
bb_bytecode_2:
2147
INTERRUPT 2u
2148
RETURN R2, 1i
2149
)"
2150
);
2151
}
2152
2153
TEST_CASE_FIXTURE(LoweringFixture, "ExplicitUpvalueAndLocalTypes")
2154
{
2155
ScopedFastFlag luauCodegenDsoPairTrackFix{FFlag::LuauCodegenDsoPairTrackFix, true};
2156
ScopedFastFlag luauCodegenGcoDse{FFlag::LuauCodegenGcoDse2, true};
2157
2158
CHECK_EQ(
2159
"\n" + getCodegenAssembly(
2160
R"(
2161
local y: vector = ...
2162
2163
local function getsum(t)
2164
local x: vector = t
2165
return x.X + x.Y + y.X + y.Y
2166
end
2167
)",
2168
/* includeIrTypes */ true
2169
),
2170
R"(
2171
; function getsum($arg0) line 4
2172
; U0: vector
2173
; R0: vector from 0 to 14
2174
bb_bytecode_0:
2175
CHECK_TAG R0, tvector, exit(0)
2176
%2 = LOAD_FLOAT R0, 0i
2177
%3 = FLOAT_TO_NUM %2
2178
%8 = LOAD_FLOAT R0, 4i
2179
%9 = FLOAT_TO_NUM %8
2180
STORE_DOUBLE R5, %9
2181
STORE_TAG R5, tnumber
2182
%18 = ADD_NUM %3, %9
2183
STORE_DOUBLE R3, %18
2184
STORE_TAG R3, tnumber
2185
%21 = GET_UPVALUE U0
2186
STORE_TVALUE R4, %21
2187
CHECK_TAG R4, tvector, exit(6)
2188
%25 = EXTRACT_VEC %21, 0i
2189
%26 = FLOAT_TO_NUM %25
2190
%35 = ADD_NUM %18, %26
2191
STORE_TVALUE R3, %21
2192
%42 = EXTRACT_VEC %21, 1i
2193
%43 = FLOAT_TO_NUM %42
2194
%52 = ADD_NUM %35, %43
2195
STORE_DOUBLE R1, %52
2196
STORE_TAG R1, tnumber
2197
INTERRUPT 13u
2198
RETURN R1, 1i
2199
)"
2200
);
2201
}
2202
2203
// In this test, we are able to only load t[n] and u[n] once, since there are no modifications
2204
// Our fast-path lowering checks that there is no metatable and all accesses are in bounds
2205
TEST_CASE_FIXTURE(LoweringFixture, "DuplicateArrayLoads1")
2206
{
2207
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
2208
2209
CHECK_EQ(
2210
"\n" + getCodegenAssembly(
2211
R"(
2212
local function foo(n: number, t: {number}, u: {number})
2213
return t[n] * t[n] + u[n] * u[n]
2214
end
2215
)",
2216
false,
2217
1,
2218
2,
2219
true
2220
),
2221
R"(
2222
; function foo($arg0, $arg1, $arg2) line 2
2223
bb_0:
2224
CHECK_TAG R0, tnumber, exit(entry)
2225
CHECK_TAG R1, ttable, exit(entry)
2226
CHECK_TAG R2, ttable, exit(entry)
2227
JUMP bb_2
2228
bb_2:
2229
JUMP bb_bytecode_1
2230
bb_bytecode_1:
2231
%12 = LOAD_POINTER R1
2232
%13 = LOAD_DOUBLE R0
2233
%14 = TRY_NUM_TO_INDEX %13, bb_fallback_3
2234
%15 = SUB_INT %14, 1i
2235
CHECK_ARRAY_SIZE %12, %15, bb_fallback_3
2236
CHECK_NO_METATABLE %12, bb_fallback_3
2237
%18 = GET_ARR_ADDR %12, %15
2238
%19 = LOAD_TVALUE %18
2239
STORE_TVALUE R5, %19
2240
JUMP bb_linear_17
2241
bb_linear_17:
2242
STORE_TVALUE R6, %19
2243
CHECK_TAG R5, tnumber, bb_fallback_7
2244
%131 = LOAD_DOUBLE R5
2245
%133 = MUL_NUM %131, %131
2246
STORE_DOUBLE R4, %133
2247
STORE_TAG R4, tnumber
2248
%137 = LOAD_POINTER R2
2249
CHECK_ARRAY_SIZE %137, %15, bb_fallback_9
2250
CHECK_NO_METATABLE %137, bb_fallback_9
2251
%143 = GET_ARR_ADDR %137, %15
2252
%144 = LOAD_TVALUE %143
2253
STORE_TVALUE R6, %144
2254
STORE_TVALUE R7, %144
2255
CHECK_TAG R6, tnumber, bb_fallback_13
2256
%161 = LOAD_DOUBLE R6
2257
%163 = MUL_NUM %161, %161
2258
%173 = ADD_NUM %133, %163
2259
STORE_DOUBLE R3, %173
2260
STORE_TAG R3, tnumber
2261
INTERRUPT 7u
2262
RETURN R3, 1i
2263
)"
2264
);
2265
}
2266
2267
// In this test, we only load t[a][b] once, since there are no modifications
2268
// Our fast-path lowering checks that there is no metatable and all accesses are in bounds
2269
TEST_CASE_FIXTURE(LoweringFixture, "DuplicateArrayLoads2")
2270
{
2271
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
2272
ScopedFastFlag luauCodegenGcoDse{FFlag::LuauCodegenGcoDse2, true};
2273
2274
CHECK_EQ(
2275
"\n" + getCodegenAssembly(
2276
R"(
2277
local function test(t, a: number, b: number)
2278
return t[a][b].x + t[a][b].y + t[a][b].z
2279
end
2280
)",
2281
false,
2282
1,
2283
2,
2284
true
2285
),
2286
R"(
2287
; function test($arg0, $arg1, $arg2) line 2
2288
bb_0:
2289
CHECK_TAG R1, tnumber, exit(entry)
2290
CHECK_TAG R2, tnumber, exit(entry)
2291
JUMP bb_2
2292
bb_2:
2293
JUMP bb_bytecode_1
2294
bb_bytecode_1:
2295
CHECK_TAG R0, ttable, bb_fallback_3
2296
%10 = LOAD_POINTER R0
2297
%11 = LOAD_DOUBLE R1
2298
%12 = TRY_NUM_TO_INDEX %11, bb_fallback_3
2299
%13 = SUB_INT %12, 1i
2300
CHECK_ARRAY_SIZE %10, %13, bb_fallback_3
2301
CHECK_NO_METATABLE %10, bb_fallback_3
2302
%16 = GET_ARR_ADDR %10, %13
2303
%17 = LOAD_TVALUE %16
2304
STORE_TVALUE R6, %17
2305
JUMP bb_linear_25
2306
bb_linear_25:
2307
CHECK_TAG R6, ttable, bb_fallback_5
2308
%168 = LOAD_POINTER R6
2309
%169 = LOAD_DOUBLE R2
2310
%170 = TRY_NUM_TO_INDEX %169, bb_fallback_5
2311
%171 = SUB_INT %170, 1i
2312
CHECK_ARRAY_SIZE %168, %171, bb_fallback_5
2313
CHECK_NO_METATABLE %168, bb_fallback_5
2314
%174 = GET_ARR_ADDR %168, %171
2315
%175 = LOAD_TVALUE %174
2316
STORE_TVALUE R5, %175
2317
CHECK_TAG R5, ttable, bb_fallback_7
2318
%180 = LOAD_POINTER R5
2319
%181 = GET_SLOT_NODE_ADDR %180, 2u, K0 ('x')
2320
CHECK_SLOT_MATCH %181, K0 ('x'), bb_fallback_7
2321
%183 = LOAD_TVALUE %181, 0i
2322
STORE_TVALUE R5, %183
2323
STORE_TVALUE R6, %175
2324
%213 = GET_SLOT_NODE_ADDR %180, 6u, K1 ('y')
2325
CHECK_SLOT_MATCH %213, K1 ('y'), bb_fallback_13
2326
%215 = LOAD_TVALUE %213, 0i
2327
STORE_TVALUE R6, %215
2328
CHECK_TAG R5, tnumber, bb_fallback_15
2329
CHECK_TAG R6, tnumber, bb_fallback_15
2330
%222 = LOAD_DOUBLE R5
2331
%224 = ADD_NUM %222, R6
2332
STORE_DOUBLE R4, %224
2333
STORE_TAG R4, tnumber
2334
STORE_TVALUE R6, %17
2335
CHECK_NO_METATABLE %168, bb_fallback_19
2336
STORE_TVALUE R5, %175
2337
%255 = GET_SLOT_NODE_ADDR %180, 11u, K2 ('z')
2338
CHECK_SLOT_MATCH %255, K2 ('z'), bb_fallback_21
2339
%257 = LOAD_TVALUE %255, 0i
2340
STORE_TVALUE R5, %257
2341
CHECK_TAG R5, tnumber, bb_fallback_23
2342
%266 = ADD_NUM %224, R5
2343
STORE_DOUBLE R3, %266
2344
STORE_TAG R3, tnumber
2345
INTERRUPT 14u
2346
RETURN R3, 1i
2347
)"
2348
);
2349
}
2350
2351
// This test shows that writes to separate array elements do not interfere with each other
2352
// Our fast-path lowering checks that there is no metatable and all accesses are in bounds
2353
TEST_CASE_FIXTURE(LoweringFixture, "DuplicateArrayLoads3")
2354
{
2355
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
2356
ScopedFastFlag luauCodegenGcoDse{FFlag::LuauCodegenGcoDse2, true};
2357
2358
// TODO: opportunity - only one array size check should be enough here
2359
CHECK_EQ(
2360
"\n" + getCodegenAssembly(
2361
R"(
2362
local function test(t: { x: number, y: number }, a: number)
2363
t[1] += a
2364
t[2] += a * a
2365
2366
t[1] = t[1] - t[2]
2367
end
2368
)",
2369
false,
2370
1,
2371
2,
2372
true
2373
),
2374
R"(
2375
; function test($arg0, $arg1) line 2
2376
bb_0:
2377
CHECK_TAG R0, ttable, exit(entry)
2378
CHECK_TAG R1, tnumber, exit(entry)
2379
JUMP bb_2
2380
bb_2:
2381
JUMP bb_bytecode_1
2382
bb_bytecode_1:
2383
%8 = LOAD_POINTER R0
2384
CHECK_ARRAY_SIZE %8, 0i, bb_fallback_3
2385
CHECK_NO_METATABLE %8, bb_fallback_3
2386
%11 = GET_ARR_ADDR %8, 0i
2387
%12 = LOAD_TVALUE %11, 0i
2388
STORE_TVALUE R2, %12
2389
JUMP bb_linear_23
2390
bb_linear_23:
2391
CHECK_TAG R2, tnumber, bb_fallback_5
2392
%144 = LOAD_DOUBLE R2
2393
%145 = LOAD_DOUBLE R1
2394
%146 = ADD_NUM %144, %145
2395
STORE_DOUBLE R2, %146
2396
CHECK_READONLY %8, bb_fallback_7
2397
STORE_SPLIT_TVALUE %11, tnumber, %146, 0i
2398
CHECK_ARRAY_SIZE %8, 1i, bb_fallback_9
2399
%162 = LOAD_TVALUE %11, 16i
2400
STORE_TVALUE R2, %162
2401
%166 = MUL_NUM %145, %145
2402
STORE_DOUBLE R3, %166
2403
STORE_TAG R3, tnumber
2404
CHECK_TAG R2, tnumber, bb_fallback_11
2405
%171 = LOAD_DOUBLE R2
2406
%172 = ADD_NUM %171, %166
2407
STORE_SPLIT_TVALUE %11, tnumber, %172, 16i
2408
%204 = SUB_NUM %146, %172
2409
STORE_SPLIT_TVALUE %11, tnumber, %204, 0i
2410
INTERRUPT 11u
2411
RETURN R0, 0i
2412
)"
2413
);
2414
}
2415
2416
// This test shows that writes to separate array elements using non-constant expressions don't have enough analysis to not interfere
2417
// Our fast-path lowering checks that there is no metatable and all accesses are in bounds
2418
TEST_CASE_FIXTURE(LoweringFixture, "DuplicateArrayLoads4")
2419
{
2420
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
2421
2422
// TODO: opportunity 1 - if we can figure out that i+1 is exactly 1 integer slot away, we can reduce arithmetic
2423
// TODO: opportunity 2 - store at [i + 1] shouldn't invalidate value at [i]
2424
CHECK_EQ(
2425
"\n" + getCodegenAssembly(
2426
R"(
2427
local function test(t: { x: number, y: number }, a: number, i: number)
2428
t[i] += a
2429
t[i + 1] += a * a
2430
2431
t[i] = t[i] - t[i + 1]
2432
end
2433
)",
2434
false,
2435
1,
2436
2,
2437
true
2438
),
2439
R"(
2440
; function test($arg0, $arg1, $arg2) line 2
2441
bb_0:
2442
CHECK_TAG R0, ttable, exit(entry)
2443
CHECK_TAG R1, tnumber, exit(entry)
2444
CHECK_TAG R2, tnumber, exit(entry)
2445
JUMP bb_2
2446
bb_2:
2447
JUMP bb_bytecode_1
2448
bb_bytecode_1:
2449
%12 = LOAD_POINTER R0
2450
%13 = LOAD_DOUBLE R2
2451
%14 = TRY_NUM_TO_INDEX %13, bb_fallback_3
2452
%15 = SUB_INT %14, 1i
2453
CHECK_ARRAY_SIZE %12, %15, bb_fallback_3
2454
CHECK_NO_METATABLE %12, bb_fallback_3
2455
%18 = GET_ARR_ADDR %12, %15
2456
%19 = LOAD_TVALUE %18
2457
STORE_TVALUE R3, %19
2458
JUMP bb_linear_23
2459
bb_linear_23:
2460
CHECK_TAG R3, tnumber, bb_fallback_5
2461
%193 = LOAD_DOUBLE R3
2462
%194 = LOAD_DOUBLE R1
2463
%195 = ADD_NUM %193, %194
2464
STORE_DOUBLE R3, %195
2465
CHECK_READONLY %12, bb_fallback_7
2466
STORE_SPLIT_TVALUE %18, tnumber, %195
2467
%211 = ADD_NUM %13, 1
2468
STORE_DOUBLE R3, %211
2469
%215 = TRY_NUM_TO_INDEX %211, bb_fallback_9
2470
%216 = SUB_INT %215, 1i
2471
CHECK_ARRAY_SIZE %12, %216, bb_fallback_9
2472
%219 = GET_ARR_ADDR %12, %216
2473
%220 = LOAD_TVALUE %219
2474
STORE_TVALUE R4, %220
2475
%224 = MUL_NUM %194, %194
2476
STORE_DOUBLE R5, %224
2477
STORE_TAG R5, tnumber
2478
CHECK_TAG R4, tnumber, bb_fallback_11
2479
%229 = LOAD_DOUBLE R4
2480
%230 = ADD_NUM %229, %224
2481
STORE_SPLIT_TVALUE %219, tnumber, %230
2482
%254 = LOAD_TVALUE %18
2483
STORE_TVALUE R4, %254
2484
%267 = LOAD_TVALUE %219
2485
STORE_TVALUE R5, %267
2486
CHECK_TAG R4, tnumber, bb_fallback_19
2487
%274 = LOAD_DOUBLE R4
2488
%276 = SUB_NUM %274, %230
2489
STORE_SPLIT_TVALUE %18, tnumber, %276
2490
INTERRUPT 13u
2491
RETURN R0, 0i
2492
)"
2493
);
2494
}
2495
2496
// This test checks that in case of known constants, we propagate them in full and can recover the constant difference
2497
// Our fast-path lowering checks that there is no metatable and all accesses are in bounds
2498
TEST_CASE_FIXTURE(LoweringFixture, "DuplicateArrayLoads5")
2499
{
2500
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
2501
ScopedFastFlag luauCodegenGcoDse{FFlag::LuauCodegenGcoDse2, true};
2502
2503
CHECK_EQ(
2504
"\n" + getCodegenAssembly(
2505
R"(
2506
local function test(t: { x: number, y: number })
2507
t[1] = 14
2508
t[2] = 28
2509
2510
t[1] = t[1] - t[2]
2511
end
2512
)",
2513
false,
2514
1,
2515
2,
2516
true
2517
),
2518
R"(
2519
; function test($arg0) line 2
2520
bb_0:
2521
CHECK_TAG R0, ttable, exit(entry)
2522
JUMP bb_2
2523
bb_2:
2524
JUMP bb_bytecode_1
2525
bb_bytecode_1:
2526
STORE_DOUBLE R1, 14
2527
STORE_TAG R1, tnumber
2528
%8 = LOAD_POINTER R0
2529
CHECK_ARRAY_SIZE %8, 0i, bb_fallback_3
2530
CHECK_NO_METATABLE %8, bb_fallback_3
2531
CHECK_READONLY %8, bb_fallback_3
2532
%12 = GET_ARR_ADDR %8, 0i
2533
STORE_SPLIT_TVALUE %12, tnumber, 14, 0i
2534
JUMP bb_linear_15
2535
bb_linear_15:
2536
STORE_DOUBLE R1, 28
2537
CHECK_ARRAY_SIZE %8, 1i, bb_fallback_5
2538
STORE_SPLIT_TVALUE %12, tnumber, 28, 16i
2539
STORE_SPLIT_TVALUE %12, tnumber, -14, 0i
2540
INTERRUPT 8u
2541
RETURN R0, 0i
2542
)"
2543
);
2544
}
2545
2546
// This test checks that writing to constant index after an unknown one invalidates it
2547
TEST_CASE_FIXTURE(LoweringFixture, "DuplicateArrayLoads6")
2548
{
2549
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
2550
ScopedFastFlag luauCodegenGcoDse{FFlag::LuauCodegenGcoDse2, true};
2551
2552
CHECK_EQ(
2553
"\n" + getCodegenAssembly(
2554
R"(
2555
local function test(t: { x: number, y: number }, a: number, i: number)
2556
t[i] = 2
2557
t[2] = 4
2558
return t[i] * 2
2559
end
2560
)",
2561
false,
2562
1,
2563
2,
2564
true
2565
),
2566
R"(
2567
; function test($arg0, $arg1, $arg2) line 2
2568
bb_0:
2569
CHECK_TAG R0, ttable, exit(entry)
2570
CHECK_TAG R1, tnumber, exit(entry)
2571
CHECK_TAG R2, tnumber, exit(entry)
2572
JUMP bb_2
2573
bb_2:
2574
JUMP bb_bytecode_1
2575
bb_bytecode_1:
2576
STORE_DOUBLE R3, 2
2577
STORE_TAG R3, tnumber
2578
%14 = LOAD_POINTER R0
2579
%15 = LOAD_DOUBLE R2
2580
%16 = TRY_NUM_TO_INDEX %15, bb_fallback_3
2581
%17 = SUB_INT %16, 1i
2582
CHECK_ARRAY_SIZE %14, %17, bb_fallback_3
2583
CHECK_NO_METATABLE %14, bb_fallback_3
2584
CHECK_READONLY %14, bb_fallback_3
2585
%21 = GET_ARR_ADDR %14, %17
2586
STORE_SPLIT_TVALUE %21, tnumber, 2
2587
JUMP bb_linear_11
2588
bb_linear_11:
2589
STORE_DOUBLE R3, 4
2590
CHECK_ARRAY_SIZE %14, 1i, bb_fallback_5
2591
%80 = GET_ARR_ADDR %14, 0i
2592
STORE_SPLIT_TVALUE %80, tnumber, 4, 16i
2593
%90 = LOAD_TVALUE %21
2594
STORE_TVALUE R4, %90
2595
CHECK_TAG R4, tnumber, bb_fallback_9
2596
%95 = LOAD_DOUBLE R4
2597
%96 = ADD_NUM %95, %95
2598
STORE_DOUBLE R3, %96
2599
INTERRUPT 6u
2600
RETURN R3, 1i
2601
)"
2602
);
2603
}
2604
2605
// This test checks that loads from the same keys are removed
2606
// Note that CHECK_SLOT_MATCH ensures that key is in mainposition and not nil, so metatable is not triggered
2607
TEST_CASE_FIXTURE(LoweringFixture, "TableNodeLoadStoreProp1")
2608
{
2609
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
2610
ScopedFastFlag luauCodegenGcoDse{FFlag::LuauCodegenGcoDse2, true};
2611
2612
CHECK_EQ(
2613
"\n" + getCodegenAssembly(
2614
R"(
2615
local function test(t: { u: number, a: { b: number, c: { x: number, y: number } } })
2616
return t.a.b + t.a.c.x + t.a.c.y
2617
end
2618
)",
2619
false,
2620
1,
2621
2,
2622
true
2623
),
2624
R"(
2625
; function test($arg0) line 2
2626
bb_0:
2627
CHECK_TAG R0, ttable, exit(entry)
2628
JUMP bb_2
2629
bb_2:
2630
JUMP bb_bytecode_1
2631
bb_bytecode_1:
2632
%6 = LOAD_POINTER R0
2633
%7 = GET_SLOT_NODE_ADDR %6, 0u, K0 ('a')
2634
CHECK_SLOT_MATCH %7, K0 ('a'), bb_fallback_3
2635
%9 = LOAD_TVALUE %7, 0i
2636
STORE_TVALUE R3, %9
2637
JUMP bb_linear_23
2638
bb_linear_23:
2639
CHECK_TAG R3, ttable, bb_fallback_5
2640
%114 = LOAD_POINTER R3
2641
%115 = GET_SLOT_NODE_ADDR %114, 2u, K1 ('b')
2642
CHECK_SLOT_MATCH %115, K1 ('b'), bb_fallback_5
2643
%117 = LOAD_TVALUE %115, 0i
2644
STORE_TVALUE R3, %117
2645
STORE_TVALUE R4, %9
2646
%129 = GET_SLOT_NODE_ADDR %114, 6u, K2 ('c')
2647
CHECK_SLOT_MATCH %129, K2 ('c'), bb_fallback_9
2648
%131 = LOAD_TVALUE %129, 0i
2649
STORE_TVALUE R4, %131
2650
CHECK_TAG R4, ttable, bb_fallback_11
2651
%136 = LOAD_POINTER R4
2652
%137 = GET_SLOT_NODE_ADDR %136, 8u, K3 ('x')
2653
CHECK_SLOT_MATCH %137, K3 ('x'), bb_fallback_11
2654
%139 = LOAD_TVALUE %137, 0i
2655
STORE_TVALUE R4, %139
2656
CHECK_TAG R3, tnumber, bb_fallback_13
2657
CHECK_TAG R4, tnumber, bb_fallback_13
2658
%146 = LOAD_DOUBLE R3
2659
%148 = ADD_NUM %146, R4
2660
STORE_DOUBLE R2, %148
2661
STORE_TAG R2, tnumber
2662
STORE_TVALUE R3, %131
2663
%169 = GET_SLOT_NODE_ADDR %136, 15u, K4 ('y')
2664
CHECK_SLOT_MATCH %169, K4 ('y'), bb_fallback_19
2665
%171 = LOAD_TVALUE %169, 0i
2666
STORE_TVALUE R3, %171
2667
CHECK_TAG R3, tnumber, bb_fallback_21
2668
%180 = ADD_NUM %148, R3
2669
STORE_DOUBLE R1, %180
2670
STORE_TAG R1, tnumber
2671
INTERRUPT 18u
2672
RETURN R1, 1i
2673
)"
2674
);
2675
}
2676
2677
// This test checks that stores to distinct keys do not interfere with each other
2678
// If the table pointers were different, they cannot have the same storage, if they are the same, slots are different
2679
// Additionally, because we know that 'nil' is not stored, we do not have to recheck that with CHECK_NODE_VALUE
2680
// Note that CHECK_SLOT_MATCH ensures that key is in mainposition and not nil, so metatable is not triggered
2681
TEST_CASE_FIXTURE(LoweringFixture, "TableNodeLoadStoreProp2")
2682
{
2683
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
2684
ScopedFastFlag luauCodegenGcoDse{FFlag::LuauCodegenGcoDse2, true};
2685
2686
CHECK_EQ(
2687
"\n" + getCodegenAssembly(
2688
R"(
2689
local function test(t: { x: number, y: number }, a: number)
2690
t.x += a
2691
t.y += a * a
2692
2693
t.x = t.x - t.y
2694
end
2695
)",
2696
false,
2697
1,
2698
2,
2699
true
2700
),
2701
R"(
2702
; function test($arg0, $arg1) line 2
2703
bb_0:
2704
CHECK_TAG R0, ttable, exit(entry)
2705
CHECK_TAG R1, tnumber, exit(entry)
2706
JUMP bb_2
2707
bb_2:
2708
JUMP bb_bytecode_1
2709
bb_bytecode_1:
2710
%8 = LOAD_POINTER R0
2711
%9 = GET_SLOT_NODE_ADDR %8, 0u, K0 ('x')
2712
CHECK_SLOT_MATCH %9, K0 ('x'), bb_fallback_3
2713
%11 = LOAD_TVALUE %9, 0i
2714
STORE_TVALUE R2, %11
2715
JUMP bb_linear_23
2716
bb_linear_23:
2717
CHECK_TAG R2, tnumber, bb_fallback_5
2718
%130 = LOAD_DOUBLE R2
2719
%131 = LOAD_DOUBLE R1
2720
%132 = ADD_NUM %130, %131
2721
STORE_DOUBLE R2, %132
2722
CHECK_READONLY %8, bb_fallback_7
2723
STORE_SPLIT_TVALUE %9, tnumber, %132, 0i
2724
%144 = GET_SLOT_NODE_ADDR %8, 5u, K1 ('y')
2725
CHECK_SLOT_MATCH %144, K1 ('y'), bb_fallback_9
2726
%146 = LOAD_TVALUE %144, 0i
2727
STORE_TVALUE R2, %146
2728
%150 = MUL_NUM %131, %131
2729
STORE_DOUBLE R3, %150
2730
STORE_TAG R3, tnumber
2731
CHECK_TAG R2, tnumber, bb_fallback_11
2732
%155 = LOAD_DOUBLE R2
2733
%156 = ADD_NUM %155, %150
2734
STORE_SPLIT_TVALUE %144, tnumber, %156, 0i
2735
%185 = SUB_NUM %132, %156
2736
STORE_SPLIT_TVALUE %9, tnumber, %185, 0i
2737
INTERRUPT 18u
2738
RETURN R0, 0i
2739
)"
2740
);
2741
}
2742
2743
// In this test we write an unknown key and t.x can be affected and has to be reloaded
2744
TEST_CASE_FIXTURE(LoweringFixture, "TableNodeLoadStoreProp3")
2745
{
2746
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
2747
ScopedFastFlag luauCodegenGcoDse{FFlag::LuauCodegenGcoDse2, true};
2748
2749
CHECK_EQ(
2750
"\n" + getCodegenAssembly(
2751
R"(
2752
local function test(t: { x: number, y: number }, a: string)
2753
t.x = 2
2754
t[a] = 4
2755
return t.x * 2
2756
end
2757
)",
2758
false,
2759
1,
2760
2,
2761
true
2762
),
2763
R"(
2764
; function test($arg0, $arg1) line 2
2765
bb_0:
2766
CHECK_TAG R0, ttable, exit(entry)
2767
CHECK_TAG R1, tstring, exit(entry)
2768
JUMP bb_2
2769
bb_2:
2770
JUMP bb_bytecode_1
2771
bb_bytecode_1:
2772
STORE_DOUBLE R2, 2
2773
STORE_TAG R2, tnumber
2774
%10 = LOAD_POINTER R0
2775
%11 = GET_SLOT_NODE_ADDR %10, 1u, K0 ('x')
2776
CHECK_SLOT_MATCH %11, K0 ('x'), bb_fallback_3
2777
CHECK_READONLY %10, bb_fallback_3
2778
STORE_SPLIT_TVALUE %11, tnumber, 2, 0i
2779
JUMP bb_linear_9
2780
bb_linear_9:
2781
STORE_DOUBLE R2, 4
2782
SET_SAVEDPC 5u
2783
SET_TABLE R2, R0, R1
2784
%50 = LOAD_POINTER R0
2785
%51 = GET_SLOT_NODE_ADDR %50, 5u, K0 ('x')
2786
CHECK_SLOT_MATCH %51, K0 ('x'), bb_fallback_5
2787
%53 = LOAD_TVALUE %51, 0i
2788
STORE_TVALUE R3, %53
2789
CHECK_TAG R3, tnumber, bb_fallback_7
2790
%58 = LOAD_DOUBLE R3
2791
%59 = ADD_NUM %58, %58
2792
STORE_DOUBLE R2, %59
2793
INTERRUPT 8u
2794
RETURN R2, 1i
2795
)"
2796
);
2797
}
2798
2799
// In this test, write to an array part cannot affect the node part
2800
// Our fast-path lowering checks that there is no metatable and all accesses are in bounds, so rehash is not possible
2801
TEST_CASE_FIXTURE(LoweringFixture, "TableNodeLoadStoreProp4")
2802
{
2803
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
2804
ScopedFastFlag luauCodegenGcoDse{FFlag::LuauCodegenGcoDse2, true};
2805
2806
CHECK_EQ(
2807
"\n" + getCodegenAssembly(
2808
R"(
2809
local function test(t: { x: number, y: number }, a: string)
2810
t.x = 2
2811
t[1] = nil
2812
return t.x * 2
2813
end
2814
)",
2815
false,
2816
1,
2817
2,
2818
true
2819
),
2820
R"(
2821
; function test($arg0, $arg1) line 2
2822
bb_0:
2823
CHECK_TAG R0, ttable, exit(entry)
2824
CHECK_TAG R1, tstring, exit(entry)
2825
JUMP bb_2
2826
bb_2:
2827
JUMP bb_bytecode_1
2828
bb_bytecode_1:
2829
STORE_DOUBLE R2, 2
2830
STORE_TAG R2, tnumber
2831
%10 = LOAD_POINTER R0
2832
%11 = GET_SLOT_NODE_ADDR %10, 1u, K0 ('x')
2833
CHECK_SLOT_MATCH %11, K0 ('x'), bb_fallback_3
2834
CHECK_READONLY %10, bb_fallback_3
2835
STORE_SPLIT_TVALUE %11, tnumber, 2, 0i
2836
JUMP bb_linear_11
2837
bb_linear_11:
2838
STORE_TAG R2, tnil
2839
CHECK_ARRAY_SIZE %10, 0i, bb_fallback_5
2840
CHECK_NO_METATABLE %10, bb_fallback_5
2841
%62 = GET_ARR_ADDR %10, 0i
2842
%63 = LOAD_TVALUE R2, 0i, tnil
2843
STORE_TVALUE %62, %63, 0i
2844
STORE_DOUBLE R2, 4
2845
STORE_TAG R2, tnumber
2846
INTERRUPT 8u
2847
RETURN R2, 1i
2848
)"
2849
);
2850
}
2851
2852
// This test is based on an example of texture bilinear interpolation, t.w/t.h only have to be loaded once
2853
TEST_CASE_FIXTURE(LoweringFixture, "TableNodeLoadStoreProp5")
2854
{
2855
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
2856
ScopedFastFlag luauCodegenGcoDse{FFlag::LuauCodegenGcoDse2, true};
2857
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
2858
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
2859
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
2860
2861
CHECK_EQ(
2862
"\n" + getCodegenAssembly(
2863
R"(
2864
local function test(t: { w: number, h: number, data: {vector} }, uv: vector)
2865
uv *= vector.create(t.w, t.h)
2866
uv -= vector.create(.5,.5)
2867
local uv0 = vector.floor(uv)
2868
local uv1 = vector.ceil(uv)
2869
local a = uv - uv0
2870
local x0 = uv0.x % t.w
2871
local x1 = uv1.x % t.w
2872
local y0 = (uv0.y % t.h) * t.w
2873
local y1 = (uv1.y % t.h) * t.w
2874
return a, x0, x1, y0, y1
2875
end
2876
)",
2877
false,
2878
1,
2879
2,
2880
true
2881
),
2882
R"(
2883
; function test($arg0, $arg1) line 2
2884
bb_0:
2885
CHECK_TAG R0, ttable, exit(entry)
2886
CHECK_TAG R1, tvector, exit(entry)
2887
JUMP bb_2
2888
bb_2:
2889
JUMP bb_bytecode_1
2890
bb_bytecode_1:
2891
%8 = LOAD_POINTER R0
2892
%9 = GET_SLOT_NODE_ADDR %8, 0u, K0 ('w')
2893
CHECK_SLOT_MATCH %9, K0 ('w'), bb_fallback_3
2894
%11 = LOAD_TVALUE %9, 0i
2895
STORE_TVALUE R3, %11
2896
JUMP bb_linear_34
2897
bb_linear_34:
2898
%248 = GET_SLOT_NODE_ADDR %8, 2u, K1 ('h')
2899
CHECK_SLOT_MATCH %248, K1 ('h'), bb_fallback_5
2900
%250 = LOAD_TVALUE %248, 0i
2901
STORE_TVALUE R4, %250
2902
CHECK_SAFE_ENV exit(4)
2903
CHECK_TAG R3, tnumber, exit(6)
2904
CHECK_TAG R4, tnumber, exit(6)
2905
%258 = LOAD_DOUBLE R3
2906
%259 = LOAD_DOUBLE R4
2907
%260 = NUM_TO_FLOAT %258
2908
%261 = NUM_TO_FLOAT %259
2909
STORE_VECTOR R2, %260, %261, 0
2910
STORE_TAG R2, tvector
2911
CHECK_TAG R1, tvector, exit(9)
2912
%266 = LOAD_TVALUE R1, 0i, tvector
2913
%267 = LOAD_TVALUE R2, 0i, tvector
2914
%268 = MUL_VEC %266, %267
2915
%271 = LOAD_TVALUE K5 (0.5, 0.5, 0), 0i, tvector
2916
%273 = SUB_VEC %268, %271
2917
%276 = FLOOR_VEC %273
2918
%279 = CEIL_VEC %273
2919
%282 = SUB_VEC %273, %276
2920
%283 = TAG_VECTOR %282
2921
STORE_TVALUE R4, %283
2922
%285 = EXTRACT_VEC %276, 0i
2923
%286 = FLOAT_TO_NUM %285
2924
STORE_TVALUE R7, %11
2925
%301 = MOD_NUM %286, %258
2926
STORE_DOUBLE R5, %301
2927
STORE_TAG R5, tnumber
2928
%307 = EXTRACT_VEC %279, 0i
2929
%308 = FLOAT_TO_NUM %307
2930
STORE_DOUBLE R7, %308
2931
STORE_TVALUE R8, %11
2932
%323 = MOD_NUM %308, %258
2933
STORE_SPLIT_TVALUE R6, tnumber, %323
2934
%329 = EXTRACT_VEC %276, 1i
2935
%330 = FLOAT_TO_NUM %329
2936
STORE_TVALUE R10, %250
2937
%345 = MOD_NUM %330, %259
2938
STORE_DOUBLE R8, %345
2939
STORE_TVALUE R9, %11
2940
%361 = MUL_NUM %345, %258
2941
STORE_DOUBLE R7, %361
2942
%367 = EXTRACT_VEC %279, 1i
2943
%368 = FLOAT_TO_NUM %367
2944
STORE_DOUBLE R10, %368
2945
%383 = MOD_NUM %368, %259
2946
STORE_DOUBLE R9, %383
2947
%399 = MUL_NUM %383, %258
2948
STORE_DOUBLE R8, %399
2949
INTERRUPT 49u
2950
RETURN R4, 5i
2951
)"
2952
);
2953
}
2954
2955
// This test checks that in case of known constants, we propagate them in full and can recover the constant difference
2956
TEST_CASE_FIXTURE(LoweringFixture, "TableNodeLoadStoreProp6")
2957
{
2958
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
2959
ScopedFastFlag luauCodegenGcoDse{FFlag::LuauCodegenGcoDse2, true};
2960
2961
CHECK_EQ(
2962
"\n" + getCodegenAssembly(
2963
R"(
2964
local function test(t: { x: number, y: number })
2965
t.x = 14
2966
t.y = 28
2967
2968
t.x = t.x - t.y
2969
end
2970
)",
2971
false,
2972
1,
2973
2,
2974
true
2975
),
2976
R"(
2977
; function test($arg0) line 2
2978
bb_0:
2979
CHECK_TAG R0, ttable, exit(entry)
2980
JUMP bb_2
2981
bb_2:
2982
JUMP bb_bytecode_1
2983
bb_bytecode_1:
2984
STORE_DOUBLE R1, 14
2985
STORE_TAG R1, tnumber
2986
%8 = LOAD_POINTER R0
2987
%9 = GET_SLOT_NODE_ADDR %8, 1u, K0 ('x')
2988
CHECK_SLOT_MATCH %9, K0 ('x'), bb_fallback_3
2989
CHECK_READONLY %8, bb_fallback_3
2990
STORE_SPLIT_TVALUE %9, tnumber, 14, 0i
2991
JUMP bb_linear_15
2992
bb_linear_15:
2993
STORE_DOUBLE R1, 28
2994
%82 = GET_SLOT_NODE_ADDR %8, 4u, K1 ('y')
2995
CHECK_SLOT_MATCH %82, K1 ('y'), bb_fallback_5
2996
STORE_SPLIT_TVALUE %82, tnumber, 28, 0i
2997
STORE_SPLIT_TVALUE %9, tnumber, -14, 0i
2998
INTERRUPT 13u
2999
RETURN R0, 0i
3000
)"
3001
);
3002
}
3003
3004
// This test shows a table key swap
3005
// Invalidating CHECK_SLOT_MATCH of one key with nil does not cause CHECK_NODE_VALUE of the other
3006
TEST_CASE_FIXTURE(LoweringFixture, "TableNodeLoadStoreProp7")
3007
{
3008
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
3009
3010
// TODO: opportunity - table barrier is not needed when values come from the same table
3011
CHECK_EQ(
3012
"\n" + getCodegenAssembly(
3013
R"(
3014
local function test(t: { x: number, y: number })
3015
t.x, t.y = t.y, t.x
3016
end
3017
)",
3018
false,
3019
1,
3020
2,
3021
true
3022
),
3023
R"(
3024
; function test($arg0) line 2
3025
bb_0:
3026
CHECK_TAG R0, ttable, exit(entry)
3027
JUMP bb_2
3028
bb_2:
3029
JUMP bb_bytecode_1
3030
bb_bytecode_1:
3031
%6 = LOAD_POINTER R0
3032
%7 = GET_SLOT_NODE_ADDR %6, 0u, K0 ('y')
3033
CHECK_SLOT_MATCH %7, K0 ('y'), bb_fallback_3
3034
%9 = LOAD_TVALUE %7, 0i
3035
STORE_TVALUE R1, %9
3036
JUMP bb_linear_11
3037
bb_linear_11:
3038
%51 = GET_SLOT_NODE_ADDR %6, 2u, K1 ('x')
3039
CHECK_SLOT_MATCH %51, K1 ('x'), bb_fallback_5
3040
%53 = LOAD_TVALUE %51, 0i
3041
STORE_TVALUE R2, %53
3042
CHECK_READONLY %6, bb_fallback_7
3043
STORE_TVALUE %51, %9, 0i
3044
BARRIER_TABLE_FORWARD %6, R1, undef
3045
STORE_TVALUE %7, %53, 0i
3046
BARRIER_TABLE_FORWARD %6, R2, undef
3047
INTERRUPT 8u
3048
RETURN R0, 0i
3049
)"
3050
);
3051
}
3052
3053
#if LUA_VECTOR_SIZE == 3
3054
TEST_CASE_FIXTURE(LoweringFixture, "FastcallTypeInferThroughLocal")
3055
{
3056
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
3057
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
3058
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
3059
ScopedFastFlag luauCodegenPropRegisterTagsAcrossChains{FFlag::LuauCodegenPropagateTagsAcrossChains2, true};
3060
ScopedFastFlag luauCodegenConstPropSetEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
3061
3062
CHECK_EQ(
3063
"\n" + getCodegenAssembly(
3064
R"(
3065
local function getsum(x, c)
3066
local v = vector(x, 2, 3)
3067
if c then
3068
return v.X + v.Y
3069
else
3070
return v.Z
3071
end
3072
end
3073
)",
3074
/* includeIrTypes */ true
3075
),
3076
R"(
3077
; function getsum($arg0, $arg1) line 2
3078
; R2: vector from 0 to 18
3079
bb_bytecode_0:
3080
implicit CHECK_SAFE_ENV exit(0)
3081
STORE_DOUBLE R4, 2
3082
STORE_TAG R4, tnumber
3083
STORE_DOUBLE R5, 3
3084
STORE_TAG R5, tnumber
3085
CHECK_TAG R0, tnumber, exit(4)
3086
%11 = LOAD_DOUBLE R0
3087
%14 = NUM_TO_FLOAT %11
3088
STORE_VECTOR R2, %14, 2, 3
3089
STORE_TAG R2, tvector
3090
JUMP_IF_FALSY R1, bb_bytecode_1, bb_3
3091
bb_3:
3092
%23 = LOAD_FLOAT R2, 0i
3093
%24 = FLOAT_TO_NUM %23
3094
%29 = LOAD_FLOAT R2, 4i
3095
%30 = FLOAT_TO_NUM %29
3096
%39 = ADD_NUM %24, %30
3097
STORE_DOUBLE R3, %39
3098
STORE_TAG R3, tnumber
3099
INTERRUPT 14u
3100
RETURN R3, 1i
3101
bb_bytecode_1:
3102
%46 = LOAD_FLOAT R2, 8i
3103
%47 = FLOAT_TO_NUM %46
3104
STORE_DOUBLE R3, %47
3105
STORE_TAG R3, tnumber
3106
INTERRUPT 17u
3107
RETURN R3, 1i
3108
)"
3109
);
3110
}
3111
3112
TEST_CASE_FIXTURE(LoweringFixture, "FastcallTypeInferThroughUpvalue")
3113
{
3114
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
3115
ScopedFastFlag luauCodegenDsoPairTrackFix{FFlag::LuauCodegenDsoPairTrackFix, true};
3116
ScopedFastFlag luauCodegenGcoDse{FFlag::LuauCodegenGcoDse2, true};
3117
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
3118
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
3119
3120
// TODO: opportunity - bb_3 and bb_bytecode_1 have only one predecessor, so they should know that the upvalue u0 is already in r2
3121
CHECK_EQ(
3122
"\n" + getCodegenAssembly(
3123
R"(
3124
local v = ...
3125
3126
local function getsum(x, c)
3127
v = vector(x, 2, 3)
3128
if c then
3129
return v.X + v.Y
3130
else
3131
return v.Z
3132
end
3133
end
3134
)",
3135
/* includeIrTypes */ true
3136
),
3137
R"(
3138
; function getsum($arg0, $arg1) line 4
3139
; U0: vector
3140
bb_bytecode_0:
3141
implicit CHECK_SAFE_ENV exit(0)
3142
STORE_DOUBLE R4, 2
3143
STORE_TAG R4, tnumber
3144
STORE_DOUBLE R5, 3
3145
STORE_TAG R5, tnumber
3146
CHECK_TAG R0, tnumber, exit(4)
3147
%11 = LOAD_DOUBLE R0
3148
%14 = NUM_TO_FLOAT %11
3149
STORE_VECTOR R2, %14, 2, 3
3150
STORE_TAG R2, tvector
3151
%20 = LOAD_TVALUE R2, 0i, tvector
3152
SET_UPVALUE U0, %20, tvector
3153
JUMP_IF_FALSY R1, bb_bytecode_1, bb_3
3154
bb_3:
3155
%23 = GET_UPVALUE U0
3156
STORE_TVALUE R3, %23
3157
CHECK_TAG R3, tvector, exit(11)
3158
%27 = EXTRACT_VEC %23, 0i
3159
%28 = FLOAT_TO_NUM %27
3160
STORE_TVALUE R4, %23
3161
%35 = EXTRACT_VEC %23, 1i
3162
%36 = FLOAT_TO_NUM %35
3163
%45 = ADD_NUM %28, %36
3164
STORE_DOUBLE R2, %45
3165
STORE_TAG R2, tnumber
3166
INTERRUPT 17u
3167
RETURN R2, 1i
3168
bb_bytecode_1:
3169
%50 = GET_UPVALUE U0
3170
STORE_TVALUE R2, %50
3171
CHECK_TAG R2, tvector, exit(19)
3172
%54 = EXTRACT_VEC %50, 2i
3173
%55 = FLOAT_TO_NUM %54
3174
STORE_DOUBLE R2, %55
3175
STORE_TAG R2, tnumber
3176
INTERRUPT 21u
3177
RETURN R2, 1i
3178
)"
3179
);
3180
}
3181
#endif
3182
3183
TEST_CASE_FIXTURE(LoweringFixture, "LoadAndMoveTypePropagation")
3184
{
3185
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
3186
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
3187
3188
CHECK_EQ(
3189
"\n" + getCodegenAssembly(
3190
R"(
3191
local function getsum(n)
3192
local seqsum = 0
3193
for i = 1,n do
3194
if i < 10 then
3195
seqsum += i
3196
else
3197
seqsum *= i
3198
end
3199
end
3200
3201
return seqsum
3202
end
3203
)",
3204
/* includeIrTypes */ true
3205
),
3206
R"(
3207
; function getsum($arg0) line 2
3208
; R1: number from 0 to 13
3209
; R4: number from 1 to 11
3210
bb_bytecode_0:
3211
STORE_DOUBLE R1, 0
3212
STORE_TAG R1, tnumber
3213
STORE_DOUBLE R4, 1
3214
STORE_TAG R4, tnumber
3215
%4 = LOAD_TVALUE R0
3216
STORE_TVALUE R2, %4
3217
STORE_DOUBLE R3, 1
3218
STORE_TAG R3, tnumber
3219
CHECK_TAG R2, tnumber, exit(4)
3220
%12 = LOAD_DOUBLE R2
3221
JUMP_CMP_NUM 1, %12, not_le, bb_bytecode_4, bb_bytecode_1
3222
bb_bytecode_1:
3223
INTERRUPT 5u
3224
STORE_DOUBLE R5, 10
3225
STORE_TAG R5, tnumber
3226
CHECK_TAG R4, tnumber, bb_fallback_6
3227
JUMP_CMP_NUM R4, 10, not_lt, bb_bytecode_2, bb_5
3228
bb_5:
3229
CHECK_TAG R1, tnumber, exit(8)
3230
CHECK_TAG R4, tnumber, exit(8)
3231
%32 = LOAD_DOUBLE R1
3232
%34 = ADD_NUM %32, R4
3233
STORE_DOUBLE R1, %34
3234
JUMP bb_bytecode_3
3235
bb_bytecode_2:
3236
CHECK_TAG R1, tnumber, exit(10)
3237
CHECK_TAG R4, tnumber, exit(10)
3238
%41 = LOAD_DOUBLE R1
3239
%43 = MUL_NUM %41, R4
3240
STORE_DOUBLE R1, %43
3241
JUMP bb_bytecode_3
3242
bb_bytecode_3:
3243
%46 = LOAD_DOUBLE R2
3244
%47 = LOAD_DOUBLE R4
3245
%48 = ADD_NUM %47, 1
3246
STORE_DOUBLE R4, %48
3247
JUMP_CMP_NUM %48, %46, le, bb_bytecode_1, bb_bytecode_4
3248
bb_bytecode_4:
3249
INTERRUPT 12u
3250
RETURN R1, 1i
3251
)"
3252
);
3253
}
3254
3255
#if LUA_VECTOR_SIZE == 3
3256
TEST_CASE_FIXTURE(LoweringFixture, "ArgumentTypeRefinement")
3257
{
3258
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
3259
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
3260
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
3261
3262
CHECK_EQ(
3263
"\n" + getCodegenAssembly(
3264
R"(
3265
local function getsum(x, y)
3266
x = vector(1, y, 3)
3267
return x.Y + x.Z
3268
end
3269
)",
3270
/* includeIrTypes */ true
3271
),
3272
R"(
3273
; function getsum($arg0, $arg1) line 2
3274
; R0: vector [argument]
3275
bb_bytecode_0:
3276
implicit CHECK_SAFE_ENV exit(0)
3277
STORE_DOUBLE R3, 1
3278
STORE_TAG R3, tnumber
3279
STORE_DOUBLE R5, 3
3280
STORE_TAG R5, tnumber
3281
CHECK_TAG R1, tnumber, exit(4)
3282
%12 = LOAD_DOUBLE R1
3283
%15 = NUM_TO_FLOAT %12
3284
STORE_VECTOR R2, 1, %15, 3
3285
STORE_TAG R2, tvector
3286
%25 = FLOAT_TO_NUM %15
3287
%40 = ADD_NUM %25, 3
3288
STORE_DOUBLE R2, %40
3289
STORE_TAG R2, tnumber
3290
INTERRUPT 14u
3291
RETURN R2, 1i
3292
)"
3293
);
3294
}
3295
#endif
3296
3297
TEST_CASE_FIXTURE(LoweringFixture, "InlineFunctionType")
3298
{
3299
CHECK_EQ(
3300
"\n" + getCodegenAssembly(
3301
R"(
3302
local function inl(v: vector, s: number)
3303
return v.Y * s
3304
end
3305
3306
local function getsum(x)
3307
return inl(x, 3) + inl(x, 5)
3308
end
3309
)",
3310
/* includeIrTypes */ true
3311
),
3312
R"(
3313
; function inl($arg0, $arg1) line 2
3314
; R0: vector [argument]
3315
; R1: number [argument]
3316
bb_0:
3317
CHECK_TAG R0, tvector, exit(entry)
3318
CHECK_TAG R1, tnumber, exit(entry)
3319
JUMP bb_2
3320
bb_2:
3321
JUMP bb_bytecode_1
3322
bb_bytecode_1:
3323
%8 = LOAD_FLOAT R0, 4i
3324
%9 = FLOAT_TO_NUM %8
3325
%18 = MUL_NUM %9, R1
3326
STORE_DOUBLE R2, %18
3327
STORE_TAG R2, tnumber
3328
INTERRUPT 3u
3329
RETURN R2, 1i
3330
; function getsum($arg0) line 6
3331
; R0: vector from 0 to 3
3332
; R0: vector from 3 to 6
3333
bb_bytecode_0:
3334
CHECK_TAG R0, tvector, exit(0)
3335
%2 = LOAD_FLOAT R0, 4i
3336
%3 = FLOAT_TO_NUM %2
3337
%9 = MUL_NUM %3, 3
3338
%21 = MUL_NUM %3, 5
3339
%30 = ADD_NUM %9, %21
3340
STORE_DOUBLE R1, %30
3341
STORE_TAG R1, tnumber
3342
INTERRUPT 7u
3343
RETURN R1, 1i
3344
)"
3345
);
3346
}
3347
3348
TEST_CASE_FIXTURE(LoweringFixture, "ResolveTablePathTypes")
3349
{
3350
CHECK_EQ(
3351
"\n" + getCodegenAssembly(
3352
R"(
3353
type Vertex = {pos: vector, normal: vector}
3354
3355
local function foo(arr: {Vertex}, i)
3356
local v = arr[i]
3357
3358
return v.pos.Y
3359
end
3360
)",
3361
/* includeIrTypes */ true,
3362
/* debugLevel */ 2
3363
),
3364
R"(
3365
; function foo(arr, i) line 4
3366
; R0: table [argument 'arr']
3367
; R2: table from 0 to 6 [local 'v']
3368
; R3: vector from 3 to 5
3369
bb_0:
3370
CHECK_TAG R0, ttable, exit(entry)
3371
JUMP bb_2
3372
bb_2:
3373
JUMP bb_bytecode_1
3374
bb_bytecode_1:
3375
CHECK_TAG R1, tnumber, bb_fallback_3
3376
%8 = LOAD_POINTER R0
3377
%9 = LOAD_DOUBLE R1
3378
%10 = TRY_NUM_TO_INDEX %9, bb_fallback_3
3379
%11 = SUB_INT %10, 1i
3380
CHECK_ARRAY_SIZE %8, %11, bb_fallback_3
3381
CHECK_NO_METATABLE %8, bb_fallback_3
3382
%14 = GET_ARR_ADDR %8, %11
3383
%15 = LOAD_TVALUE %14
3384
STORE_TVALUE R2, %15
3385
JUMP bb_4
3386
bb_4:
3387
CHECK_TAG R2, ttable, exit(1)
3388
%23 = LOAD_POINTER R2
3389
%24 = GET_SLOT_NODE_ADDR %23, 1u, K0 ('pos')
3390
CHECK_SLOT_MATCH %24, K0 ('pos'), bb_fallback_5
3391
%26 = LOAD_TVALUE %24, 0i
3392
STORE_TVALUE R3, %26
3393
JUMP bb_6
3394
bb_6:
3395
CHECK_TAG R3, tvector, exit(3)
3396
%33 = LOAD_FLOAT R3, 4i
3397
%34 = FLOAT_TO_NUM %33
3398
STORE_DOUBLE R3, %34
3399
STORE_TAG R3, tnumber
3400
INTERRUPT 5u
3401
RETURN R3, 1i
3402
)"
3403
);
3404
}
3405
3406
TEST_CASE_FIXTURE(LoweringFixture, "ResolvableSimpleMath")
3407
{
3408
CHECK_EQ(
3409
"\n" + getCodegenHeader(R"(
3410
type Vertex = { p: vector, uv: vector, n: vector, t: vector, b: vector, h: number }
3411
local mesh: { vertices: {Vertex}, indices: {number} } = ...
3412
3413
local function compute()
3414
for i = 1,#mesh.indices,3 do
3415
local a = mesh.vertices[mesh.indices[i]]
3416
local b = mesh.vertices[mesh.indices[i + 1]]
3417
local c = mesh.vertices[mesh.indices[i + 2]]
3418
3419
local vba = b.p - a.p
3420
local vca = c.p - a.p
3421
3422
local uvba = b.uv - a.uv
3423
local uvca = c.uv - a.uv
3424
3425
local r = 1.0 / (uvba.X * uvca.Y - uvca.X * uvba.Y);
3426
3427
local sdir = (uvca.Y * vba - uvba.Y * vca) * r
3428
3429
a.t += sdir
3430
end
3431
end
3432
)"),
3433
R"(
3434
; function compute() line 5
3435
; U0: table ['mesh']
3436
; R2: number from 0 to 78 [local 'i']
3437
; R3: table from 7 to 78 [local 'a']
3438
; R4: table from 15 to 78 [local 'b']
3439
; R5: table from 24 to 78 [local 'c']
3440
; R6: vector from 33 to 78 [local 'vba']
3441
; R7: vector from 37 to 38
3442
; R7: vector from 38 to 78 [local 'vca']
3443
; R8: vector from 37 to 38
3444
; R8: vector from 42 to 43
3445
; R8: vector from 43 to 78 [local 'uvba']
3446
; R9: vector from 42 to 43
3447
; R9: vector from 47 to 48
3448
; R9: vector from 48 to 78 [local 'uvca']
3449
; R10: vector from 47 to 48
3450
; R10: vector from 52 to 53
3451
; R10: number from 53 to 78 [local 'r']
3452
; R11: vector from 52 to 53
3453
; R11: vector from 65 to 78 [local 'sdir']
3454
; R12: vector from 72 to 73
3455
; R12: vector from 75 to 76
3456
; R13: vector from 71 to 72
3457
; R14: vector from 71 to 72
3458
)"
3459
);
3460
}
3461
3462
TEST_CASE_FIXTURE(LoweringFixture, "ResolvableFunctionReturns")
3463
{
3464
ScopedFastFlag luauCompileExtraTypes{FFlag::LuauCompileExtraTypes, true};
3465
3466
CHECK_EQ(
3467
"\n" + getCodegenHeader(R"(
3468
type Vertex = { p: vector, uv: vector, n: vector, t: vector, b: vector, h: number }
3469
local mesh: { vertices: {Vertex}, indices: {number} } = ...
3470
3471
local function temp(b: vector, c: vector) : number
3472
return 1 / (b.X * c.Y - c.X * b.Y)
3473
end
3474
3475
local function compute()
3476
for i = 1,#mesh.indices,3 do
3477
local a = mesh.vertices[mesh.indices[i]]
3478
local b = mesh.vertices[mesh.indices[i + 1]]
3479
local c = mesh.vertices[mesh.indices[i + 2]]
3480
3481
local uvba = b.uv - a.uv
3482
local uvca = c.uv - a.uv
3483
3484
local r = temp(uvba, uvca);
3485
3486
a.t += a.p * r
3487
end
3488
end
3489
)"),
3490
R"(
3491
; function compute() line 9
3492
; U0: table ['mesh']
3493
; R2: number from 0 to 63 [local 'i']
3494
; R3: table from 7 to 63 [local 'a']
3495
; R4: table from 15 to 63 [local 'b']
3496
; R5: table from 24 to 63 [local 'c']
3497
; R6: vector from 43 to 55 [local 'b']
3498
; R6: vector from 33 to 63 [local 'uvba']
3499
; R7: vector from 37 to 38
3500
; R7: vector from 43 to 55 [local 'c']
3501
; R7: vector from 38 to 63 [local 'uvca']
3502
; R8: vector from 37 to 38
3503
; R8: vector from 42 to 43
3504
; R8: number from 43 to 63 [local 'r']
3505
; R9: vector from 42 to 43
3506
; R9: vector from 60 to 61
3507
; R10: vector from 60 to 61
3508
; R11: vector from 59 to 60
3509
)"
3510
);
3511
}
3512
3513
TEST_CASE_FIXTURE(LoweringFixture, "ResolveVectorNamecalls")
3514
{
3515
CHECK_EQ(
3516
"\n" + getCodegenAssembly(
3517
R"(
3518
type Vertex = {pos: vector, normal: vector}
3519
3520
local function foo(arr: {Vertex}, i)
3521
return arr[i].normal:Dot(vector(0.707, 0, 0.707))
3522
end
3523
)",
3524
/* includeIrTypes */ true
3525
),
3526
R"(
3527
; function foo($arg0, $arg1) line 4
3528
; R0: table [argument]
3529
; R2: vector from 4 to 6
3530
bb_0:
3531
CHECK_TAG R0, ttable, exit(entry)
3532
JUMP bb_2
3533
bb_2:
3534
JUMP bb_bytecode_1
3535
bb_bytecode_1:
3536
CHECK_TAG R1, tnumber, bb_fallback_3
3537
%8 = LOAD_POINTER R0
3538
%9 = LOAD_DOUBLE R1
3539
%10 = TRY_NUM_TO_INDEX %9, bb_fallback_3
3540
%11 = SUB_INT %10, 1i
3541
CHECK_ARRAY_SIZE %8, %11, bb_fallback_3
3542
CHECK_NO_METATABLE %8, bb_fallback_3
3543
%14 = GET_ARR_ADDR %8, %11
3544
%15 = LOAD_TVALUE %14
3545
STORE_TVALUE R2, %15
3546
JUMP bb_4
3547
bb_4:
3548
CHECK_TAG R2, ttable, bb_fallback_5
3549
%23 = LOAD_POINTER R2
3550
%24 = GET_SLOT_NODE_ADDR %23, 1u, K0 ('normal')
3551
CHECK_SLOT_MATCH %24, K0 ('normal'), bb_fallback_5
3552
%26 = LOAD_TVALUE %24, 0i
3553
STORE_TVALUE R2, %26
3554
JUMP bb_6
3555
bb_6:
3556
%31 = LOAD_TVALUE K1 (0.707000017, 0, 0.707000017), 0i, tvector
3557
STORE_TVALUE R4, %31
3558
CHECK_TAG R2, tvector, exit(4)
3559
%37 = LOAD_FLOAT R2, 0i
3560
%39 = MUL_FLOAT %37, 0.7070000171661377
3561
%40 = LOAD_FLOAT R2, 4i
3562
%42 = MUL_FLOAT %40, 0
3563
%43 = LOAD_FLOAT R2, 8i
3564
%45 = MUL_FLOAT %43, 0.7070000171661377
3565
%46 = ADD_FLOAT %39, %42
3566
%47 = ADD_FLOAT %46, %45
3567
%48 = FLOAT_TO_NUM %47
3568
STORE_DOUBLE R2, %48
3569
STORE_TAG R2, tnumber
3570
ADJUST_STACK_TO_REG R2, 1i
3571
INTERRUPT 7u
3572
RETURN R2, -1i
3573
)"
3574
);
3575
}
3576
3577
TEST_CASE_FIXTURE(LoweringFixture, "ImmediateTypeAnnotationHelp")
3578
{
3579
CHECK_EQ(
3580
"\n" + getCodegenAssembly(
3581
R"(
3582
local function foo(arr, i)
3583
return (arr[i] :: vector) / 5
3584
end
3585
)",
3586
/* includeIrTypes */ true
3587
),
3588
R"(
3589
; function foo($arg0, $arg1) line 2
3590
; R3: vector from 1 to 2
3591
bb_bytecode_0:
3592
CHECK_TAG R0, ttable, bb_fallback_1
3593
CHECK_TAG R1, tnumber, bb_fallback_1
3594
%4 = LOAD_POINTER R0
3595
%5 = LOAD_DOUBLE R1
3596
%6 = TRY_NUM_TO_INDEX %5, bb_fallback_1
3597
%7 = SUB_INT %6, 1i
3598
CHECK_ARRAY_SIZE %4, %7, bb_fallback_1
3599
CHECK_NO_METATABLE %4, bb_fallback_1
3600
%10 = GET_ARR_ADDR %4, %7
3601
%11 = LOAD_TVALUE %10
3602
STORE_TVALUE R3, %11
3603
JUMP bb_2
3604
bb_2:
3605
CHECK_TAG R3, tvector, exit(1)
3606
%19 = LOAD_TVALUE R3, 0i, tvector
3607
%21 = FLOAT_TO_VEC 5
3608
%22 = DIV_VEC %19, %21
3609
%23 = TAG_VECTOR %22
3610
STORE_TVALUE R2, %23
3611
INTERRUPT 2u
3612
RETURN R2, 1i
3613
)"
3614
);
3615
}
3616
3617
#if LUA_VECTOR_SIZE == 3
3618
TEST_CASE_FIXTURE(LoweringFixture, "UnaryTypeResolve")
3619
{
3620
CHECK_EQ(
3621
"\n" + getCodegenHeader(R"(
3622
local function foo(a, b: vector, c)
3623
local d = not a
3624
local e = -b
3625
local f = #c
3626
return (if d then e else vector(f, 2, 3)).X
3627
end
3628
)"),
3629
R"(
3630
; function foo(a, b, c) line 2
3631
; R1: vector [argument 'b']
3632
; R3: boolean from 0 to 17 [local 'd']
3633
; R4: vector from 1 to 17 [local 'e']
3634
; R5: number from 2 to 17 [local 'f']
3635
; R6: vector from 14 to 16
3636
)"
3637
);
3638
}
3639
#endif
3640
3641
TEST_CASE_FIXTURE(LoweringFixture, "ForInManualAnnotation")
3642
{
3643
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
3644
3645
CHECK_EQ(
3646
"\n" + getCodegenAssembly(
3647
R"(
3648
type Vertex = {pos: vector, normal: vector}
3649
3650
local function foo(a: {Vertex})
3651
local sum = 0
3652
for k, v: Vertex in ipairs(a) do
3653
sum += v.pos.X
3654
end
3655
return sum
3656
end
3657
)",
3658
/* includeIrTypes */ true,
3659
/* debugLevel */ 2
3660
),
3661
R"(
3662
; function foo(a) line 4
3663
; R0: table [argument 'a']
3664
; R1: number from 0 to 14 [local 'sum']
3665
; R5: number from 5 to 11 [local 'k']
3666
; R6: table from 5 to 11 [local 'v']
3667
; R7: vector from 8 to 10
3668
bb_0:
3669
CHECK_TAG R0, ttable, exit(entry)
3670
JUMP bb_4
3671
bb_4:
3672
JUMP bb_bytecode_1
3673
bb_bytecode_1:
3674
implicit CHECK_SAFE_ENV exit(0)
3675
STORE_DOUBLE R1, 0
3676
STORE_TAG R1, tnumber
3677
GET_CACHED_IMPORT R2, K1 (nil), 1073741824u ('ipairs'), 2u
3678
%8 = LOAD_TVALUE R0, 0i, ttable
3679
STORE_TVALUE R3, %8
3680
INTERRUPT 4u
3681
SET_SAVEDPC 5u
3682
CALL R2, 1i, 3i
3683
CHECK_SAFE_ENV exit(5)
3684
CHECK_TAG R3, ttable, bb_fallback_5
3685
CHECK_TAG R4, tnumber, bb_fallback_5
3686
JUMP_CMP_NUM R4, 0, not_eq, bb_fallback_5, bb_6
3687
bb_6:
3688
STORE_TAG R2, tnil
3689
STORE_POINTER R4, 0i
3690
STORE_EXTRA R4, 128i
3691
STORE_TAG R4, tlightuserdata
3692
JUMP bb_bytecode_3
3693
bb_bytecode_2:
3694
CHECK_TAG R6, ttable, exit(6)
3695
%28 = LOAD_POINTER R6
3696
%29 = GET_SLOT_NODE_ADDR %28, 6u, K2 ('pos')
3697
CHECK_SLOT_MATCH %29, K2 ('pos'), bb_fallback_7
3698
%31 = LOAD_TVALUE %29, 0i
3699
STORE_TVALUE R7, %31
3700
JUMP bb_8
3701
bb_8:
3702
CHECK_TAG R7, tvector, exit(8)
3703
%38 = LOAD_FLOAT R7, 0i
3704
%39 = FLOAT_TO_NUM %38
3705
STORE_DOUBLE R7, %39
3706
STORE_TAG R7, tnumber
3707
CHECK_TAG R1, tnumber, exit(10)
3708
%46 = LOAD_DOUBLE R1
3709
%48 = ADD_NUM %46, %39
3710
STORE_DOUBLE R1, %48
3711
JUMP bb_bytecode_3
3712
bb_bytecode_3:
3713
INTERRUPT 11u
3714
CHECK_TAG R2, tnil, bb_fallback_10
3715
%54 = LOAD_POINTER R3
3716
%55 = LOAD_INT R4
3717
%56 = GET_ARR_ADDR %54, %55
3718
CHECK_ARRAY_SIZE %54, %55, bb_9
3719
%58 = LOAD_TAG %56
3720
JUMP_EQ_TAG %58, tnil, bb_9, bb_11
3721
bb_11:
3722
%60 = ADD_INT %55, 1i
3723
STORE_INT R4, %60
3724
%62 = INT_TO_NUM %60
3725
STORE_DOUBLE R5, %62
3726
STORE_TAG R5, tnumber
3727
%65 = LOAD_TVALUE %56
3728
STORE_TVALUE R6, %65
3729
JUMP bb_bytecode_2
3730
bb_9:
3731
INTERRUPT 13u
3732
RETURN R1, 1i
3733
)"
3734
);
3735
}
3736
3737
TEST_CASE_FIXTURE(LoweringFixture, "ForInAutoAnnotationIpairs")
3738
{
3739
CHECK_EQ(
3740
"\n" + getCodegenHeader(R"(
3741
type Vertex = {pos: vector, normal: vector}
3742
3743
local function foo(a: {Vertex})
3744
local sum = 0
3745
for k, v in ipairs(a) do
3746
local n = v.pos.X
3747
sum += n
3748
end
3749
return sum
3750
end
3751
)"),
3752
R"(
3753
; function foo(a) line 4
3754
; R0: table [argument 'a']
3755
; R1: number from 0 to 14 [local 'sum']
3756
; R5: number from 5 to 11 [local 'k']
3757
; R6: table from 5 to 11 [local 'v']
3758
; R7: vector from 8 to 10
3759
; R7: number from 6 to 11 [local 'n']
3760
)"
3761
);
3762
}
3763
3764
TEST_CASE_FIXTURE(LoweringFixture, "ForInAutoAnnotationPairs")
3765
{
3766
CHECK_EQ(
3767
"\n" + getCodegenHeader(R"(
3768
type Vertex = {pos: vector, normal: vector}
3769
3770
local function foo(a: {[string]: Vertex})
3771
local sum = 0
3772
for k, v in pairs(a) do
3773
local n = v.pos.X
3774
sum += n
3775
end
3776
return sum
3777
end
3778
)"),
3779
R"(
3780
; function foo(a) line 4
3781
; R0: table [argument 'a']
3782
; R1: number from 0 to 14 [local 'sum']
3783
; R5: string from 5 to 11 [local 'k']
3784
; R6: table from 5 to 11 [local 'v']
3785
; R7: vector from 8 to 10
3786
; R7: number from 6 to 11 [local 'n']
3787
)"
3788
);
3789
}
3790
3791
TEST_CASE_FIXTURE(LoweringFixture, "ForInAutoAnnotationGeneric")
3792
{
3793
CHECK_EQ(
3794
"\n" + getCodegenHeader(R"(
3795
type Vertex = {pos: vector, normal: vector}
3796
3797
local function foo(a: {Vertex})
3798
local sum = 0
3799
for k, v in a do
3800
local n = v.pos.X
3801
sum += n
3802
end
3803
return sum
3804
end
3805
)"),
3806
R"(
3807
; function foo(a) line 4
3808
; R0: table [argument 'a']
3809
; R1: number from 0 to 13 [local 'sum']
3810
; R5: number from 4 to 10 [local 'k']
3811
; R6: table from 4 to 10 [local 'v']
3812
; R7: vector from 7 to 9
3813
; R7: number from 5 to 10 [local 'n']
3814
)"
3815
);
3816
}
3817
3818
TEST_CASE_FIXTURE(LoweringFixture, "CustomUserdataTypes")
3819
{
3820
// This test requires runtime component to be present
3821
if (!Luau::CodeGen::isSupported())
3822
return;
3823
3824
CHECK_EQ(
3825
"\n" + getCodegenHeader(R"(
3826
local function foo(v: vec2, x: mat3)
3827
return v.X * x
3828
end
3829
)"),
3830
R"(
3831
; function foo(v, x) line 2
3832
; R0: vec2 [argument 'v']
3833
; R1: mat3 [argument 'x']
3834
)"
3835
);
3836
}
3837
3838
TEST_CASE_FIXTURE(LoweringFixture, "CustomUserdataPropertyAccess")
3839
{
3840
// This test requires runtime component to be present
3841
if (!Luau::CodeGen::isSupported())
3842
return;
3843
3844
CHECK_EQ(
3845
"\n" + getCodegenAssembly(
3846
R"(
3847
local function foo(v: vec2)
3848
return v.X + v.Y
3849
end
3850
)",
3851
/* includeIrTypes */ true
3852
),
3853
R"(
3854
; function foo($arg0) line 2
3855
; R0: vec2 [argument]
3856
bb_0:
3857
CHECK_TAG R0, tuserdata, exit(entry)
3858
JUMP bb_2
3859
bb_2:
3860
JUMP bb_bytecode_1
3861
bb_bytecode_1:
3862
%6 = LOAD_POINTER R0
3863
CHECK_USERDATA_TAG %6, 12i, exit(0)
3864
%8 = BUFFER_READF32 %6, 0i, tuserdata
3865
%9 = FLOAT_TO_NUM %8
3866
%16 = BUFFER_READF32 %6, 4i, tuserdata
3867
%17 = FLOAT_TO_NUM %16
3868
%26 = ADD_NUM %9, %17
3869
STORE_DOUBLE R1, %26
3870
STORE_TAG R1, tnumber
3871
INTERRUPT 5u
3872
RETURN R1, 1i
3873
)"
3874
);
3875
}
3876
3877
TEST_CASE_FIXTURE(LoweringFixture, "CustomUserdataPropertyAccess2")
3878
{
3879
// This test requires runtime component to be present
3880
if (!Luau::CodeGen::isSupported())
3881
return;
3882
3883
CHECK_EQ(
3884
"\n" + getCodegenAssembly(
3885
R"(
3886
local function foo(a: mat3)
3887
return a.Row1 * a.Row2
3888
end
3889
)",
3890
/* includeIrTypes */ true
3891
),
3892
R"(
3893
; function foo($arg0) line 2
3894
; R0: mat3 [argument]
3895
bb_0:
3896
CHECK_TAG R0, tuserdata, exit(entry)
3897
JUMP bb_2
3898
bb_2:
3899
JUMP bb_bytecode_1
3900
bb_bytecode_1:
3901
FALLBACK_GETTABLEKS 0u, R2, R0, K0 ('Row1')
3902
FALLBACK_GETTABLEKS 2u, R3, R0, K1 ('Row2')
3903
CHECK_TAG R2, tvector, exit(4)
3904
CHECK_TAG R3, tvector, exit(4)
3905
%14 = LOAD_TVALUE R2, 0i, tvector
3906
%15 = LOAD_TVALUE R3, 0i, tvector
3907
%16 = MUL_VEC %14, %15
3908
%17 = TAG_VECTOR %16
3909
STORE_TVALUE R1, %17
3910
INTERRUPT 5u
3911
RETURN R1, 1i
3912
)"
3913
);
3914
}
3915
3916
TEST_CASE_FIXTURE(LoweringFixture, "CustomUserdataNamecall1")
3917
{
3918
// This test requires runtime component to be present
3919
if (!Luau::CodeGen::isSupported())
3920
return;
3921
3922
CHECK_EQ(
3923
"\n" + getCodegenAssembly(
3924
R"(
3925
local function foo(a: vec2, b: vec2)
3926
return a:Dot(b)
3927
end
3928
)",
3929
/* includeIrTypes */ true
3930
),
3931
R"(
3932
; function foo($arg0, $arg1) line 2
3933
; R0: vec2 [argument]
3934
; R1: vec2 [argument]
3935
bb_0:
3936
CHECK_TAG R0, tuserdata, exit(entry)
3937
CHECK_TAG R1, tuserdata, exit(entry)
3938
JUMP bb_2
3939
bb_2:
3940
JUMP bb_bytecode_1
3941
bb_bytecode_1:
3942
%6 = LOAD_TVALUE R1, 0i, tuserdata
3943
STORE_TVALUE R4, %6
3944
%10 = LOAD_POINTER R0
3945
CHECK_USERDATA_TAG %10, 12i, exit(1)
3946
%14 = LOAD_POINTER R4
3947
CHECK_USERDATA_TAG %14, 12i, exit(1)
3948
%16 = BUFFER_READF32 %10, 0i, tuserdata
3949
%17 = BUFFER_READF32 %14, 0i, tuserdata
3950
%18 = FLOAT_TO_NUM %16
3951
%19 = FLOAT_TO_NUM %17
3952
%20 = MUL_NUM %18, %19
3953
%21 = BUFFER_READF32 %10, 4i, tuserdata
3954
%22 = BUFFER_READF32 %14, 4i, tuserdata
3955
%23 = FLOAT_TO_NUM %21
3956
%24 = FLOAT_TO_NUM %22
3957
%25 = MUL_NUM %23, %24
3958
%26 = ADD_NUM %20, %25
3959
STORE_DOUBLE R2, %26
3960
STORE_TAG R2, tnumber
3961
ADJUST_STACK_TO_REG R2, 1i
3962
INTERRUPT 4u
3963
RETURN R2, -1i
3964
)"
3965
);
3966
}
3967
3968
TEST_CASE_FIXTURE(LoweringFixture, "CustomUserdataNamecall2")
3969
{
3970
// This test requires runtime component to be present
3971
if (!Luau::CodeGen::isSupported())
3972
return;
3973
3974
CHECK_EQ(
3975
"\n" + getCodegenAssembly(
3976
R"(
3977
local function foo(a: vec2, b: vec2)
3978
return a:Min(b)
3979
end
3980
)",
3981
/* includeIrTypes */ true
3982
),
3983
R"(
3984
; function foo($arg0, $arg1) line 2
3985
; R0: vec2 [argument]
3986
; R1: vec2 [argument]
3987
bb_0:
3988
CHECK_TAG R0, tuserdata, exit(entry)
3989
CHECK_TAG R1, tuserdata, exit(entry)
3990
JUMP bb_2
3991
bb_2:
3992
JUMP bb_bytecode_1
3993
bb_bytecode_1:
3994
%6 = LOAD_TVALUE R1, 0i, tuserdata
3995
STORE_TVALUE R4, %6
3996
%10 = LOAD_POINTER R0
3997
CHECK_USERDATA_TAG %10, 12i, exit(1)
3998
%14 = LOAD_POINTER R4
3999
CHECK_USERDATA_TAG %14, 12i, exit(1)
4000
%16 = BUFFER_READF32 %10, 0i, tuserdata
4001
%17 = BUFFER_READF32 %14, 0i, tuserdata
4002
%18 = FLOAT_TO_NUM %16
4003
%19 = FLOAT_TO_NUM %17
4004
%20 = MIN_NUM %18, %19
4005
%21 = BUFFER_READF32 %10, 4i, tuserdata
4006
%22 = BUFFER_READF32 %14, 4i, tuserdata
4007
%23 = FLOAT_TO_NUM %21
4008
%24 = FLOAT_TO_NUM %22
4009
%25 = MIN_NUM %23, %24
4010
%26 = NUM_TO_FLOAT %20
4011
%27 = NUM_TO_FLOAT %25
4012
CHECK_GC
4013
%29 = NEW_USERDATA 8i, 12i
4014
BUFFER_WRITEF32 %29, 0i, %26, tuserdata
4015
BUFFER_WRITEF32 %29, 4i, %27, tuserdata
4016
STORE_POINTER R2, %29
4017
STORE_TAG R2, tuserdata
4018
ADJUST_STACK_TO_REG R2, 1i
4019
INTERRUPT 4u
4020
RETURN R2, -1i
4021
)"
4022
);
4023
}
4024
4025
TEST_CASE_FIXTURE(LoweringFixture, "CustomUserdataMetamethodDirectFlow")
4026
{
4027
// This test requires runtime component to be present
4028
if (!Luau::CodeGen::isSupported())
4029
return;
4030
4031
CHECK_EQ(
4032
"\n" + getCodegenAssembly(
4033
R"(
4034
local function foo(a: mat3, b: mat3)
4035
return a * b
4036
end
4037
)",
4038
/* includeIrTypes */ true
4039
),
4040
R"(
4041
; function foo($arg0, $arg1) line 2
4042
; R0: mat3 [argument]
4043
; R1: mat3 [argument]
4044
bb_0:
4045
CHECK_TAG R0, tuserdata, exit(entry)
4046
CHECK_TAG R1, tuserdata, exit(entry)
4047
JUMP bb_2
4048
bb_2:
4049
JUMP bb_bytecode_1
4050
bb_bytecode_1:
4051
SET_SAVEDPC 1u
4052
DO_ARITH R2, R0, R1, 10i
4053
INTERRUPT 1u
4054
RETURN R2, 1i
4055
)"
4056
);
4057
}
4058
4059
TEST_CASE_FIXTURE(LoweringFixture, "CustomUserdataMetamethodDirectFlow2")
4060
{
4061
// This test requires runtime component to be present
4062
if (!Luau::CodeGen::isSupported())
4063
return;
4064
4065
CHECK_EQ(
4066
"\n" + getCodegenAssembly(
4067
R"(
4068
local function foo(a: mat3)
4069
return -a
4070
end
4071
)",
4072
/* includeIrTypes */ true
4073
),
4074
R"(
4075
; function foo($arg0) line 2
4076
; R0: mat3 [argument]
4077
bb_0:
4078
CHECK_TAG R0, tuserdata, exit(entry)
4079
JUMP bb_2
4080
bb_2:
4081
JUMP bb_bytecode_1
4082
bb_bytecode_1:
4083
SET_SAVEDPC 1u
4084
DO_ARITH R1, R0, R0, 15i
4085
INTERRUPT 1u
4086
RETURN R1, 1i
4087
)"
4088
);
4089
}
4090
4091
TEST_CASE_FIXTURE(LoweringFixture, "CustomUserdataMetamethodDirectFlow3")
4092
{
4093
// This test requires runtime component to be present
4094
if (!Luau::CodeGen::isSupported())
4095
return;
4096
4097
CHECK_EQ(
4098
"\n" + getCodegenAssembly(
4099
R"(
4100
local function foo(a: sequence)
4101
return #a
4102
end
4103
)",
4104
/* includeIrTypes */ true
4105
),
4106
R"(
4107
; function foo($arg0) line 2
4108
; R0: userdata [argument]
4109
bb_0:
4110
CHECK_TAG R0, tuserdata, exit(entry)
4111
JUMP bb_2
4112
bb_2:
4113
JUMP bb_bytecode_1
4114
bb_bytecode_1:
4115
SET_SAVEDPC 1u
4116
DO_LEN R1, R0
4117
INTERRUPT 1u
4118
RETURN R1, 1i
4119
)"
4120
);
4121
}
4122
4123
TEST_CASE_FIXTURE(LoweringFixture, "CustomUserdataMetamethod")
4124
{
4125
ScopedFastFlag luauCodegenGcoDse{FFlag::LuauCodegenGcoDse2, true};
4126
4127
// This test requires runtime component to be present
4128
if (!Luau::CodeGen::isSupported())
4129
return;
4130
4131
CHECK_EQ(
4132
"\n" + getCodegenAssembly(
4133
R"(
4134
local function foo(a: vec2, b: vec2, c: vec2)
4135
return -c + a * b
4136
end
4137
)",
4138
/* includeIrTypes */ true
4139
),
4140
R"(
4141
; function foo($arg0, $arg1, $arg2) line 2
4142
; R0: vec2 [argument]
4143
; R1: vec2 [argument]
4144
; R2: vec2 [argument]
4145
bb_0:
4146
CHECK_TAG R0, tuserdata, exit(entry)
4147
CHECK_TAG R1, tuserdata, exit(entry)
4148
CHECK_TAG R2, tuserdata, exit(entry)
4149
JUMP bb_2
4150
bb_2:
4151
JUMP bb_bytecode_1
4152
bb_bytecode_1:
4153
%10 = LOAD_POINTER R2
4154
CHECK_USERDATA_TAG %10, 12i, exit(0)
4155
%12 = BUFFER_READF32 %10, 0i, tuserdata
4156
%13 = BUFFER_READF32 %10, 4i, tuserdata
4157
%14 = UNM_FLOAT %12
4158
%15 = UNM_FLOAT %13
4159
CHECK_GC
4160
%17 = NEW_USERDATA 8i, 12i
4161
BUFFER_WRITEF32 %17, 0i, %14, tuserdata
4162
BUFFER_WRITEF32 %17, 4i, %15, tuserdata
4163
STORE_POINTER R4, %17
4164
STORE_TAG R4, tuserdata
4165
%26 = LOAD_POINTER R0
4166
CHECK_USERDATA_TAG %26, 12i, exit(1)
4167
%28 = LOAD_POINTER R1
4168
CHECK_USERDATA_TAG %28, 12i, exit(1)
4169
%30 = BUFFER_READF32 %26, 0i, tuserdata
4170
%31 = BUFFER_READF32 %28, 0i, tuserdata
4171
%32 = MUL_FLOAT %30, %31
4172
%33 = BUFFER_READF32 %26, 4i, tuserdata
4173
%34 = BUFFER_READF32 %28, 4i, tuserdata
4174
%35 = MUL_FLOAT %33, %34
4175
%52 = ADD_FLOAT %14, %32
4176
%55 = ADD_FLOAT %15, %35
4177
%57 = NEW_USERDATA 8i, 12i
4178
BUFFER_WRITEF32 %57, 0i, %52, tuserdata
4179
BUFFER_WRITEF32 %57, 4i, %55, tuserdata
4180
STORE_POINTER R3, %57
4181
STORE_TAG R3, tuserdata
4182
INTERRUPT 3u
4183
RETURN R3, 1i
4184
)"
4185
);
4186
}
4187
4188
TEST_CASE_FIXTURE(LoweringFixture, "CustomUserdataMapping")
4189
{
4190
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
4191
4192
// This test requires runtime component to be present
4193
if (!Luau::CodeGen::isSupported())
4194
return;
4195
4196
CHECK_EQ(
4197
"\n" + getCodegenAssembly(
4198
R"(
4199
local function foo(a: mat3)
4200
print(a, vec2.create(0, 0))
4201
end
4202
)",
4203
/* includeIrTypes */ true
4204
),
4205
R"(
4206
; function foo($arg0) line 2
4207
; R0: mat3 [argument]
4208
bb_0:
4209
CHECK_TAG R0, tuserdata, exit(entry)
4210
JUMP bb_2
4211
bb_2:
4212
JUMP bb_bytecode_1
4213
bb_bytecode_1:
4214
implicit CHECK_SAFE_ENV exit(0)
4215
GET_CACHED_IMPORT R1, K1 (nil), 1073741824u ('print'), 1u
4216
%6 = LOAD_TVALUE R0, 0i, tuserdata
4217
STORE_TVALUE R2, %6
4218
GET_CACHED_IMPORT R3, K4 (nil), 2149583872u ('vec2'.'create'), 4u
4219
STORE_DOUBLE R4, 0
4220
STORE_TAG R4, tnumber
4221
STORE_DOUBLE R5, 0
4222
STORE_TAG R5, tnumber
4223
INTERRUPT 7u
4224
SET_SAVEDPC 8u
4225
CALL R3, 2i, -1i
4226
INTERRUPT 8u
4227
SET_SAVEDPC 9u
4228
CALL R1, -1i, 0i
4229
INTERRUPT 9u
4230
RETURN R0, 0i
4231
)"
4232
);
4233
}
4234
4235
TEST_CASE_FIXTURE(LoweringFixture, "LibraryFieldTypesAndConstants")
4236
{
4237
CHECK_EQ(
4238
"\n" + getCodegenAssembly(
4239
R"(
4240
local function foo(a: vector)
4241
return Vector3.xAxis * a + Vector3.yAxis
4242
end
4243
)",
4244
/* includeIrTypes */ true
4245
),
4246
R"(
4247
; function foo($arg0) line 2
4248
; R0: vector [argument]
4249
; R2: vector from 3 to 4
4250
; R3: vector from 1 to 2
4251
; R3: vector from 3 to 4
4252
bb_0:
4253
CHECK_TAG R0, tvector, exit(entry)
4254
JUMP bb_2
4255
bb_2:
4256
JUMP bb_bytecode_1
4257
bb_bytecode_1:
4258
%4 = LOAD_TVALUE K0 (1, 0, 0), 0i, tvector
4259
%11 = LOAD_TVALUE R0, 0i, tvector
4260
%12 = MUL_VEC %4, %11
4261
%15 = LOAD_TVALUE K1 (0, 1, 0), 0i, tvector
4262
%23 = ADD_VEC %12, %15
4263
%24 = TAG_VECTOR %23
4264
STORE_TVALUE R1, %24
4265
INTERRUPT 4u
4266
RETURN R1, 1i
4267
)"
4268
);
4269
}
4270
4271
TEST_CASE_FIXTURE(LoweringFixture, "LibraryFieldTypesAndConstants")
4272
{
4273
CHECK_EQ(
4274
"\n" + getCodegenAssembly(
4275
R"(
4276
local function foo(a: vector)
4277
local x = vector.zero
4278
x += a
4279
return x
4280
end
4281
)",
4282
/* includeIrTypes */ true
4283
),
4284
R"(
4285
; function foo($arg0) line 2
4286
; R0: vector [argument]
4287
; R1: vector from 0 to 3
4288
bb_0:
4289
CHECK_TAG R0, tvector, exit(entry)
4290
JUMP bb_2
4291
bb_2:
4292
JUMP bb_bytecode_1
4293
bb_bytecode_1:
4294
%4 = LOAD_TVALUE K0 (0, 0, 0), 0i, tvector
4295
%11 = LOAD_TVALUE R0, 0i, tvector
4296
%12 = ADD_VEC %4, %11
4297
%13 = TAG_VECTOR %12
4298
STORE_TVALUE R1, %13
4299
INTERRUPT 2u
4300
RETURN R1, 1i
4301
)"
4302
);
4303
}
4304
4305
TEST_CASE_FIXTURE(LoweringFixture, "LibraryFieldTypesAndConstantsCApi")
4306
{
4307
CHECK_EQ(
4308
"\n" + getCodegenAssemblyUsingCApi(
4309
R"(
4310
local function foo()
4311
return test.some_nil, test.some_boolean, test.some_number, test.some_vector, test.some_string
4312
end
4313
)",
4314
/* includeIrTypes */ true
4315
),
4316
R"(
4317
; function foo() line 2
4318
bb_bytecode_0:
4319
STORE_TAG R0, tnil
4320
STORE_INT R1, 1i
4321
STORE_TAG R1, tboolean
4322
STORE_DOUBLE R2, 4.75
4323
STORE_TAG R2, tnumber
4324
%5 = LOAD_TVALUE K1 (1, 2, 4), 0i, tvector
4325
STORE_TVALUE R3, %5
4326
%7 = LOAD_TVALUE K2 ('test'), 0i, tstring
4327
STORE_TVALUE R4, %7
4328
INTERRUPT 5u
4329
RETURN R0, 5i
4330
)"
4331
);
4332
}
4333
4334
TEST_CASE_FIXTURE(LoweringFixture, "MathIsNan")
4335
{
4336
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
4337
4338
CHECK_EQ(
4339
"\n" + getCodegenAssembly(R"(
4340
local function foo(a: number)
4341
return math.isnan(a)
4342
end
4343
)"),
4344
R"(
4345
; function foo($arg0) line 2
4346
bb_0:
4347
CHECK_TAG R0, tnumber, exit(entry)
4348
JUMP bb_2
4349
bb_2:
4350
JUMP bb_bytecode_1
4351
bb_bytecode_1:
4352
implicit CHECK_SAFE_ENV exit(0)
4353
%7 = LOAD_DOUBLE R0
4354
%8 = CMP_SPLIT_TVALUE tnumber, tnumber, %7, %7, not_eq
4355
STORE_INT R1, %8
4356
STORE_TAG R1, tboolean
4357
INTERRUPT 5u
4358
RETURN R1, 1i
4359
)"
4360
);
4361
}
4362
4363
TEST_CASE_FIXTURE(LoweringFixture, "Bit32BtestDirect")
4364
{
4365
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
4366
4367
CHECK_EQ(
4368
"\n" + getCodegenAssembly(R"(
4369
local function foo(a: number)
4370
return bit32.btest(a, 0x1f)
4371
end
4372
)"),
4373
R"(
4374
; function foo($arg0) line 2
4375
bb_0:
4376
CHECK_TAG R0, tnumber, exit(entry)
4377
JUMP bb_2
4378
bb_2:
4379
JUMP bb_bytecode_1
4380
bb_bytecode_1:
4381
implicit CHECK_SAFE_ENV exit(0)
4382
%7 = LOAD_DOUBLE R0
4383
%8 = NUM_TO_UINT %7
4384
%10 = BITAND_UINT %8, 31i
4385
%11 = CMP_INT %10, 0i, not_eq
4386
STORE_INT R1, %11
4387
STORE_TAG R1, tboolean
4388
INTERRUPT 7u
4389
RETURN R1, 1i
4390
)"
4391
);
4392
}
4393
4394
TEST_CASE_FIXTURE(LoweringFixture, "Bit32ReplaceDirect")
4395
{
4396
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
4397
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
4398
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
4399
4400
CHECK_EQ(
4401
"\n" + getCodegenAssembly(R"(
4402
local function foo(a: number, b: number)
4403
local x = bit32.band(a, 0x003FFFFF)
4404
local y = bit32.band(b, 0x003FFFFF)
4405
local z = bit32.replace(bit32.rshift(a, 22), bit32.rshift(b, 22), 10, 10)
4406
4407
local v = vector.create(x, y, z)
4408
return v, v.x + v.y -- tests UINT_TO_FLOAT propagation as well
4409
end
4410
)"),
4411
R"(
4412
; function foo($arg0, $arg1) line 2
4413
bb_0:
4414
CHECK_TAG R0, tnumber, exit(entry)
4415
CHECK_TAG R1, tnumber, exit(entry)
4416
JUMP bb_2
4417
bb_2:
4418
JUMP bb_bytecode_1
4419
bb_bytecode_1:
4420
implicit CHECK_SAFE_ENV exit(0)
4421
%9 = LOAD_DOUBLE R0
4422
%10 = NUM_TO_UINT %9
4423
%12 = BITAND_UINT %10, 4194303i
4424
%20 = LOAD_DOUBLE R1
4425
%21 = NUM_TO_UINT %20
4426
%23 = BITAND_UINT %21, 4194303i
4427
%33 = BITRSHIFT_UINT %10, 22i
4428
%43 = BITRSHIFT_UINT %21, 22i
4429
%78 = BITAND_UINT %33, -1047553i
4430
%79 = BITAND_UINT %43, 1023i
4431
%80 = BITLSHIFT_UINT %79, 10i
4432
%81 = BITOR_UINT %78, %80
4433
%96 = UINT_TO_FLOAT %12
4434
%97 = UINT_TO_FLOAT %23
4435
%98 = UINT_TO_FLOAT %81
4436
STORE_VECTOR R5, %96, %97, %98, tvector
4437
%102 = LOAD_TVALUE R5, 0i, tvector
4438
STORE_TVALUE R6, %102
4439
%107 = FLOAT_TO_NUM %96
4440
%113 = FLOAT_TO_NUM %97
4441
%122 = ADD_NUM %107, %113
4442
STORE_SPLIT_TVALUE R7, tnumber, %122
4443
INTERRUPT 48u
4444
RETURN R6, 2i
4445
)"
4446
);
4447
}
4448
4449
TEST_CASE_FIXTURE(LoweringFixture, "Bit32ExtractDirect")
4450
{
4451
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
4452
4453
CHECK_EQ(
4454
"\n" + getCodegenAssembly(R"(
4455
local function foo(a: number, b: number)
4456
return bit32.extract(a, b, 4)
4457
end
4458
)"),
4459
R"(
4460
; function foo($arg0, $arg1) line 2
4461
bb_0:
4462
CHECK_TAG R0, tnumber, exit(entry)
4463
CHECK_TAG R1, tnumber, exit(entry)
4464
JUMP bb_2
4465
bb_2:
4466
JUMP bb_bytecode_1
4467
bb_bytecode_1:
4468
implicit CHECK_SAFE_ENV exit(0)
4469
STORE_DOUBLE R5, 4
4470
STORE_TAG R5, tnumber
4471
%13 = LOAD_DOUBLE R0
4472
%14 = LOAD_DOUBLE R1
4473
%15 = NUM_TO_UINT %13
4474
%16 = NUM_TO_INT %14
4475
%21 = ADD_INT %16, 4i
4476
CHECK_CMP_INT %16, 0i, ge, exit(3)
4477
CHECK_CMP_INT %21, 32i, le, exit(3)
4478
%28 = BITRSHIFT_UINT %15, %16
4479
%29 = BITAND_UINT %28, 15i
4480
%30 = UINT_TO_NUM %29
4481
STORE_DOUBLE R2, %30
4482
STORE_TAG R2, tnumber
4483
INTERRUPT 8u
4484
RETURN R2, 1i
4485
)"
4486
);
4487
}
4488
4489
TEST_CASE_FIXTURE(LoweringFixture, "Bit32SingleArg")
4490
{
4491
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
4492
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
4493
4494
CHECK_EQ(
4495
"\n" + getCodegenAssembly(R"(
4496
local function foo(a: number, b: number, c: number)
4497
return bit32.band(a) + bit32.bor(b) + bit32.bxor(c)
4498
end
4499
)"),
4500
R"(
4501
; function foo($arg0, $arg1, $arg2) line 2
4502
bb_0:
4503
CHECK_TAG R0, tnumber, exit(entry)
4504
CHECK_TAG R1, tnumber, exit(entry)
4505
CHECK_TAG R2, tnumber, exit(entry)
4506
JUMP bb_2
4507
bb_2:
4508
JUMP bb_bytecode_1
4509
bb_bytecode_1:
4510
implicit CHECK_SAFE_ENV exit(0)
4511
%11 = LOAD_DOUBLE R0
4512
%12 = NUM_TO_UINT %11
4513
%13 = UINT_TO_NUM %12
4514
%20 = LOAD_DOUBLE R1
4515
%21 = NUM_TO_UINT %20
4516
%22 = UINT_TO_NUM %21
4517
%32 = ADD_NUM %13, %22
4518
%38 = LOAD_DOUBLE R2
4519
%39 = NUM_TO_UINT %38
4520
%40 = UINT_TO_NUM %39
4521
%50 = ADD_NUM %32, %40
4522
STORE_DOUBLE R3, %50
4523
STORE_TAG R3, tnumber
4524
INTERRUPT 17u
4525
RETURN R3, 1i
4526
)"
4527
);
4528
}
4529
4530
TEST_CASE_FIXTURE(LoweringFixture, "Bit32SingleArgBtest")
4531
{
4532
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
4533
4534
CHECK_EQ(
4535
"\n" + getCodegenAssembly(R"(
4536
local function foo(a: number)
4537
return bit32.btest(a)
4538
end
4539
)"),
4540
R"(
4541
; function foo($arg0) line 2
4542
bb_0:
4543
CHECK_TAG R0, tnumber, exit(entry)
4544
JUMP bb_2
4545
bb_2:
4546
JUMP bb_bytecode_1
4547
bb_bytecode_1:
4548
implicit CHECK_SAFE_ENV exit(0)
4549
%7 = LOAD_DOUBLE R0
4550
%8 = NUM_TO_UINT %7
4551
%9 = CMP_INT %8, 0i, not_eq
4552
STORE_INT R1, %9
4553
STORE_TAG R1, tboolean
4554
INTERRUPT 5u
4555
RETURN R1, 1i
4556
)"
4557
);
4558
}
4559
4560
TEST_CASE_FIXTURE(LoweringFixture, "VectorLoadReuse")
4561
{
4562
CHECK_EQ(
4563
"\n" + getCodegenAssembly(R"(
4564
local function shuffle(v: vector)
4565
return v.x * v.x + v.y * v.y
4566
end
4567
)"),
4568
R"(
4569
; function shuffle($arg0) line 2
4570
bb_0:
4571
CHECK_TAG R0, tvector, exit(entry)
4572
JUMP bb_2
4573
bb_2:
4574
JUMP bb_bytecode_1
4575
bb_bytecode_1:
4576
%6 = LOAD_FLOAT R0, 0i
4577
%7 = FLOAT_TO_NUM %6
4578
%22 = MUL_NUM %7, %7
4579
%27 = LOAD_FLOAT R0, 4i
4580
%28 = FLOAT_TO_NUM %27
4581
%43 = MUL_NUM %28, %28
4582
%52 = ADD_NUM %22, %43
4583
STORE_DOUBLE R1, %52
4584
STORE_TAG R1, tnumber
4585
INTERRUPT 11u
4586
RETURN R1, 1i
4587
)"
4588
);
4589
}
4590
4591
TEST_CASE_FIXTURE(LoweringFixture, "VectorShuffle1")
4592
{
4593
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
4594
4595
// TODO: opportunity - if we introduce a separate vector shuffle instruction, this can be done in a single shuffle (+/- load and store)
4596
CHECK_EQ(
4597
"\n" + getCodegenAssembly(R"(
4598
local function shuffle(v: vector)
4599
return vector.create(v.z, v.x, v.y)
4600
end
4601
)"),
4602
R"(
4603
; function shuffle($arg0) line 2
4604
bb_0:
4605
CHECK_TAG R0, tvector, exit(entry)
4606
JUMP bb_2
4607
bb_2:
4608
JUMP bb_bytecode_1
4609
bb_bytecode_1:
4610
implicit CHECK_SAFE_ENV exit(0)
4611
%6 = LOAD_FLOAT R0, 8i
4612
%12 = LOAD_FLOAT R0, 0i
4613
%18 = LOAD_FLOAT R0, 4i
4614
STORE_VECTOR R1, %6, %12, %18
4615
STORE_TAG R1, tvector
4616
INTERRUPT 10u
4617
RETURN R1, 1i
4618
)"
4619
);
4620
}
4621
4622
TEST_CASE_FIXTURE(LoweringFixture, "VectorShuffle2")
4623
{
4624
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
4625
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
4626
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
4627
4628
CHECK_EQ(
4629
"\n" + getCodegenAssembly(R"(
4630
local function crossshuffle(v: vector, t: vector)
4631
local tmp1 = vector.create(v.x, v.x, v.z)
4632
local tmp2 = vector.create(t.y, t.z, t.x)
4633
return vector.create(tmp1.z, tmp2.x, tmp1.y)
4634
end
4635
)"),
4636
R"(
4637
; function crossshuffle($arg0, $arg1) line 2
4638
bb_0:
4639
CHECK_TAG R0, tvector, exit(entry)
4640
CHECK_TAG R1, tvector, exit(entry)
4641
JUMP bb_2
4642
bb_2:
4643
JUMP bb_bytecode_1
4644
bb_bytecode_1:
4645
implicit CHECK_SAFE_ENV exit(0)
4646
%8 = LOAD_FLOAT R0, 0i
4647
%20 = LOAD_FLOAT R0, 8i
4648
%42 = LOAD_FLOAT R1, 4i
4649
STORE_VECTOR R4, %20, %42, %8, tvector
4650
INTERRUPT 30u
4651
RETURN R4, 1i
4652
)"
4653
);
4654
}
4655
4656
TEST_CASE_FIXTURE(LoweringFixture, "VectorShuffleFromComposite1")
4657
{
4658
// This test requires runtime component to be present
4659
if (!Luau::CodeGen::isSupported())
4660
return;
4661
4662
CHECK_EQ(
4663
"\n" + getCodegenAssembly(R"(
4664
local function test(v: vertex)
4665
return v.normal.X * v.normal.X + v.normal.Y * v.normal.Y
4666
end
4667
)"),
4668
R"(
4669
; function test($arg0) line 2
4670
bb_0:
4671
CHECK_TAG R0, tuserdata, exit(entry)
4672
JUMP bb_2
4673
bb_2:
4674
JUMP bb_bytecode_1
4675
bb_bytecode_1:
4676
%6 = LOAD_POINTER R0
4677
CHECK_USERDATA_TAG %6, 13i, exit(0)
4678
%8 = BUFFER_READF32 %6, 12i, tuserdata
4679
%9 = BUFFER_READF32 %6, 16i, tuserdata
4680
%16 = FLOAT_TO_NUM %8
4681
%40 = MUL_NUM %16, %16
4682
%55 = FLOAT_TO_NUM %9
4683
%79 = MUL_NUM %55, %55
4684
%88 = ADD_NUM %40, %79
4685
STORE_DOUBLE R1, %88
4686
STORE_TAG R1, tnumber
4687
INTERRUPT 19u
4688
RETURN R1, 1i
4689
)"
4690
);
4691
}
4692
4693
TEST_CASE_FIXTURE(LoweringFixture, "VectorShuffleFromComposite2")
4694
{
4695
// This test requires runtime component to be present
4696
if (!Luau::CodeGen::isSupported())
4697
return;
4698
4699
ScopedFastFlag luauCodegenGcoDse{FFlag::LuauCodegenGcoDse2, true};
4700
4701
CHECK_EQ(
4702
"\n" + getCodegenAssembly(R"(
4703
local function test(v: vertex)
4704
return v.uv.X * v.uv.Y
4705
end
4706
)"),
4707
R"(
4708
; function test($arg0) line 2
4709
bb_0:
4710
CHECK_TAG R0, tuserdata, exit(entry)
4711
JUMP bb_2
4712
bb_2:
4713
JUMP bb_bytecode_1
4714
bb_bytecode_1:
4715
%6 = LOAD_POINTER R0
4716
CHECK_USERDATA_TAG %6, 13i, exit(0)
4717
%8 = BUFFER_READF32 %6, 24i, tuserdata
4718
%9 = BUFFER_READF32 %6, 28i, tuserdata
4719
CHECK_GC
4720
%21 = FLOAT_TO_NUM %8
4721
%41 = FLOAT_TO_NUM %9
4722
%50 = MUL_NUM %21, %41
4723
STORE_DOUBLE R1, %50
4724
STORE_TAG R1, tnumber
4725
INTERRUPT 9u
4726
RETURN R1, 1i
4727
)"
4728
);
4729
}
4730
4731
TEST_CASE_FIXTURE(LoweringFixture, "VectorCreateXY")
4732
{
4733
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
4734
4735
CHECK_EQ(
4736
"\n" + getCodegenAssembly(
4737
R"(
4738
local function foo(a: number): vector
4739
return vector.create(a, a * 2.0)
4740
end
4741
)"
4742
),
4743
R"(
4744
; function foo($arg0) line 2
4745
bb_0:
4746
CHECK_TAG R0, tnumber, exit(entry)
4747
JUMP bb_2
4748
bb_2:
4749
JUMP bb_bytecode_1
4750
bb_bytecode_1:
4751
implicit CHECK_SAFE_ENV exit(0)
4752
%6 = LOAD_DOUBLE R0
4753
%7 = ADD_NUM %6, %6
4754
%17 = NUM_TO_FLOAT %6
4755
%18 = NUM_TO_FLOAT %7
4756
STORE_VECTOR R1, %17, %18, 0
4757
STORE_TAG R1, tvector
4758
INTERRUPT 7u
4759
RETURN R1, 1i
4760
)"
4761
);
4762
}
4763
4764
TEST_CASE_FIXTURE(LoweringFixture, "VectorComparison1")
4765
{
4766
CHECK_EQ(
4767
"\n" + getCodegenAssembly(R"(
4768
local function foo(a: vector, b: vector)
4769
return a == b
4770
end
4771
)"),
4772
R"(
4773
; function foo($arg0, $arg1) line 2
4774
bb_0:
4775
CHECK_TAG R0, tvector, exit(entry)
4776
CHECK_TAG R1, tvector, exit(entry)
4777
JUMP bb_4
4778
bb_4:
4779
JUMP bb_bytecode_1
4780
bb_bytecode_1:
4781
SET_SAVEDPC 1u
4782
%7 = CMP_ANY R0, R1, eq
4783
STORE_INT R2, %7
4784
STORE_TAG R2, tboolean
4785
JUMP bb_bytecode_3
4786
bb_bytecode_3:
4787
INTERRUPT 4u
4788
RETURN R2, 1i
4789
)"
4790
);
4791
}
4792
4793
TEST_CASE_FIXTURE(LoweringFixture, "VectorComparison2")
4794
{
4795
CHECK_EQ(
4796
"\n" + getCodegenAssembly(R"(
4797
local function foo(a: vector, b)
4798
return a == b
4799
end
4800
)"),
4801
R"(
4802
; function foo($arg0, $arg1) line 2
4803
bb_0:
4804
CHECK_TAG R0, tvector, exit(entry)
4805
JUMP bb_4
4806
bb_4:
4807
JUMP bb_bytecode_1
4808
bb_bytecode_1:
4809
SET_SAVEDPC 1u
4810
%5 = CMP_ANY R0, R1, eq
4811
STORE_INT R2, %5
4812
STORE_TAG R2, tboolean
4813
JUMP bb_bytecode_3
4814
bb_bytecode_3:
4815
INTERRUPT 4u
4816
RETURN R2, 1i
4817
)"
4818
);
4819
}
4820
4821
TEST_CASE_FIXTURE(LoweringFixture, "ComparisonPropagationWall")
4822
{
4823
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
4824
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
4825
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
4826
4827
// After CMP_ANY 'z' cannot reuse any SSA registers before
4828
CHECK_EQ(
4829
"\n" + getCodegenAssembly(R"(
4830
local function foo(a, b)
4831
local x = type(b)
4832
local y = (not a) ~= b
4833
local z = type(b)
4834
return x, y, z
4835
end
4836
)"),
4837
R"(
4838
; function foo($arg0, $arg1) line 2
4839
bb_bytecode_0:
4840
implicit CHECK_SAFE_ENV exit(0)
4841
%1 = LOAD_TAG R1
4842
%2 = GET_TYPE %1
4843
STORE_POINTER R2, %2
4844
STORE_TAG R2, tstring
4845
%6 = LOAD_TAG R0
4846
%7 = LOAD_INT R0
4847
%8 = NOT_ANY %6, %7
4848
STORE_INT R4, %8
4849
STORE_TAG R4, tboolean
4850
SET_SAVEDPC 7u
4851
%12 = CMP_ANY R4, R1, eq
4852
%13 = SUB_INT 1i, %12
4853
STORE_INT R3, %13
4854
STORE_TAG R3, tboolean
4855
JUMP bb_bytecode_2
4856
bb_bytecode_2:
4857
implicit CHECK_SAFE_ENV exit(10)
4858
%21 = LOAD_TAG R1
4859
%22 = GET_TYPE %21
4860
STORE_POINTER R4, %22
4861
STORE_TAG R4, tstring
4862
INTERRUPT 15u
4863
RETURN R2, 3i
4864
)"
4865
);
4866
}
4867
4868
TEST_CASE_FIXTURE(LoweringFixture, "VectorLoadStoreOnlySamePrecision")
4869
{
4870
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
4871
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
4872
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
4873
4874
CHECK_EQ(
4875
"\n" + getCodegenAssembly(R"(
4876
local function test(x: number, y: number)
4877
local vec = vector.create(x, y, 0)
4878
return vec.X + vec.Y + vec.Z
4879
end
4880
)"),
4881
R"(
4882
; function test($arg0, $arg1) line 2
4883
bb_0:
4884
CHECK_TAG R0, tnumber, exit(entry)
4885
CHECK_TAG R1, tnumber, exit(entry)
4886
JUMP bb_2
4887
bb_2:
4888
JUMP bb_bytecode_1
4889
bb_bytecode_1:
4890
implicit CHECK_SAFE_ENV exit(0)
4891
%15 = LOAD_DOUBLE R0
4892
%16 = LOAD_DOUBLE R1
4893
%18 = NUM_TO_FLOAT %15
4894
%19 = NUM_TO_FLOAT %16
4895
%27 = FLOAT_TO_NUM %18
4896
%33 = FLOAT_TO_NUM %19
4897
%42 = ADD_NUM %27, %33
4898
%57 = ADD_NUM %42, 0
4899
STORE_DOUBLE R3, %57
4900
STORE_TAG R3, tnumber
4901
INTERRUPT 16u
4902
RETURN R3, 1i
4903
)"
4904
);
4905
}
4906
4907
TEST_CASE_FIXTURE(LoweringFixture, "NonNumericalComparison1")
4908
{
4909
CHECK_EQ(
4910
"\n" + getCodegenAssembly(R"(
4911
local function foo(a: string, b: string, c: {}, d: {})
4912
return a == b and c == d
4913
end
4914
)"),
4915
R"(
4916
; function foo($arg0, $arg1, $arg2, $arg3) line 2
4917
bb_0:
4918
CHECK_TAG R0, tstring, exit(entry)
4919
CHECK_TAG R1, tstring, exit(entry)
4920
CHECK_TAG R2, ttable, exit(entry)
4921
CHECK_TAG R3, ttable, exit(entry)
4922
JUMP bb_4
4923
bb_4:
4924
JUMP bb_bytecode_1
4925
bb_bytecode_1:
4926
STORE_INT R4, 0i
4927
STORE_TAG R4, tboolean
4928
SET_SAVEDPC 2u
4929
%13 = CMP_ANY R0, R1, eq
4930
JUMP_CMP_INT %13, 0i, eq, bb_bytecode_3, bb_5
4931
bb_5:
4932
SET_SAVEDPC 4u
4933
%16 = CMP_ANY R2, R3, eq
4934
STORE_INT R4, %16
4935
STORE_TAG R4, tboolean
4936
JUMP bb_bytecode_3
4937
bb_bytecode_3:
4938
INTERRUPT 7u
4939
RETURN R4, 1i
4940
)"
4941
);
4942
}
4943
4944
TEST_CASE_FIXTURE(LoweringFixture, "NonNumericalComparison2")
4945
{
4946
CHECK_EQ(
4947
"\n" + getCodegenAssembly(R"(
4948
local function foo(a: string, b: string, c: {}, d: {})
4949
return a > b and c > d
4950
end
4951
)"),
4952
R"(
4953
; function foo($arg0, $arg1, $arg2, $arg3) line 2
4954
bb_0:
4955
CHECK_TAG R0, tstring, exit(entry)
4956
CHECK_TAG R1, tstring, exit(entry)
4957
CHECK_TAG R2, ttable, exit(entry)
4958
CHECK_TAG R3, ttable, exit(entry)
4959
JUMP bb_4
4960
bb_4:
4961
JUMP bb_bytecode_1
4962
bb_bytecode_1:
4963
STORE_INT R4, 0i
4964
STORE_TAG R4, tboolean
4965
SET_SAVEDPC 2u
4966
%13 = CMP_ANY R1, R0, lt
4967
JUMP_CMP_INT %13, 0i, eq, bb_bytecode_3, bb_5
4968
bb_5:
4969
SET_SAVEDPC 4u
4970
%16 = CMP_ANY R3, R2, lt
4971
JUMP_CMP_INT %16, 0i, eq, bb_6, bb_bytecode_2
4972
bb_6:
4973
STORE_INT R4, 0i
4974
STORE_TAG R4, tboolean
4975
JUMP bb_bytecode_3
4976
bb_bytecode_2:
4977
STORE_INT R4, 1i
4978
STORE_TAG R4, tboolean
4979
JUMP bb_bytecode_3
4980
bb_bytecode_3:
4981
INTERRUPT 7u
4982
RETURN R4, 1i
4983
)"
4984
);
4985
}
4986
4987
TEST_CASE_FIXTURE(LoweringFixture, "BufferRelatedIndicesPositiveBase")
4988
{
4989
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
4990
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
4991
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
4992
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
4993
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
4994
4995
CHECK_EQ(
4996
"\n" + getCodegenAssembly(R"(
4997
local function foo(buf: buffer, a: number)
4998
return buffer.readi32(buf, a) + buffer.readi32(buf, a + 4) + buffer.readi32(buf, a + 8)
4999
end
5000
)"),
5001
R"(
5002
; function foo($arg0, $arg1) line 2
5003
bb_0:
5004
CHECK_TAG R0, tbuffer, exit(entry)
5005
CHECK_TAG R1, tnumber, exit(entry)
5006
JUMP bb_2
5007
bb_2:
5008
JUMP bb_bytecode_1
5009
bb_bytecode_1:
5010
implicit CHECK_SAFE_ENV exit(0)
5011
%11 = LOAD_POINTER R0
5012
%12 = LOAD_DOUBLE R1
5013
%13 = NUM_TO_INT %12
5014
CHECK_BUFFER_LEN %11, %13, 0i, 12i, %12, exit(2)
5015
%15 = BUFFER_READI32 %11, %13, tbuffer
5016
%16 = INT_TO_NUM %15
5017
%33 = ADD_INT %13, 4i
5018
%35 = BUFFER_READI32 %11, %33, tbuffer
5019
%36 = INT_TO_NUM %35
5020
%46 = ADD_NUM %16, %36
5021
%62 = ADD_INT %13, 8i
5022
%64 = BUFFER_READI32 %11, %62, tbuffer
5023
%65 = INT_TO_NUM %64
5024
%75 = ADD_NUM %46, %65
5025
STORE_DOUBLE R2, %75
5026
STORE_TAG R2, tnumber
5027
INTERRUPT 23u
5028
RETURN R2, 1i
5029
)"
5030
);
5031
}
5032
5033
TEST_CASE_FIXTURE(LoweringFixture, "BufferRelatedIndicesPositiveBaseInverted")
5034
{
5035
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
5036
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
5037
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
5038
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
5039
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
5040
5041
CHECK_EQ(
5042
"\n" + getCodegenAssembly(R"(
5043
local function foo(buf: buffer, a: number)
5044
return buffer.readi32(buf, a + 8) + buffer.readi32(buf, a + 4) + buffer.readi32(buf, a + 0)
5045
end
5046
)"),
5047
R"(
5048
; function foo($arg0, $arg1) line 2
5049
bb_0:
5050
CHECK_TAG R0, tbuffer, exit(entry)
5051
CHECK_TAG R1, tnumber, exit(entry)
5052
JUMP bb_2
5053
bb_2:
5054
JUMP bb_bytecode_1
5055
bb_bytecode_1:
5056
implicit CHECK_SAFE_ENV exit(0)
5057
%8 = LOAD_DOUBLE R1
5058
%9 = ADD_NUM %8, 8
5059
STORE_DOUBLE R6, %9
5060
STORE_TAG R6, tnumber
5061
%17 = LOAD_POINTER R0
5062
%19 = NUM_TO_INT %9
5063
CHECK_BUFFER_LEN %17, %19, -8i, 4i, %9, exit(3)
5064
%21 = BUFFER_READI32 %17, %19, tbuffer
5065
%22 = INT_TO_NUM %21
5066
%39 = ADD_INT %19, -4i
5067
%41 = BUFFER_READI32 %17, %39, tbuffer
5068
%42 = INT_TO_NUM %41
5069
%52 = ADD_NUM %22, %42
5070
%68 = ADD_INT %19, -8i
5071
%70 = BUFFER_READI32 %17, %68, tbuffer
5072
%71 = INT_TO_NUM %70
5073
%81 = ADD_NUM %52, %71
5074
STORE_DOUBLE R2, %81
5075
STORE_TAG R2, tnumber
5076
INTERRUPT 23u
5077
RETURN R2, 1i
5078
)"
5079
);
5080
}
5081
TEST_CASE_FIXTURE(LoweringFixture, "BufferRelatedIndicesPositiveDynamicBase")
5082
{
5083
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
5084
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
5085
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
5086
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
5087
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
5088
5089
CHECK_EQ(
5090
"\n" + getCodegenAssembly(R"(
5091
local function foo(index: buffer, data: buffer, a: number)
5092
local i = buffer.readi32(index, a)
5093
return buffer.readf32(data, i + 0) * buffer.readf32(data, i + 4) * buffer.readf32(data, i + 8)
5094
end
5095
)"),
5096
R"(
5097
; function foo($arg0, $arg1, $arg2) line 2
5098
bb_0:
5099
CHECK_TAG R0, tbuffer, exit(entry)
5100
CHECK_TAG R1, tbuffer, exit(entry)
5101
CHECK_TAG R2, tnumber, exit(entry)
5102
JUMP bb_2
5103
bb_2:
5104
JUMP bb_bytecode_1
5105
bb_bytecode_1:
5106
implicit CHECK_SAFE_ENV exit(0)
5107
%13 = LOAD_POINTER R0
5108
%14 = LOAD_DOUBLE R2
5109
%15 = NUM_TO_INT %14
5110
CHECK_BUFFER_LEN %13, %15, 0i, 4i, undef, exit(2)
5111
%17 = BUFFER_READI32 %13, %15, tbuffer
5112
%18 = INT_TO_NUM %17
5113
STORE_DOUBLE R3, %18
5114
STORE_TAG R3, tnumber
5115
%25 = ADD_NUM %18, 0
5116
STORE_DOUBLE R8, %25
5117
STORE_TAG R8, tnumber
5118
%33 = LOAD_POINTER R1
5119
%35 = NUM_TO_INT %18
5120
CHECK_BUFFER_LEN %33, %35, 0i, 12i, %18, exit(10)
5121
%37 = BUFFER_READF32 %33, %35, tbuffer
5122
%38 = FLOAT_TO_NUM %37
5123
%55 = ADD_INT %35, 4i
5124
%57 = BUFFER_READF32 %33, %55, tbuffer
5125
%58 = FLOAT_TO_NUM %57
5126
%68 = MUL_NUM %38, %58
5127
%84 = ADD_INT %35, 8i
5128
%86 = BUFFER_READF32 %33, %84, tbuffer
5129
%87 = FLOAT_TO_NUM %86
5130
%97 = MUL_NUM %68, %87
5131
STORE_DOUBLE R4, %97
5132
STORE_TAG R4, tnumber
5133
INTERRUPT 30u
5134
RETURN R4, 1i
5135
)"
5136
);
5137
}
5138
5139
TEST_CASE_FIXTURE(LoweringFixture, "BufferRelatedIndicesPositiveLoopRangeBase")
5140
{
5141
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
5142
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
5143
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
5144
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
5145
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
5146
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
5147
5148
// TODO: opportunity 1 - buffer.len is not a fastcall, but under safe env we can treat it like one and read buffer len field
5149
// TODO: opportunity 2 - range of 'i' is known, we can check it in loop header
5150
CHECK_EQ(
5151
"\n" + getCodegenAssembly(R"(
5152
local function foo(buf: buffer, a: number)
5153
local s = 0
5154
for i = 0, buffer.len(buf) - 1, 12 do
5155
s += buffer.readf32(buf, i) * buffer.readf32(buf, i + 4) * buffer.readf32(buf, i + 8)
5156
end
5157
return s
5158
end
5159
)"),
5160
R"(
5161
; function foo($arg0, $arg1) line 2
5162
bb_0:
5163
CHECK_TAG R0, tbuffer, exit(entry)
5164
CHECK_TAG R1, tnumber, exit(entry)
5165
JUMP bb_4
5166
bb_4:
5167
JUMP bb_bytecode_1
5168
bb_bytecode_1:
5169
implicit CHECK_SAFE_ENV exit(0)
5170
STORE_DOUBLE R2, 0
5171
STORE_TAG R2, tnumber
5172
STORE_DOUBLE R5, 0
5173
STORE_TAG R5, tnumber
5174
GET_CACHED_IMPORT R6, K3 (nil), 2148534272u ('buffer'.'len'), 3u
5175
%12 = LOAD_TVALUE R0, 0i, tbuffer
5176
STORE_TVALUE R7, %12
5177
INTERRUPT 5u
5178
SET_SAVEDPC 6u
5179
CALL R6, 1i, 1i
5180
CHECK_TAG R6, tnumber, bb_fallback_5
5181
%19 = LOAD_DOUBLE R6
5182
%20 = SUB_NUM %19, 1
5183
STORE_DOUBLE R3, %20
5184
STORE_TAG R3, tnumber
5185
JUMP bb_6
5186
bb_6:
5187
STORE_DOUBLE R4, 12
5188
STORE_TAG R4, tnumber
5189
CHECK_TAG R3, tnumber, exit(8)
5190
CHECK_TAG R5, tnumber, exit(8)
5191
%33 = LOAD_DOUBLE R3
5192
JUMP_CMP_NUM R5, %33, not_le, bb_bytecode_3, bb_bytecode_2
5193
bb_bytecode_2:
5194
implicit CHECK_SAFE_ENV exit(9)
5195
INTERRUPT 9u
5196
CHECK_TAG R5, tnumber, exit(11)
5197
%42 = LOAD_POINTER R0
5198
%43 = LOAD_DOUBLE R5
5199
%44 = NUM_TO_INT %43
5200
CHECK_BUFFER_LEN %42, %44, 0i, 12i, %43, exit(11)
5201
%46 = BUFFER_READF32 %42, %44, tbuffer
5202
%47 = FLOAT_TO_NUM %46
5203
%64 = ADD_INT %44, 4i
5204
%66 = BUFFER_READF32 %42, %64, tbuffer
5205
%67 = FLOAT_TO_NUM %66
5206
%77 = MUL_NUM %47, %67
5207
STORE_DOUBLE R7, %77
5208
STORE_TAG R7, tnumber
5209
%93 = ADD_INT %44, 8i
5210
%95 = BUFFER_READF32 %42, %93, tbuffer
5211
%96 = FLOAT_TO_NUM %95
5212
STORE_SPLIT_TVALUE R8, tnumber, %96
5213
%106 = MUL_NUM %77, %96
5214
STORE_DOUBLE R6, %106
5215
STORE_TAG R6, tnumber
5216
CHECK_TAG R2, tnumber, exit(32)
5217
%113 = LOAD_DOUBLE R2
5218
%115 = ADD_NUM %113, %106
5219
STORE_DOUBLE R2, %115
5220
%117 = LOAD_DOUBLE R3
5221
%119 = ADD_NUM %43, 12
5222
STORE_DOUBLE R5, %119
5223
JUMP_CMP_NUM %119, %117, le, bb_bytecode_2, bb_bytecode_3
5224
bb_bytecode_3:
5225
INTERRUPT 34u
5226
RETURN R2, 1i
5227
)"
5228
);
5229
}
5230
5231
TEST_CASE_FIXTURE(LoweringFixture, "BufferRelatedIndicesPositiveAdvancingBase")
5232
{
5233
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
5234
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
5235
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
5236
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
5237
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
5238
5239
CHECK_EQ(
5240
"\n" + getCodegenAssembly(R"(
5241
local function foo(buf: buffer, pos: number, a: number, b: number, c: number)
5242
buffer.writei32(buf, pos, a)
5243
pos += 4
5244
buffer.writei32(buf, pos, b)
5245
pos += 4
5246
buffer.writei32(buf, pos, c)
5247
pos += 4
5248
5249
return pos
5250
end
5251
)"),
5252
R"(
5253
; function foo($arg0, $arg1, $arg2, $arg3, $arg4) line 2
5254
bb_0:
5255
CHECK_TAG R0, tbuffer, exit(entry)
5256
CHECK_TAG R1, tnumber, exit(entry)
5257
CHECK_TAG R2, tnumber, exit(entry)
5258
CHECK_TAG R3, tnumber, exit(entry)
5259
CHECK_TAG R4, tnumber, exit(entry)
5260
JUMP bb_2
5261
bb_2:
5262
JUMP bb_bytecode_1
5263
bb_bytecode_1:
5264
implicit CHECK_SAFE_ENV exit(0)
5265
%19 = LOAD_POINTER R0
5266
%20 = LOAD_DOUBLE R1
5267
%21 = NUM_TO_INT %20
5268
CHECK_BUFFER_LEN %19, %21, 0i, 12i, %20, exit(2)
5269
%23 = LOAD_DOUBLE R2
5270
%24 = NUM_TO_UINT %23
5271
BUFFER_WRITEI32 %19, %21, %24, tbuffer
5272
%30 = ADD_NUM %20, 4
5273
%41 = ADD_INT %21, 4i
5274
%43 = LOAD_DOUBLE R3
5275
%44 = NUM_TO_UINT %43
5276
BUFFER_WRITEI32 %19, %41, %44, tbuffer
5277
%50 = ADD_NUM %30, 4
5278
%61 = ADD_INT %21, 8i
5279
%63 = LOAD_DOUBLE R4
5280
%64 = NUM_TO_UINT %63
5281
BUFFER_WRITEI32 %19, %61, %64, tbuffer
5282
%70 = ADD_NUM %50, 4
5283
STORE_DOUBLE R1, %70
5284
INTERRUPT 27u
5285
RETURN R1, 1i
5286
)"
5287
);
5288
}
5289
5290
TEST_CASE_FIXTURE(LoweringFixture, "BufferRelatedIndicesNegativeBase")
5291
{
5292
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
5293
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
5294
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
5295
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
5296
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
5297
5298
CHECK_EQ(
5299
"\n" + getCodegenAssembly(R"(
5300
local function foo(buf: buffer, a: number)
5301
return buffer.readi32(buf, a - 8) + buffer.readi32(buf, a - 4) + buffer.readi32(buf, a - 0)
5302
end
5303
)"),
5304
R"(
5305
; function foo($arg0, $arg1) line 2
5306
bb_0:
5307
CHECK_TAG R0, tbuffer, exit(entry)
5308
CHECK_TAG R1, tnumber, exit(entry)
5309
JUMP bb_2
5310
bb_2:
5311
JUMP bb_bytecode_1
5312
bb_bytecode_1:
5313
implicit CHECK_SAFE_ENV exit(0)
5314
%8 = LOAD_DOUBLE R1
5315
%9 = SUB_NUM %8, 8
5316
STORE_DOUBLE R6, %9
5317
STORE_TAG R6, tnumber
5318
%17 = LOAD_POINTER R0
5319
%19 = NUM_TO_INT %9
5320
CHECK_BUFFER_LEN %17, %19, 0i, 12i, %9, exit(3)
5321
%21 = BUFFER_READI32 %17, %19, tbuffer
5322
%22 = INT_TO_NUM %21
5323
%39 = ADD_INT %19, 4i
5324
%41 = BUFFER_READI32 %17, %39, tbuffer
5325
%42 = INT_TO_NUM %41
5326
%52 = ADD_NUM %22, %42
5327
%68 = ADD_INT %19, 8i
5328
%70 = BUFFER_READI32 %17, %68, tbuffer
5329
%71 = INT_TO_NUM %70
5330
%81 = ADD_NUM %52, %71
5331
STORE_DOUBLE R2, %81
5332
STORE_TAG R2, tnumber
5333
INTERRUPT 23u
5334
RETURN R2, 1i
5335
)"
5336
);
5337
}
5338
5339
TEST_CASE_FIXTURE(LoweringFixture, "BufferRelatedIndicesMixedBase")
5340
{
5341
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
5342
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
5343
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
5344
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
5345
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
5346
5347
CHECK_EQ(
5348
"\n" + getCodegenAssembly(R"(
5349
local function foo(buf: buffer, a: number)
5350
return buffer.readi32(buf, a) + buffer.readi32(buf, a - 4) + buffer.readi32(buf, a + 4)
5351
end
5352
)"),
5353
R"(
5354
; function foo($arg0, $arg1) line 2
5355
bb_0:
5356
CHECK_TAG R0, tbuffer, exit(entry)
5357
CHECK_TAG R1, tnumber, exit(entry)
5358
JUMP bb_2
5359
bb_2:
5360
JUMP bb_bytecode_1
5361
bb_bytecode_1:
5362
implicit CHECK_SAFE_ENV exit(0)
5363
%11 = LOAD_POINTER R0
5364
%12 = LOAD_DOUBLE R1
5365
%13 = NUM_TO_INT %12
5366
CHECK_BUFFER_LEN %11, %13, -4i, 8i, %12, exit(2)
5367
%15 = BUFFER_READI32 %11, %13, tbuffer
5368
%16 = INT_TO_NUM %15
5369
%33 = ADD_INT %13, -4i
5370
%35 = BUFFER_READI32 %11, %33, tbuffer
5371
%36 = INT_TO_NUM %35
5372
%46 = ADD_NUM %16, %36
5373
%62 = ADD_INT %13, 4i
5374
%64 = BUFFER_READI32 %11, %62, tbuffer
5375
%65 = INT_TO_NUM %64
5376
%75 = ADD_NUM %46, %65
5377
STORE_DOUBLE R2, %75
5378
STORE_TAG R2, tnumber
5379
INTERRUPT 23u
5380
RETURN R2, 1i
5381
)"
5382
);
5383
}
5384
5385
TEST_CASE_FIXTURE(LoweringFixture, "BufferSanityPositive")
5386
{
5387
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
5388
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
5389
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
5390
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
5391
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
5392
ScopedFastFlag luauCodegenRemoveDuplicateDoubleIntValues{FFlag::LuauCodegenRemoveDuplicateDoubleIntValues, true};
5393
5394
CHECK_EQ(
5395
"\n" + getCodegenAssembly(R"(
5396
local function foo(zero: number, b1: buffer, b2: buffer)
5397
buffer.writei8(b1, zero + 0, buffer.readi8(b1, zero + 0))
5398
buffer.writeu8(b1, zero + 0, buffer.readu8(b1, zero + 0))
5399
5400
buffer.writei8(b2, zero + 0, buffer.readi8(b2, zero + 0))
5401
buffer.writeu8(b2, zero + 0, buffer.readu8(b2, zero + 0))
5402
buffer.writei8(b2, zero + 1, buffer.readi8(b2, zero + 1))
5403
buffer.writeu8(b2, zero + 1, buffer.readu8(b2, zero + 1))
5404
buffer.writei16(b2, zero + 0, buffer.readi16(b2, zero + 0))
5405
buffer.writeu16(b2, zero + 0, buffer.readu16(b2, zero + 0))
5406
end
5407
)"),
5408
R"(
5409
; function foo($arg0, $arg1, $arg2) line 2
5410
bb_0:
5411
CHECK_TAG R0, tnumber, exit(entry)
5412
CHECK_TAG R1, tbuffer, exit(entry)
5413
CHECK_TAG R2, tbuffer, exit(entry)
5414
JUMP bb_2
5415
bb_2:
5416
JUMP bb_bytecode_1
5417
bb_bytecode_1:
5418
implicit CHECK_SAFE_ENV exit(0)
5419
%10 = LOAD_DOUBLE R0
5420
%11 = ADD_NUM %10, 0
5421
STORE_DOUBLE R5, %11
5422
STORE_TAG R5, tnumber
5423
STORE_DOUBLE R8, %11
5424
STORE_TAG R8, tnumber
5425
%25 = LOAD_POINTER R1
5426
%27 = NUM_TO_INT %10
5427
CHECK_BUFFER_LEN %25, %27, 0i, 1i, undef, exit(4)
5428
%29 = BUFFER_READI8 %25, %27, tbuffer
5429
BUFFER_WRITEI8 %25, %27, %29, tbuffer
5430
%70 = BUFFER_READU8 %25, %27, tbuffer
5431
BUFFER_WRITEI8 %25, %27, %70, tbuffer
5432
%107 = LOAD_POINTER R2
5433
CHECK_BUFFER_LEN %107, %27, 0i, 2i, %10, exit(32)
5434
%111 = BUFFER_READI8 %107, %27, tbuffer
5435
BUFFER_WRITEI8 %107, %27, %111, tbuffer
5436
%152 = BUFFER_READU8 %107, %27, tbuffer
5437
BUFFER_WRITEI8 %107, %27, %152, tbuffer
5438
%191 = ADD_INT %27, 1i
5439
%193 = BUFFER_READI8 %107, %191, tbuffer
5440
BUFFER_WRITEI8 %107, %191, %193, tbuffer
5441
%234 = BUFFER_READU8 %107, %191, tbuffer
5442
BUFFER_WRITEI8 %107, %191, %234, tbuffer
5443
%275 = BUFFER_READI16 %107, %27, tbuffer
5444
BUFFER_WRITEI16 %107, %27, %275, tbuffer
5445
%316 = BUFFER_READU16 %107, %27, tbuffer
5446
BUFFER_WRITEI16 %107, %27, %316, tbuffer
5447
INTERRUPT 112u
5448
RETURN R0, 0i
5449
)"
5450
);
5451
}
5452
5453
TEST_CASE_FIXTURE(LoweringFixture, "BufferSanityNegative")
5454
{
5455
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
5456
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
5457
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
5458
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
5459
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
5460
ScopedFastFlag luauCodegenRemoveDuplicateDoubleIntValues{FFlag::LuauCodegenRemoveDuplicateDoubleIntValues, true};
5461
5462
CHECK_EQ(
5463
"\n" + getCodegenAssembly(R"(
5464
local function foo(one: number, b1: buffer, b2: buffer)
5465
buffer.writei8(b1, one - 1, buffer.readi8(b1, one - 1))
5466
buffer.writeu8(b1, one - 1, buffer.readu8(b1, one - 1))
5467
5468
buffer.writei8(b2, one - 1, buffer.readi8(b2, one - 1))
5469
buffer.writeu8(b2, one - 1, buffer.readu8(b2, one - 1))
5470
buffer.writei8(b2, one - 0, buffer.readi8(b2, one - 0))
5471
buffer.writeu8(b2, one - 0, buffer.readu8(b2, one - 0))
5472
buffer.writei16(b2, one - 1, buffer.readi16(b2, one - 1))
5473
buffer.writeu16(b2, one - 1, buffer.readu16(b2, one - 1))
5474
end
5475
)"),
5476
R"(
5477
; function foo($arg0, $arg1, $arg2) line 2
5478
bb_0:
5479
CHECK_TAG R0, tnumber, exit(entry)
5480
CHECK_TAG R1, tbuffer, exit(entry)
5481
CHECK_TAG R2, tbuffer, exit(entry)
5482
JUMP bb_2
5483
bb_2:
5484
JUMP bb_bytecode_1
5485
bb_bytecode_1:
5486
implicit CHECK_SAFE_ENV exit(0)
5487
%10 = LOAD_DOUBLE R0
5488
%11 = SUB_NUM %10, 1
5489
STORE_DOUBLE R5, %11
5490
STORE_TAG R5, tnumber
5491
STORE_DOUBLE R8, %11
5492
STORE_TAG R8, tnumber
5493
%25 = LOAD_POINTER R1
5494
%27 = NUM_TO_INT %11
5495
CHECK_BUFFER_LEN %25, %27, 0i, 1i, undef, exit(4)
5496
%29 = BUFFER_READI8 %25, %27, tbuffer
5497
BUFFER_WRITEI8 %25, %27, %29, tbuffer
5498
%70 = BUFFER_READU8 %25, %27, tbuffer
5499
BUFFER_WRITEI8 %25, %27, %70, tbuffer
5500
%107 = LOAD_POINTER R2
5501
CHECK_BUFFER_LEN %107, %27, 0i, 2i, %11, exit(32)
5502
%111 = BUFFER_READI8 %107, %27, tbuffer
5503
BUFFER_WRITEI8 %107, %27, %111, tbuffer
5504
%152 = BUFFER_READU8 %107, %27, tbuffer
5505
BUFFER_WRITEI8 %107, %27, %152, tbuffer
5506
%191 = ADD_INT %27, 1i
5507
%193 = BUFFER_READI8 %107, %191, tbuffer
5508
BUFFER_WRITEI8 %107, %191, %193, tbuffer
5509
%234 = BUFFER_READU8 %107, %191, tbuffer
5510
BUFFER_WRITEI8 %107, %191, %234, tbuffer
5511
%275 = BUFFER_READI16 %107, %27, tbuffer
5512
BUFFER_WRITEI16 %107, %27, %275, tbuffer
5513
%316 = BUFFER_READU16 %107, %27, tbuffer
5514
BUFFER_WRITEI16 %107, %27, %316, tbuffer
5515
INTERRUPT 112u
5516
RETURN R0, 0i
5517
)"
5518
);
5519
}
5520
5521
TEST_CASE_FIXTURE(LoweringFixture, "NumericConversionReplacementCheck")
5522
{
5523
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
5524
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
5525
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
5526
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
5527
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
5528
5529
CHECK_EQ(
5530
"\n" + getCodegenAssembly(R"(
5531
local function foo(buf: buffer, a: number)
5532
math.ldexp(a, a) -- generate NUM_TO_INT early
5533
5534
-- range checks cannot make NUM_TO_INT exit to VM at a later location
5535
return buffer.readi32(buf, a) + buffer.readi32(buf, a + 4)
5536
end
5537
)"),
5538
R"(
5539
; function foo($arg0, $arg1) line 2
5540
bb_0:
5541
CHECK_TAG R0, tbuffer, exit(entry)
5542
CHECK_TAG R1, tnumber, exit(entry)
5543
JUMP bb_2
5544
bb_2:
5545
JUMP bb_bytecode_1
5546
bb_bytecode_1:
5547
implicit CHECK_SAFE_ENV exit(0)
5548
%11 = LOAD_DOUBLE R1
5549
%13 = NUM_TO_INT %11
5550
%14 = INVOKE_LIBM 15u, %11, %13
5551
STORE_DOUBLE R2, %14
5552
STORE_TAG R2, tnumber
5553
%23 = LOAD_POINTER R0
5554
CHECK_BUFFER_LEN %23, %13, 0i, 8i, %11, exit(9)
5555
%27 = BUFFER_READI32 %23, %13, tbuffer
5556
%28 = INT_TO_NUM %27
5557
%45 = ADD_INT %13, 4i
5558
%47 = BUFFER_READI32 %23, %45, tbuffer
5559
%48 = INT_TO_NUM %47
5560
%58 = ADD_NUM %28, %48
5561
STORE_DOUBLE R2, %58
5562
INTERRUPT 22u
5563
RETURN R2, 1i
5564
)"
5565
);
5566
}
5567
5568
TEST_CASE_FIXTURE(LoweringFixture, "BufferRelatedIndicesPositiveMultBase")
5569
{
5570
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
5571
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
5572
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
5573
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
5574
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
5575
5576
CHECK_EQ(
5577
"\n" + getCodegenAssembly(R"(
5578
local function foo(buf: buffer, a: number)
5579
return buffer.readi32(buf, a * 4) + buffer.readi32(buf, (a + 1) * 4) + buffer.readi32(buf, (a + 2) * 4)
5580
end
5581
)"),
5582
R"(
5583
; function foo($arg0, $arg1) line 2
5584
bb_0:
5585
CHECK_TAG R0, tbuffer, exit(entry)
5586
CHECK_TAG R1, tnumber, exit(entry)
5587
JUMP bb_2
5588
bb_2:
5589
JUMP bb_bytecode_1
5590
bb_bytecode_1:
5591
implicit CHECK_SAFE_ENV exit(0)
5592
%8 = LOAD_DOUBLE R1
5593
%9 = MUL_NUM %8, 4
5594
STORE_DOUBLE R6, %9
5595
STORE_TAG R6, tnumber
5596
%17 = LOAD_POINTER R0
5597
%19 = NUM_TO_INT %9
5598
CHECK_BUFFER_LEN %17, %19, 0i, 12i, %9, exit(3)
5599
%21 = BUFFER_READI32 %17, %19, tbuffer
5600
%22 = INT_TO_NUM %21
5601
%45 = ADD_INT %19, 4i
5602
%47 = BUFFER_READI32 %17, %45, tbuffer
5603
%48 = INT_TO_NUM %47
5604
%58 = ADD_NUM %22, %48
5605
%80 = ADD_INT %19, 8i
5606
%82 = BUFFER_READI32 %17, %80, tbuffer
5607
%83 = INT_TO_NUM %82
5608
%93 = ADD_NUM %58, %83
5609
STORE_DOUBLE R2, %93
5610
STORE_TAG R2, tnumber
5611
INTERRUPT 25u
5612
RETURN R2, 1i
5613
)"
5614
);
5615
}
5616
5617
TEST_CASE_FIXTURE(LoweringFixture, "BufferRelatedIndicesPositiveMultBase2")
5618
{
5619
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
5620
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
5621
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
5622
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
5623
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
5624
5625
// Different index multipliers are not merged
5626
CHECK_EQ(
5627
"\n" + getCodegenAssembly(R"(
5628
local function foo(buf: buffer, a: number)
5629
return buffer.readi32(buf, a * 4) + buffer.readi32(buf, (a + 1) * 8)
5630
end
5631
)"),
5632
R"(
5633
; function foo($arg0, $arg1) line 2
5634
bb_0:
5635
CHECK_TAG R0, tbuffer, exit(entry)
5636
CHECK_TAG R1, tnumber, exit(entry)
5637
JUMP bb_2
5638
bb_2:
5639
JUMP bb_bytecode_1
5640
bb_bytecode_1:
5641
implicit CHECK_SAFE_ENV exit(0)
5642
%8 = LOAD_DOUBLE R1
5643
%9 = MUL_NUM %8, 4
5644
STORE_DOUBLE R5, %9
5645
STORE_TAG R5, tnumber
5646
%17 = LOAD_POINTER R0
5647
%19 = NUM_TO_INT %9
5648
CHECK_BUFFER_LEN %17, %19, 0i, 4i, undef, exit(3)
5649
%21 = BUFFER_READI32 %17, %19, tbuffer
5650
%22 = INT_TO_NUM %21
5651
STORE_DOUBLE R3, %22
5652
STORE_TAG R3, tnumber
5653
%29 = ADD_NUM %8, 1
5654
STORE_DOUBLE R7, %29
5655
STORE_TAG R7, tnumber
5656
%35 = MUL_NUM %29, 8
5657
STORE_DOUBLE R6, %35
5658
STORE_TAG R6, tnumber
5659
%45 = NUM_TO_INT %35
5660
CHECK_BUFFER_LEN %17, %45, 0i, 4i, undef, exit(11)
5661
%47 = BUFFER_READI32 %17, %45, tbuffer
5662
%48 = INT_TO_NUM %47
5663
%58 = ADD_NUM %22, %48
5664
STORE_DOUBLE R2, %58
5665
STORE_TAG R2, tnumber
5666
INTERRUPT 16u
5667
RETURN R2, 1i
5668
)"
5669
);
5670
}
5671
5672
TEST_CASE_FIXTURE(LoweringFixture, "BufferRelatedIndicesPositiveMultBaseInt")
5673
{
5674
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
5675
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
5676
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
5677
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
5678
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
5679
5680
CHECK_EQ(
5681
"\n" + getCodegenAssembly(R"(
5682
local function foo(buf: buffer, a: number)
5683
-- trying to be helpful
5684
local t1 = bit32.bor(a, 0)
5685
local t2 = bit32.bor(t1 + 8, 0)
5686
local t3 = bit32.bor(t1 + 16, 0)
5687
return buffer.readf64(buf, t1) + buffer.readf64(buf, t2) + buffer.readf64(buf, t3)
5688
end
5689
)"),
5690
R"(
5691
; function foo($arg0, $arg1) line 2
5692
bb_0:
5693
CHECK_TAG R0, tbuffer, exit(entry)
5694
CHECK_TAG R1, tnumber, exit(entry)
5695
JUMP bb_2
5696
bb_2:
5697
JUMP bb_bytecode_1
5698
bb_bytecode_1:
5699
implicit CHECK_SAFE_ENV exit(0)
5700
%9 = LOAD_DOUBLE R1
5701
%10 = NUM_TO_UINT %9
5702
%13 = UINT_TO_NUM %10
5703
STORE_DOUBLE R2, %13
5704
STORE_TAG R2, tnumber
5705
%27 = ADD_INT %10, 8i
5706
%30 = UINT_TO_NUM %27
5707
STORE_DOUBLE R3, %30
5708
STORE_TAG R3, tnumber
5709
%44 = ADD_INT %10, 16i
5710
%47 = UINT_TO_NUM %44
5711
STORE_SPLIT_TVALUE R4, tnumber, %47
5712
%56 = LOAD_POINTER R0
5713
%58 = TRUNCATE_UINT %10
5714
CHECK_BUFFER_LEN %56, %58, 0i, 24i, undef, exit(23)
5715
%60 = BUFFER_READF64 %56, %58, tbuffer
5716
%73 = BUFFER_READF64 %56, %27, tbuffer
5717
%83 = ADD_NUM %60, %73
5718
%95 = BUFFER_READF64 %56, %44, tbuffer
5719
%105 = ADD_NUM %83, %95
5720
STORE_SPLIT_TVALUE R5, tnumber, %105
5721
INTERRUPT 44u
5722
RETURN R5, 1i
5723
)"
5724
);
5725
}
5726
5727
TEST_CASE_FIXTURE(LoweringFixture, "BufferRelatedIndicesMixedSizes")
5728
{
5729
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
5730
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
5731
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
5732
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
5733
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
5734
5735
CHECK_EQ(
5736
"\n" + getCodegenAssembly(R"(
5737
local function foo(buf: buffer, a: number)
5738
return buffer.readi8(buf, a) + buffer.readi8(buf, a + 4) + buffer.readf64(buf, a - 1)
5739
end
5740
)"),
5741
R"(
5742
; function foo($arg0, $arg1) line 2
5743
bb_0:
5744
CHECK_TAG R0, tbuffer, exit(entry)
5745
CHECK_TAG R1, tnumber, exit(entry)
5746
JUMP bb_2
5747
bb_2:
5748
JUMP bb_bytecode_1
5749
bb_bytecode_1:
5750
implicit CHECK_SAFE_ENV exit(0)
5751
%11 = LOAD_POINTER R0
5752
%12 = LOAD_DOUBLE R1
5753
%13 = NUM_TO_INT %12
5754
CHECK_BUFFER_LEN %11, %13, -1i, 7i, %12, exit(2)
5755
%15 = BUFFER_READI8 %11, %13, tbuffer
5756
%16 = INT_TO_NUM %15
5757
%33 = ADD_INT %13, 4i
5758
%35 = BUFFER_READI8 %11, %33, tbuffer
5759
%36 = INT_TO_NUM %35
5760
%46 = ADD_NUM %16, %36
5761
%62 = ADD_INT %13, -1i
5762
%64 = BUFFER_READF64 %11, %62, tbuffer
5763
%74 = ADD_NUM %46, %64
5764
STORE_DOUBLE R2, %74
5765
STORE_TAG R2, tnumber
5766
INTERRUPT 23u
5767
RETURN R2, 1i
5768
)"
5769
);
5770
}
5771
5772
TEST_CASE_FIXTURE(LoweringFixture, "BufferVmExitSync")
5773
{
5774
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
5775
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
5776
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
5777
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
5778
5779
CHECK_EQ(
5780
"\n" + getCodegenAssembly(R"(
5781
local function foo(buf: buffer, a: number, b: number, c: number)
5782
local x = buffer.readu8(buf, a * b)
5783
local y = buffer.readu8(buf, a * b + c)
5784
return x, y
5785
end
5786
)"),
5787
R"(
5788
; function foo($arg0, $arg1, $arg2, $arg3) line 2
5789
bb_0:
5790
CHECK_TAG R0, tbuffer, exit(entry)
5791
CHECK_TAG R1, tnumber, exit(entry)
5792
CHECK_TAG R2, tnumber, exit(entry)
5793
CHECK_TAG R3, tnumber, exit(entry)
5794
JUMP bb_2
5795
bb_2:
5796
JUMP bb_bytecode_1
5797
bb_bytecode_1:
5798
implicit CHECK_SAFE_ENV exit(0)
5799
%14 = LOAD_DOUBLE R1
5800
%16 = MUL_NUM %14, R2
5801
STORE_DOUBLE R6, %16
5802
STORE_TAG R6, tnumber
5803
%24 = LOAD_POINTER R0
5804
%26 = NUM_TO_INT %16
5805
CHECK_BUFFER_LEN %24, %26, 0i, 1i, undef, exit(3)
5806
%28 = BUFFER_READU8 %24, %26, tbuffer
5807
%29 = INT_TO_NUM %28
5808
STORE_DOUBLE R4, %29
5809
STORE_TAG R4, tnumber
5810
STORE_DOUBLE R8, %16
5811
STORE_TAG R8, tnumber
5812
%48 = ADD_NUM %16, R3
5813
STORE_DOUBLE R7, %48
5814
STORE_TAG R7, tnumber
5815
%58 = NUM_TO_INT %48
5816
CHECK_BUFFER_LEN %24, %58, 0i, 1i, undef, exit(11)
5817
%60 = BUFFER_READU8 %24, %58, tbuffer
5818
%61 = INT_TO_NUM %60
5819
STORE_DOUBLE R5, %61
5820
STORE_TAG R5, tnumber
5821
INTERRUPT 15u
5822
RETURN R4, 2i
5823
)"
5824
);
5825
}
5826
5827
TEST_CASE_FIXTURE(LoweringFixture, "Bit32NoDoubleTemporariesAdd")
5828
{
5829
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
5830
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
5831
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
5832
5833
CHECK_EQ(
5834
"\n" + getCodegenAssembly(R"(
5835
local function foo(a: number, b: number)
5836
local a = bit32.band(bit32.bor(a, 0) + bit32.bor(b, 0), 0xffff)
5837
local b = bit32.band(bit32.bor(a, 0) + 127, 0xffff)
5838
local c = bit32.band(254 + bit32.bor(a, 1), 0xffff)
5839
return a, b, c
5840
end
5841
)"),
5842
R"(
5843
; function foo($arg0, $arg1) line 2
5844
bb_0:
5845
CHECK_TAG R0, tnumber, exit(entry)
5846
CHECK_TAG R1, tnumber, exit(entry)
5847
JUMP bb_2
5848
bb_2:
5849
JUMP bb_bytecode_1
5850
bb_bytecode_1:
5851
implicit CHECK_SAFE_ENV exit(0)
5852
%9 = LOAD_DOUBLE R0
5853
%10 = NUM_TO_UINT %9
5854
%20 = LOAD_DOUBLE R1
5855
%21 = NUM_TO_UINT %20
5856
%41 = ADD_INT %10, %21
5857
%43 = BITAND_UINT %41, 65535i
5858
%44 = UINT_TO_NUM %43
5859
STORE_DOUBLE R2, %44
5860
STORE_TAG R2, tnumber
5861
%69 = ADD_INT %43, 127i
5862
%71 = BITAND_UINT %69, 65535i
5863
%72 = UINT_TO_NUM %71
5864
STORE_SPLIT_TVALUE R3, tnumber, %72
5865
%82 = BITOR_UINT %43, 1i
5866
%97 = ADD_INT %82, 254i
5867
%99 = BITAND_UINT %97, 65535i
5868
%100 = UINT_TO_NUM %99
5869
STORE_SPLIT_TVALUE R4, tnumber, %100
5870
INTERRUPT 49u
5871
RETURN R2, 3i
5872
)"
5873
);
5874
}
5875
5876
TEST_CASE_FIXTURE(LoweringFixture, "Bit32HasToUseDoubleTemporariesAdd")
5877
{
5878
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
5879
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
5880
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
5881
5882
CHECK_EQ(
5883
"\n" + getCodegenAssembly(R"(
5884
local function foo(a: number, b: number)
5885
local a = bit32.band(bit32.bor(a, 0) + 0.75, 0xffff)
5886
local b = bit32.band(bit32.bor(a, 0) + 1e30, 0xffff)
5887
local c = bit32.band(1e30 + bit32.bor(a, 1), 0xffff)
5888
return a, b, c
5889
end
5890
)"),
5891
R"(
5892
; function foo($arg0, $arg1) line 2
5893
bb_0:
5894
CHECK_TAG R0, tnumber, exit(entry)
5895
CHECK_TAG R1, tnumber, exit(entry)
5896
JUMP bb_2
5897
bb_2:
5898
JUMP bb_bytecode_1
5899
bb_bytecode_1:
5900
implicit CHECK_SAFE_ENV exit(0)
5901
%9 = LOAD_DOUBLE R0
5902
%10 = NUM_TO_UINT %9
5903
%13 = UINT_TO_NUM %10
5904
%20 = ADD_NUM %13, 0.75
5905
%27 = NUM_TO_UINT %20
5906
%29 = BITAND_UINT %27, 65535i
5907
%30 = UINT_TO_NUM %29
5908
STORE_DOUBLE R2, %30
5909
STORE_TAG R2, tnumber
5910
%48 = ADD_NUM %30, 1e+30
5911
%55 = NUM_TO_UINT %48
5912
%57 = BITAND_UINT %55, 65535i
5913
%58 = UINT_TO_NUM %57
5914
STORE_SPLIT_TVALUE R3, tnumber, %58
5915
%68 = BITOR_UINT %29, 1i
5916
%69 = UINT_TO_NUM %68
5917
%76 = ADD_NUM %69, 1e+30
5918
%83 = NUM_TO_UINT %76
5919
%85 = BITAND_UINT %83, 65535i
5920
%86 = UINT_TO_NUM %85
5921
STORE_SPLIT_TVALUE R4, tnumber, %86
5922
INTERRUPT 42u
5923
RETURN R2, 3i
5924
)"
5925
);
5926
}
5927
5928
TEST_CASE_FIXTURE(LoweringFixture, "Bit32NoDoubleTemporariesSub")
5929
{
5930
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
5931
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
5932
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
5933
5934
CHECK_EQ(
5935
"\n" + getCodegenAssembly(R"(
5936
local function foo(a: number, b: number)
5937
local a = bit32.band(bit32.bor(a, 0) - bit32.bor(b, 0), 0xffff)
5938
local b = bit32.band(bit32.bor(a, 0) - 127, 0xffff)
5939
local c = bit32.band(254 - bit32.bor(a, 1), 0xffff)
5940
return a, b, c
5941
end
5942
)"),
5943
R"(
5944
; function foo($arg0, $arg1) line 2
5945
bb_0:
5946
CHECK_TAG R0, tnumber, exit(entry)
5947
CHECK_TAG R1, tnumber, exit(entry)
5948
JUMP bb_2
5949
bb_2:
5950
JUMP bb_bytecode_1
5951
bb_bytecode_1:
5952
implicit CHECK_SAFE_ENV exit(0)
5953
%9 = LOAD_DOUBLE R0
5954
%10 = NUM_TO_UINT %9
5955
%20 = LOAD_DOUBLE R1
5956
%21 = NUM_TO_UINT %20
5957
%41 = SUB_INT %10, %21
5958
%43 = BITAND_UINT %41, 65535i
5959
%44 = UINT_TO_NUM %43
5960
STORE_DOUBLE R2, %44
5961
STORE_TAG R2, tnumber
5962
%69 = SUB_INT %43, 127i
5963
%71 = BITAND_UINT %69, 65535i
5964
%72 = UINT_TO_NUM %71
5965
STORE_SPLIT_TVALUE R3, tnumber, %72
5966
%82 = BITOR_UINT %43, 1i
5967
%97 = SUB_INT 254i, %82
5968
%99 = BITAND_UINT %97, 65535i
5969
%100 = UINT_TO_NUM %99
5970
STORE_SPLIT_TVALUE R4, tnumber, %100
5971
INTERRUPT 49u
5972
RETURN R2, 3i
5973
)"
5974
);
5975
}
5976
5977
TEST_CASE_FIXTURE(LoweringFixture, "Bit32HasToUseDoubleTemporariesSub")
5978
{
5979
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
5980
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
5981
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
5982
5983
CHECK_EQ(
5984
"\n" + getCodegenAssembly(R"(
5985
local function foo(a: number, b: number)
5986
local a = bit32.band(bit32.bor(a, 0) - 0.75, 0xffff)
5987
local b = bit32.band(bit32.bor(a, 0) - 1e30, 0xffff)
5988
local c = bit32.band(1e30 - bit32.bor(a, 1), 0xffff)
5989
return a, b, c
5990
end
5991
)"),
5992
R"(
5993
; function foo($arg0, $arg1) line 2
5994
bb_0:
5995
CHECK_TAG R0, tnumber, exit(entry)
5996
CHECK_TAG R1, tnumber, exit(entry)
5997
JUMP bb_2
5998
bb_2:
5999
JUMP bb_bytecode_1
6000
bb_bytecode_1:
6001
implicit CHECK_SAFE_ENV exit(0)
6002
%9 = LOAD_DOUBLE R0
6003
%10 = NUM_TO_UINT %9
6004
%13 = UINT_TO_NUM %10
6005
%20 = SUB_NUM %13, 0.75
6006
%27 = NUM_TO_UINT %20
6007
%29 = BITAND_UINT %27, 65535i
6008
%30 = UINT_TO_NUM %29
6009
STORE_DOUBLE R2, %30
6010
STORE_TAG R2, tnumber
6011
%48 = SUB_NUM %30, 1e+30
6012
%55 = NUM_TO_UINT %48
6013
%57 = BITAND_UINT %55, 65535i
6014
%58 = UINT_TO_NUM %57
6015
STORE_SPLIT_TVALUE R3, tnumber, %58
6016
%68 = BITOR_UINT %29, 1i
6017
%69 = UINT_TO_NUM %68
6018
%76 = SUB_NUM 1e+30, %69
6019
%83 = NUM_TO_UINT %76
6020
%85 = BITAND_UINT %83, 65535i
6021
%86 = UINT_TO_NUM %85
6022
STORE_SPLIT_TVALUE R4, tnumber, %86
6023
INTERRUPT 42u
6024
RETURN R2, 3i
6025
)"
6026
);
6027
}
6028
6029
TEST_CASE_FIXTURE(LoweringFixture, "OptionalOr")
6030
{
6031
CHECK_EQ(
6032
"\n" + getCodegenAssembly(R"(
6033
local function foo(a, b)
6034
a = a or 0
6035
b = b or 0
6036
return a + b
6037
end
6038
-- when a function like 'foo' is inlined, those 'default values' collapse
6039
local function bar()
6040
return foo(3, 4)
6041
end
6042
)"),
6043
R"(
6044
; function foo($arg0, $arg1) line 2
6045
bb_bytecode_0:
6046
%0 = LOAD_TVALUE R0
6047
%1 = LOAD_TVALUE K0 (0)
6048
%2 = SELECT_IF_TRUTHY %0, %0, %1
6049
STORE_TVALUE R0, %2
6050
%4 = LOAD_TVALUE R1
6051
%5 = LOAD_TVALUE K0 (0)
6052
%6 = SELECT_IF_TRUTHY %4, %4, %5
6053
STORE_TVALUE R1, %6
6054
CHECK_TAG R0, tnumber, bb_fallback_1
6055
CHECK_TAG R1, tnumber, bb_fallback_1
6056
%12 = LOAD_DOUBLE R0
6057
%14 = ADD_NUM %12, R1
6058
STORE_DOUBLE R2, %14
6059
STORE_TAG R2, tnumber
6060
JUMP bb_2
6061
bb_2:
6062
INTERRUPT 3u
6063
RETURN R2, 1i
6064
; function bar() line 8
6065
bb_bytecode_0:
6066
STORE_DOUBLE R0, 7
6067
STORE_TAG R0, tnumber
6068
INTERRUPT 5u
6069
RETURN R0, 1i
6070
)"
6071
);
6072
}
6073
6074
TEST_CASE_FIXTURE(LoweringFixture, "LinearAndOr")
6075
{
6076
CHECK_EQ(
6077
"\n" + getCodegenAssembly(R"(
6078
local function foo(a, b)
6079
return a and b, a or b
6080
end
6081
local function bar()
6082
local a, b = foo(3, 4)
6083
return a, b
6084
end
6085
)"),
6086
R"(
6087
; function foo($arg0, $arg1) line 2
6088
bb_bytecode_0:
6089
%0 = LOAD_TVALUE R0
6090
%1 = LOAD_TVALUE R1
6091
%2 = SELECT_IF_TRUTHY %0, %1, %0
6092
STORE_TVALUE R2, %2
6093
%6 = SELECT_IF_TRUTHY %0, %0, %1
6094
STORE_TVALUE R3, %6
6095
INTERRUPT 2u
6096
RETURN R2, 2i
6097
; function bar() line 5
6098
bb_bytecode_0:
6099
STORE_DOUBLE R0, 4
6100
STORE_TAG R0, tnumber
6101
STORE_DOUBLE R1, 3
6102
STORE_TAG R1, tnumber
6103
INTERRUPT 2u
6104
RETURN R0, 2i
6105
)"
6106
);
6107
}
6108
6109
TEST_CASE_FIXTURE(LoweringFixture, "OldStyleConditional")
6110
{
6111
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
6112
6113
// TODO: opportunity - this can be done in two SELECT_IF_TRUTHY, but we cannot match such complex sequences right now
6114
CHECK_EQ(
6115
"\n" + getCodegenAssembly(R"(
6116
local function foo(a: boolean, b: number, c: number)
6117
local x = a and b or c
6118
return x + 1
6119
end
6120
)"),
6121
R"(
6122
; function foo($arg0, $arg1, $arg2) line 2
6123
bb_0:
6124
CHECK_TAG R0, tboolean, exit(entry)
6125
CHECK_TAG R1, tnumber, exit(entry)
6126
CHECK_TAG R2, tnumber, exit(entry)
6127
JUMP bb_4
6128
bb_4:
6129
JUMP bb_bytecode_1
6130
bb_bytecode_1:
6131
JUMP_IF_FALSY R0, bb_bytecode_2, bb_5
6132
bb_5:
6133
%9 = LOAD_TVALUE R1, 0i, tnumber
6134
STORE_TVALUE R3, %9
6135
JUMP bb_bytecode_3
6136
bb_bytecode_2:
6137
%12 = LOAD_TVALUE R2, 0i, tnumber
6138
STORE_TVALUE R3, %12
6139
JUMP bb_bytecode_3
6140
bb_bytecode_3:
6141
CHECK_TAG R3, tnumber, exit(4)
6142
%17 = LOAD_DOUBLE R3
6143
%18 = ADD_NUM %17, 1
6144
STORE_DOUBLE R4, %18
6145
STORE_TAG R4, tnumber
6146
INTERRUPT 5u
6147
RETURN R4, 1i
6148
)"
6149
);
6150
}
6151
6152
TEST_CASE_FIXTURE(LoweringFixture, "NewStyleConditional")
6153
{
6154
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
6155
6156
// TODO: opportunity - this can be done in one SELECT_IF_TRUTHY, but this is also hard to detect in current system
6157
CHECK_EQ(
6158
"\n" + getCodegenAssembly(R"(
6159
local function foo(a: boolean, b: number, c: number)
6160
local x = if a then b else c
6161
return x + 1
6162
end
6163
)"),
6164
R"(
6165
; function foo($arg0, $arg1, $arg2) line 2
6166
bb_0:
6167
CHECK_TAG R0, tboolean, exit(entry)
6168
CHECK_TAG R1, tnumber, exit(entry)
6169
CHECK_TAG R2, tnumber, exit(entry)
6170
JUMP bb_4
6171
bb_4:
6172
JUMP bb_bytecode_1
6173
bb_bytecode_1:
6174
JUMP_IF_FALSY R0, bb_bytecode_2, bb_5
6175
bb_5:
6176
%9 = LOAD_TVALUE R1, 0i, tnumber
6177
STORE_TVALUE R3, %9
6178
JUMP bb_bytecode_3
6179
bb_bytecode_2:
6180
%12 = LOAD_TVALUE R2, 0i, tnumber
6181
STORE_TVALUE R3, %12
6182
JUMP bb_bytecode_3
6183
bb_bytecode_3:
6184
CHECK_TAG R3, tnumber, exit(4)
6185
%17 = LOAD_DOUBLE R3
6186
%18 = ADD_NUM %17, 1
6187
STORE_DOUBLE R4, %18
6188
STORE_TAG R4, tnumber
6189
INTERRUPT 5u
6190
RETURN R4, 1i
6191
)"
6192
);
6193
}
6194
6195
TEST_CASE_FIXTURE(LoweringFixture, "RecursiveRemoval1")
6196
{
6197
// Check that this compiles with no assertions
6198
CHECK(
6199
getCodegenAssembly(R"(
6200
repeat
6201
local _ = # {} < # {} < _ < _ < _ ^ _ ^ ""
6202
until ""
6203
)")
6204
.size() > 0
6205
);
6206
}
6207
6208
TEST_CASE_FIXTURE(LoweringFixture, "RecursiveRemoval2")
6209
{
6210
// Check that this compiles with no assertions
6211
CHECK(
6212
getCodegenAssembly(R"(
6213
local _
6214
6215
for l0={[1]=(_),},_ do
6216
_ += _
6217
for l0={[1]=(_),},_ do
6218
break
6219
end
6220
end
6221
)")
6222
.size() > 0
6223
);
6224
}
6225
6226
TEST_CASE_FIXTURE(LoweringFixture, "RecursiveRemoval3")
6227
{
6228
// Check that this compiles with no assertions
6229
CHECK(
6230
getCodegenAssembly(R"(
6231
while {_,} do
6232
local _
6233
repeat
6234
_ = "x",_ or {}
6235
until "x"
6236
end
6237
return l0
6238
)")
6239
.size() > 0
6240
);
6241
}
6242
6243
TEST_CASE_FIXTURE(LoweringFixture, "RecursiveRemoval4")
6244
{
6245
// Check that this compiles with no assertions
6246
CHECK(
6247
getCodegenAssembly(R"(
6248
local _ = 5633,5633
6249
while "" do
6250
_ += bit32.replace(_,function() end,0)
6251
for l0=_,{_,} do
6252
do
6253
_ += bit32.replace(_,nil,0)
6254
local _
6255
end
6256
end
6257
end
6258
)")
6259
.size() > 0
6260
);
6261
}
6262
6263
TEST_CASE_FIXTURE(LoweringFixture, "FuzzTagsAcrossChains")
6264
{
6265
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
6266
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
6267
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
6268
6269
CHECK_EQ(
6270
"\n" + getCodegenAssembly(
6271
R"(
6272
local function f(...)
6273
if bit32.btest(538976288,4,4,4,262144) then
6274
elseif bit32.btest(538976288,4,_,4,67108864) then
6275
end
6276
end
6277
)",
6278
false,
6279
1,
6280
1
6281
),
6282
R"(
6283
; function f() line 2
6284
bb_bytecode_0:
6285
implicit CHECK_SAFE_ENV exit(0)
6286
FALLBACK_PREPVARARGS 0u, 0i
6287
STORE_INT R0, 0i
6288
STORE_TAG R0, tboolean
6289
JUMP_IF_FALSY R0, bb_bytecode_1, bb_4
6290
bb_4:
6291
INTERRUPT 11u
6292
RETURN R0, 0i
6293
bb_bytecode_1:
6294
implicit CHECK_SAFE_ENV exit(12)
6295
STORE_DOUBLE R1, 538976288
6296
STORE_TAG R1, tnumber
6297
STORE_DOUBLE R2, 4
6298
STORE_TAG R2, tnumber
6299
GET_CACHED_IMPORT R3, K6 (nil), 1078984704u ('_'), 15u
6300
STORE_DOUBLE R4, 4
6301
STORE_TAG R4, tnumber
6302
STORE_DOUBLE R5, 67108864
6303
STORE_TAG R5, tnumber
6304
CHECK_TAG R3, tnumber, exit(19)
6305
STORE_INT R0, 0i
6306
STORE_TAG R0, tboolean
6307
JUMP_IF_FALSY R0, bb_bytecode_2, bb_bytecode_2
6308
bb_bytecode_2:
6309
INTERRUPT 23u
6310
RETURN R0, 0i
6311
)"
6312
);
6313
}
6314
6315
TEST_CASE_FIXTURE(LoweringFixture, "FuzzTest1")
6316
{
6317
// Check that this compiles with no assertions
6318
CHECK(
6319
getCodegenAssembly(R"(
6320
local _ = 5633,5633
6321
while _ do
6322
_ ^= _
6323
for l0=_,_,{[# {}]=_,} do
6324
repeat
6325
until _
6326
end
6327
end
6328
)")
6329
.size() > 0
6330
);
6331
}
6332
6333
TEST_CASE_FIXTURE(LoweringFixture, "FuzzTest2")
6334
{
6335
// Check that this compiles with no assertions
6336
CHECK(
6337
getCodegenAssembly(R"(
6338
local _
6339
while true ~= _ do
6340
_ = nil
6341
end
6342
_ = _,{},16711935 ~= _,{["" ~= _]=16711935,},_ ~= _
6343
while {} ~= _ do
6344
_ = nil
6345
end
6346
)")
6347
.size() > 0
6348
);
6349
}
6350
6351
TEST_CASE_FIXTURE(LoweringFixture, "FuzzTest3")
6352
{
6353
// Check that this compiles with no assertions
6354
CHECK(
6355
getCodegenAssembly(R"(
6356
local function f(...)
6357
local _ = ``,_
6358
_ ..= _(_()(_(_ and (...),_ .. _),_(_)),_(_(_(_(_ .. _,- _),_(_)),_()(_)),_(_,_._)))
6359
_(_)
6360
end
6361
)")
6362
.size() > 0
6363
);
6364
}
6365
6366
TEST_CASE_FIXTURE(LoweringFixture, "FuzzTest4")
6367
{
6368
// Check that this compiles with no assertions
6369
CHECK(
6370
getCodegenAssembly(R"(
6371
local _ = math.exp,_(),_
6372
local _ = math._,_(_(_),_(_ and _),_(_(_),_,_,_()),`{nil}`),_
6373
for l41=_,_ do
6374
end
6375
l0 -= _(0,_(# _,_(_),_(_(_),_(_),_,_()),`{nil}`))
6376
)")
6377
.size() > 0
6378
);
6379
}
6380
6381
TEST_CASE_FIXTURE(LoweringFixture, "FuzzTest5")
6382
{
6383
// Check that this compiles with no assertions
6384
CHECK(
6385
getCodegenAssembly(R"(
6386
local _ = 32768
6387
while "" do
6388
n0 ..= 0
6389
for l0=`{-2013233152}`,65535 do
6390
local l0 = vector.create(`{-2013233152}`,-2147450880,_,_)
6391
_ = _,_ // 0,_ // _
6392
end
6393
end
6394
)")
6395
.size() > 0
6396
);
6397
}
6398
6399
TEST_CASE_FIXTURE(LoweringFixture, "FuzzTest6")
6400
{
6401
// Check that this compiles with no assertions
6402
CHECK(
6403
getCodegenAssembly(R"(
6404
local l0:any = _(393216),# 0,n0
6405
while vector.sign(_ and true) do
6406
_ ..= nil
6407
do end
6408
for l0=_,_,vector.sign(_ + - _) do
6409
for l0=_,_,vector.sign() do
6410
do end
6411
end
6412
end
6413
end
6414
)")
6415
.size() > 0
6416
);
6417
}
6418
6419
TEST_CASE_FIXTURE(LoweringFixture, "FuzzTest7")
6420
{
6421
// Check that this compiles with no assertions
6422
CHECK(
6423
getCodegenAssembly(R"(
6424
for l0=0,32768 do end
6425
6426
local _ = vector.create(14941,14941,14848)
6427
6428
local function l0() end
6429
6430
_,l0,_,_,_G,_ = vector.clamp(_,_ / _,_),- - _
6431
)")
6432
.size() > 0
6433
);
6434
}
6435
6436
TEST_CASE_FIXTURE(LoweringFixture, "FuzzTest8")
6437
{
6438
// Check that this compiles with no assertions
6439
CHECK(
6440
getCodegenAssembly(R"(
6441
local function f(...)
6442
local _ = bit32.arshift
6443
local _ = (_),_(_(# # true,0),30),_(l158(true,_),0),_(_(l9,8258560),_)(_),_ + _,_
6444
end
6445
)")
6446
.size() > 0
6447
);
6448
}
6449
6450
TEST_CASE_FIXTURE(LoweringFixture, "FuzzTest9")
6451
{
6452
// Check that this compiles with no assertions
6453
CHECK(
6454
getCodegenAssembly(R"(
6455
local function f(...)
6456
local _ = bit32.lshift
6457
local _ = nil,bit32(l0(_(_(8200202,0,_),nil),_),_),_(_,1752395619),{_=_(_,0),},_(_(_(8200202,0,_),0,""),0),_[_]
6458
end
6459
)")
6460
.size() > 0
6461
);
6462
}
6463
6464
TEST_CASE_FIXTURE(LoweringFixture, "FuzzTest10")
6465
{
6466
// Check that this compiles with no assertions
6467
CHECK(
6468
getCodegenAssembly(R"(
6469
local function f(...)
6470
local b, t = ...
6471
t[buffer.readi8(b, -52436992 * -52436992)] /= buffer.readi8(b, -52436992 * 52436991)
6472
end
6473
)")
6474
.size() > 0
6475
);
6476
}
6477
6478
TEST_CASE_FIXTURE(LoweringFixture, "FuzzTest11")
6479
{
6480
// Check that this compiles with no assertions
6481
CHECK(
6482
getCodegenAssembly(R"(
6483
local function f(...)
6484
local _ = 1024,l0[_],...
6485
function _(l1, l118, l32, ...)
6486
for l0,l0,l0 in nil,__index,_ do
6487
end
6488
_ = _,vector.min((_),nil),_,nil
6489
_ = _ + _[l0]
6490
n0 = nil
6491
end
6492
_(_,_,_,_)
6493
end
6494
)")
6495
.size() > 0
6496
);
6497
}
6498
6499
TEST_CASE_FIXTURE(LoweringFixture, "FuzzTest12")
6500
{
6501
ScopedFastFlag luauCodegenTruncatedSubsts{FFlag::LuauCodegenTruncatedSubsts, true};
6502
6503
// Check that this compiles with no assertions
6504
CHECK(
6505
getCodegenAssembly(R"(
6506
local function f(...)
6507
if buffer.readf64(_, bit32.bxor(0,_,0), function() _ += _ end) then
6508
elseif ... then
6509
end
6510
end
6511
)")
6512
.size() > 0
6513
);
6514
}
6515
6516
TEST_CASE_FIXTURE(LoweringFixture, "FuzzTest13")
6517
{
6518
ScopedFastFlag luauCodegenLengthBaseInst{FFlag::LuauCodegenLengthBaseInst, true};
6519
6520
// Check that this compiles with no assertions
6521
CHECK(
6522
getCodegenAssembly(R"(
6523
local function f(...)
6524
local l0 = require(module0)
6525
buffer.writeu8(l0,1697972224 * 4,function(l0,...)end)
6526
buffer.writef32(l0,1697972224 * 4,function(l0,...)end)
6527
end
6528
)")
6529
.size() > 0
6530
);
6531
}
6532
6533
TEST_CASE_FIXTURE(LoweringFixture, "UpvalueAccessLoadStore1")
6534
{
6535
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
6536
ScopedFastFlag luauCodegenGcoDse{FFlag::LuauCodegenGcoDse2, true};
6537
6538
CHECK_EQ(
6539
"\n" + getCodegenAssembly(R"(
6540
local m = 1
6541
6542
local function foo(a: number, b: number)
6543
return m * a + m * b
6544
end
6545
6546
function setm(x) m = x end
6547
)"),
6548
R"(
6549
; function foo($arg0, $arg1) line 4
6550
bb_0:
6551
CHECK_TAG R0, tnumber, exit(entry)
6552
CHECK_TAG R1, tnumber, exit(entry)
6553
JUMP bb_2
6554
bb_2:
6555
JUMP bb_bytecode_1
6556
bb_bytecode_1:
6557
%6 = GET_UPVALUE U0
6558
STORE_TVALUE R4, %6
6559
CHECK_TAG R4, tnumber, exit(1)
6560
%12 = LOAD_DOUBLE R4
6561
%14 = MUL_NUM %12, R0
6562
%25 = MUL_NUM %12, R1
6563
%34 = ADD_NUM %14, %25
6564
STORE_DOUBLE R2, %34
6565
STORE_TAG R2, tnumber
6566
INTERRUPT 5u
6567
RETURN R2, 1i
6568
; function setm($arg0) line 8
6569
bb_bytecode_0:
6570
%0 = LOAD_TVALUE R0
6571
SET_UPVALUE U0, %0, undef
6572
INTERRUPT 1u
6573
RETURN R0, 0i
6574
)"
6575
);
6576
}
6577
6578
TEST_CASE_FIXTURE(LoweringFixture, "UpvalueAccessLoadStore2")
6579
{
6580
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
6581
6582
// TODO: opportunity - if the value was just stored to VM register in parts, we can use those parts to store upvalue
6583
CHECK_EQ(
6584
"\n" + getCodegenAssembly(R"(
6585
local m
6586
6587
local function foo(a: number, b: number)
6588
m = a - b
6589
m = m * a + m * b
6590
return m + a
6591
end
6592
)"),
6593
R"(
6594
; function foo($arg0, $arg1) line 4
6595
bb_0:
6596
CHECK_TAG R0, tnumber, exit(entry)
6597
CHECK_TAG R1, tnumber, exit(entry)
6598
JUMP bb_2
6599
bb_2:
6600
JUMP bb_bytecode_1
6601
bb_bytecode_1:
6602
%10 = LOAD_DOUBLE R0
6603
%11 = LOAD_DOUBLE R1
6604
%12 = SUB_NUM %10, %11
6605
STORE_DOUBLE R2, %12
6606
STORE_TAG R2, tnumber
6607
%15 = LOAD_TVALUE R2, 0i, tnumber
6608
SET_UPVALUE U0, %15, tnumber
6609
%25 = MUL_NUM %12, %10
6610
%36 = MUL_NUM %12, %11
6611
%45 = ADD_NUM %25, %36
6612
STORE_DOUBLE R2, %45
6613
%48 = LOAD_TVALUE R2, 0i, tnumber
6614
SET_UPVALUE U0, %48, tnumber
6615
%58 = ADD_NUM %45, %10
6616
STORE_DOUBLE R2, %58
6617
INTERRUPT 10u
6618
RETURN R2, 1i
6619
)"
6620
);
6621
}
6622
6623
TEST_CASE_FIXTURE(LoweringFixture, "UpvalueAccessLoadStore3")
6624
{
6625
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
6626
6627
CHECK_EQ(
6628
"\n" + getCodegenAssembly(R"(
6629
local m = 1
6630
6631
local function foo()
6632
local a = m
6633
m = a
6634
local b = m
6635
m = b
6636
return m + a + b
6637
end
6638
6639
function setm(x, y) m = x end
6640
)"),
6641
R"(
6642
; function foo() line 4
6643
bb_bytecode_0:
6644
%0 = GET_UPVALUE U0
6645
STORE_TVALUE R0, %0
6646
SET_UPVALUE U0, %0, undef
6647
STORE_TVALUE R1, %0
6648
SET_UPVALUE U0, %0, undef
6649
STORE_TVALUE R4, %0
6650
CHECK_TAG R4, tnumber, exit(5)
6651
%14 = LOAD_DOUBLE R4
6652
%16 = ADD_NUM %14, %14
6653
%25 = ADD_NUM %16, %14
6654
STORE_DOUBLE R2, %25
6655
STORE_TAG R2, tnumber
6656
INTERRUPT 7u
6657
RETURN R2, 1i
6658
; function setm($arg0, $arg1) line 12
6659
bb_bytecode_0:
6660
%0 = LOAD_TVALUE R0
6661
SET_UPVALUE U0, %0, undef
6662
INTERRUPT 1u
6663
RETURN R0, 0i
6664
)"
6665
);
6666
}
6667
6668
TEST_CASE_FIXTURE(LoweringFixture, "UpvalueAccessLoadStore4")
6669
{
6670
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
6671
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
6672
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
6673
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
6674
6675
CHECK_EQ(
6676
"\n" + getCodegenAssembly(R"(
6677
local arr: {number}
6678
6679
local function foo(a: number)
6680
for i = 1,#arr do
6681
arr[i] = arr[i] + arr[i] * a
6682
end
6683
end
6684
6685
arr = {1, 2, 3, 4}
6686
)"),
6687
R"(
6688
; function foo($arg0) line 4
6689
bb_0:
6690
CHECK_TAG R0, tnumber, exit(entry)
6691
JUMP bb_4
6692
bb_4:
6693
JUMP bb_bytecode_1
6694
bb_bytecode_1:
6695
STORE_DOUBLE R3, 1
6696
STORE_TAG R3, tnumber
6697
%6 = GET_UPVALUE U0
6698
STORE_TVALUE R4, %6
6699
CHECK_TAG R4, ttable, exit(2)
6700
%10 = LOAD_POINTER R4
6701
CHECK_NO_METATABLE %10, bb_fallback_5
6702
%12 = TABLE_LEN %10
6703
%13 = INT_TO_NUM %12
6704
STORE_DOUBLE R1, %13
6705
STORE_TAG R1, tnumber
6706
JUMP bb_6
6707
bb_6:
6708
STORE_DOUBLE R2, 1
6709
STORE_TAG R2, tnumber
6710
CHECK_TAG R1, tnumber, exit(4)
6711
CHECK_TAG R3, tnumber, exit(4)
6712
%26 = LOAD_DOUBLE R1
6713
JUMP_CMP_NUM R3, %26, not_le, bb_bytecode_3, bb_bytecode_2
6714
bb_bytecode_2:
6715
INTERRUPT 5u
6716
%30 = GET_UPVALUE U0
6717
STORE_TVALUE R4, %30
6718
STORE_TVALUE R7, %30
6719
CHECK_TAG R7, ttable, exit(7)
6720
CHECK_TAG R3, tnumber, exit(7)
6721
%38 = LOAD_POINTER R7
6722
%39 = LOAD_DOUBLE R3
6723
%40 = TRY_NUM_TO_INDEX %39, bb_fallback_7
6724
%41 = SUB_INT %40, 1i
6725
CHECK_ARRAY_SIZE %38, %41, bb_fallback_7
6726
CHECK_NO_METATABLE %38, bb_fallback_7
6727
%44 = GET_ARR_ADDR %38, %41
6728
%45 = LOAD_TVALUE %44
6729
STORE_TVALUE R6, %45
6730
JUMP bb_linear_17
6731
bb_linear_17:
6732
STORE_TVALUE R8, %45
6733
CHECK_TAG R8, tnumber, bb_fallback_11
6734
%141 = LOAD_DOUBLE R8
6735
%143 = MUL_NUM %141, R0
6736
%153 = ADD_NUM %141, %143
6737
STORE_DOUBLE R5, %153
6738
STORE_TAG R5, tnumber
6739
CHECK_NO_METATABLE %38, bb_fallback_15
6740
CHECK_READONLY %38, bb_fallback_15
6741
STORE_SPLIT_TVALUE %44, tnumber, %153
6742
%173 = LOAD_DOUBLE R1
6743
%175 = ADD_NUM %39, 1
6744
STORE_DOUBLE R3, %175
6745
JUMP_CMP_NUM %175, %173, le, bb_bytecode_2, bb_bytecode_3
6746
bb_8:
6747
%51 = GET_UPVALUE U0
6748
STORE_TVALUE R9, %51
6749
CHECK_TAG R9, ttable, exit(9)
6750
CHECK_TAG R3, tnumber, exit(9)
6751
%57 = LOAD_POINTER R9
6752
%58 = LOAD_DOUBLE R3
6753
%59 = TRY_NUM_TO_INDEX %58, bb_fallback_9
6754
%60 = SUB_INT %59, 1i
6755
CHECK_ARRAY_SIZE %57, %60, bb_fallback_9
6756
CHECK_NO_METATABLE %57, bb_fallback_9
6757
%63 = GET_ARR_ADDR %57, %60
6758
%64 = LOAD_TVALUE %63
6759
STORE_TVALUE R8, %64
6760
JUMP bb_10
6761
bb_10:
6762
CHECK_TAG R8, tnumber, bb_fallback_11
6763
%74 = LOAD_DOUBLE R8
6764
%76 = MUL_NUM %74, R0
6765
STORE_DOUBLE R7, %76
6766
STORE_TAG R7, tnumber
6767
JUMP bb_12
6768
bb_12:
6769
CHECK_TAG R6, tnumber, bb_fallback_13
6770
CHECK_TAG R7, tnumber, bb_fallback_13
6771
%87 = LOAD_DOUBLE R6
6772
%89 = ADD_NUM %87, R7
6773
STORE_DOUBLE R5, %89
6774
STORE_TAG R5, tnumber
6775
JUMP bb_14
6776
bb_14:
6777
CHECK_TAG R4, ttable, exit(12)
6778
CHECK_TAG R3, tnumber, exit(12)
6779
%100 = LOAD_POINTER R4
6780
%101 = LOAD_DOUBLE R3
6781
%102 = TRY_NUM_TO_INDEX %101, bb_fallback_15
6782
%103 = SUB_INT %102, 1i
6783
CHECK_ARRAY_SIZE %100, %103, bb_fallback_15
6784
CHECK_NO_METATABLE %100, bb_fallback_15
6785
CHECK_READONLY %100, bb_fallback_15
6786
%107 = GET_ARR_ADDR %100, %103
6787
%108 = LOAD_TVALUE R5
6788
STORE_TVALUE %107, %108
6789
BARRIER_TABLE_FORWARD %100, R5, undef
6790
JUMP bb_16
6791
bb_16:
6792
%115 = LOAD_DOUBLE R1
6793
%116 = LOAD_DOUBLE R3
6794
%117 = ADD_NUM %116, 1
6795
STORE_DOUBLE R3, %117
6796
JUMP_CMP_NUM %117, %115, le, bb_bytecode_2, bb_bytecode_3
6797
bb_bytecode_3:
6798
INTERRUPT 14u
6799
RETURN R0, 0i
6800
)"
6801
);
6802
}
6803
6804
TEST_CASE_FIXTURE(LoweringFixture, "BufferLoadStoreProp1")
6805
{
6806
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
6807
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
6808
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
6809
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
6810
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
6811
6812
CHECK_EQ(
6813
"\n" + getCodegenAssembly(R"(
6814
local function test(b: buffer)
6815
return buffer.readf32(b, 0) * buffer.readf32(b, 0) + buffer.readf32(b, 4) * buffer.readf32(b, 4)
6816
end
6817
)"),
6818
R"(
6819
; function test($arg0) line 2
6820
bb_0:
6821
CHECK_TAG R0, tbuffer, exit(entry)
6822
JUMP bb_2
6823
bb_2:
6824
JUMP bb_bytecode_1
6825
bb_bytecode_1:
6826
implicit CHECK_SAFE_ENV exit(0)
6827
%7 = LOAD_POINTER R0
6828
CHECK_BUFFER_LEN %7, 0i, 0i, 8i, undef, exit(2)
6829
%10 = BUFFER_READF32 %7, 0i, tbuffer
6830
%11 = FLOAT_TO_NUM %10
6831
%32 = MUL_NUM %11, %11
6832
%41 = BUFFER_READF32 %7, 4i, tbuffer
6833
%42 = FLOAT_TO_NUM %41
6834
%63 = MUL_NUM %42, %42
6835
%72 = ADD_NUM %32, %63
6836
STORE_DOUBLE R1, %72
6837
STORE_TAG R1, tnumber
6838
INTERRUPT 31u
6839
RETURN R1, 1i
6840
)"
6841
);
6842
}
6843
6844
TEST_CASE_FIXTURE(LoweringFixture, "BufferLoadStoreProp2")
6845
{
6846
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
6847
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
6848
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
6849
6850
CHECK_EQ(
6851
"\n" + getCodegenAssembly(R"(
6852
local function test(b: buffer)
6853
buffer.writei8(b, 10, 32)
6854
assert(buffer.readi8(b, 10) == 32)
6855
6856
buffer.writei8(b, 14, 4)
6857
buffer.writei8(b, 13, 3)
6858
buffer.writei8(b, 12, 2)
6859
buffer.writei8(b, 11, 1)
6860
6861
return buffer.readi8(b, 11) + buffer.readi8(b, 12) + buffer.readi8(b, 14) + buffer.readi8(b, 13)
6862
end
6863
)"),
6864
R"(
6865
; function test($arg0) line 2
6866
bb_0:
6867
CHECK_TAG R0, tbuffer, exit(entry)
6868
JUMP bb_4
6869
bb_4:
6870
JUMP bb_bytecode_1
6871
bb_bytecode_1:
6872
implicit CHECK_SAFE_ENV exit(0)
6873
STORE_DOUBLE R3, 10
6874
STORE_TAG R3, tnumber
6875
STORE_DOUBLE R4, 32
6876
STORE_TAG R4, tnumber
6877
%15 = LOAD_POINTER R0
6878
CHECK_BUFFER_LEN %15, 10i, 0i, 5i, undef, exit(4)
6879
BUFFER_WRITEI8 %15, 10i, 32i, tbuffer
6880
JUMP bb_bytecode_3
6881
bb_bytecode_3:
6882
JUMP bb_8
6883
bb_8:
6884
BUFFER_WRITEI8 %15, 14i, 4i, tbuffer
6885
BUFFER_WRITEI8 %15, 13i, 3i, tbuffer
6886
BUFFER_WRITEI8 %15, 12i, 2i, tbuffer
6887
BUFFER_WRITEI8 %15, 11i, 1i, tbuffer
6888
STORE_DOUBLE R1, 10
6889
STORE_TAG R1, tnumber
6890
INTERRUPT 86u
6891
RETURN R1, 1i
6892
)"
6893
);
6894
}
6895
6896
// When dealing with constants and buffer loads/store of the same size, all assertions disappear as conditions are true
6897
TEST_CASE_FIXTURE(LoweringFixture, "BufferLoadStoreProp3")
6898
{
6899
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
6900
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
6901
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
6902
6903
CHECK_EQ(
6904
"\n" + getCodegenAssembly(R"(
6905
local function storeloadpreserve(b: buffer)
6906
buffer.writeu32(b, 0, 0xffffffff)
6907
assert(buffer.readi32(b, 0) == -1)
6908
assert(buffer.readu32(b, 0) == 4294967295)
6909
6910
buffer.writei32(b, 0, -1)
6911
assert(buffer.readi32(b, 0) == -1)
6912
assert(buffer.readu32(b, 0) == 4294967295)
6913
6914
buffer.writei16(b, 0, 65535)
6915
assert(buffer.readi16(b, 0) == -1)
6916
assert(buffer.readu16(b, 0) == 65535)
6917
6918
buffer.writeu16(b, 0, 65535)
6919
assert(buffer.readi16(b, 0) == -1)
6920
assert(buffer.readu16(b, 0) == 65535)
6921
6922
buffer.writeu8(b, 0, 0xffffffff)
6923
assert(buffer.readi8(b, 0) == -1)
6924
assert(buffer.readu8(b, 0) == 255)
6925
6926
buffer.writeu16(b, 0, 0xffffffff)
6927
assert(buffer.readi16(b, 0) == -1)
6928
assert(buffer.readu16(b, 0) == 65535)
6929
end
6930
)"),
6931
R"(
6932
; function storeloadpreserve($arg0) line 2
6933
bb_0:
6934
CHECK_TAG R0, tbuffer, exit(entry)
6935
JUMP bb_26
6936
bb_26:
6937
JUMP bb_bytecode_1
6938
bb_bytecode_1:
6939
implicit CHECK_SAFE_ENV exit(0)
6940
STORE_DOUBLE R3, 0
6941
STORE_TAG R3, tnumber
6942
STORE_DOUBLE R4, 4294967295
6943
STORE_TAG R4, tnumber
6944
%15 = LOAD_POINTER R0
6945
CHECK_BUFFER_LEN %15, 0i, 0i, 4i, undef, exit(4)
6946
BUFFER_WRITEI32 %15, 0i, -1i, tbuffer
6947
JUMP bb_bytecode_3
6948
bb_bytecode_3:
6949
JUMP bb_30
6950
bb_30:
6951
JUMP bb_bytecode_5
6952
bb_bytecode_5:
6953
JUMP bb_33
6954
bb_33:
6955
BUFFER_WRITEI32 %15, 0i, -1i, tbuffer
6956
JUMP bb_bytecode_7
6957
bb_bytecode_7:
6958
JUMP bb_37
6959
bb_37:
6960
JUMP bb_bytecode_9
6961
bb_bytecode_9:
6962
JUMP bb_40
6963
bb_40:
6964
BUFFER_WRITEI16 %15, 0i, 65535i, tbuffer
6965
JUMP bb_bytecode_11
6966
bb_bytecode_11:
6967
JUMP bb_44
6968
bb_44:
6969
JUMP bb_bytecode_13
6970
bb_bytecode_13:
6971
JUMP bb_47
6972
bb_47:
6973
BUFFER_WRITEI16 %15, 0i, 65535i, tbuffer
6974
JUMP bb_bytecode_15
6975
bb_bytecode_15:
6976
JUMP bb_51
6977
bb_51:
6978
JUMP bb_bytecode_17
6979
bb_bytecode_17:
6980
JUMP bb_54
6981
bb_54:
6982
BUFFER_WRITEI8 %15, 0i, -1i, tbuffer
6983
JUMP bb_bytecode_19
6984
bb_bytecode_19:
6985
JUMP bb_58
6986
bb_58:
6987
JUMP bb_bytecode_21
6988
bb_bytecode_21:
6989
JUMP bb_61
6990
bb_61:
6991
BUFFER_WRITEI16 %15, 0i, -1i, tbuffer
6992
JUMP bb_bytecode_23
6993
bb_bytecode_23:
6994
JUMP bb_65
6995
bb_65:
6996
JUMP bb_bytecode_25
6997
bb_bytecode_25:
6998
JUMP bb_68
6999
bb_68:
7000
INTERRUPT 228u
7001
RETURN R0, 0i
7002
)"
7003
);
7004
}
7005
7006
// When dealing with unknown numbers, stores can be propagated to loads with proper zero/signed extension
7007
TEST_CASE_FIXTURE(LoweringFixture, "BufferLoadStoreProp4")
7008
{
7009
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
7010
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
7011
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
7012
ScopedFastFlag luauCodegenTruncatedSubsts{FFlag::LuauCodegenTruncatedSubsts, true};
7013
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
7014
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
7015
7016
CHECK_EQ(
7017
"\n" + getCodegenAssembly(R"(
7018
local function test(b: buffer, n: number, f: number)
7019
buffer.writei8(b, 0, n)
7020
buffer.writef64(b, 100, buffer.readi8(b, 0))
7021
buffer.writei8(b, 108, buffer.readi8(b, 0))
7022
buffer.writeu8(b, 109, buffer.readi8(b, 0))
7023
7024
buffer.writeu8(b, 2, n)
7025
buffer.writef64(b, 116, buffer.readu8(b, 2))
7026
buffer.writeu8(b, 124, buffer.readu8(b, 2))
7027
buffer.writei8(b, 125, buffer.readu8(b, 2))
7028
7029
buffer.writei16(b, 4, n)
7030
buffer.writef64(b, 132, buffer.readi16(b, 4))
7031
buffer.writei16(b, 140, buffer.readi16(b, 4))
7032
buffer.writeu16(b, 142, buffer.readi16(b, 4))
7033
7034
buffer.writeu16(b, 8, n)
7035
buffer.writef64(b, 148, buffer.readu16(b, 8))
7036
buffer.writeu16(b, 156, buffer.readu16(b, 8))
7037
buffer.writei16(b, 158, buffer.readu16(b, 8))
7038
7039
buffer.writei32(b, 12, n)
7040
buffer.writef64(b, 164, buffer.readi32(b, 12))
7041
buffer.writei32(b, 172, buffer.readi32(b, 12))
7042
buffer.writeu32(b, 176, buffer.readi32(b, 12))
7043
7044
buffer.writeu32(b, 20, n)
7045
buffer.writef64(b, 180, buffer.readu32(b, 20))
7046
buffer.writeu32(b, 188, buffer.readu32(b, 20))
7047
buffer.writei32(b, 192, buffer.readu32(b, 20))
7048
7049
buffer.writef32(b, 28, f)
7050
buffer.writef64(b, 196, buffer.readf32(b, 28))
7051
buffer.writef32(b, 196, buffer.readf32(b, 28))
7052
7053
buffer.writef64(b, 32, f)
7054
buffer.writef64(b, 204, buffer.readf64(b, 32))
7055
buffer.writef32(b, 204, buffer.readf64(b, 32))
7056
end
7057
)"),
7058
R"(
7059
; function test($arg0, $arg1, $arg2) line 2
7060
bb_0:
7061
CHECK_TAG R0, tbuffer, exit(entry)
7062
CHECK_TAG R1, tnumber, exit(entry)
7063
CHECK_TAG R2, tnumber, exit(entry)
7064
JUMP bb_2
7065
bb_2:
7066
JUMP bb_bytecode_1
7067
bb_bytecode_1:
7068
implicit CHECK_SAFE_ENV exit(0)
7069
STORE_DOUBLE R5, 0
7070
STORE_TAG R5, tnumber
7071
%17 = LOAD_POINTER R0
7072
CHECK_BUFFER_LEN %17, 0i, 0i, 212i, undef, exit(3)
7073
%21 = LOAD_DOUBLE R1
7074
%22 = NUM_TO_UINT %21
7075
BUFFER_WRITEI8 %17, 0i, %22, tbuffer
7076
%33 = SEXTI8_INT %22
7077
%34 = INT_TO_NUM %33
7078
BUFFER_WRITEF64 %17, 100i, %34, tbuffer
7079
BUFFER_WRITEI8 %17, 108i, %22, tbuffer
7080
BUFFER_WRITEI8 %17, 109i, %22, tbuffer
7081
BUFFER_WRITEI8 %17, 2i, %22, tbuffer
7082
%133 = BITAND_UINT %22, 255i
7083
%134 = INT_TO_NUM %133
7084
BUFFER_WRITEF64 %17, 116i, %134, tbuffer
7085
BUFFER_WRITEI8 %17, 124i, %22, tbuffer
7086
BUFFER_WRITEI8 %17, 125i, %22, tbuffer
7087
BUFFER_WRITEI16 %17, 4i, %22, tbuffer
7088
%233 = SEXTI16_INT %22
7089
%234 = INT_TO_NUM %233
7090
BUFFER_WRITEF64 %17, 132i, %234, tbuffer
7091
BUFFER_WRITEI16 %17, 140i, %22, tbuffer
7092
BUFFER_WRITEI16 %17, 142i, %22, tbuffer
7093
BUFFER_WRITEI16 %17, 8i, %22, tbuffer
7094
%333 = BITAND_UINT %22, 65535i
7095
%334 = INT_TO_NUM %333
7096
BUFFER_WRITEF64 %17, 148i, %334, tbuffer
7097
BUFFER_WRITEI16 %17, 156i, %22, tbuffer
7098
BUFFER_WRITEI16 %17, 158i, %22, tbuffer
7099
BUFFER_WRITEI32 %17, 12i, %22, tbuffer
7100
%433 = TRUNCATE_UINT %22
7101
%434 = INT_TO_NUM %433
7102
BUFFER_WRITEF64 %17, 164i, %434, tbuffer
7103
BUFFER_WRITEI32 %17, 172i, %22, tbuffer
7104
BUFFER_WRITEI32 %17, 176i, %22, tbuffer
7105
BUFFER_WRITEI32 %17, 20i, %22, tbuffer
7106
%534 = UINT_TO_NUM %22
7107
BUFFER_WRITEF64 %17, 180i, %534, tbuffer
7108
BUFFER_WRITEI32 %17, 188i, %22, tbuffer
7109
BUFFER_WRITEI32 %17, 192i, %22, tbuffer
7110
%621 = LOAD_DOUBLE R2
7111
%622 = NUM_TO_FLOAT %621
7112
BUFFER_WRITEF32 %17, 28i, %622, tbuffer
7113
%634 = FLOAT_TO_NUM %622
7114
BUFFER_WRITEF64 %17, 196i, %634, tbuffer
7115
BUFFER_WRITEF32 %17, 196i, %622, tbuffer
7116
BUFFER_WRITEF64 %17, 32i, %621, tbuffer
7117
BUFFER_WRITEF64 %17, 204i, %621, tbuffer
7118
BUFFER_WRITEF32 %17, 204i, %622, tbuffer
7119
INTERRUPT 372u
7120
RETURN R0, 0i
7121
)"
7122
);
7123
}
7124
7125
TEST_CASE_FIXTURE(LoweringFixture, "LoopStepDetection1")
7126
{
7127
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
7128
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
7129
assemblyOptions.includeRegFlowInfo = Luau::CodeGen::IncludeRegFlowInfo::Yes;
7130
7131
CHECK_EQ(
7132
"\n" + getCodegenAssembly(
7133
R"(
7134
local function foo(n: number)
7135
local s = 0
7136
for i = 1,n do
7137
s += i
7138
end
7139
return s
7140
end
7141
)"
7142
),
7143
R"(
7144
; function foo($arg0) line 2
7145
bb_0:
7146
; in regs: R0
7147
; out regs: R0
7148
CHECK_TAG R0, tnumber, exit(entry)
7149
JUMP bb_4
7150
bb_4:
7151
; in regs: R0
7152
; out regs: R0
7153
JUMP bb_bytecode_1
7154
bb_bytecode_1:
7155
; in regs: R0
7156
; out regs: R1, R2, R3, R4
7157
STORE_DOUBLE R1, 0
7158
STORE_TAG R1, tnumber
7159
STORE_DOUBLE R4, 1
7160
STORE_TAG R4, tnumber
7161
%8 = LOAD_TVALUE R0, 0i, tnumber
7162
STORE_TVALUE R2, %8
7163
STORE_DOUBLE R3, 1
7164
STORE_TAG R3, tnumber
7165
%16 = LOAD_DOUBLE R2
7166
JUMP_CMP_NUM 1, %16, not_le, bb_bytecode_3, bb_bytecode_2
7167
bb_bytecode_2:
7168
; in regs: R1, R2, R3, R4
7169
; out regs: R1, R2, R3, R4
7170
INTERRUPT 5u
7171
CHECK_TAG R1, tnumber, exit(5)
7172
CHECK_TAG R4, tnumber, exit(5)
7173
%24 = LOAD_DOUBLE R1
7174
%25 = LOAD_DOUBLE R4
7175
%26 = ADD_NUM %24, %25
7176
STORE_DOUBLE R1, %26
7177
%28 = LOAD_DOUBLE R2
7178
%30 = ADD_NUM %25, 1
7179
STORE_DOUBLE R4, %30
7180
JUMP_CMP_NUM %30, %28, le, bb_bytecode_2, bb_bytecode_3
7181
bb_bytecode_3:
7182
; in regs: R1
7183
INTERRUPT 7u
7184
RETURN R1, 1i
7185
)"
7186
);
7187
}
7188
7189
TEST_CASE_FIXTURE(LoweringFixture, "LoopStepDetection2")
7190
{
7191
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
7192
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
7193
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
7194
7195
CHECK_EQ(
7196
"\n" + getCodegenAssembly(
7197
R"(
7198
local function foo(n: number, t: {number})
7199
local s = 0
7200
for i = 1,#t do
7201
s += t[i]
7202
end
7203
return s
7204
end
7205
)"
7206
),
7207
R"(
7208
; function foo($arg0, $arg1) line 2
7209
bb_0:
7210
CHECK_TAG R0, tnumber, exit(entry)
7211
CHECK_TAG R1, ttable, exit(entry)
7212
JUMP bb_4
7213
bb_4:
7214
JUMP bb_bytecode_1
7215
bb_bytecode_1:
7216
STORE_DOUBLE R2, 0
7217
STORE_TAG R2, tnumber
7218
STORE_DOUBLE R5, 1
7219
STORE_TAG R5, tnumber
7220
%12 = LOAD_POINTER R1
7221
CHECK_NO_METATABLE %12, bb_fallback_5
7222
%14 = TABLE_LEN %12
7223
%15 = INT_TO_NUM %14
7224
STORE_DOUBLE R3, %15
7225
STORE_TAG R3, tnumber
7226
JUMP bb_6
7227
bb_6:
7228
STORE_DOUBLE R4, 1
7229
STORE_TAG R4, tnumber
7230
CHECK_TAG R3, tnumber, exit(4)
7231
CHECK_TAG R5, tnumber, exit(4)
7232
%28 = LOAD_DOUBLE R3
7233
JUMP_CMP_NUM R5, %28, not_le, bb_bytecode_3, bb_bytecode_2
7234
bb_bytecode_2:
7235
INTERRUPT 5u
7236
CHECK_TAG R5, tnumber, exit(5)
7237
%36 = LOAD_POINTER R1
7238
%37 = LOAD_DOUBLE R5
7239
%38 = TRY_NUM_TO_INDEX %37, bb_fallback_7
7240
%39 = SUB_INT %38, 1i
7241
CHECK_ARRAY_SIZE %36, %39, bb_fallback_7
7242
CHECK_NO_METATABLE %36, bb_fallback_7
7243
%42 = GET_ARR_ADDR %36, %39
7244
%43 = LOAD_TVALUE %42
7245
STORE_TVALUE R6, %43
7246
JUMP bb_8
7247
bb_8:
7248
CHECK_TAG R2, tnumber, exit(6)
7249
CHECK_TAG R6, tnumber, bb_fallback_9
7250
%53 = LOAD_DOUBLE R2
7251
%55 = ADD_NUM %53, R6
7252
STORE_DOUBLE R2, %55
7253
JUMP bb_10
7254
bb_10:
7255
%61 = LOAD_DOUBLE R3
7256
%62 = LOAD_DOUBLE R5
7257
%63 = ADD_NUM %62, 1
7258
STORE_DOUBLE R5, %63
7259
JUMP_CMP_NUM %63, %61, le, bb_bytecode_2, bb_bytecode_3
7260
bb_bytecode_3:
7261
INTERRUPT 8u
7262
RETURN R2, 1i
7263
)"
7264
);
7265
}
7266
7267
TEST_CASE_FIXTURE(LoweringFixture, "UintSourceSanity")
7268
{
7269
ScopedFastFlag luauCodegenBufNoDefTag{FFlag::LuauCodegenBufNoDefTag, true};
7270
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
7271
ScopedFastFlag luauCodegenBufferRangeMerge{FFlag::LuauCodegenBufferRangeMerge4, true};
7272
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
7273
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
7274
7275
// TODO: opportunity - many conversions and stores remain because of VM exits
7276
CHECK_EQ(
7277
"\n" + getCodegenAssembly(
7278
R"(
7279
local function foo(b: buffer, a: number, s: string)
7280
local r1 = buffer.readi32(b, bit32.bor(a, 0))
7281
local r2 = buffer.readu32(b, r1)
7282
local r3 = buffer.readi32(b, r2)
7283
local r4 = buffer.readu32(b, string.len(s))
7284
return r1, r2, r3, r4
7285
end
7286
)"
7287
),
7288
R"(
7289
; function foo($arg0, $arg1, $arg2) line 2
7290
bb_0:
7291
CHECK_TAG R0, tbuffer, exit(entry)
7292
CHECK_TAG R1, tnumber, exit(entry)
7293
CHECK_TAG R2, tstring, exit(entry)
7294
JUMP bb_2
7295
bb_2:
7296
JUMP bb_bytecode_1
7297
bb_bytecode_1:
7298
implicit CHECK_SAFE_ENV exit(0)
7299
%11 = LOAD_DOUBLE R1
7300
%12 = NUM_TO_UINT %11
7301
%15 = UINT_TO_NUM %12
7302
STORE_DOUBLE R5, %15
7303
STORE_TAG R5, tnumber
7304
%24 = LOAD_POINTER R0
7305
%26 = TRUNCATE_UINT %12
7306
CHECK_BUFFER_LEN %24, %26, 0i, 4i, undef, exit(9)
7307
%28 = BUFFER_READI32 %24, %26, tbuffer
7308
%29 = INT_TO_NUM %28
7309
STORE_DOUBLE R3, %29
7310
STORE_TAG R3, tnumber
7311
CHECK_BUFFER_LEN %24, %28, 0i, 4i, undef, exit(15)
7312
%42 = BUFFER_READI32 %24, %28, tbuffer
7313
%43 = UINT_TO_NUM %42
7314
STORE_DOUBLE R4, %43
7315
STORE_TAG R4, tnumber
7316
CHECK_BUFFER_LEN %24, %42, 0i, 4i, undef, exit(22)
7317
%56 = BUFFER_READI32 %24, %42, tbuffer
7318
%57 = INT_TO_NUM %56
7319
STORE_DOUBLE R5, %57
7320
%64 = LOAD_POINTER R2
7321
%65 = STRING_LEN %64
7322
%66 = INT_TO_NUM %65
7323
STORE_DOUBLE R8, %66
7324
STORE_TAG R8, tnumber
7325
CHECK_BUFFER_LEN %24, %65, 0i, 4i, undef, exit(34)
7326
%79 = BUFFER_READI32 %24, %65, tbuffer
7327
%80 = UINT_TO_NUM %79
7328
STORE_DOUBLE R6, %80
7329
STORE_TAG R6, tnumber
7330
INTERRUPT 38u
7331
RETURN R3, 4i
7332
)"
7333
);
7334
}
7335
7336
TEST_CASE_FIXTURE(LoweringFixture, "LibmIsPure")
7337
{
7338
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
7339
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
7340
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
7341
ScopedFastFlag luauCompileExtraTypes{FFlag::LuauCompileExtraTypes, true};
7342
7343
CHECK_EQ(
7344
"\n" + getCodegenAssembly(
7345
R"(
7346
local function foo(p: vector, v: vector): vector
7347
return vector.create(
7348
math.cos(0.6 * p.x + 0.4 * math.sin(v.y) + 0),
7349
math.cos(0.6 * p.x + 0.4 * math.sin(v.y) + 1),
7350
math.cos(0.6 * p.x + 0.4 * math.sin(v.y) + 2)
7351
)
7352
end
7353
)"
7354
),
7355
R"(
7356
; function foo($arg0, $arg1) line 2
7357
bb_0:
7358
CHECK_TAG R0, tvector, exit(entry)
7359
CHECK_TAG R1, tvector, exit(entry)
7360
JUMP bb_2
7361
bb_2:
7362
JUMP bb_bytecode_1
7363
bb_bytecode_1:
7364
implicit CHECK_SAFE_ENV exit(0)
7365
%8 = LOAD_FLOAT R0, 0i
7366
%9 = FLOAT_TO_NUM %8
7367
%15 = MUL_NUM %9, 0.59999999999999998
7368
%20 = LOAD_FLOAT R1, 4i
7369
%21 = FLOAT_TO_NUM %20
7370
%28 = INVOKE_LIBM 24u, %21
7371
%35 = MUL_NUM %28, 0.40000000000000002
7372
%44 = ADD_NUM %15, %35
7373
%50 = ADD_NUM %44, 0
7374
%57 = INVOKE_LIBM 9u, %50
7375
%105 = ADD_NUM %44, 1
7376
%112 = INVOKE_LIBM 9u, %105
7377
%160 = ADD_NUM %44, 2
7378
%167 = INVOKE_LIBM 9u, %160
7379
%181 = NUM_TO_FLOAT %57
7380
%182 = NUM_TO_FLOAT %112
7381
%183 = NUM_TO_FLOAT %167
7382
STORE_VECTOR R2, %181, %182, %183
7383
STORE_TAG R2, tvector
7384
INTERRUPT 52u
7385
RETURN R2, 1i
7386
)"
7387
);
7388
}
7389
7390
TEST_CASE_FIXTURE(LoweringFixture, "VecOpReuse")
7391
{
7392
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
7393
ScopedFastFlag luauCodegenMarkDeadRegisters{FFlag::LuauCodegenMarkDeadRegisters2, true};
7394
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
7395
7396
CHECK_EQ(
7397
"\n" + getCodegenAssembly(
7398
R"(
7399
local function foo(c: vector): vector
7400
return vector.create(
7401
math.sin(3.0 * vector.magnitude(c) + 6.0),
7402
math.sin(3.0 * vector.magnitude(c) + 1.0),
7403
math.sin(3.0 * vector.magnitude(c) + 2.0)
7404
)
7405
end
7406
)"
7407
),
7408
R"(
7409
; function foo($arg0) line 2
7410
bb_0:
7411
CHECK_TAG R0, tvector, exit(entry)
7412
JUMP bb_2
7413
bb_2:
7414
JUMP bb_bytecode_1
7415
bb_bytecode_1:
7416
implicit CHECK_SAFE_ENV exit(0)
7417
%7 = LOAD_TVALUE R0, 0i, tvector
7418
%8 = DOT_VEC %7, %7
7419
%9 = SQRT_FLOAT %8
7420
%10 = FLOAT_TO_NUM %9
7421
%17 = MUL_NUM %10, 3
7422
%23 = ADD_NUM %17, 6
7423
%30 = INVOKE_LIBM 24u, %23
7424
%53 = ADD_NUM %17, 1
7425
%60 = INVOKE_LIBM 24u, %53
7426
%83 = ADD_NUM %17, 2
7427
%90 = INVOKE_LIBM 24u, %83
7428
%104 = NUM_TO_FLOAT %30
7429
%105 = NUM_TO_FLOAT %60
7430
%106 = NUM_TO_FLOAT %90
7431
STORE_VECTOR R1, %104, %105, %106
7432
STORE_TAG R1, tvector
7433
INTERRUPT 37u
7434
RETURN R1, 1i
7435
)"
7436
);
7437
}
7438
7439
TEST_CASE_FIXTURE(LoweringFixture, "VecOpReuse2")
7440
{
7441
ScopedFastFlag luauCodegenBlockSafeEnv{FFlag::LuauCodegenBlockSafeEnv, true};
7442
ScopedFastFlag luauCompileVectorReveseMul{FFlag::LuauCompileVectorReveseMul, true};
7443
7444
CHECK_EQ(
7445
"\n" + getCodegenAssembly(
7446
R"(
7447
local function foo(c: vector, d: vector): vector
7448
return {2 * c + d, 2 * c + d}
7449
end
7450
)"
7451
),
7452
R"(
7453
; function foo($arg0, $arg1) line 2
7454
bb_0:
7455
CHECK_TAG R0, tvector, exit(entry)
7456
CHECK_TAG R1, tvector, exit(entry)
7457
JUMP bb_2
7458
bb_2:
7459
JUMP bb_bytecode_1
7460
bb_bytecode_1:
7461
SET_SAVEDPC 1u
7462
%7 = NEW_TABLE 2u, 0u
7463
STORE_POINTER R2, %7
7464
STORE_TAG R2, ttable
7465
CHECK_GC
7466
%13 = LOAD_TVALUE R0, 0i, tvector
7467
%15 = FLOAT_TO_VEC 2
7468
%16 = MUL_VEC %13, %15
7469
%24 = LOAD_TVALUE R1, 0i, tvector
7470
%25 = ADD_VEC %16, %24
7471
%26 = TAG_VECTOR %25
7472
STORE_TVALUE R3, %26
7473
STORE_TVALUE R4, %26
7474
SETLIST 6u, R2, R3, 2i, 1u, 2u
7475
INTERRUPT 8u
7476
RETURN R2, 1i
7477
)"
7478
);
7479
}
7480
7481
TEST_CASE_FIXTURE(LoweringFixture, "TableOperationTagSuggestion1")
7482
{
7483
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
7484
7485
CHECK_EQ(
7486
"\n" + getCodegenAssembly(
7487
R"(
7488
local function test(t: { x: number, y: number }, a: string)
7489
t.x = 2
7490
t[a] = 4
7491
return t.x * 2
7492
end
7493
)",
7494
true,
7495
1,
7496
2,
7497
true
7498
),
7499
R"(
7500
; function test($arg0, $arg1) line 2
7501
; R0: table [argument]
7502
; R1: string [argument]
7503
bb_0:
7504
CHECK_TAG R0, ttable, exit(entry)
7505
CHECK_TAG R1, tstring, exit(entry)
7506
JUMP bb_2
7507
bb_2:
7508
JUMP bb_bytecode_1
7509
bb_bytecode_1:
7510
STORE_DOUBLE R2, 2
7511
STORE_TAG R2, tnumber
7512
%10 = LOAD_POINTER R0
7513
%11 = GET_SLOT_NODE_ADDR %10, 1u, K0 ('x')
7514
CHECK_SLOT_MATCH %11, K0 ('x'), bb_fallback_3
7515
CHECK_READONLY %10, bb_fallback_3
7516
STORE_SPLIT_TVALUE %11, tnumber, 2, 0i
7517
JUMP bb_linear_9
7518
bb_linear_9:
7519
STORE_DOUBLE R2, 4
7520
SET_SAVEDPC 5u
7521
SET_TABLE R2, R0, R1
7522
%50 = LOAD_POINTER R0
7523
%51 = GET_SLOT_NODE_ADDR %50, 5u, K0 ('x')
7524
CHECK_SLOT_MATCH %51, K0 ('x'), bb_fallback_5
7525
%53 = LOAD_TVALUE %51, 0i
7526
STORE_TVALUE R3, %53
7527
CHECK_TAG R3, tnumber, bb_fallback_7
7528
%58 = LOAD_DOUBLE R3
7529
%59 = ADD_NUM %58, %58
7530
STORE_DOUBLE R2, %59
7531
INTERRUPT 8u
7532
RETURN R2, 1i
7533
)"
7534
);
7535
}
7536
7537
TEST_CASE_FIXTURE(LoweringFixture, "TableOperationTagSuggestion2")
7538
{
7539
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
7540
7541
CHECK_EQ(
7542
"\n" + getCodegenAssembly(
7543
R"(
7544
local function test(self, t: { id: string }, a: number)
7545
self.map[t.id] = self.map[t.id] + a
7546
self.foo(self.map[t.id])
7547
end
7548
)",
7549
true,
7550
1,
7551
2,
7552
true
7553
),
7554
R"(
7555
; function test($arg0, $arg1, $arg2) line 2
7556
; R1: table [argument]
7557
; R2: number [argument]
7558
; R4: string from 10 to 11
7559
; R6: string from 17 to 18
7560
; R8: string from 8 to 9
7561
bb_0:
7562
CHECK_TAG R1, ttable, exit(entry)
7563
CHECK_TAG R2, tnumber, exit(entry)
7564
JUMP bb_2
7565
bb_2:
7566
JUMP bb_bytecode_1
7567
bb_bytecode_1:
7568
CHECK_TAG R0, ttable, bb_fallback_3
7569
%8 = LOAD_POINTER R0
7570
%9 = GET_SLOT_NODE_ADDR %8, 0u, K0 ('map')
7571
CHECK_SLOT_MATCH %9, K0 ('map'), bb_fallback_3
7572
%11 = LOAD_TVALUE %9, 0i
7573
STORE_TVALUE R3, %11
7574
JUMP bb_linear_19
7575
bb_linear_19:
7576
%100 = LOAD_POINTER R1
7577
%101 = GET_SLOT_NODE_ADDR %100, 2u, K1 ('id')
7578
CHECK_SLOT_MATCH %101, K1 ('id'), bb_fallback_5
7579
%103 = LOAD_TVALUE %101, 0i
7580
STORE_TVALUE R4, %103
7581
STORE_TVALUE R7, %11
7582
STORE_TVALUE R8, %103
7583
SET_SAVEDPC 9u
7584
GET_TABLE R6, R7, R8
7585
CHECK_TAG R6, tnumber, bb_fallback_11
7586
%124 = LOAD_DOUBLE R6
7587
%126 = ADD_NUM %124, R2
7588
STORE_DOUBLE R5, %126
7589
STORE_TAG R5, tnumber
7590
SET_SAVEDPC 11u
7591
SET_TABLE R5, R3, R4
7592
%134 = LOAD_POINTER R0
7593
%135 = GET_SLOT_NODE_ADDR %134, 11u, K2 ('foo')
7594
CHECK_SLOT_MATCH %135, K2 ('foo'), bb_fallback_13
7595
%137 = LOAD_TVALUE %135, 0i
7596
STORE_TVALUE R3, %137
7597
%143 = GET_SLOT_NODE_ADDR %134, 13u, K0 ('map')
7598
CHECK_SLOT_MATCH %143, K0 ('map'), bb_fallback_15
7599
%145 = LOAD_TVALUE %143, 0i
7600
STORE_TVALUE R5, %145
7601
%148 = LOAD_POINTER R1
7602
%149 = GET_SLOT_NODE_ADDR %148, 15u, K1 ('id')
7603
CHECK_SLOT_MATCH %149, K1 ('id'), bb_fallback_17
7604
%151 = LOAD_TVALUE %149, 0i
7605
STORE_TVALUE R6, %151
7606
SET_SAVEDPC 18u
7607
GET_TABLE R4, R5, R6
7608
INTERRUPT 18u
7609
SET_SAVEDPC 19u
7610
CALL R3, 1i, 0i
7611
INTERRUPT 19u
7612
RETURN R0, 0i
7613
)"
7614
);
7615
}
7616
7617
TEST_CASE_FIXTURE(LoweringFixture, "Collatz")
7618
{
7619
ScopedFastFlag luauCodegenSetBlockEntryState{FFlag::LuauCodegenSetBlockEntryState3, true};
7620
ScopedFastFlag luauCodegenDseOnCondJump{FFlag::LuauCodegenDseOnCondJump, true};
7621
7622
CHECK_EQ(
7623
"\n" + getCodegenAssembly(
7624
R"(
7625
local function collatz(x : number)
7626
return if ((x % 2) == 1) then 3 * x + 1 else x // 2
7627
end
7628
)",
7629
true,
7630
1,
7631
2
7632
),
7633
R"(
7634
; function collatz($arg0) line 2
7635
; R0: number [argument]
7636
bb_0:
7637
CHECK_TAG R0, tnumber, exit(entry)
7638
JUMP bb_3
7639
bb_3:
7640
JUMP bb_bytecode_1
7641
bb_bytecode_1:
7642
%6 = LOAD_DOUBLE R0
7643
%7 = MOD_NUM %6, 2
7644
JUMP bb_5
7645
bb_5:
7646
JUMP_CMP_NUM %7, 1, not_eq, bb_bytecode_2, bb_4
7647
bb_4:
7648
%16 = LOAD_DOUBLE R0
7649
%17 = MUL_NUM %16, 3
7650
%23 = ADD_NUM %17, 1
7651
STORE_DOUBLE R1, %23
7652
STORE_TAG R1, tnumber
7653
INTERRUPT 5u
7654
RETURN R1, 1i
7655
bb_bytecode_2:
7656
%30 = LOAD_DOUBLE R0
7657
%31 = IDIV_NUM %30, 2
7658
STORE_DOUBLE R1, %31
7659
STORE_TAG R1, tnumber
7660
INTERRUPT 7u
7661
RETURN R1, 1i
7662
)"
7663
);
7664
}
7665
7666
TEST_SUITE_END();
7667
7668