From 96c1cafff28b70054be91938c6fd004d45dfa007 Mon Sep 17 00:00:00 2001
From: Andy Friesen <afriesen@roblox.com>
Date: Fri, 13 Jan 2023 12:36:28 -0800
Subject: [PATCH] Sync to upstream/release/559

---
 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            |   96 +-
 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, 2188 insertions(+), 825 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 8d486ad5..50fd7edd 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<typename T>
 const T* get(const Predicate& predicate)
 {
diff --git a/Analysis/include/Luau/ToString.h b/Analysis/include/Luau/ToString.h
index dd8aef57..461a8fff 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 7df7edfa..a7cec946 100644
--- a/Analysis/include/Luau/TypeReduction.h
+++ b/Analysis/include/Luau/TypeReduction.h
@@ -27,8 +27,8 @@ private:
     DenseHashMap<TypeId, TypeId> cachedTypes{nullptr};
     DenseHashMap<TypePackId, TypePackId> cachedTypePacks{nullptr};
 
-    std::optional<TypeId> reduceImpl(TypeId ty);
-    std::optional<TypePackId> reduceImpl(TypePackId tp);
+    std::pair<std::optional<TypeId>, bool> reduceImpl(TypeId ty);
+    std::pair<std::optional<TypePackId>, 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 49c430e6..6fab97d5 100644
--- a/Analysis/src/Autocomplete.cpp
+++ b/Analysis/src/Autocomplete.cpp
@@ -13,6 +13,7 @@
 #include <utility>
 
 LUAU_FASTFLAGVARIABLE(LuauCompleteTableKeysBetter, false);
+LUAU_FASTFLAGVARIABLE(LuauFixAutocompleteInIf, false);
 
 static const std::unordered_set<std::string> kStatementStartingKeywords = {
     "while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
@@ -1467,8 +1468,22 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
             return {{{"then", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry, AutocompleteContext::Keyword};
     }
     else if (AstStatIf* statIf = extractStat<AstStatIf>(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<AstStatRepeat>(); statRepeat && statRepeat->condition->is<AstExprError>())
         return autocompleteExpression(sourceModule, *module, builtinTypes, typeArena, ancestry, position);
     else if (AstStatRepeat* statRepeat = extractStat<AstStatRepeat>(ancestry); statRepeat)
diff --git a/Analysis/src/ToString.cpp b/Analysis/src/ToString.cpp
index c7d9b373..86ae3cde 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, ' '));
     }
 };
 
