mirror of
https://github.com/brazilofmux/tinymux
synced 2026-08-13 00:23:11 -04:00
Add Lua bytecode → HIR → RV64 → x86-64 JIT pipeline (Phase 2)
Lua scripts compiled by lua_mod.so can now be JIT-compiled through the existing HIR/RV64/x86-64 pipeline in engine.so. The bytecode deserializer reads lua_dump() output without requiring Lua headers. New COM interface mux_IJITCompile on engine.so with CompileLuaBytecode, RunCompiled, IsCompiled, and Invalidate methods. lua_mod.so acquires this interface and transparently attempts JIT before falling back to the Lua VM. Opcode coverage: data movement (MOVE/LOADI/LOADK/LOADNIL/etc.), integer arithmetic (ADD/SUB/MUL/IDIV/MOD/UNM + immediate/constant variants), comparisons (EQ/LT/LE/EQI/LTI/LEI/GTI/GEI), control flow (JMP/TEST/TESTSET/FORPREP/FORLOOP), returns, and mux.* bridge calls pattern-matched from GETTABUP+GETFIELD+CALL to engine API ECALLs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1fa5282d54
commit
21243e8312
14 changed files with 1596 additions and 11 deletions
|
|
@ -299,6 +299,13 @@ void hir_dump(const hir_program &h);
|
|||
|
||||
// JIT compiler (jit_compiler.cpp).
|
||||
void dbt_compile_cleanup(void);
|
||||
bool run_cached_program(compiled_program *prog,
|
||||
dbref executor, dbref caller_db,
|
||||
dbref enactor,
|
||||
UTF8 *out, size_t out_size,
|
||||
const UTF8 *cargs[] = nullptr,
|
||||
int ncargs = 0,
|
||||
int eval = EV_FCHECK | EV_EVAL);
|
||||
|
||||
// Helper used by both hir_lower and hir_codegen.
|
||||
static inline const UTF8 *u8(const std::string &s) {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,17 @@ static constexpr uint64_t ECALL_ARENA_RELEASE = 0x112; // a0=arena_id
|
|||
static constexpr uint64_t ECALL_DMA_SUBMIT = 0x120; // a0=window, a1=length, a2=op
|
||||
static constexpr uint64_t ECALL_DMA_ACK = 0x121; // -> a0=window (next free)
|
||||
|
||||
// Lua bridge ECALLs — reserved range for mux.* function dispatch.
|
||||
static constexpr uint64_t ECALL_LUA_BRIDGE = 0x380; // base for Lua bridge calls
|
||||
static constexpr uint64_t ECALL_LUA_NAME = 0x380; // mux.name(dbref)
|
||||
static constexpr uint64_t ECALL_LUA_OWNER = 0x381; // mux.owner(dbref)
|
||||
static constexpr uint64_t ECALL_LUA_LOCATION = 0x382; // mux.location(dbref)
|
||||
static constexpr uint64_t ECALL_LUA_GET = 0x383; // mux.get(dbref, attr)
|
||||
static constexpr uint64_t ECALL_LUA_SET = 0x384; // mux.set(dbref, attr, val)
|
||||
static constexpr uint64_t ECALL_LUA_NOTIFY = 0x385; // mux.notify(dbref, msg)
|
||||
static constexpr uint64_t ECALL_LUA_EVAL = 0x386; // mux.eval(expr)
|
||||
static constexpr uint64_t ECALL_LUA_BRIDGE_MAX= 0x38F;
|
||||
|
||||
// Maximum number of indexed functions.
|
||||
static constexpr int ENGINE_API_MAX_FUNCS = 512;
|
||||
|
||||
|
|
|
|||
|
|
@ -1180,6 +1180,38 @@ public:
|
|||
UTF8 *buff, UTF8 **bufc) = 0;
|
||||
};
|
||||
|
||||
// Lua JIT compilation — engine-side bytecode → native compilation.
|
||||
//
|
||||
const MUX_CID CID_JITCompile = UINT64_C(0x00000002A8C3D4E5);
|
||||
const MUX_IID IID_IJITCompile = UINT64_C(0x00000002B9D4E5F6);
|
||||
|
||||
interface mux_IJITCompile : public mux_IUnknown
|
||||
{
|
||||
public:
|
||||
// Compile a Lua 5.4 bytecode blob (output of lua_dump).
|
||||
// On success, stores compiled program and returns key in *pKey.
|
||||
//
|
||||
virtual MUX_RESULT CompileLuaBytecode(const uint8_t *pData, size_t nData,
|
||||
uint64_t *pKey) = 0;
|
||||
|
||||
// Run a previously compiled program.
|
||||
// executor/caller/enactor provide the softcode context.
|
||||
// pArgs/nArgs are the Lua mux.args[].
|
||||
//
|
||||
virtual MUX_RESULT RunCompiled(uint64_t key,
|
||||
dbref executor, dbref caller, dbref enactor,
|
||||
const UTF8 *pArgs[], int nArgs,
|
||||
UTF8 *pResult, size_t nResultMax, size_t *pnResultLen) = 0;
|
||||
|
||||
// Check if a key has a compiled program.
|
||||
//
|
||||
virtual MUX_RESULT IsCompiled(uint64_t key, bool *pCompiled) = 0;
|
||||
|
||||
// Invalidate a compiled program (e.g., source changed).
|
||||
//
|
||||
virtual MUX_RESULT Invalidate(uint64_t key) = 0;
|
||||
};
|
||||
|
||||
// Lua scripting module — server-side Lua 5.4 integration.
|
||||
//
|
||||
const MUX_CID CID_LuaMod = UINT64_C(0x00000002E1A3B5C7);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ ENGINE_CXX_SRC = ast.cpp ast_scan.cpp attrcache.cpp boolexp.cpp \
|
|||
move.cpp object.cpp predicates.cpp player.cpp player_c.cpp \
|
||||
plusemail.cpp powers.cpp quota.cpp rob.cpp set.cpp \
|
||||
dbt.cpp hir_lower.cpp hir_codegen.cpp jit_compiler.cpp dbt_interp.cpp dbt_elf64.cpp \
|
||||
hir_ssa.cpp hir_opt.cpp session.cpp speech.cpp timer.cpp \
|
||||
hir_ssa.cpp hir_opt.cpp lua_bytecode.cpp hir_lower_lua.cpp jit_lua.cpp \
|
||||
session.cpp speech.cpp timer.cpp \
|
||||
unparse.cpp vattr.cpp walkdb.cpp wild.cpp wiz.cpp
|
||||
|
||||
# Add reality levels if enabled
|
||||
|
|
|
|||
|
|
@ -277,8 +277,9 @@ ENGINE_CXX_SRC = ast.cpp ast_scan.cpp attrcache.cpp boolexp.cpp \
|
|||
move.cpp object.cpp predicates.cpp player.cpp player_c.cpp \
|
||||
plusemail.cpp powers.cpp quota.cpp rob.cpp set.cpp dbt.cpp \
|
||||
hir_lower.cpp hir_codegen.cpp jit_compiler.cpp dbt_interp.cpp \
|
||||
dbt_elf64.cpp hir_ssa.cpp hir_opt.cpp session.cpp speech.cpp \
|
||||
timer.cpp unparse.cpp vattr.cpp walkdb.cpp wild.cpp wiz.cpp \
|
||||
dbt_elf64.cpp hir_ssa.cpp hir_opt.cpp lua_bytecode.cpp \
|
||||
hir_lower_lua.cpp jit_lua.cpp session.cpp speech.cpp timer.cpp \
|
||||
unparse.cpp vattr.cpp walkdb.cpp wild.cpp wiz.cpp \
|
||||
$(am__append_1) sqlitedb.cpp sqlite_backend.cpp
|
||||
ENGINE_CXX_OBJS = $(ENGINE_CXX_SRC:.cpp=.eo)
|
||||
BUILT_SOURCES = ast_scan.cpp
|
||||
|
|
|
|||
|
|
@ -75,6 +75,10 @@ DEFINE_ENGINE_FACTORY(CPlayerSessionFactory)
|
|||
DEFINE_ENGINE_FACTORY(CComsysStorageFactory)
|
||||
DEFINE_ENGINE_FACTORY(CMailStorageFactory)
|
||||
|
||||
// JIT Compile factory — implementation delegates to jit_lua.cpp.
|
||||
extern MUX_RESULT jit_compile_create_instance(MUX_IID iid, void **ppv);
|
||||
DEFINE_ENGINE_FACTORY(CJITCompileFactory)
|
||||
|
||||
// CServerEventsSource component which is not directly accessible.
|
||||
//
|
||||
class CServerEventsSource : public mux_IServerEventsControl
|
||||
|
|
@ -5071,6 +5075,53 @@ MUX_RESULT CMailStorageFactory::LockServer(bool bLock)
|
|||
return MUX_S_OK;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CJITCompileFactory — Lua bytecode → native JIT compilation.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
CJITCompileFactory::CJITCompileFactory(void) : m_cRef(1) {}
|
||||
CJITCompileFactory::~CJITCompileFactory() {}
|
||||
|
||||
MUX_RESULT CJITCompileFactory::QueryInterface(MUX_IID iid, void **ppv)
|
||||
{
|
||||
if (mux_IID_IUnknown == iid)
|
||||
{
|
||||
*ppv = static_cast<mux_IUnknown *>(static_cast<mux_IClassFactory *>(this));
|
||||
}
|
||||
else if (mux_IID_IClassFactory == iid)
|
||||
{
|
||||
*ppv = static_cast<mux_IClassFactory *>(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
*ppv = nullptr;
|
||||
return MUX_E_NOINTERFACE;
|
||||
}
|
||||
AddRef();
|
||||
return MUX_S_OK;
|
||||
}
|
||||
|
||||
uint32_t CJITCompileFactory::AddRef(void) { m_cRef++; return m_cRef; }
|
||||
uint32_t CJITCompileFactory::Release(void)
|
||||
{
|
||||
m_cRef--;
|
||||
if (0 == m_cRef) { delete this; return 0; }
|
||||
return m_cRef;
|
||||
}
|
||||
|
||||
MUX_RESULT CJITCompileFactory::CreateInstance(mux_IUnknown *pUnknownOuter,
|
||||
MUX_IID iid, void **ppv)
|
||||
{
|
||||
if (nullptr != pUnknownOuter) return MUX_E_NOAGGREGATION;
|
||||
return jit_compile_create_instance(iid, ppv);
|
||||
}
|
||||
|
||||
MUX_RESULT CJITCompileFactory::LockServer(bool bLock)
|
||||
{
|
||||
UNUSED_PARAMETER(bLock);
|
||||
return MUX_S_OK;
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// COM Front-Door — engine.so exports only these 4 functions.
|
||||
// ===========================================================================
|
||||
|
|
@ -5093,6 +5144,7 @@ static MUX_CLASS_INFO engine_classes[] =
|
|||
{ CID_PlayerSession },
|
||||
{ CID_ComsysStorage },
|
||||
{ CID_MailStorage },
|
||||
{ CID_JITCompile },
|
||||
};
|
||||
#define NUM_ENGINE_CLASSES (sizeof(engine_classes)/sizeof(engine_classes[0]))
|
||||
|
||||
|
|
@ -5127,6 +5179,7 @@ extern "C" MUX_RESULT DCL_EXPORT DCL_API mux_GetClassObject(MUX_CID cid_arg, MUX
|
|||
MAKE_FACTORY(CPlayerSessionFactory, CID_PlayerSession)
|
||||
MAKE_FACTORY(CComsysStorageFactory, CID_ComsysStorage)
|
||||
MAKE_FACTORY(CMailStorageFactory, CID_MailStorage)
|
||||
MAKE_FACTORY(CJITCompileFactory, CID_JITCompile)
|
||||
|
||||
return mr;
|
||||
}
|
||||
|
|
|
|||
603
mux/modules/engine/hir_lower_lua.cpp
Normal file
603
mux/modules/engine/hir_lower_lua.cpp
Normal file
|
|
@ -0,0 +1,603 @@
|
|||
/*! \file hir_lower_lua.cpp
|
||||
* \brief Lua 5.4 bytecode → HIR lowering.
|
||||
*
|
||||
* Two-pass approach:
|
||||
* Pass 1: scan for basic block boundaries (branch targets).
|
||||
* Pass 2: walk opcodes, emit HIR instructions.
|
||||
*
|
||||
* Lua register map: lua_reg[i] holds the current HIR value number
|
||||
* for Lua register i. Updated on each register write.
|
||||
*
|
||||
* Unsupported opcodes → return -1 (caller falls back to Lua VM).
|
||||
*/
|
||||
|
||||
#include "copyright.h"
|
||||
#include "autoconf.h"
|
||||
#include "config.h"
|
||||
#include "externs.h"
|
||||
|
||||
#include "dbt_compile.h"
|
||||
#include "engine_api.h"
|
||||
#include "lua_bytecode.h"
|
||||
#include "hir_lower_lua.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cctype>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
// Maximum Lua registers we track.
|
||||
static constexpr int MAX_LUA_REGS = 256;
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Pass 1: find basic block boundaries
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
static void find_block_starts(const lua_bc_proto *proto,
|
||||
std::vector<bool> &is_leader) {
|
||||
int n = static_cast<int>(proto->code.size());
|
||||
is_leader.assign(n, false);
|
||||
if (n > 0) is_leader[0] = true;
|
||||
|
||||
for (int pc = 0; pc < n; pc++) {
|
||||
const lua_bc_instruction &insn = proto->code[pc];
|
||||
int op = insn.opcode();
|
||||
|
||||
switch (op) {
|
||||
case OP_LUA_JMP: {
|
||||
int target = pc + 1 + insn.sJ();
|
||||
if (target >= 0 && target < n) is_leader[target] = true;
|
||||
if (pc + 1 < n) is_leader[pc + 1] = true;
|
||||
break;
|
||||
}
|
||||
case OP_LUA_FORLOOP: {
|
||||
int target = pc + 1 + insn.sBx();
|
||||
if (target >= 0 && target < n) is_leader[target] = true;
|
||||
if (pc + 1 < n) is_leader[pc + 1] = true;
|
||||
break;
|
||||
}
|
||||
case OP_LUA_FORPREP: {
|
||||
int target = pc + 1 + insn.sBx();
|
||||
if (target >= 0 && target < n) is_leader[target] = true;
|
||||
if (pc + 1 < n) is_leader[pc + 1] = true;
|
||||
break;
|
||||
}
|
||||
case OP_LUA_EQ:
|
||||
case OP_LUA_LT:
|
||||
case OP_LUA_LE:
|
||||
case OP_LUA_EQI:
|
||||
case OP_LUA_LTI:
|
||||
case OP_LUA_LEI:
|
||||
case OP_LUA_GTI:
|
||||
case OP_LUA_GEI:
|
||||
case OP_LUA_TEST:
|
||||
case OP_LUA_TESTSET:
|
||||
if (pc + 1 < n) is_leader[pc + 1] = true;
|
||||
if (pc + 2 < n) is_leader[pc + 2] = true;
|
||||
break;
|
||||
case OP_LUA_RETURN:
|
||||
case OP_LUA_RETURN0:
|
||||
case OP_LUA_RETURN1:
|
||||
if (pc + 1 < n) is_leader[pc + 1] = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Block mapping
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
static int assign_blocks(const std::vector<bool> &is_leader,
|
||||
std::vector<int> &pc_to_block, int n) {
|
||||
int block_count = 0;
|
||||
pc_to_block.resize(n, -1);
|
||||
for (int pc = 0; pc < n; pc++) {
|
||||
if (is_leader[pc]) block_count++;
|
||||
pc_to_block[pc] = block_count - 1;
|
||||
}
|
||||
return block_count;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Helper: load a Lua constant into HIR.
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
static int emit_lua_constant(hir_program &h, rv_compiler &rc,
|
||||
const lua_bc_constant &k) {
|
||||
switch (k.type) {
|
||||
case LUA_BC_TNIL:
|
||||
return h.emit_sconst(rc.pool_str("", 0), "");
|
||||
case LUA_BC_TFALSE:
|
||||
return h.emit_iconst(0);
|
||||
case LUA_BC_TTRUE:
|
||||
return h.emit_iconst(1);
|
||||
case LUA_BC_TINT:
|
||||
return h.emit_iconst(k.ival);
|
||||
case LUA_BC_TFLOAT: {
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "%g", k.fval);
|
||||
uint64_t addr = rc.pool_str(buf, strlen(buf));
|
||||
return h.emit_sconst(addr, std::string(buf));
|
||||
}
|
||||
case LUA_BC_TSHRSTR:
|
||||
case LUA_BC_TLNGSTR: {
|
||||
uint64_t addr = rc.pool_str(k.sval.c_str(), k.sval.size());
|
||||
return h.emit_sconst(addr, k.sval);
|
||||
}
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Helper: emit a comparison + branch pattern.
|
||||
// Many Lua comparison opcodes share the same structure:
|
||||
// compare → optional negate (k bit) → read JMP → emit BRC
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
static int emit_cmp_branch(hir_program &h, int cmp, int k_bit,
|
||||
const lua_bc_proto *proto, int pc,
|
||||
const std::vector<int> &pc_to_block,
|
||||
int cur_hir_block, int n) {
|
||||
if (cmp < 0) return -1;
|
||||
if (k_bit) {
|
||||
cmp = h.emit(HIR_NOT, TY_INT, cmp);
|
||||
if (cmp < 0) return -1;
|
||||
}
|
||||
if (pc + 1 >= n) return -1;
|
||||
const lua_bc_instruction &jmp_insn = proto->code[pc + 1];
|
||||
if (jmp_insn.opcode() != OP_LUA_JMP) return -1;
|
||||
int true_target = pc + 2 + jmp_insn.sJ();
|
||||
int false_target = pc + 2;
|
||||
int true_blk = (true_target >= 0 && true_target < n) ? pc_to_block[true_target] : -1;
|
||||
int false_blk = (false_target >= 0 && false_target < n) ? pc_to_block[false_target] : -1;
|
||||
if (true_blk < 0 || false_blk < 0) return -1;
|
||||
h.emit(HIR_BRC, TY_VOID, cmp, false_blk, true_blk);
|
||||
h.add_edge(cur_hir_block, true_blk);
|
||||
h.add_edge(cur_hir_block, false_blk);
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Pass 2: emit HIR
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
int hir_lower_lua_proto(hir_program &h, rv_compiler &rc,
|
||||
const lua_bc_proto *proto) {
|
||||
if (nullptr == proto) return -1;
|
||||
|
||||
int n = static_cast<int>(proto->code.size());
|
||||
if (n == 0) return -1;
|
||||
|
||||
// Pass 1: find block boundaries.
|
||||
std::vector<bool> is_leader;
|
||||
find_block_starts(proto, is_leader);
|
||||
std::vector<int> pc_to_block;
|
||||
int num_blocks = assign_blocks(is_leader, pc_to_block, n);
|
||||
|
||||
bool multi_block = (num_blocks > 1);
|
||||
|
||||
// Allocate HIR blocks.
|
||||
if (multi_block) {
|
||||
for (int b = 1; b < num_blocks; b++) {
|
||||
int nb = h.new_block();
|
||||
if (nb < 0) return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Lua register → HIR value map.
|
||||
int lua_reg[MAX_LUA_REGS];
|
||||
memset(lua_reg, -1, sizeof(lua_reg));
|
||||
|
||||
int cur_hir_block = 0;
|
||||
h.cur_block = 0;
|
||||
int result_val = -1;
|
||||
|
||||
for (int pc = 0; pc < n; pc++) {
|
||||
// Switch blocks if this PC is a leader.
|
||||
if (is_leader[pc] && pc > 0) {
|
||||
int new_block = pc_to_block[pc];
|
||||
if (new_block != cur_hir_block) {
|
||||
if (h.n_insns > 0) {
|
||||
hir_kind last = h.kind[h.n_insns - 1];
|
||||
if (last != HIR_BR && last != HIR_BRC && last != HIR_RET) {
|
||||
h.emit(HIR_BR, TY_VOID, -1, -1, new_block);
|
||||
h.add_edge(cur_hir_block, new_block);
|
||||
}
|
||||
}
|
||||
cur_hir_block = new_block;
|
||||
h.cur_block = new_block;
|
||||
}
|
||||
}
|
||||
|
||||
const lua_bc_instruction &insn = proto->code[pc];
|
||||
int op = insn.opcode();
|
||||
int A = insn.A();
|
||||
|
||||
switch (op) {
|
||||
|
||||
// ---- Data movement ----
|
||||
|
||||
case OP_LUA_MOVE:
|
||||
if (lua_reg[insn.B()] < 0) return -1;
|
||||
lua_reg[A] = lua_reg[insn.B()];
|
||||
break;
|
||||
|
||||
case OP_LUA_LOADI:
|
||||
lua_reg[A] = h.emit_iconst(insn.sBx());
|
||||
if (lua_reg[A] < 0) return -1;
|
||||
break;
|
||||
|
||||
case OP_LUA_LOADF:
|
||||
lua_reg[A] = h.emit_iconst(insn.sBx());
|
||||
if (lua_reg[A] < 0) return -1;
|
||||
break;
|
||||
|
||||
case OP_LUA_LOADK: {
|
||||
int kidx = insn.Bx();
|
||||
if (kidx < 0 || kidx >= static_cast<int>(proto->constants.size()))
|
||||
return -1;
|
||||
lua_reg[A] = emit_lua_constant(h, rc, proto->constants[kidx]);
|
||||
if (lua_reg[A] < 0) return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_LUA_LOADFALSE:
|
||||
case OP_LUA_LFALSESKIP:
|
||||
lua_reg[A] = h.emit_iconst(0);
|
||||
if (lua_reg[A] < 0) return -1;
|
||||
if (op == OP_LUA_LFALSESKIP) pc++;
|
||||
break;
|
||||
|
||||
case OP_LUA_LOADTRUE:
|
||||
lua_reg[A] = h.emit_iconst(1);
|
||||
if (lua_reg[A] < 0) return -1;
|
||||
break;
|
||||
|
||||
case OP_LUA_LOADNIL:
|
||||
for (int i = A; i <= A + insn.B(); i++) {
|
||||
lua_reg[i] = h.emit_sconst(rc.pool_str("", 0), "");
|
||||
if (lua_reg[i] < 0) return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
// ---- Integer arithmetic ----
|
||||
|
||||
#define ARITH_RR(HIR_OP, MMOP) \
|
||||
{ \
|
||||
int rb = lua_reg[insn.B()]; \
|
||||
int rc_val = lua_reg[insn.C()]; \
|
||||
if (rb < 0 || rc_val < 0) return -1; \
|
||||
lua_reg[A] = h.emit(HIR_OP, TY_INT, rb, rc_val); \
|
||||
if (lua_reg[A] < 0) return -1; \
|
||||
h.native_ops++; \
|
||||
if (pc + 1 < n && proto->code[pc + 1].opcode() == MMOP) pc++; \
|
||||
break; \
|
||||
}
|
||||
|
||||
case OP_LUA_ADD: ARITH_RR(HIR_ADD, OP_LUA_MMBIN)
|
||||
case OP_LUA_SUB: ARITH_RR(HIR_SUB, OP_LUA_MMBIN)
|
||||
case OP_LUA_MUL: ARITH_RR(HIR_MUL, OP_LUA_MMBIN)
|
||||
case OP_LUA_IDIV: ARITH_RR(HIR_DIV, OP_LUA_MMBIN)
|
||||
case OP_LUA_MOD: ARITH_RR(HIR_REM, OP_LUA_MMBIN)
|
||||
#undef ARITH_RR
|
||||
|
||||
case OP_LUA_UNM: {
|
||||
int rb = lua_reg[insn.B()];
|
||||
if (rb < 0) return -1;
|
||||
lua_reg[A] = h.emit(HIR_NEG, TY_INT, rb);
|
||||
if (lua_reg[A] < 0) return -1;
|
||||
h.native_ops++;
|
||||
if (pc + 1 < n && proto->code[pc + 1].opcode() == OP_LUA_MMBIN)
|
||||
pc++;
|
||||
break;
|
||||
}
|
||||
|
||||
// ---- Immediate arithmetic ----
|
||||
|
||||
case OP_LUA_ADDI: {
|
||||
int rb = lua_reg[insn.B()];
|
||||
if (rb < 0) return -1;
|
||||
int imm_val = h.emit_iconst(insn.sC());
|
||||
if (imm_val < 0) return -1;
|
||||
lua_reg[A] = h.emit(HIR_ADD, TY_INT, rb, imm_val);
|
||||
if (lua_reg[A] < 0) return -1;
|
||||
h.native_ops++;
|
||||
if (pc + 1 < n && proto->code[pc + 1].opcode() == OP_LUA_MMBINI)
|
||||
pc++;
|
||||
break;
|
||||
}
|
||||
|
||||
// ---- Constant arithmetic ----
|
||||
|
||||
#define ARITH_RK(HIR_OP) \
|
||||
{ \
|
||||
int rb = lua_reg[insn.B()]; \
|
||||
if (rb < 0) return -1; \
|
||||
int kidx = insn.C(); \
|
||||
if (kidx < 0 || kidx >= static_cast<int>(proto->constants.size())) \
|
||||
return -1; \
|
||||
int kval = emit_lua_constant(h, rc, proto->constants[kidx]); \
|
||||
if (kval < 0) return -1; \
|
||||
lua_reg[A] = h.emit(HIR_OP, TY_INT, rb, kval); \
|
||||
if (lua_reg[A] < 0) return -1; \
|
||||
h.native_ops++; \
|
||||
if (pc + 1 < n && proto->code[pc + 1].opcode() == OP_LUA_MMBINK) \
|
||||
pc++; \
|
||||
break; \
|
||||
}
|
||||
|
||||
case OP_LUA_ADDK: ARITH_RK(HIR_ADD)
|
||||
case OP_LUA_SUBK: ARITH_RK(HIR_SUB)
|
||||
case OP_LUA_MULK: ARITH_RK(HIR_MUL)
|
||||
#undef ARITH_RK
|
||||
|
||||
// ---- Comparisons ----
|
||||
// All share: compare → optional negate → JMP → BRC
|
||||
|
||||
#define CMP_RR(HIR_OP) \
|
||||
{ \
|
||||
int rb = lua_reg[A]; \
|
||||
int rc_val = lua_reg[insn.B()]; \
|
||||
if (rb < 0 || rc_val < 0) return -1; \
|
||||
int cmp = h.emit(HIR_OP, TY_INT, rb, rc_val); \
|
||||
h.native_ops++; \
|
||||
if (!multi_block) return -1; \
|
||||
if (emit_cmp_branch(h, cmp, insn.k(), proto, pc, pc_to_block, \
|
||||
cur_hir_block, n) < 0) return -1; \
|
||||
pc++; \
|
||||
break; \
|
||||
}
|
||||
|
||||
#define CMP_RI(HIR_OP) \
|
||||
{ \
|
||||
int rb = lua_reg[A]; \
|
||||
if (rb < 0) return -1; \
|
||||
int imm_val = h.emit_iconst(insn.sB()); \
|
||||
if (imm_val < 0) return -1; \
|
||||
int cmp = h.emit(HIR_OP, TY_INT, rb, imm_val); \
|
||||
h.native_ops++; \
|
||||
if (!multi_block) return -1; \
|
||||
if (emit_cmp_branch(h, cmp, insn.k(), proto, pc, pc_to_block, \
|
||||
cur_hir_block, n) < 0) return -1; \
|
||||
pc++; \
|
||||
break; \
|
||||
}
|
||||
|
||||
case OP_LUA_EQ: CMP_RR(HIR_EQ)
|
||||
case OP_LUA_LT: CMP_RR(HIR_LT)
|
||||
case OP_LUA_LE: CMP_RR(HIR_LE)
|
||||
case OP_LUA_EQI: CMP_RI(HIR_EQ)
|
||||
case OP_LUA_LTI: CMP_RI(HIR_LT)
|
||||
case OP_LUA_LEI: CMP_RI(HIR_LE)
|
||||
case OP_LUA_GTI: CMP_RI(HIR_GT)
|
||||
case OP_LUA_GEI: CMP_RI(HIR_GE)
|
||||
#undef CMP_RR
|
||||
#undef CMP_RI
|
||||
|
||||
case OP_LUA_TEST: {
|
||||
int rb = lua_reg[A];
|
||||
if (rb < 0) return -1;
|
||||
int cmp = h.emit(HIR_BOOL, TY_INT, rb);
|
||||
if (!multi_block) return -1;
|
||||
if (emit_cmp_branch(h, cmp, insn.k(), proto, pc, pc_to_block,
|
||||
cur_hir_block, n) < 0) return -1;
|
||||
pc++;
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_LUA_TESTSET: {
|
||||
int rb = lua_reg[insn.B()];
|
||||
if (rb < 0) return -1;
|
||||
int cmp = h.emit(HIR_BOOL, TY_INT, rb);
|
||||
lua_reg[A] = rb; // Simplified: always copy.
|
||||
if (!multi_block) return -1;
|
||||
if (emit_cmp_branch(h, cmp, insn.k(), proto, pc, pc_to_block,
|
||||
cur_hir_block, n) < 0) return -1;
|
||||
pc++;
|
||||
break;
|
||||
}
|
||||
|
||||
// ---- Control flow ----
|
||||
|
||||
case OP_LUA_JMP: {
|
||||
int target = pc + 1 + insn.sJ();
|
||||
if (!multi_block) return -1;
|
||||
int target_blk = (target >= 0 && target < n) ? pc_to_block[target] : -1;
|
||||
if (target_blk < 0) return -1;
|
||||
h.emit(HIR_BR, TY_VOID, -1, -1, target_blk);
|
||||
h.add_edge(cur_hir_block, target_blk);
|
||||
break;
|
||||
}
|
||||
|
||||
// ---- Numeric for loop ----
|
||||
|
||||
case OP_LUA_FORPREP: {
|
||||
if (!multi_block) return -1;
|
||||
if (lua_reg[A] < 0 || lua_reg[A + 1] < 0 || lua_reg[A + 2] < 0)
|
||||
return -1;
|
||||
lua_reg[A + 3] = lua_reg[A];
|
||||
int target = pc + 1 + insn.sBx();
|
||||
int target_blk = (target >= 0 && target < n) ? pc_to_block[target] : -1;
|
||||
if (target_blk < 0) return -1;
|
||||
h.emit(HIR_BR, TY_VOID, -1, -1, target_blk);
|
||||
h.add_edge(cur_hir_block, target_blk);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_LUA_FORLOOP: {
|
||||
if (!multi_block) return -1;
|
||||
int idx = lua_reg[A + 3];
|
||||
int step = lua_reg[A + 2];
|
||||
int limit = lua_reg[A + 1];
|
||||
if (idx < 0 || step < 0 || limit < 0) return -1;
|
||||
|
||||
int new_idx = h.emit(HIR_ADD, TY_INT, idx, step);
|
||||
if (new_idx < 0) return -1;
|
||||
lua_reg[A + 3] = new_idx;
|
||||
lua_reg[A] = new_idx;
|
||||
h.native_ops++;
|
||||
|
||||
int cmp = h.emit(HIR_LE, TY_INT, new_idx, limit);
|
||||
if (cmp < 0) return -1;
|
||||
h.native_ops++;
|
||||
|
||||
int loop_target = pc + 1 + insn.sBx();
|
||||
int exit_target = pc + 1;
|
||||
int loop_blk = (loop_target >= 0 && loop_target < n) ? pc_to_block[loop_target] : -1;
|
||||
int exit_blk = (exit_target >= 0 && exit_target < n) ? pc_to_block[exit_target] : -1;
|
||||
if (loop_blk < 0 || exit_blk < 0) return -1;
|
||||
h.emit(HIR_BRC, TY_VOID, cmp, exit_blk, loop_blk);
|
||||
h.add_edge(cur_hir_block, loop_blk);
|
||||
h.add_edge(cur_hir_block, exit_blk);
|
||||
break;
|
||||
}
|
||||
|
||||
// ---- Return ----
|
||||
|
||||
case OP_LUA_RETURN0:
|
||||
result_val = h.emit_sconst(rc.pool_str("", 0), "");
|
||||
if (result_val < 0) return -1;
|
||||
h.emit(HIR_RET, TY_VOID, result_val);
|
||||
break;
|
||||
|
||||
case OP_LUA_RETURN1: {
|
||||
int rv = lua_reg[A];
|
||||
if (rv < 0) return -1;
|
||||
if (h.ty[rv] == TY_INT) {
|
||||
rv = h.emit(HIR_ITOA, TY_STRING, rv);
|
||||
if (rv < 0) return -1;
|
||||
}
|
||||
result_val = rv;
|
||||
h.emit(HIR_RET, TY_VOID, result_val);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_LUA_RETURN: {
|
||||
int nret = insn.B() - 1;
|
||||
if (nret < 0) return -1;
|
||||
if (nret == 0) {
|
||||
result_val = h.emit_sconst(rc.pool_str("", 0), "");
|
||||
if (result_val < 0) return -1;
|
||||
} else {
|
||||
int rv = lua_reg[A];
|
||||
if (rv < 0) return -1;
|
||||
if (h.ty[rv] == TY_INT) {
|
||||
rv = h.emit(HIR_ITOA, TY_STRING, rv);
|
||||
if (rv < 0) return -1;
|
||||
}
|
||||
result_val = rv;
|
||||
}
|
||||
h.emit(HIR_RET, TY_VOID, result_val);
|
||||
break;
|
||||
}
|
||||
|
||||
// ---- Table access: mux.* bridge pattern ----
|
||||
|
||||
case OP_LUA_GETTABUP: {
|
||||
if (insn.B() != 0) return -1; // Only _ENV.
|
||||
int kidx = insn.C();
|
||||
if (kidx < 0 || kidx >= static_cast<int>(proto->constants.size()))
|
||||
return -1;
|
||||
const lua_bc_constant &k = proto->constants[kidx];
|
||||
if (k.type != LUA_BC_TSHRSTR && k.type != LUA_BC_TLNGSTR)
|
||||
return -1;
|
||||
if (k.sval != "mux") return -1;
|
||||
uint64_t addr = rc.pool_str("mux", 3);
|
||||
lua_reg[A] = h.emit_sconst(addr, "mux");
|
||||
if (lua_reg[A] < 0) return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_LUA_GETFIELD: {
|
||||
int table_reg = lua_reg[insn.B()];
|
||||
if (table_reg < 0) return -1;
|
||||
int kidx = insn.C();
|
||||
if (kidx < 0 || kidx >= static_cast<int>(proto->constants.size()))
|
||||
return -1;
|
||||
const lua_bc_constant &k = proto->constants[kidx];
|
||||
if (k.type != LUA_BC_TSHRSTR && k.type != LUA_BC_TLNGSTR)
|
||||
return -1;
|
||||
if (table_reg >= 0 && h.kind[table_reg] == HIR_SCONST
|
||||
&& h.sval[table_reg] == "mux") {
|
||||
std::string name = "mux." + k.sval;
|
||||
uint64_t addr = rc.pool_str(name.c_str(), name.size());
|
||||
lua_reg[A] = h.emit_sconst(addr, name);
|
||||
if (lua_reg[A] < 0) return -1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_LUA_CALL: {
|
||||
int func_reg = lua_reg[A];
|
||||
if (func_reg < 0) return -1;
|
||||
|
||||
int nargs = insn.B() - 1;
|
||||
int nresults = insn.C() - 1;
|
||||
|
||||
if (h.kind[func_reg] != HIR_SCONST) return -1;
|
||||
const std::string &fname = h.sval[func_reg];
|
||||
if (fname.substr(0, 4) != "mux.") return -1;
|
||||
|
||||
std::string bridge_name = fname.substr(4);
|
||||
std::string upper_name;
|
||||
for (char c : bridge_name) {
|
||||
upper_name += static_cast<char>(toupper(static_cast<unsigned char>(c)));
|
||||
}
|
||||
|
||||
std::vector<int> args;
|
||||
for (int i = 0; i < nargs; i++) {
|
||||
int areg = lua_reg[A + 1 + i];
|
||||
if (areg < 0) return -1;
|
||||
if (h.ty[areg] == TY_INT) {
|
||||
areg = h.emit(HIR_ITOA, TY_STRING, areg);
|
||||
if (areg < 0) return -1;
|
||||
}
|
||||
args.push_back(areg);
|
||||
}
|
||||
|
||||
int fidx = engine_api_lookup(upper_name.c_str());
|
||||
|
||||
int call_val;
|
||||
if (fidx > 0) {
|
||||
call_val = h.emit_call(TY_STRING, fidx,
|
||||
args.data(), static_cast<int>(args.size()));
|
||||
} else {
|
||||
call_val = h.emit_call(TY_STRING, 0,
|
||||
args.data(), static_cast<int>(args.size()),
|
||||
&upper_name);
|
||||
}
|
||||
if (call_val < 0) return -1;
|
||||
h.ecalls++;
|
||||
|
||||
if (nresults >= 1) {
|
||||
lua_reg[A] = call_val;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// ---- No-op instructions ----
|
||||
case OP_LUA_VARARGPREP:
|
||||
case OP_LUA_MMBIN:
|
||||
case OP_LUA_MMBINI:
|
||||
case OP_LUA_MMBINK:
|
||||
break;
|
||||
|
||||
// ---- Unsupported opcodes ----
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (result_val < 0) return -1;
|
||||
h.result = result_val;
|
||||
h.needs_jit = (h.ecalls > 0 || h.native_ops > 0);
|
||||
|
||||
return result_val;
|
||||
}
|
||||
19
mux/modules/engine/hir_lower_lua.h
Normal file
19
mux/modules/engine/hir_lower_lua.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*! \file hir_lower_lua.h
|
||||
* \brief Lua bytecode → HIR lowering.
|
||||
*/
|
||||
|
||||
#ifndef HIR_LOWER_LUA_H
|
||||
#define HIR_LOWER_LUA_H
|
||||
|
||||
struct hir_program;
|
||||
struct rv_compiler;
|
||||
struct lua_bc_proto;
|
||||
|
||||
// Lower a Lua 5.4 function prototype to HIR instructions.
|
||||
// Returns the HIR instruction index of the result, or -1 on failure
|
||||
// (unsupported opcode → caller falls back to Lua VM).
|
||||
//
|
||||
int hir_lower_lua_proto(hir_program &h, rv_compiler &rc,
|
||||
const lua_bc_proto *proto);
|
||||
|
||||
#endif // HIR_LOWER_LUA_H
|
||||
|
|
@ -926,13 +926,13 @@ static compiled_program *compile_cached(const UTF8 *expr, size_t nLen,
|
|||
// Run a cached program. Uses dbt_rerun if the DBT already has
|
||||
// translated blocks for this program, otherwise dbt_reset.
|
||||
//
|
||||
static bool run_cached_program(compiled_program *prog,
|
||||
dbref executor, dbref caller_db,
|
||||
dbref enactor,
|
||||
UTF8 *out, size_t out_size,
|
||||
const UTF8 *cargs[] = nullptr,
|
||||
int ncargs = 0,
|
||||
int eval = EV_FCHECK | EV_EVAL) {
|
||||
bool run_cached_program(compiled_program *prog,
|
||||
dbref executor, dbref caller_db,
|
||||
dbref enactor,
|
||||
UTF8 *out, size_t out_size,
|
||||
const UTF8 *cargs[],
|
||||
int ncargs,
|
||||
int eval) {
|
||||
if (!prog->needs_jit) {
|
||||
const char *r = reinterpret_cast<const char *>(
|
||||
prog->memory.data() + prog->out_addr);
|
||||
|
|
|
|||
206
mux/modules/engine/jit_lua.cpp
Normal file
206
mux/modules/engine/jit_lua.cpp
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
/*! \file jit_lua.cpp
|
||||
* \brief CJITCompile COM class — Lua bytecode → native JIT compilation.
|
||||
*
|
||||
* Implements mux_IJITCompile. Deserializes Lua 5.4 bytecode,
|
||||
* lowers through HIR/RV64/x86-64 pipeline, caches compiled programs.
|
||||
*/
|
||||
|
||||
#include "copyright.h"
|
||||
#include "autoconf.h"
|
||||
#include "config.h"
|
||||
#include "externs.h"
|
||||
|
||||
#include "dbt_compile.h"
|
||||
#include "engine_api.h"
|
||||
#include "lua_bytecode.h"
|
||||
#include "hir_lower_lua.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Compile cache
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
static std::unordered_map<uint64_t, compiled_program> s_lua_cache;
|
||||
static uint64_t s_next_key = 1;
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Statistics
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
struct lua_jit_stats {
|
||||
uint64_t compile_ok;
|
||||
uint64_t compile_fail;
|
||||
uint64_t run_ok;
|
||||
uint64_t run_fail;
|
||||
uint64_t cache_hits;
|
||||
uint64_t invalidations;
|
||||
};
|
||||
|
||||
static lua_jit_stats s_lua_jit_stats = {};
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Compile a Lua bytecode blob to a compiled_program.
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
static bool compile_lua_bytecode(const uint8_t *data, size_t len,
|
||||
compiled_program *out) {
|
||||
// Deserialize.
|
||||
lua_bc_chunk chunk;
|
||||
if (!lua_bc_load(data, len, &chunk)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create HIR program and RV64 compiler state.
|
||||
hir_program *h = new hir_program;
|
||||
h->init();
|
||||
|
||||
rv_compiler rc_state;
|
||||
|
||||
// Lower Lua bytecode to HIR.
|
||||
int result = hir_lower_lua_proto(*h, rc_state, &chunk.main);
|
||||
if (result < 0) {
|
||||
delete h;
|
||||
return false;
|
||||
}
|
||||
|
||||
// For multi-block programs, run SSA construction and optimization.
|
||||
if (h->n_blocks > 1) {
|
||||
hir_build_cfg(*h);
|
||||
hir_ssa_construct(*h);
|
||||
hir_optimize(*h);
|
||||
} else {
|
||||
// Single block: just constant folding.
|
||||
hir_const_fold(*h);
|
||||
}
|
||||
|
||||
// Code generation: HIR → RV64.
|
||||
hir_codegen(*h, rc_state);
|
||||
|
||||
// Build compiled_program output.
|
||||
out->memory = std::move(rc_state.memory);
|
||||
out->memory_size = rv_compiler::MEM_SIZE;
|
||||
out->out_addr = rc_state.final_out;
|
||||
out->out_used = rc_state.out_pool;
|
||||
out->ok = true;
|
||||
out->folds = h->folds;
|
||||
out->ecalls = h->ecalls;
|
||||
out->tier2_calls = 0;
|
||||
out->native_ops = h->native_ops;
|
||||
out->needs_jit = h->needs_jit;
|
||||
|
||||
delete h;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// CJITCompile COM class
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
class CJITCompile : public mux_IJITCompile
|
||||
{
|
||||
public:
|
||||
CJITCompile(void) : m_cRef(1) {}
|
||||
virtual ~CJITCompile() {}
|
||||
|
||||
// mux_IUnknown
|
||||
MUX_RESULT QueryInterface(MUX_IID iid, void **ppv) override {
|
||||
if (mux_IID_IUnknown == iid) {
|
||||
*ppv = static_cast<mux_IUnknown *>(static_cast<mux_IJITCompile *>(this));
|
||||
} else if (IID_IJITCompile == iid) {
|
||||
*ppv = static_cast<mux_IJITCompile *>(this);
|
||||
} else {
|
||||
*ppv = nullptr;
|
||||
return MUX_E_NOINTERFACE;
|
||||
}
|
||||
AddRef();
|
||||
return MUX_S_OK;
|
||||
}
|
||||
|
||||
uint32_t AddRef(void) override { return ++m_cRef; }
|
||||
uint32_t Release(void) override {
|
||||
uint32_t n = --m_cRef;
|
||||
if (0 == n) delete this;
|
||||
return n;
|
||||
}
|
||||
|
||||
// mux_IJITCompile
|
||||
MUX_RESULT CompileLuaBytecode(const uint8_t *pData, size_t nData,
|
||||
uint64_t *pKey) override
|
||||
{
|
||||
if (nullptr == pData || nullptr == pKey) return MUX_E_INVALIDARG;
|
||||
|
||||
compiled_program prog;
|
||||
if (!compile_lua_bytecode(pData, nData, &prog)) {
|
||||
s_lua_jit_stats.compile_fail++;
|
||||
*pKey = 0;
|
||||
return MUX_E_FAIL;
|
||||
}
|
||||
|
||||
uint64_t key = s_next_key++;
|
||||
s_lua_cache[key] = std::move(prog);
|
||||
*pKey = key;
|
||||
s_lua_jit_stats.compile_ok++;
|
||||
return MUX_S_OK;
|
||||
}
|
||||
|
||||
MUX_RESULT RunCompiled(uint64_t key,
|
||||
dbref executor, dbref caller, dbref enactor,
|
||||
const UTF8 *pArgs[], int nArgs,
|
||||
UTF8 *pResult, size_t nResultMax, size_t *pnResultLen) override
|
||||
{
|
||||
auto it = s_lua_cache.find(key);
|
||||
if (it == s_lua_cache.end()) return MUX_E_NOTFOUND;
|
||||
|
||||
s_lua_jit_stats.cache_hits++;
|
||||
|
||||
bool ok = run_cached_program(&it->second, executor, caller, enactor,
|
||||
pResult, nResultMax, pArgs, nArgs);
|
||||
if (!ok) {
|
||||
s_lua_jit_stats.run_fail++;
|
||||
return MUX_E_FAIL;
|
||||
}
|
||||
|
||||
if (pnResultLen) {
|
||||
*pnResultLen = strlen(reinterpret_cast<const char *>(pResult));
|
||||
}
|
||||
s_lua_jit_stats.run_ok++;
|
||||
return MUX_S_OK;
|
||||
}
|
||||
|
||||
MUX_RESULT IsCompiled(uint64_t key, bool *pCompiled) override {
|
||||
if (nullptr == pCompiled) return MUX_E_INVALIDARG;
|
||||
*pCompiled = (s_lua_cache.find(key) != s_lua_cache.end());
|
||||
return MUX_S_OK;
|
||||
}
|
||||
|
||||
MUX_RESULT Invalidate(uint64_t key) override {
|
||||
auto it = s_lua_cache.find(key);
|
||||
if (it != s_lua_cache.end()) {
|
||||
s_lua_cache.erase(it);
|
||||
s_lua_jit_stats.invalidations++;
|
||||
}
|
||||
return MUX_S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t m_cRef;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Factory creation function — called from engine_com.cpp.
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
MUX_RESULT jit_compile_create_instance(MUX_IID iid, void **ppv) {
|
||||
CJITCompile *pObj = nullptr;
|
||||
try { pObj = new CJITCompile; } catch (...) { ; }
|
||||
if (nullptr == pObj) return MUX_E_OUTOFMEMORY;
|
||||
|
||||
MUX_RESULT mr = pObj->QueryInterface(iid, ppv);
|
||||
pObj->Release();
|
||||
return mr;
|
||||
}
|
||||
296
mux/modules/engine/lua_bytecode.cpp
Normal file
296
mux/modules/engine/lua_bytecode.cpp
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
/*! \file lua_bytecode.cpp
|
||||
* \brief Lua 5.4 bytecode deserializer — standalone, no Lua headers.
|
||||
*
|
||||
* Reads the binary format produced by lua_dump(). Reference:
|
||||
* Lua 5.4 source: ldump.c / lundump.c
|
||||
*/
|
||||
|
||||
#include "lua_bytecode.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
// Lua 5.4 dump signature and magic values.
|
||||
//
|
||||
static const uint8_t LUA_SIGNATURE[] = { 0x1B, 'L', 'u', 'a' };
|
||||
static const uint8_t LUAC_DATA[] = { 0x19, 0x93, '\r', '\n', 0x1A, '\n' };
|
||||
static constexpr uint8_t LUAC_VERSION = 0x54; // Lua 5.4
|
||||
static constexpr uint8_t LUAC_FORMAT = 0;
|
||||
static constexpr int64_t LUAC_INT = 0x5678;
|
||||
static constexpr double LUAC_NUM = 370.5;
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Reader helper — a simple cursor over a byte buffer.
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
struct bc_reader {
|
||||
const uint8_t *data;
|
||||
size_t len;
|
||||
size_t pos;
|
||||
bool ok;
|
||||
|
||||
bc_reader(const uint8_t *d, size_t l)
|
||||
: data(d), len(l), pos(0), ok(true) {}
|
||||
|
||||
bool has(size_t n) const { return pos + n <= len; }
|
||||
|
||||
uint8_t read_byte() {
|
||||
if (!has(1)) { ok = false; return 0; }
|
||||
return data[pos++];
|
||||
}
|
||||
|
||||
void read_bytes(void *dst, size_t n) {
|
||||
if (!has(n)) { ok = false; return; }
|
||||
memcpy(dst, data + pos, n);
|
||||
pos += n;
|
||||
}
|
||||
|
||||
bool match(const void *expected, size_t n) {
|
||||
if (!has(n)) { ok = false; return false; }
|
||||
bool m = (memcmp(data + pos, expected, n) == 0);
|
||||
pos += n;
|
||||
if (!m) ok = false;
|
||||
return m;
|
||||
}
|
||||
|
||||
// Lua uses a variable-length unsigned integer encoding for sizes.
|
||||
// lundump.c LoadUnsigned: accumulate 7-bit groups, high bit = more.
|
||||
//
|
||||
size_t read_size() {
|
||||
size_t x = 0;
|
||||
int b = read_byte();
|
||||
if (!ok) return 0;
|
||||
while ((b & 0x80) == 0) {
|
||||
x = (x << 7) | b;
|
||||
b = read_byte();
|
||||
if (!ok) return 0;
|
||||
}
|
||||
x = (x << 7) | (b & 0x7F);
|
||||
return x;
|
||||
}
|
||||
|
||||
int64_t read_integer() {
|
||||
int64_t v = 0;
|
||||
read_bytes(&v, 8);
|
||||
return v;
|
||||
}
|
||||
|
||||
double read_number() {
|
||||
double v = 0;
|
||||
read_bytes(&v, 8);
|
||||
return v;
|
||||
}
|
||||
|
||||
int read_int() {
|
||||
int v = 0;
|
||||
read_bytes(&v, 4);
|
||||
return v;
|
||||
}
|
||||
|
||||
std::string read_string() {
|
||||
size_t sz = read_size();
|
||||
if (!ok) return "";
|
||||
if (sz == 0) return "";
|
||||
// In Lua 5.4 dump format, sz is the length + 1.
|
||||
// The actual string length is sz - 1.
|
||||
sz -= 1;
|
||||
if (!has(sz)) { ok = false; return ""; }
|
||||
std::string s(reinterpret_cast<const char *>(data + pos), sz);
|
||||
pos += sz;
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Proto loader (recursive).
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
static bool load_proto(bc_reader &r, lua_bc_proto *p, const std::string &parent_source);
|
||||
|
||||
static bool load_constants(bc_reader &r, lua_bc_proto *p) {
|
||||
int n = static_cast<int>(r.read_size());
|
||||
if (!r.ok) return false;
|
||||
p->constants.resize(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
uint8_t t = r.read_byte();
|
||||
if (!r.ok) return false;
|
||||
lua_bc_constant &k = p->constants[i];
|
||||
k.type = static_cast<lua_bc_const_type>(t);
|
||||
k.ival = 0;
|
||||
k.fval = 0.0;
|
||||
switch (t) {
|
||||
case LUA_BC_TNIL:
|
||||
case LUA_BC_TFALSE:
|
||||
case LUA_BC_TTRUE:
|
||||
break;
|
||||
case LUA_BC_TINT:
|
||||
k.ival = r.read_integer();
|
||||
break;
|
||||
case LUA_BC_TFLOAT:
|
||||
k.fval = r.read_number();
|
||||
break;
|
||||
case LUA_BC_TSHRSTR:
|
||||
case LUA_BC_TLNGSTR:
|
||||
k.sval = r.read_string();
|
||||
break;
|
||||
default:
|
||||
r.ok = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return r.ok;
|
||||
}
|
||||
|
||||
static bool load_upvalues(bc_reader &r, lua_bc_proto *p) {
|
||||
int n = static_cast<int>(r.read_size());
|
||||
if (!r.ok) return false;
|
||||
p->upvalues.resize(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
p->upvalues[i].instack = r.read_byte();
|
||||
p->upvalues[i].idx = r.read_byte();
|
||||
p->upvalues[i].kind = r.read_byte();
|
||||
if (!r.ok) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool load_protos(bc_reader &r, lua_bc_proto *p,
|
||||
const std::string &parent_source) {
|
||||
int n = static_cast<int>(r.read_size());
|
||||
if (!r.ok) return false;
|
||||
p->protos.resize(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (!load_proto(r, &p->protos[i], parent_source))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool skip_debug(bc_reader &r, lua_bc_proto *p) {
|
||||
// Line info.
|
||||
int n = static_cast<int>(r.read_size());
|
||||
if (!r.ok) return false;
|
||||
for (int i = 0; i < n; i++) {
|
||||
r.read_byte();
|
||||
if (!r.ok) return false;
|
||||
}
|
||||
|
||||
// Abs line info.
|
||||
n = static_cast<int>(r.read_size());
|
||||
if (!r.ok) return false;
|
||||
for (int i = 0; i < n; i++) {
|
||||
r.read_int(); // pc
|
||||
r.read_int(); // line
|
||||
if (!r.ok) return false;
|
||||
}
|
||||
|
||||
// Local variables.
|
||||
n = static_cast<int>(r.read_size());
|
||||
if (!r.ok) return false;
|
||||
for (int i = 0; i < n; i++) {
|
||||
r.read_string(); // varname
|
||||
r.read_int(); // startpc
|
||||
r.read_int(); // endpc
|
||||
if (!r.ok) return false;
|
||||
}
|
||||
|
||||
// Upvalue names.
|
||||
n = static_cast<int>(r.read_size());
|
||||
if (!r.ok) return false;
|
||||
for (int i = 0; i < n; i++) {
|
||||
r.read_string();
|
||||
if (!r.ok) return false;
|
||||
}
|
||||
|
||||
(void)p;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool load_proto(bc_reader &r, lua_bc_proto *p,
|
||||
const std::string &parent_source) {
|
||||
// Source name.
|
||||
p->source = r.read_string();
|
||||
if (!r.ok) return false;
|
||||
if (p->source.empty()) p->source = parent_source;
|
||||
|
||||
p->linedefined = r.read_int();
|
||||
p->lastlinedefined = r.read_int();
|
||||
p->numparams = r.read_byte();
|
||||
p->is_vararg = r.read_byte();
|
||||
p->maxstacksize = r.read_byte();
|
||||
if (!r.ok) return false;
|
||||
|
||||
// Instructions.
|
||||
int ncode = static_cast<int>(r.read_size());
|
||||
if (!r.ok) return false;
|
||||
p->code.resize(ncode);
|
||||
for (int i = 0; i < ncode; i++) {
|
||||
uint32_t w = 0;
|
||||
r.read_bytes(&w, 4);
|
||||
if (!r.ok) return false;
|
||||
p->code[i].raw = w;
|
||||
}
|
||||
|
||||
// Constants.
|
||||
if (!load_constants(r, p)) return false;
|
||||
|
||||
// Upvalues.
|
||||
if (!load_upvalues(r, p)) return false;
|
||||
|
||||
// Nested protos.
|
||||
if (!load_protos(r, p, p->source)) return false;
|
||||
|
||||
// Debug info (skip).
|
||||
if (!skip_debug(r, p)) return false;
|
||||
|
||||
return r.ok;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Top-level entry point.
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
bool lua_bc_load(const uint8_t *data, size_t len, lua_bc_chunk *out) {
|
||||
if (nullptr == data || nullptr == out) return false;
|
||||
if (len < 4) return false;
|
||||
|
||||
bc_reader r(data, len);
|
||||
|
||||
// Header: signature.
|
||||
if (!r.match(LUA_SIGNATURE, sizeof(LUA_SIGNATURE))) return false;
|
||||
|
||||
// Version.
|
||||
out->version = r.read_byte();
|
||||
if (!r.ok || out->version != LUAC_VERSION) return false;
|
||||
|
||||
// Format.
|
||||
out->format = r.read_byte();
|
||||
if (!r.ok || out->format != LUAC_FORMAT) return false;
|
||||
|
||||
// LUAC_DATA magic.
|
||||
if (!r.match(LUAC_DATA, sizeof(LUAC_DATA))) return false;
|
||||
|
||||
// Type sizes.
|
||||
out->insn_size = r.read_byte(); // sizeof(Instruction) = 4
|
||||
out->lua_int_size = r.read_byte(); // sizeof(lua_Integer) = 8
|
||||
out->lua_num_size = r.read_byte(); // sizeof(lua_Number) = 8
|
||||
if (!r.ok) return false;
|
||||
if (out->insn_size != 4 || out->lua_int_size != 8 || out->lua_num_size != 8)
|
||||
return false;
|
||||
|
||||
// Check integer.
|
||||
int64_t check_int = r.read_integer();
|
||||
if (!r.ok || check_int != LUAC_INT) return false;
|
||||
|
||||
// Check number.
|
||||
double check_num = r.read_number();
|
||||
if (!r.ok || check_num != LUAC_NUM) return false;
|
||||
|
||||
// Number of upvalues for main proto.
|
||||
out->num_upvalues = r.read_byte();
|
||||
if (!r.ok) return false;
|
||||
|
||||
// Load main proto.
|
||||
if (!load_proto(r, &out->main, "")) return false;
|
||||
|
||||
return r.ok;
|
||||
}
|
||||
224
mux/modules/engine/lua_bytecode.h
Normal file
224
mux/modules/engine/lua_bytecode.h
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
/*! \file lua_bytecode.h
|
||||
* \brief Lua 5.4 bytecode structures — standalone deserializer.
|
||||
*
|
||||
* Our own Lua 5.4 bytecode structures. No Lua headers needed.
|
||||
* engine.so reads the output of lua_dump() from lua_mod.so and
|
||||
* lowers it through the HIR/RV64/x86-64 pipeline.
|
||||
*/
|
||||
|
||||
#ifndef LUA_BYTECODE_H
|
||||
#define LUA_BYTECODE_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Lua 5.4 opcodes (all 83)
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
enum lua_bc_opcode {
|
||||
OP_LUA_MOVE, // 0
|
||||
OP_LUA_LOADI, // 1
|
||||
OP_LUA_LOADF, // 2
|
||||
OP_LUA_LOADK, // 3
|
||||
OP_LUA_LOADKX, // 4
|
||||
OP_LUA_LOADFALSE, // 5
|
||||
OP_LUA_LFALSESKIP, // 6
|
||||
OP_LUA_LOADTRUE, // 7
|
||||
OP_LUA_LOADNIL, // 8
|
||||
OP_LUA_GETUPVAL, // 9
|
||||
OP_LUA_SETUPVAL, // 10
|
||||
OP_LUA_GETTABUP, // 11
|
||||
OP_LUA_GETTABLE, // 12
|
||||
OP_LUA_GETTABI, // 13
|
||||
OP_LUA_GETFIELD, // 14
|
||||
OP_LUA_SETTABUP, // 15
|
||||
OP_LUA_SETTABLE, // 16
|
||||
OP_LUA_SETTABI, // 17
|
||||
OP_LUA_SETFIELD, // 18
|
||||
OP_LUA_NEWTABLE, // 19
|
||||
OP_LUA_SELF, // 20
|
||||
OP_LUA_ADDI, // 21
|
||||
OP_LUA_ADDK, // 22
|
||||
OP_LUA_SUBK, // 23
|
||||
OP_LUA_MULK, // 24
|
||||
OP_LUA_MODK, // 25
|
||||
OP_LUA_POWK, // 26
|
||||
OP_LUA_DIVK, // 27
|
||||
OP_LUA_IDIVK, // 28
|
||||
OP_LUA_BANDK, // 29
|
||||
OP_LUA_BORK, // 30
|
||||
OP_LUA_BXORK, // 31
|
||||
OP_LUA_SHRI, // 32
|
||||
OP_LUA_SHLI, // 33
|
||||
OP_LUA_ADD, // 34
|
||||
OP_LUA_SUB, // 35
|
||||
OP_LUA_MUL, // 36
|
||||
OP_LUA_MOD, // 37
|
||||
OP_LUA_POW, // 38
|
||||
OP_LUA_DIV, // 39
|
||||
OP_LUA_IDIV, // 40
|
||||
OP_LUA_BAND, // 41
|
||||
OP_LUA_BOR, // 42
|
||||
OP_LUA_BXOR, // 43
|
||||
OP_LUA_SHL, // 44
|
||||
OP_LUA_SHR, // 45
|
||||
OP_LUA_MMBIN, // 46
|
||||
OP_LUA_MMBINI, // 47
|
||||
OP_LUA_MMBINK, // 48
|
||||
OP_LUA_UNM, // 49
|
||||
OP_LUA_BNOT, // 50
|
||||
OP_LUA_NOT, // 51
|
||||
OP_LUA_LEN, // 52
|
||||
OP_LUA_CONCAT, // 53
|
||||
OP_LUA_CLOSE, // 54
|
||||
OP_LUA_TBC, // 55
|
||||
OP_LUA_JMP, // 56
|
||||
OP_LUA_EQ, // 57
|
||||
OP_LUA_LT, // 58
|
||||
OP_LUA_LE, // 59
|
||||
OP_LUA_EQK, // 60
|
||||
OP_LUA_EQI, // 61
|
||||
OP_LUA_LTI, // 62
|
||||
OP_LUA_LEI, // 63
|
||||
OP_LUA_GTI, // 64
|
||||
OP_LUA_GEI, // 65
|
||||
OP_LUA_TEST, // 66
|
||||
OP_LUA_TESTSET, // 67
|
||||
OP_LUA_CALL, // 68
|
||||
OP_LUA_TAILCALL, // 69
|
||||
OP_LUA_RETURN, // 70
|
||||
OP_LUA_RETURN0, // 71
|
||||
OP_LUA_RETURN1, // 72
|
||||
OP_LUA_FORLOOP, // 73
|
||||
OP_LUA_FORPREP, // 74
|
||||
OP_LUA_TFORPREP, // 75
|
||||
OP_LUA_TFORCALL, // 76
|
||||
OP_LUA_TFORLOOP, // 77
|
||||
OP_LUA_SETLIST, // 78
|
||||
OP_LUA_CLOSURE, // 79
|
||||
OP_LUA_VARARG, // 80
|
||||
OP_LUA_VARARGPREP, // 81
|
||||
OP_LUA_EXTRAARG, // 82
|
||||
OP_LUA_NUM_OPCODES
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Instruction field accessors
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
// Lua 5.4 instruction format: 32-bit word
|
||||
// iABC: C:8 | B:8 | k:1 | A:8 | Op:7
|
||||
// iABx: Bx:17 | A:8 | Op:7 (unsigned)
|
||||
// iAsBx: sBx:17 | A:8 | Op:7 (signed = Bx - offset)
|
||||
// iAx: Ax:25 | Op:7
|
||||
// isJ: sJ:25 | Op:7
|
||||
|
||||
struct lua_bc_instruction {
|
||||
uint32_t raw;
|
||||
|
||||
int opcode() const { return raw & 0x7F; }
|
||||
int A() const { return (raw >> 7) & 0xFF; }
|
||||
int k() const { return (raw >> 15) & 0x1; }
|
||||
int B() const { return (raw >> 16) & 0xFF; }
|
||||
int sB() const { return B() - 128; } // signed B field
|
||||
int C() const { return (raw >> 24) & 0xFF; }
|
||||
int sC() const { return C() - 128; } // signed C field
|
||||
int Bx() const { return (raw >> 15) & 0x1FFFF; } // bits 15-31
|
||||
int sBx() const { return Bx() - 65535; } // offset = (2^17 - 1) / 2
|
||||
int Ax() const { return (raw >> 7) & 0x1FFFFFF; }
|
||||
int sJ() const { return static_cast<int>((raw >> 7) & 0x1FFFFFF) - (1 << 24); }
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Constant types
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
enum lua_bc_const_type {
|
||||
LUA_BC_TNIL = 0,
|
||||
LUA_BC_TFALSE = 1,
|
||||
LUA_BC_TTRUE = 17,
|
||||
LUA_BC_TINT = 3,
|
||||
LUA_BC_TFLOAT = 19,
|
||||
LUA_BC_TSHRSTR = 4,
|
||||
LUA_BC_TLNGSTR = 20,
|
||||
};
|
||||
|
||||
struct lua_bc_constant {
|
||||
lua_bc_const_type type;
|
||||
int64_t ival;
|
||||
double fval;
|
||||
std::string sval;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Upvalue descriptor
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
struct lua_bc_upvalue {
|
||||
uint8_t instack;
|
||||
uint8_t idx;
|
||||
uint8_t kind;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Function prototype
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
struct lua_bc_proto {
|
||||
// Source info.
|
||||
std::string source;
|
||||
int linedefined;
|
||||
int lastlinedefined;
|
||||
uint8_t numparams;
|
||||
uint8_t is_vararg;
|
||||
uint8_t maxstacksize;
|
||||
|
||||
// Code.
|
||||
std::vector<lua_bc_instruction> code;
|
||||
|
||||
// Constants.
|
||||
std::vector<lua_bc_constant> constants;
|
||||
|
||||
// Upvalues.
|
||||
std::vector<lua_bc_upvalue> upvalues;
|
||||
|
||||
// Nested protos.
|
||||
std::vector<lua_bc_proto> protos;
|
||||
|
||||
// Debug info (line numbers, local names) — we skip these.
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Top-level chunk
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
struct lua_bc_chunk {
|
||||
// Header fields.
|
||||
uint8_t version;
|
||||
uint8_t format;
|
||||
uint8_t int_size;
|
||||
uint8_t size_t_size;
|
||||
uint8_t insn_size;
|
||||
uint8_t lua_int_size;
|
||||
uint8_t lua_num_size;
|
||||
|
||||
// Main function prototype.
|
||||
lua_bc_proto main;
|
||||
|
||||
// Number of upvalues in main proto (from header).
|
||||
uint8_t num_upvalues;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Deserializer
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
// Load a Lua 5.4 bytecode dump into `out`.
|
||||
// Returns true on success, false on malformed input.
|
||||
//
|
||||
bool lua_bc_load(const uint8_t *data, size_t len, lua_bc_chunk *out);
|
||||
|
||||
#endif // LUA_BYTECODE_H
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
// Module bookkeeping.
|
||||
//
|
||||
|
|
@ -800,6 +801,7 @@ CLuaMod::CLuaMod(void) : m_cRef(1),
|
|||
m_pIAttributeAccess(nullptr),
|
||||
m_pIEvaluator(nullptr),
|
||||
m_pIPermissions(nullptr),
|
||||
m_pIJITCompile(nullptr),
|
||||
m_L(nullptr),
|
||||
m_nInsnLimit(LUA_DEFAULT_INSN_LIMIT),
|
||||
m_nMemLimit(LUA_DEFAULT_MEM_LIMIT),
|
||||
|
|
@ -861,6 +863,10 @@ MUX_RESULT CLuaMod::FinalConstruct(void)
|
|||
mux_CreateInstance(CID_Permissions, nullptr, UseSameProcess,
|
||||
IID_IPermissions, reinterpret_cast<void **>(&m_pIPermissions));
|
||||
|
||||
// Acquire JIT compile interface (optional — graceful degradation).
|
||||
mux_CreateInstance(CID_JITCompile, nullptr, UseSameProcess,
|
||||
IID_IJITCompile, reinterpret_cast<void **>(&m_pIJITCompile));
|
||||
|
||||
// Create the Lua state.
|
||||
//
|
||||
if (!CreateLuaState())
|
||||
|
|
@ -948,6 +954,12 @@ CLuaMod::~CLuaMod()
|
|||
m_pIPermissions = nullptr;
|
||||
}
|
||||
|
||||
if (nullptr != m_pIJITCompile)
|
||||
{
|
||||
m_pIJITCompile->Release();
|
||||
m_pIJITCompile = nullptr;
|
||||
}
|
||||
|
||||
if (g_pLuaMod == this)
|
||||
{
|
||||
g_pLuaMod = nullptr;
|
||||
|
|
@ -1051,6 +1063,8 @@ bool CLuaMod::LoadCached(const char *source, size_t nSource,
|
|||
cache_entry entry;
|
||||
entry.lua_ref = ref;
|
||||
entry.lru_it = m_cache_lru.begin();
|
||||
entry.jit_key = 0;
|
||||
entry.jit_eligible = false;
|
||||
m_cache[key] = entry;
|
||||
|
||||
return true;
|
||||
|
|
@ -1070,6 +1084,10 @@ void CLuaMod::CacheEvict(void)
|
|||
if (it != m_cache.end())
|
||||
{
|
||||
luaL_unref(m_L, LUA_REGISTRYINDEX, it->second.lua_ref);
|
||||
if (it->second.jit_key != 0 && nullptr != m_pIJITCompile)
|
||||
{
|
||||
m_pIJITCompile->Invalidate(it->second.jit_key);
|
||||
}
|
||||
m_cache.erase(it);
|
||||
}
|
||||
m_cache_lru.pop_back();
|
||||
|
|
@ -1083,11 +1101,88 @@ void CLuaMod::CacheClear(void)
|
|||
{
|
||||
luaL_unref(m_L, LUA_REGISTRYINDEX, pair.second.lua_ref);
|
||||
}
|
||||
if (pair.second.jit_key != 0 && nullptr != m_pIJITCompile)
|
||||
{
|
||||
m_pIJITCompile->Invalidate(pair.second.jit_key);
|
||||
}
|
||||
}
|
||||
m_cache.clear();
|
||||
m_cache_lru.clear();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// lua_dump writer callback — accumulates bytecode into a vector.
|
||||
// =========================================================================
|
||||
|
||||
struct dump_buffer {
|
||||
std::vector<uint8_t> data;
|
||||
};
|
||||
|
||||
static int dump_writer(lua_State *L, const void *p, size_t sz, void *ud) {
|
||||
(void)L;
|
||||
dump_buffer *buf = static_cast<dump_buffer *>(ud);
|
||||
const uint8_t *bytes = static_cast<const uint8_t *>(p);
|
||||
buf->data.insert(buf->data.end(), bytes, bytes + sz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// TryJIT: attempt to JIT-compile a cached chunk.
|
||||
// The chunk must be on top of the Lua stack.
|
||||
// Returns true if JIT succeeded and result is in pResult.
|
||||
// Returns false on JIT failure (caller should fall through to lua_pcall).
|
||||
// Does NOT pop the chunk from the stack.
|
||||
// =========================================================================
|
||||
|
||||
bool CLuaMod::TryJIT(cache_entry &entry, dbref executor, dbref caller,
|
||||
dbref enactor, const UTF8 *pArgs[], int nArgs,
|
||||
UTF8 *pResult, size_t nResultMax, size_t *pnResultLen)
|
||||
{
|
||||
if (nullptr == m_pIJITCompile) return false;
|
||||
|
||||
// Already tried and failed?
|
||||
if (entry.jit_eligible) {
|
||||
// Already have a compiled key? Run it.
|
||||
if (entry.jit_key != 0) {
|
||||
MUX_RESULT mr = m_pIJITCompile->RunCompiled(entry.jit_key,
|
||||
executor, caller, enactor, pArgs, nArgs,
|
||||
pResult, nResultMax, pnResultLen);
|
||||
return MUX_SUCCEEDED(mr);
|
||||
}
|
||||
return false; // Previously failed to compile.
|
||||
}
|
||||
|
||||
// First attempt: dump the chunk to bytecode and try JIT compilation.
|
||||
entry.jit_eligible = true;
|
||||
|
||||
// lua_dump expects the function on top of stack. We have it there
|
||||
// from LoadCached. Push a copy so we don't consume it.
|
||||
lua_pushvalue(m_L, -1);
|
||||
|
||||
dump_buffer buf;
|
||||
int dump_status = lua_dump(m_L, dump_writer, &buf, 0);
|
||||
lua_pop(m_L, 1); // pop the copy
|
||||
|
||||
if (dump_status != 0 || buf.data.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to compile.
|
||||
uint64_t key = 0;
|
||||
MUX_RESULT mr = m_pIJITCompile->CompileLuaBytecode(
|
||||
buf.data.data(), buf.data.size(), &key);
|
||||
if (MUX_FAILED(mr) || key == 0) {
|
||||
return false; // JIT doesn't support this bytecode; fall through.
|
||||
}
|
||||
|
||||
entry.jit_key = key;
|
||||
|
||||
// Run the compiled program.
|
||||
mr = m_pIJITCompile->RunCompiled(key, executor, caller, enactor,
|
||||
pArgs, nArgs, pResult, nResultMax, pnResultLen);
|
||||
return MUX_SUCCEEDED(mr);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// mux_ILuaControl implementation.
|
||||
// =========================================================================
|
||||
|
|
@ -1156,7 +1251,22 @@ MUX_RESULT CLuaMod::CallAttr(dbref executor, dbref caller, dbref enactor,
|
|||
return MUX_S_OK;
|
||||
}
|
||||
|
||||
// Execute.
|
||||
// Try JIT execution. The compiled chunk is on top of the Lua stack.
|
||||
// If JIT succeeds, pop the chunk and return.
|
||||
//
|
||||
std::string cache_key(reinterpret_cast<const char *>(source), nSource);
|
||||
auto cache_it = m_cache.find(cache_key);
|
||||
if (cache_it != m_cache.end())
|
||||
{
|
||||
if (TryJIT(cache_it->second, executor, caller, enactor,
|
||||
pArgs, nArgs, pResult, nResultMax, pnResultLen))
|
||||
{
|
||||
lua_pop(m_L, 1); // pop the chunk
|
||||
return MUX_S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall through to Lua VM execution.
|
||||
//
|
||||
ExecuteChunk(m_L, executor, caller, enactor, pArgs, nArgs,
|
||||
pResult, nResultMax, pnResultLen);
|
||||
|
|
@ -1208,6 +1318,22 @@ MUX_RESULT CLuaMod::Eval(dbref executor, dbref caller, dbref enactor,
|
|||
return MUX_S_OK;
|
||||
}
|
||||
|
||||
// Try JIT execution.
|
||||
//
|
||||
std::string cache_key(reinterpret_cast<const char *>(pSource), nSource);
|
||||
auto cache_it = m_cache.find(cache_key);
|
||||
if (cache_it != m_cache.end())
|
||||
{
|
||||
if (TryJIT(cache_it->second, executor, caller, enactor,
|
||||
nullptr, 0, pResult, nResultMax, pnResultLen))
|
||||
{
|
||||
lua_pop(m_L, 1); // pop the chunk
|
||||
return MUX_S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall through to Lua VM execution.
|
||||
//
|
||||
ExecuteChunk(m_L, executor, caller, enactor, nullptr, 0,
|
||||
pResult, nResultMax, pnResultLen);
|
||||
return MUX_S_OK;
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ private:
|
|||
mux_IAttributeAccess *m_pIAttributeAccess;
|
||||
mux_IEvaluator *m_pIEvaluator;
|
||||
mux_IPermissions *m_pIPermissions;
|
||||
mux_IJITCompile *m_pIJITCompile;
|
||||
|
||||
// Lua state - global, shared across all executions.
|
||||
//
|
||||
|
|
@ -83,6 +84,8 @@ private:
|
|||
{
|
||||
int lua_ref; // LUA_REGISTRYINDEX ref
|
||||
std::list<std::string>::iterator lru_it; // Position in LRU list
|
||||
uint64_t jit_key; // JIT compiled program key (0 = not compiled)
|
||||
bool jit_eligible; // true if JIT compilation was attempted
|
||||
};
|
||||
std::unordered_map<std::string, cache_entry> m_cache;
|
||||
std::list<std::string> m_cache_lru; // Front = most recent
|
||||
|
|
@ -97,6 +100,9 @@ private:
|
|||
bool LoadCached(const char *source, size_t nSource, const char *chunkname);
|
||||
void CacheEvict(void);
|
||||
void CacheClear(void);
|
||||
bool TryJIT(cache_entry &entry, dbref executor, dbref caller,
|
||||
dbref enactor, const UTF8 *pArgs[], int nArgs,
|
||||
UTF8 *pResult, size_t nResultMax, size_t *pnResultLen);
|
||||
bool ExecuteChunk(lua_State *L, dbref executor, dbref caller,
|
||||
dbref enactor, const UTF8 *pArgs[], int nArgs,
|
||||
UTF8 *pResult, size_t nResultMax, size_t *pnResultLen);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue