From caee04d82d014ed104dd63edec1710fb6ab5794c Mon Sep 17 00:00:00 2001 From: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com> Date: Thu, 20 Jun 2024 16:37:55 -0700 Subject: [PATCH] Sync to upstream/release/631 (#1299) ### What's new * Added lint warning for using redundant `@native` attributes on functions inside a `--!native` module * Improved typechecking speed in old solver for modules with large types ### New Solver * Fixed the length type function sealing the table prematurely * Fixed crashes caused by general table indexing expressions ### VM * Added support for a specialized 3-argument fast-call instruction to improve performance of `vector` constructor, `buffer` writes and a few `bit32` methods --- ### Internal Contributors Co-authored-by: Aaron Weiss Co-authored-by: Andy Friesen Co-authored-by: Aviral Goel Co-authored-by: Vighnesh Vijay Co-authored-by: Vyacheslav Egorov --------- Co-authored-by: Aaron Weiss Co-authored-by: Alexander McCord Co-authored-by: Andy Friesen Co-authored-by: Vighnesh Co-authored-by: Aviral Goel Co-authored-by: David Cope Co-authored-by: Lily Brown --- Analysis/include/Luau/ConstraintSolver.h | 2 +- Analysis/include/Luau/Generalization.h | 4 +- Analysis/include/Luau/Instantiation.h | 10 + Analysis/include/Luau/Substitution.h | 5 +- Analysis/include/Luau/TypeFamily.h | 4 +- Analysis/include/Luau/TypeInfer.h | 3 + Analysis/src/BuiltinDefinitions.cpp | 49 +++-- Analysis/src/ConstraintGenerator.cpp | 6 +- Analysis/src/ConstraintSolver.cpp | 39 ++-- Analysis/src/Error.cpp | 16 +- Analysis/src/Generalization.cpp | 75 +++---- Analysis/src/Instantiation.cpp | 54 ++++- Analysis/src/Linter.cpp | 70 +++++++ Analysis/src/Substitution.cpp | 48 ++++- Analysis/src/TypeChecker2.cpp | 28 ++- Analysis/src/TypeFamily.cpp | 100 +++++----- Analysis/src/TypeInfer.cpp | 30 ++- Analysis/src/Unifier.cpp | 3 +- Analysis/src/Unifier2.cpp | 48 ++--- CodeGen/include/Luau/IrData.h | 14 +- CodeGen/include/Luau/IrUtils.h | 1 + CodeGen/include/Luau/IrVisitUseDef.h | 68 +++++-- CodeGen/src/BytecodeAnalysis.cpp | 213 +++++++++----------- CodeGen/src/CodeGenAssembly.cpp | 5 +- CodeGen/src/CodeGenLower.h | 4 +- CodeGen/src/EmitBuiltinsX64.cpp | 30 +-- CodeGen/src/EmitBuiltinsX64.h | 2 +- CodeGen/src/IrBuilder.cpp | 114 ++--------- CodeGen/src/IrLoweringA64.cpp | 213 ++++++++++---------- CodeGen/src/IrLoweringX64.cpp | 48 +++-- CodeGen/src/IrTranslateBuiltins.cpp | 157 ++++++++------- CodeGen/src/IrTranslateBuiltins.h | 3 +- CodeGen/src/IrTranslation.cpp | 37 +++- CodeGen/src/IrTranslation.h | 3 +- CodeGen/src/IrValueLocationTracking.cpp | 8 +- CodeGen/src/OptimizeConstProp.cpp | 59 +++--- CodeGen/src/OptimizeDeadStore.cpp | 1 - Common/include/Luau/Bytecode.h | 12 +- Common/include/Luau/BytecodeUtils.h | 1 + Common/include/Luau/DenseHash.h | 8 +- Compiler/src/BytecodeBuilder.cpp | 22 +++ Compiler/src/Compiler.cpp | 58 +++++- Config/include/Luau/LinterConfig.h | 2 + VM/src/ldebug.cpp | 8 +- VM/src/lfunc.cpp | 18 +- VM/src/lgc.cpp | 14 +- VM/src/lmem.cpp | 48 ++--- VM/src/ltablib.cpp | 54 ++--- VM/src/lvmexecute.cpp | 62 +++++- VM/src/lvmload.cpp | 106 ++++------ tests/Compiler.test.cpp | 56 ++++++ tests/Error.test.cpp | 6 +- tests/Fixture.h | 18 ++ tests/Generalization.test.cpp | 23 +-- tests/IrBuilder.test.cpp | 104 ++++------ tests/IrLowering.test.cpp | 238 ++++++++--------------- tests/Linter.test.cpp | 31 +++ tests/TypeInfer.functions.test.cpp | 13 +- tests/TypeInfer.provisional.test.cpp | 41 ++++ tests/TypeInfer.tables.test.cpp | 50 +++-- tests/TypeInfer.test.cpp | 2 +- tests/conformance/bitwise.lua | 1 + tests/conformance/math.lua | 23 +++ 63 files changed, 1433 insertions(+), 1160 deletions(-) diff --git a/Analysis/include/Luau/ConstraintSolver.h b/Analysis/include/Luau/ConstraintSolver.h index 6e62a2e3..925be04e 100644 --- a/Analysis/include/Luau/ConstraintSolver.h +++ b/Analysis/include/Luau/ConstraintSolver.h @@ -300,7 +300,7 @@ public: * @returns a non-free type that generalizes the argument, or `std::nullopt` if one * does not exist */ - std::optional generalizeFreeType(NotNull scope, TypeId type); + std::optional generalizeFreeType(NotNull scope, TypeId type, bool avoidSealingTables = false); /** * Checks the existing set of constraints to see if there exist any that contain diff --git a/Analysis/include/Luau/Generalization.h b/Analysis/include/Luau/Generalization.h index 44d0db67..04ac2df1 100644 --- a/Analysis/include/Luau/Generalization.h +++ b/Analysis/include/Luau/Generalization.h @@ -8,6 +8,6 @@ namespace Luau { -std::optional generalize(NotNull arena, NotNull builtinTypes, NotNull scope, NotNull> bakedTypes, TypeId ty); - +std::optional generalize(NotNull arena, NotNull builtinTypes, NotNull scope, + NotNull> bakedTypes, TypeId ty, /* avoid sealing tables*/ bool avoidSealingTables = false); } diff --git a/Analysis/include/Luau/Instantiation.h b/Analysis/include/Luau/Instantiation.h index 2122f0fa..58ba88ab 100644 --- a/Analysis/include/Luau/Instantiation.h +++ b/Analysis/include/Luau/Instantiation.h @@ -27,12 +27,16 @@ struct ReplaceGenerics : Substitution { } + void resetState(const TxnLog* log, TypeArena* arena, NotNull builtinTypes, TypeLevel level, Scope* scope, + const std::vector& generics, const std::vector& genericPacks); + NotNull builtinTypes; TypeLevel level; Scope* scope; std::vector generics; std::vector genericPacks; + bool ignoreChildren(TypeId ty) override; bool isDirty(TypeId ty) override; bool isDirty(TypePackId tp) override; @@ -48,13 +52,19 @@ struct Instantiation : Substitution , builtinTypes(builtinTypes) , level(level) , scope(scope) + , reusableReplaceGenerics(log, arena, builtinTypes, level, scope, {}, {}) { } + void resetState(const TxnLog* log, TypeArena* arena, NotNull builtinTypes, TypeLevel level, Scope* scope); + NotNull builtinTypes; TypeLevel level; Scope* scope; + + ReplaceGenerics reusableReplaceGenerics; + bool ignoreChildren(TypeId ty) override; bool isDirty(TypeId ty) override; bool isDirty(TypePackId tp) override; diff --git a/Analysis/include/Luau/Substitution.h b/Analysis/include/Luau/Substitution.h index 16e36e09..28ebc93d 100644 --- a/Analysis/include/Luau/Substitution.h +++ b/Analysis/include/Luau/Substitution.h @@ -134,7 +134,8 @@ struct Tarjan TarjanResult visitRoot(TypeId ty); TarjanResult visitRoot(TypePackId ty); - void clearTarjan(); + // Used to reuse the object for a new operation + void clearTarjan(const TxnLog* log); // Get/set the dirty bit for an index (grows the vector if needed) bool getDirty(int index); @@ -212,6 +213,8 @@ public: std::optional substitute(TypeId ty); std::optional substitute(TypePackId tp); + void resetState(const TxnLog* log, TypeArena* arena); + TypeId replace(TypeId ty); TypePackId replace(TypePackId tp); diff --git a/Analysis/include/Luau/TypeFamily.h b/Analysis/include/Luau/TypeFamily.h index fa23a6ba..7c68d815 100644 --- a/Analysis/include/Luau/TypeFamily.h +++ b/Analysis/include/Luau/TypeFamily.h @@ -82,8 +82,8 @@ struct TypeFamilyReductionResult }; template -using ReducerFunction = std::function(T, const std::vector&, const std::vector&, - NotNull)>; +using ReducerFunction = + std::function(T, const std::vector&, const std::vector&, NotNull)>; /// Represents a type function that may be applied to map a series of types and /// type packs to a single output type. diff --git a/Analysis/include/Luau/TypeInfer.h b/Analysis/include/Luau/TypeInfer.h index 26a67c7a..340c1e72 100644 --- a/Analysis/include/Luau/TypeInfer.h +++ b/Analysis/include/Luau/TypeInfer.h @@ -4,6 +4,7 @@ #include "Luau/Anyification.h" #include "Luau/ControlFlow.h" #include "Luau/Error.h" +#include "Luau/Instantiation.h" #include "Luau/Module.h" #include "Luau/Predicate.h" #include "Luau/Substitution.h" @@ -362,6 +363,8 @@ public: UnifierSharedState unifierState; Normalizer normalizer; + Instantiation reusableInstantiation; + std::vector requireCycles; // Type inference limits diff --git a/Analysis/src/BuiltinDefinitions.cpp b/Analysis/src/BuiltinDefinitions.cpp index a9c519fe..2393bd2a 100644 --- a/Analysis/src/BuiltinDefinitions.cpp +++ b/Analysis/src/BuiltinDefinitions.cpp @@ -256,21 +256,44 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC TypeId tableMetaMT = arena.addType(MetatableType{tabTy, genericMT}); + // getmetatable : ({ @metatable MT, {+ +} }) -> MT addGlobalBinding(globals, "getmetatable", makeFunction(arena, std::nullopt, {genericMT}, {}, {tableMetaMT}, {genericMT}), "@luau"); - // clang-format off - // setmetatable(T, MT) -> { @metatable MT, T } - addGlobalBinding(globals, "setmetatable", - arena.addType( - FunctionType{ - {genericMT}, - {}, - arena.addTypePack(TypePack{{tabTy, genericMT}}), - arena.addTypePack(TypePack{{tableMetaMT}}) - } - ), "@luau" - ); - // clang-format on + if (FFlag::DebugLuauDeferredConstraintResolution) + { + TypeId genericT = arena.addType(GenericType{"T"}); + TypeId tMetaMT = arena.addType(MetatableType{genericT, genericMT}); + + // clang-format off + // setmetatable(T, MT) -> { @metatable MT, T } + addGlobalBinding(globals, "setmetatable", + arena.addType( + FunctionType{ + {genericT, genericMT}, + {}, + arena.addTypePack(TypePack{{genericT, genericMT}}), + arena.addTypePack(TypePack{{tMetaMT}}) + } + ), "@luau" + ); + // clang-format on + } + else + { + // clang-format off + // setmetatable(T, MT) -> { @metatable MT, T } + addGlobalBinding(globals, "setmetatable", + arena.addType( + FunctionType{ + {genericMT}, + {}, + arena.addTypePack(TypePack{{tabTy, genericMT}}), + arena.addTypePack(TypePack{{tableMetaMT}}) + } + ), "@luau" + ); + // clang-format on + } for (const auto& pair : globals.globalScope->bindings) { diff --git a/Analysis/src/ConstraintGenerator.cpp b/Analysis/src/ConstraintGenerator.cpp index b784f4aa..7d92d9ff 100644 --- a/Analysis/src/ConstraintGenerator.cpp +++ b/Analysis/src/ConstraintGenerator.cpp @@ -787,7 +787,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocal* stat auto uc = addConstraint(scope, statLocal->location, UnpackConstraint{valueTypes, rvaluePack}); - for (TypeId t: valueTypes) + for (TypeId t : valueTypes) getMutable(t)->setOwner(uc); } @@ -920,7 +920,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatForIn* forI auto iterable = addConstraint( loopScope, getLocation(forIn->values), IterableConstraint{iterator, variableTypes, forIn->values.data[0], &module->astForInNextTypes}); - for (TypeId var: variableTypes) + for (TypeId var : variableTypes) { auto bt = getMutable(var); LUAU_ASSERT(bt); @@ -1171,7 +1171,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatAssign* ass auto uc = addConstraint(scope, assign->location, UnpackConstraint{valueTypes, resultPack}); - for (TypeId t: valueTypes) + for (TypeId t : valueTypes) getMutable(t)->setOwner(uc); } diff --git a/Analysis/src/ConstraintSolver.cpp b/Analysis/src/ConstraintSolver.cpp index e59bc8a7..8756ec44 100644 --- a/Analysis/src/ConstraintSolver.cpp +++ b/Analysis/src/ConstraintSolver.cpp @@ -694,7 +694,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNullscope, generalizedTypes, ty); + generalize(NotNull{arena}, builtinTypes, constraint->scope, generalizedTypes, ty, /* avoidSealingTables */ false); return true; } @@ -769,13 +769,8 @@ bool ConstraintSolver::tryDispatch(const IterableConstraint& c, NotNullscope); TypeId valueTy = freshType(arena, builtinTypes, constraint->scope); - TypeId tableTy = arena->addType(TableType{ - TableType::Props{}, - TableIndexer{keyTy, valueTy}, - TypeLevel{}, - constraint->scope, - TableState::Free - }); + TypeId tableTy = + arena->addType(TableType{TableType::Props{}, TableIndexer{keyTy, valueTy}, TypeLevel{}, constraint->scope, TableState::Free}); unify(constraint, nextTy, tableTy); @@ -1022,13 +1017,10 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul const TableType* tfTable = getTableType(tf->type); //clang-format off - bool needsClone = - follow(tf->type) == target || - (tfTable != nullptr && tfTable == getTableType(target)) || - std::any_of(typeArguments.begin(), typeArguments.end(), [&](const auto& other) { - return other == target; - } - ); + bool needsClone = follow(tf->type) == target || (tfTable != nullptr && tfTable == getTableType(target)) || + std::any_of(typeArguments.begin(), typeArguments.end(), [&](const auto& other) { + return other == target; + }); //clang-format on // Only tables have the properties we're trying to set. @@ -1446,6 +1438,8 @@ bool ConstraintSolver::tryDispatchHasIndexer( bind(constraint, resultType, tbl->indexer->indexResultType); return true; } + else if (auto mt = get(follow(ft->upperBound))) + return tryDispatchHasIndexer(recursionDepth, constraint, mt->table, indexType, resultType, seen); FreeType freeResult{ft->scope, builtinTypes->neverType, builtinTypes->unknownType}; emplace(constraint, resultType, freeResult); @@ -1461,11 +1455,11 @@ bool ConstraintSolver::tryDispatchHasIndexer( if (auto indexer = tt->indexer) { unify(constraint, indexType, indexer->indexType); - bind(constraint, resultType, indexer->indexResultType); return true; } - else if (tt->state == TableState::Unsealed) + + if (tt->state == TableState::Unsealed) { // FIXME this is greedy. @@ -2067,7 +2061,7 @@ bool ConstraintSolver::tryDispatchIterableTable(TypeId iteratorTy, const Iterabl } auto unpack = [&](TypeId ty) { - for (TypeId varTy: c.variables) + for (TypeId varTy : c.variables) { LUAU_ASSERT(get(varTy)); LUAU_ASSERT(varTy != ty); @@ -2228,11 +2222,12 @@ bool ConstraintSolver::tryDispatchIterableFunction( return true; } -NotNull ConstraintSolver::unpackAndAssign(const std::vector destTypes, TypePackId srcTypes, NotNull constraint) +NotNull ConstraintSolver::unpackAndAssign( + const std::vector destTypes, TypePackId srcTypes, NotNull constraint) { auto c = pushConstraint(constraint->scope, constraint->location, UnpackConstraint{destTypes, srcTypes}); - for (TypeId t: destTypes) + for (TypeId t : destTypes) { BlockedType* bt = getMutable(t); LUAU_ASSERT(bt); @@ -2834,7 +2829,7 @@ void ConstraintSolver::shiftReferences(TypeId source, TypeId target) targetRefs += count; } -std::optional ConstraintSolver::generalizeFreeType(NotNull scope, TypeId type) +std::optional ConstraintSolver::generalizeFreeType(NotNull scope, TypeId type, bool avoidSealingTables) { TypeId t = follow(type); if (get(t)) @@ -2849,7 +2844,7 @@ std::optional ConstraintSolver::generalizeFreeType(NotNull scope, // that until all constraint generation is complete. } - return generalize(NotNull{arena}, builtinTypes, scope, generalizedTypes, type); + return generalize(NotNull{arena}, builtinTypes, scope, generalizedTypes, type, avoidSealingTables); } bool ConstraintSolver::hasUnresolvedConstraints(TypeId ty) diff --git a/Analysis/src/Error.cpp b/Analysis/src/Error.cpp index d356b1cc..cb8ef20d 100644 --- a/Analysis/src/Error.cpp +++ b/Analysis/src/Error.cpp @@ -65,21 +65,15 @@ namespace Luau { // this list of binary operator type families is used for better stringification of type families errors -static const std::unordered_map kBinaryOps{ - {"add", "+"}, {"sub", "-"}, {"mul", "*"}, {"div", "/"}, {"idiv", "//"}, {"pow", "^"}, {"mod", "%"}, {"concat", ".."}, {"and", "and"}, - {"or", "or"}, {"lt", "< or >="}, {"le", "<= or >"}, {"eq", "== or ~="} -}; +static const std::unordered_map kBinaryOps{{"add", "+"}, {"sub", "-"}, {"mul", "*"}, {"div", "/"}, {"idiv", "//"}, + {"pow", "^"}, {"mod", "%"}, {"concat", ".."}, {"and", "and"}, {"or", "or"}, {"lt", "< or >="}, {"le", "<= or >"}, {"eq", "== or ~="}}; // this list of unary operator type families is used for better stringification of type families errors -static const std::unordered_map kUnaryOps{ - {"unm", "-"}, {"len", "#"}, {"not", "not"} -}; +static const std::unordered_map kUnaryOps{{"unm", "-"}, {"len", "#"}, {"not", "not"}}; // this list of type families will receive a special error indicating that the user should file a bug on the GitHub repository // putting a type family in this list indicates that it is expected to _always_ reduce -static const std::unordered_set kUnreachableTypeFamilies{ - "refine", "singleton", "union", "intersect" -}; +static const std::unordered_set kUnreachableTypeFamilies{"refine", "singleton", "union", "intersect"}; struct ErrorConverter { @@ -682,7 +676,7 @@ struct ErrorConverter if (kUnreachableTypeFamilies.count(tfit->family->name)) { return "Type family instance " + Luau::toString(e.ty) + " is uninhabited\n" + - "This is likely to be a bug, please report it at https://github.com/luau-lang/luau/issues"; + "This is likely to be a bug, please report it at https://github.com/luau-lang/luau/issues"; } // Everything should be specialized above to report a more descriptive error that hopefully does not mention "type families" explicitly. diff --git a/Analysis/src/Generalization.cpp b/Analysis/src/Generalization.cpp index c2c44d96..5020ea58 100644 --- a/Analysis/src/Generalization.cpp +++ b/Analysis/src/Generalization.cpp @@ -24,15 +24,17 @@ struct MutatingGeneralizer : TypeOnceVisitor std::vector genericPacks; bool isWithinFunction = false; + bool avoidSealingTables = false; - MutatingGeneralizer(NotNull builtinTypes, NotNull scope, NotNull> cachedTypes, DenseHashMap positiveTypes, - DenseHashMap negativeTypes) + MutatingGeneralizer(NotNull builtinTypes, NotNull scope, NotNull> cachedTypes, + DenseHashMap positiveTypes, DenseHashMap negativeTypes, bool avoidSealingTables) : TypeOnceVisitor(/* skipBoundTypes */ true) , builtinTypes(builtinTypes) , scope(scope) , cachedTypes(cachedTypes) , positiveTypes(std::move(positiveTypes)) , negativeTypes(std::move(negativeTypes)) + , avoidSealingTables(avoidSealingTables) { } @@ -268,7 +270,8 @@ struct MutatingGeneralizer : TypeOnceVisitor TableType* tt = getMutable(ty); LUAU_ASSERT(tt); - tt->state = TableState::Sealed; + if (!avoidSealingTables) + tt->state = TableState::Sealed; return true; } @@ -338,31 +341,31 @@ struct FreeTypeSearcher : TypeVisitor { switch (polarity) { - case Positive: - { - if (seenPositive.contains(ty)) - return true; + case Positive: + { + if (seenPositive.contains(ty)) + return true; - seenPositive.insert(ty); - return false; - } - case Negative: - { - if (seenNegative.contains(ty)) - return true; + seenPositive.insert(ty); + return false; + } + case Negative: + { + if (seenNegative.contains(ty)) + return true; - seenNegative.insert(ty); - return false; - } - case Both: - { - if (seenPositive.contains(ty) && seenNegative.contains(ty)) - return true; + seenNegative.insert(ty); + return false; + } + case Both: + { + if (seenPositive.contains(ty) && seenNegative.contains(ty)) + return true; - seenPositive.insert(ty); - seenNegative.insert(ty); - return false; - } + seenPositive.insert(ty); + seenNegative.insert(ty); + return false; + } } return false; @@ -519,7 +522,8 @@ struct TypeCacher : TypeOnceVisitor explicit TypeCacher(NotNull> cachedTypes) : TypeOnceVisitor(/* skipBoundTypes */ true) , cachedTypes(cachedTypes) - {} + { + } void cache(TypeId ty) { @@ -611,7 +615,7 @@ struct TypeCacher : TypeOnceVisitor traverse(ft.argTypes); traverse(ft.retTypes); - for (TypeId gen: ft.generics) + for (TypeId gen : ft.generics) traverse(gen); bool uncacheable = false; @@ -622,7 +626,7 @@ struct TypeCacher : TypeOnceVisitor else if (isUncacheable(ft.retTypes)) uncacheable = true; - for (TypeId argTy: ft.argTypes) + for (TypeId argTy : ft.argTypes) { if (isUncacheable(argTy)) { @@ -631,7 +635,7 @@ struct TypeCacher : TypeOnceVisitor } } - for (TypeId retTy: ft.retTypes) + for (TypeId retTy : ft.retTypes) { if (isUncacheable(retTy)) { @@ -640,7 +644,7 @@ struct TypeCacher : TypeOnceVisitor } } - for (TypeId g: ft.generics) + for (TypeId g : ft.generics) { if (isUncacheable(g)) { @@ -863,7 +867,8 @@ struct TypeCacher : TypeOnceVisitor } }; -std::optional generalize(NotNull arena, NotNull builtinTypes, NotNull scope, NotNull> cachedTypes, TypeId ty) +std::optional generalize(NotNull arena, NotNull builtinTypes, NotNull scope, + NotNull> cachedTypes, TypeId ty, bool avoidSealingTables) { ty = follow(ty); @@ -876,14 +881,14 @@ std::optional generalize(NotNull arena, NotNull FreeTypeSearcher fts{scope, cachedTypes}; fts.traverse(ty); - MutatingGeneralizer gen{builtinTypes, scope, cachedTypes, std::move(fts.positiveTypes), std::move(fts.negativeTypes)}; + MutatingGeneralizer gen{builtinTypes, scope, cachedTypes, std::move(fts.positiveTypes), std::move(fts.negativeTypes), avoidSealingTables}; gen.traverse(ty); /* MutatingGeneralizer mutates types in place, so it is possible that ty has - * been transmuted to a BoundType. We must follow it again and verify that - * we are allowed to mutate it before we attach generics to it. - */ + * been transmuted to a BoundType. We must follow it again and verify that + * we are allowed to mutate it before we attach generics to it. + */ ty = follow(ty); if (ty->owningArena != arena || ty->persistent) diff --git a/Analysis/src/Instantiation.cpp b/Analysis/src/Instantiation.cpp index 525319c6..811aa048 100644 --- a/Analysis/src/Instantiation.cpp +++ b/Analysis/src/Instantiation.cpp @@ -11,10 +11,23 @@ #include LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauReusableSubstitutions) namespace Luau { +void Instantiation::resetState(const TxnLog* log, TypeArena* arena, NotNull builtinTypes, TypeLevel level, Scope* scope) +{ + LUAU_ASSERT(FFlag::LuauReusableSubstitutions); + + Substitution::resetState(log, arena); + + this->builtinTypes = builtinTypes; + + this->level = level; + this->scope = scope; +} + bool Instantiation::isDirty(TypeId ty) { if (const FunctionType* ftv = log->getMutable(ty)) @@ -58,13 +71,26 @@ TypeId Instantiation::clean(TypeId ty) clone.argNames = ftv->argNames; TypeId result = addType(std::move(clone)); - // Annoyingly, we have to do this even if there are no generics, - // to replace any generic tables. - ReplaceGenerics replaceGenerics{log, arena, builtinTypes, level, scope, ftv->generics, ftv->genericPacks}; + if (FFlag::LuauReusableSubstitutions) + { + // Annoyingly, we have to do this even if there are no generics, + // to replace any generic tables. + reusableReplaceGenerics.resetState(log, arena, builtinTypes, level, scope, ftv->generics, ftv->genericPacks); - // TODO: What to do if this returns nullopt? - // We don't have access to the error-reporting machinery - result = replaceGenerics.substitute(result).value_or(result); + // TODO: What to do if this returns nullopt? + // We don't have access to the error-reporting machinery + result = reusableReplaceGenerics.substitute(result).value_or(result); + } + else + { + // Annoyingly, we have to do this even if there are no generics, + // to replace any generic tables. + ReplaceGenerics replaceGenerics{log, arena, builtinTypes, level, scope, ftv->generics, ftv->genericPacks}; + + // TODO: What to do if this returns nullopt? + // We don't have access to the error-reporting machinery + result = replaceGenerics.substitute(result).value_or(result); + } asMutable(result)->documentationSymbol = ty->documentationSymbol; return result; @@ -76,6 +102,22 @@ TypePackId Instantiation::clean(TypePackId tp) return tp; } +void ReplaceGenerics::resetState(const TxnLog* log, TypeArena* arena, NotNull builtinTypes, TypeLevel level, Scope* scope, + const std::vector& generics, const std::vector& genericPacks) +{ + LUAU_ASSERT(FFlag::LuauReusableSubstitutions); + + Substitution::resetState(log, arena); + + this->builtinTypes = builtinTypes; + + this->level = level; + this->scope = scope; + + this->generics = generics; + this->genericPacks = genericPacks; +} + bool ReplaceGenerics::ignoreChildren(TypeId ty) { if (const FunctionType* ftv = log->getMutable(ty)) diff --git a/Analysis/src/Linter.cpp b/Analysis/src/Linter.cpp index d79361c0..e9d4ca53 100644 --- a/Analysis/src/Linter.cpp +++ b/Analysis/src/Linter.cpp @@ -16,6 +16,11 @@ LUAU_FASTINTVARIABLE(LuauSuggestionDistance, 4) LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauAttributeSyntax) +LUAU_FASTFLAG(LuauAttribute) +LUAU_FASTFLAG(LuauNativeAttribute) +LUAU_FASTFLAGVARIABLE(LintRedundantNativeAttribute, false) + namespace Luau { @@ -2922,6 +2927,64 @@ static void lintComments(LintContext& context, const std::vector& ho } } +static bool hasNativeCommentDirective(const std::vector& hotcomments) +{ + LUAU_ASSERT(FFlag::LuauAttributeSyntax); + LUAU_ASSERT(FFlag::LuauNativeAttribute); + LUAU_ASSERT(FFlag::LintRedundantNativeAttribute); + + for (const HotComment& hc : hotcomments) + { + if (hc.content.empty() || hc.content[0] == ' ' || hc.content[0] == '\t') + continue; + + if (hc.header) + { + size_t space = hc.content.find_first_of(" \t"); + std::string_view first = std::string_view(hc.content).substr(0, space); + + if (first == "native") + return true; + } + } + + return false; +} + +struct LintRedundantNativeAttribute : AstVisitor +{ +public: + LUAU_NOINLINE static void process(LintContext& context) + { + LUAU_ASSERT(FFlag::LuauAttributeSyntax); + LUAU_ASSERT(FFlag::LuauNativeAttribute); + LUAU_ASSERT(FFlag::LintRedundantNativeAttribute); + + LintRedundantNativeAttribute pass; + pass.context = &context; + context.root->visit(&pass); + } + +private: + LintContext* context; + + bool visit(AstExprFunction* node) override + { + node->body->visit(this); + + for (const auto attribute : node->attributes) + { + if (attribute->type == AstAttr::Type::Native) + { + emitWarning(*context, LintWarning::Code_RedundantNativeAttribute, attribute->location, + "native attribute on a function is redundant in a native module; consider removing it"); + } + } + + return false; + } +}; + std::vector lint(AstStat* root, const AstNameTable& names, const ScopePtr& env, const Module* module, const std::vector& hotcomments, const LintOptions& options) { @@ -3008,6 +3071,13 @@ std::vector lint(AstStat* root, const AstNameTable& names, const Sc if (context.warningEnabled(LintWarning::Code_ComparisonPrecedence)) LintComparisonPrecedence::process(context); + if (FFlag::LuauAttributeSyntax && FFlag::LuauNativeAttribute && FFlag::LintRedundantNativeAttribute && + context.warningEnabled(LintWarning::Code_RedundantNativeAttribute)) + { + if (hasNativeCommentDirective(hotcomments)) + LintRedundantNativeAttribute::process(context); + } + std::sort(context.result.begin(), context.result.end(), WarningComparator()); return context.result; diff --git a/Analysis/src/Substitution.cpp b/Analysis/src/Substitution.cpp index ea9c3178..4e5dae07 100644 --- a/Analysis/src/Substitution.cpp +++ b/Analysis/src/Substitution.cpp @@ -11,6 +11,7 @@ LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000) LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256); +LUAU_FASTFLAG(LuauReusableSubstitutions) namespace Luau { @@ -146,6 +147,8 @@ static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log, bool a } Tarjan::Tarjan() + : typeToIndex(nullptr, FFlag::LuauReusableSubstitutions ? FInt::LuauTarjanPreallocationSize : 0) + , packToIndex(nullptr, FFlag::LuauReusableSubstitutions ? FInt::LuauTarjanPreallocationSize : 0) { nodes.reserve(FInt::LuauTarjanPreallocationSize); stack.reserve(FInt::LuauTarjanPreallocationSize); @@ -446,14 +449,31 @@ TarjanResult Tarjan::visitRoot(TypePackId tp) return loop(); } -void Tarjan::clearTarjan() +void Tarjan::clearTarjan(const TxnLog* log) { - typeToIndex.clear(); - packToIndex.clear(); + if (FFlag::LuauReusableSubstitutions) + { + typeToIndex.clear(~0u); + packToIndex.clear(~0u); + } + else + { + typeToIndex.clear(); + packToIndex.clear(); + } + nodes.clear(); stack.clear(); + if (FFlag::LuauReusableSubstitutions) + { + childCount = 0; + // childLimit setting stays the same + + this->log = log; + } + edgesTy.clear(); edgesTp.clear(); worklist.clear(); @@ -528,7 +548,6 @@ Substitution::Substitution(const TxnLog* log_, TypeArena* arena) { log = log_; LUAU_ASSERT(log); - LUAU_ASSERT(arena); } void Substitution::dontTraverseInto(TypeId ty) @@ -546,7 +565,7 @@ std::optional Substitution::substitute(TypeId ty) ty = log->follow(ty); // clear algorithm state for reentrancy - clearTarjan(); + clearTarjan(log); auto result = findDirty(ty); if (result != TarjanResult::Ok) @@ -579,7 +598,7 @@ std::optional Substitution::substitute(TypePackId tp) tp = log->follow(tp); // clear algorithm state for reentrancy - clearTarjan(); + clearTarjan(log); auto result = findDirty(tp); if (result != TarjanResult::Ok) @@ -607,6 +626,23 @@ std::optional Substitution::substitute(TypePackId tp) return newTp; } +void Substitution::resetState(const TxnLog* log, TypeArena* arena) +{ + LUAU_ASSERT(FFlag::LuauReusableSubstitutions); + + clearTarjan(log); + + this->arena = arena; + + newTypes.clear(); + newPacks.clear(); + replacedTypes.clear(); + replacedTypePacks.clear(); + + noTraverseTypes.clear(); + noTraverseTypePacks.clear(); +} + TypeId Substitution::clone(TypeId ty) { return shallowClone(ty, *arena, log, /* alwaysClone */ true); diff --git a/Analysis/src/TypeChecker2.cpp b/Analysis/src/TypeChecker2.cpp index cc02bea6..fe0bf2dd 100644 --- a/Analysis/src/TypeChecker2.cpp +++ b/Analysis/src/TypeChecker2.cpp @@ -1540,6 +1540,24 @@ struct TypeChecker2 visitExprName(indexName->expr, indexName->location, indexName->index.value, context, builtinTypes->stringType); } + void indexExprMetatableHelper(AstExprIndexExpr* indexExpr, const MetatableType* metaTable, TypeId exprType, TypeId indexType) + { + if (auto tt = get(follow(metaTable->table)); tt && tt->indexer) + testIsSubtype(indexType, tt->indexer->indexType, indexExpr->index->location); + else if (auto mt = get(follow(metaTable->table))) + indexExprMetatableHelper(indexExpr, mt, exprType, indexType); + else if (auto tmt = get(follow(metaTable->metatable)); tmt && tmt->indexer) + testIsSubtype(indexType, tmt->indexer->indexType, indexExpr->index->location); + else if (auto mtmt = get(follow(metaTable->metatable))) + indexExprMetatableHelper(indexExpr, mtmt, exprType, indexType); + else + { + LUAU_ASSERT(tt || get(follow(metaTable->table))); + + reportError(CannotExtendTable{exprType, CannotExtendTable::Indexer, "indexer??"}, indexExpr->location); + } + } + void visit(AstExprIndexExpr* indexExpr, ValueContext context) { if (auto str = indexExpr->index->as()) @@ -1565,15 +1583,7 @@ struct TypeChecker2 } else if (auto mt = get(exprType)) { - const TableType* tt = get(follow(mt->table)); - LUAU_ASSERT(tt); - if (tt->indexer) - testIsSubtype(indexType, tt->indexer->indexType, indexExpr->index->location); - else - { - // TODO: Maybe the metatable has a suitable indexer? - reportError(CannotExtendTable{exprType, CannotExtendTable::Indexer, "indexer??"}, indexExpr->location); - } + return indexExprMetatableHelper(indexExpr, mt, exprType, indexType); } else if (auto cls = get(exprType)) { diff --git a/Analysis/src/TypeFamily.cpp b/Analysis/src/TypeFamily.cpp index c65fde00..54d89a15 100644 --- a/Analysis/src/TypeFamily.cpp +++ b/Analysis/src/TypeFamily.cpp @@ -344,8 +344,7 @@ struct FamilyReducer if (tryGuessing(subject)) return; - TypeFamilyReductionResult result = - tfit->family->reducer(subject, tfit->typeArguments, tfit->packArguments, NotNull{&ctx}); + TypeFamilyReductionResult result = tfit->family->reducer(subject, tfit->typeArguments, tfit->packArguments, NotNull{&ctx}); handleFamilyReduction(subject, result); } } @@ -369,8 +368,7 @@ struct FamilyReducer if (tryGuessing(subject)) return; - TypeFamilyReductionResult result = - tfit->family->reducer(subject, tfit->typeArguments, tfit->packArguments, NotNull{&ctx}); + TypeFamilyReductionResult result = tfit->family->reducer(subject, tfit->typeArguments, tfit->packArguments, NotNull{&ctx}); handleFamilyReduction(subject, result); } } @@ -451,8 +449,8 @@ bool isPending(TypeId ty, ConstraintSolver* solver) } template -static std::optional> tryDistributeTypeFamilyApp(F f, TypeId instance, - const std::vector& typeParams, const std::vector& packParams, NotNull ctx, Args&& ...args) +static std::optional> tryDistributeTypeFamilyApp(F f, TypeId instance, const std::vector& typeParams, + const std::vector& packParams, NotNull ctx, Args&&... args) { // op (a | b) (c | d) ~ (op a (c | d)) | (op b (c | d)) ~ (op a c) | (op a d) | (op b c) | (op b d) bool uninhabited = false; @@ -527,8 +525,8 @@ static std::optional> tryDistributeTypeFamilyA return std::nullopt; } -TypeFamilyReductionResult notFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult notFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 1 || !packParams.empty()) { @@ -551,8 +549,8 @@ TypeFamilyReductionResult notFamilyFn(TypeId instance, const std::vector return {ctx->builtins->booleanType, false, {}, {}}; } -TypeFamilyReductionResult lenFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult lenFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 1 || !packParams.empty()) { @@ -573,7 +571,7 @@ TypeFamilyReductionResult lenFamilyFn(TypeId instance, const std::vector // if the type is free but has only one remaining reference, we can generalize it to its upper bound here. if (ctx->solver) { - std::optional maybeGeneralized = ctx->solver->generalizeFreeType(ctx->scope, operandTy); + std::optional maybeGeneralized = ctx->solver->generalizeFreeType(ctx->scope, operandTy, /* avoidSealingTables */ true); if (!maybeGeneralized) return {std::nullopt, false, {operandTy}, {}}; operandTy = *maybeGeneralized; @@ -643,8 +641,8 @@ TypeFamilyReductionResult lenFamilyFn(TypeId instance, const std::vector return {ctx->builtins->numberType, false, {}, {}}; } -TypeFamilyReductionResult unmFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult unmFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 1 || !packParams.empty()) { @@ -846,8 +844,8 @@ TypeFamilyReductionResult numericBinopFamilyFn(TypeId instance, const st return {extracted.head.front(), false, {}, {}}; } -TypeFamilyReductionResult addFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult addFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 2 || !packParams.empty()) { @@ -858,8 +856,8 @@ TypeFamilyReductionResult addFamilyFn(TypeId instance, const std::vector return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__add"); } -TypeFamilyReductionResult subFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult subFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 2 || !packParams.empty()) { @@ -870,8 +868,8 @@ TypeFamilyReductionResult subFamilyFn(TypeId instance, const std::vector return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__sub"); } -TypeFamilyReductionResult mulFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult mulFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 2 || !packParams.empty()) { @@ -882,8 +880,8 @@ TypeFamilyReductionResult mulFamilyFn(TypeId instance, const std::vector return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__mul"); } -TypeFamilyReductionResult divFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult divFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 2 || !packParams.empty()) { @@ -894,8 +892,8 @@ TypeFamilyReductionResult divFamilyFn(TypeId instance, const std::vector return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__div"); } -TypeFamilyReductionResult idivFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult idivFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 2 || !packParams.empty()) { @@ -906,8 +904,8 @@ TypeFamilyReductionResult idivFamilyFn(TypeId instance, const std::vecto return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__idiv"); } -TypeFamilyReductionResult powFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult powFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 2 || !packParams.empty()) { @@ -918,8 +916,8 @@ TypeFamilyReductionResult powFamilyFn(TypeId instance, const std::vector return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__pow"); } -TypeFamilyReductionResult modFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult modFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 2 || !packParams.empty()) { @@ -930,8 +928,8 @@ TypeFamilyReductionResult modFamilyFn(TypeId instance, const std::vector return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__mod"); } -TypeFamilyReductionResult concatFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult concatFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 2 || !packParams.empty()) { @@ -1038,8 +1036,8 @@ TypeFamilyReductionResult concatFamilyFn(TypeId instance, const std::vec return {ctx->builtins->stringType, false, {}, {}}; } -TypeFamilyReductionResult andFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult andFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 2 || !packParams.empty()) { @@ -1089,8 +1087,8 @@ TypeFamilyReductionResult andFamilyFn(TypeId instance, const std::vector return {overallResult.result, false, std::move(blockedTypes), {}}; } -TypeFamilyReductionResult orFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult orFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 2 || !packParams.empty()) { @@ -1279,8 +1277,8 @@ static TypeFamilyReductionResult comparisonFamilyFn(TypeId instance, con return {ctx->builtins->booleanType, false, {}, {}}; } -TypeFamilyReductionResult ltFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult ltFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 2 || !packParams.empty()) { @@ -1291,8 +1289,8 @@ TypeFamilyReductionResult ltFamilyFn(TypeId instance, const std::vector< return comparisonFamilyFn(instance, typeParams, packParams, ctx, "__lt"); } -TypeFamilyReductionResult leFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult leFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 2 || !packParams.empty()) { @@ -1303,8 +1301,8 @@ TypeFamilyReductionResult leFamilyFn(TypeId instance, const std::vector< return comparisonFamilyFn(instance, typeParams, packParams, ctx, "__le"); } -TypeFamilyReductionResult eqFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult eqFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 2 || !packParams.empty()) { @@ -1434,8 +1432,8 @@ struct FindRefinementBlockers : TypeOnceVisitor }; -TypeFamilyReductionResult refineFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult refineFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 2 || !packParams.empty()) { @@ -1519,8 +1517,8 @@ TypeFamilyReductionResult refineFamilyFn(TypeId instance, const std::vec return {resultTy, false, {}, {}}; } -TypeFamilyReductionResult singletonFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult singletonFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 1 || !packParams.empty()) { @@ -1556,8 +1554,8 @@ TypeFamilyReductionResult singletonFamilyFn(TypeId instance, const std:: return {ctx->builtins->unknownType, false, {}, {}}; } -TypeFamilyReductionResult unionFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult unionFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (!packParams.empty()) { @@ -1617,8 +1615,8 @@ TypeFamilyReductionResult unionFamilyFn(TypeId instance, const std::vect } -TypeFamilyReductionResult intersectFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult intersectFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (!packParams.empty()) { @@ -1841,8 +1839,8 @@ TypeFamilyReductionResult keyofFamilyImpl( return {ctx->arena->addType(UnionType{singletons}), false, {}, {}}; } -TypeFamilyReductionResult keyofFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult keyofFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 1 || !packParams.empty()) { @@ -1853,8 +1851,8 @@ TypeFamilyReductionResult keyofFamilyFn(TypeId instance, const std::vect return keyofFamilyImpl(typeParams, packParams, ctx, /* isRaw */ false); } -TypeFamilyReductionResult rawkeyofFamilyFn(TypeId instance, const std::vector& typeParams, - const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult rawkeyofFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) { if (typeParams.size() != 1 || !packParams.empty()) { diff --git a/Analysis/src/TypeInfer.cpp b/Analysis/src/TypeInfer.cpp index 3050f09e..9ce1a58a 100644 --- a/Analysis/src/TypeInfer.cpp +++ b/Analysis/src/TypeInfer.cpp @@ -33,11 +33,11 @@ LUAU_FASTFLAG(LuauKnowsTheDataModel3) LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false) LUAU_FASTFLAGVARIABLE(DebugLuauSharedSelf, false) LUAU_FASTFLAG(LuauInstantiateInSubtyping) -LUAU_FASTFLAGVARIABLE(LuauMetatableInstantiationCloneCheck, false) LUAU_FASTFLAGVARIABLE(LuauTinyControlFlowAnalysis, false) LUAU_FASTFLAGVARIABLE(LuauAlwaysCommitInferencesOfFunctionCalls, false) LUAU_FASTFLAGVARIABLE(LuauRemoveBadRelationalOperatorWarning, false) LUAU_FASTFLAGVARIABLE(LuauOkWithIteratingOverTableProperties, false) +LUAU_FASTFLAGVARIABLE(LuauReusableSubstitutions, false) namespace Luau { @@ -214,6 +214,7 @@ TypeChecker::TypeChecker(const ScopePtr& globalScope, ModuleResolver* resolver, , iceHandler(iceHandler) , unifierState(iceHandler) , normalizer(nullptr, builtinTypes, NotNull{&unifierState}) + , reusableInstantiation(TxnLog::empty(), nullptr, builtinTypes, {}, nullptr) , nilType(builtinTypes->nilType) , numberType(builtinTypes->numberType) , stringType(builtinTypes->stringType) @@ -4865,12 +4866,27 @@ TypeId TypeChecker::instantiate(const ScopePtr& scope, TypeId ty, Location locat if (ftv && ftv->hasNoFreeOrGenericTypes) return ty; - Instantiation instantiation{log, ¤tModule->internalTypes, builtinTypes, scope->level, /*scope*/ nullptr}; + std::optional instantiated; - if (instantiationChildLimit) - instantiation.childLimit = *instantiationChildLimit; + if (FFlag::LuauReusableSubstitutions) + { + reusableInstantiation.resetState(log, ¤tModule->internalTypes, builtinTypes, scope->level, /*scope*/ nullptr); + + if (instantiationChildLimit) + reusableInstantiation.childLimit = *instantiationChildLimit; + + instantiated = reusableInstantiation.substitute(ty); + } + else + { + Instantiation instantiation{log, ¤tModule->internalTypes, builtinTypes, scope->level, /*scope*/ nullptr}; + + if (instantiationChildLimit) + instantiation.childLimit = *instantiationChildLimit; + + instantiated = instantiation.substitute(ty); + } - std::optional instantiated = instantiation.substitute(ty); if (instantiated.has_value()) return *instantiated; else @@ -5619,8 +5635,8 @@ TypeId TypeChecker::instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf, TypeId instantiated = *maybeInstantiated; TypeId target = follow(instantiated); - const TableType* tfTable = FFlag::LuauMetatableInstantiationCloneCheck ? getTableType(tf.type) : nullptr; - bool needsClone = follow(tf.type) == target || (FFlag::LuauMetatableInstantiationCloneCheck && tfTable != nullptr && tfTable == getTableType(target)); + const TableType* tfTable = getTableType(tf.type); + bool needsClone = follow(tf.type) == target || (tfTable != nullptr && tfTable == getTableType(target)); bool shouldMutate = getTableType(tf.type); TableType* ttv = getMutableTableType(target); diff --git a/Analysis/src/Unifier.cpp b/Analysis/src/Unifier.cpp index 484e45d0..a0c802dd 100644 --- a/Analysis/src/Unifier.cpp +++ b/Analysis/src/Unifier.cpp @@ -974,8 +974,7 @@ void Unifier::tryUnifyTypeWithUnion(TypeId subTy, TypeId superTy, const UnionTyp if (!subNorm || !superNorm) reportError(location, NormalizationTooComplex{}); else if ((failedOptionCount == 1 || foundHeuristic) && failedOption) - tryUnifyNormalizedTypes( - subTy, superTy, *subNorm, *superNorm, "None of the union options are compatible. For example:", *failedOption); + tryUnifyNormalizedTypes(subTy, superTy, *subNorm, *superNorm, "None of the union options are compatible. For example:", *failedOption); else tryUnifyNormalizedTypes(subTy, superTy, *subNorm, *superNorm, "none of the union options are compatible"); } diff --git a/Analysis/src/Unifier2.cpp b/Analysis/src/Unifier2.cpp index f46c3372..6dcd7197 100644 --- a/Analysis/src/Unifier2.cpp +++ b/Analysis/src/Unifier2.cpp @@ -479,7 +479,7 @@ bool Unifier2::unify(const FunctionType* subFn, const AnyType* superAny) bool Unifier2::unify(const AnyType* subAny, const TableType* superTable) { - for (const auto& [propName, prop]: superTable->props) + for (const auto& [propName, prop] : superTable->props) { if (prop.readTy) unify(builtinTypes->anyType, *prop.readTy); @@ -499,7 +499,7 @@ bool Unifier2::unify(const AnyType* subAny, const TableType* superTable) bool Unifier2::unify(const TableType* subTable, const AnyType* superAny) { - for (const auto& [propName, prop]: subTable->props) + for (const auto& [propName, prop] : subTable->props) { if (prop.readTy) unify(*prop.readTy, builtinTypes->anyType); @@ -658,31 +658,31 @@ struct FreeTypeSearcher : TypeVisitor { switch (polarity) { - case Positive: - { - if (seenPositive.contains(ty)) - return true; + case Positive: + { + if (seenPositive.contains(ty)) + return true; - seenPositive.insert(ty); - return false; - } - case Negative: - { - if (seenNegative.contains(ty)) - return true; + seenPositive.insert(ty); + return false; + } + case Negative: + { + if (seenNegative.contains(ty)) + return true; - seenNegative.insert(ty); - return false; - } - case Both: - { - if (seenPositive.contains(ty) && seenNegative.contains(ty)) - return true; + seenNegative.insert(ty); + return false; + } + case Both: + { + if (seenPositive.contains(ty) && seenNegative.contains(ty)) + return true; - seenPositive.insert(ty); - seenNegative.insert(ty); - return false; - } + seenPositive.insert(ty); + seenNegative.insert(ty); + return false; + } } return false; diff --git a/CodeGen/include/Luau/IrData.h b/CodeGen/include/Luau/IrData.h index d0e40ca3..c136c721 100644 --- a/CodeGen/include/Luau/IrData.h +++ b/CodeGen/include/Luau/IrData.h @@ -326,13 +326,12 @@ enum class IrCmd : uint8_t // This is used to recover after calling a variadic function ADJUST_STACK_TO_TOP, - // Execute fastcall builtin function in-place + // Execute fastcall builtin function with 1 argument in-place + // This is used for a few builtins that can have more than 1 result and cannot be represented as a regular instruction // A: unsigned int (builtin id) // B: Rn (result start) - // C: Rn (argument start) - // D: Rn or Kn or undef (optional second argument) - // E: int (argument count) - // F: int (result count) + // C: Rn (first argument) + // D: int (result count) FASTCALL, // Call the fastcall builtin function @@ -340,8 +339,9 @@ enum class IrCmd : uint8_t // B: Rn (result start) // C: Rn (argument start) // D: Rn or Kn or undef (optional second argument) - // E: int (argument count or -1 to use all arguments up to stack top) - // F: int (result count or -1 to preserve all results and adjust stack top) + // E: Rn or Kn or undef (optional third argument) + // F: int (argument count or -1 to use all arguments up to stack top) + // G: int (result count or -1 to preserve all results and adjust stack top) INVOKE_FASTCALL, // Check that fastcall builtin function invocation was successful (negative result count jumps to fallback) diff --git a/CodeGen/include/Luau/IrUtils.h b/CodeGen/include/Luau/IrUtils.h index 8486921e..bc81fc68 100644 --- a/CodeGen/include/Luau/IrUtils.h +++ b/CodeGen/include/Luau/IrUtils.h @@ -64,6 +64,7 @@ inline bool isFastCall(LuauOpcode op) case LOP_FASTCALL1: case LOP_FASTCALL2: case LOP_FASTCALL2K: + case LOP_FASTCALL3: return true; default: diff --git a/CodeGen/include/Luau/IrVisitUseDef.h b/CodeGen/include/Luau/IrVisitUseDef.h index 32dd6c2a..6744bd65 100644 --- a/CodeGen/include/Luau/IrVisitUseDef.h +++ b/CodeGen/include/Luau/IrVisitUseDef.h @@ -4,7 +4,7 @@ #include "Luau/Common.h" #include "Luau/IrData.h" -LUAU_FASTFLAG(LuauCodegenRemoveDeadStores5) +LUAU_FASTFLAG(LuauCodegenFastcall3) namespace Luau { @@ -112,12 +112,48 @@ static void visitVmRegDefsUses(T& visitor, IrFunction& function, const IrInst& i visitor.useRange(vmRegOp(inst.a), function.intOp(inst.b)); break; - // TODO: FASTCALL is more restrictive than INVOKE_FASTCALL; we should either determine the exact semantics, or rework it case IrCmd::FASTCALL: - case IrCmd::INVOKE_FASTCALL: - if (int count = function.intOp(inst.e); count != -1) + if (FFlag::LuauCodegenFastcall3) { - if (count >= 3) + visitor.use(inst.c); + + if (int nresults = function.intOp(inst.d); nresults != -1) + visitor.defRange(vmRegOp(inst.b), nresults); + } + else + { + if (int count = function.intOp(inst.e); count != -1) + { + if (count >= 3) + { + CODEGEN_ASSERT(inst.d.kind == IrOpKind::VmReg && vmRegOp(inst.d) == vmRegOp(inst.c) + 1); + + visitor.useRange(vmRegOp(inst.c), count); + } + else + { + if (count >= 1) + visitor.use(inst.c); + + if (count >= 2) + visitor.maybeUse(inst.d); // Argument can also be a VmConst + } + } + else + { + visitor.useVarargs(vmRegOp(inst.c)); + } + + // Multiple return sequences (count == -1) are defined by ADJUST_STACK_TO_REG + if (int count = function.intOp(inst.f); count != -1) + visitor.defRange(vmRegOp(inst.b), count); + } + break; + case IrCmd::INVOKE_FASTCALL: + if (int count = function.intOp(FFlag::LuauCodegenFastcall3 ? inst.f : inst.e); count != -1) + { + // Only LOP_FASTCALL3 lowering is allowed to have third optional argument + if (count >= 3 && (!FFlag::LuauCodegenFastcall3 || inst.e.kind == IrOpKind::Undef)) { CODEGEN_ASSERT(inst.d.kind == IrOpKind::VmReg && vmRegOp(inst.d) == vmRegOp(inst.c) + 1); @@ -130,6 +166,9 @@ static void visitVmRegDefsUses(T& visitor, IrFunction& function, const IrInst& i if (count >= 2) visitor.maybeUse(inst.d); // Argument can also be a VmConst + + if (FFlag::LuauCodegenFastcall3 && count >= 3) + visitor.maybeUse(inst.e); // Argument can also be a VmConst } } else @@ -138,7 +177,7 @@ static void visitVmRegDefsUses(T& visitor, IrFunction& function, const IrInst& i } // Multiple return sequences (count == -1) are defined by ADJUST_STACK_TO_REG - if (int count = function.intOp(inst.f); count != -1) + if (int count = function.intOp(FFlag::LuauCodegenFastcall3 ? inst.g : inst.f); count != -1) visitor.defRange(vmRegOp(inst.b), count); break; case IrCmd::FORGLOOP: @@ -188,15 +227,8 @@ static void visitVmRegDefsUses(T& visitor, IrFunction& function, const IrInst& i visitor.def(inst.b); break; case IrCmd::FALLBACK_FORGPREP: - if (FFlag::LuauCodegenRemoveDeadStores5) - { - // This instruction doesn't always redefine Rn, Rn+1, Rn+2, so we have to mark it as implicit use - visitor.useRange(vmRegOp(inst.b), 3); - } - else - { - visitor.use(inst.b); - } + // This instruction doesn't always redefine Rn, Rn+1, Rn+2, so we have to mark it as implicit use + visitor.useRange(vmRegOp(inst.b), 3); visitor.defRange(vmRegOp(inst.b), 3); break; @@ -214,12 +246,6 @@ static void visitVmRegDefsUses(T& visitor, IrFunction& function, const IrInst& i visitor.use(inst.a); break; - // After optimizations with DebugLuauAbortingChecks enabled, CHECK_TAG Rn, tag, block instructions are generated - case IrCmd::CHECK_TAG: - if (!FFlag::LuauCodegenRemoveDeadStores5) - visitor.maybeUse(inst.a); - break; - default: // All instructions which reference registers have to be handled explicitly CODEGEN_ASSERT(inst.a.kind != IrOpKind::VmReg); diff --git a/CodeGen/src/BytecodeAnalysis.cpp b/CodeGen/src/BytecodeAnalysis.cpp index fc8eb900..c429188d 100644 --- a/CodeGen/src/BytecodeAnalysis.cpp +++ b/CodeGen/src/BytecodeAnalysis.cpp @@ -11,30 +11,19 @@ #include -LUAU_FASTFLAG(LuauCodegenDirectUserdataFlow) -LUAU_FASTFLAG(LuauLoadTypeInfo) // Because new VM typeinfo load changes the format used by Codegen, same flag is used -LUAU_FASTFLAGVARIABLE(LuauCodegenTypeInfo, false) // New analysis is flagged separately LUAU_FASTFLAGVARIABLE(LuauCodegenAnalyzeHostVectorOps, false) LUAU_FASTFLAGVARIABLE(LuauCodegenLoadTypeUpvalCheck, false) LUAU_FASTFLAGVARIABLE(LuauCodegenUserdataOps, false) +LUAU_FASTFLAGVARIABLE(LuauCodegenFastcall3, false) namespace Luau { namespace CodeGen { -static bool hasTypedParameters(Proto* proto) -{ - CODEGEN_ASSERT(!FFlag::LuauLoadTypeInfo); - - return proto->typeinfo && proto->numparams != 0; -} - template static T read(uint8_t* data, size_t& offset) { - CODEGEN_ASSERT(FFlag::LuauLoadTypeInfo); - T result; memcpy(&result, data + offset, sizeof(T)); offset += sizeof(T); @@ -44,8 +33,6 @@ static T read(uint8_t* data, size_t& offset) static uint32_t readVarInt(uint8_t* data, size_t& offset) { - CODEGEN_ASSERT(FFlag::LuauLoadTypeInfo); - uint32_t result = 0; uint32_t shift = 0; @@ -63,8 +50,6 @@ static uint32_t readVarInt(uint8_t* data, size_t& offset) void loadBytecodeTypeInfo(IrFunction& function) { - CODEGEN_ASSERT(FFlag::LuauLoadTypeInfo); - Proto* proto = function.proto; if (!proto) @@ -173,8 +158,6 @@ static void prepareRegTypeInfoLookups(BytecodeTypeInfo& typeInfo) static BytecodeRegTypeInfo* findRegType(BytecodeTypeInfo& info, uint8_t reg, int pc) { - CODEGEN_ASSERT(FFlag::LuauCodegenTypeInfo); - auto b = info.regTypes.begin() + info.regTypeOffsets[reg]; auto e = info.regTypes.begin() + info.regTypeOffsets[reg + 1]; @@ -199,8 +182,6 @@ static BytecodeRegTypeInfo* findRegType(BytecodeTypeInfo& info, uint8_t reg, int static void refineRegType(BytecodeTypeInfo& info, uint8_t reg, int pc, uint8_t ty) { - CODEGEN_ASSERT(FFlag::LuauCodegenTypeInfo); - if (ty != LBC_TYPE_ANY) { if (BytecodeRegTypeInfo* regType = findRegType(info, reg, pc)) @@ -219,8 +200,6 @@ static void refineRegType(BytecodeTypeInfo& info, uint8_t reg, int pc, uint8_t t static void refineUpvalueType(BytecodeTypeInfo& info, int up, uint8_t ty) { - CODEGEN_ASSERT(FFlag::LuauCodegenTypeInfo); - if (ty != LBC_TYPE_ANY) { if (size_t(up) < info.upvalueTypes.size()) @@ -662,28 +641,12 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks) // At the block start, reset or knowledge to the starting state // In the future we might be able to propagate some info between the blocks as well - if (FFlag::LuauLoadTypeInfo) + for (size_t i = 0; i < bcTypeInfo.argumentTypes.size(); i++) { - for (size_t i = 0; i < bcTypeInfo.argumentTypes.size(); i++) - { - uint8_t et = bcTypeInfo.argumentTypes[i]; + uint8_t et = bcTypeInfo.argumentTypes[i]; - // TODO: if argument is optional, this might force a VM exit unnecessarily - regTags[i] = et & ~LBC_TYPE_OPTIONAL_BIT; - } - } - else - { - if (hasTypedParameters(proto)) - { - for (int i = 0; i < proto->numparams; ++i) - { - uint8_t et = proto->typeinfo[2 + i]; - - // TODO: if argument is optional, this might force a VM exit unnecessarily - regTags[i] = et & ~LBC_TYPE_OPTIONAL_BIT; - } - } + // TODO: if argument is optional, this might force a VM exit unnecessarily + regTags[i] = et & ~LBC_TYPE_OPTIONAL_BIT; } for (int i = proto->numparams; i < proto->maxstacksize; ++i) @@ -696,16 +659,13 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks) const Instruction* pc = &proto->code[i]; LuauOpcode op = LuauOpcode(LUAU_INSN_OP(*pc)); - if (FFlag::LuauCodegenTypeInfo) + // Assign known register types from local type information + // TODO: this is an expensive walk for each instruction + // TODO: it's best to lookup when register is actually used in the instruction + for (BytecodeRegTypeInfo& el : bcTypeInfo.regTypes) { - // Assign known register types from local type information - // TODO: this is an expensive walk for each instruction - // TODO: it's best to lookup when register is actually used in the instruction - for (BytecodeRegTypeInfo& el : bcTypeInfo.regTypes) - { - if (el.type != LBC_TYPE_ANY && i >= el.startpc && i < el.endpc) - regTags[el.reg] = el.type; - } + if (el.type != LBC_TYPE_ANY && i >= el.startpc && i < el.endpc) + regTags[el.reg] = el.type; } BytecodeTypes& bcType = function.bcTypes[i]; @@ -727,8 +687,7 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks) regTags[ra] = LBC_TYPE_BOOLEAN; bcType.result = regTags[ra]; - if (FFlag::LuauCodegenTypeInfo) - refineRegType(bcTypeInfo, ra, i, bcType.result); + refineRegType(bcTypeInfo, ra, i, bcType.result); break; } case LOP_LOADN: @@ -737,8 +696,7 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks) regTags[ra] = LBC_TYPE_NUMBER; bcType.result = regTags[ra]; - if (FFlag::LuauCodegenTypeInfo) - refineRegType(bcTypeInfo, ra, i, bcType.result); + refineRegType(bcTypeInfo, ra, i, bcType.result); break; } case LOP_LOADK: @@ -749,8 +707,7 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks) regTags[ra] = bcType.a; bcType.result = regTags[ra]; - if (FFlag::LuauCodegenTypeInfo) - refineRegType(bcTypeInfo, ra, i, bcType.result); + refineRegType(bcTypeInfo, ra, i, bcType.result); break; } case LOP_LOADKX: @@ -761,8 +718,7 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks) regTags[ra] = bcType.a; bcType.result = regTags[ra]; - if (FFlag::LuauCodegenTypeInfo) - refineRegType(bcTypeInfo, ra, i, bcType.result); + refineRegType(bcTypeInfo, ra, i, bcType.result); break; } case LOP_MOVE: @@ -773,8 +729,7 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks) regTags[ra] = regTags[rb]; bcType.result = regTags[ra]; - if (FFlag::LuauCodegenTypeInfo) - refineRegType(bcTypeInfo, ra, i, bcType.result); + refineRegType(bcTypeInfo, ra, i, bcType.result); break; } case LOP_GETTABLE: @@ -1142,8 +1097,7 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks) regTags[ra + 3] = bcType.c; regTags[ra] = bcType.result; - if (FFlag::LuauCodegenTypeInfo) - refineRegType(bcTypeInfo, ra, i, bcType.result); + refineRegType(bcTypeInfo, ra, i, bcType.result); break; } case LOP_FASTCALL1: @@ -1161,8 +1115,7 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks) regTags[LUAU_INSN_B(*pc)] = bcType.a; regTags[ra] = bcType.result; - if (FFlag::LuauCodegenTypeInfo) - refineRegType(bcTypeInfo, ra, i, bcType.result); + refineRegType(bcTypeInfo, ra, i, bcType.result); break; } case LOP_FASTCALL2: @@ -1180,8 +1133,29 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks) regTags[int(pc[1])] = bcType.b; regTags[ra] = bcType.result; - if (FFlag::LuauCodegenTypeInfo) - refineRegType(bcTypeInfo, ra, i, bcType.result); + refineRegType(bcTypeInfo, ra, i, bcType.result); + break; + } + case LOP_FASTCALL3: + { + CODEGEN_ASSERT(FFlag::LuauCodegenFastcall3); + + int bfid = LUAU_INSN_A(*pc); + int skip = LUAU_INSN_C(*pc); + int aux = pc[1]; + + Instruction call = pc[skip + 1]; + CODEGEN_ASSERT(LUAU_INSN_OP(call) == LOP_CALL); + int ra = LUAU_INSN_A(call); + + applyBuiltinCall(bfid, bcType); + + regTags[LUAU_INSN_B(*pc)] = bcType.a; + regTags[aux & 0xff] = bcType.b; + regTags[(aux >> 8) & 0xff] = bcType.c; + regTags[ra] = bcType.result; + + refineRegType(bcTypeInfo, ra, i, bcType.result); break; } case LOP_FORNPREP: @@ -1192,12 +1166,9 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks) regTags[ra + 1] = LBC_TYPE_NUMBER; regTags[ra + 2] = LBC_TYPE_NUMBER; - if (FFlag::LuauCodegenTypeInfo) - { - refineRegType(bcTypeInfo, ra, i, regTags[ra]); - refineRegType(bcTypeInfo, ra + 1, i, regTags[ra + 1]); - refineRegType(bcTypeInfo, ra + 2, i, regTags[ra + 2]); - } + refineRegType(bcTypeInfo, ra, i, regTags[ra]); + refineRegType(bcTypeInfo, ra + 1, i, regTags[ra + 1]); + refineRegType(bcTypeInfo, ra + 2, i, regTags[ra + 2]); break; } case LOP_FORNLOOP: @@ -1227,42 +1198,39 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks) } case LOP_NAMECALL: { - if (FFlag::LuauCodegenDirectUserdataFlow) + int ra = LUAU_INSN_A(*pc); + int rb = LUAU_INSN_B(*pc); + uint32_t kc = pc[1]; + + bcType.a = regTags[rb]; + bcType.b = getBytecodeConstantTag(proto, kc); + + // While namecall might result in a callable table, we assume the function fast path + regTags[ra] = LBC_TYPE_FUNCTION; + + // Namecall places source register into target + 1 + regTags[ra + 1] = bcType.a; + + bcType.result = LBC_TYPE_FUNCTION; + + if (FFlag::LuauCodegenUserdataOps) { - int ra = LUAU_INSN_A(*pc); - int rb = LUAU_INSN_B(*pc); - uint32_t kc = pc[1]; + TString* str = gco2ts(function.proto->k[kc].value.gc); + const char* field = getstr(str); - bcType.a = regTags[rb]; - bcType.b = getBytecodeConstantTag(proto, kc); - - // While namecall might result in a callable table, we assume the function fast path - regTags[ra] = LBC_TYPE_FUNCTION; - - // Namecall places source register into target + 1 - regTags[ra + 1] = bcType.a; - - bcType.result = LBC_TYPE_FUNCTION; - - if (FFlag::LuauCodegenUserdataOps) + if (FFlag::LuauCodegenAnalyzeHostVectorOps && bcType.a == LBC_TYPE_VECTOR && hostHooks.vectorNamecallBytecodeType) + knownNextCallResult = LuauBytecodeType(hostHooks.vectorNamecallBytecodeType(field, str->len)); + else if (isCustomUserdataBytecodeType(bcType.a) && hostHooks.userdataNamecallBytecodeType) + knownNextCallResult = LuauBytecodeType(hostHooks.userdataNamecallBytecodeType(bcType.a, field, str->len)); + } + else + { + if (FFlag::LuauCodegenAnalyzeHostVectorOps && bcType.a == LBC_TYPE_VECTOR && hostHooks.vectorNamecallBytecodeType) { TString* str = gco2ts(function.proto->k[kc].value.gc); const char* field = getstr(str); - if (FFlag::LuauCodegenAnalyzeHostVectorOps && bcType.a == LBC_TYPE_VECTOR && hostHooks.vectorNamecallBytecodeType) - knownNextCallResult = LuauBytecodeType(hostHooks.vectorNamecallBytecodeType(field, str->len)); - else if (isCustomUserdataBytecodeType(bcType.a) && hostHooks.userdataNamecallBytecodeType) - knownNextCallResult = LuauBytecodeType(hostHooks.userdataNamecallBytecodeType(bcType.a, field, str->len)); - } - else - { - if (FFlag::LuauCodegenAnalyzeHostVectorOps && bcType.a == LBC_TYPE_VECTOR && hostHooks.vectorNamecallBytecodeType) - { - TString* str = gco2ts(function.proto->k[kc].value.gc); - const char* field = getstr(str); - - knownNextCallResult = LuauBytecodeType(hostHooks.vectorNamecallBytecodeType(field, str->len)); - } + knownNextCallResult = LuauBytecodeType(hostHooks.vectorNamecallBytecodeType(field, str->len)); } } break; @@ -1282,42 +1250,35 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks) regTags[ra] = bcType.result; } - if (FFlag::LuauCodegenTypeInfo) - refineRegType(bcTypeInfo, ra, i, bcType.result); + refineRegType(bcTypeInfo, ra, i, bcType.result); } break; } case LOP_GETUPVAL: { - if (FFlag::LuauCodegenTypeInfo) + int ra = LUAU_INSN_A(*pc); + int up = LUAU_INSN_B(*pc); + + bcType.a = LBC_TYPE_ANY; + + if (size_t(up) < bcTypeInfo.upvalueTypes.size()) { - int ra = LUAU_INSN_A(*pc); - int up = LUAU_INSN_B(*pc); + uint8_t et = bcTypeInfo.upvalueTypes[up]; - bcType.a = LBC_TYPE_ANY; - - if (size_t(up) < bcTypeInfo.upvalueTypes.size()) - { - uint8_t et = bcTypeInfo.upvalueTypes[up]; - - // TODO: if argument is optional, this might force a VM exit unnecessarily - bcType.a = et & ~LBC_TYPE_OPTIONAL_BIT; - } - - regTags[ra] = bcType.a; - bcType.result = regTags[ra]; + // TODO: if argument is optional, this might force a VM exit unnecessarily + bcType.a = et & ~LBC_TYPE_OPTIONAL_BIT; } + + regTags[ra] = bcType.a; + bcType.result = regTags[ra]; break; } case LOP_SETUPVAL: { - if (FFlag::LuauCodegenTypeInfo) - { - int ra = LUAU_INSN_A(*pc); - int up = LUAU_INSN_B(*pc); + int ra = LUAU_INSN_A(*pc); + int up = LUAU_INSN_B(*pc); - refineUpvalueType(bcTypeInfo, up, regTags[ra]); - } + refineUpvalueType(bcTypeInfo, up, regTags[ra]); break; } case LOP_GETGLOBAL: diff --git a/CodeGen/src/CodeGenAssembly.cpp b/CodeGen/src/CodeGenAssembly.cpp index 121535be..de8dcecf 100644 --- a/CodeGen/src/CodeGenAssembly.cpp +++ b/CodeGen/src/CodeGenAssembly.cpp @@ -12,7 +12,6 @@ #include "lapi.h" -LUAU_FASTFLAG(LuauCodegenTypeInfo) LUAU_FASTFLAG(LuauLoadUserdataInfo) LUAU_FASTFLAG(LuauNativeAttribute) @@ -87,7 +86,6 @@ static void logFunctionHeader(AssemblyBuilder& build, Proto* proto) template static void logFunctionTypes_DEPRECATED(AssemblyBuilder& build, const IrFunction& function) { - CODEGEN_ASSERT(FFlag::LuauCodegenTypeInfo); CODEGEN_ASSERT(!FFlag::LuauLoadUserdataInfo); const BytecodeTypeInfo& typeInfo = function.bcTypeInfo; @@ -131,7 +129,6 @@ static void logFunctionTypes_DEPRECATED(AssemblyBuilder& build, const IrFunction template static void logFunctionTypes(AssemblyBuilder& build, const IrFunction& function, const char* const* userdataTypes) { - CODEGEN_ASSERT(FFlag::LuauCodegenTypeInfo); CODEGEN_ASSERT(FFlag::LuauLoadUserdataInfo); const BytecodeTypeInfo& typeInfo = function.bcTypeInfo; @@ -240,7 +237,7 @@ static std::string getAssemblyImpl(AssemblyBuilder& build, const TValue* func, A if (options.includeAssembly || options.includeIr) logFunctionHeader(build, p); - if (FFlag::LuauCodegenTypeInfo && options.includeIrTypes) + if (options.includeIrTypes) { if (FFlag::LuauLoadUserdataInfo) logFunctionTypes(build, ir.function, options.compilationOptions.userdataTypes); diff --git a/CodeGen/src/CodeGenLower.h b/CodeGen/src/CodeGenLower.h index 4523d62b..e7701361 100644 --- a/CodeGen/src/CodeGenLower.h +++ b/CodeGen/src/CodeGenLower.h @@ -27,7 +27,6 @@ LUAU_FASTFLAG(DebugCodegenSkipNumbering) LUAU_FASTINT(CodegenHeuristicsInstructionLimit) LUAU_FASTINT(CodegenHeuristicsBlockLimit) LUAU_FASTINT(CodegenHeuristicsBlockInstructionLimit) -LUAU_FASTFLAG(LuauCodegenRemoveDeadStores5) LUAU_FASTFLAG(LuauLoadUserdataInfo) LUAU_FASTFLAG(LuauNativeAttribute) @@ -347,8 +346,7 @@ inline bool lowerFunction(IrBuilder& ir, AssemblyBuilder& build, ModuleHelpers& } } - if (FFlag::LuauCodegenRemoveDeadStores5) - markDeadStoresInBlockChains(ir); + markDeadStoresInBlockChains(ir); } std::vector sortedBlocks = getSortedBlockOrder(ir.function); diff --git a/CodeGen/src/EmitBuiltinsX64.cpp b/CodeGen/src/EmitBuiltinsX64.cpp index 96d22e13..09f69d69 100644 --- a/CodeGen/src/EmitBuiltinsX64.cpp +++ b/CodeGen/src/EmitBuiltinsX64.cpp @@ -12,8 +12,6 @@ #include "lstate.h" -LUAU_FASTFLAG(LuauCodegenRemoveDeadStores5) - namespace Luau { namespace CodeGen @@ -29,17 +27,13 @@ static void emitBuiltinMathFrexp(IrRegAllocX64& regs, AssemblyBuilderX64& build, callWrap.call(qword[rNativeContext + offsetof(NativeContext, libm_frexp)]); build.vmovsd(luauRegValue(ra), xmm0); - - if (FFlag::LuauCodegenRemoveDeadStores5) - build.mov(luauRegTag(ra), LUA_TNUMBER); + build.mov(luauRegTag(ra), LUA_TNUMBER); if (nresults > 1) { build.vcvtsi2sd(xmm0, xmm0, dword[sTemporarySlot + 0]); build.vmovsd(luauRegValue(ra + 1), xmm0); - - if (FFlag::LuauCodegenRemoveDeadStores5) - build.mov(luauRegTag(ra + 1), LUA_TNUMBER); + build.mov(luauRegTag(ra + 1), LUA_TNUMBER); } } @@ -52,16 +46,12 @@ static void emitBuiltinMathModf(IrRegAllocX64& regs, AssemblyBuilderX64& build, build.vmovsd(xmm1, qword[sTemporarySlot + 0]); build.vmovsd(luauRegValue(ra), xmm1); - - if (FFlag::LuauCodegenRemoveDeadStores5) - build.mov(luauRegTag(ra), LUA_TNUMBER); + build.mov(luauRegTag(ra), LUA_TNUMBER); if (nresults > 1) { build.vmovsd(luauRegValue(ra + 1), xmm0); - - if (FFlag::LuauCodegenRemoveDeadStores5) - build.mov(luauRegTag(ra + 1), LUA_TNUMBER); + build.mov(luauRegTag(ra + 1), LUA_TNUMBER); } } @@ -90,23 +80,21 @@ static void emitBuiltinMathSign(IrRegAllocX64& regs, AssemblyBuilderX64& build, build.vblendvpd(tmp0.reg, tmp2.reg, build.f64x2(1, 1), tmp0.reg); build.vmovsd(luauRegValue(ra), tmp0.reg); - - if (FFlag::LuauCodegenRemoveDeadStores5) - build.mov(luauRegTag(ra), LUA_TNUMBER); + build.mov(luauRegTag(ra), LUA_TNUMBER); } -void emitBuiltin(IrRegAllocX64& regs, AssemblyBuilderX64& build, int bfid, int ra, int arg, OperandX64 arg2, int nparams, int nresults) +void emitBuiltin(IrRegAllocX64& regs, AssemblyBuilderX64& build, int bfid, int ra, int arg, int nresults) { switch (bfid) { case LBF_MATH_FREXP: - CODEGEN_ASSERT(nparams == 1 && (nresults == 1 || nresults == 2)); + CODEGEN_ASSERT(nresults == 1 || nresults == 2); return emitBuiltinMathFrexp(regs, build, ra, arg, nresults); case LBF_MATH_MODF: - CODEGEN_ASSERT(nparams == 1 && (nresults == 1 || nresults == 2)); + CODEGEN_ASSERT(nresults == 1 || nresults == 2); return emitBuiltinMathModf(regs, build, ra, arg, nresults); case LBF_MATH_SIGN: - CODEGEN_ASSERT(nparams == 1 && nresults == 1); + CODEGEN_ASSERT(nresults == 1); return emitBuiltinMathSign(regs, build, ra, arg); default: CODEGEN_ASSERT(!"Missing x64 lowering"); diff --git a/CodeGen/src/EmitBuiltinsX64.h b/CodeGen/src/EmitBuiltinsX64.h index cd8b5251..72a1ad15 100644 --- a/CodeGen/src/EmitBuiltinsX64.h +++ b/CodeGen/src/EmitBuiltinsX64.h @@ -16,7 +16,7 @@ class AssemblyBuilderX64; struct OperandX64; struct IrRegAllocX64; -void emitBuiltin(IrRegAllocX64& regs, AssemblyBuilderX64& build, int bfid, int ra, int arg, OperandX64 arg2, int nparams, int nresults); +void emitBuiltin(IrRegAllocX64& regs, AssemblyBuilderX64& build, int bfid, int ra, int arg, int nresults); } // namespace X64 } // namespace CodeGen diff --git a/CodeGen/src/IrBuilder.cpp b/CodeGen/src/IrBuilder.cpp index e62885eb..672c27ad 100644 --- a/CodeGen/src/IrBuilder.cpp +++ b/CodeGen/src/IrBuilder.cpp @@ -13,10 +13,10 @@ #include -LUAU_FASTFLAG(LuauLoadTypeInfo) // Because new VM typeinfo load changes the format used by Codegen, same flag is used LUAU_FASTFLAG(LuauCodegenAnalyzeHostVectorOps) LUAU_FASTFLAG(LuauLoadUserdataInfo) LUAU_FASTFLAG(LuauCodegenInstG) +LUAU_FASTFLAG(LuauCodegenFastcall3) namespace Luau { @@ -30,96 +30,9 @@ IrBuilder::IrBuilder(const HostIrHooks& hostHooks) , constantMap({IrConstKind::Tag, ~0ull}) { } -static bool hasTypedParameters_DEPRECATED(Proto* proto) -{ - CODEGEN_ASSERT(!FFlag::LuauLoadTypeInfo); - - return proto->typeinfo && proto->numparams != 0; -} - -static void buildArgumentTypeChecks_DEPRECATED(IrBuilder& build, Proto* proto) -{ - CODEGEN_ASSERT(!FFlag::LuauLoadTypeInfo); - CODEGEN_ASSERT(hasTypedParameters_DEPRECATED(proto)); - - for (int i = 0; i < proto->numparams; ++i) - { - uint8_t et = proto->typeinfo[2 + i]; - - uint8_t tag = et & ~LBC_TYPE_OPTIONAL_BIT; - uint8_t optional = et & LBC_TYPE_OPTIONAL_BIT; - - if (tag == LBC_TYPE_ANY) - continue; - - IrOp load = build.inst(IrCmd::LOAD_TAG, build.vmReg(i)); - - IrOp nextCheck; - if (optional) - { - nextCheck = build.block(IrBlockKind::Internal); - IrOp fallbackCheck = build.block(IrBlockKind::Internal); - - build.inst(IrCmd::JUMP_EQ_TAG, load, build.constTag(LUA_TNIL), nextCheck, fallbackCheck); - - build.beginBlock(fallbackCheck); - } - - switch (tag) - { - case LBC_TYPE_NIL: - build.inst(IrCmd::CHECK_TAG, load, build.constTag(LUA_TNIL), build.vmExit(kVmExitEntryGuardPc)); - break; - case LBC_TYPE_BOOLEAN: - build.inst(IrCmd::CHECK_TAG, load, build.constTag(LUA_TBOOLEAN), build.vmExit(kVmExitEntryGuardPc)); - break; - case LBC_TYPE_NUMBER: - build.inst(IrCmd::CHECK_TAG, load, build.constTag(LUA_TNUMBER), build.vmExit(kVmExitEntryGuardPc)); - break; - case LBC_TYPE_STRING: - build.inst(IrCmd::CHECK_TAG, load, build.constTag(LUA_TSTRING), build.vmExit(kVmExitEntryGuardPc)); - break; - case LBC_TYPE_TABLE: - build.inst(IrCmd::CHECK_TAG, load, build.constTag(LUA_TTABLE), build.vmExit(kVmExitEntryGuardPc)); - break; - case LBC_TYPE_FUNCTION: - build.inst(IrCmd::CHECK_TAG, load, build.constTag(LUA_TFUNCTION), build.vmExit(kVmExitEntryGuardPc)); - break; - case LBC_TYPE_THREAD: - build.inst(IrCmd::CHECK_TAG, load, build.constTag(LUA_TTHREAD), build.vmExit(kVmExitEntryGuardPc)); - break; - case LBC_TYPE_USERDATA: - build.inst(IrCmd::CHECK_TAG, load, build.constTag(LUA_TUSERDATA), build.vmExit(kVmExitEntryGuardPc)); - break; - case LBC_TYPE_VECTOR: - build.inst(IrCmd::CHECK_TAG, load, build.constTag(LUA_TVECTOR), build.vmExit(kVmExitEntryGuardPc)); - break; - case LBC_TYPE_BUFFER: - build.inst(IrCmd::CHECK_TAG, load, build.constTag(LUA_TBUFFER), build.vmExit(kVmExitEntryGuardPc)); - break; - } - - if (optional) - { - build.inst(IrCmd::JUMP, nextCheck); - build.beginBlock(nextCheck); - } - } - - // If the last argument is optional, we can skip creating a new internal block since one will already have been created. - if (!(proto->typeinfo[2 + proto->numparams - 1] & LBC_TYPE_OPTIONAL_BIT)) - { - IrOp next = build.block(IrBlockKind::Internal); - build.inst(IrCmd::JUMP, next); - - build.beginBlock(next); - } -} static bool hasTypedParameters(const BytecodeTypeInfo& typeInfo) { - CODEGEN_ASSERT(FFlag::LuauLoadTypeInfo); - for (auto el : typeInfo.argumentTypes) { if (el != LBC_TYPE_ANY) @@ -131,8 +44,6 @@ static bool hasTypedParameters(const BytecodeTypeInfo& typeInfo) static void buildArgumentTypeChecks(IrBuilder& build) { - CODEGEN_ASSERT(FFlag::LuauLoadTypeInfo); - const BytecodeTypeInfo& typeInfo = build.function.bcTypeInfo; CODEGEN_ASSERT(hasTypedParameters(typeInfo)); @@ -228,11 +139,10 @@ void IrBuilder::buildFunctionIr(Proto* proto) function.proto = proto; function.variadic = proto->is_vararg != 0; - if (FFlag::LuauLoadTypeInfo) - loadBytecodeTypeInfo(function); + loadBytecodeTypeInfo(function); // Reserve entry block - bool generateTypeChecks = FFlag::LuauLoadTypeInfo ? hasTypedParameters(function.bcTypeInfo) : hasTypedParameters_DEPRECATED(proto); + bool generateTypeChecks = hasTypedParameters(function.bcTypeInfo); IrOp entry = generateTypeChecks ? block(IrBlockKind::Internal) : IrOp{}; // Rebuild original control flow blocks @@ -247,10 +157,7 @@ void IrBuilder::buildFunctionIr(Proto* proto) { beginBlock(entry); - if (FFlag::LuauLoadTypeInfo) - buildArgumentTypeChecks(*this); - else - buildArgumentTypeChecks_DEPRECATED(*this, proto); + buildArgumentTypeChecks(*this); inst(IrCmd::JUMP, blockAtInst(0)); } @@ -544,16 +451,21 @@ void IrBuilder::translateInst(LuauOpcode op, const Instruction* pc, int i) translateInstCloseUpvals(*this, pc); break; case LOP_FASTCALL: - handleFastcallFallback(translateFastCallN(*this, pc, i, false, 0, {}), pc, i); + handleFastcallFallback(translateFastCallN(*this, pc, i, false, 0, {}, {}), pc, i); break; case LOP_FASTCALL1: - handleFastcallFallback(translateFastCallN(*this, pc, i, true, 1, undef()), pc, i); + handleFastcallFallback(translateFastCallN(*this, pc, i, true, 1, undef(), undef()), pc, i); break; case LOP_FASTCALL2: - handleFastcallFallback(translateFastCallN(*this, pc, i, true, 2, vmReg(pc[1])), pc, i); + handleFastcallFallback(translateFastCallN(*this, pc, i, true, 2, vmReg(pc[1]), undef()), pc, i); break; case LOP_FASTCALL2K: - handleFastcallFallback(translateFastCallN(*this, pc, i, true, 2, vmConst(pc[1])), pc, i); + handleFastcallFallback(translateFastCallN(*this, pc, i, true, 2, vmConst(pc[1]), undef()), pc, i); + break; + case LOP_FASTCALL3: + CODEGEN_ASSERT(FFlag::LuauCodegenFastcall3); + + handleFastcallFallback(translateFastCallN(*this, pc, i, true, 3, vmReg(pc[1] & 0xff), vmReg((pc[1] >> 8) & 0xff)), pc, i); break; case LOP_FORNPREP: translateInstForNPrep(*this, pc, i); diff --git a/CodeGen/src/IrLoweringA64.cpp b/CodeGen/src/IrLoweringA64.cpp index ea83bb99..5b333374 100644 --- a/CodeGen/src/IrLoweringA64.cpp +++ b/CodeGen/src/IrLoweringA64.cpp @@ -11,11 +11,11 @@ #include "lstate.h" #include "lgc.h" -LUAU_FASTFLAG(LuauCodegenRemoveDeadStores5) LUAU_FASTFLAG(LuauCodegenSplitDoarith) LUAU_FASTFLAG(LuauCodegenUserdataOps) LUAU_FASTFLAGVARIABLE(LuauCodegenUserdataAlloc, false) LUAU_FASTFLAGVARIABLE(LuauCodegenUserdataOpsFixA64, false) +LUAU_FASTFLAG(LuauCodegenFastcall3) namespace Luau { @@ -197,78 +197,50 @@ static void emitInvokeLibm1P(AssemblyBuilderA64& build, size_t func, int arg) build.blr(x1); } -static bool emitBuiltin( - AssemblyBuilderA64& build, IrFunction& function, IrRegAllocA64& regs, int bfid, int res, int arg, IrOp args, int nparams, int nresults) +static bool emitBuiltin(AssemblyBuilderA64& build, IrFunction& function, IrRegAllocA64& regs, int bfid, int res, int arg, int nresults) { switch (bfid) { case LBF_MATH_FREXP: { - if (FFlag::LuauCodegenRemoveDeadStores5) - { - CODEGEN_ASSERT(nparams == 1 && (nresults == 1 || nresults == 2)); - emitInvokeLibm1P(build, offsetof(NativeContext, libm_frexp), arg); - build.str(d0, mem(rBase, res * sizeof(TValue) + offsetof(TValue, value.n))); + CODEGEN_ASSERT(nresults == 1 || nresults == 2); + emitInvokeLibm1P(build, offsetof(NativeContext, libm_frexp), arg); + build.str(d0, mem(rBase, res * sizeof(TValue) + offsetof(TValue, value.n))); - RegisterA64 temp = regs.allocTemp(KindA64::w); - build.mov(temp, LUA_TNUMBER); - build.str(temp, mem(rBase, res * sizeof(TValue) + offsetof(TValue, tt))); + RegisterA64 temp = regs.allocTemp(KindA64::w); + build.mov(temp, LUA_TNUMBER); + build.str(temp, mem(rBase, res * sizeof(TValue) + offsetof(TValue, tt))); - if (nresults == 2) - { - build.ldr(w0, sTemporary); - build.scvtf(d1, w0); - build.str(d1, mem(rBase, (res + 1) * sizeof(TValue) + offsetof(TValue, value.n))); - build.str(temp, mem(rBase, (res + 1) * sizeof(TValue) + offsetof(TValue, tt))); - } - } - else + if (nresults == 2) { - CODEGEN_ASSERT(nparams == 1 && (nresults == 1 || nresults == 2)); - emitInvokeLibm1P(build, offsetof(NativeContext, libm_frexp), arg); - build.str(d0, mem(rBase, res * sizeof(TValue) + offsetof(TValue, value.n))); - if (nresults == 2) - { - build.ldr(w0, sTemporary); - build.scvtf(d1, w0); - build.str(d1, mem(rBase, (res + 1) * sizeof(TValue) + offsetof(TValue, value.n))); - } + build.ldr(w0, sTemporary); + build.scvtf(d1, w0); + build.str(d1, mem(rBase, (res + 1) * sizeof(TValue) + offsetof(TValue, value.n))); + build.str(temp, mem(rBase, (res + 1) * sizeof(TValue) + offsetof(TValue, tt))); } return true; } case LBF_MATH_MODF: { - if (FFlag::LuauCodegenRemoveDeadStores5) - { - CODEGEN_ASSERT(nparams == 1 && (nresults == 1 || nresults == 2)); - emitInvokeLibm1P(build, offsetof(NativeContext, libm_modf), arg); - build.ldr(d1, sTemporary); - build.str(d1, mem(rBase, res * sizeof(TValue) + offsetof(TValue, value.n))); + CODEGEN_ASSERT(nresults == 1 || nresults == 2); + emitInvokeLibm1P(build, offsetof(NativeContext, libm_modf), arg); + build.ldr(d1, sTemporary); + build.str(d1, mem(rBase, res * sizeof(TValue) + offsetof(TValue, value.n))); - RegisterA64 temp = regs.allocTemp(KindA64::w); - build.mov(temp, LUA_TNUMBER); - build.str(temp, mem(rBase, res * sizeof(TValue) + offsetof(TValue, tt))); + RegisterA64 temp = regs.allocTemp(KindA64::w); + build.mov(temp, LUA_TNUMBER); + build.str(temp, mem(rBase, res * sizeof(TValue) + offsetof(TValue, tt))); - if (nresults == 2) - { - build.str(d0, mem(rBase, (res + 1) * sizeof(TValue) + offsetof(TValue, value.n))); - build.str(temp, mem(rBase, (res + 1) * sizeof(TValue) + offsetof(TValue, tt))); - } - } - else + if (nresults == 2) { - CODEGEN_ASSERT(nparams == 1 && (nresults == 1 || nresults == 2)); - emitInvokeLibm1P(build, offsetof(NativeContext, libm_modf), arg); - build.ldr(d1, sTemporary); - build.str(d1, mem(rBase, res * sizeof(TValue) + offsetof(TValue, value.n))); - if (nresults == 2) - build.str(d0, mem(rBase, (res + 1) * sizeof(TValue) + offsetof(TValue, value.n))); + build.str(d0, mem(rBase, (res + 1) * sizeof(TValue) + offsetof(TValue, value.n))); + build.str(temp, mem(rBase, (res + 1) * sizeof(TValue) + offsetof(TValue, tt))); } return true; } case LBF_MATH_SIGN: { - CODEGEN_ASSERT(nparams == 1 && nresults == 1); + CODEGEN_ASSERT(nresults == 1); build.ldr(d0, mem(rBase, arg * sizeof(TValue) + offsetof(TValue, value.n))); build.fcmpz(d0); build.fmov(d0, 0.0); @@ -278,12 +250,10 @@ static bool emitBuiltin( build.fcsel(d0, d1, d0, getConditionFP(IrCondition::Less)); build.str(d0, mem(rBase, res * sizeof(TValue) + offsetof(TValue, value.n))); - if (FFlag::LuauCodegenRemoveDeadStores5) - { - RegisterA64 temp = regs.allocTemp(KindA64::w); - build.mov(temp, LUA_TNUMBER); - build.str(temp, mem(rBase, res * sizeof(TValue) + offsetof(TValue, tt))); - } + RegisterA64 temp = regs.allocTemp(KindA64::w); + build.mov(temp, LUA_TNUMBER); + build.str(temp, mem(rBase, res * sizeof(TValue) + offsetof(TValue, tt))); + return true; } @@ -1205,34 +1175,88 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) } case IrCmd::FASTCALL: regs.spill(build, index); - error |= !emitBuiltin(build, function, regs, uintOp(inst.a), vmRegOp(inst.b), vmRegOp(inst.c), inst.d, intOp(inst.e), intOp(inst.f)); + + if (FFlag::LuauCodegenFastcall3) + error |= !emitBuiltin(build, function, regs, uintOp(inst.a), vmRegOp(inst.b), vmRegOp(inst.c), intOp(inst.d)); + else + error |= !emitBuiltin(build, function, regs, uintOp(inst.a), vmRegOp(inst.b), vmRegOp(inst.c), intOp(inst.f)); + break; case IrCmd::INVOKE_FASTCALL: { - regs.spill(build, index); - build.mov(x0, rState); - build.add(x1, rBase, uint16_t(vmRegOp(inst.b) * sizeof(TValue))); - build.add(x2, rBase, uint16_t(vmRegOp(inst.c) * sizeof(TValue))); - build.mov(w3, intOp(inst.f)); // nresults - - if (inst.d.kind == IrOpKind::VmReg) - build.add(x4, rBase, uint16_t(vmRegOp(inst.d) * sizeof(TValue))); - else if (inst.d.kind == IrOpKind::VmConst) - emitAddOffset(build, x4, rConstants, vmConstOp(inst.d) * sizeof(TValue)); - else - CODEGEN_ASSERT(inst.d.kind == IrOpKind::Undef); - - // nparams - if (intOp(inst.e) == LUA_MULTRET) + if (FFlag::LuauCodegenFastcall3) { - // L->top - (ra + 1) - build.ldr(x5, mem(rState, offsetof(lua_State, top))); - build.sub(x5, x5, rBase); - build.sub(x5, x5, uint16_t((vmRegOp(inst.b) + 1) * sizeof(TValue))); - build.lsr(x5, x5, kTValueSizeLog2); + // We might need a temporary and we have to preserve it over the spill + RegisterA64 temp = regs.allocTemp(KindA64::q); + regs.spill(build, index, {temp}); + + build.mov(x0, rState); + build.add(x1, rBase, uint16_t(vmRegOp(inst.b) * sizeof(TValue))); + build.add(x2, rBase, uint16_t(vmRegOp(inst.c) * sizeof(TValue))); + build.mov(w3, intOp(inst.g)); // nresults + + // 'E' argument can only be produced by LOP_FASTCALL3 lowering + if (inst.e.kind != IrOpKind::Undef) + { + CODEGEN_ASSERT(intOp(inst.f) == 3); + + build.ldr(x4, mem(rState, offsetof(lua_State, top))); + + build.ldr(temp, mem(rBase, vmRegOp(inst.d) * sizeof(TValue))); + build.str(temp, mem(x4, 0)); + + build.ldr(temp, mem(rBase, vmRegOp(inst.e) * sizeof(TValue))); + build.str(temp, mem(x4, sizeof(TValue))); + } + else + { + if (inst.d.kind == IrOpKind::VmReg) + build.add(x4, rBase, uint16_t(vmRegOp(inst.d) * sizeof(TValue))); + else if (inst.d.kind == IrOpKind::VmConst) + emitAddOffset(build, x4, rConstants, vmConstOp(inst.d) * sizeof(TValue)); + else + CODEGEN_ASSERT(inst.d.kind == IrOpKind::Undef); + } + + // nparams + if (intOp(inst.f) == LUA_MULTRET) + { + // L->top - (ra + 1) + build.ldr(x5, mem(rState, offsetof(lua_State, top))); + build.sub(x5, x5, rBase); + build.sub(x5, x5, uint16_t((vmRegOp(inst.b) + 1) * sizeof(TValue))); + build.lsr(x5, x5, kTValueSizeLog2); + } + else + build.mov(w5, intOp(inst.f)); } else - build.mov(w5, intOp(inst.e)); + { + regs.spill(build, index); + build.mov(x0, rState); + build.add(x1, rBase, uint16_t(vmRegOp(inst.b) * sizeof(TValue))); + build.add(x2, rBase, uint16_t(vmRegOp(inst.c) * sizeof(TValue))); + build.mov(w3, intOp(inst.f)); // nresults + + if (inst.d.kind == IrOpKind::VmReg) + build.add(x4, rBase, uint16_t(vmRegOp(inst.d) * sizeof(TValue))); + else if (inst.d.kind == IrOpKind::VmConst) + emitAddOffset(build, x4, rConstants, vmConstOp(inst.d) * sizeof(TValue)); + else + CODEGEN_ASSERT(inst.d.kind == IrOpKind::Undef); + + // nparams + if (intOp(inst.e) == LUA_MULTRET) + { + // L->top - (ra + 1) + build.ldr(x5, mem(rState, offsetof(lua_State, top))); + build.sub(x5, x5, rBase); + build.sub(x5, x5, uint16_t((vmRegOp(inst.b) + 1) * sizeof(TValue))); + build.lsr(x5, x5, kTValueSizeLog2); + } + else + build.mov(w5, intOp(inst.e)); + } build.ldr(x6, mem(rNativeContext, offsetof(NativeContext, luauF_table) + uintOp(inst.a) * sizeof(luau_FastFunction))); build.blr(x6); @@ -1443,35 +1467,14 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) Label fresh; // used when guard aborts execution or jumps to a VM exit Label& fail = getTargetLabel(inst.c, fresh); - if (FFlag::LuauCodegenRemoveDeadStores5) + if (tagOp(inst.b) == 0) { - if (tagOp(inst.b) == 0) - { - build.cbnz(regOp(inst.a), fail); - } - else - { - build.cmp(regOp(inst.a), tagOp(inst.b)); - build.b(ConditionA64::NotEqual, fail); - } + build.cbnz(regOp(inst.a), fail); } else { - // To support DebugLuauAbortingChecks, CHECK_TAG with VmReg has to be handled - RegisterA64 tag = inst.a.kind == IrOpKind::VmReg ? regs.allocTemp(KindA64::w) : regOp(inst.a); - - if (inst.a.kind == IrOpKind::VmReg) - build.ldr(tag, mem(rBase, vmRegOp(inst.a) * sizeof(TValue) + offsetof(TValue, tt))); - - if (tagOp(inst.b) == 0) - { - build.cbnz(tag, fail); - } - else - { - build.cmp(tag, tagOp(inst.b)); - build.b(ConditionA64::NotEqual, fail); - } + build.cmp(regOp(inst.a), tagOp(inst.b)); + build.b(ConditionA64::NotEqual, fail); } finalizeTargetLabel(inst.c, fresh); diff --git a/CodeGen/src/IrLoweringX64.cpp b/CodeGen/src/IrLoweringX64.cpp index 00768c70..5128dce5 100644 --- a/CodeGen/src/IrLoweringX64.cpp +++ b/CodeGen/src/IrLoweringX64.cpp @@ -17,6 +17,7 @@ LUAU_FASTFLAG(LuauCodegenUserdataOps) LUAU_FASTFLAG(LuauCodegenUserdataAlloc) +LUAU_FASTFLAG(LuauCodegenFastcall3) namespace Luau { @@ -1008,9 +1009,10 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) case IrCmd::FASTCALL: { - OperandX64 arg2 = inst.d.kind != IrOpKind::Undef ? memRegDoubleOp(inst.d) : OperandX64{0}; - - emitBuiltin(regs, build, uintOp(inst.a), vmRegOp(inst.b), vmRegOp(inst.c), arg2, intOp(inst.e), intOp(inst.f)); + if (FFlag::LuauCodegenFastcall3) + emitBuiltin(regs, build, uintOp(inst.a), vmRegOp(inst.b), vmRegOp(inst.c), intOp(inst.d)); + else + emitBuiltin(regs, build, uintOp(inst.a), vmRegOp(inst.b), vmRegOp(inst.c), intOp(inst.f)); break; } case IrCmd::INVOKE_FASTCALL: @@ -1018,25 +1020,49 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) unsigned bfid = uintOp(inst.a); OperandX64 args = 0; + ScopedRegX64 argsAlt{regs}; - if (inst.d.kind == IrOpKind::VmReg) - args = luauRegAddress(vmRegOp(inst.d)); - else if (inst.d.kind == IrOpKind::VmConst) - args = luauConstantAddress(vmConstOp(inst.d)); + // 'E' argument can only be produced by LOP_FASTCALL3 + if (FFlag::LuauCodegenFastcall3 && inst.e.kind != IrOpKind::Undef) + { + CODEGEN_ASSERT(intOp(inst.f) == 3); + + ScopedRegX64 tmp{regs, SizeX64::xmmword}; + argsAlt.alloc(SizeX64::qword); + + build.mov(argsAlt.reg, qword[rState + offsetof(lua_State, top)]); + + build.vmovups(tmp.reg, luauReg(vmRegOp(inst.d))); + build.vmovups(xmmword[argsAlt.reg], tmp.reg); + + build.vmovups(tmp.reg, luauReg(vmRegOp(inst.e))); + build.vmovups(xmmword[argsAlt.reg + sizeof(TValue)], tmp.reg); + } else - CODEGEN_ASSERT(inst.d.kind == IrOpKind::Undef); + { + if (inst.d.kind == IrOpKind::VmReg) + args = luauRegAddress(vmRegOp(inst.d)); + else if (inst.d.kind == IrOpKind::VmConst) + args = luauConstantAddress(vmConstOp(inst.d)); + else + CODEGEN_ASSERT(inst.d.kind == IrOpKind::Undef); + } int ra = vmRegOp(inst.b); int arg = vmRegOp(inst.c); - int nparams = intOp(inst.e); - int nresults = intOp(inst.f); + int nparams = intOp(FFlag::LuauCodegenFastcall3 ? inst.f : inst.e); + int nresults = intOp(FFlag::LuauCodegenFastcall3 ? inst.g : inst.f); IrCallWrapperX64 callWrap(regs, build, index); callWrap.addArgument(SizeX64::qword, rState); callWrap.addArgument(SizeX64::qword, luauRegAddress(ra)); callWrap.addArgument(SizeX64::qword, luauRegAddress(arg)); callWrap.addArgument(SizeX64::dword, nresults); - callWrap.addArgument(SizeX64::qword, args); + + if (FFlag::LuauCodegenFastcall3 && inst.e.kind != IrOpKind::Undef) + callWrap.addArgument(SizeX64::qword, argsAlt); + else + callWrap.addArgument(SizeX64::qword, args); if (nparams == LUA_MULTRET) { diff --git a/CodeGen/src/IrTranslateBuiltins.cpp b/CodeGen/src/IrTranslateBuiltins.cpp index bec5deea..668bdfe0 100644 --- a/CodeGen/src/IrTranslateBuiltins.cpp +++ b/CodeGen/src/IrTranslateBuiltins.cpp @@ -8,7 +8,7 @@ #include -LUAU_FASTFLAG(LuauCodegenRemoveDeadStores5) +LUAU_FASTFLAG(LuauCodegenFastcall3) // TODO: when nresults is less than our actual result count, we can skip computing/writing unused results @@ -46,19 +46,17 @@ static BuiltinImplResult translateBuiltinNumberToNumber( return {BuiltinImplType::None, -1}; builtinCheckDouble(build, build.vmReg(arg), pcpos); - build.inst(IrCmd::FASTCALL, build.constUint(bfid), build.vmReg(ra), build.vmReg(arg), args, build.constInt(1), build.constInt(1)); - if (!FFlag::LuauCodegenRemoveDeadStores5) - { - if (ra != arg) - build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER)); - } + if (FFlag::LuauCodegenFastcall3) + build.inst(IrCmd::FASTCALL, build.constUint(bfid), build.vmReg(ra), build.vmReg(arg), build.constInt(1)); + else + build.inst(IrCmd::FASTCALL, build.constUint(bfid), build.vmReg(ra), build.vmReg(arg), args, build.constInt(1), build.constInt(1)); return {BuiltinImplType::Full, 1}; } static BuiltinImplResult translateBuiltinNumberToNumberLibm( - IrBuilder& build, LuauBuiltinFunction bfid, int nparams, int ra, int arg, IrOp args, int nresults, int pcpos) + IrBuilder& build, LuauBuiltinFunction bfid, int nparams, int ra, int arg, int nresults, int pcpos) { if (nparams < 1 || nresults > 1) return {BuiltinImplType::None, -1}; @@ -109,17 +107,12 @@ static BuiltinImplResult translateBuiltinNumberTo2Number( return {BuiltinImplType::None, -1}; builtinCheckDouble(build, build.vmReg(arg), pcpos); - build.inst( - IrCmd::FASTCALL, build.constUint(bfid), build.vmReg(ra), build.vmReg(arg), args, build.constInt(1), build.constInt(nresults == 1 ? 1 : 2)); - if (!FFlag::LuauCodegenRemoveDeadStores5) - { - if (ra != arg) - build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER)); - - if (nresults != 1) - build.inst(IrCmd::STORE_TAG, build.vmReg(ra + 1), build.constTag(LUA_TNUMBER)); - } + if (FFlag::LuauCodegenFastcall3) + build.inst(IrCmd::FASTCALL, build.constUint(bfid), build.vmReg(ra), build.vmReg(arg), build.constInt(nresults == 1 ? 1 : 2)); + else + build.inst(IrCmd::FASTCALL, build.constUint(bfid), build.vmReg(ra), build.vmReg(arg), build.undef(), build.constInt(1), + build.constInt(nresults == 1 ? 1 : 2)); return {BuiltinImplType::Full, 2}; } @@ -198,7 +191,8 @@ static BuiltinImplResult translateBuiltinMathLog(IrBuilder& build, int nparams, return {BuiltinImplType::Full, 1}; } -static BuiltinImplResult translateBuiltinMathMinMax(IrBuilder& build, IrCmd cmd, int nparams, int ra, int arg, IrOp args, int nresults, int pcpos) +static BuiltinImplResult translateBuiltinMathMinMax( + IrBuilder& build, IrCmd cmd, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, int pcpos) { if (nparams < 2 || nparams > kMinMaxUnrolledParams || nresults > 1) return {BuiltinImplType::None, -1}; @@ -206,7 +200,10 @@ static BuiltinImplResult translateBuiltinMathMinMax(IrBuilder& build, IrCmd cmd, builtinCheckDouble(build, build.vmReg(arg), pcpos); builtinCheckDouble(build, args, pcpos); - for (int i = 3; i <= nparams; ++i) + if (FFlag::LuauCodegenFastcall3 && nparams >= 3) + builtinCheckDouble(build, arg3, pcpos); + + for (int i = (FFlag::LuauCodegenFastcall3 ? 4 : 3); i <= nparams; ++i) builtinCheckDouble(build, build.vmReg(vmRegOp(args) + (i - 2)), pcpos); IrOp varg1 = builtinLoadDouble(build, build.vmReg(arg)); @@ -214,7 +211,13 @@ static BuiltinImplResult translateBuiltinMathMinMax(IrBuilder& build, IrCmd cmd, IrOp res = build.inst(cmd, varg2, varg1); // Swapped arguments are required for consistency with VM builtins - for (int i = 3; i <= nparams; ++i) + if (FFlag::LuauCodegenFastcall3 && nparams >= 3) + { + IrOp arg = builtinLoadDouble(build, arg3); + res = build.inst(cmd, arg, res); + } + + for (int i = (FFlag::LuauCodegenFastcall3 ? 4 : 3); i <= nparams; ++i) { IrOp arg = builtinLoadDouble(build, build.vmReg(vmRegOp(args) + (i - 2))); res = build.inst(cmd, arg, res); @@ -228,7 +231,8 @@ static BuiltinImplResult translateBuiltinMathMinMax(IrBuilder& build, IrCmd cmd, return {BuiltinImplType::Full, 1}; } -static BuiltinImplResult translateBuiltinMathClamp(IrBuilder& build, int nparams, int ra, int arg, IrOp args, int nresults, IrOp fallback, int pcpos) +static BuiltinImplResult translateBuiltinMathClamp( + IrBuilder& build, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, IrOp fallback, int pcpos) { if (nparams < 3 || nresults > 1) return {BuiltinImplType::None, -1}; @@ -239,10 +243,10 @@ static BuiltinImplResult translateBuiltinMathClamp(IrBuilder& build, int nparams builtinCheckDouble(build, build.vmReg(arg), pcpos); builtinCheckDouble(build, args, pcpos); - builtinCheckDouble(build, build.vmReg(vmRegOp(args) + 1), pcpos); + builtinCheckDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(vmRegOp(args) + 1), pcpos); IrOp min = builtinLoadDouble(build, args); - IrOp max = builtinLoadDouble(build, build.vmReg(vmRegOp(args) + 1)); + IrOp max = builtinLoadDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(vmRegOp(args) + 1)); build.inst(IrCmd::JUMP_CMP_NUM, min, max, build.cond(IrCondition::NotLessEqual), fallback, block); build.beginBlock(block); @@ -305,7 +309,7 @@ static BuiltinImplResult translateBuiltinTypeof(IrBuilder& build, int nparams, i } static BuiltinImplResult translateBuiltinBit32BinaryOp( - IrBuilder& build, IrCmd cmd, bool btest, int nparams, int ra, int arg, IrOp args, int nresults, int pcpos) + IrBuilder& build, IrCmd cmd, bool btest, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, int pcpos) { if (nparams < 2 || nparams > kBit32BinaryOpUnrolledParams || nresults > 1) return {BuiltinImplType::None, -1}; @@ -313,7 +317,10 @@ static BuiltinImplResult translateBuiltinBit32BinaryOp( builtinCheckDouble(build, build.vmReg(arg), pcpos); builtinCheckDouble(build, args, pcpos); - for (int i = 3; i <= nparams; ++i) + if (FFlag::LuauCodegenFastcall3 && nparams >= 3) + builtinCheckDouble(build, arg3, pcpos); + + for (int i = (FFlag::LuauCodegenFastcall3 ? 4 : 3); i <= nparams; ++i) builtinCheckDouble(build, build.vmReg(vmRegOp(args) + (i - 2)), pcpos); IrOp va = builtinLoadDouble(build, build.vmReg(arg)); @@ -324,7 +331,15 @@ static BuiltinImplResult translateBuiltinBit32BinaryOp( IrOp res = build.inst(cmd, vaui, vbui); - for (int i = 3; i <= nparams; ++i) + if (FFlag::LuauCodegenFastcall3 && nparams >= 3) + { + IrOp vc = builtinLoadDouble(build, arg3); + IrOp arg = build.inst(IrCmd::NUM_TO_UINT, vc); + + res = build.inst(cmd, res, arg); + } + + for (int i = (FFlag::LuauCodegenFastcall3 ? 4 : 3); i <= nparams; ++i) { IrOp vc = builtinLoadDouble(build, build.vmReg(vmRegOp(args) + (i - 2))); IrOp arg = build.inst(IrCmd::NUM_TO_UINT, vc); @@ -449,7 +464,7 @@ static BuiltinImplResult translateBuiltinBit32Rotate(IrBuilder& build, IrCmd cmd } static BuiltinImplResult translateBuiltinBit32Extract( - IrBuilder& build, int nparams, int ra, int arg, IrOp args, int nresults, IrOp fallback, int pcpos) + IrBuilder& build, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, IrOp fallback, int pcpos) { if (nparams < 2 || nresults > 1) return {BuiltinImplType::None, -1}; @@ -497,8 +512,8 @@ static BuiltinImplResult translateBuiltinBit32Extract( { IrOp f = build.inst(IrCmd::NUM_TO_INT, vb); - builtinCheckDouble(build, build.vmReg(args.index + 1), pcpos); - IrOp vc = builtinLoadDouble(build, build.vmReg(args.index + 1)); + builtinCheckDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(args.index + 1), pcpos); + IrOp vc = builtinLoadDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(args.index + 1)); IrOp w = build.inst(IrCmd::NUM_TO_INT, vc); IrOp block1 = build.block(IrBlockKind::Internal); @@ -587,18 +602,18 @@ static BuiltinImplResult translateBuiltinBit32Unary(IrBuilder& build, IrCmd cmd, } static BuiltinImplResult translateBuiltinBit32Replace( - IrBuilder& build, int nparams, int ra, int arg, IrOp args, int nresults, IrOp fallback, int pcpos) + IrBuilder& build, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, IrOp fallback, int pcpos) { if (nparams < 3 || nresults > 1) return {BuiltinImplType::None, -1}; builtinCheckDouble(build, build.vmReg(arg), pcpos); builtinCheckDouble(build, args, pcpos); - builtinCheckDouble(build, build.vmReg(args.index + 1), pcpos); + builtinCheckDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(args.index + 1), pcpos); IrOp va = builtinLoadDouble(build, build.vmReg(arg)); IrOp vb = builtinLoadDouble(build, args); - IrOp vc = builtinLoadDouble(build, build.vmReg(args.index + 1)); + IrOp vc = builtinLoadDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(args.index + 1)); IrOp n = build.inst(IrCmd::NUM_TO_UINT, va); IrOp v = build.inst(IrCmd::NUM_TO_UINT, vb); @@ -623,8 +638,8 @@ static BuiltinImplResult translateBuiltinBit32Replace( } else { - builtinCheckDouble(build, build.vmReg(args.index + 2), pcpos); - IrOp vd = builtinLoadDouble(build, build.vmReg(args.index + 2)); + builtinCheckDouble(build, FFlag::LuauCodegenFastcall3 ? build.vmReg(vmRegOp(args) + 2) : build.vmReg(args.index + 2), pcpos); + IrOp vd = builtinLoadDouble(build, FFlag::LuauCodegenFastcall3 ? build.vmReg(vmRegOp(args) + 2) : build.vmReg(args.index + 2)); IrOp w = build.inst(IrCmd::NUM_TO_INT, vd); IrOp block1 = build.block(IrBlockKind::Internal); @@ -661,7 +676,7 @@ static BuiltinImplResult translateBuiltinBit32Replace( return {BuiltinImplType::UsesFallback, 1}; } -static BuiltinImplResult translateBuiltinVector(IrBuilder& build, int nparams, int ra, int arg, IrOp args, int nresults, int pcpos) +static BuiltinImplResult translateBuiltinVector(IrBuilder& build, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, int pcpos) { if (nparams < 3 || nresults > 1) return {BuiltinImplType::None, -1}; @@ -670,11 +685,11 @@ static BuiltinImplResult translateBuiltinVector(IrBuilder& build, int nparams, i builtinCheckDouble(build, build.vmReg(arg), pcpos); builtinCheckDouble(build, args, pcpos); - builtinCheckDouble(build, build.vmReg(vmRegOp(args) + 1), pcpos); + builtinCheckDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(vmRegOp(args) + 1), pcpos); IrOp x = builtinLoadDouble(build, build.vmReg(arg)); IrOp y = builtinLoadDouble(build, args); - IrOp z = builtinLoadDouble(build, build.vmReg(vmRegOp(args) + 1)); + IrOp z = builtinLoadDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(vmRegOp(args) + 1)); build.inst(IrCmd::STORE_VECTOR, build.vmReg(ra), x, y, z); build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TVECTOR)); @@ -736,13 +751,14 @@ static BuiltinImplResult translateBuiltinStringLen(IrBuilder& build, int nparams return {BuiltinImplType::Full, 1}; } -static void translateBufferArgsAndCheckBounds(IrBuilder& build, int nparams, int arg, IrOp args, int size, int pcpos, IrOp& buf, IrOp& intIndex) +static void translateBufferArgsAndCheckBounds( + IrBuilder& build, int nparams, int arg, IrOp args, IrOp arg3, int size, int pcpos, IrOp& buf, IrOp& intIndex) { build.loadAndCheckTag(build.vmReg(arg), LUA_TBUFFER, build.vmExit(pcpos)); builtinCheckDouble(build, args, pcpos); if (nparams == 3) - builtinCheckDouble(build, build.vmReg(vmRegOp(args) + 1), pcpos); + builtinCheckDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(vmRegOp(args) + 1), pcpos); buf = build.inst(IrCmd::LOAD_POINTER, build.vmReg(arg)); @@ -753,13 +769,13 @@ static void translateBufferArgsAndCheckBounds(IrBuilder& build, int nparams, int } static BuiltinImplResult translateBuiltinBufferRead( - IrBuilder& build, int nparams, int ra, int arg, IrOp args, int nresults, int pcpos, IrCmd readCmd, int size, IrCmd convCmd) + IrBuilder& build, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, int pcpos, IrCmd readCmd, int size, IrCmd convCmd) { if (nparams < 2 || nresults > 1) return {BuiltinImplType::None, -1}; IrOp buf, intIndex; - translateBufferArgsAndCheckBounds(build, nparams, arg, args, size, pcpos, buf, intIndex); + translateBufferArgsAndCheckBounds(build, nparams, arg, args, arg3, size, pcpos, buf, intIndex); IrOp result = build.inst(readCmd, buf, intIndex); build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), convCmd == IrCmd::NOP ? result : build.inst(convCmd, result)); @@ -769,21 +785,22 @@ static BuiltinImplResult translateBuiltinBufferRead( } static BuiltinImplResult translateBuiltinBufferWrite( - IrBuilder& build, int nparams, int ra, int arg, IrOp args, int nresults, int pcpos, IrCmd writeCmd, int size, IrCmd convCmd) + IrBuilder& build, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, int pcpos, IrCmd writeCmd, int size, IrCmd convCmd) { if (nparams < 3 || nresults > 0) return {BuiltinImplType::None, -1}; IrOp buf, intIndex; - translateBufferArgsAndCheckBounds(build, nparams, arg, args, size, pcpos, buf, intIndex); + translateBufferArgsAndCheckBounds(build, nparams, arg, args, arg3, size, pcpos, buf, intIndex); - IrOp numValue = builtinLoadDouble(build, build.vmReg(vmRegOp(args) + 1)); + IrOp numValue = builtinLoadDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(vmRegOp(args) + 1)); build.inst(writeCmd, buf, intIndex, convCmd == IrCmd::NOP ? numValue : build.inst(convCmd, numValue)); return {BuiltinImplType::Full, 0}; } -BuiltinImplResult translateBuiltin(IrBuilder& build, int bfid, int ra, int arg, IrOp args, int nparams, int nresults, IrOp fallback, int pcpos) +BuiltinImplResult translateBuiltin( + IrBuilder& build, int bfid, int ra, int arg, IrOp args, IrOp arg3, int nparams, int nresults, IrOp fallback, int pcpos) { // Builtins are not allowed to handle variadic arguments if (nparams == LUA_MULTRET) @@ -800,11 +817,11 @@ BuiltinImplResult translateBuiltin(IrBuilder& build, int bfid, int ra, int arg, case LBF_MATH_LOG: return translateBuiltinMathLog(build, nparams, ra, arg, args, nresults, pcpos); case LBF_MATH_MIN: - return translateBuiltinMathMinMax(build, IrCmd::MIN_NUM, nparams, ra, arg, args, nresults, pcpos); + return translateBuiltinMathMinMax(build, IrCmd::MIN_NUM, nparams, ra, arg, args, arg3, nresults, pcpos); case LBF_MATH_MAX: - return translateBuiltinMathMinMax(build, IrCmd::MAX_NUM, nparams, ra, arg, args, nresults, pcpos); + return translateBuiltinMathMinMax(build, IrCmd::MAX_NUM, nparams, ra, arg, args, arg3, nresults, pcpos); case LBF_MATH_CLAMP: - return translateBuiltinMathClamp(build, nparams, ra, arg, args, nresults, fallback, pcpos); + return translateBuiltinMathClamp(build, nparams, ra, arg, args, arg3, nresults, fallback, pcpos); case LBF_MATH_FLOOR: return translateBuiltinMathUnary(build, IrCmd::FLOOR_NUM, nparams, ra, arg, nresults, pcpos); case LBF_MATH_CEIL: @@ -826,7 +843,7 @@ BuiltinImplResult translateBuiltin(IrBuilder& build, int bfid, int ra, int arg, case LBF_MATH_TAN: case LBF_MATH_TANH: case LBF_MATH_LOG10: - return translateBuiltinNumberToNumberLibm(build, LuauBuiltinFunction(bfid), nparams, ra, arg, args, nresults, pcpos); + return translateBuiltinNumberToNumberLibm(build, LuauBuiltinFunction(bfid), nparams, ra, arg, nresults, pcpos); case LBF_MATH_SIGN: return translateBuiltinNumberToNumber(build, LuauBuiltinFunction(bfid), nparams, ra, arg, args, nresults, pcpos); case LBF_MATH_POW: @@ -838,13 +855,13 @@ BuiltinImplResult translateBuiltin(IrBuilder& build, int bfid, int ra, int arg, case LBF_MATH_MODF: return translateBuiltinNumberTo2Number(build, LuauBuiltinFunction(bfid), nparams, ra, arg, args, nresults, pcpos); case LBF_BIT32_BAND: - return translateBuiltinBit32BinaryOp(build, IrCmd::BITAND_UINT, /* btest= */ false, nparams, ra, arg, args, nresults, pcpos); + return translateBuiltinBit32BinaryOp(build, IrCmd::BITAND_UINT, /* btest= */ false, nparams, ra, arg, args, arg3, nresults, pcpos); case LBF_BIT32_BOR: - return translateBuiltinBit32BinaryOp(build, IrCmd::BITOR_UINT, /* btest= */ false, nparams, ra, arg, args, nresults, pcpos); + return translateBuiltinBit32BinaryOp(build, IrCmd::BITOR_UINT, /* btest= */ false, nparams, ra, arg, args, arg3, nresults, pcpos); case LBF_BIT32_BXOR: - return translateBuiltinBit32BinaryOp(build, IrCmd::BITXOR_UINT, /* btest= */ false, nparams, ra, arg, args, nresults, pcpos); + return translateBuiltinBit32BinaryOp(build, IrCmd::BITXOR_UINT, /* btest= */ false, nparams, ra, arg, args, arg3, nresults, pcpos); case LBF_BIT32_BTEST: - return translateBuiltinBit32BinaryOp(build, IrCmd::BITAND_UINT, /* btest= */ true, nparams, ra, arg, args, nresults, pcpos); + return translateBuiltinBit32BinaryOp(build, IrCmd::BITAND_UINT, /* btest= */ true, nparams, ra, arg, args, arg3, nresults, pcpos); case LBF_BIT32_BNOT: return translateBuiltinBit32Bnot(build, nparams, ra, arg, args, nresults, pcpos); case LBF_BIT32_LSHIFT: @@ -858,7 +875,7 @@ BuiltinImplResult translateBuiltin(IrBuilder& build, int bfid, int ra, int arg, case LBF_BIT32_RROTATE: return translateBuiltinBit32Rotate(build, IrCmd::BITRROTATE_UINT, nparams, ra, arg, args, nresults, pcpos); case LBF_BIT32_EXTRACT: - return translateBuiltinBit32Extract(build, nparams, ra, arg, args, nresults, fallback, pcpos); + return translateBuiltinBit32Extract(build, nparams, ra, arg, args, arg3, nresults, fallback, pcpos); case LBF_BIT32_EXTRACTK: return translateBuiltinBit32ExtractK(build, nparams, ra, arg, args, nresults, pcpos); case LBF_BIT32_COUNTLZ: @@ -866,13 +883,13 @@ BuiltinImplResult translateBuiltin(IrBuilder& build, int bfid, int ra, int arg, case LBF_BIT32_COUNTRZ: return translateBuiltinBit32Unary(build, IrCmd::BITCOUNTRZ_UINT, nparams, ra, arg, args, nresults, pcpos); case LBF_BIT32_REPLACE: - return translateBuiltinBit32Replace(build, nparams, ra, arg, args, nresults, fallback, pcpos); + return translateBuiltinBit32Replace(build, nparams, ra, arg, args, arg3, nresults, fallback, pcpos); case LBF_TYPE: return translateBuiltinType(build, nparams, ra, arg, args, nresults); case LBF_TYPEOF: return translateBuiltinTypeof(build, nparams, ra, arg, args, nresults); case LBF_VECTOR: - return translateBuiltinVector(build, nparams, ra, arg, args, nresults, pcpos); + return translateBuiltinVector(build, nparams, ra, arg, args, arg3, nresults, pcpos); case LBF_TABLE_INSERT: return translateBuiltinTableInsert(build, nparams, ra, arg, args, nresults, pcpos); case LBF_STRING_LEN: @@ -880,31 +897,31 @@ BuiltinImplResult translateBuiltin(IrBuilder& build, int bfid, int ra, int arg, case LBF_BIT32_BYTESWAP: return translateBuiltinBit32Unary(build, IrCmd::BYTESWAP_UINT, nparams, ra, arg, args, nresults, pcpos); case LBF_BUFFER_READI8: - return translateBuiltinBufferRead(build, nparams, ra, arg, args, nresults, pcpos, IrCmd::BUFFER_READI8, 1, IrCmd::INT_TO_NUM); + return translateBuiltinBufferRead(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_READI8, 1, IrCmd::INT_TO_NUM); case LBF_BUFFER_READU8: - return translateBuiltinBufferRead(build, nparams, ra, arg, args, nresults, pcpos, IrCmd::BUFFER_READU8, 1, IrCmd::INT_TO_NUM); + return translateBuiltinBufferRead(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_READU8, 1, IrCmd::INT_TO_NUM); case LBF_BUFFER_WRITEU8: - return translateBuiltinBufferWrite(build, nparams, ra, arg, args, nresults, pcpos, IrCmd::BUFFER_WRITEI8, 1, IrCmd::NUM_TO_UINT); + return translateBuiltinBufferWrite(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_WRITEI8, 1, IrCmd::NUM_TO_UINT); case LBF_BUFFER_READI16: - return translateBuiltinBufferRead(build, nparams, ra, arg, args, nresults, pcpos, IrCmd::BUFFER_READI16, 2, IrCmd::INT_TO_NUM); + return translateBuiltinBufferRead(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_READI16, 2, IrCmd::INT_TO_NUM); case LBF_BUFFER_READU16: - return translateBuiltinBufferRead(build, nparams, ra, arg, args, nresults, pcpos, IrCmd::BUFFER_READU16, 2, IrCmd::INT_TO_NUM); + return translateBuiltinBufferRead(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_READU16, 2, IrCmd::INT_TO_NUM); case LBF_BUFFER_WRITEU16: - return translateBuiltinBufferWrite(build, nparams, ra, arg, args, nresults, pcpos, IrCmd::BUFFER_WRITEI16, 2, IrCmd::NUM_TO_UINT); + return translateBuiltinBufferWrite(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_WRITEI16, 2, IrCmd::NUM_TO_UINT); case LBF_BUFFER_READI32: - return translateBuiltinBufferRead(build, nparams, ra, arg, args, nresults, pcpos, IrCmd::BUFFER_READI32, 4, IrCmd::INT_TO_NUM); + return translateBuiltinBufferRead(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_READI32, 4, IrCmd::INT_TO_NUM); case LBF_BUFFER_READU32: - return translateBuiltinBufferRead(build, nparams, ra, arg, args, nresults, pcpos, IrCmd::BUFFER_READI32, 4, IrCmd::UINT_TO_NUM); + return translateBuiltinBufferRead(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_READI32, 4, IrCmd::UINT_TO_NUM); case LBF_BUFFER_WRITEU32: - return translateBuiltinBufferWrite(build, nparams, ra, arg, args, nresults, pcpos, IrCmd::BUFFER_WRITEI32, 4, IrCmd::NUM_TO_UINT); + return translateBuiltinBufferWrite(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_WRITEI32, 4, IrCmd::NUM_TO_UINT); case LBF_BUFFER_READF32: - return translateBuiltinBufferRead(build, nparams, ra, arg, args, nresults, pcpos, IrCmd::BUFFER_READF32, 4, IrCmd::NOP); + return translateBuiltinBufferRead(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_READF32, 4, IrCmd::NOP); case LBF_BUFFER_WRITEF32: - return translateBuiltinBufferWrite(build, nparams, ra, arg, args, nresults, pcpos, IrCmd::BUFFER_WRITEF32, 4, IrCmd::NOP); + return translateBuiltinBufferWrite(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_WRITEF32, 4, IrCmd::NOP); case LBF_BUFFER_READF64: - return translateBuiltinBufferRead(build, nparams, ra, arg, args, nresults, pcpos, IrCmd::BUFFER_READF64, 8, IrCmd::NOP); + return translateBuiltinBufferRead(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_READF64, 8, IrCmd::NOP); case LBF_BUFFER_WRITEF64: - return translateBuiltinBufferWrite(build, nparams, ra, arg, args, nresults, pcpos, IrCmd::BUFFER_WRITEF64, 8, IrCmd::NOP); + return translateBuiltinBufferWrite(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_WRITEF64, 8, IrCmd::NOP); default: return {BuiltinImplType::None, -1}; } diff --git a/CodeGen/src/IrTranslateBuiltins.h b/CodeGen/src/IrTranslateBuiltins.h index 8ae64b94..54a05aba 100644 --- a/CodeGen/src/IrTranslateBuiltins.h +++ b/CodeGen/src/IrTranslateBuiltins.h @@ -22,7 +22,8 @@ struct BuiltinImplResult int actualResultCount; }; -BuiltinImplResult translateBuiltin(IrBuilder& build, int bfid, int ra, int arg, IrOp args, int nparams, int nresults, IrOp fallback, int pcpos); +BuiltinImplResult translateBuiltin( + IrBuilder& build, int bfid, int ra, int arg, IrOp args, IrOp arg3, int nparams, int nresults, IrOp fallback, int pcpos); } // namespace CodeGen } // namespace Luau diff --git a/CodeGen/src/IrTranslation.cpp b/CodeGen/src/IrTranslation.cpp index 5798f3e9..e06f14f8 100644 --- a/CodeGen/src/IrTranslation.cpp +++ b/CodeGen/src/IrTranslation.cpp @@ -13,9 +13,9 @@ #include "lstate.h" #include "ltm.h" -LUAU_FASTFLAGVARIABLE(LuauCodegenDirectUserdataFlow, false) LUAU_FASTFLAG(LuauCodegenAnalyzeHostVectorOps) LUAU_FASTFLAG(LuauCodegenUserdataOps) +LUAU_FASTFLAG(LuauCodegenFastcall3) namespace Luau { @@ -743,7 +743,7 @@ void translateInstCloseUpvals(IrBuilder& build, const Instruction* pc) build.inst(IrCmd::CLOSE_UPVALS, build.vmReg(ra)); } -IrOp translateFastCallN(IrBuilder& build, const Instruction* pc, int pcpos, bool customParams, int customParamCount, IrOp customArgs) +IrOp translateFastCallN(IrBuilder& build, const Instruction* pc, int pcpos, bool customParams, int customParamCount, IrOp customArgs, IrOp customArg3) { LuauOpcode opcode = LuauOpcode(LUAU_INSN_OP(*pc)); int bfid = LUAU_INSN_A(*pc); @@ -769,13 +769,15 @@ IrOp translateFastCallN(IrBuilder& build, const Instruction* pc, int pcpos, bool builtinArgs = build.constDouble(protok.value.n); } + IrOp builtinArg3 = FFlag::LuauCodegenFastcall3 ? (customParams ? customArg3 : build.vmReg(ra + 3)) : IrOp{}; + IrOp fallback = build.block(IrBlockKind::Fallback); // In unsafe environment, instead of retrying fastcall at 'pcpos' we side-exit directly to fallback sequence build.inst(IrCmd::CHECK_SAFE_ENV, build.vmExit(pcpos + getOpLength(opcode))); - BuiltinImplResult br = - translateBuiltin(build, LuauBuiltinFunction(bfid), ra, arg, builtinArgs, nparams, nresults, fallback, pcpos + getOpLength(opcode)); + BuiltinImplResult br = translateBuiltin( + build, LuauBuiltinFunction(bfid), ra, arg, builtinArgs, builtinArg3, nparams, nresults, fallback, pcpos + getOpLength(opcode)); if (br.type != BuiltinImplType::None) { @@ -792,6 +794,22 @@ IrOp translateFastCallN(IrBuilder& build, const Instruction* pc, int pcpos, bool return build.undef(); } } + else if (FFlag::LuauCodegenFastcall3) + { + IrOp arg3 = customParams ? customArg3 : build.undef(); + + // TODO: we can skip saving pc for some well-behaved builtins which we didn't inline + build.inst(IrCmd::SET_SAVEDPC, build.constUint(pcpos + getOpLength(opcode))); + + IrOp res = build.inst(IrCmd::INVOKE_FASTCALL, build.constUint(bfid), build.vmReg(ra), build.vmReg(arg), args, arg3, build.constInt(nparams), + build.constInt(nresults)); + build.inst(IrCmd::CHECK_FASTCALL_RES, res, fallback); + + if (nresults == LUA_MULTRET) + build.inst(IrCmd::ADJUST_STACK_TO_REG, build.vmReg(ra), res); + else if (nparams == LUA_MULTRET) + build.inst(IrCmd::ADJUST_STACK_TO_TOP); + } else { // TODO: we can skip saving pc for some well-behaved builtins which we didn't inline @@ -1277,7 +1295,7 @@ void translateInstGetTableKS(IrBuilder& build, const Instruction* pc, int pcpos) return; } - if (FFlag::LuauCodegenDirectUserdataFlow && (FFlag::LuauCodegenUserdataOps ? isUserdataBytecodeType(bcTypes.a) : bcTypes.a == LBC_TYPE_USERDATA)) + if (FFlag::LuauCodegenUserdataOps ? isUserdataBytecodeType(bcTypes.a) : bcTypes.a == LBC_TYPE_USERDATA) { build.inst(IrCmd::CHECK_TAG, tb, build.constTag(LUA_TUSERDATA), build.vmExit(pcpos)); @@ -1324,7 +1342,7 @@ void translateInstSetTableKS(IrBuilder& build, const Instruction* pc, int pcpos) IrOp tb = build.inst(IrCmd::LOAD_TAG, build.vmReg(rb)); - if (FFlag::LuauCodegenDirectUserdataFlow && (FFlag::LuauCodegenUserdataOps ? isUserdataBytecodeType(bcTypes.a) : bcTypes.a == LBC_TYPE_USERDATA)) + if (FFlag::LuauCodegenUserdataOps ? isUserdataBytecodeType(bcTypes.a) : bcTypes.a == LBC_TYPE_USERDATA) { build.inst(IrCmd::CHECK_TAG, tb, build.constTag(LUA_TUSERDATA), build.vmExit(pcpos)); @@ -1446,7 +1464,7 @@ bool translateInstNamecall(IrBuilder& build, const Instruction* pc, int pcpos) BytecodeTypes bcTypes = build.function.getBytecodeTypesAt(pcpos); - if (FFlag::LuauCodegenDirectUserdataFlow && bcTypes.a == LBC_TYPE_VECTOR) + if (bcTypes.a == LBC_TYPE_VECTOR) { build.loadAndCheckTag(build.vmReg(rb), LUA_TVECTOR, build.vmExit(pcpos)); @@ -1470,7 +1488,7 @@ bool translateInstNamecall(IrBuilder& build, const Instruction* pc, int pcpos) return false; } - if (FFlag::LuauCodegenDirectUserdataFlow && (FFlag::LuauCodegenUserdataOps ? isUserdataBytecodeType(bcTypes.a) : bcTypes.a == LBC_TYPE_USERDATA)) + if (FFlag::LuauCodegenUserdataOps ? isUserdataBytecodeType(bcTypes.a) : bcTypes.a == LBC_TYPE_USERDATA) { build.loadAndCheckTag(build.vmReg(rb), LUA_TUSERDATA, build.vmExit(pcpos)); @@ -1499,8 +1517,7 @@ bool translateInstNamecall(IrBuilder& build, const Instruction* pc, int pcpos) IrOp firstFastPathSuccess = build.block(IrBlockKind::Internal); IrOp secondFastPath = build.block(IrBlockKind::Internal); - build.loadAndCheckTag( - build.vmReg(rb), LUA_TTABLE, FFlag::LuauCodegenDirectUserdataFlow && bcTypes.a == LBC_TYPE_TABLE ? build.vmExit(pcpos) : fallback); + build.loadAndCheckTag(build.vmReg(rb), LUA_TTABLE, bcTypes.a == LBC_TYPE_TABLE ? build.vmExit(pcpos) : fallback); IrOp table = build.inst(IrCmd::LOAD_POINTER, build.vmReg(rb)); CODEGEN_ASSERT(build.function.proto); diff --git a/CodeGen/src/IrTranslation.h b/CodeGen/src/IrTranslation.h index 5eb01450..8b514cc1 100644 --- a/CodeGen/src/IrTranslation.h +++ b/CodeGen/src/IrTranslation.h @@ -44,7 +44,8 @@ void translateInstDupTable(IrBuilder& build, const Instruction* pc, int pcpos); void translateInstGetUpval(IrBuilder& build, const Instruction* pc, int pcpos); void translateInstSetUpval(IrBuilder& build, const Instruction* pc, int pcpos); void translateInstCloseUpvals(IrBuilder& build, const Instruction* pc); -IrOp translateFastCallN(IrBuilder& build, const Instruction* pc, int pcpos, bool customParams, int customParamCount, IrOp customArgs); +IrOp translateFastCallN( + IrBuilder& build, const Instruction* pc, int pcpos, bool customParams, int customParamCount, IrOp customArgs, IrOp customArg3); void translateInstForNPrep(IrBuilder& build, const Instruction* pc, int pcpos); void translateInstForNLoop(IrBuilder& build, const Instruction* pc, int pcpos); void translateInstForGPrepNext(IrBuilder& build, const Instruction* pc, int pcpos); diff --git a/CodeGen/src/IrValueLocationTracking.cpp b/CodeGen/src/IrValueLocationTracking.cpp index c6b2d044..0224b49b 100644 --- a/CodeGen/src/IrValueLocationTracking.cpp +++ b/CodeGen/src/IrValueLocationTracking.cpp @@ -3,6 +3,8 @@ #include "Luau/IrUtils.h" +LUAU_FASTFLAG(LuauCodegenFastcall3) + namespace Luau { namespace CodeGen @@ -44,11 +46,11 @@ void IrValueLocationTracking::beforeInstLowering(IrInst& inst) invalidateRestoreVmRegs(vmRegOp(inst.a), -1); break; case IrCmd::FASTCALL: - invalidateRestoreVmRegs(vmRegOp(inst.b), function.intOp(inst.f)); + invalidateRestoreVmRegs(vmRegOp(inst.b), function.intOp(FFlag::LuauCodegenFastcall3 ? inst.d : inst.f)); break; case IrCmd::INVOKE_FASTCALL: // Multiple return sequences (count == -1) are defined by ADJUST_STACK_TO_REG - if (int count = function.intOp(inst.f); count != -1) + if (int count = function.intOp(FFlag::LuauCodegenFastcall3 ? inst.g : inst.f); count != -1) invalidateRestoreVmRegs(vmRegOp(inst.b), count); break; case IrCmd::DO_ARITH: @@ -119,7 +121,7 @@ void IrValueLocationTracking::beforeInstLowering(IrInst& inst) break; // These instructions read VmReg only after optimizeMemoryOperandsX64 - case IrCmd::CHECK_TAG: // TODO: remove with FFlagLuauCodegenRemoveDeadStores5 + case IrCmd::CHECK_TAG: case IrCmd::CHECK_TRUTHY: case IrCmd::ADD_NUM: case IrCmd::SUB_NUM: diff --git a/CodeGen/src/OptimizeConstProp.cpp b/CodeGen/src/OptimizeConstProp.cpp index 4ff49570..0cd2aa51 100644 --- a/CodeGen/src/OptimizeConstProp.cpp +++ b/CodeGen/src/OptimizeConstProp.cpp @@ -18,10 +18,10 @@ LUAU_FASTINTVARIABLE(LuauCodeGenMinLinearBlockPath, 3) LUAU_FASTINTVARIABLE(LuauCodeGenReuseSlotLimit, 64) LUAU_FASTINTVARIABLE(LuauCodeGenReuseUdataTagLimit, 64) LUAU_FASTFLAGVARIABLE(DebugLuauAbortingChecks, false) -LUAU_FASTFLAG(LuauCodegenRemoveDeadStores5) LUAU_FASTFLAGVARIABLE(LuauCodegenFixSplitStoreConstMismatch, false) LUAU_FASTFLAG(LuauCodegenUserdataOps) LUAU_FASTFLAG(LuauCodegenUserdataAlloc) +LUAU_FASTFLAG(LuauCodegenFastcall3) namespace Luau { @@ -621,16 +621,9 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction& std::tie(activeLoadCmd, activeLoadValue) = state.getPreviousVersionedLoadForTag(value, source); if (state.tryGetTag(source) == value) - { - if (FFlag::DebugLuauAbortingChecks && !FFlag::LuauCodegenRemoveDeadStores5) - replace(function, block, index, {IrCmd::CHECK_TAG, inst.a, inst.b, build.undef()}); - else - kill(function, inst); - } + kill(function, inst); else - { state.saveTag(source, value); - } } else { @@ -1150,39 +1143,33 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction& case IrCmd::FASTCALL: { - if (FFlag::LuauCodegenRemoveDeadStores5) + LuauBuiltinFunction bfid = LuauBuiltinFunction(function.uintOp(inst.a)); + int firstReturnReg = vmRegOp(inst.b); + int nresults = function.intOp(FFlag::LuauCodegenFastcall3 ? inst.d : inst.f); + + // TODO: FASTCALL is more restrictive than INVOKE_FASTCALL; we should either determine the exact semantics, or rework it + handleBuiltinEffects(state, bfid, firstReturnReg, nresults); + + switch (bfid) { - LuauBuiltinFunction bfid = LuauBuiltinFunction(function.uintOp(inst.a)); - int firstReturnReg = vmRegOp(inst.b); - int nresults = function.intOp(inst.f); + case LBF_MATH_MODF: + case LBF_MATH_FREXP: + state.updateTag(IrOp{IrOpKind::VmReg, uint8_t(firstReturnReg)}, LUA_TNUMBER); - // TODO: FASTCALL is more restrictive than INVOKE_FASTCALL; we should either determine the exact semantics, or rework it - handleBuiltinEffects(state, bfid, firstReturnReg, nresults); - - switch (bfid) - { - case LBF_MATH_MODF: - case LBF_MATH_FREXP: - state.updateTag(IrOp{IrOpKind::VmReg, uint8_t(firstReturnReg)}, LUA_TNUMBER); - - if (nresults > 1) - state.updateTag(IrOp{IrOpKind::VmReg, uint8_t(firstReturnReg + 1)}, LUA_TNUMBER); - break; - case LBF_MATH_SIGN: - state.updateTag(IrOp{IrOpKind::VmReg, uint8_t(firstReturnReg)}, LUA_TNUMBER); - break; - default: - break; - } - } - else - { - handleBuiltinEffects(state, LuauBuiltinFunction(function.uintOp(inst.a)), vmRegOp(inst.b), function.intOp(inst.f)); + if (nresults > 1) + state.updateTag(IrOp{IrOpKind::VmReg, uint8_t(firstReturnReg + 1)}, LUA_TNUMBER); + break; + case LBF_MATH_SIGN: + state.updateTag(IrOp{IrOpKind::VmReg, uint8_t(firstReturnReg)}, LUA_TNUMBER); + break; + default: + break; } break; } case IrCmd::INVOKE_FASTCALL: - handleBuiltinEffects(state, LuauBuiltinFunction(function.uintOp(inst.a)), vmRegOp(inst.b), function.intOp(inst.f)); + handleBuiltinEffects( + state, LuauBuiltinFunction(function.uintOp(inst.a)), vmRegOp(inst.b), function.intOp(FFlag::LuauCodegenFastcall3 ? inst.g : inst.f)); break; // These instructions don't have an effect on register/memory state we are tracking diff --git a/CodeGen/src/OptimizeDeadStore.cpp b/CodeGen/src/OptimizeDeadStore.cpp index d18b75c5..9fa6f062 100644 --- a/CodeGen/src/OptimizeDeadStore.cpp +++ b/CodeGen/src/OptimizeDeadStore.cpp @@ -9,7 +9,6 @@ #include "lobject.h" -LUAU_FASTFLAGVARIABLE(LuauCodegenRemoveDeadStores5, false) LUAU_FASTFLAG(LuauCodegenUserdataOps) // TODO: optimization can be improved by knowing which registers are live in at each VM exit diff --git a/Common/include/Luau/Bytecode.h b/Common/include/Luau/Bytecode.h index 85fef5aa..f971391b 100644 --- a/Common/include/Luau/Bytecode.h +++ b/Common/include/Luau/Bytecode.h @@ -46,6 +46,7 @@ // Version 3: Adds FORGPREP/JUMPXEQK* and enhances AUX encoding for FORGLOOP. Removes FORGLOOP_NEXT/INEXT and JUMPIFEQK/JUMPIFNOTEQK. Currently supported. // Version 4: Adds Proto::flags, typeinfo, and floor division opcodes IDIV/IDIVK. Currently supported. // Version 5: Adds SUBRK/DIVRK and vector constants. Currently supported. +// Version 6: Adds FASTCALL3. Currently supported. // # Bytecode type information history // Version 1: (from bytecode version 4) Type information for function signature. Currently supported. @@ -299,8 +300,13 @@ enum LuauOpcode // A: target register (see FORGLOOP for register layout) LOP_FORGPREP_INEXT, - // removed in v3 - LOP_DEP_FORGLOOP_INEXT, + // FASTCALL3: perform a fast call of a built-in function using 3 register arguments + // A: builtin function id (see LuauBuiltinFunction) + // B: source argument register + // C: jump offset to get to following CALL + // AUX: source register 2 in least-significant byte + // AUX: source register 3 in second least-significant byte + LOP_FASTCALL3, // FORGPREP_NEXT: prepare FORGLOOP with 2 output variables (no AUX encoding), assuming generator is luaB_next, and jump to FORGLOOP // A: target register (see FORGLOOP for register layout) @@ -434,7 +440,7 @@ enum LuauBytecodeTag { // Bytecode version; runtime supports [MIN, MAX], compiler emits TARGET by default but may emit a higher version when flags are enabled LBC_VERSION_MIN = 3, - LBC_VERSION_MAX = 5, + LBC_VERSION_MAX = 6, LBC_VERSION_TARGET = 5, // Type encoding version LBC_TYPE_VERSION_DEPRECATED = 1, diff --git a/Common/include/Luau/BytecodeUtils.h b/Common/include/Luau/BytecodeUtils.h index 957c804c..6f110311 100644 --- a/Common/include/Luau/BytecodeUtils.h +++ b/Common/include/Luau/BytecodeUtils.h @@ -28,6 +28,7 @@ inline int getOpLength(LuauOpcode op) case LOP_LOADKX: case LOP_FASTCALL2: case LOP_FASTCALL2K: + case LOP_FASTCALL3: case LOP_JUMPXEQKNIL: case LOP_JUMPXEQKB: case LOP_JUMPXEQKN: diff --git a/Common/include/Luau/DenseHash.h b/Common/include/Luau/DenseHash.h index 507a9c48..39e50f92 100644 --- a/Common/include/Luau/DenseHash.h +++ b/Common/include/Luau/DenseHash.h @@ -120,12 +120,12 @@ public: return *this; } - void clear() + void clear(size_t thresholdToDestroy = 32) { if (count == 0) return; - if (capacity > 32) + if (capacity > thresholdToDestroy) { destroy(); } @@ -583,9 +583,9 @@ public: { } - void clear() + void clear(size_t thresholdToDestroy = 32) { - impl.clear(); + impl.clear(thresholdToDestroy); } // Note: this reference is invalidated by any insert operation (i.e. operator[]) diff --git a/Compiler/src/BytecodeBuilder.cpp b/Compiler/src/BytecodeBuilder.cpp index 59aee1e7..fac740c2 100644 --- a/Compiler/src/BytecodeBuilder.cpp +++ b/Compiler/src/BytecodeBuilder.cpp @@ -9,6 +9,7 @@ LUAU_FASTFLAGVARIABLE(LuauCompileTypeInfo, false) LUAU_FASTFLAG(LuauCompileUserdataInfo) +LUAU_FASTFLAG(LuauCompileFastcall3) namespace Luau { @@ -113,6 +114,7 @@ inline bool isFastCall(LuauOpcode op) case LOP_FASTCALL1: case LOP_FASTCALL2: case LOP_FASTCALL2K: + case LOP_FASTCALL3: return true; default: @@ -1241,6 +1243,9 @@ std::string BytecodeBuilder::getError(const std::string& message) uint8_t BytecodeBuilder::getVersion() { // This function usually returns LBC_VERSION_TARGET but may sometimes return a higher number (within LBC_VERSION_MIN/MAX) under fast flags + if (FFlag::LuauCompileFastcall3) + return 6; + return LBC_VERSION_TARGET; } @@ -1621,6 +1626,16 @@ void BytecodeBuilder::validateInstructions() const VCONSTANY(insns[i + 1]); break; + case LOP_FASTCALL3: + LUAU_ASSERT(FFlag::LuauCompileFastcall3); + + VREG(LUAU_INSN_B(insn)); + VJUMP(LUAU_INSN_C(insn)); + LUAU_ASSERT(LUAU_INSN_OP(insns[i + 1 + LUAU_INSN_C(insn)]) == LOP_CALL); + VREG(insns[i + 1] & 0xff); + VREG((insns[i + 1] >> 8) & 0xff); + break; + case LOP_COVERAGE: break; @@ -2235,6 +2250,13 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result, code++; break; + case LOP_FASTCALL3: + LUAU_ASSERT(FFlag::LuauCompileFastcall3); + + formatAppend(result, "FASTCALL3 %d R%d R%d R%d L%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), *code & 0xff, (*code >> 8) & 0xff, targetLabel); + code++; + break; + case LOP_COVERAGE: formatAppend(result, "COVERAGE\n"); break; diff --git a/Compiler/src/Compiler.cpp b/Compiler/src/Compiler.cpp index db86fbc6..26d3100c 100644 --- a/Compiler/src/Compiler.cpp +++ b/Compiler/src/Compiler.cpp @@ -29,6 +29,7 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5) LUAU_FASTFLAG(LuauCompileTypeInfo) LUAU_FASTFLAGVARIABLE(LuauCompileTempTypeInfo, false) LUAU_FASTFLAGVARIABLE(LuauCompileUserdataInfo, false) +LUAU_FASTFLAGVARIABLE(LuauCompileFastcall3, false) LUAU_FASTFLAG(LuauNativeAttribute) @@ -473,10 +474,32 @@ struct Compiler { LUAU_ASSERT(!expr->self); LUAU_ASSERT(expr->args.size >= 1); - LUAU_ASSERT(expr->args.size <= 2 || (bfid == LBF_BIT32_EXTRACTK && expr->args.size == 3)); + + if (FFlag::LuauCompileFastcall3) + LUAU_ASSERT(expr->args.size <= 3); + else + LUAU_ASSERT(expr->args.size <= 2 || (bfid == LBF_BIT32_EXTRACTK && expr->args.size == 3)); + LUAU_ASSERT(bfid == LBF_BIT32_EXTRACTK ? bfK >= 0 : bfK < 0); - LuauOpcode opc = expr->args.size == 1 ? LOP_FASTCALL1 : (bfK >= 0 || isConstant(expr->args.data[1])) ? LOP_FASTCALL2K : LOP_FASTCALL2; + LuauOpcode opc = LOP_NOP; + + if (FFlag::LuauCompileFastcall3) + { + if (expr->args.size == 1) + opc = LOP_FASTCALL1; + else if (bfK >= 0 || (expr->args.size == 2 && isConstant(expr->args.data[1]))) + opc = LOP_FASTCALL2K; + else if (expr->args.size == 2) + opc = LOP_FASTCALL2; + else + opc = LOP_FASTCALL3; + } + else + { + opc = expr->args.size == 1 ? LOP_FASTCALL1 + : (bfK >= 0 || (expr->args.size == 2 && isConstant(expr->args.data[1]))) ? LOP_FASTCALL2K : LOP_FASTCALL2; + } uint32_t args[3] = {}; @@ -504,8 +527,16 @@ struct Compiler size_t fastcallLabel = bytecode.emitLabel(); bytecode.emitABC(opc, uint8_t(bfid), uint8_t(args[0]), 0); - if (opc != LOP_FASTCALL1) + + if (FFlag::LuauCompileFastcall3 && opc == LOP_FASTCALL3) + { + LUAU_ASSERT(bfK < 0); + bytecode.emitAux(args[1] | (args[2] << 8)); + } + else if (opc != LOP_FASTCALL1) + { bytecode.emitAux(bfK >= 0 ? bfK : args[1]); + } // Set up a traditional Lua stack for the subsequent LOP_CALL. // Note, as with other instructions that immediately follow FASTCALL, these are normally not executed and are used as a fallback for @@ -857,11 +888,28 @@ struct Compiler } } - // Optimization: for 1/2 argument fast calls use specialized opcodes - if (bfid >= 0 && expr->args.size >= 1 && expr->args.size <= 2) + unsigned maxFastcallArgs = 2; + + // Fastcall with 3 arguments is only used if it can help save one or more move instructions + if (FFlag::LuauCompileFastcall3 && bfid >= 0 && expr->args.size == 3) + { + for (size_t i = 0; i < expr->args.size; ++i) + { + if (int reg = getExprLocalReg(expr->args.data[i]); reg >= 0) + { + maxFastcallArgs = 3; + break; + } + } + } + + // Optimization: for 1/2/3 argument fast calls use specialized opcodes + if (bfid >= 0 && expr->args.size >= 1 && expr->args.size <= (FFlag::LuauCompileFastcall3 ? maxFastcallArgs : 2u)) { if (!isExprMultRet(expr->args.data[expr->args.size - 1])) + { return compileExprFastcallN(expr, target, targetCount, targetTop, multRet, regs, bfid); + } else if (options.optimizationLevel >= 2) { // when a builtin is none-safe with matching arity, even if the last expression returns 0 or >1 arguments, diff --git a/Config/include/Luau/LinterConfig.h b/Config/include/Luau/LinterConfig.h index a598a3df..3a68c0d7 100644 --- a/Config/include/Luau/LinterConfig.h +++ b/Config/include/Luau/LinterConfig.h @@ -49,6 +49,7 @@ struct LintWarning Code_CommentDirective = 26, Code_IntegerParsing = 27, Code_ComparisonPrecedence = 28, + Code_RedundantNativeAttribute = 29, Code__Count }; @@ -115,6 +116,7 @@ static const char* kWarningNames[] = { "CommentDirective", "IntegerParsing", "ComparisonPrecedence", + "RedundantNativeAttribute", }; // clang-format on diff --git a/VM/src/ldebug.cpp b/VM/src/ldebug.cpp index 0e792366..07cc117e 100644 --- a/VM/src/ldebug.cpp +++ b/VM/src/ldebug.cpp @@ -12,8 +12,6 @@ #include #include -LUAU_FASTFLAGVARIABLE(LuauPushErrorStackCheck, false) - static const char* getfuncname(Closure* f); static int currentpc(lua_State* L, CallInfo* ci) @@ -332,8 +330,7 @@ l_noret luaG_runerrorL(lua_State* L, const char* fmt, ...) vsnprintf(result, sizeof(result), fmt, argp); va_end(argp); - if (FFlag::LuauPushErrorStackCheck) - lua_rawcheckstack(L, 1); + lua_rawcheckstack(L, 1); pusherror(L, result); luaD_throw(L, LUA_ERRRUN); @@ -341,8 +338,7 @@ l_noret luaG_runerrorL(lua_State* L, const char* fmt, ...) void luaG_pusherror(lua_State* L, const char* error) { - if (FFlag::LuauPushErrorStackCheck) - lua_rawcheckstack(L, 1); + lua_rawcheckstack(L, 1); pusherror(L, error); } diff --git a/VM/src/lfunc.cpp b/VM/src/lfunc.cpp index b33fe9dd..2a1e45c4 100644 --- a/VM/src/lfunc.cpp +++ b/VM/src/lfunc.cpp @@ -6,8 +6,6 @@ #include "lmem.h" #include "lgc.h" -LUAU_FASTFLAGVARIABLE(LuauLoadTypeInfo, false) - Proto* luaF_newproto(lua_State* L) { Proto* f = luaM_newgco(L, Proto, sizeof(Proto), L->activememcat); @@ -52,9 +50,7 @@ Proto* luaF_newproto(lua_State* L) f->linegaplog2 = 0; f->linedefined = 0; f->bytecodeid = 0; - - if (FFlag::LuauLoadTypeInfo) - f->sizetypeinfo = 0; + f->sizetypeinfo = 0; return f; } @@ -178,16 +174,8 @@ void luaF_freeproto(lua_State* L, Proto* f, lua_Page* page) if (f->execdata) L->global->ecb.destroy(L, f); - if (FFlag::LuauLoadTypeInfo) - { - if (f->typeinfo) - luaM_freearray(L, f->typeinfo, f->sizetypeinfo, uint8_t, f->memcat); - } - else - { - if (f->typeinfo) - luaM_freearray(L, f->typeinfo, f->numparams + 2, uint8_t, f->memcat); - } + if (f->typeinfo) + luaM_freearray(L, f->typeinfo, f->sizetypeinfo, uint8_t, f->memcat); luaM_freegco(L, f, sizeof(Proto), f->memcat, page); } diff --git a/VM/src/lgc.cpp b/VM/src/lgc.cpp index f8389422..4473f04f 100644 --- a/VM/src/lgc.cpp +++ b/VM/src/lgc.cpp @@ -14,8 +14,6 @@ #include -LUAU_FASTFLAG(LuauLoadTypeInfo) - /* * Luau uses an incremental non-generational non-moving mark&sweep garbage collector. * @@ -507,16 +505,8 @@ static size_t propagatemark(global_State* g) g->gray = p->gclist; traverseproto(g, p); - if (FFlag::LuauLoadTypeInfo) - { - return sizeof(Proto) + sizeof(Instruction) * p->sizecode + sizeof(Proto*) * p->sizep + sizeof(TValue) * p->sizek + p->sizelineinfo + - sizeof(LocVar) * p->sizelocvars + sizeof(TString*) * p->sizeupvalues + p->sizetypeinfo; - } - else - { - return sizeof(Proto) + sizeof(Instruction) * p->sizecode + sizeof(Proto*) * p->sizep + sizeof(TValue) * p->sizek + p->sizelineinfo + - sizeof(LocVar) * p->sizelocvars + sizeof(TString*) * p->sizeupvalues; - } + return sizeof(Proto) + sizeof(Instruction) * p->sizecode + sizeof(Proto*) * p->sizep + sizeof(TValue) * p->sizek + p->sizelineinfo + + sizeof(LocVar) * p->sizelocvars + sizeof(TString*) * p->sizeupvalues + p->sizetypeinfo; } default: LUAU_ASSERT(0); diff --git a/VM/src/lmem.cpp b/VM/src/lmem.cpp index f6cc07c9..5ff5de72 100644 --- a/VM/src/lmem.cpp +++ b/VM/src/lmem.cpp @@ -124,11 +124,16 @@ static_assert(offsetof(Udata, data) == ABISWITCH(16, 16, 12), "size mismatch for static_assert(sizeof(Table) == ABISWITCH(48, 32, 32), "size mismatch for table header"); static_assert(offsetof(Buffer, data) == ABISWITCH(8, 8, 8), "size mismatch for buffer header"); -LUAU_FASTFLAGVARIABLE(LuauExtendedSizeClasses, false) - const size_t kSizeClasses = LUA_SIZECLASSES; -const size_t kMaxSmallSize_DEPRECATED = 512; // TODO: remove with FFlagLuauExtendedSizeClasses + +// Controls the number of entries in SizeClassConfig and define the maximum possible paged allocation size +// Modifications require updates the SizeClassConfig initialization const size_t kMaxSmallSize = 1024; + +// Effective limit on object size to use paged allocation +// Can be modified without additional changes to code, provided it is smaller or equal to kMaxSmallSize +const size_t kMaxSmallSizeUsed = 1024; + const size_t kLargePageThreshold = 512; // larger pages are used for objects larger than this size to fit more of them into a page // constant factor to reduce our page sizes by, to increase the chances that pages we allocate will @@ -187,8 +192,7 @@ struct SizeClassConfig const SizeClassConfig kSizeClassConfig; // size class for a block of size sz; returns -1 for size=0 because empty allocations take no space -#define sizeclass(sz) \ - (size_t((sz)-1) < (FFlag::LuauExtendedSizeClasses ? kMaxSmallSize : kMaxSmallSize_DEPRECATED) ? kSizeClassConfig.classForSize[sz] : -1) +#define sizeclass(sz) (size_t((sz)-1) < kMaxSmallSizeUsed ? kSizeClassConfig.classForSize[sz] : -1) // metadata for a block is stored in the first pointer of the block #define metadata(block) (*(void**)(block)) @@ -275,34 +279,18 @@ static lua_Page* newpage(lua_State* L, lua_Page** pageset, int pageSize, int blo // if it is inlined, then the compiler may determine those functions are "too big" to be profitably inlined, which results in reduced performance LUAU_NOINLINE static lua_Page* newclasspage(lua_State* L, lua_Page** freepageset, lua_Page** pageset, uint8_t sizeClass, bool storeMetadata) { - if (FFlag::LuauExtendedSizeClasses) - { - int sizeOfClass = kSizeClassConfig.sizeOfClass[sizeClass]; - int pageSize = sizeOfClass > int(kLargePageThreshold) ? kLargePageSize : kSmallPageSize; - int blockSize = sizeOfClass + (storeMetadata ? kBlockHeader : 0); - int blockCount = (pageSize - offsetof(lua_Page, data)) / blockSize; + int sizeOfClass = kSizeClassConfig.sizeOfClass[sizeClass]; + int pageSize = sizeOfClass > int(kLargePageThreshold) ? kLargePageSize : kSmallPageSize; + int blockSize = sizeOfClass + (storeMetadata ? kBlockHeader : 0); + int blockCount = (pageSize - offsetof(lua_Page, data)) / blockSize; - lua_Page* page = newpage(L, pageset, pageSize, blockSize, blockCount); + lua_Page* page = newpage(L, pageset, pageSize, blockSize, blockCount); - // prepend a page to page freelist (which is empty because we only ever allocate a new page when it is!) - LUAU_ASSERT(!freepageset[sizeClass]); - freepageset[sizeClass] = page; + // prepend a page to page freelist (which is empty because we only ever allocate a new page when it is!) + LUAU_ASSERT(!freepageset[sizeClass]); + freepageset[sizeClass] = page; - return page; - } - else - { - int blockSize = kSizeClassConfig.sizeOfClass[sizeClass] + (storeMetadata ? kBlockHeader : 0); - int blockCount = (kSmallPageSize - offsetof(lua_Page, data)) / blockSize; - - lua_Page* page = newpage(L, pageset, kSmallPageSize, blockSize, blockCount); - - // prepend a page to page freelist (which is empty because we only ever allocate a new page when it is!) - LUAU_ASSERT(!freepageset[sizeClass]); - freepageset[sizeClass] = page; - - return page; - } + return page; } static void freepage(lua_State* L, lua_Page** pageset, lua_Page* page) diff --git a/VM/src/ltablib.cpp b/VM/src/ltablib.cpp index 3e14d4ad..75d9f400 100644 --- a/VM/src/ltablib.cpp +++ b/VM/src/ltablib.cpp @@ -11,9 +11,6 @@ #include "ldebug.h" #include "lvm.h" -LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauFastTableMaxn, false) -LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauFasterConcat, false) - static int foreachi(lua_State* L) { luaL_checktype(L, 1, LUA_TTABLE); @@ -56,41 +53,24 @@ static int maxn(lua_State* L) double max = 0; luaL_checktype(L, 1, LUA_TTABLE); - if (DFFlag::LuauFastTableMaxn) + Table* t = hvalue(L->base); + + for (int i = 0; i < t->sizearray; i++) { - Table* t = hvalue(L->base); - - for (int i = 0; i < t->sizearray; i++) - { - if (!ttisnil(&t->array[i])) - max = i + 1; - } - - for (int i = 0; i < sizenode(t); i++) - { - LuaNode* n = gnode(t, i); - - if (!ttisnil(gval(n)) && ttisnumber(gkey(n))) - { - double v = nvalue(gkey(n)); - - if (v > max) - max = v; - } - } + if (!ttisnil(&t->array[i])) + max = i + 1; } - else + + for (int i = 0; i < sizenode(t); i++) { - lua_pushnil(L); // first key - while (lua_next(L, 1)) + LuaNode* n = gnode(t, i); + + if (!ttisnil(gval(n)) && ttisnumber(gkey(n))) { - lua_pop(L, 1); // remove value - if (lua_type(L, -1) == LUA_TNUMBER) - { - double v = lua_tonumber(L, -1); - if (v > max) - max = v; - } + double v = nvalue(gkey(n)); + + if (v > max) + max = v; } } @@ -251,7 +231,7 @@ static int tmove(lua_State* L) static void addfield(lua_State* L, luaL_Strbuf* b, int i, Table* t) { - if (DFFlag::LuauFasterConcat && t && unsigned(i - 1) < unsigned(t->sizearray) && ttisstring(&t->array[i - 1])) + if (t && unsigned(i - 1) < unsigned(t->sizearray) && ttisstring(&t->array[i - 1])) { TString* ts = tsvalue(&t->array[i - 1]); luaL_addlstring(b, getstr(ts), ts->len); @@ -273,14 +253,14 @@ static int tconcat(lua_State* L) int i = luaL_optinteger(L, 3, 1); int last = luaL_opt(L, luaL_checkinteger, 4, lua_objlen(L, 1)); - Table* t = DFFlag::LuauFasterConcat ? hvalue(L->base) : NULL; + Table* t = hvalue(L->base); luaL_Strbuf b; luaL_buffinit(L, &b); for (; i < last; i++) { addfield(L, &b, i, t); - if (!DFFlag::LuauFasterConcat || lsep != 0) + if (lsep != 0) luaL_addlstring(&b, sep, lsep); } if (i == last) // add last value (if interval was not empty) diff --git a/VM/src/lvmexecute.cpp b/VM/src/lvmexecute.cpp index 4ac21db3..bc89458e 100644 --- a/VM/src/lvmexecute.cpp +++ b/VM/src/lvmexecute.cpp @@ -98,7 +98,7 @@ LUAU_FASTFLAGVARIABLE(LuauVmSplitDoarith, false) VM_DISPATCH_OP(LOP_POWK), VM_DISPATCH_OP(LOP_AND), VM_DISPATCH_OP(LOP_OR), VM_DISPATCH_OP(LOP_ANDK), VM_DISPATCH_OP(LOP_ORK), \ VM_DISPATCH_OP(LOP_CONCAT), VM_DISPATCH_OP(LOP_NOT), VM_DISPATCH_OP(LOP_MINUS), VM_DISPATCH_OP(LOP_LENGTH), VM_DISPATCH_OP(LOP_NEWTABLE), \ VM_DISPATCH_OP(LOP_DUPTABLE), VM_DISPATCH_OP(LOP_SETLIST), VM_DISPATCH_OP(LOP_FORNPREP), VM_DISPATCH_OP(LOP_FORNLOOP), \ - VM_DISPATCH_OP(LOP_FORGLOOP), VM_DISPATCH_OP(LOP_FORGPREP_INEXT), VM_DISPATCH_OP(LOP_DEP_FORGLOOP_INEXT), VM_DISPATCH_OP(LOP_FORGPREP_NEXT), \ + VM_DISPATCH_OP(LOP_FORGLOOP), VM_DISPATCH_OP(LOP_FORGPREP_INEXT), VM_DISPATCH_OP(LOP_FASTCALL3), VM_DISPATCH_OP(LOP_FORGPREP_NEXT), \ VM_DISPATCH_OP(LOP_NATIVECALL), VM_DISPATCH_OP(LOP_GETVARARGS), VM_DISPATCH_OP(LOP_DUPCLOSURE), VM_DISPATCH_OP(LOP_PREPVARARGS), \ VM_DISPATCH_OP(LOP_LOADKX), VM_DISPATCH_OP(LOP_JUMPX), VM_DISPATCH_OP(LOP_FASTCALL), VM_DISPATCH_OP(LOP_COVERAGE), \ VM_DISPATCH_OP(LOP_CAPTURE), VM_DISPATCH_OP(LOP_SUBRK), VM_DISPATCH_OP(LOP_DIVRK), VM_DISPATCH_OP(LOP_FASTCALL1), \ @@ -2539,12 +2539,6 @@ reentry: VM_NEXT(); } - VM_CASE(LOP_DEP_FORGLOOP_INEXT) - { - LUAU_ASSERT(!"Unsupported deprecated opcode"); - LUAU_UNREACHABLE(); - } - VM_CASE(LOP_FORGPREP_NEXT) { Instruction insn = *pc++; @@ -3013,6 +3007,60 @@ reentry: } } + VM_CASE(LOP_FASTCALL3) + { + Instruction insn = *pc++; + int bfid = LUAU_INSN_A(insn); + int skip = LUAU_INSN_C(insn) - 1; + uint32_t aux = *pc++; + TValue* arg1 = VM_REG(LUAU_INSN_B(insn)); + TValue* arg2 = VM_REG(aux & 0xff); + TValue* arg3 = VM_REG((aux >> 8) & 0xff); + + LUAU_ASSERT(unsigned(pc - cl->l.p->code + skip) < unsigned(cl->l.p->sizecode)); + + Instruction call = pc[skip]; + LUAU_ASSERT(LUAU_INSN_OP(call) == LOP_CALL); + + StkId ra = VM_REG(LUAU_INSN_A(call)); + + int nparams = 3; + int nresults = LUAU_INSN_C(call) - 1; + + luau_FastFunction f = luauF_table[bfid]; + LUAU_ASSERT(f); + + if (cl->env->safeenv) + { + VM_PROTECT_PC(); // f may fail due to OOM + + setobj2s(L, L->top, arg2); + setobj2s(L, L->top + 1, arg3); + + int n = f(L, ra, arg1, nresults, L->top, nparams); + + if (n >= 0) + { + if (nresults == LUA_MULTRET) + L->top = ra + n; + + pc += skip + 1; // skip instructions that compute function as well as CALL + LUAU_ASSERT(unsigned(pc - cl->l.p->code) < unsigned(cl->l.p->sizecode)); + VM_NEXT(); + } + else + { + // continue execution through the fallback code + VM_NEXT(); + } + } + else + { + // continue execution through the fallback code + VM_NEXT(); + } + } + VM_CASE(LOP_BREAK) { LUAU_ASSERT(cl->l.p->debuginsn); diff --git a/VM/src/lvmload.cpp b/VM/src/lvmload.cpp index ed564bba..112a7197 100644 --- a/VM/src/lvmload.cpp +++ b/VM/src/lvmload.cpp @@ -13,7 +13,6 @@ #include -LUAU_FASTFLAG(LuauLoadTypeInfo) LUAU_FASTFLAGVARIABLE(LuauLoadUserdataInfo, false) // TODO: RAII deallocation doesn't work for longjmp builds if a memory error happens @@ -357,70 +356,11 @@ int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size { p->flags = read(data, size, offset); - if (FFlag::LuauLoadTypeInfo) - { - if (typesversion == 1) - { - uint32_t typesize = readVarInt(data, size, offset); - - if (typesize) - { - uint8_t* types = (uint8_t*)data + offset; - - LUAU_ASSERT(typesize == unsigned(2 + p->numparams)); - LUAU_ASSERT(types[0] == LBC_TYPE_FUNCTION); - LUAU_ASSERT(types[1] == p->numparams); - - // transform v1 into v2 format - int headersize = typesize > 127 ? 4 : 3; - - p->typeinfo = luaM_newarray(L, headersize + typesize, uint8_t, p->memcat); - p->sizetypeinfo = headersize + typesize; - - if (headersize == 4) - { - p->typeinfo[0] = (typesize & 127) | (1 << 7); - p->typeinfo[1] = typesize >> 7; - p->typeinfo[2] = 0; - p->typeinfo[3] = 0; - } - else - { - p->typeinfo[0] = uint8_t(typesize); - p->typeinfo[1] = 0; - p->typeinfo[2] = 0; - } - - memcpy(p->typeinfo + headersize, types, typesize); - } - - offset += typesize; - } - else if (typesversion == 2 || (FFlag::LuauLoadUserdataInfo && typesversion == 3)) - { - uint32_t typesize = readVarInt(data, size, offset); - - if (typesize) - { - uint8_t* types = (uint8_t*)data + offset; - - p->typeinfo = luaM_newarray(L, typesize, uint8_t, p->memcat); - p->sizetypeinfo = typesize; - memcpy(p->typeinfo, types, typesize); - offset += typesize; - - if (FFlag::LuauLoadUserdataInfo && typesversion == 3) - { - remapUserdataTypes((char*)(uint8_t*)p->typeinfo, p->sizetypeinfo, userdataRemapping, userdataTypeLimit); - } - } - } - } - else + if (typesversion == 1) { uint32_t typesize = readVarInt(data, size, offset); - if (typesize && typesversion == LBC_TYPE_VERSION_DEPRECATED) + if (typesize) { uint8_t* types = (uint8_t*)data + offset; @@ -428,12 +368,50 @@ int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size LUAU_ASSERT(types[0] == LBC_TYPE_FUNCTION); LUAU_ASSERT(types[1] == p->numparams); - p->typeinfo = luaM_newarray(L, typesize, uint8_t, p->memcat); - memcpy(p->typeinfo, types, typesize); + // transform v1 into v2 format + int headersize = typesize > 127 ? 4 : 3; + + p->typeinfo = luaM_newarray(L, headersize + typesize, uint8_t, p->memcat); + p->sizetypeinfo = headersize + typesize; + + if (headersize == 4) + { + p->typeinfo[0] = (typesize & 127) | (1 << 7); + p->typeinfo[1] = typesize >> 7; + p->typeinfo[2] = 0; + p->typeinfo[3] = 0; + } + else + { + p->typeinfo[0] = uint8_t(typesize); + p->typeinfo[1] = 0; + p->typeinfo[2] = 0; + } + + memcpy(p->typeinfo + headersize, types, typesize); } offset += typesize; } + else if (typesversion == 2 || (FFlag::LuauLoadUserdataInfo && typesversion == 3)) + { + uint32_t typesize = readVarInt(data, size, offset); + + if (typesize) + { + uint8_t* types = (uint8_t*)data + offset; + + p->typeinfo = luaM_newarray(L, typesize, uint8_t, p->memcat); + p->sizetypeinfo = typesize; + memcpy(p->typeinfo, types, typesize); + offset += typesize; + + if (FFlag::LuauLoadUserdataInfo && typesversion == 3) + { + remapUserdataTypes((char*)(uint8_t*)p->typeinfo, p->sizetypeinfo, userdataRemapping, userdataTypeLimit); + } + } + } } const int sizecode = readVarInt(data, size, offset); diff --git a/tests/Compiler.test.cpp b/tests/Compiler.test.cpp index 6255d73f..eeca416c 100644 --- a/tests/Compiler.test.cpp +++ b/tests/Compiler.test.cpp @@ -25,6 +25,7 @@ LUAU_FASTINT(LuauRecursionLimit) LUAU_FASTFLAG(LuauCompileTypeInfo) LUAU_FASTFLAG(LuauCompileTempTypeInfo) LUAU_FASTFLAG(LuauCompileUserdataInfo) +LUAU_FASTFLAG(LuauCompileFastcall3) using namespace Luau; @@ -3486,6 +3487,33 @@ RETURN R1 -1 )"); } +TEST_CASE("Fastcall3") +{ + ScopedFastFlag luauCompileFastcall3{FFlag::LuauCompileFastcall3, true}; + + CHECK_EQ("\n" + compileFunction0(R"( +local a, b, c = ... +return math.min(a, b, c) + math.clamp(a, b, c) +)"), + R"( +GETVARARGS R0 3 +FASTCALL3 19 R0 R1 R2 L0 +MOVE R5 R0 +MOVE R6 R1 +MOVE R7 R2 +GETIMPORT R4 2 [math.min] +CALL R4 3 1 +L0: FASTCALL3 46 R0 R1 R2 L1 +MOVE R6 R0 +MOVE R7 R1 +MOVE R8 R2 +GETIMPORT R5 4 [math.clamp] +CALL R5 3 1 +L1: ADD R3 R4 R5 +RETURN R3 1 +)"); +} + TEST_CASE("FastcallSelect") { // select(_, ...) compiles to a builtin call @@ -4668,6 +4696,34 @@ L0: RETURN R0 -1 )"); } +TEST_CASE("VectorFastCall3") +{ + ScopedFastFlag luauCompileFastcall3{FFlag::LuauCompileFastcall3, true}; + + const char* source = R"( +local a, b, c = ... +return Vector3.new(a, b, c) +)"; + + Luau::BytecodeBuilder bcb; + bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Code); + Luau::CompileOptions options; + options.vectorLib = "Vector3"; + options.vectorCtor = "new"; + Luau::compileOrThrow(bcb, source, options); + + CHECK_EQ("\n" + bcb.dumpFunction(0), R"( +GETVARARGS R0 3 +FASTCALL3 54 R0 R1 R2 L0 +MOVE R4 R0 +MOVE R5 R1 +MOVE R6 R2 +GETIMPORT R3 2 [Vector3.new] +CALL R3 3 -1 +L0: RETURN R3 -1 +)"); +} + TEST_CASE("VectorLiterals") { CHECK_EQ("\n" + compileFunction("return Vector3.new(1, 2, 3)", 0, 2, /*enableVectors*/ true), R"( diff --git a/tests/Error.test.cpp b/tests/Error.test.cpp index 8dfcbde0..00a5a2e7 100644 --- a/tests/Error.test.cpp +++ b/tests/Error.test.cpp @@ -48,7 +48,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "binary_op_type_family_errors") LUAU_REQUIRE_ERROR_COUNT(1, result); if (FFlag::DebugLuauDeferredConstraintResolution) - CHECK_EQ("Operator '+' could not be applied to operands of types number and string; there is no corresponding overload for __add", toString(result.errors[0])); + CHECK_EQ("Operator '+' could not be applied to operands of types number and string; there is no corresponding overload for __add", + toString(result.errors[0])); else CHECK_EQ("Type 'string' could not be converted into 'number'", toString(result.errors[0])); } @@ -66,7 +67,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "unary_op_type_family_errors") if (FFlag::DebugLuauDeferredConstraintResolution) { LUAU_REQUIRE_ERROR_COUNT(2, result); - CHECK_EQ("Operator '-' could not be applied to operand of type string; there is no corresponding overload for __unm", toString(result.errors[0])); + CHECK_EQ( + "Operator '-' could not be applied to operand of type string; there is no corresponding overload for __unm", toString(result.errors[0])); CHECK_EQ("Type 'string' could not be converted into 'number'", toString(result.errors[1])); } else diff --git a/tests/Fixture.h b/tests/Fixture.h index 481f79d3..e0c04e8b 100644 --- a/tests/Fixture.h +++ b/tests/Fixture.h @@ -241,3 +241,21 @@ using DifferFixtureWithBuiltins = DifferFixtureGeneric; } while (false) #define LUAU_REQUIRE_NO_ERRORS(result) LUAU_REQUIRE_ERROR_COUNT(0, result) + +#define LUAU_CHECK_ERRORS(result) \ + do \ + { \ + auto&& r = (result); \ + validateErrors(r.errors); \ + CHECK(!r.errors.empty()); \ + } while (false) + +#define LUAU_CHECK_ERROR_COUNT(count, result) \ + do \ + { \ + auto&& r = (result); \ + validateErrors(r.errors); \ + CHECK_MESSAGE(count == r.errors.size(), getErrors(r)); \ + } while (false) + +#define LUAU_CHECK_NO_ERRORS(result) LUAU_CHECK_ERROR_COUNT(0, result) diff --git a/tests/Generalization.test.cpp b/tests/Generalization.test.cpp index 43bd7325..e9344911 100644 --- a/tests/Generalization.test.cpp +++ b/tests/Generalization.test.cpp @@ -125,11 +125,7 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "cache_fully_generalized_types") CHECK(generalizedTypes->empty()); TypeId tinyTable = arena.addType(TableType{ - TableType::Props{{"one", builtinTypes.numberType}, {"two", builtinTypes.stringType}}, - std::nullopt, - TypeLevel{}, - TableState::Sealed - }); + TableType::Props{{"one", builtinTypes.numberType}, {"two", builtinTypes.stringType}}, std::nullopt, TypeLevel{}, TableState::Sealed}); generalize(tinyTable); @@ -142,17 +138,10 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "dont_cache_types_that_arent_done_yet") { TypeId freeTy = arena.addType(FreeType{NotNull{globalScope.get()}, builtinTypes.neverType, builtinTypes.stringType}); - TypeId fnTy = arena.addType(FunctionType{ - builtinTypes.emptyTypePack, - arena.addTypePack(TypePack{{builtinTypes.numberType}}) - }); + TypeId fnTy = arena.addType(FunctionType{builtinTypes.emptyTypePack, arena.addTypePack(TypePack{{builtinTypes.numberType}})}); TypeId tableTy = arena.addType(TableType{ - TableType::Props{{"one", builtinTypes.numberType}, {"two", freeTy}, {"three", fnTy}}, - std::nullopt, - TypeLevel{}, - TableState::Sealed - }); + TableType::Props{{"one", builtinTypes.numberType}, {"two", freeTy}, {"three", fnTy}}, std::nullopt, TypeLevel{}, TableState::Sealed}); generalize(tableTy); @@ -174,11 +163,7 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "functions_containing_cyclic_tables_can }); asMutable(selfTy)->ty.emplace( - TableType::Props{{"count", builtinTypes.numberType}, {"method", methodTy}}, - std::nullopt, - TypeLevel{}, - TableState::Sealed - ); + TableType::Props{{"count", builtinTypes.numberType}, {"method", methodTy}}, std::nullopt, TypeLevel{}, TableState::Sealed); generalize(methodTy); diff --git a/tests/IrBuilder.test.cpp b/tests/IrBuilder.test.cpp index da7cd9b1..bd7a02b2 100644 --- a/tests/IrBuilder.test.cpp +++ b/tests/IrBuilder.test.cpp @@ -12,9 +12,10 @@ #include -LUAU_FASTFLAG(LuauCodegenRemoveDeadStores5) LUAU_FASTFLAG(DebugLuauAbortingChecks) LUAU_FASTFLAG(LuauCodegenFixSplitStoreConstMismatch) +LUAU_FASTFLAG(LuauCodegenInstG) +LUAU_FASTFLAG(LuauCodegenFastcall3) using namespace Luau::CodeGen; @@ -1117,6 +1118,8 @@ bb_0: TEST_CASE_FIXTURE(IrBuilderFixture, "BuiltinFastcallsMayInvalidateMemory") { + ScopedFastFlag luauCodegenInstG{FFlag::LuauCodegenInstG, true}; + IrOp block = build.block(IrBlockKind::Internal); IrOp fallback = build.block(IrBlockKind::Fallback); @@ -1129,8 +1132,8 @@ TEST_CASE_FIXTURE(IrBuilderFixture, "BuiltinFastcallsMayInvalidateMemory") build.inst(IrCmd::CHECK_NO_METATABLE, table, fallback); build.inst(IrCmd::CHECK_READONLY, table, fallback); - build.inst(IrCmd::INVOKE_FASTCALL, build.constUint(LBF_SETMETATABLE), build.vmReg(1), build.vmReg(2), build.vmReg(3), build.constInt(3), - build.constInt(1)); + build.inst(IrCmd::INVOKE_FASTCALL, build.constUint(LBF_SETMETATABLE), build.vmReg(1), build.vmReg(2), build.vmReg(3), build.undef(), + build.constInt(3), build.constInt(1)); build.inst(IrCmd::CHECK_NO_METATABLE, table, fallback); build.inst(IrCmd::CHECK_READONLY, table, fallback); @@ -1151,7 +1154,7 @@ bb_0: %1 = LOAD_POINTER R0 CHECK_NO_METATABLE %1, bb_fallback_1 CHECK_READONLY %1, bb_fallback_1 - %4 = INVOKE_FASTCALL 61u, R1, R2, R3, 3i, 1i + %4 = INVOKE_FASTCALL 61u, R1, R2, R3, undef, 3i, 1i CHECK_NO_METATABLE %1, bb_fallback_1 CHECK_READONLY %1, bb_fallback_1 STORE_DOUBLE R1, 0.5 @@ -2546,8 +2549,6 @@ bb_0: ; useCount: 0 TEST_CASE_FIXTURE(IrBuilderFixture, "ForgprepInvalidation") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp block = build.block(IrBlockKind::Internal); IrOp followup = build.block(IrBlockKind::Internal); @@ -2587,14 +2588,14 @@ bb_1: TEST_CASE_FIXTURE(IrBuilderFixture, "FastCallEffects1") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; + ScopedFastFlag luauCodegenFastcall3{FFlag::LuauCodegenFastcall3, true}; IrOp entry = build.block(IrBlockKind::Internal); build.beginBlock(entry); - build.inst(IrCmd::FASTCALL, build.constUint(LBF_MATH_FREXP), build.vmReg(1), build.vmReg(2), build.undef(), build.constInt(1), build.constInt(2)); - build.inst(IrCmd::CHECK_TAG, build.vmReg(1), build.constTag(tnumber), build.vmExit(1)); - build.inst(IrCmd::CHECK_TAG, build.vmReg(2), build.constTag(tnumber), build.vmExit(1)); + build.inst(IrCmd::FASTCALL, build.constUint(LBF_MATH_FREXP), build.vmReg(1), build.vmReg(2), build.constInt(2)); + build.inst(IrCmd::CHECK_TAG, build.inst(IrCmd::LOAD_TAG, build.vmReg(1)), build.constTag(tnumber), build.vmExit(1)); + build.inst(IrCmd::CHECK_TAG, build.inst(IrCmd::LOAD_TAG, build.vmReg(2)), build.constTag(tnumber), build.vmExit(1)); build.inst(IrCmd::RETURN, build.vmReg(1), build.constInt(2)); updateUseCounts(build.function); @@ -2604,7 +2605,7 @@ TEST_CASE_FIXTURE(IrBuilderFixture, "FastCallEffects1") CHECK("\n" + toString(build.function, IncludeUseInfo::No) == R"( bb_0: ; in regs: R2 - FASTCALL 14u, R1, R2, undef, 1i, 2i + FASTCALL 14u, R1, R2, 2i RETURN R1, 2i )"); @@ -2612,14 +2613,14 @@ bb_0: TEST_CASE_FIXTURE(IrBuilderFixture, "FastCallEffects2") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; + ScopedFastFlag luauCodegenFastcall3{FFlag::LuauCodegenFastcall3, true}; IrOp entry = build.block(IrBlockKind::Internal); build.beginBlock(entry); - build.inst(IrCmd::FASTCALL, build.constUint(LBF_MATH_MODF), build.vmReg(1), build.vmReg(2), build.undef(), build.constInt(1), build.constInt(1)); - build.inst(IrCmd::CHECK_TAG, build.vmReg(1), build.constTag(tnumber), build.vmExit(1)); - build.inst(IrCmd::CHECK_TAG, build.vmReg(2), build.constTag(tnumber), build.vmExit(1)); + build.inst(IrCmd::FASTCALL, build.constUint(LBF_MATH_MODF), build.vmReg(1), build.vmReg(2), build.constInt(1)); + build.inst(IrCmd::CHECK_TAG, build.inst(IrCmd::LOAD_TAG, build.vmReg(1)), build.constTag(tnumber), build.vmExit(1)); + build.inst(IrCmd::CHECK_TAG, build.inst(IrCmd::LOAD_TAG, build.vmReg(2)), build.constTag(tnumber), build.vmExit(1)); build.inst(IrCmd::RETURN, build.vmReg(1), build.constInt(2)); updateUseCounts(build.function); @@ -2629,8 +2630,9 @@ TEST_CASE_FIXTURE(IrBuilderFixture, "FastCallEffects2") CHECK("\n" + toString(build.function, IncludeUseInfo::No) == R"( bb_0: ; in regs: R2 - FASTCALL 20u, R1, R2, undef, 1i, 1i - CHECK_TAG R2, tnumber, exit(1) + FASTCALL 20u, R1, R2, 1i + %3 = LOAD_TAG R2 + CHECK_TAG %3, tnumber, exit(1) RETURN R1, 2i )"); @@ -2642,7 +2644,7 @@ TEST_CASE_FIXTURE(IrBuilderFixture, "InferNumberTagFromLimitedContext") build.beginBlock(entry); build.inst(IrCmd::STORE_DOUBLE, build.vmReg(0), build.constDouble(2.0)); - build.inst(IrCmd::CHECK_TAG, build.vmReg(0), build.constTag(ttable), build.vmExit(1)); + build.inst(IrCmd::CHECK_TAG, build.inst(IrCmd::LOAD_TAG, build.vmReg(0)), build.constTag(ttable), build.vmExit(1)); build.inst(IrCmd::STORE_TVALUE, build.vmReg(1), build.inst(IrCmd::LOAD_TVALUE, build.vmReg(0))); build.inst(IrCmd::RETURN, build.vmReg(1), build.constInt(1)); @@ -2666,7 +2668,7 @@ TEST_CASE_FIXTURE(IrBuilderFixture, "DoNotProduceInvalidSplitStore1") build.beginBlock(entry); build.inst(IrCmd::STORE_INT, build.vmReg(0), build.constInt(1)); - build.inst(IrCmd::CHECK_TAG, build.vmReg(0), build.constTag(ttable), build.vmExit(1)); + build.inst(IrCmd::CHECK_TAG, build.inst(IrCmd::LOAD_TAG, build.vmReg(0)), build.constTag(ttable), build.vmExit(1)); build.inst(IrCmd::STORE_TVALUE, build.vmReg(1), build.inst(IrCmd::LOAD_TVALUE, build.vmReg(0))); build.inst(IrCmd::RETURN, build.vmReg(1), build.constInt(1)); @@ -2677,9 +2679,10 @@ TEST_CASE_FIXTURE(IrBuilderFixture, "DoNotProduceInvalidSplitStore1") CHECK("\n" + toString(build.function, IncludeUseInfo::No) == R"( bb_0: STORE_INT R0, 1i - CHECK_TAG R0, ttable, exit(1) - %2 = LOAD_TVALUE R0 - STORE_TVALUE R1, %2 + %1 = LOAD_TAG R0 + CHECK_TAG %1, ttable, exit(1) + %3 = LOAD_TVALUE R0 + STORE_TVALUE R1, %3 RETURN R1, 1i )"); @@ -2693,7 +2696,7 @@ TEST_CASE_FIXTURE(IrBuilderFixture, "DoNotProduceInvalidSplitStore2") build.beginBlock(entry); build.inst(IrCmd::STORE_INT, build.vmReg(0), build.constInt(1)); - build.inst(IrCmd::CHECK_TAG, build.vmReg(0), build.constTag(tnumber), build.vmExit(1)); + build.inst(IrCmd::CHECK_TAG, build.inst(IrCmd::LOAD_TAG, build.vmReg(0)), build.constTag(tnumber), build.vmExit(1)); build.inst(IrCmd::STORE_TVALUE, build.vmReg(1), build.inst(IrCmd::LOAD_TVALUE, build.vmReg(0))); build.inst(IrCmd::RETURN, build.vmReg(1), build.constInt(1)); @@ -2704,9 +2707,10 @@ TEST_CASE_FIXTURE(IrBuilderFixture, "DoNotProduceInvalidSplitStore2") CHECK("\n" + toString(build.function, IncludeUseInfo::No) == R"( bb_0: STORE_INT R0, 1i - CHECK_TAG R0, tnumber, exit(1) - %2 = LOAD_TVALUE R0 - STORE_TVALUE R1, %2 + %1 = LOAD_TAG R0 + CHECK_TAG %1, tnumber, exit(1) + %3 = LOAD_TVALUE R0 + STORE_TVALUE R1, %3 RETURN R1, 1i )"); @@ -2809,13 +2813,16 @@ bb_1: TEST_CASE_FIXTURE(IrBuilderFixture, "ExplicitUseOfRegisterInVarargSequence") { + ScopedFastFlag luauCodegenInstG{FFlag::LuauCodegenInstG, true}; + ScopedFastFlag luauCodegenFastcall3{FFlag::LuauCodegenFastcall3, true}; + IrOp entry = build.block(IrBlockKind::Internal); IrOp exit = build.block(IrBlockKind::Internal); build.beginBlock(entry); build.inst(IrCmd::FALLBACK_GETVARARGS, build.constUint(0), build.vmReg(1), build.constInt(-1)); - IrOp results = build.inst( - IrCmd::INVOKE_FASTCALL, build.constUint(0), build.vmReg(0), build.vmReg(1), build.vmReg(2), build.constInt(-1), build.constInt(-1)); + IrOp results = build.inst(IrCmd::INVOKE_FASTCALL, build.constUint(0), build.vmReg(0), build.vmReg(1), build.vmReg(2), build.undef(), + build.constInt(-1), build.constInt(-1)); build.inst(IrCmd::ADJUST_STACK_TO_REG, build.vmReg(0), results); build.inst(IrCmd::JUMP, exit); @@ -2830,7 +2837,7 @@ bb_0: ; successors: bb_1 ; out regs: R0... FALLBACK_GETVARARGS 0u, R1, -1i - %1 = INVOKE_FASTCALL 0u, R0, R1, R2, -1i, -1i + %1 = INVOKE_FASTCALL 0u, R0, R1, R2, undef, -1i, -1i ADJUST_STACK_TO_REG R0, %1 JUMP bb_1 @@ -3023,8 +3030,6 @@ bb_1: TEST_CASE_FIXTURE(IrBuilderFixture, "ForgprepImplicitUse") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); IrOp direct = build.block(IrBlockKind::Internal); IrOp fallback = build.block(IrBlockKind::Internal); @@ -3585,8 +3590,6 @@ TEST_SUITE_BEGIN("DeadStoreRemoval"); TEST_CASE_FIXTURE(IrBuilderFixture, "SimpleDoubleStore") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); build.beginBlock(entry); @@ -3636,8 +3639,6 @@ bb_0: TEST_CASE_FIXTURE(IrBuilderFixture, "UnusedAtReturn") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); build.beginBlock(entry); @@ -3669,8 +3670,6 @@ bb_0: TEST_CASE_FIXTURE(IrBuilderFixture, "UnusedAtReturnPartial") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); build.beginBlock(entry); @@ -3699,8 +3698,6 @@ bb_0: TEST_CASE_FIXTURE(IrBuilderFixture, "HiddenPointerUse1") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); build.beginBlock(entry); @@ -3729,8 +3726,6 @@ bb_0: TEST_CASE_FIXTURE(IrBuilderFixture, "HiddenPointerUse2") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); build.beginBlock(entry); @@ -3763,8 +3758,6 @@ bb_0: TEST_CASE_FIXTURE(IrBuilderFixture, "HiddenPointerUse3") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); build.beginBlock(entry); @@ -3793,8 +3786,6 @@ bb_0: TEST_CASE_FIXTURE(IrBuilderFixture, "HiddenPointerUse4") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); build.beginBlock(entry); @@ -3827,8 +3818,6 @@ bb_0: TEST_CASE_FIXTURE(IrBuilderFixture, "PartialVsFullStoresWithRecombination") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); build.beginBlock(entry); @@ -3852,8 +3841,6 @@ bb_0: TEST_CASE_FIXTURE(IrBuilderFixture, "IgnoreFastcallAdjustment") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); build.beginBlock(entry); @@ -3880,8 +3867,6 @@ bb_0: TEST_CASE_FIXTURE(IrBuilderFixture, "JumpImplicitLiveOut") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); IrOp next = build.block(IrBlockKind::Internal); @@ -3917,8 +3902,6 @@ bb_1: TEST_CASE_FIXTURE(IrBuilderFixture, "KeepCapturedRegisterStores") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); build.beginBlock(entry); @@ -3956,7 +3939,6 @@ bb_0: TEST_CASE_FIXTURE(IrBuilderFixture, "StoreCannotBeReplacedWithCheck") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; ScopedFastFlag debugLuauAbortingChecks{FFlag::DebugLuauAbortingChecks, true}; IrOp block = build.block(IrBlockKind::Internal); @@ -4025,8 +4007,6 @@ bb_2: TEST_CASE_FIXTURE(IrBuilderFixture, "FullStoreHasToBeObservableFromFallbacks") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); IrOp fallback = build.block(IrBlockKind::Fallback); IrOp last = build.block(IrBlockKind::Internal); @@ -4083,8 +4063,6 @@ bb_2: TEST_CASE_FIXTURE(IrBuilderFixture, "FullStoreHasToBeObservableFromFallbacks2") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); IrOp fallback = build.block(IrBlockKind::Fallback); IrOp last = build.block(IrBlockKind::Internal); @@ -4139,8 +4117,6 @@ bb_2: TEST_CASE_FIXTURE(IrBuilderFixture, "FullStoreHasToBeObservableFromFallbacks3") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); IrOp fallback = build.block(IrBlockKind::Fallback); IrOp last = build.block(IrBlockKind::Internal); @@ -4198,8 +4174,6 @@ bb_2: TEST_CASE_FIXTURE(IrBuilderFixture, "SafePartialValueStoresWithPreservedTag") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); IrOp fallback = build.block(IrBlockKind::Fallback); IrOp last = build.block(IrBlockKind::Internal); @@ -4253,8 +4227,6 @@ bb_2: TEST_CASE_FIXTURE(IrBuilderFixture, "SafePartialValueStoresWithPreservedTag2") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); IrOp fallback = build.block(IrBlockKind::Fallback); IrOp last = build.block(IrBlockKind::Internal); @@ -4307,8 +4279,6 @@ bb_2: TEST_CASE_FIXTURE(IrBuilderFixture, "DoNotReturnWithPartialStores") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); IrOp success = build.block(IrBlockKind::Internal); IrOp fail = build.block(IrBlockKind::Internal); @@ -4379,8 +4349,6 @@ bb_3: TEST_CASE_FIXTURE(IrBuilderFixture, "PartialOverFullValue") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - IrOp entry = build.block(IrBlockKind::Internal); build.beginBlock(entry); diff --git a/tests/IrLowering.test.cpp b/tests/IrLowering.test.cpp index ecdb522c..33d5602b 100644 --- a/tests/IrLowering.test.cpp +++ b/tests/IrLowering.test.cpp @@ -15,17 +15,15 @@ #include #include -LUAU_FASTFLAG(LuauCodegenRemoveDeadStores5) -LUAU_FASTFLAG(LuauCodegenDirectUserdataFlow) LUAU_FASTFLAG(LuauCompileTypeInfo) -LUAU_FASTFLAG(LuauLoadTypeInfo) -LUAU_FASTFLAG(LuauCodegenTypeInfo) LUAU_FASTFLAG(LuauCompileTempTypeInfo) LUAU_FASTFLAG(LuauCodegenAnalyzeHostVectorOps) LUAU_FASTFLAG(LuauCompileUserdataInfo) LUAU_FASTFLAG(LuauLoadUserdataInfo) LUAU_FASTFLAG(LuauCodegenUserdataOps) LUAU_FASTFLAG(LuauCodegenUserdataAlloc) +LUAU_FASTFLAG(LuauCompileFastcall3) +LUAU_FASTFLAG(LuauCodegenFastcall3) static std::string getCodegenAssembly(const char* source, bool includeIrTypes = false, int debugLevel = 1) { @@ -159,8 +157,6 @@ bb_bytecode_1: 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 @@ -238,8 +234,6 @@ bb_bytecode_1: 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 @@ -272,8 +266,6 @@ bb_bytecode_1: TEST_CASE("VectorSubMulDiv2") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - CHECK_EQ("\n" + getCodegenAssembly(R"( local function vec3combo(a: vector) local tmp = a * a @@ -302,8 +294,6 @@ bb_bytecode_1: 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 @@ -344,8 +334,6 @@ bb_bytecode_1: 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) @@ -382,8 +370,6 @@ bb_bytecode_1: TEST_CASE("DseInitialStackState") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo() while {} do @@ -422,7 +408,7 @@ bb_5: TEST_CASE("DseInitialStackState2") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; + ScopedFastFlag luauCodegenFastcall3{FFlag::LuauCodegenFastcall3, true}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(a) @@ -435,7 +421,7 @@ end bb_bytecode_0: CHECK_SAFE_ENV exit(1) CHECK_TAG R0, tnumber, exit(1) - FASTCALL 14u, R1, R0, undef, 1i, 2i + FASTCALL 14u, R1, R0, 2i INTERRUPT 5u RETURN R0, 1i )"); @@ -443,7 +429,7 @@ bb_bytecode_0: TEST_CASE("DseInitialStackState3") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; + ScopedFastFlag luauCodegenFastcall3{FFlag::LuauCodegenFastcall3, true}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(a) @@ -456,7 +442,7 @@ end bb_bytecode_0: CHECK_SAFE_ENV exit(1) CHECK_TAG R0, tnumber, exit(1) - FASTCALL 47u, R1, R0, undef, 1i, 1i + FASTCALL 47u, R1, R0, 1i INTERRUPT 5u RETURN R0, 1i )"); @@ -464,8 +450,6 @@ bb_bytecode_0: TEST_CASE("VectorConstantTag") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - CHECK_EQ("\n" + getCodegenAssembly(R"( local function vecrcp(a: vector) return vector(1, 2, 3) + a @@ -491,8 +475,6 @@ bb_bytecode_1: TEST_CASE("VectorNamecall") { - ScopedFastFlag luauCodegenDirectUserdataFlow{FFlag::LuauCodegenDirectUserdataFlow, true}; - CHECK_EQ("\n" + getCodegenAssembly(R"( local function abs(a: vector) return a:Abs() @@ -517,8 +499,6 @@ bb_bytecode_1: TEST_CASE("VectorRandomProp") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(a: vector) return a.XX + a.YY + a.ZZ @@ -559,7 +539,6 @@ bb_6: TEST_CASE("VectorCustomAccess") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; ScopedFastFlag luauCodegenAnalyzeHostVectorOps{FFlag::LuauCodegenAnalyzeHostVectorOps, true}; CHECK_EQ("\n" + getCodegenAssembly(R"( @@ -594,8 +573,6 @@ bb_bytecode_1: TEST_CASE("VectorCustomNamecall") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - ScopedFastFlag LuauCodegenDirectUserdataFlow{FFlag::LuauCodegenDirectUserdataFlow, true}; ScopedFastFlag luauCodegenAnalyzeHostVectorOps{FFlag::LuauCodegenAnalyzeHostVectorOps, true}; CHECK_EQ("\n" + getCodegenAssembly(R"( @@ -634,8 +611,6 @@ bb_bytecode_1: TEST_CASE("VectorCustomAccessChain") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - ScopedFastFlag LuauCodegenDirectUserdataFlow{FFlag::LuauCodegenDirectUserdataFlow, true}; ScopedFastFlag luauCodegenAnalyzeHostVectorOps{FFlag::LuauCodegenAnalyzeHostVectorOps, true}; CHECK_EQ("\n" + getCodegenAssembly(R"( @@ -688,8 +663,6 @@ bb_bytecode_1: TEST_CASE("VectorCustomNamecallChain") { - ScopedFastFlag luauCodegenRemoveDeadStores{FFlag::LuauCodegenRemoveDeadStores5, true}; - ScopedFastFlag LuauCodegenDirectUserdataFlow{FFlag::LuauCodegenDirectUserdataFlow, true}; ScopedFastFlag luauCodegenAnalyzeHostVectorOps{FFlag::LuauCodegenAnalyzeHostVectorOps, true}; CHECK_EQ("\n" + getCodegenAssembly(R"( @@ -749,9 +722,7 @@ bb_bytecode_1: TEST_CASE("VectorCustomNamecallChain2") { - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCodegenDirectUserdataFlow, true}, - {FFlag::LuauCodegenAnalyzeHostVectorOps, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCodegenAnalyzeHostVectorOps, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( type Vertex = {n: vector, b: vector} @@ -827,8 +798,6 @@ bb_6: TEST_CASE("UserDataGetIndex") { - ScopedFastFlag luauCodegenDirectUserdataFlow{FFlag::LuauCodegenDirectUserdataFlow, true}; - CHECK_EQ("\n" + getCodegenAssembly(R"( local function getxy(a: Point) return a.x + a.y @@ -859,8 +828,6 @@ bb_4: TEST_CASE("UserDataSetIndex") { - ScopedFastFlag luauCodegenDirectUserdataFlow{FFlag::LuauCodegenDirectUserdataFlow, true}; - CHECK_EQ("\n" + getCodegenAssembly(R"( local function setxy(a: Point) a.x = 3 @@ -887,8 +854,6 @@ bb_bytecode_1: TEST_CASE("UserDataNamecall") { - ScopedFastFlag luauCodegenDirectUserdataFlow{FFlag::LuauCodegenDirectUserdataFlow, true}; - CHECK_EQ("\n" + getCodegenAssembly(R"( local function getxy(a: Point) return a:GetX() + a:GetY() @@ -925,8 +890,7 @@ bb_4: TEST_CASE("ExplicitUpvalueAndLocalTypes") { - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local y: vector = ... @@ -969,8 +933,7 @@ bb_bytecode_0: TEST_CASE("FastcallTypeInferThroughLocal") { - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function getsum(x, c) @@ -985,43 +948,40 @@ end /* includeIrTypes */ true), R"( ; function getsum($arg0, $arg1) line 2 -; R2: vector from 0 to 17 +; R2: vector from 0 to 18 bb_bytecode_0: - %0 = LOAD_TVALUE R0 - STORE_TVALUE R3, %0 STORE_DOUBLE R4, 2 STORE_TAG R4, tnumber STORE_DOUBLE R5, 3 STORE_TAG R5, tnumber CHECK_SAFE_ENV exit(4) - CHECK_TAG R3, tnumber, exit(4) - %13 = LOAD_DOUBLE R3 - STORE_VECTOR R2, %13, 2, 3 + CHECK_TAG R0, tnumber, exit(4) + %11 = LOAD_DOUBLE R0 + STORE_VECTOR R2, %11, 2, 3 STORE_TAG R2, tvector JUMP_IF_FALSY R1, bb_bytecode_1, bb_3 bb_3: - CHECK_TAG R2, tvector, exit(8) - %21 = LOAD_FLOAT R2, 0i - %26 = LOAD_FLOAT R2, 4i - %35 = ADD_NUM %21, %26 - STORE_DOUBLE R3, %35 + CHECK_TAG R2, tvector, exit(9) + %19 = LOAD_FLOAT R2, 0i + %24 = LOAD_FLOAT R2, 4i + %33 = ADD_NUM %19, %24 + STORE_DOUBLE R3, %33 STORE_TAG R3, tnumber - INTERRUPT 13u + INTERRUPT 14u RETURN R3, 1i bb_bytecode_1: - CHECK_TAG R2, tvector, exit(14) - %42 = LOAD_FLOAT R2, 8i - STORE_DOUBLE R3, %42 + CHECK_TAG R2, tvector, exit(15) + %40 = LOAD_FLOAT R2, 8i + STORE_DOUBLE R3, %40 STORE_TAG R3, tnumber - INTERRUPT 16u + INTERRUPT 17u RETURN R3, 1i )"); } TEST_CASE("FastcallTypeInferThroughUpvalue") { - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local v = ... @@ -1040,48 +1000,45 @@ end ; function getsum($arg0, $arg1) line 4 ; U0: vector bb_bytecode_0: - %0 = LOAD_TVALUE R0 - STORE_TVALUE R3, %0 STORE_DOUBLE R4, 2 STORE_TAG R4, tnumber STORE_DOUBLE R5, 3 STORE_TAG R5, tnumber CHECK_SAFE_ENV exit(4) - CHECK_TAG R3, tnumber, exit(4) - %13 = LOAD_DOUBLE R3 - STORE_VECTOR R2, %13, 2, 3 + CHECK_TAG R0, tnumber, exit(4) + %11 = LOAD_DOUBLE R0 + STORE_VECTOR R2, %11, 2, 3 STORE_TAG R2, tvector SET_UPVALUE U0, R2, tvector JUMP_IF_FALSY R1, bb_bytecode_1, bb_3 bb_3: GET_UPVALUE R4, U0 - CHECK_TAG R4, tvector, exit(10) - %23 = LOAD_FLOAT R4, 0i - STORE_DOUBLE R3, %23 + CHECK_TAG R4, tvector, exit(11) + %21 = LOAD_FLOAT R4, 0i + STORE_DOUBLE R3, %21 STORE_TAG R3, tnumber GET_UPVALUE R5, U0 - CHECK_TAG R5, tvector, exit(13) - %29 = LOAD_FLOAT R5, 4i - %38 = ADD_NUM %23, %29 - STORE_DOUBLE R2, %38 + CHECK_TAG R5, tvector, exit(14) + %27 = LOAD_FLOAT R5, 4i + %36 = ADD_NUM %21, %27 + STORE_DOUBLE R2, %36 STORE_TAG R2, tnumber - INTERRUPT 16u + INTERRUPT 17u RETURN R2, 1i bb_bytecode_1: GET_UPVALUE R3, U0 - CHECK_TAG R3, tvector, exit(18) - %46 = LOAD_FLOAT R3, 8i - STORE_DOUBLE R2, %46 + CHECK_TAG R3, tvector, exit(19) + %44 = LOAD_FLOAT R3, 8i + STORE_DOUBLE R2, %44 STORE_TAG R2, tnumber - INTERRUPT 20u + INTERRUPT 21u RETURN R2, 1i )"); } TEST_CASE("LoadAndMoveTypePropagation") { - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function getsum(n) @@ -1148,8 +1105,7 @@ bb_bytecode_4: TEST_CASE("ArgumentTypeRefinement") { - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function getsum(x, y) @@ -1164,31 +1120,28 @@ end bb_bytecode_0: STORE_DOUBLE R3, 1 STORE_TAG R3, tnumber - %2 = LOAD_TVALUE R1 - STORE_TVALUE R4, %2 STORE_DOUBLE R5, 3 STORE_TAG R5, tnumber CHECK_SAFE_ENV exit(4) - CHECK_TAG R4, tnumber, exit(4) - %14 = LOAD_DOUBLE R4 - STORE_VECTOR R2, 1, %14, 3 + CHECK_TAG R1, tnumber, exit(4) + %12 = LOAD_DOUBLE R1 + STORE_VECTOR R2, 1, %12, 3 STORE_TAG R2, tvector - %18 = LOAD_TVALUE R2 - STORE_TVALUE R0, %18 - %22 = LOAD_FLOAT R0, 4i - %27 = LOAD_FLOAT R0, 8i - %36 = ADD_NUM %22, %27 - STORE_DOUBLE R2, %36 + %16 = LOAD_TVALUE R2 + STORE_TVALUE R0, %16 + %20 = LOAD_FLOAT R0, 4i + %25 = LOAD_FLOAT R0, 8i + %34 = ADD_NUM %20, %25 + STORE_DOUBLE R2, %34 STORE_TAG R2, tnumber - INTERRUPT 13u + INTERRUPT 14u RETURN R2, 1i )"); } TEST_CASE("InlineFunctionType") { - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function inl(v: vector, s: number) @@ -1236,8 +1189,7 @@ bb_bytecode_0: TEST_CASE("ResolveTablePathTypes") { - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( type Vertex = {pos: vector, normal: vector} @@ -1291,8 +1243,7 @@ bb_6: TEST_CASE("ResolvableSimpleMath") { - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}}; CHECK_EQ("\n" + getCodegenHeader(R"( type Vertex = { p: vector, uv: vector, n: vector, t: vector, b: vector, h: number } @@ -1348,9 +1299,7 @@ end TEST_CASE("ResolveVectorNamecalls") { - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCodegenDirectUserdataFlow, true}, - {FFlag::LuauCodegenAnalyzeHostVectorOps, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCodegenAnalyzeHostVectorOps, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( type Vertex = {pos: vector, normal: vector} @@ -1414,8 +1363,7 @@ bb_6: TEST_CASE("ImmediateTypeAnnotationHelp") { - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(arr, i) @@ -1453,8 +1401,8 @@ bb_2: TEST_CASE("UnaryTypeResolve") { - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileFastcall3, true}, + {FFlag::LuauCodegenFastcall3, true}}; CHECK_EQ("\n" + getCodegenHeader(R"( local function foo(a, b: vector, c) @@ -1467,17 +1415,16 @@ end R"( ; function foo(a, b, c) line 2 ; R1: vector [argument 'b'] -; R3: boolean from 0 to 16 [local 'd'] -; R4: vector from 1 to 16 [local 'e'] -; R5: number from 2 to 16 [local 'f'] -; R7: vector from 13 to 15 +; R3: boolean from 0 to 17 [local 'd'] +; R4: vector from 1 to 17 [local 'e'] +; R5: number from 2 to 17 [local 'f'] +; R7: vector from 14 to 16 )"); } TEST_CASE("ForInManualAnnotation") { - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( type Vertex = {pos: vector, normal: vector} @@ -1572,8 +1519,7 @@ bb_12: TEST_CASE("ForInAutoAnnotationIpairs") { - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}}; CHECK_EQ("\n" + getCodegenHeader(R"( type Vertex = {pos: vector, normal: vector} @@ -1600,8 +1546,7 @@ end TEST_CASE("ForInAutoAnnotationPairs") { - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}}; CHECK_EQ("\n" + getCodegenHeader(R"( type Vertex = {pos: vector, normal: vector} @@ -1628,8 +1573,7 @@ end TEST_CASE("ForInAutoAnnotationGeneric") { - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}}; CHECK_EQ("\n" + getCodegenHeader(R"( type Vertex = {pos: vector, normal: vector} @@ -1661,8 +1605,7 @@ TEST_CASE("CustomUserdataTypesTemp") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, false}, + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, false}, {FFlag::LuauLoadUserdataInfo, true}}; CHECK_EQ("\n" + getCodegenHeader(R"( @@ -1683,8 +1626,7 @@ TEST_CASE("CustomUserdataTypes") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}}; CHECK_EQ("\n" + getCodegenHeader(R"( @@ -1705,9 +1647,8 @@ TEST_CASE("CustomUserdataPropertyAccess") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, - {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenDirectUserdataFlow, true}, {FFlag::LuauCodegenUserdataOps, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, + {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(v: vec2) @@ -1742,9 +1683,8 @@ TEST_CASE("CustomUserdataPropertyAccess2") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, - {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenDirectUserdataFlow, true}, {FFlag::LuauCodegenUserdataOps, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, + {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(a: mat3) @@ -1781,10 +1721,8 @@ TEST_CASE("CustomUserdataNamecall1") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, - {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenDirectUserdataFlow, true}, {FFlag::LuauCodegenAnalyzeHostVectorOps, true}, - {FFlag::LuauCodegenUserdataOps, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, + {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenAnalyzeHostVectorOps, true}, {FFlag::LuauCodegenUserdataOps, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(a: vec2, b: vec2) @@ -1830,10 +1768,9 @@ TEST_CASE("CustomUserdataNamecall2") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, - {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenDirectUserdataFlow, true}, {FFlag::LuauCodegenAnalyzeHostVectorOps, true}, - {FFlag::LuauCodegenUserdataOps, true}, {FFlag::LuauCodegenUserdataAlloc, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, + {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenAnalyzeHostVectorOps, true}, {FFlag::LuauCodegenUserdataOps, true}, + {FFlag::LuauCodegenUserdataAlloc, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(a: vec2, b: vec2) @@ -1882,9 +1819,8 @@ TEST_CASE("CustomUserdataMetamethodDirectFlow") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, - {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenDirectUserdataFlow, true}, {FFlag::LuauCodegenUserdataOps, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, + {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(a: mat3, b: mat3) @@ -1916,9 +1852,8 @@ TEST_CASE("CustomUserdataMetamethodDirectFlow2") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, - {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenDirectUserdataFlow, true}, {FFlag::LuauCodegenUserdataOps, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, + {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(a: mat3) @@ -1948,9 +1883,8 @@ TEST_CASE("CustomUserdataMetamethodDirectFlow3") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, - {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenDirectUserdataFlow, true}, {FFlag::LuauCodegenUserdataOps, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, + {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(a: sequence) @@ -1980,10 +1914,8 @@ TEST_CASE("CustomUserdataMetamethod") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauLoadTypeInfo, true}, {FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCodegenTypeInfo, true}, - {FFlag::LuauCodegenRemoveDeadStores5, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, - {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenDirectUserdataFlow, true}, {FFlag::LuauCodegenUserdataOps, true}, - {FFlag::LuauCodegenUserdataAlloc, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, + {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}, {FFlag::LuauCodegenUserdataAlloc, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(a: vec2, b: vec2, c: vec2) diff --git a/tests/Linter.test.cpp b/tests/Linter.test.cpp index 9f7aa77a..807b5e73 100644 --- a/tests/Linter.test.cpp +++ b/tests/Linter.test.cpp @@ -8,6 +8,9 @@ #include "doctest.h" LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauAttributeSyntax); +LUAU_FASTFLAG(LuauNativeAttribute); +LUAU_FASTFLAG(LintRedundantNativeAttribute); using namespace Luau; @@ -1955,4 +1958,32 @@ local _ = a <= (b == 0) CHECK_EQ(result.warnings[4].text, "X <= Y <= Z is equivalent to (X <= Y) <= Z; did you mean X <= Y and Y <= Z?"); } +TEST_CASE_FIXTURE(Fixture, "RedundantNativeAttribute") +{ + ScopedFastFlag sff[] = {{FFlag::LuauAttributeSyntax, true}, {FFlag::LuauNativeAttribute, true}, {FFlag::LintRedundantNativeAttribute, true}}; + + LintResult result = lint(R"( +--!native + +@native +local function f(a) + @native + local function g(b) + return (a + b) + end + return g +end + +f(3)(4) +)"); + + REQUIRE(2 == result.warnings.size()); + + CHECK_EQ(result.warnings[0].text, "native attribute on a function is redundant in a native module; consider removing it"); + CHECK_EQ(result.warnings[0].location, Location(Position(3, 0), Position(3, 7))); + + CHECK_EQ(result.warnings[1].text, "native attribute on a function is redundant in a native module; consider removing it"); + CHECK_EQ(result.warnings[1].location, Location(Position(5, 4), Position(5, 11))); +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.functions.test.cpp b/tests/TypeInfer.functions.test.cpp index 48d130dd..4f8ed3eb 100644 --- a/tests/TypeInfer.functions.test.cpp +++ b/tests/TypeInfer.functions.test.cpp @@ -2298,10 +2298,14 @@ end if (FFlag::DebugLuauDeferredConstraintResolution) { LUAU_REQUIRE_ERROR_COUNT(4, result); - CHECK(toString(result.errors[0]) == "Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub"); - CHECK(toString(result.errors[1]) == "Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub"); - CHECK(toString(result.errors[2]) == "Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub"); - CHECK(toString(result.errors[3]) == "Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub"); + CHECK(toString(result.errors[0]) == + "Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub"); + CHECK(toString(result.errors[1]) == + "Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub"); + CHECK(toString(result.errors[2]) == + "Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub"); + CHECK(toString(result.errors[3]) == + "Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub"); } else { @@ -2719,7 +2723,6 @@ end _ = _,{} )"); - } diff --git a/tests/TypeInfer.provisional.test.cpp b/tests/TypeInfer.provisional.test.cpp index 8e81b0cc..a34af12d 100644 --- a/tests/TypeInfer.provisional.test.cpp +++ b/tests/TypeInfer.provisional.test.cpp @@ -1230,4 +1230,45 @@ TEST_CASE_FIXTURE(Fixture, "table_containing_non_final_type_is_erroneously_cache CHECK(n1 == n2); } +// This is doable with the new solver, but there are some problems we have to work out first. +// CLI-111113 +TEST_CASE_FIXTURE(Fixture, "we_cannot_infer_functions_that_return_inconsistently") +{ + CheckResult result = check(R"( + function find_first(tbl: {T}, el) + for i, e in tbl do + if e == el then + return i + end + end + return nil + end + )"); + +#if 0 + // This #if block describes what should happen. + LUAU_CHECK_NO_ERRORS(result); + + // The second argument has type unknown because the == operator does not + // constrain the type of el. + CHECK("({T}, unknown) -> number?" == toString(requireType("find_first"))); +#else + // This is what actually happens right now. + + if (FFlag::DebugLuauDeferredConstraintResolution) + { + LUAU_CHECK_ERROR_COUNT(2, result); + + // The second argument should be unknown. CLI-111111 + CHECK("({T}, 'b) -> number" == toString(requireType("find_first"))); + } + else + { + LUAU_CHECK_ERROR_COUNT(1, result); + + CHECK("({T}, b) -> number" == toString(requireType("find_first"))); + } +#endif +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.tables.test.cpp b/tests/TypeInfer.tables.test.cpp index 2c9614d0..4dbedd51 100644 --- a/tests/TypeInfer.tables.test.cpp +++ b/tests/TypeInfer.tables.test.cpp @@ -21,12 +21,30 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping); LUAU_FASTFLAG(LuauAlwaysCommitInferencesOfFunctionCalls); LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering); LUAU_FASTFLAG(DebugLuauSharedSelf); -LUAU_FASTFLAG(LuauMetatableInstantiationCloneCheck); LUAU_DYNAMIC_FASTFLAG(LuauImproveNonFunctionCallError) TEST_SUITE_BEGIN("TableTests"); +TEST_CASE_FIXTURE(BuiltinsFixture, "generalization_shouldnt_seal_table_in_len_family_fn") +{ + if (!FFlag::DebugLuauDeferredConstraintResolution) + return; + CheckResult result = check(R"( +local t = {} +for i = #t, 2, -1 do + t[i] = t[i + 1] +end + )"); + + LUAU_REQUIRE_NO_ERRORS(result); + const TableType* tType = get(requireType("t")); + REQUIRE(tType != nullptr); + REQUIRE(tType->indexer); + CHECK_EQ(tType->indexer->indexType, builtinTypes->numberType); + CHECK_EQ(follow(tType->indexer->indexResultType), builtinTypes->unknownType); +} + TEST_CASE_FIXTURE(BuiltinsFixture, "LUAU_ASSERT_arg_exprs_doesnt_trigger_assert") { CheckResult result = check(R"( @@ -4150,9 +4168,7 @@ TEST_CASE_FIXTURE(Fixture, "write_annotations_are_unsupported_even_with_the_new_ TEST_CASE_FIXTURE(Fixture, "read_and_write_only_table_properties_are_unsupported") { - ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, false} - }; + ScopedFastFlag sff[] = {{FFlag::DebugLuauDeferredConstraintResolution, false}}; CheckResult result = check(R"( type W = {read x: number} @@ -4176,9 +4192,7 @@ TEST_CASE_FIXTURE(Fixture, "read_and_write_only_table_properties_are_unsupported TEST_CASE_FIXTURE(Fixture, "read_ond_write_only_indexers_are_unsupported") { - ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, false} - }; + ScopedFastFlag sff[] = {{FFlag::DebugLuauDeferredConstraintResolution, false}}; CheckResult result = check(R"( type T = {read [string]: number} @@ -4198,9 +4212,7 @@ TEST_CASE_FIXTURE(Fixture, "table_writes_introduce_write_properties") if (!FFlag::DebugLuauDeferredConstraintResolution) return; - ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true} - }; + ScopedFastFlag sff[] = {{FFlag::DebugLuauDeferredConstraintResolution, true}}; CheckResult result = check(R"( function oc(player, speaker) @@ -4354,8 +4366,6 @@ TEST_CASE_FIXTURE(Fixture, "mymovie_read_write_tables_bug_2") TEST_CASE_FIXTURE(BuiltinsFixture, "instantiated_metatable_frozen_table_clone_mutation") { - ScopedFastFlag luauMetatableInstantiationCloneCheck{FFlag::LuauMetatableInstantiationCloneCheck, true}; - fileResolver.source["game/worker"] = R"( type WorkerImpl = { destroy: (self: Worker) -> boolean, @@ -4533,8 +4543,24 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_literal_inference_assert") end; } )"); +} +TEST_CASE_FIXTURE(BuiltinsFixture, "metatable_table_assertion_crash") +{ + CheckResult result = check(R"( + local NexusInstance = {} + function NexusInstance:__InitMetaMethods(): () + local Metatable = {} + local OriginalIndexTable = getmetatable(self).__index + setmetatable(self, Metatable) + Metatable.__newindex = function(_, Index: string, Value: any): () + --Return if the new and old values are the same. + if self[Index] == Value then + end + end + end + )"); } TEST_SUITE_END(); diff --git a/tests/TypeInfer.test.cpp b/tests/TypeInfer.test.cpp index 60903733..1d1dd999 100644 --- a/tests/TypeInfer.test.cpp +++ b/tests/TypeInfer.test.cpp @@ -1536,7 +1536,7 @@ TEST_CASE_FIXTURE(Fixture, "typeof_cannot_refine_builtin_alias") freeze(arena); - (void) check(R"( + (void)check(R"( function foo(x) if typeof(x) == 'GlobalTable' then end diff --git a/tests/conformance/bitwise.lua b/tests/conformance/bitwise.lua index f394dc5b..c2536508 100644 --- a/tests/conformance/bitwise.lua +++ b/tests/conformance/bitwise.lua @@ -72,6 +72,7 @@ for _, b in pairs(c) do assert(bit32.bxor(b, b) == 0) assert(bit32.bxor(b, 0) == b) assert(bit32.bxor(b, b, b) == b) + assert(bit32.bxor(b, b, b, b) == 0) assert(bit32.bnot(b) ~= b) assert(bit32.bnot(bit32.bnot(b)) == b) assert(bit32.bnot(b) == 2^32 - 1 - b) diff --git a/tests/conformance/math.lua b/tests/conformance/math.lua index 9262f4ea..b8fc882a 100644 --- a/tests/conformance/math.lua +++ b/tests/conformance/math.lua @@ -268,10 +268,33 @@ assert(math.min(1) == 1) assert(math.min(1, 2) == 1) assert(math.min(1, 2, -1) == -1) assert(math.min(1, -1, 2) == -1) +assert(math.min(1, -1, 2, -2) == -2) assert(math.max(1) == 1) assert(math.max(1, 2) == 2) assert(math.max(1, 2, -1) == 2) assert(math.max(1, -1, 2) == 2) +assert(math.max(1, -1, 2, -2) == 2) + +local ma, mb, mc, md + +assert(pcall(function() + ma = 1 + mb = -1 + mc = 2 + md = -2 +end) == true) + +-- min/max without contant-folding +assert(math.min(ma) == 1) +assert(math.min(ma, mc) == 1) +assert(math.min(ma, mc, mb) == -1) +assert(math.min(ma, mb, mc) == -1) +assert(math.min(ma, mb, mc, md) == -2) +assert(math.max(ma) == 1) +assert(math.max(ma, mc) == 2) +assert(math.max(ma, mc, mb) == 2) +assert(math.max(ma, mb, mc) == 2) +assert(math.max(ma, mb, mc, md) == 2) -- noise assert(math.noise(0.5) == 0)