@@ -780,11 +768,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 e47ee39c..8d837ddb 100644
--- a/Analysis/src/TypeReduction.cpp
+++ b/Analysis/src/TypeReduction.cpp
@@ -9,7 +9,7 @@
 #include <deque>
 
 LUAU_FASTINTVARIABLE(LuauTypeReductionCartesianProductLimit, 100'000)
-LUAU_FASTINTVARIABLE(LuauTypeReductionRecursionLimit, 900)
+LUAU_FASTINTVARIABLE(LuauTypeReductionRecursionLimit, 700)
 LUAU_FASTFLAGVARIABLE(DebugLuauDontReduceTypes, false)
 
 namespace Luau
@@ -27,7 +27,7 @@ struct RecursionGuard : RecursionLimiter
         , seen(seen)
     {
         // count has been incremented, which should imply that seen has already had an element pushed in.
-        LUAU_ASSERT(*count == seen->size());
+        LUAU_ASSERT(size_t(*count) == seen->size());
     }
 
     ~RecursionGuard()
@@ -51,6 +51,16 @@ struct TypeReducer
     NotNull<BuiltinTypes> builtinTypes;
     NotNull<InternalErrorReporter> handle;
 
+    std::unordered_map<TypeId, TypeId> copies;
+    std::deque<const void*> 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<const void*> seen;
-    int depth = 0;
-
     RecursionGuard guard(TypeId ty);
     RecursionGuard guard(TypePackId tp);
 
-    std::unordered_map<TypeId, TypeId> copies;
+    void checkCacheable(TypeId ty);
+    void checkCacheable(TypePackId tp);
 
     template<typename T>
     LUAU_NOINLINE std::pair<TypeId, T*> 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<IntersectionType>(ty))
         return foldl<IntersectionType>(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<TypeId> TypeReducer::intersectionType(TypeId left, TypeId right)
         return right; // any & T ~ T
     else if (get<AnyType>(right))
         return left; // T & any ~ T
+    else if (get<FreeType>(left))
+        return std::nullopt; // 'a & T ~ 'a & T
+    else if (get<FreeType>(right))
+        return std::nullopt; // T & 'a ~ T & 'a
+    else if (get<GenericType>(left))
+        return std::nullopt; // G & T ~ G & T
+    else if (get<GenericType>(right))
+        return std::nullopt; // T & G ~ T & G
     else if (get<ErrorType>(left))
         return std::nullopt; // error & T ~ error & T
     else if (get<ErrorType>(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<FreeType>(ty) || get<BlockedType>(ty) || get<PendingExpansionType>(ty))
+        cacheOk = false;
+    else if (auto tt = get<TableType>(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<FreeTypePack>(tp) || get<BlockedTypePack>(tp))
+        cacheOk = false;
+}
+
 } // namespace
 
 TypeReduction::TypeReduction(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<InternalErrorReporter> handle)
@@ -715,13 +759,11 @@ std::optional<TypeId> 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<TypePackId> TypeReduction::reduce(TypePackId tp)
@@ -729,50 +771,48 @@ std::optional<TypePackId> 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<TypeId> TypeReduction::reduceImpl(TypeId ty)
+std::pair<std::optional<TypeId>, 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<TypePackId> TypeReduction::reduceImpl(TypePackId tp)
+std::pair<std::optional<TypePackId>, 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 5cd5f743..dea54c16 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<AstType*> 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<AstTypePackExplicit>(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<AstTypePackExplicit>(begin.location, AstTypeList{paramTypes, varargAnnotation})};
-    }
 
     AstArray<std::optional<AstArgumentName>> paramNames = copy(names);
 
