mirror of
https://github.com/luau-lang/luau.git
synced 2025-08-26 11:27:08 +01:00
# What's Changed? We've been hard at work fixing bugs and introducing new features! ## VM * Include constant-folding information in Luau cost model for inlining and loop unrolling * ~1% improvement in compile times ## New Type Solver * `Luau::shallowClone`'s last argument, whether to clone persistent (builtin) types, is now non-optional. * Refinements on properties of tables are now computed with a `read` table property. This resolves some issues around refining table properies and then trying to set them. Fixes #1344. Fixes #1651. ``` if foo.bar then -- Prior to this release, this would be `typeof(foo) & { bar: ~(false?) } -- Now, this is `typeof(foo) & { read bar: ~(false?) } end ``` * The type function `keyof` should respect the empty string as a property, as in: ``` -- equivalent to type Foo ="" type Foo = keyof<{ [""]: number }> ``` * Descend into literals to report subtyping errors for function calls: this both improves bidirectional inference and makes errors more specific. Before, the error reporting for a table with incorrect members passed to a function would cite the entire table, but now it only cites the members that are incorrectly typed. * Fixes a case where intersecting two tables without any common properties would create `never`, instead of a table with both of their properties. # Internal Contributors Co-authored-by: Ariel Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: James McNellis <jmcnellis@roblox.com> Co-authored-by: Sora Kanosue <skanosue@roblox.com> Co-authored-by: Talha Pathan <tpathan@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com> Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Ariel Weiss <aaronweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com>
266 lines
5.5 KiB
C++
266 lines
5.5 KiB
C++
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
#include "Luau/Parser.h"
|
|
|
|
#include "ScopedFlags.h"
|
|
|
|
#include "doctest.h"
|
|
|
|
LUAU_FASTFLAG(LuauCompileCostModelConstants)
|
|
|
|
using namespace Luau;
|
|
|
|
namespace Luau
|
|
{
|
|
namespace Compile
|
|
{
|
|
|
|
uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount);
|
|
int computeCost(uint64_t model, const bool* varsConst, size_t varCount);
|
|
|
|
} // namespace Compile
|
|
} // namespace Luau
|
|
|
|
TEST_SUITE_BEGIN("CostModel");
|
|
|
|
static uint64_t modelFunction(const char* source)
|
|
{
|
|
Allocator allocator;
|
|
AstNameTable names(allocator);
|
|
|
|
ParseResult result = Parser::parse(source, strlen(source), names, allocator);
|
|
REQUIRE(result.root != nullptr);
|
|
|
|
AstStatFunction* func = result.root->body.data[0]->as<AstStatFunction>();
|
|
REQUIRE(func);
|
|
|
|
return Luau::Compile::modelCost(func->func->body, func->func->args.data, func->func->args.size);
|
|
}
|
|
|
|
TEST_CASE("Expression")
|
|
{
|
|
uint64_t model = modelFunction(R"(
|
|
function test(a, b, c)
|
|
return a + (b + 1) * (b + 1) - c
|
|
end
|
|
)");
|
|
|
|
const bool args1[] = {false, false, false};
|
|
const bool args2[] = {false, true, false};
|
|
|
|
CHECK_EQ(5, Luau::Compile::computeCost(model, args1, 3));
|
|
CHECK_EQ(2, Luau::Compile::computeCost(model, args2, 3));
|
|
}
|
|
|
|
TEST_CASE("PropagateVariable")
|
|
{
|
|
uint64_t model = modelFunction(R"(
|
|
function test(a)
|
|
local b = a * a * a
|
|
return b * b
|
|
end
|
|
)");
|
|
|
|
const bool args1[] = {false};
|
|
const bool args2[] = {true};
|
|
|
|
CHECK_EQ(3, Luau::Compile::computeCost(model, args1, 1));
|
|
CHECK_EQ(0, Luau::Compile::computeCost(model, args2, 1));
|
|
}
|
|
|
|
TEST_CASE("LoopAssign")
|
|
{
|
|
uint64_t model = modelFunction(R"(
|
|
function test(a)
|
|
for i=1,3 do
|
|
a[i] = i
|
|
end
|
|
end
|
|
)");
|
|
|
|
const bool args1[] = {false};
|
|
const bool args2[] = {true};
|
|
|
|
// loop baseline cost is 5
|
|
CHECK_EQ(6, Luau::Compile::computeCost(model, args1, 1));
|
|
CHECK_EQ(6, Luau::Compile::computeCost(model, args2, 1));
|
|
}
|
|
|
|
TEST_CASE("MutableVariable")
|
|
{
|
|
uint64_t model = modelFunction(R"(
|
|
function test(a, b)
|
|
local x = a * a
|
|
x += b
|
|
return x * x
|
|
end
|
|
)");
|
|
|
|
const bool args1[] = {false};
|
|
const bool args2[] = {true};
|
|
|
|
CHECK_EQ(3, Luau::Compile::computeCost(model, args1, 1));
|
|
CHECK_EQ(2, Luau::Compile::computeCost(model, args2, 1));
|
|
}
|
|
|
|
TEST_CASE("ImportCall")
|
|
{
|
|
uint64_t model = modelFunction(R"(
|
|
function test(a)
|
|
return Instance.new(a)
|
|
end
|
|
)");
|
|
|
|
const bool args1[] = {false};
|
|
const bool args2[] = {true};
|
|
|
|
CHECK_EQ(6, Luau::Compile::computeCost(model, args1, 1));
|
|
CHECK_EQ(6, Luau::Compile::computeCost(model, args2, 1));
|
|
}
|
|
|
|
TEST_CASE("FastCall")
|
|
{
|
|
uint64_t model = modelFunction(R"(
|
|
function test(a)
|
|
return math.abs(a + 1)
|
|
end
|
|
)");
|
|
|
|
const bool args1[] = {false};
|
|
const bool args2[] = {true};
|
|
|
|
// note: we currently don't treat fast calls differently from cost model perspective
|
|
CHECK_EQ(6, Luau::Compile::computeCost(model, args1, 1));
|
|
CHECK_EQ(5, Luau::Compile::computeCost(model, args2, 1));
|
|
}
|
|
|
|
TEST_CASE("ControlFlow")
|
|
{
|
|
ScopedFastFlag luauCompileCostModelConstants{FFlag::LuauCompileCostModelConstants, false};
|
|
|
|
uint64_t model = modelFunction(R"(
|
|
function test(a)
|
|
while a < 0 do
|
|
a += 1
|
|
end
|
|
for i=10,1,-1 do
|
|
a += 1
|
|
end
|
|
for i in pairs({}) do
|
|
a += 1
|
|
if a % 2 == 0 then continue end
|
|
end
|
|
repeat
|
|
a += 1
|
|
if a % 2 == 0 then break end
|
|
until a > 10
|
|
return a
|
|
end
|
|
)");
|
|
|
|
const bool args1[] = {false};
|
|
const bool args2[] = {true};
|
|
|
|
CHECK_EQ(76, Luau::Compile::computeCost(model, args1, 1));
|
|
CHECK_EQ(73, Luau::Compile::computeCost(model, args2, 1));
|
|
}
|
|
|
|
TEST_CASE("Conditional")
|
|
{
|
|
uint64_t model = modelFunction(R"(
|
|
function test(a)
|
|
return if a < 0 then -a else a
|
|
end
|
|
)");
|
|
|
|
const bool args1[] = {false};
|
|
const bool args2[] = {true};
|
|
|
|
CHECK_EQ(4, Luau::Compile::computeCost(model, args1, 1));
|
|
CHECK_EQ(2, Luau::Compile::computeCost(model, args2, 1));
|
|
}
|
|
|
|
TEST_CASE("VarArgs")
|
|
{
|
|
uint64_t model = modelFunction(R"(
|
|
function test(...)
|
|
return select('#', ...) :: number
|
|
end
|
|
)");
|
|
|
|
CHECK_EQ(8, Luau::Compile::computeCost(model, nullptr, 0));
|
|
}
|
|
|
|
TEST_CASE("TablesFunctions")
|
|
{
|
|
uint64_t model = modelFunction(R"(
|
|
function test()
|
|
return { 42, op = function() end }
|
|
end
|
|
)");
|
|
|
|
CHECK_EQ(22, Luau::Compile::computeCost(model, nullptr, 0));
|
|
}
|
|
|
|
TEST_CASE("CostOverflow")
|
|
{
|
|
uint64_t model = modelFunction(R"(
|
|
function test()
|
|
return {{{{{{{{{{{{{{{}}}}}}}}}}}}}}}
|
|
end
|
|
)");
|
|
|
|
CHECK_EQ(127, Luau::Compile::computeCost(model, nullptr, 0));
|
|
}
|
|
|
|
TEST_CASE("TableAssign")
|
|
{
|
|
uint64_t model = modelFunction(R"(
|
|
function test(a)
|
|
for i=1,#a do
|
|
a[i] = i
|
|
end
|
|
end
|
|
)");
|
|
|
|
const bool args1[] = {false};
|
|
const bool args2[] = {true};
|
|
|
|
CHECK_EQ(7, Luau::Compile::computeCost(model, args1, 1));
|
|
CHECK_EQ(6, Luau::Compile::computeCost(model, args2, 1));
|
|
}
|
|
|
|
TEST_CASE("InterpString")
|
|
{
|
|
uint64_t model = modelFunction(R"(
|
|
function test(a)
|
|
return `hello, {a}!`
|
|
end
|
|
)");
|
|
|
|
const bool args1[] = {false};
|
|
const bool args2[] = {true};
|
|
|
|
CHECK_EQ(3, Luau::Compile::computeCost(model, args1, 1));
|
|
CHECK_EQ(3, Luau::Compile::computeCost(model, args2, 1));
|
|
}
|
|
|
|
TEST_CASE("MultipleAssignments")
|
|
{
|
|
uint64_t model = modelFunction(R"(
|
|
function test(a)
|
|
local x = 0
|
|
x = a
|
|
x = a + 1
|
|
x, x, x = a
|
|
x = a, a, a
|
|
end
|
|
)");
|
|
|
|
const bool args1[] = {false};
|
|
const bool args2[] = {true};
|
|
|
|
CHECK_EQ(8, Luau::Compile::computeCost(model, args1, 1));
|
|
CHECK_EQ(7, Luau::Compile::computeCost(model, args2, 1));
|
|
}
|
|
|
|
TEST_SUITE_END();
|