Skip to content

Commit f036f75

Browse files
Peter Neissclaude
andcommitted
Phase 122 Part 2 COMPLETE: Update all luaV_* wrappers to delegate to VirtualMachine
Updated all wrapper functions in lvm_*.cpp files to delegate to VirtualMachine methods: **Comparison (lvm_comparison.cpp)**: - luaV_lessthan() → vm.lessThan() - luaV_lessequal() → vm.lessEqual() - luaV_equalobj() → vm.equalObj() **Arithmetic (lvm_arithmetic.cpp)**: - Already updated in previous commit **Conversion (lvm_conversion.cpp)**: - luaV_flttointeger() → VirtualMachine::flttointeger() (static) **Table Operations (lvm_table.cpp)**: - luaV_finishget() → vm.finishGet() - luaV_finishset() → vm.finishSet() **String Operations (lvm_string.cpp)**: - luaV_concat() → vm.concat() - luaV_objlen() → vm.objlen() - Removed duplicate helper functions (tostring, isemptystr, copy2buff) **Implementation (lvirtualmachine.cpp)**: - Added <algorithm> include for std::copy_n - Contains full implementations of all VM operations All implementations moved to VirtualMachine class, wrapper functions now serve as thin C API compatibility layer. **Tests**: All tests pass (final OK !!!) **Performance**: 2.20s avg (5 runs: 2.25, 2.25, 2.21, 2.20, 2.11) - Target: ≤4.33s ✅ - Baseline: 4.20s - Improvement: -48% (outstanding!) 🎯 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 9e521a0 commit f036f75

File tree

5 files changed

+258
-284
lines changed

5 files changed

+258
-284
lines changed

src/vm/lvirtualmachine.cpp

Lines changed: 241 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
#include "lprefix.h"
1111

12+
#include <algorithm>
13+
1214
#include "lvirtualmachine.h"
1315
#include "lapi.h"
1416
#include "ldebug.h"
@@ -61,6 +63,13 @@ inline void luai_threadyield(lua_State* L) noexcept {
6163
#define vmcase(l) case l:
6264
#define vmbreak break
6365

66+
/*
67+
** Constants from lvm.cpp needed for VM operations
68+
*/
69+
70+
/* Limit for table tag-method (metamethod) chains to prevent infinite loops */
71+
inline constexpr int MAXTAGLOOP = 2000;
72+
6473
/*
6574
** Arithmetic and bitwise helper functions for execute()
6675
** (copied from lvm.cpp since they're needed here now)
@@ -1322,33 +1331,257 @@ lua_Integer VirtualMachine::shiftl(lua_Integer x, lua_Integer y) {
13221331
// === COMPARISONS ===
13231332

13241333
int VirtualMachine::lessThan(const TValue *l, const TValue *r) {
1325-
return luaV_lessthan(L, l, r);
1334+
if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */
1335+
return *l < *r; /* Use operator< for cleaner syntax */
1336+
else return L->lessThanOthers(l, r);
13261337
}
13271338

13281339
int VirtualMachine::lessEqual(const TValue *l, const TValue *r) {
1329-
return luaV_lessequal(L, l, r);
1340+
if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */
1341+
return *l <= *r; /* Use operator<= for cleaner syntax */
1342+
else return L->lessEqualOthers(l, r);
13301343
}
13311344

