mirror of
https://github.com/luau-lang/luau.git
synced 2024-12-14 06:00:39 +00:00
2e6fdd90a0
Some checks are pending
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Waiting to run
build / macos (push) Waiting to run
build / macos-arm (push) Waiting to run
build / ubuntu (push) Waiting to run
build / windows (Win32) (push) Waiting to run
build / windows (x64) (push) Waiting to run
build / coverage (push) Waiting to run
build / web (push) Waiting to run
release / macos (push) Waiting to run
release / ubuntu (push) Waiting to run
release / windows (push) Waiting to run
release / web (push) Waiting to run
## New Solver * Type functions should be able to signal whether or not irreducibility is due to an error * Do not generate extra expansion constraint for uninvoked user-defined type functions * Print in a user-defined type function reports as an error instead of logging to stdout * Many e-graphs bugfixes and performance improvements * Many general bugfixes and improvements to the new solver as a whole * Fixed issue with used-defined type functions not being able to call each other * Infer types of globals under new type solver ## Fragment Autocomplete * Miscellaneous fixes to make interop with the old solver better ## Runtime * Support disabling specific built-in functions from being fast-called or constant-evaluated (Closes #1538) * New compiler option `disabledBuiltins` accepts a list of library function names like "tonumber" or "math.cos" * Added constant folding for vector arithmetic * Added constant propagation and type inference for vector globals (Fixes #1511) * New compiler option `librariesWithKnownMembers` accepts a list of libraries for members of which a request for constant value and/or type will be made * `libraryMemberTypeCb` callback is called to get the type of a global, return one of the `LuauBytecodeType` values. 'boolean', 'number', 'string' and 'vector' type are supported. * `libraryMemberConstantCb` callback is called to setup the constant value of a global. To set a value, C API `luau_set_compile_constant_*` or C++ API `setCompileConstant*` functions should be used. --- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Daniel Angel <danielangel@roblox.com> Co-authored-by: Jonathan Kelaty <jkelaty@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Varun Saini <vsaini@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- 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: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@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: Varun Saini <vsaini@roblox.com> Co-authored-by: Andrew Miranti <amiranti@roblox.com> Co-authored-by: Shiqi Ai <sai@roblox.com> Co-authored-by: Yohoo Lin <yohoo@roblox.com> Co-authored-by: Daniel Angel <danielangel@roblox.com> Co-authored-by: Jonathan Kelaty <jkelaty@roblox.com>
424 lines
8.8 KiB
C++
424 lines
8.8 KiB
C++
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||
#pragma once
|
||
|
||
#include "Luau/Id.h"
|
||
#include "Luau/LanguageHash.h"
|
||
#include "Luau/Slice.h"
|
||
#include "Luau/Variant.h"
|
||
|
||
#include <algorithm>
|
||
#include <array>
|
||
#include <type_traits>
|
||
#include <unordered_set>
|
||
#include <utility>
|
||
#include <vector>
|
||
|
||
#define LUAU_EQSAT_UNIT(name) \
|
||
struct name : ::Luau::EqSat::Unit<name> \
|
||
{ \
|
||
static constexpr const char* tag = #name; \
|
||
using Unit::Unit; \
|
||
}
|
||
|
||
#define LUAU_EQSAT_ATOM(name, t) \
|
||
struct name : public ::Luau::EqSat::Atom<name, t> \
|
||
{ \
|
||
static constexpr const char* tag = #name; \
|
||
using Atom::Atom; \
|
||
}
|
||
|
||
#define LUAU_EQSAT_NODE_ARRAY(name, ops) \
|
||
struct name : public ::Luau::EqSat::NodeVector<name, std::array<::Luau::EqSat::Id, ops>> \
|
||
{ \
|
||
static constexpr const char* tag = #name; \
|
||
using NodeVector::NodeVector; \
|
||
}
|
||
|
||
#define LUAU_EQSAT_NODE_VECTOR(name) \
|
||
struct name : public ::Luau::EqSat::NodeVector<name, std::vector<::Luau::EqSat::Id>> \
|
||
{ \
|
||
static constexpr const char* tag = #name; \
|
||
using NodeVector::NodeVector; \
|
||
}
|
||
|
||
#define LUAU_EQSAT_NODE_SET(name) \
|
||
struct name : public ::Luau::EqSat::NodeSet<name, std::vector<::Luau::EqSat::Id>> \
|
||
{ \
|
||
static constexpr const char* tag = #name; \
|
||
using NodeSet::NodeSet; \
|
||
}
|
||
|
||
#define LUAU_EQSAT_NODE_ATOM_WITH_VECTOR(name, t) \
|
||
struct name : public ::Luau::EqSat::NodeAtomAndVector<name, t, std::vector<::Luau::EqSat::Id>> \
|
||
{ \
|
||
static constexpr const char* tag = #name; \
|
||
using NodeAtomAndVector::NodeAtomAndVector; \
|
||
}
|
||
|
||
namespace Luau::EqSat
|
||
{
|
||
|
||
template<typename Phantom>
|
||
struct Unit
|
||
{
|
||
Slice<Id> mutableOperands()
|
||
{
|
||
return {};
|
||
}
|
||
|
||
Slice<const Id> operands() const
|
||
{
|
||
return {};
|
||
}
|
||
|
||
bool operator==(const Unit& rhs) const
|
||
{
|
||
return true;
|
||
}
|
||
|
||
bool operator!=(const Unit& rhs) const
|
||
{
|
||
return false;
|
||
}
|
||
|
||
struct Hash
|
||
{
|
||
size_t operator()(const Unit& value) const
|
||
{
|
||
// chosen by fair dice roll.
|
||
// guaranteed to be random.
|
||
return 4;
|
||
}
|
||
};
|
||
};
|
||
|
||
template<typename Phantom, typename T>
|
||
struct Atom
|
||
{
|
||
Atom(const T& value)
|
||
: _value(value)
|
||
{
|
||
}
|
||
|
||
const T& value() const
|
||
{
|
||
return _value;
|
||
}
|
||
|
||
public:
|
||
Slice<Id> mutableOperands()
|
||
{
|
||
return {};
|
||
}
|
||
|
||
Slice<const Id> operands() const
|
||
{
|
||
return {};
|
||
}
|
||
|
||
bool operator==(const Atom& rhs) const
|
||
{
|
||
return _value == rhs._value;
|
||
}
|
||
|
||
bool operator!=(const Atom& rhs) const
|
||
{
|
||
return !(*this == rhs);
|
||
}
|
||
|
||
struct Hash
|
||
{
|
||
size_t operator()(const Atom& value) const
|
||
{
|
||
return languageHash(value._value);
|
||
}
|
||
};
|
||
|
||
private:
|
||
T _value;
|
||
};
|
||
|
||
template<typename Phantom, typename X, typename T>
|
||
struct NodeAtomAndVector
|
||
{
|
||
template<typename... Args>
|
||
NodeAtomAndVector(const X& value, Args&&... args)
|
||
: _value(value)
|
||
, vector{std::forward<Args>(args)...}
|
||
{
|
||
}
|
||
|
||
Id operator[](size_t i) const
|
||
{
|
||
return vector[i];
|
||
}
|
||
|
||
public:
|
||
const X& value() const
|
||
{
|
||
return _value;
|
||
}
|
||
|
||
Slice<Id> mutableOperands()
|
||
{
|
||
return Slice{vector.data(), vector.size()};
|
||
}
|
||
|
||
Slice<const Id> operands() const
|
||
{
|
||
return Slice{vector.data(), vector.size()};
|
||
}
|
||
|
||
bool operator==(const NodeAtomAndVector& rhs) const
|
||
{
|
||
return _value == rhs._value && vector == rhs.vector;
|
||
}
|
||
|
||
bool operator!=(const NodeAtomAndVector& rhs) const
|
||
{
|
||
return !(*this == rhs);
|
||
}
|
||
|
||
struct Hash
|
||
{
|
||
size_t operator()(const NodeAtomAndVector& value) const
|
||
{
|
||
size_t result = languageHash(value._value);
|
||
hashCombine(result, languageHash(value.vector));
|
||
return result;
|
||
}
|
||
};
|
||
|
||
private:
|
||
X _value;
|
||
T vector;
|
||
};
|
||
|
||
template<typename Phantom, typename T>
|
||
struct NodeVector
|
||
{
|
||
template<typename... Args>
|
||
NodeVector(Args&&... args)
|
||
: vector{std::forward<Args>(args)...}
|
||
{
|
||
}
|
||
|
||
Id operator[](size_t i) const
|
||
{
|
||
return vector[i];
|
||
}
|
||
|
||
public:
|
||
Slice<Id> mutableOperands()
|
||
{
|
||
return Slice{vector.data(), vector.size()};
|
||
}
|
||
|
||
Slice<const Id> operands() const
|
||
{
|
||
return Slice{vector.data(), vector.size()};
|
||
}
|
||
|
||
bool operator==(const NodeVector& rhs) const
|
||
{
|
||
return vector == rhs.vector;
|
||
}
|
||
|
||
bool operator!=(const NodeVector& rhs) const
|
||
{
|
||
return !(*this == rhs);
|
||
}
|
||
|
||
struct Hash
|
||
{
|
||
size_t operator()(const NodeVector& value) const
|
||
{
|
||
return languageHash(value.vector);
|
||
}
|
||
};
|
||
|
||
private:
|
||
T vector;
|
||
};
|
||
|
||
template<typename Phantom, typename T>
|
||
struct NodeSet
|
||
{
|
||
template <typename P_, typename T_, typename Find>
|
||
friend void canonicalize(NodeSet<P_, T_>& node, Find&& find);
|
||
|
||
template<typename... Args>
|
||
NodeSet(Args&&... args)
|
||
: vector{std::forward<Args>(args)...}
|
||
{
|
||
std::sort(begin(vector), end(vector));
|
||
auto it = std::unique(begin(vector), end(vector));
|
||
vector.erase(it, end(vector));
|
||
}
|
||
|
||
Id operator[](size_t i) const
|
||
{
|
||
return vector[i];
|
||
}
|
||
|
||
public:
|
||
Slice<Id> mutableOperands()
|
||
{
|
||
return Slice{vector.data(), vector.size()};
|
||
}
|
||
|
||
Slice<const Id> operands() const
|
||
{
|
||
return Slice{vector.data(), vector.size()};
|
||
}
|
||
|
||
bool operator==(const NodeSet& rhs) const
|
||
{
|
||
return vector == rhs.vector;
|
||
}
|
||
|
||
bool operator!=(const NodeSet& rhs) const
|
||
{
|
||
return !(*this == rhs);
|
||
}
|
||
|
||
struct Hash
|
||
{
|
||
size_t operator()(const NodeSet& value) const
|
||
{
|
||
return languageHash(value.vector);
|
||
}
|
||
};
|
||
|
||
protected:
|
||
T vector;
|
||
};
|
||
|
||
template<typename... Ts>
|
||
struct Language final
|
||
{
|
||
using VariantTy = Luau::Variant<Ts...>;
|
||
|
||
template<typename T>
|
||
using WithinDomain = std::disjunction<std::is_same<std::decay_t<T>, Ts>...>;
|
||
|
||
template <typename Find, typename... Vs>
|
||
friend void canonicalize(Language<Vs...>& enode, Find&& find);
|
||
|
||
template<typename T>
|
||
Language(T&& t, std::enable_if_t<WithinDomain<T>::value>* = 0) noexcept
|
||
: v(std::forward<T>(t))
|
||
{
|
||
}
|
||
|
||
int index() const noexcept
|
||
{
|
||
return v.index();
|
||
}
|
||
|
||
/// This should only be used in canonicalization!
|
||
/// Always prefer operands()
|
||
Slice<Id> mutableOperands() noexcept
|
||
{
|
||
return visit(
|
||
[](auto&& v) -> Slice<Id>
|
||
{
|
||
return v.mutableOperands();
|
||
},
|
||
v
|
||
);
|
||
}
|
||
|
||
Slice<const Id> operands() const noexcept
|
||
{
|
||
return visit(
|
||
[](auto&& v) -> Slice<const Id>
|
||
{
|
||
return v.operands();
|
||
},
|
||
v
|
||
);
|
||
}
|
||
|
||
template<typename T>
|
||
T* get() noexcept
|
||
{
|
||
static_assert(WithinDomain<T>::value);
|
||
return v.template get_if<T>();
|
||
}
|
||
|
||
template<typename T>
|
||
const T* get() const noexcept
|
||
{
|
||
static_assert(WithinDomain<T>::value);
|
||
return v.template get_if<T>();
|
||
}
|
||
|
||
bool operator==(const Language& rhs) const noexcept
|
||
{
|
||
return v == rhs.v;
|
||
}
|
||
|
||
bool operator!=(const Language& rhs) const noexcept
|
||
{
|
||
return !(*this == rhs);
|
||
}
|
||
|
||
public:
|
||
struct Hash
|
||
{
|
||
size_t operator()(const Language& language) const
|
||
{
|
||
size_t seed = std::hash<int>{}(language.index());
|
||
hashCombine(
|
||
seed,
|
||
visit(
|
||
[](auto&& v)
|
||
{
|
||
return typename std::decay_t<decltype(v)>::Hash{}(v);
|
||
},
|
||
language.v
|
||
)
|
||
);
|
||
return seed;
|
||
}
|
||
};
|
||
|
||
private:
|
||
VariantTy v;
|
||
};
|
||
|
||
template <typename Node, typename Find>
|
||
void canonicalize(Node& node, Find&& find)
|
||
{
|
||
// An e-node 𝑛 is canonical iff 𝑛 = canonicalize(𝑛), where
|
||
// canonicalize(𝑓(𝑎1, 𝑎2, ...)) = 𝑓(find(𝑎1), find(𝑎2), ...).
|
||
for (Id& id : node.mutableOperands())
|
||
id = find(id);
|
||
}
|
||
|
||
// Canonicalizing the Ids in a NodeSet may result in the set decreasing in size.
|
||
template <typename Phantom, typename T, typename Find>
|
||
void canonicalize(NodeSet<Phantom, T>& node, Find&& find)
|
||
{
|
||
for (Id& id : node.vector)
|
||
id = find(id);
|
||
|
||
std::sort(begin(node.vector), end(node.vector));
|
||
auto endIt = std::unique(begin(node.vector), end(node.vector));
|
||
node.vector.erase(endIt, end(node.vector));
|
||
}
|
||
|
||
template <typename Find, typename... Vs>
|
||
void canonicalize(Language<Vs...>& enode, Find&& find)
|
||
{
|
||
visit(
|
||
[&](auto&& v)
|
||
{
|
||
Luau::EqSat::canonicalize(v, find);
|
||
},
|
||
enode.v
|
||
);
|
||
}
|
||
|
||
} // namespace Luau::EqSat
|