luau/tests/IrLowering.test.cpp
aaron 68bd1b2349
Sync to upstream/release/622 (#1232)
# What's changed?

* Improved the actual message for the type errors for `cannot call
non-function` when attempting to call a union of functions/callable
tables. The error now correctly explains the issue is an inability to
determine the return type of the call in this situation.
* Resolve an issue where tables and metatables were not correctly being
cloned during instantiation (fixes #1176).
* Refactor `luaM_getnextgcopage` to `luaM_getnextpage` (generally
removing `gco` prefix where appropriate).
* Optimize `table.move` between tables for large ranges of elements.
* Reformat a bunch of code automatically using `clang-format`.

### New Type Solver

* Clean up minimally-used or unused constraints in the constraint solver
(`InstantiationConstraint`, `SetOpConstraint`,
`SingletonOrTopTypeConstraint`).
* Add a builtin `singleton` type family to replace
`SingletonOrTopTypeConstraint` when inferring refinements.
* Fixed a crash involving type path reasoning by recording when type
family reduction has taken place in the path.
* Improved constraint ordering by blocking on unreduced types families
that are not yet proven uninhabitable.
* Improved the handling of `SetIndexerConstraints` for both better
inference quality and to resolve crashes.
* Fix a crash when normalizing cyclic unions of intersections.
* Fix a crash when normalizing an intersection with the negation of
`unknown`.
* Fix a number of crashes caused by missing `follow` calls on `TypeId`s.
* Changed type family reduction to correctly use a semantic notion of
uninhabited types, rather than checking for `never` types specifically.
* Refactor the `union` and `intersect` type families to be variadic.

### Native Code Generation

* Improve translation for userdata key get/set and userdata/vector
namecall.
* Provide `[top level]` and `[anonymous]` as function names to
`FunctionStats` as appropriate when no function name is available.
* Disable unwind support on Android platforms since it is unsupported.
*  

---

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-04-19 14:48:02 -07:00

549 lines
12 KiB
C++

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lua.h"
#include "lualib.h"
#include "Luau/BytecodeBuilder.h"
#include "Luau/CodeGen.h"
#include "Luau/Compiler.h"
#include "Luau/Parser.h"
#include "doctest.h"
#include "ScopedFlags.h"
#include <memory>
LUAU_FASTFLAG(LuauCodegenRemoveDeadStores5)
LUAU_FASTFLAG(LuauCodegenLoadTVTag)
LUAU_FASTFLAG(LuauCodegenDirectUserdataFlow)
static std::string getCodegenAssembly(const char* source)
{
Luau::CodeGen::AssemblyOptions options;
// For IR, we don't care about assembly, but we want a stable target
options.target = Luau::CodeGen::AssemblyOptions::Target::X64_SystemV;
options.outputBinary = false;
options.includeAssembly = false;
options.includeIr = true;
options.includeOutlinedCode = false;
options.includeIrPrefix = Luau::CodeGen::IncludeIrPrefix::No;
options.includeUseInfo = Luau::CodeGen::IncludeUseInfo::No;
options.includeCfgInfo = Luau::CodeGen::IncludeCfgInfo::No;
options.includeRegFlowInfo = Luau::CodeGen::IncludeRegFlowInfo::No;
Luau::Allocator allocator;
Luau::AstNameTable names(allocator);
Luau::ParseResult result = Luau::Parser::parse(source, strlen(source), names, allocator);
if (!result.errors.empty())
throw Luau::ParseErrors(result.errors);
Luau::CompileOptions copts = {};
copts.optimizationLevel = 2;
copts.debugLevel = 1;
copts.vectorCtor = "vector";
copts.vectorType = "vector";
Luau::BytecodeBuilder bcb;
Luau::compileOrThrow(bcb, result, names, copts);
std::string bytecode = bcb.getBytecode();
std::unique_ptr<lua_State, void (*)(lua_State*)> globalState(luaL_newstate(), lua_close);
lua_State* L = globalState.get();
if (luau_load(L, "name", bytecode.data(), bytecode.size(), 0) == 0)
return Luau::CodeGen::getAssembly(L, -1, options, nullptr);
FAIL("Failed to load bytecode");
return "";
}
TEST_SUITE_BEGIN("IrLowering");
TEST_CASE("VectorReciprocal")
{
CHECK_EQ("\n" + getCodegenAssembly(R"(
local function vecrcp(a: vector)
return 1 / a
end
)"),
R"(
; function vecrcp($arg0) line 2
bb_0:
CHECK_TAG R0, tvector, exit(entry)
JUMP bb_2
bb_2:
JUMP bb_bytecode_1
bb_bytecode_1:
%6 = NUM_TO_VEC 1
%7 = LOAD_TVALUE R0
%8 = DIV_VEC %6, %7
%9 = TAG_VECTOR %8
STORE_TVALUE R1, %9
INTERRUPT 1u
RETURN R1, 1i
)");
}
TEST_CASE("VectorComponentRead")
{
ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true};
CHECK_EQ("\n" + getCodegenAssembly(R"(
local function compsum(a: vector)
return a.X + a.Y + a.Z
end
)"),
R"(
; function compsum($arg0) line 2
bb_0:
CHECK_TAG R0, tvector, exit(entry)
JUMP bb_2
bb_2:
JUMP bb_bytecode_1
bb_bytecode_1:
%6 = LOAD_FLOAT R0, 0i
%11 = LOAD_FLOAT R0, 4i
%20 = ADD_NUM %6, %11
%25 = LOAD_FLOAT R0, 8i
%34 = ADD_NUM %20, %25
STORE_DOUBLE R1, %34
STORE_TAG R1, tnumber
INTERRUPT 8u
RETURN R1, 1i
)");
}
TEST_CASE("VectorAdd")
{
CHECK_EQ("\n" + getCodegenAssembly(R"(
local function vec3add(a: vector, b: vector)
return a + b
end
)"),
R"(
; function vec3add($arg0, $arg1) line 2
bb_0:
CHECK_TAG R0, tvector, exit(entry)
CHECK_TAG R1, tvector, exit(entry)
JUMP bb_2
bb_2:
JUMP bb_bytecode_1
bb_bytecode_1:
%10 = LOAD_TVALUE R0
%11 = LOAD_TVALUE R1
%12 = ADD_VEC %10, %11
%13 = TAG_VECTOR %12
STORE_TVALUE R2, %13
INTERRUPT 1u
RETURN R2, 1i
)");
}
TEST_CASE("VectorMinus")
{
CHECK_EQ("\n" + getCodegenAssembly(R"(
local function vec3minus(a: vector)
return -a
end
)"),
R"(
; function vec3minus($arg0) line 2
bb_0:
CHECK_TAG R0, tvector, exit(entry)
JUMP bb_2
bb_2:
JUMP bb_bytecode_1
bb_bytecode_1:
%6 = LOAD_TVALUE R0
%7 = UNM_VEC %6
%8 = TAG_VECTOR %7
STORE_TVALUE R1, %8
INTERRUPT 1u
RETURN R1, 1i
)");
}
TEST_CASE("VectorSubMulDiv")
{
ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true};
CHECK_EQ("\n" + getCodegenAssembly(R"(
local function vec3combo(a: vector, b: vector, c: vector, d: vector)
return a * b - c / d
end
)"),
R"(
; function vec3combo($arg0, $arg1, $arg2, $arg3) line 2
bb_0:
CHECK_TAG R0, tvector, exit(entry)
CHECK_TAG R1, tvector, exit(entry)
CHECK_TAG R2, tvector, exit(entry)
CHECK_TAG R3, tvector, exit(entry)
JUMP bb_2
bb_2:
JUMP bb_bytecode_1
bb_bytecode_1:
%14 = LOAD_TVALUE R0
%15 = LOAD_TVALUE R1
%16 = MUL_VEC %14, %15
%23 = LOAD_TVALUE R2
%24 = LOAD_TVALUE R3
%25 = DIV_VEC %23, %24
%34 = SUB_VEC %16, %25
%35 = TAG_VECTOR %34
STORE_TVALUE R4, %35
INTERRUPT 3u
RETURN R4, 1i
)");
}
TEST_CASE("VectorSubMulDiv2")
{
ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true};
CHECK_EQ("\n" + getCodegenAssembly(R"(
local function vec3combo(a: vector)
local tmp = a * a
return (tmp - tmp) / (tmp + tmp)
end
)"),
R"(
; function vec3combo($arg0) line 2
bb_0:
CHECK_TAG R0, tvector, exit(entry)
JUMP bb_2
bb_2:
JUMP bb_bytecode_1
bb_bytecode_1:
%8 = LOAD_TVALUE R0
%10 = MUL_VEC %8, %8
%19 = SUB_VEC %10, %10
%28 = ADD_VEC %10, %10
%37 = DIV_VEC %19, %28
%38 = TAG_VECTOR %37
STORE_TVALUE R2, %38
INTERRUPT 4u
RETURN R2, 1i
)");
}
TEST_CASE("VectorMulDivMixed")
{
ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true};
CHECK_EQ("\n" + getCodegenAssembly(R"(
local function vec3combo(a: vector, b: vector, c: vector, d: vector)
return a * 2 + b / 4 + 0.5 * c + 40 / d
end
)"),
R"(
; function vec3combo($arg0, $arg1, $arg2, $arg3) line 2
bb_0:
CHECK_TAG R0, tvector, exit(entry)
CHECK_TAG R1, tvector, exit(entry)
CHECK_TAG R2, tvector, exit(entry)
CHECK_TAG R3, tvector, exit(entry)
JUMP bb_2
bb_2:
JUMP bb_bytecode_1
bb_bytecode_1:
%12 = LOAD_TVALUE R0
%13 = NUM_TO_VEC 2
%14 = MUL_VEC %12, %13
%19 = LOAD_TVALUE R1
%20 = NUM_TO_VEC 4
%21 = DIV_VEC %19, %20
%30 = ADD_VEC %14, %21
%40 = NUM_TO_VEC 0.5
%41 = LOAD_TVALUE R2
%42 = MUL_VEC %40, %41
%51 = ADD_VEC %30, %42
%56 = NUM_TO_VEC 40
%57 = LOAD_TVALUE R3
%58 = DIV_VEC %56, %57
%67 = ADD_VEC %51, %58
%68 = TAG_VECTOR %67
STORE_TVALUE R4, %68
INTERRUPT 8u
RETURN R4, 1i
)");
}
TEST_CASE("ExtraMathMemoryOperands")
{
ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true};
CHECK_EQ("\n" + getCodegenAssembly(R"(
local function foo(a: number, b: number, c: number, d: number, e: number)
return math.floor(a) + math.ceil(b) + math.round(c) + math.sqrt(d) + math.abs(e)
end
)"),
R"(
; function foo($arg0, $arg1, $arg2, $arg3, $arg4) line 2
bb_0:
CHECK_TAG R0, tnumber, exit(entry)
CHECK_TAG R1, tnumber, exit(entry)
CHECK_TAG R2, tnumber, exit(entry)
CHECK_TAG R3, tnumber, exit(entry)
CHECK_TAG R4, tnumber, exit(entry)
JUMP bb_2
bb_2:
JUMP bb_bytecode_1
bb_bytecode_1:
CHECK_SAFE_ENV exit(1)
%16 = FLOOR_NUM R0
%23 = CEIL_NUM R1
%32 = ADD_NUM %16, %23
%39 = ROUND_NUM R2
%48 = ADD_NUM %32, %39
%55 = SQRT_NUM R3
%64 = ADD_NUM %48, %55
%71 = ABS_NUM R4
%80 = ADD_NUM %64, %71
STORE_DOUBLE R5, %80
STORE_TAG R5, tnumber
INTERRUPT 29u
RETURN R5, 1i
)");
}
TEST_CASE("DseInitialStackState")
{
ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true};
CHECK_EQ("\n" + getCodegenAssembly(R"(
local function foo()
while {} do
local _ = not _,{}
_ = nil
end
end
)"),
R"(
; function foo() line 2
bb_bytecode_0:
SET_SAVEDPC 1u
%1 = NEW_TABLE 0u, 0u
STORE_POINTER R0, %1
STORE_TAG R0, ttable
CHECK_GC
JUMP bb_2
bb_2:
CHECK_SAFE_ENV exit(3)
JUMP_EQ_TAG K1, tnil, bb_fallback_4, bb_3
bb_3:
%9 = LOAD_TVALUE K1
STORE_TVALUE R1, %9
JUMP bb_5
bb_5:
SET_SAVEDPC 7u
%21 = NEW_TABLE 0u, 0u
STORE_POINTER R1, %21
STORE_TAG R1, ttable
CHECK_GC
STORE_TAG R0, tnil
INTERRUPT 9u
JUMP bb_bytecode_0
)");
}
TEST_CASE("DseInitialStackState2")
{
ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true};
CHECK_EQ("\n" + getCodegenAssembly(R"(
local function foo(a)
math.frexp(a)
return a
end
)"),
R"(
; function foo($arg0) line 2
bb_bytecode_0:
CHECK_SAFE_ENV exit(1)
CHECK_TAG R0, tnumber, exit(1)
FASTCALL 14u, R1, R0, undef, 1i, 2i
INTERRUPT 5u
RETURN R0, 1i
)");
}
TEST_CASE("DseInitialStackState3")
{
ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true};
CHECK_EQ("\n" + getCodegenAssembly(R"(
local function foo(a)
math.sign(a)
return a
end
)"),
R"(
; function foo($arg0) line 2
bb_bytecode_0:
CHECK_SAFE_ENV exit(1)
CHECK_TAG R0, tnumber, exit(1)
FASTCALL 47u, R1, R0, undef, 1i, 1i
INTERRUPT 5u
RETURN R0, 1i
)");
}
TEST_CASE("VectorConstantTag")
{
ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true};
ScopedFastFlag luauCodegenLoadTVTag{FFlag::LuauCodegenLoadTVTag, true};
CHECK_EQ("\n" + getCodegenAssembly(R"(
local function vecrcp(a: vector)
return vector(1, 2, 3) + a
end
)"),
R"(
; function vecrcp($arg0) line 2
bb_0:
CHECK_TAG R0, tvector, exit(entry)
JUMP bb_2
bb_2:
JUMP bb_bytecode_1
bb_bytecode_1:
%4 = LOAD_TVALUE K0, 0i, tvector
%11 = LOAD_TVALUE R0
%12 = ADD_VEC %4, %11
%13 = TAG_VECTOR %12
STORE_TVALUE R1, %13
INTERRUPT 2u
RETURN R1, 1i
)");
}
TEST_CASE("VectorNamecall")
{
ScopedFastFlag luauCodegenDirectUserdataFlow{FFlag::LuauCodegenDirectUserdataFlow, true};
CHECK_EQ("\n" + getCodegenAssembly(R"(
local function abs(a: vector)
return a:Abs()
end
)"),
R"(
; function abs($arg0) line 2
bb_0:
CHECK_TAG R0, tvector, exit(entry)
JUMP bb_2
bb_2:
JUMP bb_bytecode_1
bb_bytecode_1:
FALLBACK_NAMECALL 0u, R1, R0, K0
INTERRUPT 2u
SET_SAVEDPC 3u
CALL R1, 1i, -1i
INTERRUPT 3u
RETURN R1, -1i
)");
}
TEST_CASE("UserDataGetIndex")
{
ScopedFastFlag luauCodegenDirectUserdataFlow{FFlag::LuauCodegenDirectUserdataFlow, true};
CHECK_EQ("\n" + getCodegenAssembly(R"(
local function getxy(a: Point)
return a.x + a.y
end
)"),
R"(
; function getxy($arg0) line 2
bb_0:
CHECK_TAG R0, tuserdata, exit(entry)
JUMP bb_2
bb_2:
JUMP bb_bytecode_1
bb_bytecode_1:
FALLBACK_GETTABLEKS 0u, R2, R0, K0
FALLBACK_GETTABLEKS 2u, R3, R0, K1
CHECK_TAG R2, tnumber, bb_fallback_3
CHECK_TAG R3, tnumber, bb_fallback_3
%14 = LOAD_DOUBLE R2
%16 = ADD_NUM %14, R3
STORE_DOUBLE R1, %16
STORE_TAG R1, tnumber
JUMP bb_4
bb_4:
INTERRUPT 5u
RETURN R1, 1i
)");
}
TEST_CASE("UserDataSetIndex")
{
ScopedFastFlag luauCodegenDirectUserdataFlow{FFlag::LuauCodegenDirectUserdataFlow, true};
CHECK_EQ("\n" + getCodegenAssembly(R"(
local function setxy(a: Point)
a.x = 3
a.y = 4
end
)"),
R"(
; function setxy($arg0) line 2
bb_0:
CHECK_TAG R0, tuserdata, exit(entry)
JUMP bb_2
bb_2:
JUMP bb_bytecode_1
bb_bytecode_1:
STORE_DOUBLE R1, 3
STORE_TAG R1, tnumber
FALLBACK_SETTABLEKS 1u, R1, R0, K0
STORE_DOUBLE R1, 4
FALLBACK_SETTABLEKS 4u, R1, R0, K1
INTERRUPT 6u
RETURN R0, 0i
)");
}
TEST_CASE("UserDataNamecall")
{
ScopedFastFlag luauCodegenDirectUserdataFlow{FFlag::LuauCodegenDirectUserdataFlow, true};
CHECK_EQ("\n" + getCodegenAssembly(R"(
local function getxy(a: Point)
return a:GetX() + a:GetY()
end
)"),
R"(
; function getxy($arg0) line 2
bb_0:
CHECK_TAG R0, tuserdata, exit(entry)
JUMP bb_2
bb_2:
JUMP bb_bytecode_1
bb_bytecode_1:
FALLBACK_NAMECALL 0u, R2, R0, K0
INTERRUPT 2u
SET_SAVEDPC 3u
CALL R2, 1i, 1i
FALLBACK_NAMECALL 3u, R3, R0, K1
INTERRUPT 5u
SET_SAVEDPC 6u
CALL R3, 1i, 1i
CHECK_TAG R2, tnumber, bb_fallback_3
CHECK_TAG R3, tnumber, bb_fallback_3
%20 = LOAD_DOUBLE R2
%22 = ADD_NUM %20, R3
STORE_DOUBLE R1, %22
STORE_TAG R1, tnumber
JUMP bb_4
bb_4:
INTERRUPT 7u
RETURN R1, 1i
)");
}
TEST_SUITE_END();