13321345
int VirtualMachine::equalObj(const TValue *t1, const TValue *t2) {
1333-
return luaV_equalobj(L, t1, t2);
1346+
const TValue *tm;
1347+
if (ttype(t1) != ttype(t2)) /* not the same type? */
1348+
return 0;
1349+
else if (ttypetag(t1) != ttypetag(t2)) {
1350+
switch (ttypetag(t1)) {
1351+
case LuaT::NUMINT: { /* integer == float? */
1352+
lua_Integer i2;
1353+
return (flttointeger(fltvalue(t2), &i2, F2Imod::F2Ieq) &&
1354+
ivalue(t1) == i2);
1355+
}
1356+
case LuaT::NUMFLT: { /* float == integer? */
1357+
lua_Integer i1;
1358+
return (flttointeger(fltvalue(t1), &i1, F2Imod::F2Ieq) &&
1359+
i1 == ivalue(t2));
1360+
}
1361+
case LuaT::SHRSTR: case LuaT::LNGSTR: {
1362+
return tsvalue(t1)->equals(tsvalue(t2));
1363+
}
1364+
default:
1365+
return 0;
1366+
}
1367+
}
1368+
else { /* equal variants */
1369+
switch (ttypetag(t1)) {
1370+
case LuaT::NIL: case LuaT::VFALSE: case LuaT::VTRUE:
1371+
return 1;
1372+
case LuaT::NUMINT:
1373+
return (ivalue(t1) == ivalue(t2));
1374+
case LuaT::NUMFLT:
1375+
return (fltvalue(t1) == fltvalue(t2));
1376+
case LuaT::LIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
1377+
case LuaT::SHRSTR:
1378+
return eqshrstr(tsvalue(t1), tsvalue(t2));
1379+
case LuaT::LNGSTR:
1380+
return tsvalue(t1)->equals(tsvalue(t2));
1381+
case LuaT::USERDATA: {
1382+
if (uvalue(t1) == uvalue(t2)) return 1;
1383+
else if (L == nullptr) return 0;
1384+
tm = fasttm(L, uvalue(t1)->getMetatable(), TMS::TM_EQ);
1385+
if (tm == nullptr)
1386+
tm = fasttm(L, uvalue(t2)->getMetatable(), TMS::TM_EQ);
1387+
break; /* will try TM */
1388+
}
1389+
case LuaT::TABLE: {
1390+
if (hvalue(t1) == hvalue(t2)) return 1;
1391+
else if (L == nullptr) return 0;
1392+
tm = fasttm(L, hvalue(t1)->getMetatable(), TMS::TM_EQ);
1393+
if (tm == nullptr)
1394+
tm = fasttm(L, hvalue(t2)->getMetatable(), TMS::TM_EQ);
1395+
break; /* will try TM */
1396+
}
1397+
case LuaT::LCF:
1398+
return (fvalue(t1) == fvalue(t2));
1399+
default: /* functions and threads */
1400+
return (gcvalue(t1) == gcvalue(t2));
1401+
}
1402+
if (tm == nullptr) /* no TM? */
1403+
return 0; /* objects are different */
1404+
else {
1405+
auto tag = luaT_callTMres(L, tm, t1, t2, L->getTop().p); /* call TM */
1406+
return !tagisfalse(tag);
1407+
}
1408+
}
13341409
}
13351410

13361411
// === TABLE OPERATIONS ===
13371412

13381413
LuaT VirtualMachine::finishGet(const TValue *t, TValue *key, StkId val, LuaT tag) {
1339-
return luaV_finishget(L, t, key, val, tag);
1414+
const TValue *tm; /* metamethod */
1415+
for (int loop = 0; loop < MAXTAGLOOP; loop++) {
1416+
if (tag == LuaT::NOTABLE) { /* 't' is not a table? */
1417+
lua_assert(!ttistable(t));
1418+
tm = luaT_gettmbyobj(L, t, TMS::TM_INDEX);
1419+
if (l_unlikely(notm(tm)))
1420+
luaG_typeerror(L, t, "index"); /* no metamethod */
1421+
/* else will try the metamethod */
1422+
}
1423+
else { /* 't' is a table */
1424+
tm = fasttm(L, hvalue(t)->getMetatable(), TMS::TM_INDEX); /* table's metamethod */
1425+
if (tm == nullptr) { /* no metamethod? */
1426+
setnilvalue(s2v(val)); /* result is nil */
1427+
return LuaT::NIL;
1428+
}
1429+
/* else will try the metamethod */
1430+
}
1431+
if (ttisfunction(tm)) { /* is metamethod a function? */
1432+
tag = luaT_callTMres(L, tm, t, key, val); /* call it */
1433+
return tag; /* return tag of the result */
1434+
}
1435+
t = tm; /* else try to access 'tm[key]' */
1436+
tag = luaV_fastget(t, key, s2v(val), [](Table* tbl, const TValue* k, TValue* res) { return tbl->get(k, res); });
1437+
if (!tagisempty(tag))
1438+
return tag; /* done */
1439+
/* else repeat (tail call 'luaV_finishget') */
1440+
}
1441+
luaG_runerror(L, "'__index' chain too long; possible loop");
1442+
return LuaT::NIL; /* to avoid warnings */
13401443
}
13411444

1342-
void VirtualMachine::finishSet(const TValue *t, TValue *key, TValue *val, int aux) {
1343-
luaV_finishset(L, t, key, val, aux);
1445+
void VirtualMachine::finishSet(const TValue *t, TValue *key, TValue *val, int hres) {
1446+
for (int loop = 0; loop < MAXTAGLOOP; loop++) {
1447+
const TValue *tm; /* '__newindex' metamethod */
1448+
if (hres != HNOTATABLE) { /* is 't' a table? */
1449+
auto *h = hvalue(t); /* save 't' table */
1450+
tm = fasttm(L, h->getMetatable(), TMS::TM_NEWINDEX); /* get metamethod */
1451+
if (tm == nullptr) { /* no metamethod? */
1452+
sethvalue2s(L, L->getTop().p, h); /* anchor 't' */
1453+
L->getStackSubsystem().push(); /* assume EXTRA_STACK */
1454+
h->finishSet(L, key, val, hres); /* set new value */
1455+
L->getStackSubsystem().pop();
1456+
invalidateTMcache(h);
1457+
luaC_barrierback(L, obj2gco(h), val);
1458+
return;
1459+
}
1460+
/* else will try the metamethod */
1461+
}
1462+
else { /* not a table; check metamethod */
1463+
tm = luaT_gettmbyobj(L, t, TMS::TM_NEWINDEX);
1464+
if (l_unlikely(notm(tm)))
1465+
luaG_typeerror(L, t, "index");
1466+
}
1467+
/* try the metamethod */
1468+
if (ttisfunction(tm)) {
1469+
luaT_callTM(L, tm, t, key, val);
1470+
return;
1471+
}
1472+
t = tm; /* else repeat assignment over 'tm' */
1473+
hres = luaV_fastset(t, key, val, [](Table* tbl, const TValue* k, TValue* v) { return tbl->pset(k, v); });
1474+
if (hres == HOK) {
1475+
luaV_finishfastset(L, t, val);
1476+
return; /* done */
1477+
}
1478+
/* else 'return luaV_finishset(L, t, key, val, slot)' (loop) */
1479+
}
1480+
luaG_runerror(L, "'__newindex' chain too long; possible loop");
13441481
}
13451482

13461483
// === STRING/OBJECT OPERATIONS ===
13471484

1485+
/* Helper functions for string concatenation */
1486+
1487+
/* Function to ensure that element at 'o' is a string (converts if possible) */
1488+
static inline bool tostring(lua_State* L, TValue* o) {
1489+
if (ttisstring(o)) return true;
1490+
if (!cvt2str(o)) return false;
1491+
luaO_tostring(L, o);
1492+
return true;
1493+
}
1494+
1495+
static inline bool isemptystr(const TValue* o) noexcept {
1496+
return ttisshrstring(o) && tsvalue(o)->length() == 0;
1497+
}
1498+
1499+
/* copy strings in stack from top - n up to top - 1 to buffer */
1500+
static void copy2buff (StkId top, int n, char *buff) {
1501+
auto tl = size_t{0}; /* size already copied */
1502+
do {
1503+
auto *st = tsvalue(s2v(top - n));
1504+
size_t l; /* length of string being copied */
1505+
auto *s = getlstr(st, l);
1506+
std::copy_n(s, l, buff + tl);
1507+
tl += l;
1508+
} while (--n > 0);
1509+
}
1510+
13481511
void VirtualMachine::concat(int total) {
1349-
luaV_concat(L, total);
1512+
if (total == 1)
1513+
return; /* "all" values already concatenated */
1514+
do {
1515+
auto top = L->getTop().p;
1516+
auto n = 2; /* number of elements handled in this pass (at least 2) */
1517+
if (!(ttisstring(s2v(top - 2)) || cvt2str(s2v(top - 2))) ||
1518+
!tostring(L, s2v(top - 1))) {
1519+
luaT_tryconcatTM(L); /* may invalidate 'top' */
1520+
top = L->getTop().p; /* recapture after potential GC */
1521+
}
1522+
else if (isemptystr(s2v(top - 1))) { /* second operand is empty? */
1523+
cast_void(tostring(L, s2v(top - 2))); /* result is first operand */
1524+
top = L->getTop().p; /* recapture after potential GC */
1525+
}
1526+
else if (isemptystr(s2v(top - 2))) { /* first operand is empty string? */
1527+
*s2v(top - 2) = *s2v(top - 1); /* result is second op. (operator=) */
1528+
}
1529+
else {
1530+
/* at least two non-empty string values; get as many as possible */
1531+
auto tl = tsslen(tsvalue(s2v(top - 1)));
1532+
TString *ts;
1533+
/* collect total length and number of strings */
1534+
for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) {
1535+
top = L->getTop().p; /* recapture after tostring() which can trigger GC */
1536+
auto l = tsslen(tsvalue(s2v(top - n - 1)));
1537+
if (l_unlikely(l >= MAX_SIZE - sizeof(TString) - tl)) {
1538+
L->getStackSubsystem().setTopPtr(top - total); /* pop strings to avoid wasting stack */
1539+
luaG_runerror(L, "string length overflow");
1540+
}
1541+
tl += l;
1542+
}
1543+
if (tl <= LUAI_MAXSHORTLEN) { /* is result a short string? */
1544+
char buff[LUAI_MAXSHORTLEN];
1545+
copy2buff(top, n, buff); /* copy strings to buffer */
1546+
ts = TString::create(L, buff, tl);
1547+
top = L->getTop().p; /* recapture after potential GC */
1548+
}
1549+
else { /* long string; copy strings directly to final result */
1550+
ts = TString::createLongString(L, tl);
1551+
top = L->getTop().p; /* recapture after potential GC */
1552+
copy2buff(top, n, getlngstr(ts));
1553+
}
1554+
setsvalue2s(L, top - n, ts); /* create result */
1555+
}
1556+
total -= n - 1; /* got 'n' strings to create one new */
1557+
L->getStackSubsystem().popN(n - 1); /* popped 'n' strings and pushed one */
1558+
} while (total > 1); /* repeat until only 1 result left */
13501559
}
13511560

