v0.2.2+luau521

This commit is contained in:
Alex Orlenko 2022-03-31 23:20:58 +01:00
parent d9a2a46c68
commit 4e923b679b
No known key found for this signature in database
GPG Key ID: 4C150C250863B96D
23 changed files with 320 additions and 293 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "luau0-src" name = "luau0-src"
version = "0.2.1+luau519" version = "0.2.2+luau521"
authors = ["Aleksandr Orlenko <zxteam@protonmail.com>"] authors = ["Aleksandr Orlenko <zxteam@protonmail.com>"]
edition = "2018" edition = "2018"
repository = "https://github.com/khvzak/luau-src-rs" repository = "https://github.com/khvzak/luau-src-rs"

View File

@ -130,8 +130,8 @@ ThreadContext& getThreadContext();
struct Scope struct Scope
{ {
explicit Scope(ThreadContext& context, uint16_t token) explicit Scope(uint16_t token)
: context(context) : context(getThreadContext())
{ {
if (!FFlag::DebugLuauTimeTracing) if (!FFlag::DebugLuauTimeTracing)
return; return;
@ -152,8 +152,8 @@ struct Scope
struct OptionalTailScope struct OptionalTailScope
{ {
explicit OptionalTailScope(ThreadContext& context, uint16_t token, uint32_t threshold) explicit OptionalTailScope(uint16_t token, uint32_t threshold)
: context(context) : context(getThreadContext())
, token(token) , token(token)
, threshold(threshold) , threshold(threshold)
{ {
@ -188,27 +188,27 @@ struct OptionalTailScope
uint32_t pos; uint32_t pos;
}; };
LUAU_NOINLINE std::pair<uint16_t, Luau::TimeTrace::ThreadContext&> createScopeData(const char* name, const char* category); LUAU_NOINLINE uint16_t createScopeData(const char* name, const char* category);
} // namespace TimeTrace } // namespace TimeTrace
} // namespace Luau } // namespace Luau
// Regular scope // Regular scope
#define LUAU_TIMETRACE_SCOPE(name, category) \ #define LUAU_TIMETRACE_SCOPE(name, category) \
static auto lttScopeStatic = Luau::TimeTrace::createScopeData(name, category); \ static uint16_t lttScopeStatic = Luau::TimeTrace::createScopeData(name, category); \
Luau::TimeTrace::Scope lttScope(lttScopeStatic.second, lttScopeStatic.first) Luau::TimeTrace::Scope lttScope(lttScopeStatic)
// A scope without nested scopes that may be skipped if the time it took is less than the threshold // 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) \ #define LUAU_TIMETRACE_OPTIONAL_TAIL_SCOPE(name, category, microsec) \
static auto lttScopeStaticOptTail = Luau::TimeTrace::createScopeData(name, category); \ static uint16_t lttScopeStaticOptTail = Luau::TimeTrace::createScopeData(name, category); \
Luau::TimeTrace::OptionalTailScope lttScope(lttScopeStaticOptTail.second, lttScopeStaticOptTail.first, microsec) Luau::TimeTrace::OptionalTailScope lttScope(lttScopeStaticOptTail, microsec)
// Extra key/value data can be added to regular scopes // Extra key/value data can be added to regular scopes
#define LUAU_TIMETRACE_ARGUMENT(name, value) \ #define LUAU_TIMETRACE_ARGUMENT(name, value) \
do \ do \
{ \ { \
if (FFlag::DebugLuauTimeTracing) \ if (FFlag::DebugLuauTimeTracing) \
lttScopeStatic.second.eventArgument(name, value); \ lttScope.context.eventArgument(name, value); \
} while (false) } while (false)
#else #else

View File

@ -6,6 +6,8 @@
#include <limits.h> #include <limits.h>
LUAU_FASTFLAGVARIABLE(LuauParseLocationIgnoreCommentSkip, false)
namespace Luau namespace Luau
{ {
@ -352,6 +354,8 @@ const Lexeme& Lexer::next()
const Lexeme& Lexer::next(bool skipComments) const Lexeme& Lexer::next(bool skipComments)
{ {
bool first = true;
// in skipComments mode we reject valid comments // in skipComments mode we reject valid comments
do do
{ {
@ -359,9 +363,11 @@ const Lexeme& Lexer::next(bool skipComments)
while (isSpace(peekch())) while (isSpace(peekch()))
consume(); consume();
prevLocation = lexeme.location; if (!FFlag::LuauParseLocationIgnoreCommentSkip || first)
prevLocation = lexeme.location;
lexeme = readNext(); lexeme = readNext();
first = false;
} while (skipComments && (lexeme.type == Lexeme::Comment || lexeme.type == Lexeme::BlockComment)); } while (skipComments && (lexeme.type == Lexeme::Comment || lexeme.type == Lexeme::BlockComment));
return lexeme; return lexeme;

View File

@ -10,8 +10,6 @@
// See docs/SyntaxChanges.md for an explanation. // See docs/SyntaxChanges.md for an explanation.
LUAU_FASTINTVARIABLE(LuauRecursionLimit, 1000) LUAU_FASTINTVARIABLE(LuauRecursionLimit, 1000)
LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100) LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
LUAU_FASTFLAGVARIABLE(LuauParseSingletonTypes, false)
LUAU_FASTFLAGVARIABLE(LuauTableFieldFunctionDebugname, false)
namespace Luau namespace Luau
{ {
@ -1233,8 +1231,7 @@ AstType* Parser::parseTableTypeAnnotation()
while (lexer.current().type != '}') while (lexer.current().type != '}')
{ {
if (FFlag::LuauParseSingletonTypes && lexer.current().type == '[' && if (lexer.current().type == '[' && (lexer.lookahead().type == Lexeme::RawString || lexer.lookahead().type == Lexeme::QuotedString))
(lexer.lookahead().type == Lexeme::RawString || lexer.lookahead().type == Lexeme::QuotedString))
{ {
const Lexeme begin = lexer.current(); const Lexeme begin = lexer.current();
nextLexeme(); // [ nextLexeme(); // [
@ -1500,17 +1497,17 @@ AstTypeOrPack Parser::parseSimpleTypeAnnotation(bool allowPack)
nextLexeme(); nextLexeme();
return {allocator.alloc<AstTypeReference>(begin, std::nullopt, nameNil), {}}; return {allocator.alloc<AstTypeReference>(begin, std::nullopt, nameNil), {}};
} }
else if (FFlag::LuauParseSingletonTypes && lexer.current().type == Lexeme::ReservedTrue) else if (lexer.current().type == Lexeme::ReservedTrue)
{ {
nextLexeme(); nextLexeme();
return {allocator.alloc<AstTypeSingletonBool>(begin, true)}; return {allocator.alloc<AstTypeSingletonBool>(begin, true)};
} }
else if (FFlag::LuauParseSingletonTypes && lexer.current().type == Lexeme::ReservedFalse) else if (lexer.current().type == Lexeme::ReservedFalse)
{ {
nextLexeme(); nextLexeme();
return {allocator.alloc<AstTypeSingletonBool>(begin, false)}; return {allocator.alloc<AstTypeSingletonBool>(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<AstArray<char>> value = parseCharArray()) if (std::optional<AstArray<char>> value = parseCharArray())
{ {
@ -1520,7 +1517,7 @@ AstTypeOrPack Parser::parseSimpleTypeAnnotation(bool allowPack)
else else
return {reportTypeAnnotationError(begin, {}, /*isMissing*/ false, "String literal contains malformed escape sequence")}; 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; Location location = lexer.current().location;
nextLexeme(); nextLexeme();
@ -2189,11 +2186,8 @@ AstExpr* Parser::parseTableConstructor()
AstExpr* key = allocator.alloc<AstExprConstantString>(name.location, nameString); AstExpr* key = allocator.alloc<AstExprConstantString>(name.location, nameString);
AstExpr* value = parseExpr(); AstExpr* value = parseExpr();
if (FFlag::LuauTableFieldFunctionDebugname) if (AstExprFunction* func = value->as<AstExprFunction>())
{ func->debugname = name.name;
if (AstExprFunction* func = value->as<AstExprFunction>())
func->debugname = name.name;
}
items.push_back({AstExprTable::Item::Record, key, value}); items.push_back({AstExprTable::Item::Record, key, value});
} }

View File

@ -246,10 +246,9 @@ ThreadContext& getThreadContext()
return context; return context;
} }
std::pair<uint16_t, Luau::TimeTrace::ThreadContext&> 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 createToken(Luau::TimeTrace::getGlobalContext(), name, category);
return {token, Luau::TimeTrace::getThreadContext()};
} }
} // namespace TimeTrace } // namespace TimeTrace
} // namespace Luau } // namespace Luau

View File

@ -376,8 +376,7 @@ enum LuauOpcode
enum LuauBytecodeTag enum LuauBytecodeTag
{ {
// Bytecode version // Bytecode version
LBC_VERSION = 1, LBC_VERSION = 2,
LBC_VERSION_FUTURE = 2, // TODO: This will be removed in favor of LBC_VERSION with LuauBytecodeV2Force
// Types of constant table entries // Types of constant table entries
LBC_CONSTANT_NIL = 0, LBC_CONSTANT_NIL = 0,
LBC_CONSTANT_BOOLEAN, LBC_CONSTANT_BOOLEAN,

View File

@ -508,7 +508,7 @@ uint32_t BytecodeBuilder::getDebugPC() const
void BytecodeBuilder::finalize() void BytecodeBuilder::finalize()
{ {
LUAU_ASSERT(bytecode.empty()); LUAU_ASSERT(bytecode.empty());
bytecode = char(LBC_VERSION_FUTURE); bytecode = char(LBC_VERSION);
writeStringTable(bytecode); writeStringTable(bytecode);

View File

@ -14,8 +14,6 @@
#include <string.h> #include <string.h>
LUAU_FASTFLAG(LuauGcAdditionalStats)
const char* lua_ident = "$Lua: Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio $\n" 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" "$Authors: R. Ierusalimschy, L. H. de Figueiredo & W. Celes $\n"
"$URL: www.lua.org $\n"; "$URL: www.lua.org $\n";
@ -1060,8 +1058,11 @@ int lua_gc(lua_State* L, int what, int data)
g->GCthreshold = 0; g->GCthreshold = 0;
bool waspaused = g->gcstate == GCSpause; 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 // track how much work the loop will actually perform
size_t actualwork = 0; 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 cyclemetrics->markexplicitsteps++;
GCCycleStats* cyclestats = g->gcstate == GCSpause ? &g->gcstats.lastcycle : &g->gcstats.currcycle;
double totalmarktime = cyclestats->marktime - startmarktime; if (totalmarktime > cyclemetrics->markmaxexplicittime)
double totalsweeptime = cyclestats->sweeptime - startsweeptime; cyclemetrics->markmaxexplicittime = totalmarktime;
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 (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 cycle hasn't finished, advance threshold forward for the amount of extra work performed
if (g->gcstate != GCSpause) if (g->gcstate != GCSpause)
{ {

View File

@ -11,8 +11,6 @@
#include <string.h> #include <string.h>
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauMorePreciseLuaLTypeName, false)
/* convert a stack index to positive */ /* convert a stack index to positive */
#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1) #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) const char* luaL_typename(lua_State* L, int idx)
{ {
if (DFFlag::LuauMorePreciseLuaLTypeName) const TValue* obj = luaA_toobject(L, idx);
{ return luaT_objtypename(L, obj);
const TValue* obj = luaA_toobject(L, idx);
return luaT_objtypename(L, obj);
}
else
{
return lua_typename(L, lua_type(L, idx));
}
} }
/* /*

View File

@ -11,8 +11,6 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
LUAU_DYNAMIC_FASTFLAG(LuauMorePreciseLuaLTypeName)
static void writestring(const char* s, size_t l) static void writestring(const char* s, size_t l)
{ {
fwrite(s, 1, l, stdout); fwrite(s, 1, l, stdout);
@ -189,31 +187,16 @@ static int luaB_gcinfo(lua_State* L)
static int luaB_type(lua_State* L) static int luaB_type(lua_State* L)
{ {
luaL_checkany(L, 1); 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)));
/* 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));
}
return 1; return 1;
} }
static int luaB_typeof(lua_State* L) static int luaB_typeof(lua_State* L)
{ {
luaL_checkany(L, 1); 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));
/* 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));
}
return 1; return 1;
} }

View File

@ -12,8 +12,6 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
LUAU_FASTFLAG(LuauBytecodeV2Force)
static const char* getfuncname(Closure* f); static const char* getfuncname(Closure* f);
static int currentpc(lua_State* L, CallInfo* ci) 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; 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) static int auxgetinfo(lua_State* L, const char* what, lua_Debug* ar, Closure* f, CallInfo* ci)
{ {
int status = 1; 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->source = getstr(f->l.p->source);
ar->what = "Lua"; ar->what = "Lua";
ar->linedefined = getlinedefined(f->l.p); ar->linedefined = f->l.p->linedefined;
} }
luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);
break; break;
@ -133,7 +121,7 @@ static int auxgetinfo(lua_State* L, const char* what, lua_Debug* ar, Closure* f,
} }
else else
{ {
ar->currentline = f->isC ? -1 : getlinedefined(f->l.p); ar->currentline = f->isC ? -1 : f->l.p->linedefined;
} }
break; 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; const char* debugname = p->debugname ? getstr(p->debugname) : NULL;
int linedefined = getlinedefined(p); int linedefined = p->linedefined;
callback(context, debugname, linedefined, depth, buffer, size); callback(context, debugname, linedefined, depth, buffer, size);

View File

@ -17,8 +17,6 @@
#include <string.h> #include <string.h>
LUAU_FASTFLAG(LuauReduceStackReallocs)
/* /*
** {====================================================== ** {======================================================
** Error-recovery functions ** Error-recovery functions
@ -33,6 +31,15 @@ struct lua_jmpbuf
jmp_buf buf; 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) int luaD_rawrunprotected(lua_State* L, Pfunc f, void* ud)
{ {
lua_jmpbuf jb; lua_jmpbuf jb;
@ -40,7 +47,7 @@ int luaD_rawrunprotected(lua_State* L, Pfunc f, void* ud)
jb.status = 0; jb.status = 0;
L->global->errorjmp = &jb; L->global->errorjmp = &jb;
if (setjmp(jb.buf) == 0) if (LUAU_SETJMP(jb.buf) == 0)
f(L, ud); f(L, ud);
L->global->errorjmp = jb.prev; 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) if (lua_jmpbuf* jb = L->global->errorjmp)
{ {
jb->status = errcode; jb->status = errcode;
longjmp(jb->buf, 1); LUAU_LONGJMP(jb->buf, 1);
} }
if (L->global->cb.panic) 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) void luaD_reallocstack(lua_State* L, int newsize)
{ {
TValue* oldstack = L->stack; TValue* oldstack = L->stack;
int realsize = newsize + (FFlag::LuauReduceStackReallocs ? EXTRA_STACK : 1 + EXTRA_STACK); int realsize = newsize + EXTRA_STACK;
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);
luaM_reallocarray(L, L->stack, L->stacksize, realsize, TValue, L->memcat); luaM_reallocarray(L, L->stack, L->stacksize, realsize, TValue, L->memcat);
TValue* newstack = L->stack; TValue* newstack = L->stack;
for (int i = L->stacksize; i < realsize; i++) 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) 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) if (L->size_ci > LUAI_MAXCALLS)
{ /* there was an overflow? */ { /* there was an overflow? */
int inuse = cast_int(L->ci - L->base_ci); int inuse = cast_int(L->ci - L->base_ci);

View File

@ -11,7 +11,7 @@
if ((char*)L->stack_last - (char*)L->top <= (n) * (int)sizeof(TValue)) \ if ((char*)L->stack_last - (char*)L->top <= (n) * (int)sizeof(TValue)) \
luaD_growstack(L, n); \ luaD_growstack(L, n); \
else \ 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) \ #define incr_top(L) \
{ \ { \

View File

@ -11,8 +11,6 @@
#include "lmem.h" #include "lmem.h"
#include "ludata.h" #include "ludata.h"
LUAU_FASTFLAGVARIABLE(LuauGcAdditionalStats, false)
#include <string.h> #include <string.h>
#define GC_SWEEPMAX 40 #define GC_SWEEPMAX 40
@ -48,7 +46,8 @@ LUAU_FASTFLAGVARIABLE(LuauGcAdditionalStats, false)
reallymarkobject(g, obj2gco(t)); \ 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) 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 // record root mark time if we have switched to next state
if (g->gcstate == GCSpropagate) if (g->gcstate == GCSpropagate)
{ {
g->gcstats.currcycle.marktime += seconds; g->gcmetrics.currcycle.marktime += seconds;
if (FFlag::LuauGcAdditionalStats && assist) if (assist)
g->gcstats.currcycle.markassisttime += seconds; g->gcmetrics.currcycle.markassisttime += seconds;
} }
break; break;
case GCSpropagate: case GCSpropagate:
case GCSpropagateagain: case GCSpropagateagain:
g->gcstats.currcycle.marktime += seconds; g->gcmetrics.currcycle.marktime += seconds;
g->gcmetrics.currcycle.markrequests += g->gcstepsize;
if (FFlag::LuauGcAdditionalStats && assist) if (assist)
g->gcstats.currcycle.markassisttime += seconds; g->gcmetrics.currcycle.markassisttime += seconds;
break; break;
case GCSatomic: case GCSatomic:
g->gcstats.currcycle.atomictime += seconds; g->gcmetrics.currcycle.atomictime += seconds;
break; break;
case GCSsweep: case GCSsweep:
g->gcstats.currcycle.sweeptime += seconds; g->gcmetrics.currcycle.sweeptime += seconds;
g->gcmetrics.currcycle.sweeprequests += g->gcstepsize;
if (FFlag::LuauGcAdditionalStats && assist) if (assist)
g->gcstats.currcycle.sweepassisttime += seconds; g->gcmetrics.currcycle.sweepassisttime += seconds;
break; break;
default: default:
LUAU_ASSERT(!"Unexpected GC state"); LUAU_ASSERT(!"Unexpected GC state");
} }
if (assist) if (assist)
g->gcstats.stepassisttimeacc += seconds; {
g->gcmetrics.stepassisttimeacc += seconds;
g->gcmetrics.currcycle.assistwork += work;
g->gcmetrics.currcycle.assistrequests += g->gcstepsize;
}
else 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(); double now = lua_clock();
g->gcstats.currcycle.pausetime = g->gcstats.currcycle.starttimestamp - g->gcstats.lastcycle.endtimestamp; 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->gcmetrics.currcycle.starttimestamp = lua_clock();
g->gcstats.currcycle.endtotalsizebytes = g->totalbytes; g->gcmetrics.currcycle.pausetime = g->gcmetrics.currcycle.starttimestamp - g->gcmetrics.lastcycle.endtimestamp;
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;
} }
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) static void removeentry(LuaNode* n)
{ {
LUAU_ASSERT(ttisnil(gval(n))); LUAU_ASSERT(ttisnil(gval(n)));
@ -598,20 +615,19 @@ static size_t atomic(lua_State* L)
LUAU_ASSERT(g->gcstate == GCSatomic); LUAU_ASSERT(g->gcstate == GCSatomic);
size_t work = 0; size_t work = 0;
#ifdef LUAI_GCMETRICS
double currts = lua_clock(); double currts = lua_clock();
double prevts = currts; #endif
/* remark occasional upvalues of (maybe) dead threads */ /* remark occasional upvalues of (maybe) dead threads */
work += remarkupvals(g); work += remarkupvals(g);
/* traverse objects caught by write barrier and by 'remarkupvals' */ /* traverse objects caught by write barrier and by 'remarkupvals' */
work += propagateall(g); work += propagateall(g);
if (FFlag::LuauGcAdditionalStats) #ifdef LUAI_GCMETRICS
{ g->gcmetrics.currcycle.atomictimeupval += recordGcDeltaTime(currts);
currts = lua_clock(); #endif
g->gcstats.currcycle.atomictimeupval += currts - prevts;
prevts = currts;
}
/* remark weak tables */ /* remark weak tables */
g->gray = g->weak; g->gray = g->weak;
@ -621,34 +637,26 @@ static size_t atomic(lua_State* L)
markmt(g); /* mark basic metatables (again) */ markmt(g); /* mark basic metatables (again) */
work += propagateall(g); work += propagateall(g);
if (FFlag::LuauGcAdditionalStats) #ifdef LUAI_GCMETRICS
{ g->gcmetrics.currcycle.atomictimeweak += recordGcDeltaTime(currts);
currts = lua_clock(); #endif
g->gcstats.currcycle.atomictimeweak += currts - prevts;
prevts = currts;
}
/* remark gray again */ /* remark gray again */
g->gray = g->grayagain; g->gray = g->grayagain;
g->grayagain = NULL; g->grayagain = NULL;
work += propagateall(g); work += propagateall(g);
if (FFlag::LuauGcAdditionalStats) #ifdef LUAI_GCMETRICS
{ g->gcmetrics.currcycle.atomictimegray += recordGcDeltaTime(currts);
currts = lua_clock(); #endif
g->gcstats.currcycle.atomictimegray += currts - prevts;
prevts = currts;
}
/* remove collected objects from weak tables */ /* remove collected objects from weak tables */
work += cleartable(L, g->weak); work += cleartable(L, g->weak);
g->weak = NULL; g->weak = NULL;
if (FFlag::LuauGcAdditionalStats) #ifdef LUAI_GCMETRICS
{ g->gcmetrics.currcycle.atomictimeclear += recordGcDeltaTime(currts);
currts = lua_clock(); #endif
g->gcstats.currcycle.atomictimeclear += currts - prevts;
}
/* flip current white */ /* flip current white */
g->currentwhite = cast_byte(otherwhite(g)); g->currentwhite = cast_byte(otherwhite(g));
@ -742,8 +750,9 @@ static size_t gcstep(lua_State* L, size_t limit)
if (!g->gray) if (!g->gray)
{ {
if (FFlag::LuauGcAdditionalStats) #ifdef LUAI_GCMETRICS
g->gcstats.currcycle.propagatework = g->gcstats.currcycle.explicitwork + g->gcstats.currcycle.assistwork; g->gcmetrics.currcycle.propagatework = g->gcmetrics.currcycle.explicitwork + g->gcmetrics.currcycle.assistwork;
#endif
// perform one iteration over 'gray again' list // perform one iteration over 'gray again' list
g->gray = g->grayagain; 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 (!g->gray) /* no more `gray' objects */
{ {
if (FFlag::LuauGcAdditionalStats) #ifdef LUAI_GCMETRICS
g->gcstats.currcycle.propagateagainwork = g->gcmetrics.currcycle.propagateagainwork =
g->gcstats.currcycle.explicitwork + g->gcstats.currcycle.assistwork - g->gcstats.currcycle.propagatework; g->gcmetrics.currcycle.explicitwork + g->gcmetrics.currcycle.assistwork - g->gcmetrics.currcycle.propagatework;
#endif
g->gcstate = GCSatomic; g->gcstate = GCSatomic;
} }
@ -772,8 +782,13 @@ static size_t gcstep(lua_State* L, size_t limit)
} }
case GCSatomic: case GCSatomic:
{ {
g->gcstats.currcycle.atomicstarttimestamp = lua_clock(); #ifdef LUAI_GCMETRICS
g->gcstats.currcycle.atomicstarttotalsizebytes = g->totalbytes; 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 */ cost = atomic(L); /* finish mark phase */
@ -809,18 +824,20 @@ static size_t gcstep(lua_State* L, size_t limit)
return cost; return cost;
} }
static int64_t getheaptriggererroroffset(GCHeapTriggerStats* triggerstats, GCCycleStats* cyclestats) static int64_t getheaptriggererroroffset(global_State* g)
{ {
// adjust for error using Proportional-Integral controller // adjust for error using Proportional-Integral controller
// https://en.wikipedia.org/wiki/PID_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 // 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; int32_t prev = *slot;
*slot = errorKb; *slot = errorKb;
triggerstats->integral += errorKb - prev; g->gcstats.triggerintegral += errorKb - prev;
triggerstats->termpos++; g->gcstats.triggertermpos++;
// controller tuning // controller tuning
// https://en.wikipedia.org/wiki/Ziegler%E2%80%93Nichols_method // 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 const double Ki = 0.54 * Ku / Ti; // integral gain
double proportionalTerm = Kp * errorKb; double proportionalTerm = Kp * errorKb;
double integralTerm = Ki * triggerstats->integral; double integralTerm = Ki * g->gcstats.triggerintegral;
double totalTerm = proportionalTerm + integralTerm; 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) 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 // 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 // our goal is to begin the sweep when used memory has reached the heap goal
const double durationthreshold = 1e-3; 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 // avoid measuring intervals smaller than 1ms
if (allocationduration < durationthreshold) if (allocationduration < durationthreshold)
return heapgoal; return heapgoal;
double allocationrate = (currcycle->atomicstarttotalsizebytes - lastcycle->endtotalsizebytes) / allocationduration; double allocationrate = (g->gcstats.atomicstarttotalsizebytes - g->gcstats.endtotalsizebytes) / allocationduration;
double markduration = currcycle->atomicstarttimestamp - currcycle->starttimestamp; double markduration = g->gcstats.atomicstarttimestamp - g->gcstats.starttimestamp;
int64_t expectedgrowth = int64_t(markduration * allocationrate); 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); int64_t heaptrigger = heapgoal - (expectedgrowth + offset);
// clamp the trigger between memory use at the end of the cycle and the heap goal // 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; 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 */ int lim = (g->gcstepsize / 100) * g->gcstepmul; /* how much to work */
LUAU_ASSERT(g->totalbytes >= g->GCthreshold); LUAU_ASSERT(g->totalbytes >= g->GCthreshold);
size_t debt = 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 // at the start of the new cycle
if (g->gcstate == GCSpause) 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; int lastgcstate = g->gcstate;
double lasttimestamp = lua_clock();
size_t work = gcstep(L, lim); size_t work = gcstep(L, lim);
(void)work;
if (assist) #ifdef LUAI_GCMETRICS
g->gcstats.currcycle.assistwork += work; recordGcStateStep(g, lastgcstate, lua_clock() - lasttimestamp, assist, work);
else #endif
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;
// at the end of the last cycle // at the end of the last cycle
if (g->gcstate == GCSpause) if (g->gcstate == GCSpause)
@ -909,13 +917,13 @@ void luaC_step(lua_State* L, bool assist)
g->GCthreshold = heaptrigger; g->GCthreshold = heaptrigger;
finishGcCycleStats(g); g->gcstats.heapgoalsizebytes = heapgoal;
g->gcstats.endtimestamp = lua_clock();
g->gcstats.endtotalsizebytes = g->totalbytes;
if (FFlag::LuauGcAdditionalStats) #ifdef LUAI_GCMETRICS
g->gcstats.currcycle.starttotalsizebytes = g->totalbytes; finishGcCycleMetrics(g);
#endif
g->gcstats.currcycle.heapgoalsizebytes = heapgoal;
g->gcstats.currcycle.heaptriggersizebytes = heaptrigger;
} }
else else
{ {
@ -933,8 +941,10 @@ void luaC_fullgc(lua_State* L)
{ {
global_State* g = L->global; global_State* g = L->global;
#ifdef LUAI_GCMETRICS
if (g->gcstate == GCSpause) if (g->gcstate == GCSpause)
startGcCycleStats(g); startGcCycleMetrics(g);
#endif
if (g->gcstate <= GCSatomic) if (g->gcstate <= GCSatomic)
{ {
@ -954,11 +964,12 @@ void luaC_fullgc(lua_State* L)
gcstep(L, SIZE_MAX); gcstep(L, SIZE_MAX);
} }
finishGcCycleStats(g); #ifdef LUAI_GCMETRICS
finishGcCycleMetrics(g);
startGcCycleMetrics(g);
#endif
/* run a full collection cycle */ /* run a full collection cycle */
startGcCycleStats(g);
markroot(L); markroot(L);
while (g->gcstate != GCSpause) while (g->gcstate != GCSpause)
{ {
@ -980,10 +991,11 @@ void luaC_fullgc(lua_State* L)
if (g->GCthreshold < g->totalbytes) if (g->GCthreshold < g->totalbytes)
g->GCthreshold = g->totalbytes; g->GCthreshold = g->totalbytes;
finishGcCycleStats(g); g->gcstats.heapgoalsizebytes = heapgoalsizebytes;
g->gcstats.currcycle.heapgoalsizebytes = heapgoalsizebytes; #ifdef LUAI_GCMETRICS
g->gcstats.currcycle.heaptriggersizebytes = g->GCthreshold; finishGcCycleMetrics(g);
#endif
} }
void luaC_barrierupval(lua_State* L, GCObject* v) void luaC_barrierupval(lua_State* L, GCObject* v)
@ -1075,21 +1087,21 @@ int64_t luaC_allocationrate(lua_State* L)
if (g->gcstate <= GCSatomic) if (g->gcstate <= GCSatomic)
{ {
double duration = lua_clock() - g->gcstats.lastcycle.endtimestamp; double duration = lua_clock() - g->gcstats.endtimestamp;
if (duration < durationthreshold) if (duration < durationthreshold)
return -1; 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 // 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) if (duration < durationthreshold)
return -1; 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) void luaC_wakethread(lua_State* L)

View File

@ -82,7 +82,7 @@
#define luaC_checkGC(L) \ #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) \ if (L->global->totalbytes >= L->global->GCthreshold) \
{ \ { \
condhardmemtests(luaC_validate(L), 1); \ condhardmemtests(luaC_validate(L), 1); \

View File

@ -10,12 +10,12 @@ union GCObject;
#define luaM_newgco(L, t, size, memcat) cast_to(t*, luaM_newgco_(L, size, memcat)) #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_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_freearray(L, b, n, t, memcat) luaM_free_(L, (b), (n) * sizeof(t), memcat)
#define luaM_reallocarray(L, v, oldn, n, 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 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); LUAI_FUNC GCObject* luaM_newgco_(lua_State* L, size_t nsize, uint8_t memcat);

View File

@ -10,8 +10,6 @@
#include "ldo.h" #include "ldo.h"
#include "ldebug.h" #include "ldebug.h"
LUAU_FASTFLAGVARIABLE(LuauReduceStackReallocs, false)
/* /*
** Main thread combines a thread state and the global state ** 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++) for (int i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
setnilvalue(stack + i); /* erase new stack */ setnilvalue(stack + i); /* erase new stack */
L1->top = 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 */ /* initialize first ci */
L1->ci->func = L1->top; L1->ci->func = L1->top;
setnilvalue(L1->top++); /* `function' entry for this `ci' */ setnilvalue(L1->top++); /* `function' entry for this `ci' */
@ -141,30 +139,16 @@ void lua_resetthread(lua_State* L)
ci->top = ci->base + LUA_MINSTACK; ci->top = ci->base + LUA_MINSTACK;
setnilvalue(ci->func); setnilvalue(ci->func);
L->ci = ci; L->ci = ci;
if (FFlag::LuauReduceStackReallocs) if (L->size_ci != BASIC_CI_SIZE)
{
if (L->size_ci != BASIC_CI_SIZE)
luaD_reallocCI(L, BASIC_CI_SIZE);
}
else
{
luaD_reallocCI(L, BASIC_CI_SIZE); luaD_reallocCI(L, BASIC_CI_SIZE);
}
/* clear thread state */ /* clear thread state */
L->status = LUA_OK; L->status = LUA_OK;
L->base = L->ci->base; L->base = L->ci->base;
L->top = L->ci->base; L->top = L->ci->base;
L->nCcalls = L->baseCcalls = 0; L->nCcalls = L->baseCcalls = 0;
/* clear thread stack */ /* clear thread stack */
if (FFlag::LuauReduceStackReallocs) if (L->stacksize != BASIC_STACK_SIZE + EXTRA_STACK)
{
if (L->stacksize != BASIC_STACK_SIZE + EXTRA_STACK)
luaD_reallocstack(L, BASIC_STACK_SIZE);
}
else
{
luaD_reallocstack(L, BASIC_STACK_SIZE); luaD_reallocstack(L, BASIC_STACK_SIZE);
}
for (int i = 0; i < L->stacksize; i++) for (int i = 0; i < L->stacksize; i++)
setnilvalue(L->stack + i); setnilvalue(L->stack + i);
} }
@ -234,6 +218,10 @@ lua_State* lua_newstate(lua_Alloc f, void* ud)
g->cb = lua_Callbacks(); g->cb = lua_Callbacks();
g->gcstats = GCStats(); g->gcstats = GCStats();
#ifdef LUAI_GCMETRICS
g->gcmetrics = GCMetrics();
#endif
if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0)
{ {
/* memory allocation error: free partial state */ /* memory allocation error: free partial state */

View File

@ -75,10 +75,26 @@ typedef struct CallInfo
#define f_isLua(ci) (!ci_func(ci)->isC) #define f_isLua(ci) (!ci_func(ci)->isC)
#define isLua(ci) (ttisfunction((ci)->func) && f_isLua(ci)) #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 starttotalsizebytes = 0;
size_t heapgoalsizebytes = 0;
size_t heaptriggersizebytes = 0; size_t heaptriggersizebytes = 0;
double pausetime = 0.0; // time from end of the last cycle to the start of a new one 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; size_t endtotalsizebytes = 0;
}; };
// data for proportional-integral controller of heap trigger value struct GCMetrics
struct GCHeapTriggerStats
{
static const unsigned termcount = 32;
int32_t terms[termcount] = {0};
uint32_t termpos = 0;
int32_t integral = 0;
};
struct GCStats
{ {
double stepexplicittimeacc = 0.0; double stepexplicittimeacc = 0.0;
double stepassisttimeacc = 0.0; double stepassisttimeacc = 0.0;
@ -137,14 +144,10 @@ struct GCStats
// when cycle is completed, last cycle values are updated // when cycle is completed, last cycle values are updated
uint64_t completedcycles = 0; uint64_t completedcycles = 0;
GCCycleStats lastcycle; GCCycleMetrics lastcycle;
GCCycleStats currcycle; GCCycleMetrics currcycle;
// only step count and their time is accumulated
GCCycleStats cyclestatsacc;
GCHeapTriggerStats triggerstats;
}; };
#endif
/* /*
** `global state', shared by all threads of this state ** `global state', shared by all threads of this state
@ -206,6 +209,9 @@ typedef struct global_State
GCStats gcstats; GCStats gcstats;
#ifdef LUAI_GCMETRICS
GCMetrics gcmetrics;
#endif
} global_State; } global_State;
// clang-format on // clang-format on

View File

@ -2,17 +2,26 @@
// This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details // 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). * 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 * Tables keep the elements in two parts: an array part and a hash part.
** part. The actual size of the array is the largest `n' such that at * Integer keys >=1 are all candidates to be kept in the array part. The actual size of the array is the
** least half the slots between 0 and n are in use. * 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. * 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 * A main invariant of these tables is that, if an element is not in its main position (i.e. the original
** to it), then the colliding element is in its own main position. * 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. * 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" #include "ltable.h"
@ -25,6 +34,7 @@
#include <string.h> #include <string.h>
LUAU_FASTFLAGVARIABLE(LuauTableRehashRework, false) LUAU_FASTFLAGVARIABLE(LuauTableRehashRework, false)
LUAU_FASTFLAGVARIABLE(LuauTableNewBoundary, false)
// max size of both array and hash part is 2^MAXBITS // max size of both array and hash part is 2^MAXBITS
#define MAXBITS 26 #define MAXBITS 26
@ -460,7 +470,20 @@ static void rehash(lua_State* L, Table* t, const TValue* ek)
totaluse++; totaluse++;
/* compute new size for array part */ /* compute new size for array part */
int na = computesizes(nums, &nasize); 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 */ /* resize the table to new computed sizes */
LUAU_ASSERT(na <= totaluse);
resize(L, t, nasize, totaluse - na); 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) 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); LuaNode* mp = mainposition(t, key);
if (!ttisnil(gval(mp)) || mp == dummynode) if (!ttisnil(gval(mp)) || mp == dummynode)
{ {
LuaNode* othern;
LuaNode* n = getfreepos(t); /* get a free place */ LuaNode* n = getfreepos(t); /* get a free place */
if (n == NULL) if (n == NULL)
{ /* cannot find a free place? */ { /* cannot find a free place? */
rehash(L, t, key); /* grow table */ rehash(L, t, key); /* grow table */
if (!FFlag::LuauTableRehashRework) if (!FFlag::LuauTableRehashRework)
{ {
@ -542,7 +573,7 @@ static TValue* newkey(lua_State* L, Table* t, const TValue* key)
LUAU_ASSERT(n != dummynode); LUAU_ASSERT(n != dummynode);
TValue mk; TValue mk;
getnodekey(L, &mk, mp); getnodekey(L, &mk, mp);
othern = mainposition(t, &mk); LuaNode* othern = mainposition(t, &mk);
if (othern != mp) if (othern != mp)
{ /* is colliding node out of its main position? */ { /* is colliding node out of its main position? */
/* yes; move colliding node into free 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) 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 */ unsigned int i = j; /* i is zero or a present index */
j++; j++;
/* find `i' and `j' such that i is present and j is not */ /* 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); maybesetaboundary(t, boundary);
return 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 must find a boundary in hash part */
else if (t->node == dummynode) /* hash part is empty? */ else if (t->node == dummynode) /* hash part is empty? */
return j; /* that is easy... */ return j; /* that is easy... */

View File

@ -10,7 +10,9 @@
#include "ldebug.h" #include "ldebug.h"
#include "lvm.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) 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 */ int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */
luaL_checktype(L, tt, LUA_TTABLE); 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) if (e >= f)
{ /* otherwise, nothing to move */ { /* otherwise, nothing to move */
luaL_argcheck(L, f > 0 || e < INT_MAX + f, 3, "too many elements 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) static int tclone(lua_State* L)
{ {
if (!FFlag::LuauTableClone)
luaG_runerror(L, "table.clone is not available");
luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 1, LUA_TTABLE);
luaL_argcheck(L, !luaL_getmetafield(L, 1, "__metatable"), 1, "table has a protected metatable"); luaL_argcheck(L, !luaL_getmetafield(L, 1, "__metatable"), 1, "table has a protected metatable");

View File

@ -16,6 +16,8 @@
#include <string.h> #include <string.h>
LUAU_FASTFLAG(LuauTableNewBoundary)
// Disable c99-designator to avoid the warning in CGOTO dispatch table // Disable c99-designator to avoid the warning in CGOTO dispatch table
#ifdef __clang__ #ifdef __clang__
#if __has_warning("-Wc99-designator") #if __has_warning("-Wc99-designator")
@ -2266,9 +2268,9 @@ static void luau_execute(lua_State* L)
VM_NEXT(); 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(); VM_NEXT();
} }
else else

View File

@ -13,8 +13,6 @@
#include <string.h> #include <string.h>
LUAU_FASTFLAGVARIABLE(LuauBytecodeV2Force, false)
// TODO: RAII deallocation doesn't work for longjmp builds if a memory error happens // TODO: RAII deallocation doesn't work for longjmp builds if a memory error happens
template<typename T> template<typename T>
struct TempBuffer struct TempBuffer
@ -156,12 +154,11 @@ int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size
return 1; return 1;
} }
if (FFlag::LuauBytecodeV2Force ? (version != LBC_VERSION_FUTURE) : (version != LBC_VERSION && version != LBC_VERSION_FUTURE)) if (version != LBC_VERSION)
{ {
char chunkid[LUA_IDSIZE]; char chunkid[LUA_IDSIZE];
luaO_chunkid(chunkid, chunkname, LUA_IDSIZE); luaO_chunkid(chunkid, chunkname, LUA_IDSIZE);
lua_pushfstring(L, "%s: bytecode version mismatch (expected %d, got %d)", chunkid, lua_pushfstring(L, "%s: bytecode version mismatch (expected %d, got %d)", chunkid, LBC_VERSION, version);
FFlag::LuauBytecodeV2Force ? LBC_VERSION_FUTURE : LBC_VERSION, version);
return 1; 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]; p->p[j] = protos[fid];
} }
if (FFlag::LuauBytecodeV2Force || version == LBC_VERSION_FUTURE) p->linedefined = readVarInt(data, size, offset);
p->linedefined = readVarInt(data, size, offset);
else
p->linedefined = -1;
p->debugname = readString(strings, data, size, offset); p->debugname = readString(strings, data, size, offset);
uint8_t lineinfo = read<uint8_t>(data, size, offset); uint8_t lineinfo = read<uint8_t>(data, size, offset);

View File

@ -111,7 +111,6 @@ impl Build {
.clone() .clone()
.include(&vm_include_dir) .include(&vm_include_dir)
.define("LUA_API", "extern \"C\"") .define("LUA_API", "extern \"C\"")
// Works 9 times slower, see https://github.com/Roblox/luau/issues/425
// .define("LUA_USE_LONGJMP", "1") // .define("LUA_USE_LONGJMP", "1")
.add_files_by_ext(&vm_source_dir, "cpp") .add_files_by_ext(&vm_source_dir, "cpp")
.out_dir(&lib_dir) .out_dir(&lib_dir)