luau/VM/src/lstate.h

310 lines
11 KiB
C
Raw Normal View History

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details
#pragma once
#include "lobject.h"
#include "ltm.h"
2022-08-04 23:35:33 +01:00
// registry
#define registry(L) (&L->global->registry)
2022-08-04 23:35:33 +01:00
// extra stack space to handle TM calls and some other extras
#define EXTRA_STACK 5
#define BASIC_CI_SIZE 8
#define BASIC_STACK_SIZE (2 * LUA_MINSTACK)
// clang-format off
typedef struct stringtable
{
TString** hash;
2022-08-04 23:35:33 +01:00
uint32_t nuse; // number of elements
int size;
} stringtable;
// clang-format on
/*
** informations about a call
**
** the general Lua stack frame structure is as follows:
** - each function gets a stack frame, with function "registers" being stack slots on the frame
** - function arguments are associated with registers 0+
** - function locals and temporaries follow after; usually locals are a consecutive block per scope, and temporaries are allocated after this, but
*this is up to the compiler
**
** when function doesn't have varargs, the stack layout is as follows:
** ^ (func) ^^ [fixed args] [locals + temporaries]
** where ^ is the 'func' pointer in CallInfo struct, and ^^ is the 'base' pointer (which is what registers are relative to)
**
** when function *does* have varargs, the stack layout is more complex - the runtime has to copy the fixed arguments so that the 0+ addressing still
*works as follows:
** ^ (func) [fixed args] [varargs] ^^ [fixed args] [locals + temporaries]
**
** computing the sizes of these individual blocks works as follows:
** - the number of fixed args is always matching the `numparams` in a function's Proto object; runtime adds `nil` during the call execution as
*necessary
** - the number of variadic args can be computed by evaluating (ci->base - ci->func - 1 - numparams)
**
** the CallInfo structures are allocated as an array, with each subsequent call being *appended* to this array (so if f calls g, CallInfo for g
*immediately follows CallInfo for f)
** the `nresults` field in CallInfo is set by the caller to tell the function how many arguments the caller is expecting on the stack after the
*function returns
** the `flags` field in CallInfo contains internal execution flags that are important for pcall/etc, see LUA_CALLINFO_*
*/
// clang-format off
typedef struct CallInfo
{
2022-08-04 23:35:33 +01:00
StkId base; // base for this function
StkId func; // function index in the stack
StkId top; // top for this function
const Instruction* savedpc;
2022-08-04 23:35:33 +01:00
int nresults; // expected number of results from this function
unsigned int flags; // call frame flags, see LUA_CALLINFO_*
} CallInfo;
// clang-format on
2022-08-04 23:35:33 +01:00
#define LUA_CALLINFO_RETURN (1 << 0) // should the interpreter return after returning from this callinfo? first frame must have this set
#define LUA_CALLINFO_HANDLE (1 << 1) // should the error thrown during execution get handled by continuation from this callinfo? func must be C
#define LUA_CALLINFO_NATIVE (1 << 2) // should this function be executed using execution callback for native code
#define curr_func(L) (clvalue(L->ci->func))
#define ci_func(ci) (clvalue((ci)->func))
#define f_isLua(ci) (!ci_func(ci)->isC)
#define isLua(ci) (ttisfunction((ci)->func) && f_isLua(ci))
2022-03-24 22:04:14 +00:00
struct GCStats
{
2022-03-24 22:04:14 +00:00
// 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;
2022-03-24 22:04:14 +00:00
double starttimestamp = 0;
double atomicstarttimestamp = 0;
double endtimestamp = 0;
};
#ifdef LUAI_GCMETRICS
struct GCCycleMetrics
{
size_t starttotalsizebytes = 0;
size_t heaptriggersizebytes = 0;
2022-03-04 16:36:33 +00:00
double pausetime = 0.0; // time from end of the last cycle to the start of a new one
double starttimestamp = 0.0;
double endtimestamp = 0.0;
double marktime = 0.0;
2022-03-04 16:36:33 +00:00
double markassisttime = 0.0;
double markmaxexplicittime = 0.0;
size_t markexplicitsteps = 0;
2022-04-21 22:44:27 +01:00
size_t markwork = 0;
double atomicstarttimestamp = 0.0;
size_t atomicstarttotalsizebytes = 0;
double atomictime = 0.0;
2022-03-04 16:36:33 +00:00
// specific atomic stage parts
double atomictimeupval = 0.0;
double atomictimeweak = 0.0;
double atomictimegray = 0.0;
double atomictimeclear = 0.0;
double sweeptime = 0.0;
2022-03-04 16:36:33 +00:00
double sweepassisttime = 0.0;
double sweepmaxexplicittime = 0.0;
size_t sweepexplicitsteps = 0;
2022-04-21 22:44:27 +01:00
size_t sweepwork = 0;
size_t assistwork = 0;
size_t explicitwork = 0;
2022-03-04 16:36:33 +00:00
size_t propagatework = 0;
size_t propagateagainwork = 0;
size_t endtotalsizebytes = 0;
};
2022-03-24 22:04:14 +00:00
struct GCMetrics
{
double stepexplicittimeacc = 0.0;
double stepassisttimeacc = 0.0;
// when cycle is completed, last cycle values are updated
uint64_t completedcycles = 0;
2022-03-24 22:04:14 +00:00
GCCycleMetrics lastcycle;
GCCycleMetrics currcycle;
};
2022-03-24 22:04:14 +00:00
#endif
2022-09-23 20:17:25 +01:00
// Callbacks that can be used to to redirect code execution from Luau bytecode VM to a custom implementation (AoT/JiT/sandboxing/...)
struct lua_ExecutionCallbacks
2022-09-23 20:17:25 +01:00
{
void* context;
void (*close)(lua_State* L); // called when global VM state is closed
2022-09-23 20:17:25 +01:00
void (*destroy)(lua_State* L, Proto* proto); // called when function is destroyed
int (*enter)(lua_State* L, Proto* proto); // called when function is about to start/resume (when execdata is present), return 0 to exit VM
Sync to upstream/release/610 (#1154) # What's changed? * Check interrupt handler inside the pattern match engine to eliminate potential for programs to hang during string library function execution. * Allow iteration over table properties to pass the old type solver. ### Native Code Generation * Use in-place memory operands for math library operations on x64. * Replace opaque bools with separate enum classes in IrDump to improve code maintainability. * Translate operations on inferred vectors to IR. * Enable support for debugging native-compiled functions in Roblox Studio. ### New Type Solver * Rework type inference for boolean and string literals to introduce bounded free types (bounded below by the singleton type, and above by the primitive type) and reworked primitive type constraint to decide which is the appropriate type for the literal. * Introduce `FunctionCheckConstraint` to handle bidirectional typechecking for function calls, pushing the expected parameter types from the function onto the arguments. * Introduce `union` and `intersect` type families to compute deferred simplified unions and intersections to be employed by the constraint generation logic in the new solver. * Implement support for expanding the domain of local types in `Unifier2`. * Rework type inference for iteration variables bound by for in loops to use local types. * Change constraint blocking logic to use a set to prevent accidental re-blocking. * Add logic to detect missing return statements in functions. ### Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-01-27 03:20:56 +00:00
void (*disable)(lua_State* L, Proto* proto); // called when function has to be switched from native to bytecode in the debugger
Sync to upstream/release/617 (#1204) # What's Changed * Fix a case where the stack wasn't completely cleaned up where `debug.info` errored when passed `"f"` option and a thread. * Fix a case of uninitialized field in `luaF_newproto`. ### New Type Solver * When a local is captured in a function, don't add a new entry to the `DfgScope::bindings` if the capture occurs within a loop. * Fix a poor performance characteristic during unification by not trying to simplify an intersection. * Fix a case of multiple constraints mutating the same blocked type causing incorrect inferences. * Fix a case of assertion failure when overload resolution encounters a return typepack mismatch. * When refining a property of the top `table` type, we no longer signal an unknown property error. * Fix a misuse of free types when trying to infer the type of a subscript expression. * Fix a case of assertion failure when trying to resolve an overload from `never`. ### Native Code Generation * Fix dead store optimization issues caused by partial stores. --- ### Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-03-15 23:37:39 +00:00
size_t (*getmemorysize)(lua_State* L, Proto* proto); // called to request the size of memory associated with native part of the Proto
Sync to upstream/release/628 (#1278) ### What's new? * Remove a case of unsound `table.move` optimization * Add Luau stack slot reservations that were missing in REPL (fixes #1273) ### New Type Solver * Assignments have been completely reworked to fix a case of cyclic constraint dependency * When indexing, if the fresh type's upper bound already contains a compatible indexer, do not add another upper bound * Distribute type arguments over all type families sans `eq`, `keyof`, `rawkeyof`, and other internal type families * Fix a case where `buffers` component weren't read in two places (fixes #1267) * Fix a case where things that constitutes a strong ref were slightly incorrect * Fix a case where constraint dependencies weren't setup wrt `for ... in` statement ### Native Codegen * Fix an optimization that splits TValue store only when its value and its tag are compatible * Implement a system to plug additional type information for custom host userdata types --- ### Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-05-31 20:18:18 +01:00
uint8_t (*gettypemapping)(lua_State* L, const char* str, size_t len); // called to get the userdata type index
};
2022-09-23 20:17:25 +01:00
/*
** `global state', shared by all threads of this state
*/
// clang-format off
typedef struct global_State
{
2022-08-04 23:35:33 +01:00
stringtable strt; // hash table for strings
2022-08-04 23:35:33 +01:00
lua_Alloc frealloc; // function to reallocate memory
void* ud; // auxiliary data to `frealloc'
uint8_t currentwhite;
2022-08-04 23:35:33 +01:00
uint8_t gcstate; // state of garbage collector
2022-08-04 23:35:33 +01:00
GCObject* gray; // list of gray objects
GCObject* grayagain; // list of objects to be traversed atomically
GCObject* weak; // list of weak tables (to be cleared)
2022-08-04 23:35:33 +01:00
size_t GCthreshold; // when totalbytes > GCthreshold, run GC step
size_t totalbytes; // number of bytes currently allocated
int gcgoal; // see LUAI_GCGOAL
int gcstepmul; // see LUAI_GCSTEPMUL
int gcstepsize; // see LUAI_GCSTEPSIZE
struct lua_Page* freepages[LUA_SIZECLASSES]; // free page linked list for each size class for non-collectable objects
2022-08-04 23:35:33 +01:00
struct lua_Page* freegcopages[LUA_SIZECLASSES]; // free page linked list for each size class for collectable objects
struct lua_Page* allpages; // page linked list with all pages for all non-collectable object classes (available with LUAU_ASSERTENABLED)
struct lua_Page* allgcopages; // page linked list with all pages for all collectable object classes
struct lua_Page* sweepgcopage; // position of the sweep in `allgcopages'
2022-08-04 23:35:33 +01:00
size_t memcatbytes[LUA_MEMORY_CATEGORIES]; // total amount of memory used by each memory category
struct lua_State* mainthread;
2022-08-04 23:35:33 +01:00
UpVal uvhead; // head of double-linked list of all open upvalues
struct Table* mt[LUA_T_COUNT]; // metatables for basic types
TString* ttname[LUA_T_COUNT]; // names for basic types
TString* tmname[TM_N]; // array with tag-method names
2022-08-04 23:35:33 +01:00
TValue pseudotemp; // storage for temporary values used in pseudo2addr
2022-02-18 01:18:01 +00:00
2022-08-04 23:35:33 +01:00
TValue registry; // registry table, used by lua_ref and LUA_REGISTRYINDEX
int registryfree; // next free slot in registry
2022-08-04 23:35:33 +01:00
struct lua_jmpbuf* errorjmp; // jump buffer data for longjmp-style error handling
2022-08-04 23:35:33 +01:00
uint64_t rngstate; // PCG random number generator state
uint64_t ptrenckey[4]; // pointer encoding key for display
lua_Callbacks cb;
2022-09-23 20:17:25 +01:00
lua_ExecutionCallbacks ecb;
void (*udatagc[LUA_UTAG_LIMIT])(lua_State*, void*); // for each userdata tag, a gc callback to be called immediately before freeing memory
Table* udatamt[LUA_UTAG_LIMIT]; // metatables for tagged userdata
Add tagged lightuserdata (#1087) This change adds support for tagged lightuserdata and optional custom typenames for lightuserdata. Background: Lightuserdata is an efficient representation for many kinds of unmanaged handles and resources in a game engine. However, currently the VM only supports one kind of lightuserdata, which makes it problematic in practice. For example, it's not possible to distinguish between different kinds of lightuserdata in Lua bindings, which can lead to unsafe practices and even crashes when a wrong kind of lightuserdata is passed to a binding function. Tagged lightuserdata work similarly to tagged userdata, i.e. they allow checking the tag quickly using lua_tolightuserdatatagged (or lua_lightuserdatatag). The tag is stored in the 'extra' field of TValue so it will add no cost to the (untagged) lightuserdata type. Alternatives would be to use full userdata values or use bitpacking to embed type information into lightuserdata on application level. Unfortunately these options are not that great in practice: full userdata have major performance implications and bitpacking fails in cases where full 64 bits are already used (e.g. pointers or 64-bit hashes). Lightuserdata names are not strictly necessary but they are rather convenient when debugging Lua code. More precise error messages and tostring returning more specific typename are useful to have in practice (e.g. "resource" or "entity" instead of the more generic "userdata"). Impl note: I did not add support for renaming tags in lua_setlightuserdataname as I'm not sure if it's possible to free fixed strings. If it's simple enough, maybe we should allow renaming (although I can't think of a specific need for it)? --------- Co-authored-by: Petri Häkkinen <petrih@rmd.remedy.fi>
2023-12-14 23:05:51 +00:00
TString* lightuserdataname[LUA_LUTAG_LIMIT]; // names for tagged lightuserdata
GCStats gcstats;
2022-03-24 22:04:14 +00:00
#ifdef LUAI_GCMETRICS
GCMetrics gcmetrics;
#endif
} global_State;
// clang-format on
/*
** `per thread' state
*/
// clang-format off
struct lua_State
{
CommonHeader;
uint8_t status;
2022-08-04 23:35:33 +01:00
uint8_t activememcat; // memory category that is used for new GC object allocations
bool isactive; // thread is currently executing, stack may be mutated without barriers
2022-08-04 23:35:33 +01:00
bool singlestep; // call debugstep hook after each instruction
2022-08-04 23:35:33 +01:00
StkId top; // first free slot in the stack
StkId base; // base of current function
global_State* global;
2022-08-04 23:35:33 +01:00
CallInfo* ci; // call info for current function
StkId stack_last; // last free slot in the stack
StkId stack; // stack base
2022-08-04 23:35:33 +01:00
CallInfo* end_ci; // points after end of ci array
CallInfo* base_ci; // array of CallInfo's
int stacksize;
2022-08-04 23:35:33 +01:00
int size_ci; // size of array `base_ci'
2022-08-04 23:35:33 +01:00
unsigned short nCcalls; // number of nested C calls
unsigned short baseCcalls; // nested C calls when resuming coroutine
2022-08-04 23:35:33 +01:00
int cachedslot; // when table operations or INDEX/NEWINDEX is invoked from Luau, what is the expected slot for lookup?
2022-08-04 23:35:33 +01:00
Table* gt; // table of globals
UpVal* openupval; // list of open upvalues in this stack
GCObject* gclist;
2022-08-04 23:35:33 +01:00
TString* namecall; // when invoked from Luau using NAMECALL, what method do we need to invoke?
void* userdata;
};
// clang-format on
/*
** Union of all collectible objects
*/
union GCObject
{
GCheader gch;
struct TString ts;
struct Udata u;
struct Closure cl;
struct Table h;
struct Proto p;
struct UpVal uv;
2022-08-04 23:35:33 +01:00
struct lua_State th; // thread
Sync to upstream/release/600 (#1076) ### What's Changed - Improve readability of unions and intersections by limiting the number of elements of those types that can be presented on a single line (gated under `FFlag::LuauToStringSimpleCompositeTypesSingleLine`) - Adds a new option to the compiler `--record-stats` to record and output compilation statistics - `if...then...else` expressions are now optimized into `AND/OR` form when possible. ### VM - Add a new `buffer` type to Luau based on the [buffer RFC](https://github.com/Roblox/luau/pull/739) and additional C API functions to work with it; this release does not include the library. - Internal C API to work with string buffers has been updated to align with Lua version more closely ### Native Codegen - Added support for new X64 instruction (rev) and new A64 instruction (bswap) in the assembler - Simplified the way numerical loop condition is translated to IR ### New Type Solver - Operator inference now handled by type families - Created a new system called `Type Paths` to explain why subtyping tests fail in order to improve the quality of error messages. - Systematic changes to implement Data Flow analysis in the new solver (`Breadcrumb` removed and replaced with `RefinementKey`) --- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com>
2023-10-21 02:10:30 +01:00
struct Buffer buf;
};
2022-08-04 23:35:33 +01:00
// macros to convert a GCObject into a specific value
#define gco2ts(o) check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts))
#define gco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u))
#define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl))
#define gco2h(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h))
#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p))
#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv))
#define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th))
Sync to upstream/release/600 (#1076) ### What's Changed - Improve readability of unions and intersections by limiting the number of elements of those types that can be presented on a single line (gated under `FFlag::LuauToStringSimpleCompositeTypesSingleLine`) - Adds a new option to the compiler `--record-stats` to record and output compilation statistics - `if...then...else` expressions are now optimized into `AND/OR` form when possible. ### VM - Add a new `buffer` type to Luau based on the [buffer RFC](https://github.com/Roblox/luau/pull/739) and additional C API functions to work with it; this release does not include the library. - Internal C API to work with string buffers has been updated to align with Lua version more closely ### Native Codegen - Added support for new X64 instruction (rev) and new A64 instruction (bswap) in the assembler - Simplified the way numerical loop condition is translated to IR ### New Type Solver - Operator inference now handled by type families - Created a new system called `Type Paths` to explain why subtyping tests fail in order to improve the quality of error messages. - Systematic changes to implement Data Flow analysis in the new solver (`Breadcrumb` removed and replaced with `RefinementKey`) --- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com>
2023-10-21 02:10:30 +01:00
#define gco2buf(o) check_exp((o)->gch.tt == LUA_TBUFFER, &((o)->buf))
2022-08-04 23:35:33 +01:00
// macro to convert any Lua object into a GCObject
#define obj2gco(v) check_exp(iscollectable(v), cast_to(GCObject*, (v) + 0))
LUAI_FUNC lua_State* luaE_newthread(lua_State* L);
LUAI_FUNC void luaE_freethread(lua_State* L, lua_State* L1, struct lua_Page* page);