completely untested, speculative mockup of fatuserdata

This commit is contained in:
LoganDark 2021-11-13 06:32:19 -08:00
parent a6a2b86c9b
commit fc82c8cec2
No known key found for this signature in database
GPG key ID: B8C37CEDE1AC60EA
8 changed files with 63 additions and 10 deletions

View file

@ -65,6 +65,7 @@ enum lua_Type
LUA_TLIGHTUSERDATA,
LUA_TFATUSERDATA,
LUA_TNUMBER,
LUA_TVECTOR,
@ -166,6 +167,7 @@ LUA_API void lua_pushcfunction(
lua_State* L, lua_CFunction fn, const char* debugname = NULL, int nup = 0, lua_Continuation cont = NULL);
LUA_API void lua_pushboolean(lua_State* L, int b);
LUA_API void lua_pushlightuserdata(lua_State* L, void* p);
LUA_API void lua_pushfatuserdata(lua_State* L);
LUA_API int lua_pushthread(lua_State* L);
/*

View file

@ -102,6 +102,14 @@
/* maximum number of captures supported by pattern matching */
#define LUA_MAXCAPTURES 32
/*
@@ LUAU_FATUSERDATA_WORDS defines the amount of extra space embedded in
@* fatuserdata values.
** This inflates every value in the VM, so use it carefully.
** At least 1 is required to store the `vector` value type.
*/
#define LUAU_FATUSERDATA_WORDS 1
/* }================================================================== */
/* Default number printing format and the string length limit */

View file

@ -447,6 +447,8 @@ int lua_objlen(lua_State* L, int idx)
return tsvalue(o)->len;
case LUA_TUSERDATA:
return uvalue(o)->len;
case LUA_TFATUSERDATA:
return LUAU_FATUSERDATA_WORDS;
case LUA_TTABLE:
return luaH_getn(hvalue(o));
case LUA_TNUMBER:
@ -473,6 +475,7 @@ void* lua_touserdata(lua_State* L, int idx)
case LUA_TUSERDATA:
return uvalue(o)->data;
case LUA_TLIGHTUSERDATA:
case LUA_TFATUSERDATA:
return pvalue(o);
default:
return NULL;
@ -625,6 +628,15 @@ void lua_pushlightuserdata(lua_State* L, void* p)
return;
}
const int UNINITIALIZED_FATUSERDATA[LUAU_FATUSERDATA_WORDS] = {};
void lua_pushfatuserdata(lua_State* L)
{
setmpvalue(L->top, 0, &UNINITIALIZED_FATUSERDATA);
api_incr_top(L);
return;
}
int lua_pushthread(lua_State* L)
{
luaC_checkthreadsleep(L);
@ -740,6 +752,9 @@ int lua_getmetatable(lua_State* L, int objindex)
case LUA_TUSERDATA:
mt = uvalue(obj)->metatable;
break;
case LUA_TFATUSERDATA:
mt = (Table*)(mpvalue(obj));
break;
default:
mt = L->global->mt[ttype(obj)];
break;
@ -864,6 +879,13 @@ int lua_setmetatable(lua_State* L, int objindex)
luaC_objbarrier(L, uvalue(obj), mt);
break;
}
case LUA_TFATUSERDATA:
{
mpvalue(obj) = (void*)mt;
if (mt)
luaC_objbarrier(L, uvalue(obj), mt);
break;
}
default:
{
L->global->mt[ttype(obj)] = mt;

View file

@ -42,6 +42,8 @@ LUAU_FASTFLAG(LuauArrayBoundary)
checkconsistency(o); \
if (iscollectable(o) && iswhite(gcvalue(o))) \
reallymarkobject(g, gcvalue(o)); \
else if (ttisfatuserdata(o) && mpvalue(o) && iswhite((GCObject*)mpvalue(o))) \
reallymarkobject(g, (GCObject*)mpvalue(o)); \
}
#define markobject(g, t) \

View file

@ -33,11 +33,16 @@
#define ABISWITCH(x64, ms32, gcc32) (sizeof(void*) == 8 ? x64 : ms32)
#endif
// Padding makes it difficult to verify these statically, we'd basically be mirroring the compiler's layout algorithm
// So instead, only assert these when LUAU_FATUSERDATA_WORDS is 1, which is the go-to default/embedded configuration
#if LUAU_FATUSERDATA_WORDS == 1
static_assert(sizeof(TValue) == ABISWITCH(16, 16, 16), "size mismatch for value");
static_assert(sizeof(LuaNode) == ABISWITCH(32, 32, 32), "size mismatch for table entry");
#endif
static_assert(offsetof(TString, data) == ABISWITCH(24, 20, 20), "size mismatch for string header");
static_assert(offsetof(Udata, data) == ABISWITCH(24, 16, 16), "size mismatch for userdata header");
static_assert(sizeof(Table) == ABISWITCH(56, 36, 36), "size mismatch for table header");
static_assert(sizeof(LuaNode) == ABISWITCH(32, 32, 32), "size mismatch for table entry");
const size_t kSizeClasses = LUA_SIZECLASSES;
const size_t kMaxSmallSize = 512;