13521561
void VirtualMachine::objlen(StkId ra, const TValue *rb) {
1353-
luaV_objlen(L, ra, rb);
1562+
const TValue *tm;
1563+
switch (ttypetag(rb)) {
1564+
case LuaT::TABLE: {
1565+
Table *h = hvalue(rb);
1566+
tm = fasttm(L, h->getMetatable(), TMS::TM_LEN);
1567+
if (tm) break; /* metamethod? break switch to call it */
1568+
s2v(ra)->setInt(l_castU2S(h->getn(L))); /* else primitive len */
1569+
return;
1570+
}
1571+
case LuaT::SHRSTR: {
1572+
s2v(ra)->setInt(static_cast<lua_Integer>(tsvalue(rb)->length()));
1573+
return;
1574+
}
1575+
case LuaT::LNGSTR: {
1576+
s2v(ra)->setInt(cast_st2S(tsvalue(rb)->getLnglen()));
1577+
return;
1578+
}
1579+
default: { /* try metamethod */
1580+
tm = luaT_gettmbyobj(L, rb, TMS::TM_LEN);
1581+
if (l_unlikely(notm(tm))) /* no metamethod? */
1582+
luaG_typeerror(L, rb, "get length of");
1583+
break;
1584+
}
1585+
}
1586+
luaT_callTMres(L, tm, rb, rb, ra);
13541587
}

src/vm/lvm_comparison.cpp

Lines changed: 4 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "ltable.h"
2222
#include "ltm.h"
2323
#include "lvm.h"
24+
#include "lvirtualmachine.h"
2425

2526

2627
/*
@@ -155,9 +156,7 @@ int lua_State::lessThanOthers(const TValue *l, const TValue *r) {
155156
** Main operation less than; return 'l < r'.
156157
*/
157158
int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) {
158-
if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */
159-
return *l < *r; /* Use operator< for cleaner syntax */
160-
else return L->lessThanOthers(l, r);
159+
return L->getVM().lessThan(l, r);
161160
}
162161

