From a5c6a38b103266809ebfae4831a17523a23c8dde Mon Sep 17 00:00:00 2001 From: Andy Friesen Date: Fri, 13 Jan 2023 14:10:01 -0800 Subject: [PATCH] Sync to upstream/release/559 (#804) * Fix autocompletion of if-then-else expressions * Fix a potential crash surrounding improper use of `%*` in a string format specifier * All Python scripts now invoke Python via `python3` rather than `python`. * Improved error handling for string interpolation with too many arguments. Co-authored-by: Arseny Kapoulkine Co-authored-by: Vyacheslav Egorov --- Analysis/include/Luau/Predicate.h | 25 +- Analysis/include/Luau/ToString.h | 1 - Analysis/include/Luau/TypeReduction.h | 4 +- Analysis/src/Autocomplete.cpp | 19 +- Analysis/src/ToString.cpp | 25 +- Analysis/src/TypeReduction.cpp | 92 +- Ast/src/Parser.cpp | 15 +- CodeGen/include/Luau/AssemblyBuilderX64.h | 5 +- CodeGen/src/AssemblyBuilderX64.cpp | 56 +- CodeGen/src/CodeGen.cpp | 10 + CodeGen/src/EmitCommonX64.cpp | 16 + CodeGen/src/EmitCommonX64.h | 17 +- CodeGen/src/EmitInstructionX64.cpp | 68 ++ CodeGen/src/EmitInstructionX64.h | 3 + CodeGen/src/Fallbacks.cpp | 13 - CodeGen/src/Fallbacks.h | 1 - CodeGen/src/IrData.h | 280 +++++ CodeGen/src/IrDump.cpp | 379 +++++++ CodeGen/src/IrDump.h | 32 + CodeGen/src/IrUtils.h | 161 +++ CodeGen/src/NativeState.cpp | 3 +- CodeGen/src/NativeState.h | 2 + Compiler/include/Luau/BytecodeBuilder.h | 4 + Compiler/src/BytecodeBuilder.cpp | 201 +++- Compiler/src/Compiler.cpp | 4 +- Sources.cmake | 4 + VM/include/lualib.h | 4 +- VM/src/laux.cpp | 4 +- VM/src/ldblib.cpp | 2 +- VM/src/loslib.cpp | 2 +- VM/src/lstrlib.cpp | 46 +- VM/src/ltablib.cpp | 2 +- VM/src/lutf8lib.cpp | 2 +- bench/bench.py | 2 +- tests/AssemblyBuilderX64.test.cpp | 6 + tests/Autocomplete.test.cpp | 48 +- tests/Compiler.test.cpp | 1255 +++++++++++---------- tests/Conformance.test.cpp | 2 + tests/Parser.test.cpp | 2 - tests/ToString.test.cpp | 3 - tests/TypeReduction.test.cpp | 132 ++- tests/conformance/strings.lua | 10 + tests/conformance/tables.lua | 14 + tools/faillist.txt | 12 +- tools/heapgraph.py | 2 +- tools/heapstat.py | 2 +- tools/lvmexecute_split.py | 4 +- tools/numprint.py | 2 +- tools/patchtests.py | 2 +- tools/perfgraph.py | 2 +- tools/perfstat.py | 2 +- tools/stack-usage-reporter.py | 2 +- tools/test_dcr.py | 1 + tools/tracegraph.py | 2 +- 54 files changed, 2186 insertions(+), 823 deletions(-) create mode 100644 CodeGen/src/IrData.h create mode 100644 CodeGen/src/IrDump.cpp create mode 100644 CodeGen/src/IrDump.h create mode 100644 CodeGen/src/IrUtils.h diff --git a/Analysis/include/Luau/Predicate.h b/Analysis/include/Luau/Predicate.h index 8d486ad..50fd7ed 100644 --- a/Analysis/include/Luau/Predicate.h +++ b/Analysis/include/Luau/Predicate.h @@ -57,11 +57,7 @@ struct AndPredicate PredicateVec lhs; PredicateVec rhs; - AndPredicate(PredicateVec&& lhs, PredicateVec&& rhs) - : lhs(std::move(lhs)) - , rhs(std::move(rhs)) - { - } + AndPredicate(PredicateVec&& lhs, PredicateVec&& rhs); }; struct OrPredicate @@ -69,11 +65,7 @@ struct OrPredicate PredicateVec lhs; PredicateVec rhs; - OrPredicate(PredicateVec&& lhs, PredicateVec&& rhs) - : lhs(std::move(lhs)) - , rhs(std::move(rhs)) - { - } + OrPredicate(PredicateVec&& lhs, PredicateVec&& rhs); }; struct NotPredicate @@ -81,6 +73,19 @@ struct NotPredicate PredicateVec predicates; }; +// Outside definition works around clang 15 issue where vector instantiation is triggered while Predicate is still incomplete +inline AndPredicate::AndPredicate(PredicateVec&& lhs, PredicateVec&& rhs) + : lhs(std::move(lhs)) + , rhs(std::move(rhs)) +{ +} + +inline OrPredicate::OrPredicate(PredicateVec&& lhs, PredicateVec&& rhs) + : lhs(std::move(lhs)) + , rhs(std::move(rhs)) +{ +} + template const T* get(const Predicate& predicate) { diff --git a/Analysis/include/Luau/ToString.h b/Analysis/include/Luau/ToString.h index dd8aef5..461a8ff 100644 --- a/Analysis/include/Luau/ToString.h +++ b/Analysis/include/Luau/ToString.h @@ -45,7 +45,6 @@ struct ToStringOptions bool hideTableKind = false; // If true, all tables will be surrounded with plain '{}' bool hideNamedFunctionTypeParameters = false; // If true, type parameters of functions will be hidden at top-level. bool hideFunctionSelfArgument = false; // If true, `self: X` will be omitted from the function signature if the function has self - bool DEPRECATED_indent = false; // TODO Deprecated field, prune when clipping flag FFlagLuauLineBreaksDeterminIndents size_t maxTableLength = size_t(FInt::LuauTableTypeMaximumStringifierLength); // Only applied to TableTypes size_t maxTypeLength = size_t(FInt::LuauTypeMaximumStringifierLength); ToStringNameMap nameMap; diff --git a/Analysis/include/Luau/TypeReduction.h b/Analysis/include/Luau/TypeReduction.h index 7df7edf..a7cec94 100644 --- a/Analysis/include/Luau/TypeReduction.h +++ b/Analysis/include/Luau/TypeReduction.h @@ -27,8 +27,8 @@ private: DenseHashMap cachedTypes{nullptr}; DenseHashMap cachedTypePacks{nullptr}; - std::optional reduceImpl(TypeId ty); - std::optional reduceImpl(TypePackId tp); + std::pair, bool> reduceImpl(TypeId ty); + std::pair, bool> reduceImpl(TypePackId tp); // Computes an *estimated length* of the cartesian product of the given type. size_t cartesianProductSize(TypeId ty) const; diff --git a/Analysis/src/Autocomplete.cpp b/Analysis/src/Autocomplete.cpp index 9f046b8..dd75aa0 100644 --- a/Analysis/src/Autocomplete.cpp +++ b/Analysis/src/Autocomplete.cpp @@ -13,6 +13,7 @@ #include LUAU_FASTFLAGVARIABLE(LuauCompleteTableKeysBetter, false); +LUAU_FASTFLAGVARIABLE(LuauFixAutocompleteInIf, false); static const std::unordered_set kStatementStartingKeywords = { "while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"}; @@ -1487,8 +1488,22 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M return {{{"then", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry, AutocompleteContext::Keyword}; } else if (AstStatIf* statIf = extractStat(ancestry); - statIf && (!statIf->thenLocation || statIf->thenLocation->containsClosed(position))) - return {{{"then", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry, AutocompleteContext::Keyword}; + statIf && (!statIf->thenLocation || statIf->thenLocation->containsClosed(position)) && + (!FFlag::LuauFixAutocompleteInIf || (statIf->condition && !statIf->condition->location.containsClosed(position)))) + { + if (FFlag::LuauFixAutocompleteInIf) + { + AutocompleteEntryMap ret; + ret["then"] = {AutocompleteEntryKind::Keyword}; + ret["and"] = {AutocompleteEntryKind::Keyword}; + ret["or"] = {AutocompleteEntryKind::Keyword}; + return {std::move(ret), ancestry, AutocompleteContext::Keyword}; + } + else + { + return {{{"then", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry, AutocompleteContext::Keyword}; + } + } else if (AstStatRepeat* statRepeat = node->as(); statRepeat && statRepeat->condition->is()) return autocompleteExpression(sourceModule, *module, builtinTypes, typeArena, ancestry, position); else if (AstStatRepeat* statRepeat = extractStat(ancestry); statRepeat) diff --git a/Analysis/src/ToString.cpp b/Analysis/src/ToString.cpp index af038f1..3e8dc65 100644 --- a/Analysis/src/ToString.cpp +++ b/Analysis/src/ToString.cpp @@ -15,10 +15,8 @@ LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) LUAU_FASTFLAG(LuauUnknownAndNeverType) -LUAU_FASTFLAGVARIABLE(LuauLineBreaksDetermineIndents, false) LUAU_FASTFLAGVARIABLE(LuauFunctionReturnStringificationFixup, false) LUAU_FASTFLAGVARIABLE(LuauUnseeArrayTtv, false) -LUAU_FASTFLAGVARIABLE(LuauSerializeNilUnionAsNil, false) /* * Prefix generic typenames with gen- @@ -277,20 +275,10 @@ struct StringifierState private: void emitIndentation() { - if (!FFlag::LuauLineBreaksDetermineIndents) - { - if (!opts.DEPRECATED_indent) - return; + if (!opts.useLineBreaks) + return; - emit(std::string(indentation, ' ')); - } - else - { - if (!opts.useLineBreaks) - return; - - emit(std::string(indentation, ' ')); - } + emit(std::string(indentation, ' ')); } }; @@ -781,11 +769,8 @@ struct TypeStringifier if (results.size() > 1) s = ")?"; - if (FFlag::LuauSerializeNilUnionAsNil) - { - if (!hasNonNilDisjunct) - s = "nil"; - } + if (!hasNonNilDisjunct) + s = "nil"; state.emit(s); } diff --git a/Analysis/src/TypeReduction.cpp b/Analysis/src/TypeReduction.cpp index db79deb..8d837dd 100644 --- a/Analysis/src/TypeReduction.cpp +++ b/Analysis/src/TypeReduction.cpp @@ -51,6 +51,16 @@ struct TypeReducer NotNull builtinTypes; NotNull handle; + std::unordered_map copies; + std::deque seen; + int depth = 0; + + // When we encounter _any type_ that which is usually mutated in-place, we need to not cache the result. + // e.g. `'a & {} T` may have an upper bound constraint `{}` placed upon `'a`, but this constraint was not + // known when we decided to reduce this intersection type. By not caching, we'll always be forced to perform + // the reduction calculus over again. + bool cacheOk = true; + TypeId reduce(TypeId ty); TypePackId reduce(TypePackId tp); @@ -60,13 +70,11 @@ struct TypeReducer TypeId functionType(TypeId ty); TypeId negationType(TypeId ty); - std::deque seen; - int depth = 0; - RecursionGuard guard(TypeId ty); RecursionGuard guard(TypePackId tp); - std::unordered_map copies; + void checkCacheable(TypeId ty); + void checkCacheable(TypePackId tp); template LUAU_NOINLINE std::pair copy(TypeId ty, const T* t) @@ -153,6 +161,7 @@ TypeId TypeReducer::reduce(TypeId ty) return ty; RecursionGuard rg = guard(ty); + checkCacheable(ty); if (auto i = get(ty)) return foldl(begin(i), end(i), &TypeReducer::intersectionType); @@ -176,6 +185,7 @@ TypePackId TypeReducer::reduce(TypePackId tp) return tp; RecursionGuard rg = guard(tp); + checkCacheable(tp); TypePackIterator it = begin(tp); @@ -213,6 +223,14 @@ std::optional TypeReducer::intersectionType(TypeId left, TypeId right) return right; // any & T ~ T else if (get(right)) return left; // T & any ~ T + else if (get(left)) + return std::nullopt; // 'a & T ~ 'a & T + else if (get(right)) + return std::nullopt; // T & 'a ~ T & 'a + else if (get(left)) + return std::nullopt; // G & T ~ G & T + else if (get(right)) + return std::nullopt; // T & G ~ T & G else if (get(left)) return std::nullopt; // error & T ~ error & T else if (get(right)) @@ -701,6 +719,32 @@ RecursionGuard TypeReducer::guard(TypePackId tp) return RecursionGuard{&depth, FInt::LuauTypeReductionRecursionLimit, &seen}; } +void TypeReducer::checkCacheable(TypeId ty) +{ + if (!cacheOk) + return; + + ty = follow(ty); + + // Only does shallow check, the TypeReducer itself already does deep traversal. + if (get(ty) || get(ty) || get(ty)) + cacheOk = false; + else if (auto tt = get(ty); tt && (tt->state == TableState::Free || tt->state == TableState::Unsealed)) + cacheOk = false; +} + +void TypeReducer::checkCacheable(TypePackId tp) +{ + if (!cacheOk) + return; + + tp = follow(tp); + + // Only does shallow check, the TypeReducer itself already does deep traversal. + if (get(tp) || get(tp)) + cacheOk = false; +} + } // namespace TypeReduction::TypeReduction(NotNull arena, NotNull builtinTypes, NotNull handle) @@ -715,13 +759,11 @@ std::optional TypeReduction::reduce(TypeId ty) if (auto found = cachedTypes.find(ty)) return *found; - if (auto reduced = reduceImpl(ty)) - { - cachedTypes[ty] = *reduced; - return *reduced; - } + auto [reducedTy, cacheOk] = reduceImpl(ty); + if (cacheOk) + cachedTypes[ty] = *reducedTy; - return std::nullopt; + return reducedTy; } std::optional TypeReduction::reduce(TypePackId tp) @@ -729,50 +771,48 @@ std::optional TypeReduction::reduce(TypePackId tp) if (auto found = cachedTypePacks.find(tp)) return *found; - if (auto reduced = reduceImpl(tp)) - { - cachedTypePacks[tp] = *reduced; - return *reduced; - } + auto [reducedTp, cacheOk] = reduceImpl(tp); + if (cacheOk) + cachedTypePacks[tp] = *reducedTp; - return std::nullopt; + return reducedTp; } -std::optional TypeReduction::reduceImpl(TypeId ty) +std::pair, bool> TypeReduction::reduceImpl(TypeId ty) { if (FFlag::DebugLuauDontReduceTypes) - return ty; + return {ty, false}; if (hasExceededCartesianProductLimit(ty)) - return std::nullopt; + return {std::nullopt, false}; try { TypeReducer reducer{arena, builtinTypes, handle}; - return reducer.reduce(ty); + return {reducer.reduce(ty), reducer.cacheOk}; } catch (const RecursionLimitException&) { - return std::nullopt; + return {std::nullopt, false}; } } -std::optional TypeReduction::reduceImpl(TypePackId tp) +std::pair, bool> TypeReduction::reduceImpl(TypePackId tp) { if (FFlag::DebugLuauDontReduceTypes) - return tp; + return {tp, false}; if (hasExceededCartesianProductLimit(tp)) - return std::nullopt; + return {std::nullopt, false}; try { TypeReducer reducer{arena, builtinTypes, handle}; - return reducer.reduce(tp); + return {reducer.reduce(tp), reducer.cacheOk}; } catch (const RecursionLimitException&) { - return std::nullopt; + return {std::nullopt, false}; } } diff --git a/Ast/src/Parser.cpp b/Ast/src/Parser.cpp index 5cd5f74..dea54c1 100644 --- a/Ast/src/Parser.cpp +++ b/Ast/src/Parser.cpp @@ -14,11 +14,6 @@ LUAU_FASTINTVARIABLE(LuauRecursionLimit, 1000) LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100) -LUAU_FASTFLAGVARIABLE(LuauFixNamedFunctionParse, false) -LUAU_DYNAMIC_FASTFLAGVARIABLE(LuaReportParseWrongNamedType, false) - -bool lua_telemetry_parsed_named_non_function_type = false; - LUAU_FASTFLAGVARIABLE(LuauErrorDoubleHexPrefix, false) LUAU_DYNAMIC_FASTFLAGVARIABLE(LuaReportParseIntegerIssues, false) @@ -1423,7 +1418,7 @@ AstTypeOrPack Parser::parseFunctionTypeAnnotation(bool allowPack) AstArray paramTypes = copy(params); - if (FFlag::LuauFixNamedFunctionParse && !names.empty()) + if (!names.empty()) forceFunctionType = true; bool returnTypeIntroducer = lexer.current().type == Lexeme::SkinnyArrow || lexer.current().type == ':'; @@ -1431,9 +1426,6 @@ AstTypeOrPack Parser::parseFunctionTypeAnnotation(bool allowPack) // Not a function at all. Just a parenthesized type. Or maybe a type pack with a single element if (params.size() == 1 && !varargAnnotation && !forceFunctionType && !returnTypeIntroducer) { - if (DFFlag::LuaReportParseWrongNamedType && !names.empty()) - lua_telemetry_parsed_named_non_function_type = true; - if (allowPack) return {{}, allocator.alloc(begin.location, AstTypeList{paramTypes, nullptr})}; else @@ -1441,12 +1433,7 @@ AstTypeOrPack Parser::parseFunctionTypeAnnotation(bool allowPack) } if (!forceFunctionType && !returnTypeIntroducer && allowPack) - { - if (DFFlag::LuaReportParseWrongNamedType && !names.empty()) - lua_telemetry_parsed_named_non_function_type = true; - return {{}, allocator.alloc(begin.location, AstTypeList{paramTypes, varargAnnotation})}; - } AstArray> paramNames = copy(names); diff --git a/CodeGen/include/Luau/AssemblyBuilderX64.h b/CodeGen/include/Luau/AssemblyBuilderX64.h index dbb366b..918c826 100644 --- a/CodeGen/include/Luau/AssemblyBuilderX64.h +++ b/CodeGen/include/Luau/AssemblyBuilderX64.h @@ -78,6 +78,7 @@ public: void test(OperandX64 lhs, OperandX64 rhs); void lea(OperandX64 lhs, OperandX64 rhs); + void setcc(ConditionX64 cond, OperandX64 op); void push(OperandX64 op); void pop(OperandX64 op); @@ -189,8 +190,8 @@ private: const char* name, OperandX64 dst, OperandX64 src1, OperandX64 src2, uint8_t imm8, uint8_t code, bool setW, uint8_t mode, uint8_t prefix); // Instruction components - void placeRegAndModRegMem(OperandX64 lhs, OperandX64 rhs); - void placeModRegMem(OperandX64 rhs, uint8_t regop); + void placeRegAndModRegMem(OperandX64 lhs, OperandX64 rhs, int32_t extraCodeBytes = 0); + void placeModRegMem(OperandX64 rhs, uint8_t regop, int32_t extraCodeBytes = 0); void placeRex(RegisterX64 op); void placeRex(OperandX64 op); void placeRexNoW(OperandX64 op); diff --git a/CodeGen/src/AssemblyBuilderX64.cpp b/CodeGen/src/AssemblyBuilderX64.cpp index f23fe46..77856e9 100644 --- a/CodeGen/src/AssemblyBuilderX64.cpp +++ b/CodeGen/src/AssemblyBuilderX64.cpp @@ -17,9 +17,13 @@ static const uint8_t codeForCondition[] = { 0x0, 0x1, 0x2, 0x3, 0x2, 0x6, 0x7, 0x3, 0x4, 0xc, 0xe, 0xf, 0xd, 0x3, 0x7, 0x6, 0x2, 0x5, 0xd, 0xf, 0xe, 0xc, 0x4, 0x5, 0xa, 0xb}; static_assert(sizeof(codeForCondition) / sizeof(codeForCondition[0]) == size_t(ConditionX64::Count), "all conditions have to be covered"); -static const char* textForCondition[] = {"jo", "jno", "jc", "jnc", "jb", "jbe", "ja", "jae", "je", "jl", "jle", "jg", "jge", "jnb", "jnbe", "jna", +static const char* jccTextForCondition[] = {"jo", "jno", "jc", "jnc", "jb", "jbe", "ja", "jae", "je", "jl", "jle", "jg", "jge", "jnb", "jnbe", "jna", "jnae", "jne", "jnl", "jnle", "jng", "jnge", "jz", "jnz", "jp", "jnp"}; -static_assert(sizeof(textForCondition) / sizeof(textForCondition[0]) == size_t(ConditionX64::Count), "all conditions have to be covered"); +static_assert(sizeof(jccTextForCondition) / sizeof(jccTextForCondition[0]) == size_t(ConditionX64::Count), "all conditions have to be covered"); + +static const char* setccTextForCondition[] = {"seto", "setno", "setc", "setnc", "setb", "setbe", "seta", "setae", "sete", "setl", "setle", "setg", + "setge", "setnb", "setnbe", "setna", "setnae", "setne", "setnl", "setnle", "setng", "setnge", "setz", "setnz", "setp", "setnp"}; +static_assert(sizeof(setccTextForCondition) / sizeof(setccTextForCondition[0]) == size_t(ConditionX64::Count), "all conditions have to be covered"); #define OP_PLUS_REG(op, reg) ((op) + (reg & 0x7)) #define OP_PLUS_CC(op, cc) ((op) + uint8_t(cc)) @@ -169,7 +173,7 @@ void AssemblyBuilderX64::mov(OperandX64 lhs, OperandX64 rhs) if (size == SizeX64::byte) { place(0xc6); - placeModRegMem(lhs, 0); + placeModRegMem(lhs, 0, /*extraCodeBytes=*/1); placeImm8(rhs.imm); } else @@ -177,7 +181,7 @@ void AssemblyBuilderX64::mov(OperandX64 lhs, OperandX64 rhs) LUAU_ASSERT(size == SizeX64::dword || size == SizeX64::qword); place(0xc7); - placeModRegMem(lhs, 0); + placeModRegMem(lhs, 0, /*extraCodeBytes=*/4); placeImm32(rhs.imm); } } @@ -304,13 +308,13 @@ void AssemblyBuilderX64::imul(OperandX64 dst, OperandX64 lhs, int32_t rhs) if (int8_t(rhs) == rhs) { place(0x6b); - placeRegAndModRegMem(dst, lhs); + placeRegAndModRegMem(dst, lhs, /*extraCodeBytes=*/1); placeImm8(rhs); } else { place(0x69); - placeRegAndModRegMem(dst, lhs); + placeRegAndModRegMem(dst, lhs, /*extraCodeBytes=*/4); placeImm32(rhs); } @@ -366,9 +370,24 @@ void AssemblyBuilderX64::ret() commit(); } +void AssemblyBuilderX64::setcc(ConditionX64 cond, OperandX64 op) +{ + SizeX64 size = op.cat == CategoryX64::reg ? op.base.size : op.memSize; + LUAU_ASSERT(size == SizeX64::byte); + + if (logText) + log(setccTextForCondition[size_t(cond)], op); + + placeRex(op); + place(0x0f); + place(0x90 | codeForCondition[size_t(cond)]); + placeModRegMem(op, 0); + commit(); +} + void AssemblyBuilderX64::jcc(ConditionX64 cond, Label& label) { - placeJcc(textForCondition[size_t(cond)], label, codeForCondition[size_t(cond)]); + placeJcc(jccTextForCondition[size_t(cond)], label, codeForCondition[size_t(cond)]); } void AssemblyBuilderX64::jmp(Label& label) @@ -866,7 +885,7 @@ void AssemblyBuilderX64::placeBinaryRegMemAndImm(OperandX64 lhs, OperandX64 rhs, if (size == SizeX64::byte) { place(code8); - placeModRegMem(lhs, opreg); + placeModRegMem(lhs, opreg, /*extraCodeBytes=*/1); placeImm8(rhs.imm); } else @@ -876,13 +895,13 @@ void AssemblyBuilderX64::placeBinaryRegMemAndImm(OperandX64 lhs, OperandX64 rhs, if (int8_t(rhs.imm) == rhs.imm && code != codeImm8) { place(codeImm8); - placeModRegMem(lhs, opreg); + placeModRegMem(lhs, opreg, /*extraCodeBytes=*/1); placeImm8(rhs.imm); } else { place(code); - placeModRegMem(lhs, opreg); + placeModRegMem(lhs, opreg, /*extraCodeBytes=*/4); placeImm32(rhs.imm); } } @@ -950,7 +969,7 @@ void AssemblyBuilderX64::placeShift(const char* name, OperandX64 lhs, OperandX64 LUAU_ASSERT(int8_t(rhs.imm) == rhs.imm); place(size == SizeX64::byte ? 0xc0 : 0xc1); - placeModRegMem(lhs, opreg); + placeModRegMem(lhs, opreg, /*extraCodeBytes=*/1); placeImm8(rhs.imm); } else @@ -1042,7 +1061,7 @@ void AssemblyBuilderX64::placeAvx( placeVex(dst, src1, src2, setW, mode, prefix); place(code); - placeRegAndModRegMem(dst, src2); + placeRegAndModRegMem(dst, src2, /*extraCodeBytes=*/1); placeImm8(imm8); commit(); @@ -1118,14 +1137,14 @@ static uint8_t getScaleEncoding(uint8_t scale) return scales[scale]; } -void AssemblyBuilderX64::placeRegAndModRegMem(OperandX64 lhs, OperandX64 rhs) +void AssemblyBuilderX64::placeRegAndModRegMem(OperandX64 lhs, OperandX64 rhs, int32_t extraCodeBytes) { LUAU_ASSERT(lhs.cat == CategoryX64::reg); - placeModRegMem(rhs, lhs.base.index); + placeModRegMem(rhs, lhs.base.index, extraCodeBytes); } -void AssemblyBuilderX64::placeModRegMem(OperandX64 rhs, uint8_t regop) +void AssemblyBuilderX64::placeModRegMem(OperandX64 rhs, uint8_t regop, int32_t extraCodeBytes) { if (rhs.cat == CategoryX64::reg) { @@ -1180,7 +1199,12 @@ void AssemblyBuilderX64::placeModRegMem(OperandX64 rhs, uint8_t regop) else if (base == rip) { place(MOD_RM(0b00, regop, 0b101)); - placeImm32(-int32_t(getCodeSize() + 4) + rhs.imm); + + // As a reminder: we do (getCodeSize() + 4) here to calculate the offset of the end of the current instruction we are placing. + // Since we have already placed all of the instruction bytes for this instruction, we add +4 to account for the imm32 displacement. + // Some instructions, however, are encoded such that an additional imm8 byte, or imm32 bytes, is placed after the ModRM byte, thus, + // we need to account for that case here as well. + placeImm32(-int32_t(getCodeSize() + 4 + extraCodeBytes) + rhs.imm); } else if (base != noreg) { diff --git a/CodeGen/src/CodeGen.cpp b/CodeGen/src/CodeGen.cpp index 1c05b29..72c1294 100644 --- a/CodeGen/src/CodeGen.cpp +++ b/CodeGen/src/CodeGen.cpp @@ -91,6 +91,9 @@ static int emitInst(AssemblyBuilderX64& build, NativeState& data, ModuleHelpers& case LOP_SETGLOBAL: emitInstSetGlobal(build, pc, i, next, fallback); break; + case LOP_NAMECALL: + emitInstNameCall(build, pc, i, proto->k, next, fallback); + break; case LOP_CALL: emitInstCall(build, helpers, pc, i); break; @@ -270,6 +273,9 @@ static int emitInst(AssemblyBuilderX64& build, NativeState& data, ModuleHelpers& case LOP_CONCAT: emitInstConcat(build, pc, i, next); break; + case LOP_COVERAGE: + emitInstCoverage(build, i); + break; default: emitFallback(build, data, op, i); break; @@ -298,6 +304,10 @@ static void emitInstFallback(AssemblyBuilderX64& build, NativeState& data, LuauO case LOP_SETTABLEN: emitInstSetTableNFallback(build, pc, i); break; + case LOP_NAMECALL: + // TODO: fast-paths that we've handled can be removed from the fallback + emitFallback(build, data, op, i); + break; case LOP_JUMPIFEQ: emitInstJumpIfEqFallback(build, pc, i, labelarr, /* not_ */ false); break; diff --git a/CodeGen/src/EmitCommonX64.cpp b/CodeGen/src/EmitCommonX64.cpp index fe258ff..2c410ae 100644 --- a/CodeGen/src/EmitCommonX64.cpp +++ b/CodeGen/src/EmitCommonX64.cpp @@ -239,6 +239,22 @@ void callCheckGc(AssemblyBuilderX64& build, int pcpos, bool savepc, Label& skip) emitUpdateBase(build); } +void callGetFastTmOrFallback(AssemblyBuilderX64& build, RegisterX64 table, TMS tm, Label& fallback) +{ + build.mov(rArg1, qword[table + offsetof(Table, metatable)]); + build.test(rArg1, rArg1); + build.jcc(ConditionX64::Zero, fallback); // no metatable + + build.test(byte[rArg1 + offsetof(Table, tmcache)], 1 << tm); + build.jcc(ConditionX64::NotZero, fallback); // no tag method + + // rArg1 is already prepared + build.mov(rArg2, tm); + build.mov(rax, qword[rState + offsetof(lua_State, global)]); + build.mov(rArg3, qword[rax + offsetof(global_State, tmname[tm])]); + build.call(qword[rNativeContext + offsetof(NativeContext, luaT_gettm)]); +} + void emitExit(AssemblyBuilderX64& build, bool continueInVm) { if (continueInVm) diff --git a/CodeGen/src/EmitCommonX64.h b/CodeGen/src/EmitCommonX64.h index 238a0ed..e475da6 100644 --- a/CodeGen/src/EmitCommonX64.h +++ b/CodeGen/src/EmitCommonX64.h @@ -67,8 +67,10 @@ constexpr OperandX64 sArg6 = noreg; constexpr unsigned kTValueSizeLog2 = 4; constexpr unsigned kLuaNodeSizeLog2 = 5; constexpr unsigned kLuaNodeTagMask = 0xf; +constexpr unsigned kNextBitOffset = 4; constexpr unsigned kOffsetOfLuaNodeTag = 12; // offsetof cannot be used on a bit field +constexpr unsigned kOffsetOfLuaNodeNext = 12; // offsetof cannot be used on a bit field constexpr unsigned kOffsetOfInstructionC = 3; // Leaf functions that are placed in every module to perform common instruction sequences @@ -168,6 +170,12 @@ inline void jumpIfTagIsNot(AssemblyBuilderX64& build, int ri, lua_Type tag, Labe build.jcc(ConditionX64::NotEqual, label); } +inline void jumpIfTagIsNot(AssemblyBuilderX64& build, RegisterX64 reg, lua_Type tag, Label& label) +{ + build.cmp(dword[reg + offsetof(TValue, tt)], tag); + build.jcc(ConditionX64::NotEqual, label); +} + // Note: fallthrough label should be placed after this condition inline void jumpIfFalsy(AssemblyBuilderX64& build, int ri, Label& target, Label& fallthrough) { @@ -224,6 +232,13 @@ inline void jumpIfNodeValueTagIs(AssemblyBuilderX64& build, RegisterX64 node, lu build.jcc(ConditionX64::Equal, label); } +inline void jumpIfNodeHasNext(AssemblyBuilderX64& build, RegisterX64 node, Label& label) +{ + build.mov(ecx, dword[node + offsetof(LuaNode, key) + kOffsetOfLuaNodeNext]); + build.shr(ecx, kNextBitOffset); + build.jcc(ConditionX64::NotZero, label); +} + inline void jumpIfNodeKeyNotInExpectedSlot(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 node, OperandX64 expectedKey, Label& label) { jumpIfNodeKeyTagIsNot(build, tmp, node, LUA_TSTRING, label); @@ -250,6 +265,7 @@ void callBarrierTable(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 ta void callBarrierObject(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 object, int ra, Label& skip); void callBarrierTableFast(AssemblyBuilderX64& build, RegisterX64 table, Label& skip); void callCheckGc(AssemblyBuilderX64& build, int pcpos, bool savepc, Label& skip); +void callGetFastTmOrFallback(AssemblyBuilderX64& build, RegisterX64 table, TMS tm, Label& fallback); void emitExit(AssemblyBuilderX64& build, bool continueInVm); void emitUpdateBase(AssemblyBuilderX64& build); @@ -258,7 +274,6 @@ void emitInterrupt(AssemblyBuilderX64& build, int pcpos); void emitFallback(AssemblyBuilderX64& build, NativeState& data, int op, int pcpos); void emitContinueCallInVm(AssemblyBuilderX64& build); -void emitExitFromLastReturn(AssemblyBuilderX64& build); } // namespace CodeGen } // namespace Luau diff --git a/CodeGen/src/EmitInstructionX64.cpp b/CodeGen/src/EmitInstructionX64.cpp index abbdb65..7b6e1c6 100644 --- a/CodeGen/src/EmitInstructionX64.cpp +++ b/CodeGen/src/EmitInstructionX64.cpp @@ -69,6 +69,51 @@ void emitInstMove(AssemblyBuilderX64& build, const Instruction* pc) build.vmovups(luauReg(ra), xmm0); } +void emitInstNameCall(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, const TValue* k, Label& next, Label& fallback) +{ + int ra = LUAU_INSN_A(*pc); + int rb = LUAU_INSN_B(*pc); + uint32_t aux = pc[1]; + + Label secondfpath; + + jumpIfTagIsNot(build, rb, LUA_TTABLE, fallback); + + RegisterX64 table = r8; + build.mov(table, luauRegValue(rb)); + + // &h->node[tsvalue(kv)->hash & (sizenode(h) - 1)]; + RegisterX64 node = rdx; + build.mov(node, qword[table + offsetof(Table, node)]); + build.mov(eax, 1); + build.mov(cl, byte[table + offsetof(Table, lsizenode)]); + build.shl(eax, cl); + build.dec(eax); + build.and_(eax, tsvalue(&k[aux])->hash); + build.shl(rax, kLuaNodeSizeLog2); + build.add(node, rax); + + jumpIfNodeKeyNotInExpectedSlot(build, rax, node, luauConstantValue(aux), secondfpath); + + setLuauReg(build, xmm0, ra + 1, luauReg(rb)); + setLuauReg(build, xmm0, ra, luauNodeValue(node)); + build.jmp(next); + + build.setLabel(secondfpath); + + jumpIfNodeHasNext(build, node, fallback); + callGetFastTmOrFallback(build, table, TM_INDEX, fallback); + jumpIfTagIsNot(build, rax, LUA_TTABLE, fallback); + + build.mov(table, qword[rax + offsetof(TValue, value)]); + + getTableNodeAtCachedSlot(build, rax, node, table, pcpos); + jumpIfNodeKeyNotInExpectedSlot(build, rax, node, luauConstantValue(aux), fallback); + + setLuauReg(build, xmm0, ra + 1, luauReg(rb)); + setLuauReg(build, xmm0, ra, luauNodeValue(node)); +} + void emitInstCall(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos) { int ra = LUAU_INSN_A(*pc); @@ -1627,5 +1672,28 @@ void emitInstConcat(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, callCheckGc(build, pcpos, /* savepc= */ false, next); } +void emitInstCoverage(AssemblyBuilderX64& build, int pcpos) +{ + build.mov(rcx, sCode); + build.add(rcx, pcpos * sizeof(Instruction)); + + // hits = LUAU_INSN_E(*pc) + build.mov(edx, dword[rcx]); + build.sar(edx, 8); + + // hits = (hits < (1 << 23) - 1) ? hits + 1 : hits; + build.xor_(eax, eax); + build.cmp(edx, (1 << 23) - 1); + build.setcc(ConditionX64::NotEqual, al); + build.add(edx, eax); + + + // VM_PATCH_E(pc, hits); + build.sal(edx, 8); + build.movzx(eax, byte[rcx]); + build.or_(eax, edx); + build.mov(dword[rcx], eax); +} + } // namespace CodeGen } // namespace Luau diff --git a/CodeGen/src/EmitInstructionX64.h b/CodeGen/src/EmitInstructionX64.h index 1ecb06d..96501e6 100644 --- a/CodeGen/src/EmitInstructionX64.h +++ b/CodeGen/src/EmitInstructionX64.h @@ -17,6 +17,7 @@ class AssemblyBuilderX64; enum class ConditionX64 : uint8_t; struct Label; struct ModuleHelpers; +struct NativeState; void emitInstLoadNil(AssemblyBuilderX64& build, const Instruction* pc); void emitInstLoadB(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); @@ -24,6 +25,7 @@ void emitInstLoadN(AssemblyBuilderX64& build, const Instruction* pc); void emitInstLoadK(AssemblyBuilderX64& build, const Instruction* pc); void emitInstLoadKX(AssemblyBuilderX64& build, const Instruction* pc); void emitInstMove(AssemblyBuilderX64& build, const Instruction* pc); +void emitInstNameCall(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, const TValue* k, Label& next, Label& fallback); void emitInstCall(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos); void emitInstReturn(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos); void emitInstJump(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); @@ -84,6 +86,7 @@ void emitInstSetTableKS(AssemblyBuilderX64& build, const Instruction* pc, int pc void emitInstGetGlobal(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback); void emitInstSetGlobal(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next, Label& fallback); void emitInstConcat(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next); +void emitInstCoverage(AssemblyBuilderX64& build, int pcpos); } // namespace CodeGen } // namespace Luau diff --git a/CodeGen/src/Fallbacks.cpp b/CodeGen/src/Fallbacks.cpp index 41f2bc8..e84ee21 100644 --- a/CodeGen/src/Fallbacks.cpp +++ b/CodeGen/src/Fallbacks.cpp @@ -594,19 +594,6 @@ const Instruction* execute_LOP_PREPVARARGS(lua_State* L, const Instruction* pc, return pc; } -const Instruction* execute_LOP_COVERAGE(lua_State* L, const Instruction* pc, StkId base, TValue* k) -{ - [[maybe_unused]] Closure* cl = clvalue(L->ci->func); - Instruction insn = *pc++; - int hits = LUAU_INSN_E(insn); - - // update hits with saturated add and patch the instruction in place - hits = (hits < (1 << 23) - 1) ? hits + 1 : hits; - VM_PATCH_E(pc - 1, hits); - - return pc; -} - const Instruction* execute_LOP_BREAK(lua_State* L, const Instruction* pc, StkId base, TValue* k) { LUAU_ASSERT(!"Unsupported deprecated opcode"); diff --git a/CodeGen/src/Fallbacks.h b/CodeGen/src/Fallbacks.h index 72573b1..bfc0e2b 100644 --- a/CodeGen/src/Fallbacks.h +++ b/CodeGen/src/Fallbacks.h @@ -20,5 +20,4 @@ const Instruction* execute_LOP_FORGPREP(lua_State* L, const Instruction* pc, Stk const Instruction* execute_LOP_GETVARARGS(lua_State* L, const Instruction* pc, StkId base, TValue* k); const Instruction* execute_LOP_DUPCLOSURE(lua_State* L, const Instruction* pc, StkId base, TValue* k); const Instruction* execute_LOP_PREPVARARGS(lua_State* L, const Instruction* pc, StkId base, TValue* k); -const Instruction* execute_LOP_COVERAGE(lua_State* L, const Instruction* pc, StkId base, TValue* k); const Instruction* execute_LOP_BREAK(lua_State* L, const Instruction* pc, StkId base, TValue* k); diff --git a/CodeGen/src/IrData.h b/CodeGen/src/IrData.h new file mode 100644 index 0000000..c4ed47c --- /dev/null +++ b/CodeGen/src/IrData.h @@ -0,0 +1,280 @@ +// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details +#pragma once + +#include "Luau/Label.h" +#include "Luau/RegisterX64.h" +#include "Luau/RegisterA64.h" + +#include + +#include + +namespace Luau +{ +namespace CodeGen +{ + +enum class IrCmd : uint8_t +{ + NOP, + + LOAD_TAG, + LOAD_POINTER, + LOAD_DOUBLE, + LOAD_INT, + LOAD_TVALUE, + LOAD_NODE_VALUE_TV, // TODO: we should find a way to generalize LOAD_TVALUE + LOAD_ENV, + + GET_ARR_ADDR, + GET_SLOT_NODE_ADDR, + + STORE_TAG, + STORE_POINTER, + STORE_DOUBLE, + STORE_INT, + STORE_TVALUE, + STORE_NODE_VALUE_TV, // TODO: we should find a way to generalize STORE_TVALUE + + ADD_INT, + SUB_INT, + + ADD_NUM, + SUB_NUM, + MUL_NUM, + DIV_NUM, + MOD_NUM, + POW_NUM, + + UNM_NUM, + + NOT_ANY, // TODO: boolean specialization will be useful + + JUMP, + JUMP_IF_TRUTHY, + JUMP_IF_FALSY, + JUMP_EQ_TAG, + JUMP_EQ_BOOLEAN, + JUMP_EQ_POINTER, + + JUMP_CMP_NUM, + JUMP_CMP_STR, + JUMP_CMP_ANY, + + TABLE_LEN, + NEW_TABLE, + DUP_TABLE, + + NUM_TO_INDEX, + + // Fallback functions + DO_ARITH, + DO_LEN, + GET_TABLE, + SET_TABLE, + GET_IMPORT, + CONCAT, + GET_UPVALUE, + SET_UPVALUE, + + // Guards and checks + CHECK_TAG, + CHECK_READONLY, + CHECK_NO_METATABLE, + CHECK_SAFE_ENV, + CHECK_ARRAY_SIZE, + CHECK_SLOT_MATCH, + + // Special operations + INTERRUPT, + CHECK_GC, + BARRIER_OBJ, + BARRIER_TABLE_BACK, + BARRIER_TABLE_FORWARD, + SET_SAVEDPC, + CLOSE_UPVALS, + + // While capture is a no-op right now, it might be useful to track register/upvalue lifetimes + CAPTURE, + + // Operations that don't have an IR representation yet + LOP_SETLIST, + LOP_CALL, + LOP_RETURN, + LOP_FASTCALL, + LOP_FASTCALL1, + LOP_FASTCALL2, + LOP_FASTCALL2K, + LOP_FORNPREP, + LOP_FORNLOOP, + LOP_FORGLOOP, + LOP_FORGLOOP_FALLBACK, + LOP_FORGPREP_NEXT, + LOP_FORGPREP_INEXT, + LOP_FORGPREP_XNEXT_FALLBACK, + LOP_AND, + LOP_ANDK, + LOP_OR, + LOP_ORK, + + // Operations that have a translation, but use a full instruction fallback + FALLBACK_GETGLOBAL, + FALLBACK_SETGLOBAL, + FALLBACK_GETTABLEKS, + FALLBACK_SETTABLEKS, + + // Operations that don't have assembly lowering at all + FALLBACK_NAMECALL, + FALLBACK_PREPVARARGS, + FALLBACK_GETVARARGS, + FALLBACK_NEWCLOSURE, + FALLBACK_DUPCLOSURE, + FALLBACK_FORGPREP, + FALLBACK_COVERAGE, +}; + +enum class IrConstKind : uint8_t +{ + Bool, + Int, + Uint, + Double, + Tag, +}; + +struct IrConst +{ + IrConstKind kind; + + union + { + bool valueBool; + int valueInt; + unsigned valueUint; + double valueDouble; + uint8_t valueTag; + }; +}; + +enum class IrCondition : uint8_t +{ + Equal, + NotEqual, + Less, + NotLess, + LessEqual, + NotLessEqual, + Greater, + NotGreater, + GreaterEqual, + NotGreaterEqual, + + UnsignedLess, + UnsignedLessEqual, + UnsignedGreater, + UnsignedGreaterEqual, + + Count +}; + +enum class IrOpKind : uint32_t +{ + None, + + // To reference a constant value + Constant, + + // To specify a condition code + Condition, + + // To reference a result of a previous instruction + Inst, + + // To reference a basic block in control flow + Block, + + // To reference a VM register + VmReg, + + // To reference a VM constant + VmConst, + + // To reference a VM upvalue + VmUpvalue, +}; + +struct IrOp +{ + IrOpKind kind : 4; + uint32_t index : 28; + + IrOp() + : kind(IrOpKind::None) + , index(0) + { + } + + IrOp(IrOpKind kind, uint32_t index) + : kind(kind) + , index(index) + { + } +}; + +static_assert(sizeof(IrOp) == 4); + +struct IrInst +{ + IrCmd cmd; + + // Operands + IrOp a; + IrOp b; + IrOp c; + IrOp d; + IrOp e; + + uint32_t lastUse = 0; + uint16_t useCount = 0; + + // Location of the result (optional) + RegisterX64 regX64 = noreg; + RegisterA64 regA64{KindA64::none, 0}; + bool reusedReg = false; +}; + +enum class IrBlockKind : uint8_t +{ + Bytecode, + Fallback, + Internal, +}; + +struct IrBlock +{ + IrBlockKind kind; + + // Start points to an instruction index in a stream + // End is implicit + uint32_t start; + + Label label; +}; + +struct BytecodeMapping +{ + uint32_t irLocation; + uint32_t asmLocation; +}; + +struct IrFunction +{ + std::vector blocks; + std::vector instructions; + std::vector constants; + + std::vector bcMapping; +}; + +} // namespace CodeGen +} // namespace Luau diff --git a/CodeGen/src/IrDump.cpp b/CodeGen/src/IrDump.cpp new file mode 100644 index 0000000..5d54026 --- /dev/null +++ b/CodeGen/src/IrDump.cpp @@ -0,0 +1,379 @@ +// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details +#include "IrDump.h" + +#include "IrUtils.h" + +#include "lua.h" + +#include + +namespace Luau +{ +namespace CodeGen +{ + +static const char* textForCondition[] = { + "eq", "not_eq", "lt", "not_lt", "le", "not_le", "gt", "not_gt", "ge", "not_ge", "u_lt", "u_le", "u_gt", "u_ge"}; +static_assert(sizeof(textForCondition) / sizeof(textForCondition[0]) == size_t(IrCondition::Count), "all conditions have to be covered"); + +const int kDetailsAlignColumn = 60; + +LUAU_PRINTF_ATTR(2, 3) +static void append(std::string& result, const char* fmt, ...) +{ + char buf[256]; + va_list args; + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + result.append(buf); +} + +static const char* getTagName(uint8_t tag) +{ + switch (tag) + { + case LUA_TNIL: + return "tnil"; + case LUA_TBOOLEAN: + return "tboolean"; + case LUA_TLIGHTUSERDATA: + return "tlightuserdata"; + case LUA_TNUMBER: + return "tnumber"; + case LUA_TVECTOR: + return "tvector"; + case LUA_TSTRING: + return "tstring"; + case LUA_TTABLE: + return "ttable"; + case LUA_TFUNCTION: + return "tfunction"; + case LUA_TUSERDATA: + return "tuserdata"; + case LUA_TTHREAD: + return "tthread"; + default: + LUAU_UNREACHABLE(); + } +} + +const char* getCmdName(IrCmd cmd) +{ + switch (cmd) + { + case IrCmd::NOP: + return "NOP"; + case IrCmd::LOAD_TAG: + return "LOAD_TAG"; + case IrCmd::LOAD_POINTER: + return "LOAD_POINTER"; + case IrCmd::LOAD_DOUBLE: + return "LOAD_DOUBLE"; + case IrCmd::LOAD_INT: + return "LOAD_INT"; + case IrCmd::LOAD_TVALUE: + return "LOAD_TVALUE"; + case IrCmd::LOAD_NODE_VALUE_TV: + return "LOAD_NODE_VALUE_TV"; + case IrCmd::LOAD_ENV: + return "LOAD_ENV"; + case IrCmd::GET_ARR_ADDR: + return "GET_ARR_ADDR"; + case IrCmd::GET_SLOT_NODE_ADDR: + return "GET_SLOT_NODE_ADDR"; + case IrCmd::STORE_TAG: + return "STORE_TAG"; + case IrCmd::STORE_POINTER: + return "STORE_POINTER"; + case IrCmd::STORE_DOUBLE: + return "STORE_DOUBLE"; + case IrCmd::STORE_INT: + return "STORE_INT"; + case IrCmd::STORE_TVALUE: + return "STORE_TVALUE"; + case IrCmd::STORE_NODE_VALUE_TV: + return "STORE_NODE_VALUE_TV"; + case IrCmd::ADD_INT: + return "ADD_INT"; + case IrCmd::SUB_INT: + return "SUB_INT"; + case IrCmd::ADD_NUM: + return "ADD_NUM"; + case IrCmd::SUB_NUM: + return "SUB_NUM"; + case IrCmd::MUL_NUM: + return "MUL_NUM"; + case IrCmd::DIV_NUM: + return "DIV_NUM"; + case IrCmd::MOD_NUM: + return "MOD_NUM"; + case IrCmd::POW_NUM: + return "POW_NUM"; + case IrCmd::UNM_NUM: + return "UNM_NUM"; + case IrCmd::NOT_ANY: + return "NOT_ANY"; + case IrCmd::JUMP: + return "JUMP"; + case IrCmd::JUMP_IF_TRUTHY: + return "JUMP_IF_TRUTHY"; + case IrCmd::JUMP_IF_FALSY: + return "JUMP_IF_FALSY"; + case IrCmd::JUMP_EQ_TAG: + return "JUMP_EQ_TAG"; + case IrCmd::JUMP_EQ_BOOLEAN: + return "JUMP_EQ_BOOLEAN"; + case IrCmd::JUMP_EQ_POINTER: + return "JUMP_EQ_POINTER"; + case IrCmd::JUMP_CMP_NUM: + return "JUMP_CMP_NUM"; + case IrCmd::JUMP_CMP_STR: + return "JUMP_CMP_STR"; + case IrCmd::JUMP_CMP_ANY: + return "JUMP_CMP_ANY"; + case IrCmd::TABLE_LEN: + return "TABLE_LEN"; + case IrCmd::NEW_TABLE: + return "NEW_TABLE"; + case IrCmd::DUP_TABLE: + return "DUP_TABLE"; + case IrCmd::NUM_TO_INDEX: + return "NUM_TO_INDEX"; + case IrCmd::DO_ARITH: + return "DO_ARITH"; + case IrCmd::DO_LEN: + return "DO_LEN"; + case IrCmd::GET_TABLE: + return "GET_TABLE"; + case IrCmd::SET_TABLE: + return "SET_TABLE"; + case IrCmd::GET_IMPORT: + return "GET_IMPORT"; + case IrCmd::CONCAT: + return "CONCAT"; + case IrCmd::GET_UPVALUE: + return "GET_UPVALUE"; + case IrCmd::SET_UPVALUE: + return "SET_UPVALUE"; + case IrCmd::CHECK_TAG: + return "CHECK_TAG"; + case IrCmd::CHECK_READONLY: + return "CHECK_READONLY"; + case IrCmd::CHECK_NO_METATABLE: + return "CHECK_NO_METATABLE"; + case IrCmd::CHECK_SAFE_ENV: + return "CHECK_SAFE_ENV"; + case IrCmd::CHECK_ARRAY_SIZE: + return "CHECK_ARRAY_SIZE"; + case IrCmd::CHECK_SLOT_MATCH: + return "CHECK_SLOT_MATCH"; + case IrCmd::INTERRUPT: + return "INTERRUPT"; + case IrCmd::CHECK_GC: + return "CHECK_GC"; + case IrCmd::BARRIER_OBJ: + return "BARRIER_OBJ"; + case IrCmd::BARRIER_TABLE_BACK: + return "BARRIER_TABLE_BACK"; + case IrCmd::BARRIER_TABLE_FORWARD: + return "BARRIER_TABLE_FORWARD"; + case IrCmd::SET_SAVEDPC: + return "SET_SAVEDPC"; + case IrCmd::CLOSE_UPVALS: + return "CLOSE_UPVALS"; + case IrCmd::CAPTURE: + return "CAPTURE"; + case IrCmd::LOP_SETLIST: + return "LOP_SETLIST"; + case IrCmd::LOP_CALL: + return "LOP_CALL"; + case IrCmd::LOP_RETURN: + return "LOP_RETURN"; + case IrCmd::LOP_FASTCALL: + return "LOP_FASTCALL"; + case IrCmd::LOP_FASTCALL1: + return "LOP_FASTCALL1"; + case IrCmd::LOP_FASTCALL2: + return "LOP_FASTCALL2"; + case IrCmd::LOP_FASTCALL2K: + return "LOP_FASTCALL2K"; + case IrCmd::LOP_FORNPREP: + return "LOP_FORNPREP"; + case IrCmd::LOP_FORNLOOP: + return "LOP_FORNLOOP"; + case IrCmd::LOP_FORGLOOP: + return "LOP_FORGLOOP"; + case IrCmd::LOP_FORGLOOP_FALLBACK: + return "LOP_FORGLOOP_FALLBACK"; + case IrCmd::LOP_FORGPREP_NEXT: + return "LOP_FORGPREP_NEXT"; + case IrCmd::LOP_FORGPREP_INEXT: + return "LOP_FORGPREP_INEXT"; + case IrCmd::LOP_FORGPREP_XNEXT_FALLBACK: + return "LOP_FORGPREP_XNEXT_FALLBACK"; + case IrCmd::LOP_AND: + return "LOP_AND"; + case IrCmd::LOP_ANDK: + return "LOP_ANDK"; + case IrCmd::LOP_OR: + return "LOP_OR"; + case IrCmd::LOP_ORK: + return "LOP_ORK"; + case IrCmd::FALLBACK_GETGLOBAL: + return "FALLBACK_GETGLOBAL"; + case IrCmd::FALLBACK_SETGLOBAL: + return "FALLBACK_SETGLOBAL"; + case IrCmd::FALLBACK_GETTABLEKS: + return "FALLBACK_GETTABLEKS"; + case IrCmd::FALLBACK_SETTABLEKS: + return "FALLBACK_SETTABLEKS"; + case IrCmd::FALLBACK_NAMECALL: + return "FALLBACK_NAMECALL"; + case IrCmd::FALLBACK_PREPVARARGS: + return "FALLBACK_PREPVARARGS"; + case IrCmd::FALLBACK_GETVARARGS: + return "FALLBACK_GETVARARGS"; + case IrCmd::FALLBACK_NEWCLOSURE: + return "FALLBACK_NEWCLOSURE"; + case IrCmd::FALLBACK_DUPCLOSURE: + return "FALLBACK_DUPCLOSURE"; + case IrCmd::FALLBACK_FORGPREP: + return "FALLBACK_FORGPREP"; + case IrCmd::FALLBACK_COVERAGE: + return "FALLBACK_COVERAGE"; + } + + LUAU_UNREACHABLE(); +} + +const char* getBlockKindName(IrBlockKind kind) +{ + switch (kind) + { + case IrBlockKind::Bytecode: + return "bb_bytecode"; + case IrBlockKind::Fallback: + return "bb_fallback"; + case IrBlockKind::Internal: + return "bb"; + } + + LUAU_UNREACHABLE(); +} + +void toString(IrToStringContext& ctx, IrInst inst, uint32_t index) +{ + append(ctx.result, " "); + + // Instructions with a result display target virtual register + if (hasResult(inst.cmd)) + append(ctx.result, "%%%u = ", index); + + ctx.result.append(getCmdName(inst.cmd)); + + if (inst.a.kind != IrOpKind::None) + { + append(ctx.result, " "); + toString(ctx, inst.a); + } + + if (inst.b.kind != IrOpKind::None) + { + append(ctx.result, ", "); + toString(ctx, inst.b); + } + + if (inst.c.kind != IrOpKind::None) + { + append(ctx.result, ", "); + toString(ctx, inst.c); + } + + if (inst.d.kind != IrOpKind::None) + { + append(ctx.result, ", "); + toString(ctx, inst.d); + } + + if (inst.e.kind != IrOpKind::None) + { + append(ctx.result, ", "); + toString(ctx, inst.e); + } +} + +void toString(IrToStringContext& ctx, IrOp op) +{ + switch (op.kind) + { + case IrOpKind::None: + break; + case IrOpKind::Constant: + toString(ctx.result, ctx.constants[op.index]); + break; + case IrOpKind::Condition: + LUAU_ASSERT(op.index < uint32_t(IrCondition::Count)); + ctx.result.append(textForCondition[op.index]); + break; + case IrOpKind::Inst: + append(ctx.result, "%%%u", op.index); + break; + case IrOpKind::Block: + append(ctx.result, "%s_%u", getBlockKindName(ctx.blocks[op.index].kind), op.index); + break; + case IrOpKind::VmReg: + append(ctx.result, "R%u", op.index); + break; + case IrOpKind::VmConst: + append(ctx.result, "K%u", op.index); + break; + case IrOpKind::VmUpvalue: + append(ctx.result, "U%u", op.index); + break; + } +} + +void toString(std::string& result, IrConst constant) +{ + switch (constant.kind) + { + case IrConstKind::Bool: + append(result, constant.valueBool ? "true" : "false"); + break; + case IrConstKind::Int: + append(result, "%di", constant.valueInt); + break; + case IrConstKind::Uint: + append(result, "%uu", constant.valueUint); + break; + case IrConstKind::Double: + append(result, "%.17g", constant.valueDouble); + break; + case IrConstKind::Tag: + result.append(getTagName(constant.valueTag)); + break; + } +} + +void toStringDetailed(IrToStringContext& ctx, IrInst inst, uint32_t index) +{ + size_t start = ctx.result.size(); + + toString(ctx, inst, index); + + int pad = kDetailsAlignColumn - int(ctx.result.size() - start); + + if (pad > 0) + ctx.result.append(pad, ' '); + + LUAU_ASSERT(inst.useCount == 0 || inst.lastUse != 0); + + if (inst.useCount == 0 && hasSideEffects(inst.cmd)) + append(ctx.result, "; %%%u, has side-effects\n", index); + else + append(ctx.result, "; useCount: %d, lastUse: %%%u\n", inst.useCount, inst.lastUse); +} + +} // namespace CodeGen +} // namespace Luau diff --git a/CodeGen/src/IrDump.h b/CodeGen/src/IrDump.h new file mode 100644 index 0000000..8fb4d6e --- /dev/null +++ b/CodeGen/src/IrDump.h @@ -0,0 +1,32 @@ +// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details +#pragma once + +#include "IrData.h" + +#include +#include + +namespace Luau +{ +namespace CodeGen +{ + +const char* getCmdName(IrCmd cmd); +const char* getBlockKindName(IrBlockKind kind); + +struct IrToStringContext +{ + std::string& result; + std::vector& blocks; + std::vector& constants; +}; + +void toString(IrToStringContext& ctx, IrInst inst, uint32_t index); +void toString(IrToStringContext& ctx, IrOp op); + +void toString(std::string& result, IrConst constant); + +void toStringDetailed(IrToStringContext& ctx, IrInst inst, uint32_t index); + +} // namespace CodeGen +} // namespace Luau diff --git a/CodeGen/src/IrUtils.h b/CodeGen/src/IrUtils.h new file mode 100644 index 0000000..f0e4cee --- /dev/null +++ b/CodeGen/src/IrUtils.h @@ -0,0 +1,161 @@ +// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details +#pragma once + +#include "Luau/Bytecode.h" +#include "Luau/Common.h" + +#include "IrData.h" + +namespace Luau +{ +namespace CodeGen +{ + +inline bool isJumpD(LuauOpcode op) +{ + switch (op) + { + case LOP_JUMP: + case LOP_JUMPIF: + case LOP_JUMPIFNOT: + case LOP_JUMPIFEQ: + case LOP_JUMPIFLE: + case LOP_JUMPIFLT: + case LOP_JUMPIFNOTEQ: + case LOP_JUMPIFNOTLE: + case LOP_JUMPIFNOTLT: + case LOP_FORNPREP: + case LOP_FORNLOOP: + case LOP_FORGPREP: + case LOP_FORGLOOP: + case LOP_FORGPREP_INEXT: + case LOP_FORGPREP_NEXT: + case LOP_JUMPBACK: + case LOP_JUMPXEQKNIL: + case LOP_JUMPXEQKB: + case LOP_JUMPXEQKN: + case LOP_JUMPXEQKS: + return true; + + default: + return false; + } +} + +inline bool isSkipC(LuauOpcode op) +{ + switch (op) + { + case LOP_LOADB: + return true; + + default: + return false; + } +} + +inline bool isFastCall(LuauOpcode op) +{ + switch (op) + { + case LOP_FASTCALL: + case LOP_FASTCALL1: + case LOP_FASTCALL2: + case LOP_FASTCALL2K: + return true; + + default: + return false; + } +} + +inline int getJumpTarget(uint32_t insn, uint32_t pc) +{ + LuauOpcode op = LuauOpcode(LUAU_INSN_OP(insn)); + + if (isJumpD(op)) + return int(pc + LUAU_INSN_D(insn) + 1); + else if (isFastCall(op)) + return int(pc + LUAU_INSN_C(insn) + 2); + else if (isSkipC(op) && LUAU_INSN_C(insn)) + return int(pc + LUAU_INSN_C(insn) + 1); + else if (op == LOP_JUMPX) + return int(pc + LUAU_INSN_E(insn) + 1); + else + return -1; +} + +inline bool isBlockTerminator(IrCmd cmd) +{ + switch (cmd) + { + case IrCmd::JUMP: + case IrCmd::JUMP_IF_TRUTHY: + case IrCmd::JUMP_IF_FALSY: + case IrCmd::JUMP_EQ_TAG: + case IrCmd::JUMP_EQ_BOOLEAN: + case IrCmd::JUMP_EQ_POINTER: + case IrCmd::JUMP_CMP_NUM: + case IrCmd::JUMP_CMP_STR: + case IrCmd::JUMP_CMP_ANY: + case IrCmd::LOP_RETURN: + case IrCmd::LOP_FORNPREP: + case IrCmd::LOP_FORNLOOP: + case IrCmd::LOP_FORGLOOP: + case IrCmd::LOP_FORGLOOP_FALLBACK: + case IrCmd::LOP_FORGPREP_NEXT: + case IrCmd::LOP_FORGPREP_INEXT: + case IrCmd::LOP_FORGPREP_XNEXT_FALLBACK: + case IrCmd::FALLBACK_FORGPREP: + return true; + default: + break; + } + + return false; +} + +inline bool hasResult(IrCmd cmd) +{ + switch (cmd) + { + case IrCmd::LOAD_TAG: + case IrCmd::LOAD_POINTER: + case IrCmd::LOAD_DOUBLE: + case IrCmd::LOAD_INT: + case IrCmd::LOAD_TVALUE: + case IrCmd::LOAD_NODE_VALUE_TV: + case IrCmd::LOAD_ENV: + case IrCmd::GET_ARR_ADDR: + case IrCmd::GET_SLOT_NODE_ADDR: + case IrCmd::ADD_INT: + case IrCmd::SUB_INT: + case IrCmd::ADD_NUM: + case IrCmd::SUB_NUM: + case IrCmd::MUL_NUM: + case IrCmd::DIV_NUM: + case IrCmd::MOD_NUM: + case IrCmd::POW_NUM: + case IrCmd::UNM_NUM: + case IrCmd::NOT_ANY: + case IrCmd::TABLE_LEN: + case IrCmd::NEW_TABLE: + case IrCmd::DUP_TABLE: + case IrCmd::NUM_TO_INDEX: + return true; + default: + break; + } + + return false; +} + +inline bool hasSideEffects(IrCmd cmd) +{ + // Instructions that don't produce a result most likely have other side-effects to make them useful + // Right now, a full switch would mirror the 'hasResult' function, so we use this simple condition + return !hasResult(cmd); +} + +} // namespace CodeGen +} // namespace Luau diff --git a/CodeGen/src/NativeState.cpp b/CodeGen/src/NativeState.cpp index 62974fe..22d38aa 100644 --- a/CodeGen/src/NativeState.cpp +++ b/CodeGen/src/NativeState.cpp @@ -42,7 +42,6 @@ void initFallbackTable(NativeState& data) CODEGEN_SET_FALLBACK(LOP_GETVARARGS, 0); CODEGEN_SET_FALLBACK(LOP_DUPCLOSURE, 0); CODEGEN_SET_FALLBACK(LOP_PREPVARARGS, 0); - CODEGEN_SET_FALLBACK(LOP_COVERAGE, 0); CODEGEN_SET_FALLBACK(LOP_BREAK, 0); // Fallbacks that are called from partial implementation of an instruction @@ -80,6 +79,8 @@ void initHelperFunctions(NativeState& data) data.context.luaF_close = luaF_close; + data.context.luaT_gettm = luaT_gettm; + data.context.libm_exp = exp; data.context.libm_pow = pow; data.context.libm_fmod = fmod; diff --git a/CodeGen/src/NativeState.h b/CodeGen/src/NativeState.h index 9138ba4..03d8ae3 100644 --- a/CodeGen/src/NativeState.h +++ b/CodeGen/src/NativeState.h @@ -78,6 +78,8 @@ struct NativeContext void (*luaF_close)(lua_State* L, StkId level) = nullptr; + const TValue* (*luaT_gettm)(Table* events, TMS event, TString* ename) = nullptr; + double (*libm_exp)(double) = nullptr; double (*libm_pow)(double, double) = nullptr; double (*libm_fmod)(double, double) = nullptr; diff --git a/Compiler/include/Luau/BytecodeBuilder.h b/Compiler/include/Luau/BytecodeBuilder.h index 0d4543f..047c1b6 100644 --- a/Compiler/include/Luau/BytecodeBuilder.h +++ b/Compiler/include/Luau/BytecodeBuilder.h @@ -123,6 +123,8 @@ public: static uint32_t getImportId(int32_t id0, int32_t id1); static uint32_t getImportId(int32_t id0, int32_t id1, int32_t id2); + static int decomposeImportId(uint32_t ids, int32_t& id0, int32_t& id1, int32_t& id2); + static uint32_t getStringHash(StringRef key); static std::string getError(const std::string& message); @@ -243,6 +245,7 @@ private: std::vector debugUpvals; DenseHashMap stringTable; + std::vector debugStrings; std::vector> debugRemarks; std::string debugRemarkBuffer; @@ -261,6 +264,7 @@ private: void validateVariadic() const; std::string dumpCurrentFunction(std::vector& dumpinstoffs) const; + void dumpConstant(std::string& result, int k) const; void dumpInstruction(const uint32_t* opcode, std::string& output, int targetLabel) const; void writeFunction(std::string& ss, uint32_t id) const; diff --git a/Compiler/src/BytecodeBuilder.cpp b/Compiler/src/BytecodeBuilder.cpp index 7d23073..11bf242 100644 --- a/Compiler/src/BytecodeBuilder.cpp +++ b/Compiler/src/BytecodeBuilder.cpp @@ -318,8 +318,13 @@ unsigned int BytecodeBuilder::addStringTableEntry(StringRef value) // note: bytecode serialization format uses 1-based table indices, 0 is reserved to mean nil if (index == 0) + { index = uint32_t(stringTable.size()); + if ((dumpFlags & Dump_Code) != 0) + debugStrings.push_back(value); + } + return index; } @@ -870,6 +875,15 @@ uint32_t BytecodeBuilder::getImportId(int32_t id0, int32_t id1, int32_t id2) return (3u << 30) | (id0 << 20) | (id1 << 10) | id2; } +int BytecodeBuilder::decomposeImportId(uint32_t ids, int32_t& id0, int32_t& id1, int32_t& id2) +{ + int count = ids >> 30; + id0 = count > 0 ? int(ids >> 20) & 1023 : -1; + id1 = count > 1 ? int(ids >> 10) & 1023 : -1; + id2 = count > 2 ? int(ids) & 1023 : -1; + return count; +} + uint32_t BytecodeBuilder::getStringHash(StringRef key) { // This hashing algorithm should match luaS_hash defined in VM/lstring.cpp for short inputs; we can't use that code directly to keep compiler and @@ -1598,6 +1612,95 @@ void BytecodeBuilder::validateVariadic() const } #endif +static bool printableStringConstant(const char* str, size_t len) +{ + for (size_t i = 0; i < len; ++i) + { + if (unsigned(str[i]) < ' ') + return false; + } + + return true; +} + +void BytecodeBuilder::dumpConstant(std::string& result, int k) const +{ + LUAU_ASSERT(unsigned(k) < constants.size()); + const Constant& data = constants[k]; + + switch (data.type) + { + case Constant::Type_Nil: + formatAppend(result, "nil"); + break; + case Constant::Type_Boolean: + formatAppend(result, "%s", data.valueBoolean ? "true" : "false"); + break; + case Constant::Type_Number: + formatAppend(result, "%.17g", data.valueNumber); + break; + case Constant::Type_String: + { + const StringRef& str = debugStrings[data.valueString - 1]; + + if (printableStringConstant(str.data, str.length)) + { + if (str.length < 32) + formatAppend(result, "'%.*s'", int(str.length), str.data); + else + formatAppend(result, "'%.*s'...", 32, str.data); + } + break; + } + case Constant::Type_Import: + { + int id0 = -1, id1 = -1, id2 = -1; + if (int count = decomposeImportId(data.valueImport, id0, id1, id2)) + { + { + const Constant& id = constants[id0]; + LUAU_ASSERT(id.type == Constant::Type_String && id.valueString <= debugStrings.size()); + + const StringRef& str = debugStrings[id.valueString - 1]; + formatAppend(result, "%.*s", int(str.length), str.data); + } + + if (count > 1) + { + const Constant& id = constants[id1]; + LUAU_ASSERT(id.type == Constant::Type_String && id.valueString <= debugStrings.size()); + + const StringRef& str = debugStrings[id.valueString - 1]; + formatAppend(result, ".%.*s", int(str.length), str.data); + } + + if (count > 2) + { + const Constant& id = constants[id2]; + LUAU_ASSERT(id.type == Constant::Type_String && id.valueString <= debugStrings.size()); + + const StringRef& str = debugStrings[id.valueString - 1]; + formatAppend(result, ".%.*s", int(str.length), str.data); + } + } + break; + } + case Constant::Type_Table: + formatAppend(result, "{...}"); + break; + case Constant::Type_Closure: + { + const Function& func = functions[data.valueClosure]; + + if (!func.dumpname.empty()) + formatAppend(result, "'%s'", func.dumpname.c_str()); + break; + } + default: + LUAU_UNREACHABLE(); + } +} + void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result, int targetLabel) const { uint32_t insn = *code++; @@ -1620,7 +1723,9 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result, break; case LOP_LOADK: - formatAppend(result, "LOADK R%d K%d\n", LUAU_INSN_A(insn), LUAU_INSN_D(insn)); + formatAppend(result, "LOADK R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_D(insn)); + dumpConstant(result, LUAU_INSN_D(insn)); + result.append("]\n"); break; case LOP_MOVE: @@ -1628,11 +1733,17 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result, break; case LOP_GETGLOBAL: - formatAppend(result, "GETGLOBAL R%d K%d\n", LUAU_INSN_A(insn), *code++); + formatAppend(result, "GETGLOBAL R%d K%d [", LUAU_INSN_A(insn), *code); + dumpConstant(result, *code); + result.append("]\n"); + code++; break; case LOP_SETGLOBAL: - formatAppend(result, "SETGLOBAL R%d K%d\n", LUAU_INSN_A(insn), *code++); + formatAppend(result, "SETGLOBAL R%d K%d [", LUAU_INSN_A(insn), *code); + dumpConstant(result, *code); + result.append("]\n"); + code++; break; case LOP_GETUPVAL: @@ -1648,7 +1759,9 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result, break; case LOP_GETIMPORT: - formatAppend(result, "GETIMPORT R%d %d\n", LUAU_INSN_A(insn), LUAU_INSN_D(insn)); + formatAppend(result, "GETIMPORT R%d %d [", LUAU_INSN_A(insn), LUAU_INSN_D(insn)); + dumpConstant(result, LUAU_INSN_D(insn)); + result.append("]\n"); code++; // AUX break; @@ -1661,11 +1774,17 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result, break; case LOP_GETTABLEKS: - formatAppend(result, "GETTABLEKS R%d R%d K%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), *code++); + formatAppend(result, "GETTABLEKS R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), *code); + dumpConstant(result, *code); + result.append("]\n"); + code++; break; case LOP_SETTABLEKS: - formatAppend(result, "SETTABLEKS R%d R%d K%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), *code++); + formatAppend(result, "SETTABLEKS R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), *code); + dumpConstant(result, *code); + result.append("]\n"); + code++; break; case LOP_GETTABLEN: @@ -1681,7 +1800,10 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result, break; case LOP_NAMECALL: - formatAppend(result, "NAMECALL R%d R%d K%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), *code++); + formatAppend(result, "NAMECALL R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), *code); + dumpConstant(result, *code); + result.append("]\n"); + code++; break; case LOP_CALL: @@ -1753,27 +1875,39 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result, break; case LOP_ADDK: - formatAppend(result, "ADDK R%d R%d K%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn)); + formatAppend(result, "ADDK R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn)); + dumpConstant(result, LUAU_INSN_C(insn)); + result.append("]\n"); break; case LOP_SUBK: - formatAppend(result, "SUBK R%d R%d K%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn)); + formatAppend(result, "SUBK R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn)); + dumpConstant(result, LUAU_INSN_C(insn)); + result.append("]\n"); break; case LOP_MULK: - formatAppend(result, "MULK R%d R%d K%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn)); + formatAppend(result, "MULK R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn)); + dumpConstant(result, LUAU_INSN_C(insn)); + result.append("]\n"); break; case LOP_DIVK: - formatAppend(result, "DIVK R%d R%d K%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn)); + formatAppend(result, "DIVK R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn)); + dumpConstant(result, LUAU_INSN_C(insn)); + result.append("]\n"); break; case LOP_MODK: - formatAppend(result, "MODK R%d R%d K%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn)); + formatAppend(result, "MODK R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn)); + dumpConstant(result, LUAU_INSN_C(insn)); + result.append("]\n"); break; case LOP_POWK: - formatAppend(result, "POWK R%d R%d K%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn)); + formatAppend(result, "POWK R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn)); + dumpConstant(result, LUAU_INSN_C(insn)); + result.append("]\n"); break; case LOP_AND: @@ -1785,11 +1919,15 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result, break; case LOP_ANDK: - formatAppend(result, "ANDK R%d R%d K%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn)); + formatAppend(result, "ANDK R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn)); + dumpConstant(result, LUAU_INSN_C(insn)); + result.append("]\n"); break; case LOP_ORK: - formatAppend(result, "ORK R%d R%d K%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn)); + formatAppend(result, "ORK R%d R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), LUAU_INSN_C(insn)); + dumpConstant(result, LUAU_INSN_C(insn)); + result.append("]\n"); break; case LOP_CONCAT: @@ -1850,7 +1988,9 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result, break; case LOP_DUPCLOSURE: - formatAppend(result, "DUPCLOSURE R%d K%d\n", LUAU_INSN_A(insn), LUAU_INSN_D(insn)); + formatAppend(result, "DUPCLOSURE R%d K%d [", LUAU_INSN_A(insn), LUAU_INSN_D(insn)); + dumpConstant(result, LUAU_INSN_D(insn)); + result.append("]\n"); break; case LOP_BREAK: @@ -1862,7 +2002,10 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result, break; case LOP_LOADKX: - formatAppend(result, "LOADKX R%d K%d\n", LUAU_INSN_A(insn), *code++); + formatAppend(result, "LOADKX R%d K%d [", LUAU_INSN_A(insn), *code); + dumpConstant(result, *code); + result.append("]\n"); + code++; break; case LOP_JUMPX: @@ -1876,18 +2019,18 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result, case LOP_FASTCALL1: formatAppend(result, "FASTCALL1 %d R%d L%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), targetLabel); break; + case LOP_FASTCALL2: - { - uint32_t aux = *code++; - formatAppend(result, "FASTCALL2 %d R%d R%d L%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), aux, targetLabel); + formatAppend(result, "FASTCALL2 %d R%d R%d L%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), *code, targetLabel); + code++; break; - } + case LOP_FASTCALL2K: - { - uint32_t aux = *code++; - formatAppend(result, "FASTCALL2K %d R%d K%d L%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), aux, targetLabel); + formatAppend(result, "FASTCALL2K %d R%d K%d L%d [", LUAU_INSN_A(insn), LUAU_INSN_B(insn), *code, targetLabel); + dumpConstant(result, *code); + result.append("]\n"); + code++; break; - } case LOP_COVERAGE: formatAppend(result, "COVERAGE\n"); @@ -1910,12 +2053,16 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result, break; case LOP_JUMPXEQKN: - formatAppend(result, "JUMPXEQKN R%d K%d L%d%s\n", LUAU_INSN_A(insn), *code & 0xffffff, targetLabel, *code >> 31 ? " NOT" : ""); + formatAppend(result, "JUMPXEQKN R%d K%d L%d%s [", LUAU_INSN_A(insn), *code & 0xffffff, targetLabel, *code >> 31 ? " NOT" : ""); + dumpConstant(result, *code & 0xffffff); + result.append("]\n"); code++; break; case LOP_JUMPXEQKS: - formatAppend(result, "JUMPXEQKS R%d K%d L%d%s\n", LUAU_INSN_A(insn), *code & 0xffffff, targetLabel, *code >> 31 ? " NOT" : ""); + formatAppend(result, "JUMPXEQKS R%d K%d L%d%s [", LUAU_INSN_A(insn), *code & 0xffffff, targetLabel, *code >> 31 ? " NOT" : ""); + dumpConstant(result, *code & 0xffffff); + result.append("]\n"); code++; break; diff --git a/Compiler/src/Compiler.cpp b/Compiler/src/Compiler.cpp index 8a1e80f..fbeef68 100644 --- a/Compiler/src/Compiler.cpp +++ b/Compiler/src/Compiler.cpp @@ -28,6 +28,7 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5) LUAU_FASTFLAG(LuauInterpolatedStringBaseSupport) LUAU_FASTFLAGVARIABLE(LuauMultiAssignmentConflictFix, false) LUAU_FASTFLAGVARIABLE(LuauSelfAssignmentSkip, false) +LUAU_FASTFLAGVARIABLE(LuauCompileInterpStringLimit, false) namespace Luau { @@ -1580,7 +1581,8 @@ struct Compiler RegScope rs(this); - uint8_t baseReg = allocReg(expr, uint8_t(2 + expr->expressions.size)); + uint8_t baseReg = FFlag::LuauCompileInterpStringLimit ? allocReg(expr, unsigned(2 + expr->expressions.size)) + : allocReg(expr, uint8_t(2 + expr->expressions.size)); emitLoadK(baseReg, formatStringIndex); diff --git a/Sources.cmake b/Sources.cmake index 87d76bf..36e4f04 100644 --- a/Sources.cmake +++ b/Sources.cmake @@ -82,6 +82,7 @@ target_sources(Luau.CodeGen PRIVATE CodeGen/src/EmitCommonX64.cpp CodeGen/src/EmitInstructionX64.cpp CodeGen/src/Fallbacks.cpp + CodeGen/src/IrDump.cpp CodeGen/src/NativeState.cpp CodeGen/src/UnwindBuilderDwarf2.cpp CodeGen/src/UnwindBuilderWin.cpp @@ -95,6 +96,9 @@ target_sources(Luau.CodeGen PRIVATE CodeGen/src/EmitInstructionX64.h CodeGen/src/Fallbacks.h CodeGen/src/FallbacksProlog.h + CodeGen/src/IrDump.h + CodeGen/src/IrData.h + CodeGen/src/IrUtils.h CodeGen/src/NativeState.h ) diff --git a/VM/include/lualib.h b/VM/include/lualib.h index 955604d..190cf66 100644 --- a/VM/include/lualib.h +++ b/VM/include/lualib.h @@ -91,13 +91,13 @@ typedef struct luaL_Buffer luaL_Buffer; // all the buffer users we have in Luau match this pattern, but it's something to keep in mind for new uses of buffers #define luaL_addchar(B, c) ((void)((B)->p < (B)->end || luaL_extendbuffer(B, 1, -1)), (*(B)->p++ = (char)(c))) -#define luaL_addstring(B, s) luaL_addlstring(B, s, strlen(s)) +#define luaL_addstring(B, s) luaL_addlstring(B, s, strlen(s), -1) LUALIB_API void luaL_buffinit(lua_State* L, luaL_Buffer* B); LUALIB_API char* luaL_buffinitsize(lua_State* L, luaL_Buffer* B, size_t size); LUALIB_API char* luaL_extendbuffer(luaL_Buffer* B, size_t additionalsize, int boxloc); LUALIB_API void luaL_reservebuffer(luaL_Buffer* B, size_t size, int boxloc); -LUALIB_API void luaL_addlstring(luaL_Buffer* B, const char* s, size_t l); +LUALIB_API void luaL_addlstring(luaL_Buffer* B, const char* s, size_t l, int boxloc); LUALIB_API void luaL_addvalue(luaL_Buffer* B); LUALIB_API void luaL_pushresult(luaL_Buffer* B); LUALIB_API void luaL_pushresultsize(luaL_Buffer* B, size_t size); diff --git a/VM/src/laux.cpp b/VM/src/laux.cpp index c2d07dd..b4490ff 100644 --- a/VM/src/laux.cpp +++ b/VM/src/laux.cpp @@ -414,10 +414,10 @@ void luaL_reservebuffer(luaL_Buffer* B, size_t size, int boxloc) luaL_extendbuffer(B, size - (B->end - B->p), boxloc); } -void luaL_addlstring(luaL_Buffer* B, const char* s, size_t len) +void luaL_addlstring(luaL_Buffer* B, const char* s, size_t len, int boxloc) { if (size_t(B->end - B->p) < len) - luaL_extendbuffer(B, len - (B->end - B->p), -1); + luaL_extendbuffer(B, len - (B->end - B->p), boxloc); memcpy(B->p, s, len); B->p += len; diff --git a/VM/src/ldblib.cpp b/VM/src/ldblib.cpp index ece4f55..97ddfa2 100644 --- a/VM/src/ldblib.cpp +++ b/VM/src/ldblib.cpp @@ -137,7 +137,7 @@ static int db_traceback(lua_State* L) *--lineptr = '0' + (r % 10); luaL_addchar(&buf, ':'); - luaL_addlstring(&buf, lineptr, lineend - lineptr); + luaL_addlstring(&buf, lineptr, lineend - lineptr, -1); } if (ar.name) diff --git a/VM/src/loslib.cpp b/VM/src/loslib.cpp index 62a5668..9b10229 100644 --- a/VM/src/loslib.cpp +++ b/VM/src/loslib.cpp @@ -151,7 +151,7 @@ static int os_date(lua_State* L) char buff[200]; // should be big enough for any conversion result cc[1] = *(++s); reslen = strftime(buff, sizeof(buff), cc, stm); - luaL_addlstring(&b, buff, reslen); + luaL_addlstring(&b, buff, reslen, -1); } } luaL_pushresult(&b); diff --git a/VM/src/lstrlib.cpp b/VM/src/lstrlib.cpp index 192ea0b..59f16e5 100644 --- a/VM/src/lstrlib.cpp +++ b/VM/src/lstrlib.cpp @@ -8,6 +8,8 @@ #include #include +LUAU_FASTFLAGVARIABLE(LuauStringFormatAnyFix, false) + // macro to `unsign' a character #define uchar(c) ((unsigned char)(c)) @@ -771,7 +773,7 @@ static void add_s(MatchState* ms, luaL_Buffer* b, const char* s, const char* e) luaL_addchar(b, news[i]); } else if (news[i] == '0') - luaL_addlstring(b, s, e - s); + luaL_addlstring(b, s, e - s, -1); else { push_onecapture(ms, news[i] - '1', s, e); @@ -854,7 +856,7 @@ static int str_gsub(lua_State* L) if (anchor) break; } - luaL_addlstring(&b, src, ms.src_end - src); + luaL_addlstring(&b, src, ms.src_end - src, -1); luaL_pushresult(&b); lua_pushinteger(L, n); // number of substitutions return 2; @@ -891,12 +893,12 @@ static void addquoted(lua_State* L, luaL_Buffer* b, int arg) } case '\r': { - luaL_addlstring(b, "\\r", 2); + luaL_addlstring(b, "\\r", 2, -1); break; } case '\0': { - luaL_addlstring(b, "\\000", 4); + luaL_addlstring(b, "\\000", 4, -1); break; } default: @@ -1012,7 +1014,7 @@ static int str_format(lua_State* L) case 'q': { addquoted(L, &b, arg); - continue; // skip the 'addsize' at the end + continue; // skip the 'luaL_addlstring' at the end } case 's': { @@ -1024,7 +1026,7 @@ static int str_format(lua_State* L) keep original string */ lua_pushvalue(L, arg); luaL_addvalue(&b); - continue; // skip the `addsize' at the end + continue; // skip the `luaL_addlstring' at the end } else { @@ -1037,18 +1039,30 @@ static int str_format(lua_State* L) if (formatItemSize != 1) luaL_error(L, "'%%*' does not take a form"); - size_t length; - const char* string = luaL_tolstring(L, arg, &length); + if (FFlag::LuauStringFormatAnyFix) + { + size_t length; + const char* string = luaL_tolstring(L, arg, &length); - luaL_addlstring(&b, string, length); - continue; // skip the `addsize' at the end + luaL_addlstring(&b, string, length, -2); + lua_pop(L, 1); + } + else + { + size_t length; + const char* string = luaL_tolstring(L, arg, &length); + + luaL_addlstring(&b, string, length, -1); + } + + continue; // skip the `luaL_addlstring' at the end } default: { // also treat cases `pnLlh' luaL_error(L, "invalid option '%%%c' to 'format'", *(strfrmt - 1)); } } - luaL_addlstring(&b, buff, strlen(buff)); + luaL_addlstring(&b, buff, strlen(buff), -1); } } luaL_pushresult(&b); @@ -1360,7 +1374,7 @@ static void packint(luaL_Buffer* b, unsigned long long n, int islittle, int size for (i = SZINT; i < size; i++) // correct extra bytes buff[islittle ? i : size - 1 - i] = (char)MC; } - luaL_addlstring(b, buff, size); // add result to buffer + luaL_addlstring(b, buff, size, -1); // add result to buffer } /* @@ -1434,7 +1448,7 @@ static int str_pack(lua_State* L) u.n = n; // move 'u' to final result, correcting endianness if needed copywithendian(buff, u.buff, size, h.islittle); - luaL_addlstring(&b, buff, size); + luaL_addlstring(&b, buff, size, -1); break; } case Kchar: @@ -1442,7 +1456,7 @@ static int str_pack(lua_State* L) size_t len; const char* s = luaL_checklstring(L, arg, &len); luaL_argcheck(L, len <= (size_t)size, arg, "string longer than given size"); - luaL_addlstring(&b, s, len); // add string + luaL_addlstring(&b, s, len, -1); // add string while (len++ < (size_t)size) // pad extra space luaL_addchar(&b, LUAL_PACKPADBYTE); break; @@ -1453,7 +1467,7 @@ static int str_pack(lua_State* L) const char* s = luaL_checklstring(L, arg, &len); luaL_argcheck(L, size >= (int)sizeof(size_t) || len < ((size_t)1 << (size * NB)), arg, "string length does not fit in given size"); packint(&b, len, h.islittle, size, 0); // pack length - luaL_addlstring(&b, s, len); + luaL_addlstring(&b, s, len, -1); totalsize += len; break; } @@ -1462,7 +1476,7 @@ static int str_pack(lua_State* L) size_t len; const char* s = luaL_checklstring(L, arg, &len); luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); - luaL_addlstring(&b, s, len); + luaL_addlstring(&b, s, len, -1); luaL_addchar(&b, '\0'); // add zero at the end totalsize += len + 1; break; diff --git a/VM/src/ltablib.cpp b/VM/src/ltablib.cpp index 6dd9414..0efa9ee 100644 --- a/VM/src/ltablib.cpp +++ b/VM/src/ltablib.cpp @@ -238,7 +238,7 @@ static int tconcat(lua_State* L) for (; i < last; i++) { addfield(L, &b, i); - luaL_addlstring(&b, sep, lsep); + luaL_addlstring(&b, sep, lsep, -1); } if (i == last) // add last value (if interval was not empty) addfield(L, &b, i); diff --git a/VM/src/lutf8lib.cpp b/VM/src/lutf8lib.cpp index 837d0e1..0bbce01 100644 --- a/VM/src/lutf8lib.cpp +++ b/VM/src/lutf8lib.cpp @@ -175,7 +175,7 @@ static int utfchar(lua_State* L) for (int i = 1; i <= n; i++) { int l = buffutfchar(L, i, buff, &charstr); - luaL_addlstring(&b, charstr, l); + luaL_addlstring(&b, charstr, l, -1); } luaL_pushresult(&b); } diff --git a/bench/bench.py b/bench/bench.py index 0db3395..547e0d3 100644 --- a/bench/bench.py +++ b/bench/bench.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details import argparse import os diff --git a/tests/AssemblyBuilderX64.test.cpp b/tests/AssemblyBuilderX64.test.cpp index b5dbf58..d6d1650 100644 --- a/tests/AssemblyBuilderX64.test.cpp +++ b/tests/AssemblyBuilderX64.test.cpp @@ -243,6 +243,12 @@ TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "FormsOfLea") SINGLE_COMPARE(lea(rax, addr[r13 + r12 * 4 + 4]), 0x4b, 0x8d, 0x44, 0xa5, 0x04); } +TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "FormsOfSetcc") +{ + SINGLE_COMPARE(setcc(ConditionX64::NotEqual, bl), 0x0f, 0x95, 0xc3); + SINGLE_COMPARE(setcc(ConditionX64::BelowEqual, byte[rcx]), 0x0f, 0x96, 0x01); +} + TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "FormsOfAbsoluteJumps") { SINGLE_COMPARE(jmp(rax), 0xff, 0xe0); diff --git a/tests/Autocomplete.test.cpp b/tests/Autocomplete.test.cpp index 0ac86e8..b578752 100644 --- a/tests/Autocomplete.test.cpp +++ b/tests/Autocomplete.test.cpp @@ -15,6 +15,7 @@ LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2) LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel) +LUAU_FASTFLAG(LuauFixAutocompleteInIf) using namespace Luau; @@ -789,14 +790,30 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_if_middle_keywords") CHECK_EQ(ac2.entryMap.count("end"), 0); CHECK_EQ(ac2.context, AutocompleteContext::Keyword); - check(R"( - if x t@1 - )"); + if (FFlag::LuauFixAutocompleteInIf) + { + check(R"( + if x t@1 + )"); - auto ac3 = autocomplete('1'); - CHECK_EQ(1, ac3.entryMap.size()); - CHECK_EQ(ac3.entryMap.count("then"), 1); - CHECK_EQ(ac3.context, AutocompleteContext::Keyword); + auto ac3 = autocomplete('1'); + CHECK_EQ(3, ac3.entryMap.size()); + CHECK_EQ(ac3.entryMap.count("then"), 1); + CHECK_EQ(ac3.entryMap.count("and"), 1); + CHECK_EQ(ac3.entryMap.count("or"), 1); + CHECK_EQ(ac3.context, AutocompleteContext::Keyword); + } + else + { + check(R"( + if x t@1 + )"); + + auto ac3 = autocomplete('1'); + CHECK_EQ(1, ac3.entryMap.size()); + CHECK_EQ(ac3.entryMap.count("then"), 1); + CHECK_EQ(ac3.context, AutocompleteContext::Keyword); + } check(R"( if x then @@ -839,6 +856,23 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_if_middle_keywords") CHECK_EQ(ac5.entryMap.count("elseif"), 0); CHECK_EQ(ac5.entryMap.count("end"), 0); CHECK_EQ(ac5.context, AutocompleteContext::Statement); + + if (FFlag::LuauFixAutocompleteInIf) + { + check(R"( + if t@1 + )"); + + auto ac6 = autocomplete('1'); + CHECK_EQ(ac6.entryMap.count("true"), 1); + CHECK_EQ(ac6.entryMap.count("false"), 1); + CHECK_EQ(ac6.entryMap.count("then"), 0); + CHECK_EQ(ac6.entryMap.count("function"), 1); + CHECK_EQ(ac6.entryMap.count("else"), 0); + CHECK_EQ(ac6.entryMap.count("elseif"), 0); + CHECK_EQ(ac6.entryMap.count("end"), 0); + CHECK_EQ(ac6.context, AutocompleteContext::Expression); + } } TEST_CASE_FIXTURE(ACFixture, "autocomplete_until_in_repeat") diff --git a/tests/Compiler.test.cpp b/tests/Compiler.test.cpp index 1a60612..42d88c7 100644 --- a/tests/Compiler.test.cpp +++ b/tests/Compiler.test.cpp @@ -59,14 +59,14 @@ TEST_CASE("CompileToBytecode") CHECK_EQ("\n" + bcb.dumpFunction(0), R"( LOADN R0 5 -LOADK R1 K0 +LOADK R1 K0 [6.5] RETURN R0 2 )"); CHECK_EQ("\n" + bcb.dumpEverything(), R"( Function 0 (??): LOADN R0 5 -LOADK R1 K0 +LOADK R1 K0 [6.5] RETURN R0 2 )"); @@ -102,7 +102,7 @@ TEST_CASE("BasicFunction") Luau::compileOrThrow(bcb, "local function foo(a, b) return b end"); CHECK_EQ("\n" + bcb.dumpFunction(1), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] RETURN R0 0 )"); @@ -129,15 +129,15 @@ TEST_CASE("FunctionCallOptimization") { // direct call into local CHECK_EQ("\n" + compileFunction0("local foo = math.foo()"), R"( -GETIMPORT R0 2 +GETIMPORT R0 2 [math.foo] CALL R0 0 1 RETURN R0 0 )"); // direct call into temp CHECK_EQ("\n" + compileFunction0("local foo = math.foo(math.bar())"), R"( -GETIMPORT R0 2 -GETIMPORT R1 4 +GETIMPORT R0 2 [math.foo] +GETIMPORT R1 4 [math.bar] CALL R1 0 -1 CALL R0 -1 1 RETURN R0 0 @@ -146,7 +146,7 @@ RETURN R0 0 // can't directly call into local since foo might be used as arguments of caller CHECK_EQ("\n" + compileFunction0("local foo foo = math.foo(foo)"), R"( LOADNIL R0 -GETIMPORT R1 2 +GETIMPORT R1 2 [math.foo] MOVE R2 R0 CALL R1 1 1 MOVE R0 R1 @@ -162,19 +162,19 @@ part.Size = Vector3.new(1, 2, 3) return part.Size.Z * part:GetMass() )"), R"( -GETIMPORT R0 2 -LOADK R1 K3 -GETIMPORT R2 5 +GETIMPORT R0 2 [Instance.new] +LOADK R1 K3 ['Part'] +GETIMPORT R2 5 [workspace] CALL R0 2 1 -GETIMPORT R1 7 +GETIMPORT R1 7 [Vector3.new] LOADN R2 1 LOADN R3 2 LOADN R4 3 CALL R1 3 1 -SETTABLEKS R1 R0 K8 -GETTABLEKS R3 R0 K8 -GETTABLEKS R2 R3 K9 -NAMECALL R3 R0 K10 +SETTABLEKS R1 R0 K8 ['Size'] +GETTABLEKS R3 R0 K8 ['Size'] +GETTABLEKS R2 R3 K9 ['Z'] +NAMECALL R3 R0 K10 ['GetMass'] CALL R3 1 1 MUL R1 R2 R3 RETURN R1 1 @@ -185,9 +185,9 @@ TEST_CASE("ImportCall") { CHECK_EQ("\n" + compileFunction0("return math.max(1, 2)"), R"( LOADN R1 1 -FASTCALL2K 18 R1 K0 L0 -LOADK R2 K0 -GETIMPORT R0 3 +FASTCALL2K 18 R1 K0 L0 [2] +LOADK R2 K0 [2] +GETIMPORT R0 3 [math.max] CALL R0 2 -1 L0: RETURN R0 -1 )"); @@ -198,8 +198,8 @@ TEST_CASE("FakeImportCall") const char* source = "math = {} function math.max() return 0 end function test() return math.max(1, 2) end"; CHECK_EQ("\n" + compileFunction(source, 1), R"( -GETGLOBAL R1 K0 -GETTABLEKS R0 R1 K1 +GETGLOBAL R1 K0 ['math'] +GETTABLEKS R0 R1 K1 ['max'] LOADN R1 1 LOADN R2 2 CALL R0 2 -1 @@ -220,7 +220,7 @@ TEST_CASE("AssignmentGlobal") { CHECK_EQ("\n" + compileFunction0("a = 2"), R"( LOADN R0 2 -SETGLOBAL R0 K0 +SETGLOBAL R0 K0 ['a'] RETURN R0 0 )"); } @@ -233,8 +233,8 @@ TEST_CASE("AssignmentTable") GETVARARGS R0 1 NEWTABLE R1 1 0 LOADN R2 2 -SETTABLEKS R2 R1 K0 -SETTABLEKS R0 R1 K0 +SETTABLEKS R2 R1 K0 ['b'] +SETTABLEKS R0 R1 K0 ['b'] RETURN R0 0 )"); } @@ -242,25 +242,25 @@ RETURN R0 0 TEST_CASE("ConcatChainOptimization") { CHECK_EQ("\n" + compileFunction0("return '1' .. '2'"), R"( -LOADK R1 K0 -LOADK R2 K1 +LOADK R1 K0 ['1'] +LOADK R2 K1 ['2'] CONCAT R0 R1 R2 RETURN R0 1 )"); CHECK_EQ("\n" + compileFunction0("return '1' .. '2' .. '3'"), R"( -LOADK R1 K0 -LOADK R2 K1 -LOADK R3 K2 +LOADK R1 K0 ['1'] +LOADK R2 K1 ['2'] +LOADK R3 K2 ['3'] CONCAT R0 R1 R3 RETURN R0 1 )"); CHECK_EQ("\n" + compileFunction0("return ('1' .. '2') .. '3'"), R"( -LOADK R3 K0 -LOADK R4 K1 +LOADK R3 K0 ['1'] +LOADK R4 K1 ['2'] CONCAT R1 R3 R4 -LOADK R2 K2 +LOADK R2 K2 ['3'] CONCAT R0 R1 R2 RETURN R0 1 )"); @@ -271,10 +271,10 @@ TEST_CASE("RepeatLocals") CHECK_EQ("\n" + compileFunction0("repeat local a a = 5 until a - 4 < 0 or a - 4 >= 0"), R"( L0: LOADNIL R0 LOADN R0 5 -SUBK R1 R0 K0 +SUBK R1 R0 K0 [4] LOADN R2 0 JUMPIFLT R1 R2 L1 -SUBK R1 R0 K0 +SUBK R1 R0 K0 [4] LOADN R2 0 JUMPIFLE R2 R1 L1 JUMPBACK L0 @@ -290,7 +290,7 @@ LOADN R2 1 LOADN R0 5 LOADN R1 1 FORNPREP R0 L1 -L0: GETIMPORT R3 1 +L0: GETIMPORT R3 1 [print] MOVE R4 R2 CALL R3 1 0 FORNLOOP R0 L0 @@ -305,7 +305,7 @@ LOADN R1 1 FORNPREP R0 L1 L0: MOVE R3 R2 LOADN R3 7 -GETIMPORT R4 1 +GETIMPORT R4 1 [print] MOVE R5 R3 CALL R4 1 0 FORNLOOP R0 L0 @@ -314,12 +314,12 @@ L1: RETURN R0 0 // basic for-in loop, generic version CHECK_EQ("\n" + compileFunction0("for word in string.gmatch(\"Hello Lua user\", \"%a+\") do print(word) end"), R"( -GETIMPORT R0 2 -LOADK R1 K3 -LOADK R2 K4 +GETIMPORT R0 2 [string.gmatch] +LOADK R1 K3 ['Hello Lua user'] +LOADK R2 K4 ['%a+'] CALL R0 2 3 FORGPREP R0 L1 -L0: GETIMPORT R5 6 +L0: GETIMPORT R5 6 [print] MOVE R6 R3 CALL R5 1 0 L1: FORGLOOP R0 L0 1 @@ -328,11 +328,11 @@ RETURN R0 0 // basic for-in loop, using inext specialization CHECK_EQ("\n" + compileFunction0("for k,v in ipairs({}) do print(k,v) end"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [ipairs] NEWTABLE R1 0 0 CALL R0 1 3 FORGPREP_INEXT R0 L1 -L0: GETIMPORT R5 3 +L0: GETIMPORT R5 3 [print] MOVE R6 R3 MOVE R7 R4 CALL R5 2 0 @@ -342,11 +342,11 @@ RETURN R0 0 // basic for-in loop, using next specialization CHECK_EQ("\n" + compileFunction0("for k,v in pairs({}) do print(k,v) end"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [pairs] NEWTABLE R1 0 0 CALL R0 1 3 FORGPREP_NEXT R0 L1 -L0: GETIMPORT R5 3 +L0: GETIMPORT R5 3 [print] MOVE R6 R3 MOVE R7 R4 CALL R5 2 0 @@ -355,11 +355,11 @@ RETURN R0 0 )"); CHECK_EQ("\n" + compileFunction0("for k,v in next,{} do print(k,v) end"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [next] NEWTABLE R1 0 0 LOADNIL R2 FORGPREP_NEXT R0 L1 -L0: GETIMPORT R5 3 +L0: GETIMPORT R5 3 [print] MOVE R6 R3 MOVE R7 R4 CALL R5 2 0 @@ -372,7 +372,7 @@ TEST_CASE("ForBytecodeBuiltin") { // we generally recognize builtins like pairs/ipairs and emit special opcodes CHECK_EQ("\n" + compileFunction0("for k,v in ipairs({}) do end"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [ipairs] NEWTABLE R1 0 0 CALL R0 1 3 FORGPREP_INEXT R0 L0 @@ -382,7 +382,7 @@ RETURN R0 0 // ... even if they are using a local variable CHECK_EQ("\n" + compileFunction0("local ip = ipairs for k,v in ip({}) do end"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [ipairs] MOVE R1 R0 NEWTABLE R2 0 0 CALL R1 1 3 @@ -403,8 +403,8 @@ RETURN R0 0 // but if it's reassigned then all bets are off CHECK_EQ("\n" + compileFunction0("local ip = ipairs ip = pairs for k,v in ip({}) do end"), R"( -GETIMPORT R0 1 -GETIMPORT R0 3 +GETIMPORT R0 1 [ipairs] +GETIMPORT R0 3 [pairs] MOVE R1 R0 NEWTABLE R2 0 0 CALL R1 1 3 @@ -415,9 +415,9 @@ RETURN R0 0 // or if the global is hijacked CHECK_EQ("\n" + compileFunction0("ipairs = pairs for k,v in ipairs({}) do end"), R"( -GETIMPORT R0 1 -SETGLOBAL R0 K2 -GETGLOBAL R0 K2 +GETIMPORT R0 1 [pairs] +SETGLOBAL R0 K2 ['ipairs'] +GETGLOBAL R0 K2 ['ipairs'] NEWTABLE R1 0 0 CALL R0 1 3 FORGPREP R0 L0 @@ -427,7 +427,7 @@ RETURN R0 0 // or if we don't even know the global to begin with CHECK_EQ("\n" + compileFunction0("for k,v in unknown({}) do end"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [unknown] NEWTABLE R1 0 0 CALL R0 1 3 FORGPREP R0 L0 @@ -512,11 +512,11 @@ RETURN R0 1 CHECK_EQ("\n" + compileFunction0("return {a=1,b=2,c=3}"), R"( DUPTABLE R0 3 LOADN R1 1 -SETTABLEKS R1 R0 K0 +SETTABLEKS R1 R0 K0 ['a'] LOADN R1 2 -SETTABLEKS R1 R0 K1 +SETTABLEKS R1 R0 K1 ['b'] LOADN R1 3 -SETTABLEKS R1 R0 K2 +SETTABLEKS R1 R0 K2 ['c'] RETURN R0 1 )"); @@ -524,9 +524,9 @@ RETURN R0 1 CHECK_EQ("\n" + compileFunction0("return {a=1,b=2,3,4}"), R"( NEWTABLE R0 2 2 LOADN R3 1 -SETTABLEKS R3 R0 K0 +SETTABLEKS R3 R0 K0 ['a'] LOADN R3 2 -SETTABLEKS R3 R0 K1 +SETTABLEKS R3 R0 K1 ['b'] LOADN R1 3 LOADN R2 4 SETLIST R0 R1 2 [1] @@ -536,9 +536,9 @@ RETURN R0 1 // expression assignment CHECK_EQ("\n" + compileFunction0("a = 7 return {[a]=42}"), R"( LOADN R0 7 -SETGLOBAL R0 K0 +SETGLOBAL R0 K0 ['a'] NEWTABLE R0 1 0 -GETGLOBAL R1 K0 +GETGLOBAL R1 K0 ['a'] LOADN R2 42 SETTABLE R2 R0 R1 RETURN R0 1 @@ -548,19 +548,19 @@ RETURN R0 1 CHECK_EQ("\n" + compileFunction0("return {a=1,b=2},{b=3,a=4},{a=5,b=6}"), R"( DUPTABLE R0 2 LOADN R1 1 -SETTABLEKS R1 R0 K0 +SETTABLEKS R1 R0 K0 ['a'] LOADN R1 2 -SETTABLEKS R1 R0 K1 +SETTABLEKS R1 R0 K1 ['b'] DUPTABLE R1 3 LOADN R2 3 -SETTABLEKS R2 R1 K1 +SETTABLEKS R2 R1 K1 ['b'] LOADN R2 4 -SETTABLEKS R2 R1 K0 +SETTABLEKS R2 R1 K0 ['a'] DUPTABLE R2 2 LOADN R3 5 -SETTABLEKS R3 R2 K0 +SETTABLEKS R3 R2 K0 ['a'] LOADN R3 6 -SETTABLEKS R3 R2 K1 +SETTABLEKS R3 R2 K1 ['b'] RETURN R0 3 )"); } @@ -624,9 +624,9 @@ RETURN R0 1 CHECK_EQ("\n" + compileFunction0("return {key = 1, value = 2, [1] = 42}"), R"( NEWTABLE R0 2 1 LOADN R1 1 -SETTABLEKS R1 R0 K0 +SETTABLEKS R1 R0 K0 ['key'] LOADN R1 2 -SETTABLEKS R1 R0 K1 +SETTABLEKS R1 R0 K1 ['value'] LOADN R1 42 SETTABLEN R1 R0 1 RETURN R0 1 @@ -643,9 +643,9 @@ TEST_CASE("TableLiteralsIndexConstant") R"( NEWTABLE R0 2 0 LOADN R1 42 -SETTABLEKS R1 R0 K0 +SETTABLEKS R1 R0 K0 ['key'] LOADN R1 0 -SETTABLEKS R1 R0 K1 +SETTABLEKS R1 R0 K1 ['value'] RETURN R0 1 )"); @@ -681,23 +681,23 @@ t.i = 1 R"( NEWTABLE R0 16 0 LOADN R1 1 -SETTABLEKS R1 R0 K0 +SETTABLEKS R1 R0 K0 ['a'] LOADN R1 1 -SETTABLEKS R1 R0 K1 +SETTABLEKS R1 R0 K1 ['b'] LOADN R1 1 -SETTABLEKS R1 R0 K2 +SETTABLEKS R1 R0 K2 ['c'] LOADN R1 1 -SETTABLEKS R1 R0 K3 +SETTABLEKS R1 R0 K3 ['d'] LOADN R1 1 -SETTABLEKS R1 R0 K4 +SETTABLEKS R1 R0 K4 ['e'] LOADN R1 1 -SETTABLEKS R1 R0 K5 +SETTABLEKS R1 R0 K5 ['f'] LOADN R1 1 -SETTABLEKS R1 R0 K6 +SETTABLEKS R1 R0 K6 ['g'] LOADN R1 1 -SETTABLEKS R1 R0 K7 +SETTABLEKS R1 R0 K7 ['h'] LOADN R1 1 -SETTABLEKS R1 R0 K8 +SETTABLEKS R1 R0 K8 ['i'] RETURN R0 0 )"); @@ -716,23 +716,23 @@ t.x = 9 R"( NEWTABLE R0 1 0 LOADN R1 1 -SETTABLEKS R1 R0 K0 +SETTABLEKS R1 R0 K0 ['x'] LOADN R1 2 -SETTABLEKS R1 R0 K0 +SETTABLEKS R1 R0 K0 ['x'] LOADN R1 3 -SETTABLEKS R1 R0 K0 +SETTABLEKS R1 R0 K0 ['x'] LOADN R1 4 -SETTABLEKS R1 R0 K0 +SETTABLEKS R1 R0 K0 ['x'] LOADN R1 5 -SETTABLEKS R1 R0 K0 +SETTABLEKS R1 R0 K0 ['x'] LOADN R1 6 -SETTABLEKS R1 R0 K0 +SETTABLEKS R1 R0 K0 ['x'] LOADN R1 7 -SETTABLEKS R1 R0 K0 +SETTABLEKS R1 R0 K0 ['x'] LOADN R1 8 -SETTABLEKS R1 R0 K0 +SETTABLEKS R1 R0 K0 ['x'] LOADN R1 9 -SETTABLEKS R1 R0 K0 +SETTABLEKS R1 R0 K0 ['x'] RETURN R0 0 )"); @@ -789,9 +789,9 @@ return t R"( NEWTABLE R0 2 0 LOADN R1 1 -SETTABLEKS R1 R0 K0 -DUPCLOSURE R1 K1 -SETTABLEKS R1 R0 K2 +SETTABLEKS R1 R0 K0 ['field'] +DUPCLOSURE R1 K1 ['getfield'] +SETTABLEKS R1 R0 K2 ['getfield'] RETURN R0 1 )"); } @@ -806,14 +806,14 @@ return t )"), R"( NEWTABLE R1 2 0 -FASTCALL2K 61 R1 K0 L0 -LOADK R2 K0 -GETIMPORT R0 2 +FASTCALL2K 61 R1 K0 L0 [nil] +LOADK R2 K0 [nil] +GETIMPORT R0 2 [setmetatable] CALL R0 2 1 L0: LOADN R1 1 -SETTABLEKS R1 R0 K3 +SETTABLEKS R1 R0 K3 ['field1'] LOADN R1 2 -SETTABLEKS R1 R0 K4 +SETTABLEKS R1 R0 K4 ['field2'] RETURN R0 1 )"); } @@ -843,7 +843,7 @@ L1: RETURN R0 1 TEST_CASE("ReflectionEnums") { CHECK_EQ("\n" + compileFunction0("return Enum.EasingStyle.Linear"), R"( -GETIMPORT R0 3 +GETIMPORT R0 3 [Enum.EasingStyle.Linear] RETURN R0 1 )"); } @@ -877,7 +877,7 @@ RETURN R0 0 CHECK_EQ("\n" + bcb.dumpFunction(0), R"( GETUPVAL R0 0 LOADN R1 5 -SETTABLEKS R1 R0 K0 +SETTABLEKS R1 R0 K0 ['_tweakingTooltipFrame'] RETURN R0 0 )"); } @@ -1030,7 +1030,7 @@ TEST_CASE("AndOr") // codegen for constant, local, global for and CHECK_EQ("\n" + compileFunction0("local a = 1 a = a and 2 return a"), R"( LOADN R0 1 -ANDK R0 R0 K0 +ANDK R0 R0 K0 [2] RETURN R0 1 )"); @@ -1044,10 +1044,10 @@ RETURN R0 1 CHECK_EQ("\n" + compileFunction0("local a = 1 b = 2 a = a and b return a"), R"( LOADN R0 1 LOADN R1 2 -SETGLOBAL R1 K0 +SETGLOBAL R1 K0 ['b'] MOVE R1 R0 JUMPIFNOT R1 L0 -GETGLOBAL R1 K0 +GETGLOBAL R1 K0 ['b'] L0: MOVE R0 R1 RETURN R0 1 )"); @@ -1055,7 +1055,7 @@ RETURN R0 1 // codegen for constant, local, global for or CHECK_EQ("\n" + compileFunction0("local a = 1 a = a or 2 return a"), R"( LOADN R0 1 -ORK R0 R0 K0 +ORK R0 R0 K0 [2] RETURN R0 1 )"); @@ -1069,10 +1069,10 @@ RETURN R0 1 CHECK_EQ("\n" + compileFunction0("local a = 1 b = 2 a = a or b return a"), R"( LOADN R0 1 LOADN R1 2 -SETGLOBAL R1 K0 +SETGLOBAL R1 K0 ['b'] MOVE R1 R0 JUMPIF R1 L0 -GETGLOBAL R1 K0 +GETGLOBAL R1 K0 ['b'] L0: MOVE R0 R1 RETURN R0 1 )"); @@ -1082,20 +1082,20 @@ RETURN R0 1 CHECK_EQ("\n" + compileFunction0("local a = 1 a = a b = 2 local c = a and b return c"), R"( LOADN R0 1 LOADN R1 2 -SETGLOBAL R1 K0 +SETGLOBAL R1 K0 ['b'] MOVE R1 R0 JUMPIFNOT R1 L0 -GETGLOBAL R1 K0 +GETGLOBAL R1 K0 ['b'] L0: RETURN R1 1 )"); CHECK_EQ("\n" + compileFunction0("local a = 1 a = a b = 2 local c = a or b return c"), R"( LOADN R0 1 LOADN R1 2 -SETGLOBAL R1 K0 +SETGLOBAL R1 K0 ['b'] MOVE R1 R0 JUMPIF R1 L0 -GETGLOBAL R1 K0 +GETGLOBAL R1 K0 ['b'] L0: RETURN R1 1 )"); } @@ -1108,7 +1108,7 @@ RETURN R0 0 )"); CHECK_EQ("\n" + compileFunction0("local a = true if a or b then b() end"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [b] CALL R0 0 0 RETURN R0 0 )"); @@ -1116,18 +1116,18 @@ RETURN R0 0 // however, if right hand side is constant we can't constant fold the entire expression // (note that we don't need to evaluate the right hand side, but we do need a branch) CHECK_EQ("\n" + compileFunction0("local a = false if b and a then b() end"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [b] JUMPIFNOT R0 L0 RETURN R0 0 -GETIMPORT R0 1 +GETIMPORT R0 1 [b] CALL R0 0 0 L0: RETURN R0 0 )"); CHECK_EQ("\n" + compileFunction0("local a = true if b or a then b() end"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [b] JUMPIF R0 L0 -L0: GETIMPORT R0 1 +L0: GETIMPORT R0 1 [b] CALL R0 0 0 RETURN R0 0 )"); @@ -1144,22 +1144,22 @@ TEST_CASE("AndOrChainCodegen") CHECK_EQ("\n" + compileFunction0(source), R"( LOADN R2 1 -GETIMPORT R3 1 +GETIMPORT R3 1 [verticalGradientTurbulence] SUB R1 R2 R3 -GETIMPORT R3 4 -ADDK R2 R3 K2 +GETIMPORT R3 4 [waterLevel] +ADDK R2 R3 K2 [0.014999999999999999] JUMPIFNOTLT R1 R2 L0 -GETIMPORT R0 8 +GETIMPORT R0 8 [Enum.Material.Sand] JUMPIF R0 L2 -L0: GETIMPORT R1 10 +L0: GETIMPORT R1 10 [sandbank] LOADN R2 0 JUMPIFNOTLT R2 R1 L1 -GETIMPORT R1 10 +GETIMPORT R1 10 [sandbank] LOADN R2 1 JUMPIFNOTLT R1 R2 L1 -GETIMPORT R0 8 +GETIMPORT R0 8 [Enum.Material.Sand] JUMPIF R0 L2 -L1: GETIMPORT R0 12 +L1: GETIMPORT R0 12 [Enum.Material.Sandstone] L2: RETURN R0 1 )"); } @@ -1205,7 +1205,7 @@ RETURN R0 1 // codegen for a non-constant condition CHECK_EQ("\n" + compileFunction0("return if condition then 10 else 20"), R"( -GETIMPORT R1 1 +GETIMPORT R1 1 [condition] JUMPIFNOT R1 L0 LOADN R0 10 RETURN R0 1 @@ -1215,18 +1215,18 @@ RETURN R0 1 // codegen for a non-constant condition using an assignment CHECK_EQ("\n" + compileFunction0("result = if condition then 10 else 20"), R"( -GETIMPORT R1 1 +GETIMPORT R1 1 [condition] JUMPIFNOT R1 L0 LOADN R0 10 JUMP L1 L0: LOADN R0 20 -L1: SETGLOBAL R0 K2 +L1: SETGLOBAL R0 K2 ['result'] RETURN R0 0 )"); // codegen for a non-constant condition using an assignment to a local variable CHECK_EQ("\n" + compileFunction0("local result = if condition then 10 else 20"), R"( -GETIMPORT R1 1 +GETIMPORT R1 1 [condition] JUMPIFNOT R1 L0 LOADN R0 10 RETURN R0 0 @@ -1236,20 +1236,20 @@ RETURN R0 0 // codegen for an if-else expression with multiple elseif's CHECK_EQ("\n" + compileFunction0("result = if condition1 then 10 elseif condition2 then 20 elseif condition3 then 30 else 40"), R"( -GETIMPORT R1 1 +GETIMPORT R1 1 [condition1] JUMPIFNOT R1 L0 LOADN R0 10 JUMP L3 -L0: GETIMPORT R1 3 +L0: GETIMPORT R1 3 [condition2] JUMPIFNOT R1 L1 LOADN R0 20 JUMP L3 -L1: GETIMPORT R1 5 +L1: GETIMPORT R1 5 [condition3] JUMPIFNOT R1 L2 LOADN R0 30 JUMP L3 L2: LOADN R0 40 -L3: SETGLOBAL R0 K6 +L3: SETGLOBAL R0 K6 ['result'] RETURN R0 0 )"); } @@ -1288,9 +1288,9 @@ TEST_CASE("InterpStringZeroCost") CHECK_EQ("\n" + compileFunction0(R"(local _ = `hello, {"world"}!`)"), R"( -LOADK R1 K0 -LOADK R3 K1 -NAMECALL R1 R1 K2 +LOADK R1 K0 ['hello, %*!'] +LOADK R3 K1 ['world'] +NAMECALL R1 R1 K2 ['format'] CALL R1 2 1 MOVE R0 R1 RETURN R0 0 @@ -1309,20 +1309,29 @@ TEST_CASE("InterpStringRegisterCleanup") R"( LOADNIL R0 -LOADK R1 K0 -LOADK R2 K1 -LOADK R3 K2 -LOADK R5 K3 -NAMECALL R3 R3 K4 +LOADK R1 K0 ['um'] +LOADK R2 K1 ['uh oh'] +LOADK R3 K2 ['foo%*'] +LOADK R5 K3 ['bar'] +NAMECALL R3 R3 K4 ['format'] CALL R3 2 1 MOVE R0 R3 -GETIMPORT R3 6 +GETIMPORT R3 6 [print] MOVE R4 R0 CALL R3 1 0 RETURN R0 0 )"); } +TEST_CASE("InterpStringRegisterLimit") +{ + ScopedFastFlag luauInterpolatedStringBaseSupport{"LuauInterpolatedStringBaseSupport", true}; + ScopedFastFlag luauCompileInterpStringLimit{"LuauCompileInterpStringLimit", true}; + + CHECK_THROWS_AS(compileFunction0(("local a = `" + rep("{1}", 254) + "`").c_str()), std::exception); + CHECK_THROWS_AS(compileFunction0(("local a = `" + rep("{1}", 253) + "`").c_str()), std::exception); +} + TEST_CASE("ConstantFoldArith") { CHECK_EQ("\n" + compileFunction0("return 10 + 2"), R"( @@ -1485,7 +1494,7 @@ RETURN R0 2 // local values for multiple assignments w/multret CHECK_EQ("\n" + compileFunction0("local a, b = ... return a + 1, b"), R"( GETVARARGS R0 2 -ADDK R2 R0 K0 +ADDK R2 R0 K0 [1] MOVE R3 R1 RETURN R2 2 )"); @@ -1534,7 +1543,7 @@ RETURN R0 1 // and/or constant folding when left hand side is constant CHECK_EQ("\n" + compileFunction0("return true and a"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [a] RETURN R0 1 )"); @@ -1549,22 +1558,22 @@ RETURN R0 1 )"); CHECK_EQ("\n" + compileFunction0("return false or a"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [a] RETURN R0 1 )"); // constant fold parts in chains of and/or statements CHECK_EQ("\n" + compileFunction0("return a and true and b"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [a] JUMPIFNOT R0 L0 -GETIMPORT R0 3 +GETIMPORT R0 3 [b] L0: RETURN R0 1 )"); CHECK_EQ("\n" + compileFunction0("return a or false or b"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [a] JUMPIF R0 L0 -GETIMPORT R0 3 +GETIMPORT R0 3 [b] L0: RETURN R0 1 )"); } @@ -1574,7 +1583,7 @@ TEST_CASE("ConstantFoldConditionalAndOr") CHECK_EQ("\n" + compileFunction0("local a = ... if false or a then print(1) end"), R"( GETVARARGS R0 1 JUMPIFNOT R0 L0 -GETIMPORT R1 1 +GETIMPORT R1 1 [print] LOADN R2 1 CALL R1 1 0 L0: RETURN R0 0 @@ -1583,7 +1592,7 @@ L0: RETURN R0 0 CHECK_EQ("\n" + compileFunction0("local a = ... if not (false or a) then print(1) end"), R"( GETVARARGS R0 1 JUMPIF R0 L0 -GETIMPORT R1 1 +GETIMPORT R1 1 [print] LOADN R2 1 CALL R1 1 0 L0: RETURN R0 0 @@ -1592,7 +1601,7 @@ L0: RETURN R0 0 CHECK_EQ("\n" + compileFunction0("local a = ... if true and a then print(1) end"), R"( GETVARARGS R0 1 JUMPIFNOT R0 L0 -GETIMPORT R1 1 +GETIMPORT R1 1 [print] LOADN R2 1 CALL R1 1 0 L0: RETURN R0 0 @@ -1601,7 +1610,7 @@ L0: RETURN R0 0 CHECK_EQ("\n" + compileFunction0("local a = ... if not (true and a) then print(1) end"), R"( GETVARARGS R0 1 JUMPIF R0 L0 -GETIMPORT R1 1 +GETIMPORT R1 1 [print] LOADN R2 1 CALL R1 1 0 L0: RETURN R0 0 @@ -1612,7 +1621,7 @@ TEST_CASE("ConstantFoldFlowControl") { // if CHECK_EQ("\n" + compileFunction0("if true then print(1) end"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [print] LOADN R1 1 CALL R0 1 0 RETURN R0 0 @@ -1623,14 +1632,14 @@ RETURN R0 0 )"); CHECK_EQ("\n" + compileFunction0("if true then print(1) else print(2) end"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [print] LOADN R1 1 CALL R0 1 0 RETURN R0 0 )"); CHECK_EQ("\n" + compileFunction0("if false then print(1) else print(2) end"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [print] LOADN R1 2 CALL R0 1 0 RETURN R0 0 @@ -1638,7 +1647,7 @@ RETURN R0 0 // while CHECK_EQ("\n" + compileFunction0("while true do print(1) end"), R"( -L0: GETIMPORT R0 1 +L0: GETIMPORT R0 1 [print] LOADN R1 1 CALL R0 1 0 JUMPBACK L0 @@ -1651,14 +1660,14 @@ RETURN R0 0 // repeat CHECK_EQ("\n" + compileFunction0("repeat print(1) until true"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [print] LOADN R1 1 CALL R0 1 0 RETURN R0 0 )"); CHECK_EQ("\n" + compileFunction0("repeat print(1) until false"), R"( -L0: GETIMPORT R0 1 +L0: GETIMPORT R0 1 [print] LOADN R1 1 CALL R0 1 0 JUMPBACK L0 @@ -1667,10 +1676,10 @@ RETURN R0 0 // there's an odd case in repeat..until compilation where we evaluate the expression that is always false for side-effects of the left hand side CHECK_EQ("\n" + compileFunction0("repeat print(1) until five and false"), R"( -L0: GETIMPORT R0 1 +L0: GETIMPORT R0 1 [print] LOADN R1 1 CALL R0 1 0 -GETIMPORT R0 3 +GETIMPORT R0 3 [five] JUMPIFNOT R0 L1 L1: JUMPBACK L0 RETURN R0 0 @@ -1681,9 +1690,9 @@ TEST_CASE("LoopBreak") { // default codegen: compile breaks as unconditional jumps CHECK_EQ("\n" + compileFunction0("while true do if math.random() < 0.5 then break else end end"), R"( -L0: GETIMPORT R0 2 +L0: GETIMPORT R0 2 [math.random] CALL R0 0 1 -LOADK R1 K3 +LOADK R1 K3 [0.5] JUMPIFNOTLT R0 R1 L1 RETURN R0 0 JUMP L1 @@ -1693,9 +1702,9 @@ RETURN R0 0 // optimization: if then body is a break statement, flip the branches CHECK_EQ("\n" + compileFunction0("while true do if math.random() < 0.5 then break end end"), R"( -L0: GETIMPORT R0 2 +L0: GETIMPORT R0 2 [math.random] CALL R0 0 1 -LOADK R1 K3 +LOADK R1 K3 [0.5] JUMPIFLT R0 R1 L1 JUMPBACK L0 L1: RETURN R0 0 @@ -1706,28 +1715,28 @@ TEST_CASE("LoopContinue") { // default codegen: compile continue as unconditional jumps CHECK_EQ("\n" + compileFunction0("repeat if math.random() < 0.5 then continue else end break until false error()"), R"( -L0: GETIMPORT R0 2 +L0: GETIMPORT R0 2 [math.random] CALL R0 0 1 -LOADK R1 K3 +LOADK R1 K3 [0.5] JUMPIFNOTLT R0 R1 L2 JUMP L1 JUMP L2 JUMP L2 L1: JUMPBACK L0 -L2: GETIMPORT R0 5 +L2: GETIMPORT R0 5 [error] CALL R0 0 0 RETURN R0 0 )"); // optimization: if then body is a continue statement, flip the branches CHECK_EQ("\n" + compileFunction0("repeat if math.random() < 0.5 then continue end break until false error()"), R"( -L0: GETIMPORT R0 2 +L0: GETIMPORT R0 2 [math.random] CALL R0 0 1 -LOADK R1 K3 +LOADK R1 K3 [0.5] JUMPIFLT R0 R1 L1 JUMP L2 L1: JUMPBACK L0 -L2: GETIMPORT R0 5 +L2: GETIMPORT R0 5 [error] CALL R0 0 0 RETURN R0 0 )"); @@ -1737,12 +1746,12 @@ TEST_CASE("LoopContinueUntil") { // it's valid to use locals defined inside the loop in until expression if they're defined before continue CHECK_EQ("\n" + compileFunction0("repeat local r = math.random() if r > 0.5 then continue end r = r + 0.3 until r < 0.5"), R"( -L0: GETIMPORT R0 2 +L0: GETIMPORT R0 2 [math.random] CALL R0 0 1 -LOADK R1 K3 +LOADK R1 K3 [0.5] JUMPIFLT R1 R0 L1 -ADDK R0 R0 K4 -L1: LOADK R1 K3 +ADDK R0 R0 K4 [0.29999999999999999] +L1: LOADK R1 K3 [0.5] JUMPIFLT R0 R1 L2 JUMPBACK L0 L2: RETURN R0 0 @@ -1776,13 +1785,13 @@ until rr < 0.5 CHECK_EQ("\n" + compileFunction0( "repeat local r = math.random() repeat if r > 0.5 then continue end r = r - 0.1 until true r = r + 0.3 until r < 0.5"), R"( -L0: GETIMPORT R0 2 +L0: GETIMPORT R0 2 [math.random] CALL R0 0 1 -LOADK R1 K3 +LOADK R1 K3 [0.5] JUMPIFLT R1 R0 L1 -SUBK R0 R0 K4 -L1: ADDK R0 R0 K5 -LOADK R1 K3 +SUBK R0 R0 K4 [0.10000000000000001] +L1: ADDK R0 R0 K5 [0.29999999999999999] +LOADK R1 K3 [0.5] JUMPIFLT R0 R1 L2 JUMPBACK L0 L2: RETURN R0 0 @@ -1793,13 +1802,13 @@ L2: RETURN R0 0 "\n" + compileFunction( "repeat local r = math.random() if r > 0.5 then continue end r = r + 0.3 until (function() local a = r return a < 0.5 end)()", 1), R"( -L0: GETIMPORT R0 2 +L0: GETIMPORT R0 2 [math.random] CALL R0 0 1 -LOADK R1 K3 +LOADK R1 K3 [0.5] JUMPIFNOTLT R1 R0 L1 CLOSEUPVALS R0 JUMP L2 -L1: ADDK R0 R0 K4 +L1: ADDK R0 R0 K4 [0.29999999999999999] L2: NEWCLOSURE R1 P0 CAPTURE REF R0 CALL R1 0 1 @@ -1837,14 +1846,14 @@ until (function() return rr end)() < 0.5 CHECK_EQ("\n" + compileFunction0("local stop = false stop = true function test() repeat local r = math.random() if r > 0.5 then " "continue end r = r + 0.3 until stop or r < 0.5 end"), R"( -L0: GETIMPORT R0 2 +L0: GETIMPORT R0 2 [math.random] CALL R0 0 1 -LOADK R1 K3 +LOADK R1 K3 [0.5] JUMPIFLT R1 R0 L1 -ADDK R0 R0 K4 +ADDK R0 R0 K4 [0.29999999999999999] L1: GETUPVAL R1 0 JUMPIF R1 L2 -LOADK R1 K3 +LOADK R1 K3 [0.5] JUMPIFLT R0 R1 L2 JUMPBACK L0 L2: RETURN R0 0 @@ -1855,13 +1864,13 @@ L2: RETURN R0 0 "end r = r + 0.3 until (function() return stop or r < 0.5 end)() end", 1), R"( -L0: GETIMPORT R0 2 +L0: GETIMPORT R0 2 [math.random] CALL R0 0 1 -LOADK R1 K3 +LOADK R1 K3 [0.5] JUMPIFNOTLT R1 R0 L1 CLOSEUPVALS R0 JUMP L2 -L1: ADDK R0 R0 K4 +L1: ADDK R0 R0 K4 [0.29999999999999999] L2: NEWCLOSURE R1 P0 CAPTURE UPVAL U0 CAPTURE REF R0 @@ -1906,7 +1915,7 @@ end )", 0), R"( -ORK R2 R1 K0 +ORK R2 R1 K0 [0.5] SUB R0 R0 R2 LOADN R4 1 LOADN R8 0 @@ -1914,7 +1923,7 @@ JUMPIFNOTLT R0 R8 L0 MINUS R7 R0 JUMPIF R7 L1 L0: MOVE R7 R0 -L1: MULK R6 R7 K1 +L1: MULK R6 R7 K1 [1] LOADN R8 1 SUB R7 R8 R2 DIV R5 R6 R7 @@ -1931,12 +1940,12 @@ end 0), R"( LOADB R2 0 -LOADK R4 K0 -MULK R5 R1 K1 +LOADK R4 K0 [0.5] +MULK R5 R1 K1 [0.40000000000000002] SUB R3 R4 R5 JUMPIFNOTLT R3 R0 L1 -LOADK R4 K0 -MULK R5 R1 K1 +LOADK R4 K0 [0.5] +MULK R5 R1 K1 [0.40000000000000002] ADD R3 R4 R5 JUMPIFLT R0 R3 L0 LOADB R2 0 +1 @@ -1953,12 +1962,12 @@ end 0), R"( LOADB R2 1 -LOADK R4 K0 -MULK R5 R1 K1 +LOADK R4 K0 [0.5] +MULK R5 R1 K1 [0.40000000000000002] SUB R3 R4 R5 JUMPIFLT R0 R3 L1 -LOADK R4 K0 -MULK R5 R1 K1 +LOADK R4 K0 [0.5] +MULK R5 R1 K1 [0.40000000000000002] ADD R3 R4 R5 JUMPIFLT R3 R0 L0 LOADB R2 0 +1 @@ -2006,7 +2015,7 @@ TEST_CASE("JumpFold") { // jump-to-return folding to return CHECK_EQ("\n" + compileFunction0("return a and 1 or 0"), R"( -GETIMPORT R1 1 +GETIMPORT R1 1 [a] JUMPIFNOT R1 L0 LOADN R0 1 RETURN R0 1 @@ -2016,26 +2025,26 @@ RETURN R0 1 // conditional jump in the inner if() folding to jump out of the expression (JUMPIFNOT+5 skips over all jumps, JUMP+1 skips over JUMP+0) CHECK_EQ("\n" + compileFunction0("if a then if b then b() else end else end d()"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [a] JUMPIFNOT R0 L0 -GETIMPORT R0 3 +GETIMPORT R0 3 [b] JUMPIFNOT R0 L0 -GETIMPORT R0 3 +GETIMPORT R0 3 [b] CALL R0 0 0 JUMP L0 JUMP L0 -L0: GETIMPORT R0 5 +L0: GETIMPORT R0 5 [d] CALL R0 0 0 RETURN R0 0 )"); // same as example before but the unconditional jumps are folded with RETURN CHECK_EQ("\n" + compileFunction0("if a then if b then b() else end else end"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [a] JUMPIFNOT R0 L0 -GETIMPORT R0 3 +GETIMPORT R0 3 [b] JUMPIFNOT R0 L0 -GETIMPORT R0 3 +GETIMPORT R0 3 [b] CALL R0 0 0 RETURN R0 0 RETURN R0 0 @@ -2057,33 +2066,33 @@ end )", 0), R"( -ORK R6 R3 K0 -ORK R7 R4 K1 +ORK R6 R3 K0 [0] +ORK R7 R4 K1 [1] JUMPIF R5 L0 -GETIMPORT R10 5 +GETIMPORT R10 5 [math.noise] DIV R13 R0 R7 -MULK R14 R6 K6 +MULK R14 R6 K6 [17] ADD R12 R13 R14 -GETIMPORT R13 8 +GETIMPORT R13 8 [masterSeed] ADD R11 R12 R13 DIV R13 R1 R7 -GETIMPORT R14 8 +GETIMPORT R14 8 [masterSeed] SUB R12 R13 R14 DIV R14 R2 R7 MUL R15 R6 R6 SUB R13 R14 R15 CALL R10 3 1 -MULK R9 R10 K2 -ADDK R8 R9 K2 +MULK R9 R10 K2 [0.5] +ADDK R8 R9 K2 [0.5] RETURN R8 1 -L0: GETIMPORT R8 5 +L0: GETIMPORT R8 5 [math.noise] DIV R11 R0 R7 -MULK R12 R6 K6 +MULK R12 R6 K6 [17] ADD R10 R11 R12 -GETIMPORT R11 8 +GETIMPORT R11 8 [masterSeed] ADD R9 R10 R11 DIV R11 R1 R7 -GETIMPORT R12 8 +GETIMPORT R12 8 [masterSeed] SUB R10 R11 R12 DIV R12 R2 R7 MUL R13 R6 R6 @@ -2248,11 +2257,11 @@ TEST_CASE("NestedFunctionCalls") FASTCALL2 18 R0 R1 L0 MOVE R5 R0 MOVE R6 R1 -GETIMPORT R4 2 +GETIMPORT R4 2 [math.max] CALL R4 2 1 L0: FASTCALL2 19 R4 R2 L1 MOVE R5 R2 -GETIMPORT R3 4 +GETIMPORT R3 4 [math.min] CALL R3 2 -1 L1: RETURN R3 -1 )"); @@ -2281,11 +2290,11 @@ LOADN R0 10 LOADN R1 1 FORNPREP R0 L2 L0: MOVE R3 R2 -GETIMPORT R4 1 +GETIMPORT R4 1 [foo] NEWCLOSURE R5 P0 CAPTURE REF R3 CALL R4 1 0 -GETIMPORT R4 3 +GETIMPORT R4 3 [bar] JUMPIFNOT R4 L1 CLOSEUPVALS R3 JUMP L2 @@ -2309,15 +2318,15 @@ end )", 1), R"( -GETIMPORT R0 1 -GETIMPORT R1 3 +GETIMPORT R0 1 [ipairs] +GETIMPORT R1 3 [data] CALL R0 1 3 FORGPREP_INEXT R0 L2 -L0: GETIMPORT R5 5 +L0: GETIMPORT R5 5 [foo] NEWCLOSURE R6 P0 CAPTURE REF R3 CALL R5 1 0 -GETIMPORT R5 7 +GETIMPORT R5 7 [bar] JUMPIFNOT R5 L1 CLOSEUPVALS R3 JUMP L3 @@ -2349,12 +2358,12 @@ L0: LOADN R1 5 JUMPIFNOTLT R0 R1 L2 LOADNIL R1 MOVE R1 R0 -GETIMPORT R2 1 +GETIMPORT R2 1 [foo] NEWCLOSURE R3 P0 CAPTURE REF R1 CALL R2 1 0 -ADDK R0 R0 K2 -GETIMPORT R2 4 +ADDK R0 R0 K2 [1] +GETIMPORT R2 4 [bar] JUMPIFNOT R2 L1 CLOSEUPVALS R1 JUMP L2 @@ -2384,12 +2393,12 @@ end LOADN R0 0 L0: LOADNIL R1 MOVE R1 R0 -GETIMPORT R2 1 +GETIMPORT R2 1 [foo] NEWCLOSURE R3 P0 CAPTURE REF R1 CALL R2 1 0 -ADDK R0 R0 K2 -GETIMPORT R2 4 +ADDK R0 R0 K2 [1] +GETIMPORT R2 4 [bar] JUMPIFNOT R2 L1 CLOSEUPVALS R1 JUMP L3 @@ -2437,25 +2446,25 @@ return result CHECK_EQ("\n" + bcb.dumpFunction(0), R"( 2: NEWTABLE R0 16 0 3: LOADB R1 1 -3: SETTABLEKS R1 R0 K0 +3: SETTABLEKS R1 R0 K0 ['Mountains'] 4: LOADB R1 1 -4: SETTABLEKS R1 R0 K1 +4: SETTABLEKS R1 R0 K1 ['Canyons'] 5: LOADB R1 1 -5: SETTABLEKS R1 R0 K2 +5: SETTABLEKS R1 R0 K2 ['Dunes'] 6: LOADB R1 1 -6: SETTABLEKS R1 R0 K3 +6: SETTABLEKS R1 R0 K3 ['Arctic'] 7: LOADB R1 1 -7: SETTABLEKS R1 R0 K4 +7: SETTABLEKS R1 R0 K4 ['Lavaflow'] 8: LOADB R1 1 -8: SETTABLEKS R1 R0 K5 +8: SETTABLEKS R1 R0 K5 ['Hills'] 9: LOADB R1 1 -9: SETTABLEKS R1 R0 K6 +9: SETTABLEKS R1 R0 K6 ['Plains'] 10: LOADB R1 1 -10: SETTABLEKS R1 R0 K7 +10: SETTABLEKS R1 R0 K7 ['Marsh'] 11: LOADB R1 1 -11: SETTABLEKS R1 R0 K8 -13: LOADK R1 K9 -14: GETIMPORT R2 11 +11: SETTABLEKS R1 R0 K8 ['Water'] +13: LOADK R1 K9 [''] +14: GETIMPORT R2 11 [pairs] 14: MOVE R3 R0 14: CALL R2 1 3 14: FORGPREP_NEXT R2 L1 @@ -2490,7 +2499,7 @@ end 7: LOADN R1 2 9: LOADN R2 3 9: FORGPREP R0 L1 -11: L0: GETIMPORT R5 1 +11: L0: GETIMPORT R5 1 [print] 11: MOVE R6 R3 11: CALL R5 1 0 2: L1: FORGLOOP R0 L0 1 @@ -2515,11 +2524,11 @@ end CHECK_EQ("\n" + bcb.dumpFunction(0), R"( 2: LOADN R0 0 -4: L0: ADDK R0 R0 K0 +4: L0: ADDK R0 R0 K0 [1] 5: LOADN R1 1 5: JUMPIFNOTLT R1 R0 L1 -6: GETIMPORT R1 2 -6: LOADK R2 K3 +6: GETIMPORT R1 2 [print] +6: LOADK R2 K3 ['done!'] 6: CALL R1 1 0 10: RETURN R0 0 3: L1: JUMPBACK L0 @@ -2543,14 +2552,14 @@ until f == 0 0), R"( 2: LOADN R0 0 -4: L0: ADDK R0 R0 K0 -5: JUMPXEQKN R0 K0 L1 NOT -6: GETIMPORT R1 2 +4: L0: ADDK R0 R0 K0 [1] +5: JUMPXEQKN R0 K0 L1 NOT [1] +6: GETIMPORT R1 2 [print] 6: MOVE R2 R0 6: CALL R1 1 0 6: JUMP L2 8: L1: LOADN R0 0 -10: L2: JUMPXEQKN R0 K3 L3 +10: L2: JUMPXEQKN R0 K3 L3 [0] 10: JUMPBACK L0 11: L3: RETURN R0 0 )"); @@ -2575,14 +2584,14 @@ Table.SubTable["Key"] = { CHECK_EQ("\n" + bcb.dumpFunction(0), R"( 2: GETVARARGS R0 3 3: NEWTABLE R3 0 0 -5: GETTABLEKS R4 R3 K0 +5: GETTABLEKS R4 R3 K0 ['SubTable'] 5: DUPTABLE R5 5 -6: SETTABLEKS R0 R5 K1 -7: SETTABLEKS R1 R5 K2 -8: SETTABLEKS R2 R5 K3 +6: SETTABLEKS R0 R5 K1 ['Key1'] +7: SETTABLEKS R1 R5 K2 ['Key2'] +8: SETTABLEKS R2 R5 K3 ['Key3'] 9: LOADB R6 1 -9: SETTABLEKS R6 R5 K4 -5: SETTABLEKS R5 R4 K6 +9: SETTABLEKS R6 R5 K4 ['Key4'] +5: SETTABLEKS R5 R4 K6 ['Key'] 11: RETURN R0 0 )"); } @@ -2605,7 +2614,7 @@ Foo:Bar( 5: LOADN R3 1 6: LOADN R4 2 7: LOADN R5 3 -4: NAMECALL R1 R0 K0 +4: NAMECALL R1 R0 K0 ['Bar'] 4: CALL R1 4 0 8: RETURN R0 0 )"); @@ -2627,12 +2636,12 @@ Foo CHECK_EQ("\n" + bcb.dumpFunction(0), R"( 2: GETVARARGS R0 1 5: LOADN R4 1 -5: NAMECALL R2 R0 K0 +5: NAMECALL R2 R0 K0 ['Bar'] 5: CALL R2 2 1 6: LOADN R4 2 -6: NAMECALL R2 R2 K1 +6: NAMECALL R2 R2 K1 ['Baz'] 6: CALL R2 2 1 -7: GETTABLEKS R1 R2 K2 +7: GETTABLEKS R1 R2 K2 ['Qux'] 7: LOADN R2 3 7: CALL R1 1 0 8: RETURN R0 0 @@ -2657,7 +2666,7 @@ return 5: FASTCALL2 18 R0 R1 L0 5: MOVE R3 R0 5: MOVE R4 R1 -5: GETIMPORT R2 2 +5: GETIMPORT R2 2 [math.max] 5: CALL R2 2 -1 5: L0: RETURN R2 -1 )"); @@ -2681,13 +2690,13 @@ a 2: DUPTABLE R1 3 2: DUPTABLE R2 5 2: LOADN R3 3 -2: SETTABLEKS R3 R2 K4 -2: SETTABLEKS R2 R1 K2 -2: SETTABLEKS R1 R0 K0 -5: GETTABLEKS R2 R0 K0 -6: GETTABLEKS R1 R2 K2 +2: SETTABLEKS R3 R2 K4 ['d'] +2: SETTABLEKS R2 R1 K2 ['c'] +2: SETTABLEKS R1 R0 K0 ['b'] +5: GETTABLEKS R2 R0 K0 ['b'] +6: GETTABLEKS R1 R2 K2 ['c'] 7: LOADN R2 4 -7: SETTABLEKS R2 R1 K4 +7: SETTABLEKS R2 R1 K4 ['d'] 8: RETURN R0 0 )"); } @@ -2724,35 +2733,35 @@ return result NEWTABLE R0 16 0 3: ['Mountains'] = true, LOADB R1 1 -SETTABLEKS R1 R0 K0 +SETTABLEKS R1 R0 K0 ['Mountains'] 4: ['Canyons'] = true, LOADB R1 1 -SETTABLEKS R1 R0 K1 +SETTABLEKS R1 R0 K1 ['Canyons'] 5: ['Dunes'] = true, LOADB R1 1 -SETTABLEKS R1 R0 K2 +SETTABLEKS R1 R0 K2 ['Dunes'] 6: ['Arctic'] = true, LOADB R1 1 -SETTABLEKS R1 R0 K3 +SETTABLEKS R1 R0 K3 ['Arctic'] 7: ['Lavaflow'] = true, LOADB R1 1 -SETTABLEKS R1 R0 K4 +SETTABLEKS R1 R0 K4 ['Lavaflow'] 8: ['Hills'] = true, LOADB R1 1 -SETTABLEKS R1 R0 K5 +SETTABLEKS R1 R0 K5 ['Hills'] 9: ['Plains'] = true, LOADB R1 1 -SETTABLEKS R1 R0 K6 +SETTABLEKS R1 R0 K6 ['Plains'] 10: ['Marsh'] = true, LOADB R1 1 -SETTABLEKS R1 R0 K7 +SETTABLEKS R1 R0 K7 ['Marsh'] 11: ['Water'] = true, LOADB R1 1 -SETTABLEKS R1 R0 K8 +SETTABLEKS R1 R0 K8 ['Water'] 13: local result = "" -LOADK R1 K9 +LOADK R1 K9 [''] 14: for k in pairs(kSelectedBiomes) do -GETIMPORT R2 11 +GETIMPORT R2 11 [pairs] MOVE R3 R0 CALL R2 1 3 FORGPREP_NEXT R2 L1 @@ -2817,25 +2826,25 @@ local 8: reg 3, start pc 35 line 21, end pc 35 line 21 4: LOADN R3 3 4: LOADN R4 1 4: FORNPREP R3 L1 -5: L0: GETIMPORT R6 1 +5: L0: GETIMPORT R6 1 [print] 5: MOVE R7 R5 5: CALL R6 1 0 4: FORNLOOP R3 L0 -7: L1: GETIMPORT R3 3 +7: L1: GETIMPORT R3 3 [pairs] 7: CALL R3 0 3 7: FORGPREP_NEXT R3 L3 -8: L2: GETIMPORT R8 1 +8: L2: GETIMPORT R8 1 [print] 8: MOVE R9 R6 8: MOVE R10 R7 8: CALL R8 2 0 7: L3: FORGLOOP R3 L2 2 11: LOADN R3 2 -12: GETIMPORT R4 1 +12: GETIMPORT R4 1 [print] 12: LOADN R5 2 12: CALL R4 1 0 15: LOADN R3 2 -16: GETIMPORT R4 1 -16: GETIMPORT R5 5 +16: GETIMPORT R4 1 [print] +16: GETIMPORT R5 5 [b] 16: CALL R4 1 0 18: NEWCLOSURE R3 P0 18: CAPTURE VAL R3 @@ -2944,7 +2953,7 @@ RETURN R0 0 LOADNIL R0 LOADN R1 1 LOADN R2 2 -SETTABLEKS R2 R0 K0 +SETTABLEKS R2 R0 K0 ['foo'] MOVE R0 R1 RETURN R0 0 )"); @@ -2952,7 +2961,7 @@ RETURN R0 0 // ... or a table index ... CHECK_EQ("\n" + compileFunction0("local a a, foo[a] = 1, 2"), R"( LOADNIL R0 -GETIMPORT R1 1 +GETIMPORT R1 1 [foo] LOADN R2 1 LOADN R3 2 SETTABLE R3 R1 R0 @@ -2987,8 +2996,8 @@ RETURN R0 0 // into a temp register CHECK_EQ("\n" + compileFunction0("local a a, foo[a + 1] = 1, 2"), R"( LOADNIL R0 -GETIMPORT R1 1 -ADDK R2 R0 K2 +GETIMPORT R1 1 [foo] +ADDK R2 R0 K2 [1] LOADN R0 1 LOADN R3 2 SETTABLE R3 R1 R2 @@ -3002,14 +3011,14 @@ TEST_CASE("FastcallBytecode") CHECK_EQ("\n" + compileFunction0("return math.abs(-5)"), R"( LOADN R1 -5 FASTCALL1 2 R1 L0 -GETIMPORT R0 2 +GETIMPORT R0 2 [math.abs] CALL R0 1 -1 L0: RETURN R0 -1 )"); // call through a local variable CHECK_EQ("\n" + compileFunction0("local abs = math.abs return abs(-5)"), R"( -GETIMPORT R0 2 +GETIMPORT R0 2 [math.abs] LOADN R2 -5 FASTCALL1 2 R2 L0 MOVE R1 R0 @@ -3029,9 +3038,9 @@ L0: RETURN R0 -1 // mutating the global in the script breaks the optimization CHECK_EQ("\n" + compileFunction0("math = {} return math.abs(-5)"), R"( NEWTABLE R0 0 0 -SETGLOBAL R0 K0 -GETGLOBAL R1 K0 -GETTABLEKS R0 R1 K1 +SETGLOBAL R0 K0 ['math'] +GETGLOBAL R1 K0 ['math'] +GETTABLEKS R0 R1 K1 ['abs'] LOADN R1 -5 CALL R0 1 -1 RETURN R0 -1 @@ -3039,7 +3048,7 @@ RETURN R0 -1 // mutating the local in the script breaks the optimization CHECK_EQ("\n" + compileFunction0("local abs = math.abs abs = nil return abs(-5)"), R"( -GETIMPORT R0 2 +GETIMPORT R0 2 [math.abs] LOADNIL R0 MOVE R1 R0 LOADN R2 -5 @@ -3049,10 +3058,10 @@ RETURN R1 -1 // mutating the global in the script breaks the optimization, even if you do this after computing the local (for simplicity) CHECK_EQ("\n" + compileFunction0("local abs = math.abs math = {} return abs(-5)"), R"( -GETGLOBAL R1 K0 -GETTABLEKS R0 R1 K1 +GETGLOBAL R1 K0 ['math'] +GETTABLEKS R0 R1 K1 ['abs'] NEWTABLE R1 0 0 -SETGLOBAL R1 K0 +SETGLOBAL R1 K0 ['math'] MOVE R1 R0 LOADN R2 -5 CALL R1 1 -1 @@ -3064,9 +3073,9 @@ TEST_CASE("FastcallSelect") { // select(_, ...) compiles to a builtin call CHECK_EQ("\n" + compileFunction0("return (select('#', ...))"), R"( -LOADK R1 K0 +LOADK R1 K0 ['#'] FASTCALL1 57 R1 L0 -GETIMPORT R0 2 +GETIMPORT R0 2 [select] GETVARARGS R2 -1 CALL R0 -1 1 L0: RETURN R0 1 @@ -3083,16 +3092,16 @@ return sum R"( LOADN R0 0 LOADN R3 1 -LOADK R5 K0 +LOADK R5 K0 ['#'] FASTCALL1 57 R5 L0 -GETIMPORT R4 2 +GETIMPORT R4 2 [select] GETVARARGS R6 -1 CALL R4 -1 1 L0: MOVE R1 R4 LOADN R2 1 FORNPREP R1 L3 L1: FASTCALL1 57 R3 L2 -GETIMPORT R4 2 +GETIMPORT R4 2 [select] MOVE R5 R3 GETVARARGS R6 -1 CALL R4 -1 1 @@ -3103,8 +3112,8 @@ L3: RETURN R0 1 // currently we assume a single value return to avoid dealing with stack resizing CHECK_EQ("\n" + compileFunction0("return select('#', ...)"), R"( -GETIMPORT R0 1 -LOADK R1 K2 +GETIMPORT R0 1 [select] +LOADK R1 K2 ['#'] GETVARARGS R2 -1 CALL R0 -1 -1 RETURN R0 -1 @@ -3112,17 +3121,17 @@ RETURN R0 -1 // note that select with a non-variadic second argument doesn't get optimized CHECK_EQ("\n" + compileFunction0("return select('#')"), R"( -GETIMPORT R0 1 -LOADK R1 K2 +GETIMPORT R0 1 [select] +LOADK R1 K2 ['#'] CALL R0 1 -1 RETURN R0 -1 )"); // note that select with a non-variadic second argument doesn't get optimized CHECK_EQ("\n" + compileFunction0("return select('#', foo())"), R"( -GETIMPORT R0 1 -LOADK R1 K2 -GETIMPORT R2 4 +GETIMPORT R0 1 [select] +LOADK R1 K2 ['#'] +GETIMPORT R2 4 [foo] CALL R2 0 -1 CALL R0 -1 -1 RETURN R0 -1 @@ -3194,7 +3203,7 @@ RETURN R0 1 )"); CHECK_EQ("\n" + compileFunction0("return -0"), R"( -LOADK R0 K0 +LOADK R0 K0 [-0] RETURN R0 1 )"); } @@ -3253,14 +3262,14 @@ RETURN R0 1 // recursive capture CHECK_EQ("\n" + compileFunction("local function foo() return foo() end", 1), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] CAPTURE VAL R0 RETURN R0 0 )"); // multi-level recursive capture CHECK_EQ("\n" + compileFunction("local function foo() return function() return foo() end end", 1), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 [] CAPTURE UPVAL U0 RETURN R0 1 )"); @@ -3411,12 +3420,12 @@ TEST_CASE("FastCallImportFallback") // note: it's important that GETGLOBAL below doesn't overwrite R2 CHECK_EQ("\n" + fragment, R"( LOADN R1 1024 -LOADK R2 K1023 +LOADK R2 K1023 ['1024'] SETTABLE R2 R0 R1 LOADN R2 -1 FASTCALL1 2 R2 L0 -GETGLOBAL R3 K1024 -GETTABLEKS R1 R3 K1025 +GETGLOBAL R3 K1024 ['math'] +GETTABLEKS R1 R3 K1025 ['abs'] CALL R1 1 -1 )"); } @@ -3425,25 +3434,25 @@ TEST_CASE("CompoundAssignment") { // globals vs constants CHECK_EQ("\n" + compileFunction0("a += 1"), R"( -GETGLOBAL R0 K0 -ADDK R0 R0 K1 -SETGLOBAL R0 K0 +GETGLOBAL R0 K0 ['a'] +ADDK R0 R0 K1 [1] +SETGLOBAL R0 K0 ['a'] RETURN R0 0 )"); // globals vs expressions CHECK_EQ("\n" + compileFunction0("a -= a"), R"( -GETGLOBAL R0 K0 -GETGLOBAL R1 K0 +GETGLOBAL R0 K0 ['a'] +GETGLOBAL R1 K0 ['a'] SUB R0 R0 R1 -SETGLOBAL R0 K0 +SETGLOBAL R0 K0 ['a'] RETURN R0 0 )"); // locals vs constants CHECK_EQ("\n" + compileFunction0("local a = 1 a *= 2"), R"( LOADN R0 1 -MULK R0 R0 K0 +MULK R0 R0 K0 [2] RETURN R0 0 )"); @@ -3457,7 +3466,7 @@ RETURN R0 0 // locals vs expressions CHECK_EQ("\n" + compileFunction0("local a = 1 a /= a + 1"), R"( LOADN R0 1 -ADDK R1 R0 K0 +ADDK R1 R0 K0 [1] DIV R0 R0 R1 RETURN R0 0 )"); @@ -3465,7 +3474,7 @@ RETURN R0 0 // upvalues CHECK_EQ("\n" + compileFunction0("local a = 1 function foo() a += 4 end"), R"( GETUPVAL R0 0 -ADDK R0 R0 K0 +ADDK R0 R0 K0 [4] SETUPVAL R0 0 RETURN R0 0 )"); @@ -3473,16 +3482,16 @@ RETURN R0 0 // table variants (indexed by string, number, variable) CHECK_EQ("\n" + compileFunction0("local a = {} a.foo += 5"), R"( NEWTABLE R0 0 0 -GETTABLEKS R1 R0 K0 -ADDK R1 R1 K1 -SETTABLEKS R1 R0 K0 +GETTABLEKS R1 R0 K0 ['foo'] +ADDK R1 R1 K1 [5] +SETTABLEKS R1 R0 K0 ['foo'] RETURN R0 0 )"); CHECK_EQ("\n" + compileFunction0("local a = {} a[1] += 5"), R"( NEWTABLE R0 0 0 GETTABLEN R1 R0 1 -ADDK R1 R1 K0 +ADDK R1 R1 K0 [5] SETTABLEN R1 R0 1 RETURN R0 0 )"); @@ -3490,19 +3499,19 @@ RETURN R0 0 CHECK_EQ("\n" + compileFunction0("local a = {} a[a] += 5"), R"( NEWTABLE R0 0 0 GETTABLE R1 R0 R0 -ADDK R1 R1 K0 +ADDK R1 R1 K0 [5] SETTABLE R1 R0 R0 RETURN R0 0 )"); // left hand side is evaluated once CHECK_EQ("\n" + compileFunction0("foo()[bar()] += 5"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [foo] CALL R0 0 1 -GETIMPORT R1 3 +GETIMPORT R1 3 [bar] CALL R1 0 1 GETTABLE R2 R0 R1 -ADDK R2 R2 K4 +ADDK R2 R2 K4 [5] SETTABLE R2 R0 R1 RETURN R0 0 )"); @@ -3512,40 +3521,40 @@ TEST_CASE("CompoundAssignmentConcat") { // basic concat CHECK_EQ("\n" + compileFunction0("local a = '' a ..= 'a'"), R"( -LOADK R0 K0 +LOADK R0 K0 [''] MOVE R1 R0 -LOADK R2 K1 +LOADK R2 K1 ['a'] CONCAT R0 R1 R2 RETURN R0 0 )"); // concat chains CHECK_EQ("\n" + compileFunction0("local a = '' a ..= 'a' .. 'b'"), R"( -LOADK R0 K0 +LOADK R0 K0 [''] MOVE R1 R0 -LOADK R2 K1 -LOADK R3 K2 +LOADK R2 K1 ['a'] +LOADK R3 K2 ['b'] CONCAT R0 R1 R3 RETURN R0 0 )"); CHECK_EQ("\n" + compileFunction0("local a = '' a ..= 'a' .. 'b' .. 'c'"), R"( -LOADK R0 K0 +LOADK R0 K0 [''] MOVE R1 R0 -LOADK R2 K1 -LOADK R3 K2 -LOADK R4 K3 +LOADK R2 K1 ['a'] +LOADK R3 K2 ['b'] +LOADK R4 K3 ['c'] CONCAT R0 R1 R4 RETURN R0 0 )"); // concat on non-local CHECK_EQ("\n" + compileFunction0("_VERSION ..= 'a' .. 'b'"), R"( -GETGLOBAL R1 K0 -LOADK R2 K1 -LOADK R3 K2 +GETGLOBAL R1 K0 ['_VERSION'] +LOADK R2 K1 ['a'] +LOADK R3 K2 ['b'] CONCAT R0 R1 R3 -SETGLOBAL R0 K0 +SETGLOBAL R0 K0 ['_VERSION'] RETURN R0 0 )"); } @@ -3588,12 +3597,12 @@ JUMP L1 L0: JUMPX L14543 L1: FORNPREP R1 L0 L2: ADD R0 R0 R3 -LOADK R4 K0 +LOADK R4 K0 [150000] JUMP L4 L3: JUMPX L14543 L4: JUMPIFLT R4 R0 L3 ADD R0 R0 R3 -LOADK R4 K0 +LOADK R4 K0 [150000] JUMP L6 L5: JUMPX L14543 )"); @@ -3606,10 +3615,10 @@ L5: JUMPX L14543 CHECK_EQ("\n" + tail, R"( ADD R0 R0 R3 -LOADK R4 K0 +LOADK R4 K0 [150000] JUMPIFLT R4 R0 L14543 ADD R0 R0 R3 -LOADK R4 K0 +LOADK R4 K0 [150000] JUMPIFLT R4 R0 L14543 JUMP L14542 L14541: JUMPX L2 @@ -3634,13 +3643,13 @@ return obj:Method(1):Method(2):Method(3) R"( GETVARARGS R0 1 LOADN R3 1 -NAMECALL R1 R0 K0 +NAMECALL R1 R0 K0 ['Method'] CALL R1 2 1 LOADN R3 2 -NAMECALL R1 R1 K0 +NAMECALL R1 R1 K0 ['Method'] CALL R1 2 1 LOADN R3 3 -NAMECALL R1 R1 K0 +NAMECALL R1 R1 K0 ['Method'] CALL R1 2 -1 RETURN R1 -1 )"); @@ -3664,7 +3673,7 @@ local a = g() return a )"), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [g] CALL R0 0 1 RETURN R0 1 )"); @@ -3676,7 +3685,7 @@ return a )"), R"( LOADN R0 1 -GETIMPORT R1 1 +GETIMPORT R1 1 [g] CALL R1 0 1 RETURN R0 1 )"); @@ -3690,7 +3699,7 @@ local b = obj == 1 )"), R"( GETVARARGS R0 1 -JUMPXEQKN R0 K0 L0 +JUMPXEQKN R0 K0 L0 [1] LOADB R1 0 +1 L0: LOADB R1 1 L1: RETURN R0 0 @@ -3702,7 +3711,7 @@ local b = 1 == obj )"), R"( GETVARARGS R0 1 -JUMPXEQKN R0 K0 L0 +JUMPXEQKN R0 K0 L0 [1] LOADB R1 0 +1 L0: LOADB R1 1 L1: RETURN R0 0 @@ -3714,7 +3723,7 @@ local b = "Hello, Sailor!" == obj )"), R"( GETVARARGS R0 1 -JUMPXEQKS R0 K0 L0 +JUMPXEQKS R0 K0 L0 ['Hello, Sailor!'] LOADB R1 0 +1 L0: LOADB R1 1 L1: RETURN R0 0 @@ -3780,8 +3789,8 @@ return t['a'] R"( DUPTABLE R0 1 LOADN R1 2 -SETTABLEKS R1 R0 K0 -GETTABLEKS R1 R0 K0 +SETTABLEKS R1 R0 K0 ['a'] +GETTABLEKS R1 R0 K0 ['a'] RETURN R1 1 )"); @@ -3792,7 +3801,7 @@ t['a'] = 2 R"( NEWTABLE R0 0 0 LOADN R1 2 -SETTABLEKS R1 R0 K0 +SETTABLEKS R1 R0 K0 ['a'] RETURN R0 0 )"); } @@ -3807,11 +3816,11 @@ print(2) 1), R"( 2: COVERAGE -2: GETIMPORT R0 1 +2: GETIMPORT R0 1 [print] 2: LOADN R1 1 2: CALL R0 1 0 3: COVERAGE -3: GETIMPORT R0 1 +3: GETIMPORT R0 1 [print] 3: LOADN R1 2 3: CALL R0 1 0 4: RETURN R0 0 @@ -3828,15 +3837,15 @@ end 1), R"( 2: COVERAGE -2: GETIMPORT R0 1 +2: GETIMPORT R0 1 [x] 2: JUMPIFNOT R0 L0 3: COVERAGE -3: GETIMPORT R0 3 +3: GETIMPORT R0 3 [print] 3: LOADN R1 1 3: CALL R0 1 0 7: RETURN R0 0 5: L0: COVERAGE -5: GETIMPORT R0 3 +5: GETIMPORT R0 3 [print] 5: LOADN R1 2 5: CALL R0 1 0 7: RETURN R0 0 @@ -3856,15 +3865,15 @@ end 1), R"( 2: COVERAGE -2: GETIMPORT R0 1 +2: GETIMPORT R0 1 [x] 2: JUMPIFNOT R0 L0 4: COVERAGE -4: GETIMPORT R0 3 +4: GETIMPORT R0 3 [print] 4: LOADN R1 1 4: CALL R0 1 0 9: RETURN R0 0 7: L0: COVERAGE -7: GETIMPORT R0 3 +7: GETIMPORT R0 3 [print] 7: LOADN R1 2 7: CALL R0 1 0 9: RETURN R0 0 @@ -3891,13 +3900,13 @@ local t = { 4: COVERAGE 4: COVERAGE 4: LOADN R2 1 -4: SETTABLEKS R2 R1 K0 +4: SETTABLEKS R2 R1 K0 ['a'] 5: COVERAGE 5: COVERAGE 5: LOADN R2 2 -5: SETTABLEKS R2 R1 K1 +5: SETTABLEKS R2 R1 K1 ['b'] 6: COVERAGE -6: SETTABLEKS R0 R1 K2 +6: SETTABLEKS R0 R1 K2 ['c'] 8: RETURN R0 0 )"); } @@ -3910,7 +3919,7 @@ return function() end )", 1), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 [] RETURN R0 1 )"); @@ -3920,7 +3929,7 @@ return function() print("hi") end )", 1), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 [] RETURN R0 1 )"); @@ -3933,7 +3942,7 @@ end )", 1), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [print] NEWCLOSURE R1 P0 CAPTURE VAL R0 RETURN R1 1 @@ -3946,7 +3955,7 @@ return function() print("hi") end )", 1), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [setfenv] LOADN R1 1 NEWTABLE R2 0 0 CALL R0 2 0 @@ -3978,7 +3987,7 @@ end )", 1), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 [] CAPTURE UPVAL U0 RETURN R0 1 )"); @@ -4027,9 +4036,9 @@ end )", 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['bar'] CAPTURE UPVAL U0 -DUPCLOSURE R1 K1 +DUPCLOSURE R1 K1 [] CAPTURE VAL R0 RETURN R1 1 )"); @@ -4055,7 +4064,7 @@ RETURN R2 1 // we also allow recursive function captures to share the object, even when it's not top-level CHECK_EQ("\n" + compileFunction("function test() local function foo() return foo() end end", 1), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] CAPTURE VAL R0 RETURN R0 0 )"); @@ -4097,16 +4106,16 @@ LOADN R2 1 LOADN R0 10 LOADN R1 1 FORNPREP R0 L1 -L0: GETIMPORT R3 1 +L0: GETIMPORT R3 1 [print] NEWCLOSURE R4 P0 CAPTURE VAL R2 CALL R3 1 0 FORNLOOP R0 L0 -L1: GETIMPORT R0 3 +L1: GETIMPORT R0 3 [pairs] GETVARARGS R1 -1 CALL R0 -1 3 FORGPREP_NEXT R0 L3 -L2: GETIMPORT R5 1 +L2: GETIMPORT R5 1 [print] NEWCLOSURE R6 P1 CAPTURE VAL R3 CALL R5 1 0 @@ -4115,7 +4124,7 @@ LOADN R2 1 LOADN R0 10 LOADN R1 1 FORNPREP R0 L5 -L4: GETIMPORT R3 1 +L4: GETIMPORT R3 1 [print] NEWCLOSURE R4 P2 CAPTURE VAL R2 CALL R3 1 0 @@ -4140,24 +4149,24 @@ workspace.print() // Check Roblox globals are no longer here CHECK_EQ("\n" + compileFunction0(source), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [print] CALL R0 0 0 -GETIMPORT R0 3 +GETIMPORT R0 3 [Game.print] CALL R0 0 0 -GETIMPORT R0 5 +GETIMPORT R0 5 [Workspace.print] CALL R0 0 0 -GETIMPORT R1 7 -GETTABLEKS R0 R1 K0 +GETIMPORT R1 7 [_G] +GETTABLEKS R0 R1 K0 ['print'] CALL R0 0 0 -GETIMPORT R0 9 +GETIMPORT R0 9 [game.print] CALL R0 0 0 -GETIMPORT R0 11 +GETIMPORT R0 11 [plugin.print] CALL R0 0 0 -GETIMPORT R0 13 +GETIMPORT R0 13 [script.print] CALL R0 0 0 -GETIMPORT R0 15 +GETIMPORT R0 15 [shared.print] CALL R0 0 0 -GETIMPORT R0 17 +GETIMPORT R0 17 [workspace.print] CALL R0 0 0 RETURN R0 0 )"); @@ -4171,31 +4180,31 @@ RETURN R0 0 Luau::compileOrThrow(bcb, source, options); CHECK_EQ("\n" + bcb.dumpFunction(0), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [print] CALL R0 0 0 -GETIMPORT R1 3 -GETTABLEKS R0 R1 K0 +GETIMPORT R1 3 [Game] +GETTABLEKS R0 R1 K0 ['print'] CALL R0 0 0 -GETIMPORT R1 5 -GETTABLEKS R0 R1 K0 +GETIMPORT R1 5 [Workspace] +GETTABLEKS R0 R1 K0 ['print'] CALL R0 0 0 -GETIMPORT R1 7 -GETTABLEKS R0 R1 K0 +GETIMPORT R1 7 [_G] +GETTABLEKS R0 R1 K0 ['print'] CALL R0 0 0 -GETIMPORT R1 9 -GETTABLEKS R0 R1 K0 +GETIMPORT R1 9 [game] +GETTABLEKS R0 R1 K0 ['print'] CALL R0 0 0 -GETIMPORT R1 11 -GETTABLEKS R0 R1 K0 +GETIMPORT R1 11 [plugin] +GETTABLEKS R0 R1 K0 ['print'] CALL R0 0 0 -GETIMPORT R1 13 -GETTABLEKS R0 R1 K0 +GETIMPORT R1 13 [script] +GETTABLEKS R0 R1 K0 ['print'] CALL R0 0 0 -GETIMPORT R1 15 -GETTABLEKS R0 R1 K0 +GETIMPORT R1 15 [shared] +GETTABLEKS R0 R1 K0 ['print'] CALL R0 0 0 -GETIMPORT R1 17 -GETTABLEKS R0 R1 K0 +GETIMPORT R1 17 [workspace] +GETTABLEKS R0 R1 K0 ['print'] CALL R0 0 0 RETURN R0 0 )"); @@ -4214,8 +4223,8 @@ TEST_CASE("ConstantsNoFolding") CHECK_EQ("\n" + bcb.dumpFunction(0), R"( LOADNIL R0 LOADB R1 1 -LOADK R2 K0 -LOADK R3 K1 +LOADK R2 K0 [42] +LOADK R3 K1 ['hello'] RETURN R0 4 )"); } @@ -4236,7 +4245,7 @@ LOADN R1 1 LOADN R2 2 LOADN R3 3 FASTCALL 54 L0 -GETIMPORT R0 2 +GETIMPORT R0 2 [Vector3.new] CALL R0 3 -1 L0: RETURN R0 -1 )"); @@ -4249,8 +4258,8 @@ TEST_CASE("TypeAssertion") print(foo() :: typeof(error("compile time"))) )"), R"( -GETIMPORT R0 1 -GETIMPORT R1 3 +GETIMPORT R0 1 [print] +GETIMPORT R1 3 [foo] CALL R1 0 1 CALL R0 1 0 RETURN R0 0 @@ -4261,8 +4270,8 @@ RETURN R0 0 print(foo()) )"), R"( -GETIMPORT R0 1 -GETIMPORT R1 3 +GETIMPORT R0 1 [print] +GETIMPORT R1 3 [foo] CALL R1 0 -1 CALL R0 -1 0 RETURN R0 0 @@ -4295,12 +4304,12 @@ return a + 1, a - 1, a / 1, a * 1, a % 1, a ^ 1 )"), R"( GETVARARGS R0 1 -ADDK R1 R0 K0 -SUBK R2 R0 K0 -DIVK R3 R0 K0 -MULK R4 R0 K0 -MODK R5 R0 K0 -POWK R6 R0 K0 +ADDK R1 R0 K0 [1] +SUBK R2 R0 K0 [1] +DIVK R3 R0 K0 [1] +MULK R4 R0 K0 [1] +MODK R5 R0 K0 [1] +POWK R6 R0 K0 [1] RETURN R1 6 )"); } @@ -4413,20 +4422,20 @@ LOADN R3 0 LOADN R1 3 LOADN R2 1 FORNPREP R1 L1 -L0: MULK R5 R3 K1 -ADDK R4 R5 K0 +L0: MULK R5 R3 K1 [4] +ADDK R4 R5 K0 [1] LOADN R5 0 SETTABLE R5 R0 R4 -MULK R5 R3 K1 -ADDK R4 R5 K2 +MULK R5 R3 K1 [4] +ADDK R4 R5 K2 [2] LOADN R5 0 SETTABLE R5 R0 R4 -MULK R5 R3 K1 -ADDK R4 R5 K3 +MULK R5 R3 K1 [4] +ADDK R4 R5 K3 [3] LOADN R5 0 SETTABLE R5 R0 R4 -MULK R5 R3 K1 -ADDK R4 R5 K1 +MULK R5 R3 K1 [4] +ADDK R4 R5 K1 [4] LOADN R5 0 SETTABLE R5 R0 R4 FORNLOOP R1 L0 @@ -4464,9 +4473,9 @@ end )", 0, 2), R"( -GETIMPORT R2 1 -GETIMPORT R0 3 -GETIMPORT R1 5 +GETIMPORT R2 1 [x] +GETIMPORT R0 3 [y] +GETIMPORT R1 5 [z] FORNPREP R0 L1 L0: FORNLOOP R0 L0 L1: RETURN R0 0 @@ -4496,7 +4505,7 @@ end R"( LOADN R2 1 LOADN R0 2 -LOADK R1 K0 +LOADK R1 K0 [0.10000000000000001] FORNPREP R0 L1 L0: FORNLOOP R0 L0 L1: RETURN R0 0 @@ -4509,8 +4518,8 @@ end )", 0, 2), R"( -LOADK R2 K0 -LOADK R0 K1 +LOADK R2 K0 [4294967295] +LOADK R0 K1 [4294967296] LOADN R1 1 FORNPREP R0 L1 L0: FORNLOOP R0 L0 @@ -4535,17 +4544,17 @@ end )", 0, 2), R"( -GETIMPORT R0 2 +GETIMPORT R0 2 [math.random] CALL R0 0 1 -LOADK R1 K3 +LOADK R1 K3 [0.5] JUMPIFLT R0 R1 L0 -GETIMPORT R0 2 +GETIMPORT R0 2 [math.random] CALL R0 0 1 -LOADK R1 K3 +LOADK R1 K3 [0.5] JUMPIFLT R0 R1 L0 -GETIMPORT R0 2 +GETIMPORT R0 2 [math.random] CALL R0 0 1 -LOADK R1 K3 +LOADK R1 K3 [0.5] JUMPIFLT R0 R1 L0 L0: RETURN R0 0 )"); @@ -4561,25 +4570,25 @@ end )", 0, 2), R"( -GETIMPORT R0 2 +GETIMPORT R0 2 [math.random] CALL R0 0 1 -LOADK R1 K3 +LOADK R1 K3 [0.5] JUMPIFLT R0 R1 L0 -GETIMPORT R0 5 +GETIMPORT R0 5 [print] LOADN R1 1 CALL R0 1 0 -L0: GETIMPORT R0 2 +L0: GETIMPORT R0 2 [math.random] CALL R0 0 1 -LOADK R1 K3 +LOADK R1 K3 [0.5] JUMPIFLT R0 R1 L1 -GETIMPORT R0 5 +GETIMPORT R0 5 [print] LOADN R1 2 CALL R0 1 0 -L1: GETIMPORT R0 2 +L1: GETIMPORT R0 2 [math.random] CALL R0 0 1 -LOADK R1 K3 +LOADK R1 K3 [0.5] JUMPIFLT R0 R1 L2 -GETIMPORT R0 5 +GETIMPORT R0 5 [print] LOADN R1 3 CALL R0 1 0 L2: RETURN R0 0 @@ -4598,20 +4607,20 @@ end )", 1, 2), R"( -GETIMPORT R0 1 +GETIMPORT R0 1 [global] LOADN R1 1 CALL R0 1 1 -GETIMPORT R1 3 +GETIMPORT R1 3 [print] NEWCLOSURE R2 P0 CAPTURE REF R0 CALL R1 1 0 -GETIMPORT R1 6 +GETIMPORT R1 6 [math.random] CALL R1 0 1 -LOADK R2 K7 +LOADK R2 K7 [0.5] JUMPIFNOTLT R1 R2 L0 CLOSEUPVALS R0 RETURN R0 0 -L0: ADDK R0 R0 K8 +L0: ADDK R0 R0 K8 [1] CLOSEUPVALS R0 RETURN R0 0 )"); @@ -4797,10 +4806,10 @@ LOADN R2 1 FORNPREP R1 L3 L0: FASTCALL1 24 R3 L1 MOVE R6 R3 -GETIMPORT R5 2 +GETIMPORT R5 2 [math.sin] CALL R5 1 -1 L1: FASTCALL 2 L2 -GETIMPORT R4 4 +GETIMPORT R4 4 [math.abs] CALL R4 -1 1 L2: SETTABLE R4 R0 R3 FORNLOOP R1 L0 @@ -4825,7 +4834,7 @@ LOADN R1 1 FORNPREP R0 L1 L0: MOVE R3 R2 LOADN R3 3 -GETIMPORT R4 1 +GETIMPORT R4 1 [print] MOVE R5 R3 CALL R4 1 0 FORNLOOP R0 L0 @@ -4850,44 +4859,44 @@ end )", 0, 2), R"( -FASTCALL2K 39 R1 K0 L0 +FASTCALL2K 39 R1 K0 L0 [0] MOVE R4 R1 -LOADK R5 K0 -GETIMPORT R3 3 +LOADK R5 K0 [0] +GETIMPORT R3 3 [bit32.rshift] CALL R3 2 1 -L0: FASTCALL2K 29 R3 K4 L1 -LOADK R4 K4 -GETIMPORT R2 6 +L0: FASTCALL2K 29 R3 K4 L1 [255] +LOADK R4 K4 [255] +GETIMPORT R2 6 [bit32.band] CALL R2 2 1 L1: SETTABLEN R2 R0 1 -FASTCALL2K 39 R1 K7 L2 +FASTCALL2K 39 R1 K7 L2 [8] MOVE R4 R1 -LOADK R5 K7 -GETIMPORT R3 3 +LOADK R5 K7 [8] +GETIMPORT R3 3 [bit32.rshift] CALL R3 2 1 -L2: FASTCALL2K 29 R3 K4 L3 -LOADK R4 K4 -GETIMPORT R2 6 +L2: FASTCALL2K 29 R3 K4 L3 [255] +LOADK R4 K4 [255] +GETIMPORT R2 6 [bit32.band] CALL R2 2 1 L3: SETTABLEN R2 R0 2 -FASTCALL2K 39 R1 K8 L4 +FASTCALL2K 39 R1 K8 L4 [16] MOVE R4 R1 -LOADK R5 K8 -GETIMPORT R3 3 +LOADK R5 K8 [16] +GETIMPORT R3 3 [bit32.rshift] CALL R3 2 1 -L4: FASTCALL2K 29 R3 K4 L5 -LOADK R4 K4 -GETIMPORT R2 6 +L4: FASTCALL2K 29 R3 K4 L5 [255] +LOADK R4 K4 [255] +GETIMPORT R2 6 [bit32.band] CALL R2 2 1 L5: SETTABLEN R2 R0 3 -FASTCALL2K 39 R1 K9 L6 +FASTCALL2K 39 R1 K9 L6 [24] MOVE R4 R1 -LOADK R5 K9 -GETIMPORT R3 3 +LOADK R5 K9 [24] +GETIMPORT R3 3 [bit32.rshift] CALL R3 2 1 -L6: FASTCALL2K 29 R3 K4 L7 -LOADK R4 K4 -GETIMPORT R2 6 +L6: FASTCALL2K 29 R3 K4 L7 [255] +LOADK R4 K4 [255] +GETIMPORT R2 6 [bit32.band] CALL R2 2 1 L7: SETTABLEN R2 R0 4 RETURN R0 0 @@ -4909,13 +4918,13 @@ LOADN R4 0 LOADN R2 3 LOADN R3 1 FORNPREP R2 L1 -L0: ADDK R5 R4 K0 -GETGLOBAL R7 K1 -GETTABLEKS R6 R7 K2 -GETGLOBAL R8 K1 -GETTABLEKS R7 R8 K3 +L0: ADDK R5 R4 K0 [1] +GETGLOBAL R7 K1 ['bit32'] +GETTABLEKS R6 R7 K2 ['band'] +GETGLOBAL R8 K1 ['bit32'] +GETTABLEKS R7 R8 K3 ['rshift'] MOVE R8 R1 -MULK R9 R4 K4 +MULK R9 R4 K4 [8] CALL R7 2 1 LOADN R8 255 CALL R6 2 1 @@ -4938,11 +4947,11 @@ LOADN R4 0 LOADN R2 3 LOADN R3 1 FORNPREP R2 L3 -L0: ADDK R5 R4 K0 -MULK R9 R4 K1 +L0: ADDK R5 R4 K0 [1] +MULK R9 R4 K1 [8] FASTCALL2 39 R1 R9 L1 MOVE R8 R1 -GETIMPORT R7 4 +GETIMPORT R7 4 [bit32.rshift] CALL R7 2 1 L1: LOADN R8 255 LOADN R9 255 @@ -4950,7 +4959,7 @@ LOADN R10 255 LOADN R11 255 LOADN R12 255 FASTCALL 29 L2 -GETIMPORT R6 6 +GETIMPORT R6 6 [bit32.band] CALL R6 6 1 L2: SETTABLE R6 R0 R5 FORNLOOP R2 L0 @@ -4971,7 +4980,7 @@ return x )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] LOADN R1 42 RETURN R1 1 )"); @@ -4987,7 +4996,7 @@ return x )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] LOADN R1 42 RETURN R1 1 )"); @@ -5007,8 +5016,8 @@ return x )", 1, 2), R"( -DUPCLOSURE R0 K0 -GETIMPORT R2 3 +DUPCLOSURE R0 K0 ['foo'] +GETIMPORT R2 3 [math.random] CALL R2 0 1 MOVE R1 R2 RETURN R1 1 @@ -5030,8 +5039,8 @@ return x )", 1, 2), R"( -DUPCLOSURE R0 K0 -GETIMPORT R2 3 +DUPCLOSURE R0 K0 ['foo'] +GETIMPORT R2 3 [math.random] CALL R2 0 1 LOADN R1 5 RETURN R1 1 @@ -5052,7 +5061,7 @@ return x )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] MOVE R1 R0 CALL R1 0 1 RETURN R1 1 @@ -5070,10 +5079,10 @@ return x )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] MOVE R1 R0 CALL R1 0 1 -GETIMPORT R2 2 +GETIMPORT R2 2 [getfenv] CALL R2 0 0 RETURN R1 1 )"); @@ -5095,7 +5104,7 @@ return x )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] NEWTABLE R2 0 0 LOADN R3 1 SETTABLEN R3 R2 1 @@ -5121,7 +5130,7 @@ return x )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] NEWTABLE R2 0 0 LOADN R3 1 SETTABLEN R3 R2 1 @@ -5147,7 +5156,7 @@ return x )", 2, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] LOADN R2 1 NEWCLOSURE R1 P1 CAPTURE VAL R2 @@ -5173,9 +5182,9 @@ return x )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] LOADN R2 42 -ORK R2 R2 K1 +ORK R2 R2 K1 [5] MOVE R1 R2 RETURN R1 1 )"); @@ -5192,7 +5201,7 @@ return y )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] GETVARARGS R1 1 MOVE R2 R1 RETURN R2 1 @@ -5211,7 +5220,7 @@ return y )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] GETVARARGS R1 1 LOADNIL R1 MOVE R3 R1 @@ -5232,7 +5241,7 @@ return x )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] MOVE R1 R0 LOADN R2 42 CALL R1 1 1 @@ -5276,7 +5285,7 @@ return x 1, 2), R"( GETVARARGS R0 1 -DUPCLOSURE R1 K0 +DUPCLOSURE R1 K0 ['foo'] CAPTURE VAL R0 LOADN R3 42 ADD R2 R3 R0 @@ -5298,7 +5307,7 @@ end )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] CAPTURE UPVAL U0 LOADN R2 42 GETUPVAL R3 0 @@ -5321,7 +5330,7 @@ return y )", 2, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] GETVARARGS R1 1 NEWCLOSURE R2 P1 CAPTURE VAL R1 @@ -5339,7 +5348,7 @@ return y )", 2, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] LOADN R2 42 NEWCLOSURE R1 P1 CAPTURE VAL R2 @@ -5358,7 +5367,7 @@ return y )", 2, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] LOADNIL R1 LOADN R1 42 MOVE R3 R1 @@ -5379,9 +5388,9 @@ return y )", 2, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] LOADNIL R2 -ORK R2 R2 K1 +ORK R2 R2 K1 [42] NEWCLOSURE R1 P1 CAPTURE REF R2 CLOSEUPVALS R2 @@ -5401,11 +5410,11 @@ return y )", 2, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] GETVARARGS R1 1 MOVE R3 R1 -ORK R3 R3 K1 -GETIMPORT R4 3 +ORK R3 R3 K1 [42] +GETIMPORT R4 3 [print] NEWCLOSURE R5 P1 CAPTURE REF R3 CALL R4 1 0 @@ -5431,7 +5440,7 @@ return y, x )", 2, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] GETVARARGS R1 1 JUMPIF R1 L0 LOADNIL R3 @@ -5460,7 +5469,7 @@ return a, b )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] LOADNIL R1 LOADNIL R2 RETURN R1 2 @@ -5477,7 +5486,7 @@ return a, b )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] LOADNIL R1 LOADNIL R2 RETURN R1 2 @@ -5493,7 +5502,7 @@ return foo() )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] MOVE R1 R0 CALL R1 0 -1 RETURN R1 -1 @@ -5515,7 +5524,7 @@ return x )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] LOADNIL R1 RETURN R1 1 )"); @@ -5531,10 +5540,10 @@ return x )", 1, 2), R"( -DUPCLOSURE R0 K0 -LOADK R3 K1 +DUPCLOSURE R0 K0 ['foo'] +LOADK R3 K1 [1.5] FASTCALL1 20 R3 L0 -GETIMPORT R2 4 +GETIMPORT R2 4 [math.modf] CALL R2 1 2 L0: ADD R1 R2 R3 RETURN R1 1 @@ -5551,7 +5560,7 @@ return x )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] GETVARARGS R2 2 ADD R1 R2 R3 RETURN R1 1 @@ -5568,8 +5577,8 @@ return x )", 1, 2), R"( -DUPCLOSURE R0 K0 -GETIMPORT R2 2 +DUPCLOSURE R0 K0 ['foo'] +GETIMPORT R2 2 [print] CALL R2 0 1 LOADN R1 42 RETURN R1 1 @@ -5587,7 +5596,7 @@ return x )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] LOADNIL R2 LOADN R2 42 MOVE R1 R2 @@ -5612,9 +5621,9 @@ return a, b, c, d )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] GETVARARGS R1 2 -ADDK R3 R1 K1 +ADDK R3 R1 K1 [1] LOADN R5 1 ADD R4 R5 R1 LOADN R5 3 @@ -5643,9 +5652,9 @@ return (baz()) )", 3, 2), R"( -DUPCLOSURE R0 K0 -DUPCLOSURE R1 K1 -DUPCLOSURE R2 K2 +DUPCLOSURE R0 K0 ['foo'] +DUPCLOSURE R1 K1 ['bar'] +DUPCLOSURE R2 K2 ['baz'] LOADN R4 43 LOADN R5 41 MUL R3 R4 R5 @@ -5671,7 +5680,7 @@ return (foo()) )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] MOVE R1 R0 CALL R1 0 1 RETURN R1 1 @@ -5687,7 +5696,7 @@ return (foo()) )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] MOVE R1 R0 CALL R1 0 1 RETURN R1 1 @@ -5711,9 +5720,9 @@ return (baz()) )", 3, 2), R"( -DUPCLOSURE R0 K0 -DUPCLOSURE R1 K1 -DUPCLOSURE R2 K2 +DUPCLOSURE R0 K0 ['foo'] +DUPCLOSURE R1 K1 ['bar'] +DUPCLOSURE R2 K2 ['baz'] MOVE R4 R0 LOADN R5 42 LOADN R6 1 @@ -5772,7 +5781,7 @@ foo(foo(foo,foo(foo,foo))[foo]) )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] MOVE R2 R0 MOVE R3 R0 MOVE R4 R0 @@ -5797,17 +5806,17 @@ set({}) )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['set'] NEWTABLE R2 0 0 -FASTCALL2K 49 R2 K1 L0 -LOADK R3 K1 -GETIMPORT R1 3 +FASTCALL2K 49 R2 K1 L0 [false] +LOADK R3 K1 [false] +GETIMPORT R1 3 [rawset] CALL R1 2 0 L0: NEWTABLE R1 0 0 NEWTABLE R3 0 0 FASTCALL2 49 R3 R1 L1 MOVE R4 R1 -GETIMPORT R2 3 +GETIMPORT R2 3 [rawset] CALL R2 2 0 L1: RETURN R0 0 )"); @@ -5838,7 +5847,7 @@ end )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 [] L0: LOADNIL R4 LOADNIL R5 CALL R4 1 1 @@ -5847,7 +5856,7 @@ GETTABLE R3 R4 R5 JUMPIFNOT R3 L1 JUMPBACK L0 L1: LOADNIL R2 -GETTABLEKS R1 R2 K1 +GETTABLEKS R1 R2 K1 [''] JUMPIFNOT R1 L2 RETURN R0 0 L2: JUMPIFNOT R1 L3 @@ -5889,7 +5898,7 @@ return y )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] GETVARARGS R1 1 MOVE R3 R1 LOADN R3 42 @@ -5912,13 +5921,13 @@ return y )", 2, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] GETVARARGS R1 1 NEWCLOSURE R2 P1 CAPTURE REF R1 -SETGLOBAL R2 K1 +SETGLOBAL R2 K1 ['mutator'] MOVE R3 R1 -GETGLOBAL R4 K1 +GETGLOBAL R4 K1 ['mutator'] CALL R4 0 0 MOVE R2 R3 CLOSEUPVALS R1 @@ -5938,7 +5947,7 @@ return foo(42) )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] MOVE R1 R0 LOADN R2 42 CALL R1 1 -1 @@ -5955,7 +5964,7 @@ return foo(42) )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] LOADN R1 42 RETURN R1 1 )"); @@ -5974,8 +5983,8 @@ return bar(42) )", 2, 2), R"( -DUPCLOSURE R0 K0 -DUPCLOSURE R1 K1 +DUPCLOSURE R0 K0 ['foo'] +DUPCLOSURE R1 K1 ['bar'] LOADN R2 42 RETURN R2 1 )"); @@ -5990,7 +5999,7 @@ return foo(42) )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] CAPTURE VAL R0 MOVE R1 R0 LOADN R2 42 @@ -6008,7 +6017,7 @@ return foo(42) )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] MOVE R1 R0 LOADN R2 42 CALL R1 1 -1 @@ -6046,7 +6055,7 @@ return x, y + 1 R"( GETVARARGS R0 2 MOVE R2 R0 -ADDK R3 R1 K0 +ADDK R3 R1 K0 [1] RETURN R2 2 )"); @@ -6093,7 +6102,7 @@ return foo(42) )", 1, 1), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] MOVE R1 R0 LOADN R2 42 CALL R1 1 -1 @@ -6111,7 +6120,7 @@ return foo(42) )", 1, 1), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] LOADN R1 42 RETURN R1 1 )"); @@ -6126,7 +6135,7 @@ return foo(42) )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] LOADN R1 42 RETURN R1 1 )"); @@ -6142,7 +6151,7 @@ return foo(42) )", 1, 2), R"( -DUPCLOSURE R0 K0 +DUPCLOSURE R0 K0 ['foo'] MOVE R1 R0 LOADN R2 42 CALL R1 1 -1 @@ -6235,7 +6244,7 @@ LOADN R22 0 LOADN R23 3 LOADN R24 0 LOADN R25 0 -LOADK R26 K0 +LOADK R26 K0 [4294967291] LOADN R27 5 LOADN R28 1 LOADN R29 1 @@ -6248,19 +6257,19 @@ LOADN R35 200 LOADN R36 106 LOADN R37 200 LOADN R38 50 -LOADK R39 K1 +LOADK R39 K1 ['number'] LOADN R40 97 LOADN R41 98 LOADN R42 3 -LOADK R43 K2 +LOADK R43 K2 ['boolean'] LOADN R44 0 LOADN R45 1 LOADN R46 8 LOADN R47 1 LOADN R48 101 LOADN R49 2 -LOADK R50 K3 -LOADK R51 K4 +LOADK R50 K3 ['nil'] +LOADK R51 K4 ['string'] RETURN R0 52 )"); } @@ -6283,53 +6292,53 @@ return 0, 2), R"( FASTCALL 2 L0 -GETIMPORT R0 2 +GETIMPORT R0 2 [math.abs] CALL R0 0 1 L0: LOADN R2 1 -FASTCALL2K 18 R2 K3 L1 -LOADK R3 K3 -GETIMPORT R1 5 +FASTCALL2K 18 R2 K3 L1 [true] +LOADK R3 K3 [true] +GETIMPORT R1 5 [math.max] CALL R1 2 1 -L1: LOADK R3 K6 -FASTCALL2K 41 R3 K7 L2 -LOADK R4 K7 -GETIMPORT R2 10 +L1: LOADK R3 K6 ['abc'] +FASTCALL2K 41 R3 K7 L2 [42] +LOADK R4 K7 [42] +GETIMPORT R2 10 [string.byte] CALL R2 2 1 L2: LOADN R4 10 -FASTCALL2K 39 R4 K7 L3 -LOADK R5 K7 -GETIMPORT R3 13 +FASTCALL2K 39 R4 K7 L3 [42] +LOADK R5 K7 [42] +GETIMPORT R3 13 [bit32.rshift] CALL R3 2 1 L3: LOADN R5 1 LOADN R6 2 -LOADK R7 K14 +LOADK R7 K14 ['3'] FASTCALL 34 L4 -GETIMPORT R4 16 +GETIMPORT R4 16 [bit32.extract] CALL R4 3 1 L4: LOADN R6 1 -FASTCALL2K 31 R6 K3 L5 -LOADK R7 K3 -GETIMPORT R5 18 +FASTCALL2K 31 R6 K3 L5 [true] +LOADK R7 K3 [true] +GETIMPORT R5 18 [bit32.bor] CALL R5 2 1 L5: LOADN R7 1 -FASTCALL2K 29 R7 K3 L6 -LOADK R8 K3 -GETIMPORT R6 20 +FASTCALL2K 29 R7 K3 L6 [true] +LOADK R8 K3 [true] +GETIMPORT R6 20 [bit32.band] CALL R6 2 1 L6: LOADN R8 1 -FASTCALL2K 32 R8 K3 L7 -LOADK R9 K3 -GETIMPORT R7 22 +FASTCALL2K 32 R8 K3 L7 [true] +LOADK R9 K3 [true] +GETIMPORT R7 22 [bit32.bxor] CALL R7 2 1 L7: LOADN R9 1 -FASTCALL2K 33 R9 K3 L8 -LOADK R10 K3 -GETIMPORT R8 24 +FASTCALL2K 33 R9 K3 L8 [true] +LOADK R10 K3 [true] +GETIMPORT R8 24 [bit32.btest] CALL R8 2 1 L8: LOADN R10 1 -FASTCALL2K 19 R10 K3 L9 -LOADK R11 K3 -GETIMPORT R9 26 +FASTCALL2K 19 R10 K3 L9 [true] +LOADK R11 K3 [true] +GETIMPORT R9 26 [math.min] CALL R9 2 -1 L9: RETURN R0 -1 )"); @@ -6414,20 +6423,20 @@ end )", 0, 2), R"( -GETTABLEKS R2 R0 K0 -FASTCALL2K 29 R2 K1 L0 -LOADK R3 K1 -GETIMPORT R1 4 +GETTABLEKS R2 R0 K0 ['pendingLanes'] +FASTCALL2K 29 R2 K1 L0 [3221225471] +LOADK R3 K1 [3221225471] +GETIMPORT R1 4 [bit32.band] CALL R1 2 1 -L0: JUMPXEQKN R1 K5 L1 +L0: JUMPXEQKN R1 K5 L1 [0] RETURN R1 1 -L1: FASTCALL2K 29 R1 K6 L2 +L1: FASTCALL2K 29 R1 K6 L2 [1073741824] MOVE R3 R1 -LOADK R4 K6 -GETIMPORT R2 4 +LOADK R4 K6 [1073741824] +GETIMPORT R2 4 [bit32.band] CALL R2 2 1 -L2: JUMPXEQKN R2 K5 L3 -LOADK R2 K6 +L2: JUMPXEQKN R2 K5 L3 [0] +LOADK R2 K6 [1073741824] RETURN R2 1 L3: LOADN R2 0 RETURN R2 1 @@ -6482,9 +6491,9 @@ end )"), R"( MOVE R2 R0 -ADDK R2 R2 K0 +ADDK R2 R2 K0 [0] MOVE R3 R1 -ADDK R1 R1 K0 +ADDK R1 R1 K0 [0] ADD R4 R2 R3 RETURN R4 1 )"); @@ -6543,11 +6552,11 @@ TEST_CASE("MultipleAssignments") R"( LOADNIL R0 LOADNIL R1 -GETIMPORT R2 1 +GETIMPORT R2 1 [f] LOADN R3 1 CALL R2 1 1 MOVE R0 R2 -GETIMPORT R2 1 +GETIMPORT R2 1 [f] LOADN R3 2 CALL R2 1 1 MOVE R1 R2 @@ -6597,8 +6606,8 @@ RETURN R0 0 GETVARARGS R0 4 MOVE R0 R1 MOVE R1 R2 -ADDK R2 R2 K0 -SUBK R3 R3 K0 +ADDK R2 R2 K0 [1] +SUBK R3 R3 K0 [1] RETURN R0 0 )"); @@ -6670,7 +6679,7 @@ RETURN R0 0 R"( GETVARARGS R0 4 LOADN R0 1 -GETIMPORT R4 1 +GETIMPORT R4 1 [foo] CALL R4 0 3 MOVE R1 R4 MOVE R2 R5 @@ -6686,7 +6695,7 @@ RETURN R0 0 R"( GETVARARGS R0 4 LOADN R4 1 -GETIMPORT R6 1 +GETIMPORT R6 1 [foo] CALL R6 0 3 SETTABLE R6 R1 R0 SETTABLE R7 R2 R3 @@ -6704,7 +6713,7 @@ RETURN R0 0 R"( GETVARARGS R0 4 LOADN R0 1 -GETIMPORT R4 1 +GETIMPORT R4 1 [foo] LOADNIL R5 LOADNIL R6 MOVE R1 R4 @@ -6720,7 +6729,7 @@ RETURN R0 0 )"), R"( GETVARARGS R0 2 -ADDK R2 R1 K0 +ADDK R2 R1 K0 [1] SETTABLEN R1 R0 1 SETTABLEN R2 R0 2 RETURN R0 0 @@ -6763,7 +6772,7 @@ RETURN R0 0 )"), R"( GETVARARGS R0 2 -ADDK R2 R1 K0 +ADDK R2 R1 K0 [1] SETTABLEN R1 R0 1 MOVE R1 R2 RETURN R0 0 @@ -6781,11 +6790,11 @@ return bit32.extract(v, 1, 3) )"), R"( GETVARARGS R0 1 -FASTCALL2K 59 R0 K0 L0 +FASTCALL2K 59 R0 K0 L0 [65] MOVE R2 R0 -LOADK R3 K1 -LOADK R4 K2 -GETIMPORT R1 5 +LOADK R3 K1 [1] +LOADK R4 K2 [3] +GETIMPORT R1 5 [bit32.extract] CALL R1 3 -1 L0: RETURN R1 -1 )"); diff --git a/tests/Conformance.test.cpp b/tests/Conformance.test.cpp index c32f287..d7340ce 100644 --- a/tests/Conformance.test.cpp +++ b/tests/Conformance.test.cpp @@ -297,6 +297,8 @@ TEST_CASE("Clear") TEST_CASE("Strings") { + ScopedFastFlag luauStringFormatAnyFix{"LuauStringFormatAnyFix", true}; + runConformance("strings.lua"); } diff --git a/tests/Parser.test.cpp b/tests/Parser.test.cpp index 18e91e1..fae8bc4 100644 --- a/tests/Parser.test.cpp +++ b/tests/Parser.test.cpp @@ -2177,8 +2177,6 @@ type C = Packed<(number, X...)> TEST_CASE_FIXTURE(Fixture, "invalid_type_forms") { - ScopedFastFlag luauFixNamedFunctionParse{"LuauFixNamedFunctionParse", true}; - matchParseError("type A = (b: number)", "Expected '->' when parsing function type, got "); matchParseError("type P = () -> T... type B = P<(x: number, y: string)>", "Expected '->' when parsing function type, got '>'"); matchParseError("type F = (T...) -> ()", "Expected '->' when parsing function type, got '>'"); diff --git a/tests/ToString.test.cpp b/tests/ToString.test.cpp index 7ccda8d..7d27437 100644 --- a/tests/ToString.test.cpp +++ b/tests/ToString.test.cpp @@ -83,7 +83,6 @@ TEST_CASE_FIXTURE(Fixture, "table_respects_use_line_break") ToStringOptions opts; opts.useLineBreaks = true; - opts.DEPRECATED_indent = true; //clang-format off CHECK_EQ("{|\n" @@ -97,7 +96,6 @@ TEST_CASE_FIXTURE(Fixture, "table_respects_use_line_break") TEST_CASE_FIXTURE(Fixture, "nil_or_nil_is_nil_not_question_mark") { - ScopedFastFlag sff("LuauSerializeNilUnionAsNil", true); CheckResult result = check(R"( type nil_ty = nil | nil local a : nil_ty = nil @@ -109,7 +107,6 @@ TEST_CASE_FIXTURE(Fixture, "nil_or_nil_is_nil_not_question_mark") TEST_CASE_FIXTURE(Fixture, "long_disjunct_of_nil_is_nil_not_question_mark") { - ScopedFastFlag sff("LuauSerializeNilUnionAsNil", true); CheckResult result = check(R"( type nil_ty = nil | nil | nil | nil | nil local a : nil_ty = nil diff --git a/tests/TypeReduction.test.cpp b/tests/TypeReduction.test.cpp index c629b3e..f2d7b02 100644 --- a/tests/TypeReduction.test.cpp +++ b/tests/TypeReduction.test.cpp @@ -88,6 +88,96 @@ TEST_CASE_FIXTURE(ReductionFixture, "cartesian_product_is_zero") CHECK(ty); } +TEST_CASE_FIXTURE(ReductionFixture, "stress_test_recursion_limits") +{ + TypeId ty = arena.addType(IntersectionType{{builtinTypes->numberType, builtinTypes->stringType}}); + for (size_t i = 0; i < 20'000; ++i) + { + TableType table; + table.state = TableState::Sealed; + table.props["x"] = {ty}; + ty = arena.addType(IntersectionType{{arena.addType(table), arena.addType(table)}}); + } + + CHECK(!reduction.reduce(ty)); +} + +TEST_CASE_FIXTURE(ReductionFixture, "caching") +{ + SUBCASE("free_tables") + { + TypeId ty1 = arena.addType(TableType{}); + getMutable(ty1)->state = TableState::Free; + getMutable(ty1)->props["x"] = {builtinTypes->stringType}; + + TypeId ty2 = arena.addType(TableType{}); + getMutable(ty2)->state = TableState::Sealed; + + TypeId intersectionTy = arena.addType(IntersectionType{{ty1, ty2}}); + + ToStringOptions opts; + opts.exhaustive = true; + + CHECK("{- x: string -} & {| |}" == toString(reductionof(intersectionTy))); + + getMutable(ty1)->state = TableState::Sealed; + CHECK("{| x: string |}" == toString(reductionof(intersectionTy))); + } + + SUBCASE("unsealed_tables") + { + TypeId ty1 = arena.addType(TableType{}); + getMutable(ty1)->state = TableState::Unsealed; + getMutable(ty1)->props["x"] = {builtinTypes->stringType}; + + TypeId ty2 = arena.addType(TableType{}); + getMutable(ty2)->state = TableState::Sealed; + + TypeId intersectionTy = arena.addType(IntersectionType{{ty1, ty2}}); + + ToStringOptions opts; + opts.exhaustive = true; + + CHECK("{| x: string |}" == toString(reductionof(intersectionTy))); + + getMutable(ty1)->state = TableState::Sealed; + CHECK("{| x: string |}" == toString(reductionof(intersectionTy))); + } + + SUBCASE("free_types") + { + TypeId ty1 = arena.freshType(nullptr); + TypeId ty2 = arena.addType(TableType{}); + getMutable(ty2)->state = TableState::Sealed; + + TypeId intersectionTy = arena.addType(IntersectionType{{ty1, ty2}}); + + ToStringOptions opts; + opts.exhaustive = true; + + CHECK("a & {| |}" == toString(reductionof(intersectionTy))); + + *asMutable(ty1) = BoundType{ty2}; + CHECK("{| |}" == toString(reductionof(intersectionTy))); + } + + SUBCASE("we_can_see_that_the_cache_works_if_we_mutate_a_normally_not_mutated_type") + { + TypeId ty1 = arena.addType(BoundType{builtinTypes->stringType}); + TypeId ty2 = builtinTypes->numberType; + + TypeId intersectionTy = arena.addType(IntersectionType{{ty1, ty2}}); + + ToStringOptions opts; + opts.exhaustive = true; + + CHECK("never" == toString(reductionof(intersectionTy))); // Bound & number ~ never + + *asMutable(ty1) = BoundType{ty2}; + CHECK("never" == toString(reductionof(intersectionTy))); // Bound & number ~ number, but the cache is `never`. + } +} // caching + TEST_CASE_FIXTURE(ReductionFixture, "intersections_without_negations") { SUBCASE("string_and_string") @@ -359,6 +449,34 @@ TEST_CASE_FIXTURE(ReductionFixture, "intersections_without_negations") TypeId ty = reductionof("{ p: string } & { p: string, [string]: number }"); CHECK("{| [string]: number, p: string |}" == toString(ty)); } + + SUBCASE("fresh_type_and_string") + { + TypeId freshTy = arena.freshType(nullptr); + TypeId ty = reductionof(arena.addType(IntersectionType{{freshTy, builtinTypes->stringType}})); + CHECK("a & string" == toString(ty)); + } + + SUBCASE("string_and_fresh_type") + { + TypeId freshTy = arena.freshType(nullptr); + TypeId ty = reductionof(arena.addType(IntersectionType{{builtinTypes->stringType, freshTy}})); + CHECK("a & string" == toString(ty)); + } + + SUBCASE("generic_and_string") + { + TypeId genericTy = arena.addType(GenericType{"G"}); + TypeId ty = reductionof(arena.addType(IntersectionType{{genericTy, builtinTypes->stringType}})); + CHECK("G & string" == toString(ty)); + } + + SUBCASE("string_and_generic") + { + TypeId genericTy = arena.addType(GenericType{"G"}); + TypeId ty = reductionof(arena.addType(IntersectionType{{builtinTypes->stringType, genericTy}})); + CHECK("G & string" == toString(ty)); + } } // intersections_without_negations TEST_CASE_FIXTURE(ReductionFixture, "intersections_with_negations") @@ -1232,18 +1350,4 @@ TEST_CASE_FIXTURE(ReductionFixture, "cycles") } } -TEST_CASE_FIXTURE(ReductionFixture, "stress_test_recursion_limits") -{ - TypeId ty = arena.addType(IntersectionType{{builtinTypes->numberType, builtinTypes->stringType}}); - for (size_t i = 0; i < 20'000; ++i) - { - TableType table; - table.state = TableState::Sealed; - table.props["x"] = {ty}; - ty = arena.addType(IntersectionType{{arena.addType(table), arena.addType(table)}}); - } - - CHECK(!reduction.reduce(ty)); -} - TEST_SUITE_END(); diff --git a/tests/conformance/strings.lua b/tests/conformance/strings.lua index 61bac72..59d3421 100644 --- a/tests/conformance/strings.lua +++ b/tests/conformance/strings.lua @@ -164,6 +164,16 @@ local ud = newproxy(true) getmetatable(ud).__tostring = function() return "good" end assert(string.format("%*", ud) == "good") +assert(string.format(string.rep("%*", 100), table.unpack(table.create(100, 1))) == string.rep("1", 100)) + +do + local a = "1234567890" + a = string.format("%*%*%*%*%*", a, a, a, a, a) + a = string.format("%*%*%*%*%*", a, a, a, a, a) + a = string.format("%*%*%*%*%*", a, a, a, a, a) + assert(a == string.rep("1234567890", 125)) +end + assert(pcall(function() string.format("%#*", "bad form") end) == false) diff --git a/tests/conformance/tables.lua b/tests/conformance/tables.lua index 7ae80cc..4b47ed2 100644 --- a/tests/conformance/tables.lua +++ b/tests/conformance/tables.lua @@ -686,4 +686,18 @@ do assert(pcall(table.clear, table.freeze({})) == false) end +-- check that namecall lookup doesn't give up on entries missing from cached slot position +do + for i = 1,10 do + local t = setmetatable({}, { __index = { foo = 1 }}) + + assert(t.foo == 1) + + t[-i] = 2 + t.foo = function(t, i) return -i end + + assert(t:foo(i) == -i) + end +end + return"OK" diff --git a/tools/faillist.txt b/tools/faillist.txt index f336bb2..3fcd420 100644 --- a/tools/faillist.txt +++ b/tools/faillist.txt @@ -2,8 +2,6 @@ AnnotationTests.corecursive_types_error_on_tight_loop AnnotationTests.duplicate_type_param_name AnnotationTests.for_loop_counter_annotation_is_checked AnnotationTests.generic_aliases_are_cloned_properly -AnnotationTests.instantiation_clone_has_to_follow -AnnotationTests.luau_print_is_not_special_without_the_flag AnnotationTests.occurs_check_on_cyclic_intersection_type AnnotationTests.occurs_check_on_cyclic_union_type AnnotationTests.too_many_type_params @@ -87,6 +85,8 @@ DefinitionTests.class_definition_overload_metamethods DefinitionTests.class_definition_string_props DefinitionTests.declaring_generic_functions DefinitionTests.definition_file_classes +DefinitionTests.definitions_symbols_are_generated_for_recursively_referenced_types +DefinitionTests.single_class_type_identity_in_global_types FrontendTest.environments FrontendTest.it_should_be_safe_to_stringify_errors_when_full_type_graph_is_discarded FrontendTest.nocheck_cycle_used_by_checked @@ -140,6 +140,7 @@ NonstrictModeTests.parameters_having_type_any_are_optional NonstrictModeTests.table_dot_insert_and_recursive_calls NonstrictModeTests.table_props_are_any Normalize.cyclic_table_normalizes_sensibly +Normalize.negations_of_classes ParseErrorRecovery.generic_type_list_recovery ParseErrorRecovery.recovery_of_parenthesized_expressions ParserTests.parse_nesting_based_end_detection_failsafe_earlier @@ -165,8 +166,10 @@ RefinementTest.call_an_incompatible_function_after_using_typeguard RefinementTest.correctly_lookup_property_whose_base_was_previously_refined2 RefinementTest.discriminate_on_properties_of_disjoint_tables_where_that_property_is_true_or_false RefinementTest.discriminate_tag +RefinementTest.eliminate_subclasses_of_instance RefinementTest.else_with_no_explicit_expression_should_also_refine_the_tagged_union RefinementTest.falsiness_of_TruthyPredicate_narrows_into_nil +RefinementTest.narrow_from_subclasses_of_instance_or_string_or_vector3 RefinementTest.narrow_property_of_a_bounded_variable RefinementTest.nonoptional_type_can_narrow_to_nil_if_sense_is_true RefinementTest.refine_a_property_not_to_be_nil_through_an_intersection_table @@ -176,6 +179,7 @@ RefinementTest.type_guard_narrowed_into_nothingness RefinementTest.type_narrow_for_all_the_userdata RefinementTest.type_narrow_to_vector RefinementTest.typeguard_cast_free_table_to_vector +RefinementTest.typeguard_cast_instance_or_vector3_to_vector RefinementTest.typeguard_in_assert_position RefinementTest.typeguard_narrows_for_table RefinementTest.x_as_any_if_x_is_instance_elseif_x_is_table @@ -457,6 +461,10 @@ TypePackTests.type_pack_type_parameters TypePackTests.unify_variadic_tails_in_arguments TypePackTests.unify_variadic_tails_in_arguments_free TypePackTests.variadic_packs +TypeReductionTests.discriminable_unions +TypeReductionTests.intersections_with_negations +TypeReductionTests.negations +TypeReductionTests.unions_with_negations TypeSingletons.error_detailed_tagged_union_mismatch_bool TypeSingletons.error_detailed_tagged_union_mismatch_string TypeSingletons.function_call_with_singletons diff --git a/tools/heapgraph.py b/tools/heapgraph.py index b8dc207..17ce7a4 100644 --- a/tools/heapgraph.py +++ b/tools/heapgraph.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details # Given two heap snapshots (A & B), this tool performs reachability analysis on new objects allocated in B diff --git a/tools/heapstat.py b/tools/heapstat.py index 7337aa4..d9fd839 100644 --- a/tools/heapstat.py +++ b/tools/heapstat.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details # Given a heap snapshot, this tool gathers basic statistics about the allocated objects diff --git a/tools/lvmexecute_split.py b/tools/lvmexecute_split.py index f4a7896..16de45d 100644 --- a/tools/lvmexecute_split.py +++ b/tools/lvmexecute_split.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details # This code can be used to split lvmexecute.cpp VM switch into separate functions for use as native code generation fallbacks @@ -34,7 +34,7 @@ source = """// This file is part of the Luau programming language and is license function = "" signature = "" -includeInsts = ["LOP_NEWCLOSURE", "LOP_NAMECALL", "LOP_FORGPREP", "LOP_GETVARARGS", "LOP_DUPCLOSURE", "LOP_PREPVARARGS", "LOP_COVERAGE", "LOP_BREAK", "LOP_GETGLOBAL", "LOP_SETGLOBAL", "LOP_GETTABLEKS", "LOP_SETTABLEKS"] +includeInsts = ["LOP_NEWCLOSURE", "LOP_NAMECALL", "LOP_FORGPREP", "LOP_GETVARARGS", "LOP_DUPCLOSURE", "LOP_PREPVARARGS", "LOP_BREAK", "LOP_GETGLOBAL", "LOP_SETGLOBAL", "LOP_GETTABLEKS", "LOP_SETTABLEKS"] state = 0 diff --git a/tools/numprint.py b/tools/numprint.py index 47ad36d..4fb64e6 100644 --- a/tools/numprint.py +++ b/tools/numprint.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details # This code can be used to generate power tables for Schubfach algorithm (see lnumprint.cpp) diff --git a/tools/patchtests.py b/tools/patchtests.py index 56970c9..82d8364 100644 --- a/tools/patchtests.py +++ b/tools/patchtests.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details # This code can be used to patch Compiler.test.cpp following bytecode changes, based on error output diff --git a/tools/perfgraph.py b/tools/perfgraph.py index eb6b68c..94c57cc 100644 --- a/tools/perfgraph.py +++ b/tools/perfgraph.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details # Given a profile dump, this tool generates a flame graph based on the stacks listed in the profile diff --git a/tools/perfstat.py b/tools/perfstat.py index e5cfd11..1af2473 100644 --- a/tools/perfstat.py +++ b/tools/perfstat.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details # Given a profile dump, this tool displays top functions based on the stacks listed in the profile diff --git a/tools/stack-usage-reporter.py b/tools/stack-usage-reporter.py index 91e7488..9f11b65 100644 --- a/tools/stack-usage-reporter.py +++ b/tools/stack-usage-reporter.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details # The purpose of this script is to analyze disassembly generated by objdump or diff --git a/tools/test_dcr.py b/tools/test_dcr.py index 6d553b6..d30490b 100644 --- a/tools/test_dcr.py +++ b/tools/test_dcr.py @@ -1,3 +1,4 @@ +#!/usr/bin/python3 # This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details import argparse diff --git a/tools/tracegraph.py b/tools/tracegraph.py index a46423e..1cdd32d 100644 --- a/tools/tracegraph.py +++ b/tools/tracegraph.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details # Given a trace event file, this tool generates a flame graph based on the event scopes present in the file