View file

@ -47,7 +47,7 @@ typedef union
typedef struct lua_TValue
{
Value value;
int extra;
int extra[LUAU_FATUSERDATA_WORDS];
int tt;
} TValue;
@ -61,6 +61,7 @@ typedef struct lua_TValue
#define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA)
#define ttisthread(o) (ttype(o) == LUA_TTHREAD)
#define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA)
#define ttisfatuserdata(o) (ttype(o) == LUA_TFATUSERDATA)
#define ttisvector(o) (ttype(o) == LUA_TVECTOR)
#define ttisupval(o) (ttype(o) == LUA_TUPVAL)
@ -68,6 +69,7 @@ typedef struct lua_TValue
#define ttype(o) ((o)->tt)
#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc)
#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p)
#define mpvalue(o) check_exp(ttisfatuserdata(o), (o)->value.p)
#define nvalue(o) check_exp(ttisnumber(o), (o)->value.n)
#define vvalue(o) check_exp(ttisvector(o), (o)->value.v)
#define tsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts)
@ -122,6 +124,14 @@ typedef struct lua_TValue
i_o->tt = LUA_TLIGHTUSERDATA; \
}
#define setmpvalue(obj, mt, data) \
{ \
TValue* i_o = (obj); \
i_o->value.p = (void*)(mt); \
memcpy(&i_o->extra, data, LUAU_FATUSERDATA_WORDS * sizeof(int)); \
i_o->tt = LUA_TLIGHTUSERDATA; \
}
#define setbvalue(obj, x) \
{ \
TValue* i_o = (obj); \
@ -364,7 +374,7 @@ typedef struct Closure
typedef struct TKey
{
::Value value;
int extra;
int extra[LUAU_FATUSERDATA_WORDS];
unsigned tt : 4;
int next : 28; /* for chaining */
} TKey;
@ -381,7 +391,7 @@ typedef struct LuaNode
LuaNode* n_ = (node); \
const TValue* i_o = (obj); \
n_->key.value = i_o->value; \
n_->key.extra = i_o->extra; \
memcpy(&n_->key.extra, &i_o->extra, LUAU_FATUSERDATA_WORDS * sizeof(int)); \
n_->key.tt = i_o->tt; \
checkliveness(L->global, i_o); \
}
@ -392,7 +402,7 @@ typedef struct LuaNode
TValue* i_o = (obj); \
const LuaNode* n_ = (node); \
i_o->value = n_->key.value; \
i_o->extra = n_->key.extra; \
memcpy(&i_o->extra, &n_->key.extra, LUAU_FATUSERDATA_WORDS * sizeof(int)); \
i_o->tt = n_->key.tt; \
checkliveness(L->global, i_o); \
}

View file

@ -32,17 +32,17 @@ LUAU_FASTFLAGVARIABLE(LuauArrayBoundary, false)
static_assert(offsetof(LuaNode, val) == 0, "Unexpected Node memory layout, pointer cast in gval2slot is incorrect");
// TKey is bitpacked for memory efficiency so we need to validate bit counts for worst case
static_assert(TKey{{NULL}, 0, LUA_TDEADKEY, 0}.tt == LUA_TDEADKEY, "not enough bits for tt");
static_assert(TKey{{NULL}, 0, LUA_TNIL, MAXSIZE - 1}.next == MAXSIZE - 1, "not enough bits for next");
static_assert(TKey{{NULL}, 0, LUA_TNIL, -(MAXSIZE - 1)}.next == -(MAXSIZE - 1), "not enough bits for next");
static_assert(TKey{{NULL}, {}, LUA_TDEADKEY, 0}.tt == LUA_TDEADKEY, "not enough bits for tt");
static_assert(TKey{{NULL}, {}, LUA_TNIL, MAXSIZE - 1}.next == MAXSIZE - 1, "not enough bits for next");
static_assert(TKey{{NULL}, {}, LUA_TNIL, -(MAXSIZE - 1)}.next == -(MAXSIZE - 1), "not enough bits for next");
// reset cache of absent metamethods, cache is updated in luaT_gettm
#define invalidateTMcache(t) t->flags = 0
// empty hash data points to dummynode so that we can always dereference it
const LuaNode luaH_dummynode = {
{{NULL}, 0, LUA_TNIL}, /* value */
{{NULL}, 0, LUA_TNIL, 0} /* key */
{{NULL}, {}, LUA_TNIL}, /* value */
{{NULL}, {}, LUA_TNIL, 0} /* key */
};
#define dummynode (&luaH_dummynode)

View file

@ -16,6 +16,7 @@ const char* const luaT_typenames[] = {
"boolean",
"userdata",
"userdata",
"number",
"vector",
@ -108,6 +109,9 @@ const TValue* luaT_gettmbyobj(lua_State* L, const TValue* o, TMS event)
case LUA_TUSERDATA:
mt = uvalue(o)->metatable;
break;
case LUA_TFATUSERDATA:
mt = (Table*)(mpvalue(o));
break;
default:
mt = L->global->mt[ttype(o)];
}