diff --git a/CodeGen/include/Luau/AssemblyBuilderX64.h b/CodeGen/include/Luau/AssemblyBuilderX64.h
index dbb366b2..918c8266 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 f23fe463..77856e9e 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 1c05b298..72c1294f 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 fe258ff8..2c410ae8 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 238a0ed4..e475da60 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 abbdb65c..7b6e1c64 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 1ecb06d4..96501e63 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 41f2bc8c..e84ee213 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 72573b15..bfc0e2b7 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 00000000..c4ed47cc
--- /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 <vector>
+
+#include <stdint.h>
+
+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<IrBlock> blocks;
+    std::vector<IrInst> instructions;
+    std::vector<IrConst> constants;
+
+    std::vector<BytecodeMapping> bcMapping;
+};
+
+} // namespace CodeGen
+} // namespace Luau
diff --git a/CodeGen/src/IrDump.cpp b/CodeGen/src/IrDump.cpp
new file mode 100644
index 00000000..5d54026a
--- /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 <stdarg.h>
+
+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 00000000..8fb4d6e5
--- /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 <string>
+#include <vector>
+
+namespace Luau
+{
+namespace CodeGen
+{
+
+const char* getCmdName(IrCmd cmd);
+const char* getBlockKindName(IrBlockKind kind);
+
+struct IrToStringContext
+{
+    std::string& result;
+    std::vector<IrBlock>& blocks;
+    std::vector<IrConst>& 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 00000000..f0e4cee6
--- /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 62974fe3..22d38aa6 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 9138ba47..03d8ae3a 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 0d4543f7..047c1b67 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<DebugUpval> debugUpvals;
 
     DenseHashMap<StringRef, unsigned int, StringRefHash> stringTable;
+    std::vector<StringRef> debugStrings;
 
     std::vector<std::pair<uint32_t, uint32_t>> debugRemarks;
     std::string debugRemarkBuffer;
@@ -261,6 +264,7 @@ private:
     void validateVariadic() const;
 
     std::string dumpCurrentFunction(std::vector<int>& 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 7d230738..11bf2429 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 8a1e80fc..fbeef68e 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 87d76bf3..36e4f04d 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 955604de..190cf66a 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 c2d07ddd..b4490fff 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 ece4f551..97ddfa2c 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 62a5668b..9b10229e 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 192ea0b5..59f16e5c 100644
--- a/VM/src/lstrlib.cpp
+++ b/VM/src/lstrlib.cpp
@@ -8,6 +8,8 @@
 #include <string.h>
 #include <stdio.h>
 
+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 6dd94149..0efa9ee0 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 837d0e12..0bbce01f 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 0db33950..547e0d38 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 b5dbf583..d6d16509 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 f241963a..3b59bc33 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 1a606126..42d88c7e 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 c32f2870..d7340ce5 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 18e91e1b..fae8bc4d 100644
--- a/tests/Parser.test.cpp
+++ b/tests/Parser.test.cpp
@@ -2177,8 +2177,6 @@ type C<X...> = 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 <eof>");
     matchParseError("type P<T...> = () -> T... type B = P<(x: number, y: string)>", "Expected '->' when parsing function type, got '>'");
     matchParseError("type F<T... = (a: string)> = (T...) -> ()", "Expected '->' when parsing function type, got '>'");
diff --git a/tests/ToString.test.cpp b/tests/ToString.test.cpp
index 7ccda8de..7d27437d 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 c629b3e3..f2d7b027 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<TableType>(ty1)->state = TableState::Free;
+        getMutable<TableType>(ty1)->props["x"] = {builtinTypes->stringType};
+
+        TypeId ty2 = arena.addType(TableType{});
+        getMutable<TableType>(ty2)->state = TableState::Sealed;
+
+        TypeId intersectionTy = arena.addType(IntersectionType{{ty1, ty2}});
+
+        ToStringOptions opts;
+        opts.exhaustive = true;
+
+        CHECK("{- x: string -} & {|  |}" == toString(reductionof(intersectionTy)));
+
+        getMutable<TableType>(ty1)->state = TableState::Sealed;
+        CHECK("{| x: string |}" == toString(reductionof(intersectionTy)));
+    }
+
+    SUBCASE("unsealed_tables")
+    {
+        TypeId ty1 = arena.addType(TableType{});
+        getMutable<TableType>(ty1)->state = TableState::Unsealed;
+        getMutable<TableType>(ty1)->props["x"] = {builtinTypes->stringType};
+
+        TypeId ty2 = arena.addType(TableType{});
+        getMutable<TableType>(ty2)->state = TableState::Sealed;
+
+        TypeId intersectionTy = arena.addType(IntersectionType{{ty1, ty2}});
+
+        ToStringOptions opts;
+        opts.exhaustive = true;
+
+        CHECK("{| x: string |}" == toString(reductionof(intersectionTy)));
+
+        getMutable<TableType>(ty1)->state = TableState::Sealed;
+        CHECK("{| x: string |}" == toString(reductionof(intersectionTy)));
+    }
+
+    SUBCASE("free_types")
+    {
+        TypeId ty1 = arena.freshType(nullptr);
+        TypeId ty2 = arena.addType(TableType{});
+        getMutable<TableType>(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<string> & number ~ never
+
+        *asMutable(ty1) = BoundType{ty2};
+        CHECK("never" == toString(reductionof(intersectionTy))); // Bound<number> & 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 61bac726..59d34218 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 7ae80cc4..4b47ed26 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 f336bb22..3fcd4200 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 b8dc207f..17ce7a40 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 7337aa44..d9fd839a 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 f4a78960..16de45dc 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 47ad36d9..4fb64e62 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 56970c9f..82d8364c 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 eb6b68ce..94c57cc7 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 e5cfd117..1af2473b 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 91e74887..9f11b650 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 6d553b64..d30490b3 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 a46423e7..1cdd32d6 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