From 4e923b679be038c0f3f3335880b80bc0fd7ca7c4 Mon Sep 17 00:00:00 2001 From: Alex Orlenko Date: Thu, 31 Mar 2022 23:20:58 +0100 Subject: [PATCH] v0.2.2+luau521 --- Cargo.toml | 2 +- luau/Ast/include/Luau/TimeTrace.h | 20 +-- luau/Ast/src/Lexer.cpp | 8 +- luau/Ast/src/Parser.cpp | 20 +-- luau/Ast/src/TimeTrace.cpp | 5 +- luau/Compiler/include/Luau/Bytecode.h | 3 +- luau/Compiler/src/BytecodeBuilder.cpp | 2 +- luau/VM/src/lapi.cpp | 50 +++--- luau/VM/src/laux.cpp | 13 +- luau/VM/src/lbaselib.cpp | 25 +-- luau/VM/src/ldebug.cpp | 18 +- luau/VM/src/ldo.cpp | 21 ++- luau/VM/src/ldo.h | 2 +- luau/VM/src/lgc.cpp | 228 ++++++++++++++------------ luau/VM/src/lgc.h | 2 +- luau/VM/src/lmem.h | 6 +- luau/VM/src/lstate.cpp | 26 +-- luau/VM/src/lstate.h | 44 ++--- luau/VM/src/ltable.cpp | 68 ++++++-- luau/VM/src/ltablib.cpp | 30 +++- luau/VM/src/lvmexecute.cpp | 6 +- luau/VM/src/lvmload.cpp | 13 +- src/lib.rs | 1 - 23 files changed, 320 insertions(+), 293 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7826b78..1424d78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "luau0-src" -version = "0.2.1+luau519" +version = "0.2.2+luau521" authors = ["Aleksandr Orlenko "] edition = "2018" repository = "https://github.com/khvzak/luau-src-rs" diff --git a/luau/Ast/include/Luau/TimeTrace.h b/luau/Ast/include/Luau/TimeTrace.h index 503eca6..5018456 100644 --- a/luau/Ast/include/Luau/TimeTrace.h +++ b/luau/Ast/include/Luau/TimeTrace.h @@ -130,8 +130,8 @@ ThreadContext& getThreadContext(); struct Scope { - explicit Scope(ThreadContext& context, uint16_t token) - : context(context) + explicit Scope(uint16_t token) + : context(getThreadContext()) { if (!FFlag::DebugLuauTimeTracing) return; @@ -152,8 +152,8 @@ struct Scope struct OptionalTailScope { - explicit OptionalTailScope(ThreadContext& context, uint16_t token, uint32_t threshold) - : context(context) + explicit OptionalTailScope(uint16_t token, uint32_t threshold) + : context(getThreadContext()) , token(token) , threshold(threshold) { @@ -188,27 +188,27 @@ struct OptionalTailScope uint32_t pos; }; -LUAU_NOINLINE std::pair createScopeData(const char* name, const char* category); +LUAU_NOINLINE uint16_t createScopeData(const char* name, const char* category); } // namespace TimeTrace } // namespace Luau // Regular scope #define LUAU_TIMETRACE_SCOPE(name, category) \ - static auto lttScopeStatic = Luau::TimeTrace::createScopeData(name, category); \ - Luau::TimeTrace::Scope lttScope(lttScopeStatic.second, lttScopeStatic.first) + static uint16_t lttScopeStatic = Luau::TimeTrace::createScopeData(name, category); \ + Luau::TimeTrace::Scope lttScope(lttScopeStatic) // A scope without nested scopes that may be skipped if the time it took is less than the threshold #define LUAU_TIMETRACE_OPTIONAL_TAIL_SCOPE(name, category, microsec) \ - static auto lttScopeStaticOptTail = Luau::TimeTrace::createScopeData(name, category); \ - Luau::TimeTrace::OptionalTailScope lttScope(lttScopeStaticOptTail.second, lttScopeStaticOptTail.first, microsec) + static uint16_t lttScopeStaticOptTail = Luau::TimeTrace::createScopeData(name, category); \ + Luau::TimeTrace::OptionalTailScope lttScope(lttScopeStaticOptTail, microsec) // Extra key/value data can be added to regular scopes #define LUAU_TIMETRACE_ARGUMENT(name, value) \ do \ { \ if (FFlag::DebugLuauTimeTracing) \ - lttScopeStatic.second.eventArgument(name, value); \ + lttScope.context.eventArgument(name, value); \ } while (false) #else diff --git a/luau/Ast/src/Lexer.cpp b/luau/Ast/src/Lexer.cpp index d56c886..70c6c78 100644 --- a/luau/Ast/src/Lexer.cpp +++ b/luau/Ast/src/Lexer.cpp @@ -6,6 +6,8 @@ #include +LUAU_FASTFLAGVARIABLE(LuauParseLocationIgnoreCommentSkip, false) + namespace Luau { @@ -352,6 +354,8 @@ const Lexeme& Lexer::next() const Lexeme& Lexer::next(bool skipComments) { + bool first = true; + // in skipComments mode we reject valid comments do { @@ -359,9 +363,11 @@ const Lexeme& Lexer::next(bool skipComments) while (isSpace(peekch())) consume(); - prevLocation = lexeme.location; + if (!FFlag::LuauParseLocationIgnoreCommentSkip || first) + prevLocation = lexeme.location; lexeme = readNext(); + first = false; } while (skipComments && (lexeme.type == Lexeme::Comment || lexeme.type == Lexeme::BlockComment)); return lexeme; diff --git a/luau/Ast/src/Parser.cpp b/luau/Ast/src/Parser.cpp index 941a3ea..f6dfd90 100644 --- a/luau/Ast/src/Parser.cpp +++ b/luau/Ast/src/Parser.cpp @@ -10,8 +10,6 @@ // See docs/SyntaxChanges.md for an explanation. LUAU_FASTINTVARIABLE(LuauRecursionLimit, 1000) LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100) -LUAU_FASTFLAGVARIABLE(LuauParseSingletonTypes, false) -LUAU_FASTFLAGVARIABLE(LuauTableFieldFunctionDebugname, false) namespace Luau { @@ -1233,8 +1231,7 @@ AstType* Parser::parseTableTypeAnnotation() while (lexer.current().type != '}') { - if (FFlag::LuauParseSingletonTypes && lexer.current().type == '[' && - (lexer.lookahead().type == Lexeme::RawString || lexer.lookahead().type == Lexeme::QuotedString)) + if (lexer.current().type == '[' && (lexer.lookahead().type == Lexeme::RawString || lexer.lookahead().type == Lexeme::QuotedString)) { const Lexeme begin = lexer.current(); nextLexeme(); // [ @@ -1500,17 +1497,17 @@ AstTypeOrPack Parser::parseSimpleTypeAnnotation(bool allowPack) nextLexeme(); return {allocator.alloc(begin, std::nullopt, nameNil), {}}; } - else if (FFlag::LuauParseSingletonTypes && lexer.current().type == Lexeme::ReservedTrue) + else if (lexer.current().type == Lexeme::ReservedTrue) { nextLexeme(); return {allocator.alloc(begin, true)}; } - else if (FFlag::LuauParseSingletonTypes && lexer.current().type == Lexeme::ReservedFalse) + else if (lexer.current().type == Lexeme::ReservedFalse) { nextLexeme(); return {allocator.alloc(begin, false)}; } - else if (FFlag::LuauParseSingletonTypes && (lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString)) + else if (lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString) { if (std::optional> value = parseCharArray()) { @@ -1520,7 +1517,7 @@ AstTypeOrPack Parser::parseSimpleTypeAnnotation(bool allowPack) else return {reportTypeAnnotationError(begin, {}, /*isMissing*/ false, "String literal contains malformed escape sequence")}; } - else if (FFlag::LuauParseSingletonTypes && lexer.current().type == Lexeme::BrokenString) + else if (lexer.current().type == Lexeme::BrokenString) { Location location = lexer.current().location; nextLexeme(); @@ -2189,11 +2186,8 @@ AstExpr* Parser::parseTableConstructor() AstExpr* key = allocator.alloc(name.location, nameString); AstExpr* value = parseExpr(); - if (FFlag::LuauTableFieldFunctionDebugname) - { - if (AstExprFunction* func = value->as()) - func->debugname = name.name; - } + if (AstExprFunction* func = value->as()) + func->debugname = name.name; items.push_back({AstExprTable::Item::Record, key, value}); } diff --git a/luau/Ast/src/TimeTrace.cpp b/luau/Ast/src/TimeTrace.cpp index 8079830..19564f0 100644 --- a/luau/Ast/src/TimeTrace.cpp +++ b/luau/Ast/src/TimeTrace.cpp @@ -246,10 +246,9 @@ ThreadContext& getThreadContext() return context; } -std::pair createScopeData(const char* name, const char* category) +uint16_t createScopeData(const char* name, const char* category) { - uint16_t token = createToken(Luau::TimeTrace::getGlobalContext(), name, category); - return {token, Luau::TimeTrace::getThreadContext()}; + return createToken(Luau::TimeTrace::getGlobalContext(), name, category); } } // namespace TimeTrace } // namespace Luau diff --git a/luau/Compiler/include/Luau/Bytecode.h b/luau/Compiler/include/Luau/Bytecode.h index 679712f..c6e5a03 100644 --- a/luau/Compiler/include/Luau/Bytecode.h +++ b/luau/Compiler/include/Luau/Bytecode.h @@ -376,8 +376,7 @@ enum LuauOpcode enum LuauBytecodeTag { // Bytecode version - LBC_VERSION = 1, - LBC_VERSION_FUTURE = 2, // TODO: This will be removed in favor of LBC_VERSION with LuauBytecodeV2Force + LBC_VERSION = 2, // Types of constant table entries LBC_CONSTANT_NIL = 0, LBC_CONSTANT_BOOLEAN, diff --git a/luau/Compiler/src/BytecodeBuilder.cpp b/luau/Compiler/src/BytecodeBuilder.cpp index 09f06b6..6944de0 100644 --- a/luau/Compiler/src/BytecodeBuilder.cpp +++ b/luau/Compiler/src/BytecodeBuilder.cpp @@ -508,7 +508,7 @@ uint32_t BytecodeBuilder::getDebugPC() const void BytecodeBuilder::finalize() { LUAU_ASSERT(bytecode.empty()); - bytecode = char(LBC_VERSION_FUTURE); + bytecode = char(LBC_VERSION); writeStringTable(bytecode); diff --git a/luau/VM/src/lapi.cpp b/luau/VM/src/lapi.cpp index 3c08731..46b1093 100644 --- a/luau/VM/src/lapi.cpp +++ b/luau/VM/src/lapi.cpp @@ -14,8 +14,6 @@ #include -LUAU_FASTFLAG(LuauGcAdditionalStats) - const char* lua_ident = "$Lua: Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio $\n" "$Authors: R. Ierusalimschy, L. H. de Figueiredo & W. Celes $\n" "$URL: www.lua.org $\n"; @@ -1060,8 +1058,11 @@ int lua_gc(lua_State* L, int what, int data) g->GCthreshold = 0; bool waspaused = g->gcstate == GCSpause; - double startmarktime = g->gcstats.currcycle.marktime; - double startsweeptime = g->gcstats.currcycle.sweeptime; + +#ifdef LUAI_GCMETRICS + double startmarktime = g->gcmetrics.currcycle.marktime; + double startsweeptime = g->gcmetrics.currcycle.sweeptime; +#endif // track how much work the loop will actually perform size_t actualwork = 0; @@ -1079,31 +1080,30 @@ int lua_gc(lua_State* L, int what, int data) } } - if (FFlag::LuauGcAdditionalStats) +#ifdef LUAI_GCMETRICS + // record explicit step statistics + GCCycleMetrics* cyclemetrics = g->gcstate == GCSpause ? &g->gcmetrics.lastcycle : &g->gcmetrics.currcycle; + + double totalmarktime = cyclemetrics->marktime - startmarktime; + double totalsweeptime = cyclemetrics->sweeptime - startsweeptime; + + if (totalmarktime > 0.0) { - // record explicit step statistics - GCCycleStats* cyclestats = g->gcstate == GCSpause ? &g->gcstats.lastcycle : &g->gcstats.currcycle; + cyclemetrics->markexplicitsteps++; - double totalmarktime = cyclestats->marktime - startmarktime; - double totalsweeptime = cyclestats->sweeptime - startsweeptime; - - if (totalmarktime > 0.0) - { - cyclestats->markexplicitsteps++; - - if (totalmarktime > cyclestats->markmaxexplicittime) - cyclestats->markmaxexplicittime = totalmarktime; - } - - if (totalsweeptime > 0.0) - { - cyclestats->sweepexplicitsteps++; - - if (totalsweeptime > cyclestats->sweepmaxexplicittime) - cyclestats->sweepmaxexplicittime = totalsweeptime; - } + if (totalmarktime > cyclemetrics->markmaxexplicittime) + cyclemetrics->markmaxexplicittime = totalmarktime; } + if (totalsweeptime > 0.0) + { + cyclemetrics->sweepexplicitsteps++; + + if (totalsweeptime > cyclemetrics->sweepmaxexplicittime) + cyclemetrics->sweepmaxexplicittime = totalsweeptime; + } +#endif + // if cycle hasn't finished, advance threshold forward for the amount of extra work performed if (g->gcstate != GCSpause) { diff --git a/luau/VM/src/laux.cpp b/luau/VM/src/laux.cpp index 9fe2ebb..72169a8 100644 --- a/luau/VM/src/laux.cpp +++ b/luau/VM/src/laux.cpp @@ -11,8 +11,6 @@ #include -LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauMorePreciseLuaLTypeName, false) - /* convert a stack index to positive */ #define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1) @@ -337,15 +335,8 @@ const char* luaL_findtable(lua_State* L, int idx, const char* fname, int szhint) const char* luaL_typename(lua_State* L, int idx) { - if (DFFlag::LuauMorePreciseLuaLTypeName) - { - const TValue* obj = luaA_toobject(L, idx); - return luaT_objtypename(L, obj); - } - else - { - return lua_typename(L, lua_type(L, idx)); - } + const TValue* obj = luaA_toobject(L, idx); + return luaT_objtypename(L, obj); } /* diff --git a/luau/VM/src/lbaselib.cpp b/luau/VM/src/lbaselib.cpp index 96ad493..f791761 100644 --- a/luau/VM/src/lbaselib.cpp +++ b/luau/VM/src/lbaselib.cpp @@ -11,8 +11,6 @@ #include #include -LUAU_DYNAMIC_FASTFLAG(LuauMorePreciseLuaLTypeName) - static void writestring(const char* s, size_t l) { fwrite(s, 1, l, stdout); @@ -189,31 +187,16 @@ static int luaB_gcinfo(lua_State* L) static int luaB_type(lua_State* L) { luaL_checkany(L, 1); - if (DFFlag::LuauMorePreciseLuaLTypeName) - { - /* resulting name doesn't differentiate between userdata types */ - lua_pushstring(L, lua_typename(L, lua_type(L, 1))); - } - else - { - lua_pushstring(L, luaL_typename(L, 1)); - } + /* resulting name doesn't differentiate between userdata types */ + lua_pushstring(L, lua_typename(L, lua_type(L, 1))); return 1; } static int luaB_typeof(lua_State* L) { luaL_checkany(L, 1); - if (DFFlag::LuauMorePreciseLuaLTypeName) - { - /* resulting name returns __type if specified unless the input is a newproxy-created userdata */ - lua_pushstring(L, luaL_typename(L, 1)); - } - else - { - const TValue* obj = luaA_toobject(L, 1); - lua_pushstring(L, luaT_objtypename(L, obj)); - } + /* resulting name returns __type if specified unless the input is a newproxy-created userdata */ + lua_pushstring(L, luaL_typename(L, 1)); return 1; } diff --git a/luau/VM/src/ldebug.cpp b/luau/VM/src/ldebug.cpp index 7a9947b..e050050 100644 --- a/luau/VM/src/ldebug.cpp +++ b/luau/VM/src/ldebug.cpp @@ -12,8 +12,6 @@ #include #include -LUAU_FASTFLAG(LuauBytecodeV2Force) - static const char* getfuncname(Closure* f); static int currentpc(lua_State* L, CallInfo* ci) @@ -91,16 +89,6 @@ const char* lua_setlocal(lua_State* L, int level, int n) return name; } -static int getlinedefined(Proto* p) -{ - if (FFlag::LuauBytecodeV2Force) - return p->linedefined; - else if (p->linedefined >= 0) - return p->linedefined; - else - return luaG_getline(p, 0); -} - static int auxgetinfo(lua_State* L, const char* what, lua_Debug* ar, Closure* f, CallInfo* ci) { int status = 1; @@ -120,7 +108,7 @@ static int auxgetinfo(lua_State* L, const char* what, lua_Debug* ar, Closure* f, { ar->source = getstr(f->l.p->source); ar->what = "Lua"; - ar->linedefined = getlinedefined(f->l.p); + ar->linedefined = f->l.p->linedefined; } luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); break; @@ -133,7 +121,7 @@ static int auxgetinfo(lua_State* L, const char* what, lua_Debug* ar, Closure* f, } else { - ar->currentline = f->isC ? -1 : getlinedefined(f->l.p); + ar->currentline = f->isC ? -1 : f->l.p->linedefined; } break; @@ -424,7 +412,7 @@ static void getcoverage(Proto* p, int depth, int* buffer, size_t size, void* con } const char* debugname = p->debugname ? getstr(p->debugname) : NULL; - int linedefined = getlinedefined(p); + int linedefined = p->linedefined; callback(context, debugname, linedefined, depth, buffer, size); diff --git a/luau/VM/src/ldo.cpp b/luau/VM/src/ldo.cpp index b5ae496..c133a59 100644 --- a/luau/VM/src/ldo.cpp +++ b/luau/VM/src/ldo.cpp @@ -17,8 +17,6 @@ #include -LUAU_FASTFLAG(LuauReduceStackReallocs) - /* ** {====================================================== ** Error-recovery functions @@ -33,6 +31,15 @@ struct lua_jmpbuf jmp_buf buf; }; +/* use POSIX versions of setjmp/longjmp if possible: they don't save/restore signal mask and are therefore faster */ +#if defined(__linux__) || defined(__APPLE__) +#define LUAU_SETJMP(buf) _setjmp(buf) +#define LUAU_LONGJMP(buf, code) _longjmp(buf, code) +#else +#define LUAU_SETJMP(buf) setjmp(buf) +#define LUAU_LONGJMP(buf, code) longjmp(buf, code) +#endif + int luaD_rawrunprotected(lua_State* L, Pfunc f, void* ud) { lua_jmpbuf jb; @@ -40,7 +47,7 @@ int luaD_rawrunprotected(lua_State* L, Pfunc f, void* ud) jb.status = 0; L->global->errorjmp = &jb; - if (setjmp(jb.buf) == 0) + if (LUAU_SETJMP(jb.buf) == 0) f(L, ud); L->global->errorjmp = jb.prev; @@ -52,7 +59,7 @@ l_noret luaD_throw(lua_State* L, int errcode) if (lua_jmpbuf* jb = L->global->errorjmp) { jb->status = errcode; - longjmp(jb->buf, 1); + LUAU_LONGJMP(jb->buf, 1); } if (L->global->cb.panic) @@ -165,8 +172,8 @@ static void correctstack(lua_State* L, TValue* oldstack) void luaD_reallocstack(lua_State* L, int newsize) { TValue* oldstack = L->stack; - int realsize = newsize + (FFlag::LuauReduceStackReallocs ? EXTRA_STACK : 1 + EXTRA_STACK); - LUAU_ASSERT(L->stack_last - L->stack == L->stacksize - (FFlag::LuauReduceStackReallocs ? EXTRA_STACK : 1 + EXTRA_STACK)); + int realsize = newsize + EXTRA_STACK; + LUAU_ASSERT(L->stack_last - L->stack == L->stacksize - EXTRA_STACK); luaM_reallocarray(L, L->stack, L->stacksize, realsize, TValue, L->memcat); TValue* newstack = L->stack; for (int i = L->stacksize; i < realsize; i++) @@ -514,7 +521,7 @@ static void callerrfunc(lua_State* L, void* ud) static void restore_stack_limit(lua_State* L) { - LUAU_ASSERT(L->stack_last - L->stack == L->stacksize - (FFlag::LuauReduceStackReallocs ? EXTRA_STACK : 1 + EXTRA_STACK)); + LUAU_ASSERT(L->stack_last - L->stack == L->stacksize - EXTRA_STACK); if (L->size_ci > LUAI_MAXCALLS) { /* there was an overflow? */ int inuse = cast_int(L->ci - L->base_ci); diff --git a/luau/VM/src/ldo.h b/luau/VM/src/ldo.h index 1c1480d..6e16e6f 100644 --- a/luau/VM/src/ldo.h +++ b/luau/VM/src/ldo.h @@ -11,7 +11,7 @@ if ((char*)L->stack_last - (char*)L->top <= (n) * (int)sizeof(TValue)) \ luaD_growstack(L, n); \ else \ - condhardstacktests(luaD_reallocstack(L, L->stacksize - (FFlag::LuauReduceStackReallocs ? EXTRA_STACK : 1 + EXTRA_STACK))); + condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK)); #define incr_top(L) \ { \ diff --git a/luau/VM/src/lgc.cpp b/luau/VM/src/lgc.cpp index a656854..8fc930d 100644 --- a/luau/VM/src/lgc.cpp +++ b/luau/VM/src/lgc.cpp @@ -11,8 +11,6 @@ #include "lmem.h" #include "ludata.h" -LUAU_FASTFLAGVARIABLE(LuauGcAdditionalStats, false) - #include #define GC_SWEEPMAX 40 @@ -48,7 +46,8 @@ LUAU_FASTFLAGVARIABLE(LuauGcAdditionalStats, false) reallymarkobject(g, obj2gco(t)); \ } -static void recordGcStateTime(global_State* g, int startgcstate, double seconds, bool assist) +#ifdef LUAI_GCMETRICS +static void recordGcStateStep(global_State* g, int startgcstate, double seconds, bool assist, size_t work) { switch (startgcstate) { @@ -56,58 +55,76 @@ static void recordGcStateTime(global_State* g, int startgcstate, double seconds, // record root mark time if we have switched to next state if (g->gcstate == GCSpropagate) { - g->gcstats.currcycle.marktime += seconds; + g->gcmetrics.currcycle.marktime += seconds; - if (FFlag::LuauGcAdditionalStats && assist) - g->gcstats.currcycle.markassisttime += seconds; + if (assist) + g->gcmetrics.currcycle.markassisttime += seconds; } break; case GCSpropagate: case GCSpropagateagain: - g->gcstats.currcycle.marktime += seconds; + g->gcmetrics.currcycle.marktime += seconds; + g->gcmetrics.currcycle.markrequests += g->gcstepsize; - if (FFlag::LuauGcAdditionalStats && assist) - g->gcstats.currcycle.markassisttime += seconds; + if (assist) + g->gcmetrics.currcycle.markassisttime += seconds; break; case GCSatomic: - g->gcstats.currcycle.atomictime += seconds; + g->gcmetrics.currcycle.atomictime += seconds; break; case GCSsweep: - g->gcstats.currcycle.sweeptime += seconds; + g->gcmetrics.currcycle.sweeptime += seconds; + g->gcmetrics.currcycle.sweeprequests += g->gcstepsize; - if (FFlag::LuauGcAdditionalStats && assist) - g->gcstats.currcycle.sweepassisttime += seconds; + if (assist) + g->gcmetrics.currcycle.sweepassisttime += seconds; break; default: LUAU_ASSERT(!"Unexpected GC state"); } if (assist) - g->gcstats.stepassisttimeacc += seconds; + { + g->gcmetrics.stepassisttimeacc += seconds; + g->gcmetrics.currcycle.assistwork += work; + g->gcmetrics.currcycle.assistrequests += g->gcstepsize; + } else - g->gcstats.stepexplicittimeacc += seconds; + { + g->gcmetrics.stepexplicittimeacc += seconds; + g->gcmetrics.currcycle.explicitwork += work; + g->gcmetrics.currcycle.explicitrequests += g->gcstepsize; + } } -static void startGcCycleStats(global_State* g) +static double recordGcDeltaTime(double& timer) { - g->gcstats.currcycle.starttimestamp = lua_clock(); - g->gcstats.currcycle.pausetime = g->gcstats.currcycle.starttimestamp - g->gcstats.lastcycle.endtimestamp; + double now = lua_clock(); + double delta = now - timer; + timer = now; + return delta; } -static void finishGcCycleStats(global_State* g) +static void startGcCycleMetrics(global_State* g) { - g->gcstats.currcycle.endtimestamp = lua_clock(); - g->gcstats.currcycle.endtotalsizebytes = g->totalbytes; - - g->gcstats.completedcycles++; - g->gcstats.lastcycle = g->gcstats.currcycle; - g->gcstats.currcycle = GCCycleStats(); - - g->gcstats.cyclestatsacc.marktime += g->gcstats.lastcycle.marktime; - g->gcstats.cyclestatsacc.atomictime += g->gcstats.lastcycle.atomictime; - g->gcstats.cyclestatsacc.sweeptime += g->gcstats.lastcycle.sweeptime; + g->gcmetrics.currcycle.starttimestamp = lua_clock(); + g->gcmetrics.currcycle.pausetime = g->gcmetrics.currcycle.starttimestamp - g->gcmetrics.lastcycle.endtimestamp; } +static void finishGcCycleMetrics(global_State* g) +{ + g->gcmetrics.currcycle.endtimestamp = lua_clock(); + g->gcmetrics.currcycle.endtotalsizebytes = g->totalbytes; + + g->gcmetrics.completedcycles++; + g->gcmetrics.lastcycle = g->gcmetrics.currcycle; + g->gcmetrics.currcycle = GCCycleMetrics(); + + g->gcmetrics.currcycle.starttotalsizebytes = g->totalbytes; + g->gcmetrics.currcycle.heaptriggersizebytes = g->GCthreshold; +} +#endif + static void removeentry(LuaNode* n) { LUAU_ASSERT(ttisnil(gval(n))); @@ -598,20 +615,19 @@ static size_t atomic(lua_State* L) LUAU_ASSERT(g->gcstate == GCSatomic); size_t work = 0; + +#ifdef LUAI_GCMETRICS double currts = lua_clock(); - double prevts = currts; +#endif /* remark occasional upvalues of (maybe) dead threads */ work += remarkupvals(g); /* traverse objects caught by write barrier and by 'remarkupvals' */ work += propagateall(g); - if (FFlag::LuauGcAdditionalStats) - { - currts = lua_clock(); - g->gcstats.currcycle.atomictimeupval += currts - prevts; - prevts = currts; - } +#ifdef LUAI_GCMETRICS + g->gcmetrics.currcycle.atomictimeupval += recordGcDeltaTime(currts); +#endif /* remark weak tables */ g->gray = g->weak; @@ -621,34 +637,26 @@ static size_t atomic(lua_State* L) markmt(g); /* mark basic metatables (again) */ work += propagateall(g); - if (FFlag::LuauGcAdditionalStats) - { - currts = lua_clock(); - g->gcstats.currcycle.atomictimeweak += currts - prevts; - prevts = currts; - } +#ifdef LUAI_GCMETRICS + g->gcmetrics.currcycle.atomictimeweak += recordGcDeltaTime(currts); +#endif /* remark gray again */ g->gray = g->grayagain; g->grayagain = NULL; work += propagateall(g); - if (FFlag::LuauGcAdditionalStats) - { - currts = lua_clock(); - g->gcstats.currcycle.atomictimegray += currts - prevts; - prevts = currts; - } +#ifdef LUAI_GCMETRICS + g->gcmetrics.currcycle.atomictimegray += recordGcDeltaTime(currts); +#endif /* remove collected objects from weak tables */ work += cleartable(L, g->weak); g->weak = NULL; - if (FFlag::LuauGcAdditionalStats) - { - currts = lua_clock(); - g->gcstats.currcycle.atomictimeclear += currts - prevts; - } +#ifdef LUAI_GCMETRICS + g->gcmetrics.currcycle.atomictimeclear += recordGcDeltaTime(currts); +#endif /* flip current white */ g->currentwhite = cast_byte(otherwhite(g)); @@ -742,8 +750,9 @@ static size_t gcstep(lua_State* L, size_t limit) if (!g->gray) { - if (FFlag::LuauGcAdditionalStats) - g->gcstats.currcycle.propagatework = g->gcstats.currcycle.explicitwork + g->gcstats.currcycle.assistwork; +#ifdef LUAI_GCMETRICS + g->gcmetrics.currcycle.propagatework = g->gcmetrics.currcycle.explicitwork + g->gcmetrics.currcycle.assistwork; +#endif // perform one iteration over 'gray again' list g->gray = g->grayagain; @@ -762,9 +771,10 @@ static size_t gcstep(lua_State* L, size_t limit) if (!g->gray) /* no more `gray' objects */ { - if (FFlag::LuauGcAdditionalStats) - g->gcstats.currcycle.propagateagainwork = - g->gcstats.currcycle.explicitwork + g->gcstats.currcycle.assistwork - g->gcstats.currcycle.propagatework; +#ifdef LUAI_GCMETRICS + g->gcmetrics.currcycle.propagateagainwork = + g->gcmetrics.currcycle.explicitwork + g->gcmetrics.currcycle.assistwork - g->gcmetrics.currcycle.propagatework; +#endif g->gcstate = GCSatomic; } @@ -772,8 +782,13 @@ static size_t gcstep(lua_State* L, size_t limit) } case GCSatomic: { - g->gcstats.currcycle.atomicstarttimestamp = lua_clock(); - g->gcstats.currcycle.atomicstarttotalsizebytes = g->totalbytes; +#ifdef LUAI_GCMETRICS + g->gcmetrics.currcycle.atomicstarttimestamp = lua_clock(); + g->gcmetrics.currcycle.atomicstarttotalsizebytes = g->totalbytes; +#endif + + g->gcstats.atomicstarttimestamp = lua_clock(); + g->gcstats.atomicstarttotalsizebytes = g->totalbytes; cost = atomic(L); /* finish mark phase */ @@ -809,18 +824,20 @@ static size_t gcstep(lua_State* L, size_t limit) return cost; } -static int64_t getheaptriggererroroffset(GCHeapTriggerStats* triggerstats, GCCycleStats* cyclestats) +static int64_t getheaptriggererroroffset(global_State* g) { // adjust for error using Proportional-Integral controller // https://en.wikipedia.org/wiki/PID_controller - int32_t errorKb = int32_t((cyclestats->atomicstarttotalsizebytes - cyclestats->heapgoalsizebytes) / 1024); + int32_t errorKb = int32_t((g->gcstats.atomicstarttotalsizebytes - g->gcstats.heapgoalsizebytes) / 1024); // we use sliding window for the error integral to avoid error sum 'windup' when the desired target cannot be reached - int32_t* slot = &triggerstats->terms[triggerstats->termpos % triggerstats->termcount]; + const size_t triggertermcount = sizeof(g->gcstats.triggerterms) / sizeof(g->gcstats.triggerterms[0]); + + int32_t* slot = &g->gcstats.triggerterms[g->gcstats.triggertermpos % triggertermcount]; int32_t prev = *slot; *slot = errorKb; - triggerstats->integral += errorKb - prev; - triggerstats->termpos++; + g->gcstats.triggerintegral += errorKb - prev; + g->gcstats.triggertermpos++; // controller tuning // https://en.wikipedia.org/wiki/Ziegler%E2%80%93Nichols_method @@ -832,7 +849,7 @@ static int64_t getheaptriggererroroffset(GCHeapTriggerStats* triggerstats, GCCyc const double Ki = 0.54 * Ku / Ti; // integral gain double proportionalTerm = Kp * errorKb; - double integralTerm = Ki * triggerstats->integral; + double integralTerm = Ki * g->gcstats.triggerintegral; double totalTerm = proportionalTerm + integralTerm; @@ -841,23 +858,20 @@ static int64_t getheaptriggererroroffset(GCHeapTriggerStats* triggerstats, GCCyc static size_t getheaptrigger(global_State* g, size_t heapgoal) { - GCCycleStats* lastcycle = &g->gcstats.lastcycle; - GCCycleStats* currcycle = &g->gcstats.currcycle; - // adjust threshold based on a guess of how many bytes will be allocated between the cycle start and sweep phase // our goal is to begin the sweep when used memory has reached the heap goal const double durationthreshold = 1e-3; - double allocationduration = currcycle->atomicstarttimestamp - lastcycle->endtimestamp; + double allocationduration = g->gcstats.atomicstarttimestamp - g->gcstats.endtimestamp; // avoid measuring intervals smaller than 1ms if (allocationduration < durationthreshold) return heapgoal; - double allocationrate = (currcycle->atomicstarttotalsizebytes - lastcycle->endtotalsizebytes) / allocationduration; - double markduration = currcycle->atomicstarttimestamp - currcycle->starttimestamp; + double allocationrate = (g->gcstats.atomicstarttotalsizebytes - g->gcstats.endtotalsizebytes) / allocationduration; + double markduration = g->gcstats.atomicstarttimestamp - g->gcstats.starttimestamp; int64_t expectedgrowth = int64_t(markduration * allocationrate); - int64_t offset = getheaptriggererroroffset(&g->gcstats.triggerstats, currcycle); + int64_t offset = getheaptriggererroroffset(g); int64_t heaptrigger = heapgoal - (expectedgrowth + offset); // clamp the trigger between memory use at the end of the cycle and the heap goal @@ -868,11 +882,6 @@ void luaC_step(lua_State* L, bool assist) { global_State* g = L->global; - if (assist) - g->gcstats.currcycle.assistrequests += g->gcstepsize; - else - g->gcstats.currcycle.explicitrequests += g->gcstepsize; - int lim = (g->gcstepsize / 100) * g->gcstepmul; /* how much to work */ LUAU_ASSERT(g->totalbytes >= g->GCthreshold); size_t debt = g->totalbytes - g->GCthreshold; @@ -881,24 +890,23 @@ void luaC_step(lua_State* L, bool assist) // at the start of the new cycle if (g->gcstate == GCSpause) - startGcCycleStats(g); + g->gcstats.starttimestamp = lua_clock(); + +#ifdef LUAI_GCMETRICS + if (g->gcstate == GCSpause) + startGcCycleMetrics(g); + + double lasttimestamp = lua_clock(); +#endif int lastgcstate = g->gcstate; - double lasttimestamp = lua_clock(); size_t work = gcstep(L, lim); + (void)work; - if (assist) - g->gcstats.currcycle.assistwork += work; - else - g->gcstats.currcycle.explicitwork += work; - - recordGcStateTime(g, lastgcstate, lua_clock() - lasttimestamp, assist); - - if (lastgcstate == GCSpropagate) - g->gcstats.currcycle.markrequests += g->gcstepsize; - else if (lastgcstate == GCSsweep) - g->gcstats.currcycle.sweeprequests += g->gcstepsize; +#ifdef LUAI_GCMETRICS + recordGcStateStep(g, lastgcstate, lua_clock() - lasttimestamp, assist, work); +#endif // at the end of the last cycle if (g->gcstate == GCSpause) @@ -909,13 +917,13 @@ void luaC_step(lua_State* L, bool assist) g->GCthreshold = heaptrigger; - finishGcCycleStats(g); + g->gcstats.heapgoalsizebytes = heapgoal; + g->gcstats.endtimestamp = lua_clock(); + g->gcstats.endtotalsizebytes = g->totalbytes; - if (FFlag::LuauGcAdditionalStats) - g->gcstats.currcycle.starttotalsizebytes = g->totalbytes; - - g->gcstats.currcycle.heapgoalsizebytes = heapgoal; - g->gcstats.currcycle.heaptriggersizebytes = heaptrigger; +#ifdef LUAI_GCMETRICS + finishGcCycleMetrics(g); +#endif } else { @@ -933,8 +941,10 @@ void luaC_fullgc(lua_State* L) { global_State* g = L->global; +#ifdef LUAI_GCMETRICS if (g->gcstate == GCSpause) - startGcCycleStats(g); + startGcCycleMetrics(g); +#endif if (g->gcstate <= GCSatomic) { @@ -954,11 +964,12 @@ void luaC_fullgc(lua_State* L) gcstep(L, SIZE_MAX); } - finishGcCycleStats(g); +#ifdef LUAI_GCMETRICS + finishGcCycleMetrics(g); + startGcCycleMetrics(g); +#endif /* run a full collection cycle */ - startGcCycleStats(g); - markroot(L); while (g->gcstate != GCSpause) { @@ -980,10 +991,11 @@ void luaC_fullgc(lua_State* L) if (g->GCthreshold < g->totalbytes) g->GCthreshold = g->totalbytes; - finishGcCycleStats(g); + g->gcstats.heapgoalsizebytes = heapgoalsizebytes; - g->gcstats.currcycle.heapgoalsizebytes = heapgoalsizebytes; - g->gcstats.currcycle.heaptriggersizebytes = g->GCthreshold; +#ifdef LUAI_GCMETRICS + finishGcCycleMetrics(g); +#endif } void luaC_barrierupval(lua_State* L, GCObject* v) @@ -1075,21 +1087,21 @@ int64_t luaC_allocationrate(lua_State* L) if (g->gcstate <= GCSatomic) { - double duration = lua_clock() - g->gcstats.lastcycle.endtimestamp; + double duration = lua_clock() - g->gcstats.endtimestamp; if (duration < durationthreshold) return -1; - return int64_t((g->totalbytes - g->gcstats.lastcycle.endtotalsizebytes) / duration); + return int64_t((g->totalbytes - g->gcstats.endtotalsizebytes) / duration); } // totalbytes is unstable during the sweep, use the rate measured at the end of mark phase - double duration = g->gcstats.currcycle.atomicstarttimestamp - g->gcstats.lastcycle.endtimestamp; + double duration = g->gcstats.atomicstarttimestamp - g->gcstats.endtimestamp; if (duration < durationthreshold) return -1; - return int64_t((g->gcstats.currcycle.atomicstarttotalsizebytes - g->gcstats.lastcycle.endtotalsizebytes) / duration); + return int64_t((g->gcstats.atomicstarttotalsizebytes - g->gcstats.endtotalsizebytes) / duration); } void luaC_wakethread(lua_State* L) diff --git a/luau/VM/src/lgc.h b/luau/VM/src/lgc.h index ebf999b..dcd070b 100644 --- a/luau/VM/src/lgc.h +++ b/luau/VM/src/lgc.h @@ -82,7 +82,7 @@ #define luaC_checkGC(L) \ { \ - condhardstacktests(luaD_reallocstack(L, L->stacksize - (FFlag::LuauReduceStackReallocs ? EXTRA_STACK : 1 + EXTRA_STACK))); \ + condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK)); \ if (L->global->totalbytes >= L->global->GCthreshold) \ { \ condhardmemtests(luaC_validate(L), 1); \ diff --git a/luau/VM/src/lmem.h b/luau/VM/src/lmem.h index 0078845..e552d73 100644 --- a/luau/VM/src/lmem.h +++ b/luau/VM/src/lmem.h @@ -10,12 +10,12 @@ union GCObject; #define luaM_newgco(L, t, size, memcat) cast_to(t*, luaM_newgco_(L, size, memcat)) #define luaM_freegco(L, p, size, memcat, page) luaM_freegco_(L, obj2gco(p), size, memcat, page) -#define luaM_arraysize_(n, e) ((cast_to(size_t, (n)) <= SIZE_MAX / (e)) ? (n) * (e) : (luaM_toobig(L), SIZE_MAX)) +#define luaM_arraysize_(L, n, e) ((cast_to(size_t, (n)) <= SIZE_MAX / (e)) ? (n) * (e) : (luaM_toobig(L), SIZE_MAX)) -#define luaM_newarray(L, n, t, memcat) cast_to(t*, luaM_new_(L, luaM_arraysize_(n, sizeof(t)), memcat)) +#define luaM_newarray(L, n, t, memcat) cast_to(t*, luaM_new_(L, luaM_arraysize_(L, n, sizeof(t)), memcat)) #define luaM_freearray(L, b, n, t, memcat) luaM_free_(L, (b), (n) * sizeof(t), memcat) #define luaM_reallocarray(L, v, oldn, n, t, memcat) \ - ((v) = cast_to(t*, luaM_realloc_(L, v, (oldn) * sizeof(t), luaM_arraysize_(n, sizeof(t)), memcat))) + ((v) = cast_to(t*, luaM_realloc_(L, v, (oldn) * sizeof(t), luaM_arraysize_(L, n, sizeof(t)), memcat))) LUAI_FUNC void* luaM_new_(lua_State* L, size_t nsize, uint8_t memcat); LUAI_FUNC GCObject* luaM_newgco_(lua_State* L, size_t nsize, uint8_t memcat); diff --git a/luau/VM/src/lstate.cpp b/luau/VM/src/lstate.cpp index d4f3f0a..fbc6fb1 100644 --- a/luau/VM/src/lstate.cpp +++ b/luau/VM/src/lstate.cpp @@ -10,8 +10,6 @@ #include "ldo.h" #include "ldebug.h" -LUAU_FASTFLAGVARIABLE(LuauReduceStackReallocs, false) - /* ** Main thread combines a thread state and the global state */ @@ -35,7 +33,7 @@ static void stack_init(lua_State* L1, lua_State* L) for (int i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++) setnilvalue(stack + i); /* erase new stack */ L1->top = stack; - L1->stack_last = stack + (L1->stacksize - (FFlag::LuauReduceStackReallocs ? EXTRA_STACK : 1 + EXTRA_STACK)); + L1->stack_last = stack + (L1->stacksize - EXTRA_STACK); /* initialize first ci */ L1->ci->func = L1->top; setnilvalue(L1->top++); /* `function' entry for this `ci' */ @@ -141,30 +139,16 @@ void lua_resetthread(lua_State* L) ci->top = ci->base + LUA_MINSTACK; setnilvalue(ci->func); L->ci = ci; - if (FFlag::LuauReduceStackReallocs) - { - if (L->size_ci != BASIC_CI_SIZE) - luaD_reallocCI(L, BASIC_CI_SIZE); - } - else - { + if (L->size_ci != BASIC_CI_SIZE) luaD_reallocCI(L, BASIC_CI_SIZE); - } /* clear thread state */ L->status = LUA_OK; L->base = L->ci->base; L->top = L->ci->base; L->nCcalls = L->baseCcalls = 0; /* clear thread stack */ - if (FFlag::LuauReduceStackReallocs) - { - if (L->stacksize != BASIC_STACK_SIZE + EXTRA_STACK) - luaD_reallocstack(L, BASIC_STACK_SIZE); - } - else - { + if (L->stacksize != BASIC_STACK_SIZE + EXTRA_STACK) luaD_reallocstack(L, BASIC_STACK_SIZE); - } for (int i = 0; i < L->stacksize; i++) setnilvalue(L->stack + i); } @@ -234,6 +218,10 @@ lua_State* lua_newstate(lua_Alloc f, void* ud) g->cb = lua_Callbacks(); g->gcstats = GCStats(); +#ifdef LUAI_GCMETRICS + g->gcmetrics = GCMetrics(); +#endif + if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) { /* memory allocation error: free partial state */ diff --git a/luau/VM/src/lstate.h b/luau/VM/src/lstate.h index b2bedb4..e7c3737 100644 --- a/luau/VM/src/lstate.h +++ b/luau/VM/src/lstate.h @@ -75,10 +75,26 @@ typedef struct CallInfo #define f_isLua(ci) (!ci_func(ci)->isC) #define isLua(ci) (ttisfunction((ci)->func) && f_isLua(ci)) -struct GCCycleStats +struct GCStats +{ + // data for proportional-integral controller of heap trigger value + int32_t triggerterms[32] = {0}; + uint32_t triggertermpos = 0; + int32_t triggerintegral = 0; + + size_t atomicstarttotalsizebytes = 0; + size_t endtotalsizebytes = 0; + size_t heapgoalsizebytes = 0; + + double starttimestamp = 0; + double atomicstarttimestamp = 0; + double endtimestamp = 0; +}; + +#ifdef LUAI_GCMETRICS +struct GCCycleMetrics { size_t starttotalsizebytes = 0; - size_t heapgoalsizebytes = 0; size_t heaptriggersizebytes = 0; double pausetime = 0.0; // time from end of the last cycle to the start of a new one @@ -120,16 +136,7 @@ struct GCCycleStats size_t endtotalsizebytes = 0; }; -// data for proportional-integral controller of heap trigger value -struct GCHeapTriggerStats -{ - static const unsigned termcount = 32; - int32_t terms[termcount] = {0}; - uint32_t termpos = 0; - int32_t integral = 0; -}; - -struct GCStats +struct GCMetrics { double stepexplicittimeacc = 0.0; double stepassisttimeacc = 0.0; @@ -137,14 +144,10 @@ struct GCStats // when cycle is completed, last cycle values are updated uint64_t completedcycles = 0; - GCCycleStats lastcycle; - GCCycleStats currcycle; - - // only step count and their time is accumulated - GCCycleStats cyclestatsacc; - - GCHeapTriggerStats triggerstats; + GCCycleMetrics lastcycle; + GCCycleMetrics currcycle; }; +#endif /* ** `global state', shared by all threads of this state @@ -206,6 +209,9 @@ typedef struct global_State GCStats gcstats; +#ifdef LUAI_GCMETRICS + GCMetrics gcmetrics; +#endif } global_State; // clang-format on diff --git a/luau/VM/src/ltable.cpp b/luau/VM/src/ltable.cpp index 2deec2b..1c75c0b 100644 --- a/luau/VM/src/ltable.cpp +++ b/luau/VM/src/ltable.cpp @@ -2,17 +2,26 @@ // This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details /* -** Implementation of tables (aka arrays, objects, or hash tables). -** Tables keep its elements in two parts: an array part and a hash part. -** Non-negative integer keys are all candidates to be kept in the array -** part. The actual size of the array is the largest `n' such that at -** least half the slots between 0 and n are in use. -** Hash uses a mix of chained scatter table with Brent's variation. -** A main invariant of these tables is that, if an element is not -** in its main position (i.e. the `original' position that its hash gives -** to it), then the colliding element is in its own main position. -** Hence even when the load factor reaches 100%, performance remains good. -*/ + * Implementation of tables (aka arrays, objects, or hash tables). + * + * Tables keep the elements in two parts: an array part and a hash part. + * Integer keys >=1 are all candidates to be kept in the array part. The actual size of the array is the + * largest n such that at least half the slots between 0 and n are in use. + * Hash uses a mix of chained scatter table with Brent's variation. + * + * A main invariant of these tables is that, if an element is not in its main position (i.e. the original + * position that its hash gives to it), then the colliding element is in its own main position. + * Hence even when the load factor reaches 100%, performance remains good. + * + * Table keys can be arbitrary values unless they contain NaN. Keys are hashed and compared using raw equality, + * so even if the key is a userdata with an overridden __eq, it's not used during hash lookups. + * + * Each table has a "boundary", defined as the index k where t[k] ~= nil and t[k+1] == nil. The boundary can be + * computed using a binary search and can be adjusted when the table is modified; crucially, Luau enforces an + * invariant where the boundary must be in the array part - this enforces a consistent iteration order through the + * prefix of the table when using pairs(), and allows to implement algorithms that access elements in 1..#t range + * more efficiently. + */ #include "ltable.h" @@ -25,6 +34,7 @@ #include LUAU_FASTFLAGVARIABLE(LuauTableRehashRework, false) +LUAU_FASTFLAGVARIABLE(LuauTableNewBoundary, false) // max size of both array and hash part is 2^MAXBITS #define MAXBITS 26 @@ -460,7 +470,20 @@ static void rehash(lua_State* L, Table* t, const TValue* ek) totaluse++; /* compute new size for array part */ int na = computesizes(nums, &nasize); + /* enforce the boundary invariant; for performance, only do hash lookups if we must */ + if (FFlag::LuauTableNewBoundary) + { + bool tbound = t->node != dummynode || nasize < t->sizearray; + int ekindex = ttisnumber(ek) ? arrayindex(nvalue(ek)) : -1; + /* move the array size up until the boundary is guaranteed to be inside the array part */ + while (nasize + 1 == ekindex || (tbound && !ttisnil(luaH_getnum(t, nasize + 1)))) + { + nasize++; + na++; + } + } /* resize the table to new computed sizes */ + LUAU_ASSERT(na <= totaluse); resize(L, t, nasize, totaluse - na); } @@ -520,14 +543,22 @@ static LuaNode* getfreepos(Table* t) */ static TValue* newkey(lua_State* L, Table* t, const TValue* key) { + /* enforce boundary invariant */ + if (FFlag::LuauTableNewBoundary && ttisnumber(key) && nvalue(key) == t->sizearray + 1) + { + rehash(L, t, key); /* grow table */ + + // after rehash, numeric keys might be located in the new array part, but won't be found in the node part + return arrayornewkey(L, t, key); + } + LuaNode* mp = mainposition(t, key); if (!ttisnil(gval(mp)) || mp == dummynode) { - LuaNode* othern; LuaNode* n = getfreepos(t); /* get a free place */ if (n == NULL) - { /* cannot find a free place? */ - rehash(L, t, key); /* grow table */ + { /* cannot find a free place? */ + rehash(L, t, key); /* grow table */ if (!FFlag::LuauTableRehashRework) { @@ -542,7 +573,7 @@ static TValue* newkey(lua_State* L, Table* t, const TValue* key) LUAU_ASSERT(n != dummynode); TValue mk; getnodekey(L, &mk, mp); - othern = mainposition(t, &mk); + LuaNode* othern = mainposition(t, &mk); if (othern != mp) { /* is colliding node out of its main position? */ /* yes; move colliding node into free position */ @@ -704,6 +735,7 @@ TValue* luaH_setstr(lua_State* L, Table* t, TString* key) static LUAU_NOINLINE int unbound_search(Table* t, unsigned int j) { + LUAU_ASSERT(!FFlag::LuauTableNewBoundary); unsigned int i = j; /* i is zero or a present index */ j++; /* find `i' and `j' such that i is present and j is not */ @@ -788,6 +820,12 @@ int luaH_getn(Table* t) maybesetaboundary(t, boundary); return boundary; } + else if (FFlag::LuauTableNewBoundary) + { + /* validate boundary invariant */ + LUAU_ASSERT(t->node == dummynode || ttisnil(luaH_getnum(t, j + 1))); + return j; + } /* else must find a boundary in hash part */ else if (t->node == dummynode) /* hash part is empty? */ return j; /* that is easy... */ diff --git a/luau/VM/src/ltablib.cpp b/luau/VM/src/ltablib.cpp index 0075374..241a99e 100644 --- a/luau/VM/src/ltablib.cpp +++ b/luau/VM/src/ltablib.cpp @@ -10,7 +10,9 @@ #include "ldebug.h" #include "lvm.h" -LUAU_FASTFLAGVARIABLE(LuauTableClone, false) +LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauTableMoveTelemetry2, false) + +void (*lua_table_move_telemetry)(lua_State* L, int f, int e, int t, int nf, int nt); static int foreachi(lua_State* L) { @@ -197,6 +199,29 @@ static int tmove(lua_State* L) int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */ luaL_checktype(L, tt, LUA_TTABLE); + void (*telemetrycb)(lua_State* L, int f, int e, int t, int nf, int nt) = lua_table_move_telemetry; + + if (DFFlag::LuauTableMoveTelemetry2 && telemetrycb) + { + int nf = lua_objlen(L, 1); + int nt = lua_objlen(L, tt); + + bool report = false; + + // source index range must be in bounds in source table unless the table is empty (permits 1..#t moves) + if (!(f == 1 || (f >= 1 && f <= nf))) + report = true; + if (!(e == nf || (e >= 1 && e <= nf))) + report = true; + + // destination index must be in bounds in dest table or be exactly at the first empty element (permits concats) + if (!(t == nt + 1 || (t >= 1 && t <= nt))) + report = true; + + if (report) + telemetrycb(L, f, e, t, nf, nt); + } + if (e >= f) { /* otherwise, nothing to move */ luaL_argcheck(L, f > 0 || e < INT_MAX + f, 3, "too many elements to move"); @@ -512,9 +537,6 @@ static int tisfrozen(lua_State* L) static int tclone(lua_State* L) { - if (!FFlag::LuauTableClone) - luaG_runerror(L, "table.clone is not available"); - luaL_checktype(L, 1, LUA_TTABLE); luaL_argcheck(L, !luaL_getmetafield(L, 1, "__metatable"), 1, "table has a protected metatable"); diff --git a/luau/VM/src/lvmexecute.cpp b/luau/VM/src/lvmexecute.cpp index 96a87b7..34949ef 100644 --- a/luau/VM/src/lvmexecute.cpp +++ b/luau/VM/src/lvmexecute.cpp @@ -16,6 +16,8 @@ #include +LUAU_FASTFLAG(LuauTableNewBoundary) + // Disable c99-designator to avoid the warning in CGOTO dispatch table #ifdef __clang__ #if __has_warning("-Wc99-designator") @@ -2266,9 +2268,9 @@ static void luau_execute(lua_State* L) VM_NEXT(); } } - else if (h->lsizenode == 0 && ttisnil(gval(h->node))) + else if (FFlag::LuauTableNewBoundary || (h->lsizenode == 0 && ttisnil(gval(h->node)))) { - // hash part is empty: fallthrough to exit + // fallthrough to exit VM_NEXT(); } else diff --git a/luau/VM/src/lvmload.cpp b/luau/VM/src/lvmload.cpp index 4e5435b..8b742f1 100644 --- a/luau/VM/src/lvmload.cpp +++ b/luau/VM/src/lvmload.cpp @@ -13,8 +13,6 @@ #include -LUAU_FASTFLAGVARIABLE(LuauBytecodeV2Force, false) - // TODO: RAII deallocation doesn't work for longjmp builds if a memory error happens template struct TempBuffer @@ -156,12 +154,11 @@ int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size return 1; } - if (FFlag::LuauBytecodeV2Force ? (version != LBC_VERSION_FUTURE) : (version != LBC_VERSION && version != LBC_VERSION_FUTURE)) + if (version != LBC_VERSION) { char chunkid[LUA_IDSIZE]; luaO_chunkid(chunkid, chunkname, LUA_IDSIZE); - lua_pushfstring(L, "%s: bytecode version mismatch (expected %d, got %d)", chunkid, - FFlag::LuauBytecodeV2Force ? LBC_VERSION_FUTURE : LBC_VERSION, version); + lua_pushfstring(L, "%s: bytecode version mismatch (expected %d, got %d)", chunkid, LBC_VERSION, version); return 1; } @@ -292,11 +289,7 @@ int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size p->p[j] = protos[fid]; } - if (FFlag::LuauBytecodeV2Force || version == LBC_VERSION_FUTURE) - p->linedefined = readVarInt(data, size, offset); - else - p->linedefined = -1; - + p->linedefined = readVarInt(data, size, offset); p->debugname = readString(strings, data, size, offset); uint8_t lineinfo = read(data, size, offset); diff --git a/src/lib.rs b/src/lib.rs index 3a77a39..0bb08ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,7 +111,6 @@ impl Build { .clone() .include(&vm_include_dir) .define("LUA_API", "extern \"C\"") - // Works 9 times slower, see https://github.com/Roblox/luau/issues/425 // .define("LUA_USE_LONGJMP", "1") .add_files_by_ext(&vm_source_dir, "cpp") .out_dir(&lib_dir)