163162

@@ -177,9 +176,7 @@ int lua_State::lessEqualOthers(const TValue *l, const TValue *r) {
177176
** Main operation less than or equal to; return 'l <= r'.
178177
*/
179178
int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) {
180-
if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */
181-
return *l <= *r; /* Use operator<= for cleaner syntax */
182-
else return L->lessEqualOthers(l, r);
179+
return L->getVM().lessEqual(l, r);
183180
}
184181

185182

@@ -188,74 +185,5 @@ int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) {
188185
** L == nullptr means raw equality (no metamethods)
189186
*/
190187
int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
191-
const TValue *tm;
192-
if (ttype(t1) != ttype(t2)) /* not the same type? */
193-
return 0;
194-
else if (ttypetag(t1) != ttypetag(t2)) {
195-
switch (ttypetag(t1)) {
196-
case LuaT::NUMINT: { /* integer == float? */
197-
/* integer and float can only be equal if float has an integer
198-
value equal to the integer */
199-
lua_Integer i2;
200-
return (luaV_flttointeger(fltvalue(t2), &i2, F2Imod::F2Ieq) &&
201-
ivalue(t1) == i2);
202-
}
203-
case LuaT::NUMFLT: { /* float == integer? */
204-
lua_Integer i1; /* see comment in previous case */
205-
return (luaV_flttointeger(fltvalue(t1), &i1, F2Imod::F2Ieq) &&
206-
i1 == ivalue(t2));
207-
}
208-
case LuaT::SHRSTR: case LuaT::LNGSTR: {
209-
/* compare two strings with different variants: they can be
210-
equal when one string is a short string and the other is
211-
an external string */
212-
return tsvalue(t1)->equals(tsvalue(t2));
213-
}
214-
default:
215-
/* only numbers (integer/float) and strings (long/short) can have
216-
equal values with different variants */
217-
return 0;
218-
}
219-
}
220-
else { /* equal variants */
221-
switch (ttypetag(t1)) {
222-
case LuaT::NIL: case LuaT::VFALSE: case LuaT::VTRUE:
223-
return 1;
224-
case LuaT::NUMINT:
225-
return (ivalue(t1) == ivalue(t2));
226-
case LuaT::NUMFLT:
227-
return (fltvalue(t1) == fltvalue(t2));
228-
case LuaT::LIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
229-
case LuaT::SHRSTR:
230-
return eqshrstr(tsvalue(t1), tsvalue(t2));
231-
case LuaT::LNGSTR:
232-
return tsvalue(t1)->equals(tsvalue(t2));
233-
case LuaT::USERDATA: {
234-
if (uvalue(t1) == uvalue(t2)) return 1;
235-
else if (L == nullptr) return 0;
236-
tm = fasttm(L, uvalue(t1)->getMetatable(), TMS::TM_EQ);
237-
if (tm == nullptr)
238-
tm = fasttm(L, uvalue(t2)->getMetatable(), TMS::TM_EQ);
239-
break; /* will try TM */
240-
}
241-
case LuaT::TABLE: {
242-
if (hvalue(t1) == hvalue(t2)) return 1;
243-
else if (L == nullptr) return 0;
244-
tm = fasttm(L, hvalue(t1)->getMetatable(), TMS::TM_EQ);
245-
if (tm == nullptr)
246-
tm = fasttm(L, hvalue(t2)->getMetatable(), TMS::TM_EQ);
247-
break; /* will try TM */
248-
}
249-
case LuaT::LCF:
250-
return (fvalue(t1) == fvalue(t2));
251-
default: /* functions and threads */
252-
return (gcvalue(t1) == gcvalue(t2));
253-
}
254-
if (tm == nullptr) /* no TM? */
255-
return 0; /* objects are different */
256-
else {
257-
auto tag = luaT_callTMres(L, tm, t1, t2, L->getTop().p); /* call TM */
258-
return !tagisfalse(tag);
259-
}
260-
}
188+
return L->getVM().equalObj(t1, t2);
261189
}

0 commit comments

Comments
 (0)