From a6def9eb4e0a60338c243907c2f0a11324abb4c8 Mon Sep 17 00:00:00 2001 From: leap0x7b Date: Sat, 21 Mar 2026 14:52:45 +0700 Subject: [PATCH 1/2] feat(vm): add jit support --- .gitmodules | 3 + CMakeLists.txt | 8 ++ src/data_win.c | 8 +- src/data_win.h | 1 + src/sljitConfigPre.h | 19 +++ src/vm.c | 103 ++++++++++------ src/vm_internal.h | 46 +++++++ src/vm_jit.c | 282 +++++++++++++++++++++++++++++++++++++++++++ src/vm_jit.h | 10 ++ vendor/sljit | 1 + 10 files changed, 440 insertions(+), 41 deletions(-) create mode 100644 .gitmodules create mode 100644 src/sljitConfigPre.h create mode 100644 src/vm_internal.h create mode 100644 src/vm_jit.c create mode 100644 src/vm_jit.h create mode 160000 vendor/sljit diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..8bf56ee8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "vendor/sljit"] + path = vendor/sljit + url = https://github.com/zherczeg/sljit diff --git a/CMakeLists.txt b/CMakeLists.txt index 64183d4f..38733841 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,14 @@ file(GLOB PLATFORM_SOURCES src/${PLATFORM}/*.c) add_executable(butterscotch ${SOURCES} ${PLATFORM_SOURCES}) target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src) +option(USE_JIT "Enable JIT compilation" ON) +if(USE_JIT) + add_compile_definitions(USE_JIT SLJIT_HAVE_CONFIG_PRE=1) + target_include_directories(butterscotch PRIVATE vendor/sljit/sljit_src) + target_sources(butterscotch PRIVATE vendor/sljit/sljit_src/sljitLir.c) + target_compile_definitions(butterscotch PRIVATE SLJIT_CONFIG_AUTO=1) +endif() + # stb_ds target_include_directories(butterscotch PUBLIC vendor/stb/ds) diff --git a/src/data_win.c b/src/data_win.c index 604e4346..eca022dc 100644 --- a/src/data_win.c +++ b/src/data_win.c @@ -1,6 +1,6 @@ #include "data_win.h" #include "binary_reader.h" - +#include "vm_jit.h" #include #include #include @@ -1111,6 +1111,7 @@ static void parseCODE(BinaryReader* reader, DataWin* dw, uint32_t chunkLength, s entry->bytecodeAbsoluteOffset = (uint32_t)((int64_t)relAddrFieldPos + bytecodeRelAddr); entry->offset = BinaryReader_readUint32(reader); + entry->jitCode = NULL; } free(codePtrs); @@ -1615,6 +1616,11 @@ void DataWin_free(DataWin* dw) { hmfree(dw->tpagOffsetMap); // CODE +#ifdef USE_JIT + repeat(dw->code.count, i) { + VM_jitFree(&dw->code.entries[i]); + } +#endif free(dw->code.entries); // VARI diff --git a/src/data_win.h b/src/data_win.h index 259bbba3..1496ba8a 100644 --- a/src/data_win.h +++ b/src/data_win.h @@ -546,6 +546,7 @@ typedef struct { uint16_t argumentsCount; uint32_t bytecodeAbsoluteOffset; uint32_t offset; + void* jitCode; } CodeEntry; typedef struct { diff --git a/src/sljitConfigPre.h b/src/sljitConfigPre.h new file mode 100644 index 00000000..743673f4 --- /dev/null +++ b/src/sljitConfigPre.h @@ -0,0 +1,19 @@ +#ifndef SLJIT_CONFIG_PRE_H_ +#define SLJIT_CONFIG_PRE_H_ + +#ifdef PS2 +#include +#include + +/* PS2-specific SLJIT configuration */ +#define SLJIT_UTIL_STACK 0 +#define SLJIT_EXECUTABLE_ALLOCATOR 0 +#define SLJIT_MALLOC_EXEC(size, exec_allocator_data) malloc(size) +#define SLJIT_FREE_EXEC(ptr, exec_allocator_data) free(ptr) + +/* Cache flush for Emotion Engine (MIPS) */ +#define SLJIT_CACHE_FLUSH(from, to) FlushCache(0) + +#endif + +#endif /* SLJIT_CONFIG_PRE_H_ */ diff --git a/src/vm.c b/src/vm.c index 710b9037..ab78f1c1 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1,9 +1,11 @@ #include "vm.h" +#include "vm_jit.h" #include "vm_builtins.h" #include "instance.h" #include "runner.h" #include "binary_utils.h" #include "utils.h" +#include "vm_internal.h" #include #include @@ -19,7 +21,7 @@ static bool shouldTraceStack(VMContext* ctx) { return shgeti(ctx->stackToBeTraced, "*") != -1 || shgeti(ctx->stackToBeTraced, ctx->currentCodeName) != -1; } -static void stackPush(VMContext* ctx, RValue val) { +void stackPush(VMContext* ctx, RValue val) { require(VM_STACK_SIZE > ctx->stack.top); if (shouldTraceStack(ctx)) { char* valStr = RValue_toStringTyped(val); @@ -29,7 +31,7 @@ static void stackPush(VMContext* ctx, RValue val) { ctx->stack.slots[ctx->stack.top++] = val; } -static RValue stackPop(VMContext* ctx) { +RValue stackPop(VMContext* ctx) { require(ctx->stack.top > 0); RValue val = ctx->stack.slots[--ctx->stack.top]; if (shouldTraceStack(ctx)) { @@ -40,18 +42,18 @@ static RValue stackPop(VMContext* ctx) { return val; } -static RValue* stackPeek(VMContext* ctx) { +RValue* stackPeek(VMContext* ctx) { require(ctx->stack.top > 0); return &ctx->stack.slots[ctx->stack.top - 1]; } // ===[ Instruction Decoding ]=== -static uint8_t instrOpcode(uint32_t instr) { +uint8_t instrOpcode(uint32_t instr) { return (instr >> 24) & 0xFF; } -static uint8_t instrType1(uint32_t instr) { +uint8_t instrType1(uint32_t instr) { return (instr >> 16) & 0xF; } @@ -67,16 +69,16 @@ static uint8_t instrCmpKind(uint32_t instr) { return (instr >> 8) & 0xFF; } -static bool instrHasExtraData(uint32_t instr) { +bool instrHasExtraData(uint32_t instr) { return (instr & 0x40000000) != 0; } // Jump offset for branch instructions: sign-extend 23 bits, multiply by 4 -static int32_t instrJumpOffset(uint32_t instr) { +int32_t instrJumpOffset(uint32_t instr) { return ((int32_t) (instr << 9)) >> 7; } -static uint32_t extraDataSize(uint8_t type1) { +uint32_t extraDataSize(uint8_t type1) { switch (type1) { case GML_TYPE_DOUBLE: return 8; case GML_TYPE_INT64: return 8; @@ -745,11 +747,10 @@ static RValue convertValue(RValue val, uint8_t targetType) { // ===[ Forward Declarations ]=== static RValue executeLoop(VMContext* ctx); -static void handleCall(VMContext* ctx, uint32_t instr, const uint8_t* extraData); // ===[ Opcode Handlers ]=== -static void handlePush(VMContext* ctx, uint32_t instr, const uint8_t* extraData) { +void handlePush(VMContext* ctx, uint32_t instr, const uint8_t* extraData) { uint8_t type1 = instrType1(instr); switch (type1) { @@ -820,15 +821,15 @@ static void handlePushScoped(VMContext* ctx, uint32_t instr, const uint8_t* extr stackPush(ctx,val); } -static void handlePushLoc(VMContext* ctx, uint32_t instr, const uint8_t* extraData) { +void handlePushLoc(VMContext* ctx, uint32_t instr, const uint8_t* extraData) { handlePushScoped(ctx, instr, extraData, ctx->localArrayMap, ctx->localVarCount, ctx->localVars, "local", nullptr, nullptr); } -static void handlePushGlb(VMContext* ctx, uint32_t instr, const uint8_t* extraData) { +void handlePushGlb(VMContext* ctx, uint32_t instr, const uint8_t* extraData) { handlePushScoped(ctx, instr, extraData, ctx->globalArrayMap, ctx->globalVarCount, ctx->globalVars, "global", nullptr, ctx->varReadsToBeTraced); } -static void handlePushBltn(VMContext* ctx, uint32_t instr, const uint8_t* extraData) { +void handlePushBltn(VMContext* ctx, uint32_t instr, const uint8_t* extraData) { (void) instr; uint32_t varRef = resolveVarOperand(extraData); Variable* varDef = resolveVarDef(ctx, varRef); @@ -839,12 +840,12 @@ static void handlePushBltn(VMContext* ctx, uint32_t instr, const uint8_t* extraD stackPush(ctx,val); } -static void handlePushI(VMContext* ctx, uint32_t instr) { +void handlePushI(VMContext* ctx, uint32_t instr) { int16_t value = (int16_t) (instr & 0xFFFF); stackPush(ctx,RValue_makeInt32((int32_t) value)); } -static void handlePop(VMContext* ctx, uint32_t instr, const uint8_t* extraData) { +void handlePop(VMContext* ctx, uint32_t instr, const uint8_t* extraData) { int32_t instanceType = (int32_t) instrInstanceType(instr); uint8_t type1 = instrType1(instr); // destination type uint8_t type2 = instrType2(instr); // source type (what's on stack) @@ -1002,12 +1003,12 @@ static void handlePop(VMContext* ctx, uint32_t instr, const uint8_t* extraData) } } -static void handlePopz(VMContext* ctx) { +void handlePopz(VMContext* ctx) { RValue val = stackPop(ctx); RValue_free(&val); } -static void handleAdd(VMContext* ctx) { +void handleAdd(VMContext* ctx) { RValue b = stackPop(ctx); RValue a = stackPop(ctx); @@ -1049,7 +1050,7 @@ static void handleAdd(VMContext* ctx) { } } -static void handleSub(VMContext* ctx) { +void handleSub(VMContext* ctx) { RValue b = stackPop(ctx); RValue a = stackPop(ctx); if (a.type == RVALUE_INT32 && b.type == RVALUE_INT32) { @@ -1064,7 +1065,7 @@ static void handleSub(VMContext* ctx) { } } -static void handleMul(VMContext* ctx) { +void handleMul(VMContext* ctx) { RValue b = stackPop(ctx); RValue a = stackPop(ctx); @@ -1099,7 +1100,7 @@ static void handleMul(VMContext* ctx) { } } -static void handleDiv(VMContext* ctx) { +void handleDiv(VMContext* ctx) { RValue b = stackPop(ctx); RValue a = stackPop(ctx); double divisor = RValue_toReal(b); @@ -1113,7 +1114,7 @@ static void handleDiv(VMContext* ctx) { stackPush(ctx,RValue_makeReal(result)); } -static void handleRem(VMContext* ctx) { +void handleRem(VMContext* ctx) { RValue b = stackPop(ctx); RValue a = stackPop(ctx); int32_t ib = RValue_toInt32(b); @@ -1127,7 +1128,7 @@ static void handleRem(VMContext* ctx) { stackPush(ctx,RValue_makeInt32(result)); } -static void handleMod(VMContext* ctx) { +void handleMod(VMContext* ctx) { RValue b = stackPop(ctx); RValue a = stackPop(ctx); double divisor = RValue_toReal(b); @@ -1149,26 +1150,26 @@ static void handleMod(VMContext* ctx) { RValue_free(&b); \ stackPush(ctx,RValue_makeInt32(result)) -static void handleAnd(VMContext* ctx) { +void handleAnd(VMContext* ctx) { SIMPLE_BYTECODE_BITWISE_OPERATION(&); } -static void handleOr(VMContext* ctx) { +void handleOr(VMContext* ctx) { SIMPLE_BYTECODE_BITWISE_OPERATION(|); } -static void handleXor(VMContext* ctx) { +void handleXor(VMContext* ctx) { SIMPLE_BYTECODE_BITWISE_OPERATION(^); } -static void handleNeg(VMContext* ctx) { +void handleNeg(VMContext* ctx) { RValue a = stackPop(ctx); double result = -RValue_toReal(a); RValue_free(&a); stackPush(ctx,RValue_makeReal(result)); } -static void handleNot(VMContext* ctx, uint32_t instr) { +void handleNot(VMContext* ctx, uint32_t instr) { RValue a = stackPop(ctx); uint8_t type1 = instrType1(instr); if (GML_TYPE_BOOL == type1) { @@ -1184,15 +1185,15 @@ static void handleNot(VMContext* ctx, uint32_t instr) { } } -static void handleShl(VMContext* ctx) { +void handleShl(VMContext* ctx) { SIMPLE_BYTECODE_BITWISE_OPERATION(<<); } -static void handleShr(VMContext* ctx) { +void handleShr(VMContext* ctx) { SIMPLE_BYTECODE_BITWISE_OPERATION(>>); } -static void handleConv(VMContext* ctx, uint32_t instr) { +void handleConv(VMContext* ctx, uint32_t instr) { uint8_t srcType = instrType1(instr); uint8_t dstType = instrType2(instr); @@ -1284,7 +1285,7 @@ static void handleConv(VMContext* ctx, uint32_t instr) { stackPush(ctx,result); } -static void handleCmp(VMContext* ctx, uint32_t instr) { +void handleCmp(VMContext* ctx, uint32_t instr) { uint8_t cmpKind = instrCmpKind(instr); RValue b = stackPop(ctx); RValue a = stackPop(ctx); @@ -1323,7 +1324,7 @@ static void handleCmp(VMContext* ctx, uint32_t instr) { stackPush(ctx,RValue_makeBool(result)); } -static void handleDup(VMContext* ctx, uint32_t instr) { +void handleDup(VMContext* ctx, uint32_t instr) { // The Extra field (lower 8 bits) encodes how many additional items beyond 1 to duplicate. // dup.i 0 = duplicate 1 item, dup.i 1 = duplicate 2 items (used for array access: instanceType + arrayIndex), etc. uint8_t extra = (uint8_t)(instr & 0xFF); @@ -1345,12 +1346,12 @@ static void handleDup(VMContext* ctx, uint32_t instr) { } } -static void handleBranch(VMContext* ctx, uint32_t instr, uint32_t instrAddr) { +void handleBranch(VMContext* ctx, uint32_t instr, uint32_t instrAddr) { int32_t offset = instrJumpOffset(instr); ctx->ip = instrAddr + offset; } -static void handleBranchTrue(VMContext* ctx, uint32_t instr, uint32_t instrAddr) { +void handleBranchTrue(VMContext* ctx, uint32_t instr, uint32_t instrAddr) { RValue val = stackPop(ctx); bool condition = RValue_toInt32(val) != 0; RValue_free(&val); @@ -1360,7 +1361,7 @@ static void handleBranchTrue(VMContext* ctx, uint32_t instr, uint32_t instrAddr) } } -static void handleBranchFalse(VMContext* ctx, uint32_t instr, uint32_t instrAddr) { +void handleBranchFalse(VMContext* ctx, uint32_t instr, uint32_t instrAddr) { RValue val = stackPop(ctx); bool condition = RValue_toInt32(val) != 0; RValue_free(&val); @@ -1372,7 +1373,7 @@ static void handleBranchFalse(VMContext* ctx, uint32_t instr, uint32_t instrAddr // ===[ Function Call Handler ]=== -static void handleCall(VMContext* ctx, uint32_t instr, const uint8_t* extraData) { +void handleCall(VMContext* ctx, uint32_t instr, const uint8_t* extraData) { int32_t argCount = instr & 0xFFFF; uint32_t funcIndex = resolveFuncOperand(extraData); require(ctx->dataWin->func.functionCount > funcIndex); @@ -1508,7 +1509,7 @@ static void restoreEnvContext(VMContext* ctx, EnvFrame* frame) { ctx->otherInstance = frame->savedOtherInstance; } -static void handlePushEnv(VMContext* ctx, uint32_t instr, uint32_t instrAddr) { +void handlePushEnv(VMContext* ctx, uint32_t instr, uint32_t instrAddr) { int32_t jumpOffset = instrJumpOffset(instr); // Pop target from stack @@ -1616,7 +1617,7 @@ static void handlePushEnv(VMContext* ctx, uint32_t instr, uint32_t instrAddr) { ctx->ip = instrAddr + jumpOffset; } -static void handlePopEnv(VMContext* ctx, uint32_t instr, uint32_t instrAddr) { +void handlePopEnv(VMContext* ctx, uint32_t instr, uint32_t instrAddr) { EnvFrame* frame = ctx->envStack; require(frame != nullptr); @@ -1943,7 +1944,18 @@ RValue VM_executeCode(VMContext* ctx, int32_t codeIndex) { // Reset stack for top-level execution ctx->stack.top = 0; - RValue result = executeLoop(ctx); + RValue result; +#ifdef USE_JIT + VM_jitCompile(ctx, codeIndex); + if (code->jitCode != NULL) { + JitFunc jitFunc = (JitFunc)code->jitCode; + jitFunc(ctx, &result); + } else { + result = executeLoop(ctx); + } +#else + result = executeLoop(ctx); +#endif // Free locals repeat(ctx->localVarCount, i) { @@ -2022,7 +2034,18 @@ RValue VM_callCodeIndex(VMContext* ctx, int32_t codeIndex, RValue* args, int32_t } // Execute the callee - RValue result = executeLoop(ctx); + RValue result; +#ifdef USE_JIT + VM_jitCompile(ctx, codeIndex); + if (code->jitCode != NULL) { + JitFunc jitFunc = (JitFunc)code->jitCode; + jitFunc(ctx, &result); + } else { + result = executeLoop(ctx); + } +#else + result = executeLoop(ctx); +#endif // Make result string owning BEFORE freeing callee locals/arrays to prevent // dangling pointer if the returned string points into a callee local var or array map. diff --git a/src/vm_internal.h b/src/vm_internal.h new file mode 100644 index 00000000..d57e2645 --- /dev/null +++ b/src/vm_internal.h @@ -0,0 +1,46 @@ +#pragma once + +#include "vm.h" + +// ===[ VM functions exposed for JIT ]=== +void handlePush(VMContext* ctx, uint32_t instr, const uint8_t* extraData); +void handlePushLoc(VMContext* ctx, uint32_t instr, const uint8_t* extraData); +void handlePushGlb(VMContext* ctx, uint32_t instr, const uint8_t* extraData); +void handlePushBltn(VMContext* ctx, uint32_t instr, const uint8_t* extraData); +void handlePushI(VMContext* ctx, uint32_t instr); +void handlePop(VMContext* ctx, uint32_t instr, const uint8_t* extraData); +void handlePopz(VMContext* ctx); +void handleAdd(VMContext* ctx); +void handleSub(VMContext* ctx); +void handleMul(VMContext* ctx); +void handleDiv(VMContext* ctx); +void handleRem(VMContext* ctx); +void handleMod(VMContext* ctx); +void handleAnd(VMContext* ctx); +void handleOr(VMContext* ctx); +void handleXor(VMContext* ctx); +void handleShl(VMContext* ctx); +void handleShr(VMContext* ctx); +void handleNeg(VMContext* ctx); +void handleNot(VMContext* ctx, uint32_t instr); +void handleConv(VMContext* ctx, uint32_t instr); +void handleCmp(VMContext* ctx, uint32_t instr); +void handleDup(VMContext* ctx, uint32_t instr); +void handleBranch(VMContext* ctx, uint32_t instr, uint32_t instrAddr); +void handleBranchTrue(VMContext* ctx, uint32_t instr, uint32_t instrAddr); +void handleBranchFalse(VMContext* ctx, uint32_t instr, uint32_t instrAddr); +void handleCall(VMContext* ctx, uint32_t instr, const uint8_t* extraData); +void handlePushEnv(VMContext* ctx, uint32_t instr, uint32_t instrAddr); +void handlePopEnv(VMContext* ctx, uint32_t instr, uint32_t instrAddr); + +// ===[ Decoding helpers ]=== +uint8_t instrOpcode(uint32_t instr); +uint8_t instrType1(uint32_t instr); +uint32_t extraDataSize(uint8_t type1); +bool instrHasExtraData(uint32_t instr); +int32_t instrJumpOffset(uint32_t instr); + +// ===[ Stack helpers ]=== +void stackPush(VMContext* ctx, RValue val); +RValue stackPop(VMContext* ctx); +RValue* stackPeek(VMContext* ctx); diff --git a/src/vm_jit.c b/src/vm_jit.c new file mode 100644 index 00000000..d73cc8ce --- /dev/null +++ b/src/vm_jit.c @@ -0,0 +1,282 @@ +#include "vm_jit.h" +#include "vm_internal.h" +#include "binary_utils.h" +#include "utils.h" +#include "stb_ds.h" +#include +#include + +#ifdef USE_JIT +#include "sljitLir.h" + +// ===[ Forward declarations ]=== +sljit_sw RValue_toBool_Pop(VMContext* ctx); +void VM_popAndStoreResult(VMContext* ctx, RValue* dest); +void VM_storeUndefined(RValue* dest); +void VM_jitAbort(uint8_t opcode); + +typedef struct { + struct sljit_jump* jump; + uint32_t target_ip; +} JumpFixup; + +void VM_jitCompile(VMContext* ctx, int32_t codeIndex) { + CodeEntry* code = &ctx->dataWin->code.entries[codeIndex]; + if (code->jitCode != NULL) return; + + struct sljit_compiler* compiler = sljit_create_compiler(NULL); + if (!compiler) return; + + // Signature: void jit_func(VMContext* ctx, RValue* out_result) + // S0 = ctx, S1 = out_result + sljit_emit_enter(compiler, 0, SLJIT_ARGS2V(W, W), 3, 2, 0); + + uint8_t* bytecode = ctx->dataWin->bytecodeBuffer + (code->bytecodeAbsoluteOffset - ctx->dataWin->bytecodeBufferBase); + + // Labels for each instruction offset + struct sljit_label** labels = safeCalloc(code->length + 1, sizeof(struct sljit_label*)); + JumpFixup* fixups = NULL; + + uint32_t ip = 0; + while (ip < code->length) { + labels[ip] = sljit_emit_label(compiler); + + uint32_t instrAddr = ip; + uint32_t instr = BinaryUtils_readUint32(bytecode + ip); + ip += 4; + + uint8_t* extraData = bytecode + ip; + uint32_t eDataSize = 0; + if (instrHasExtraData(instr)) { + eDataSize = extraDataSize(instrType1(instr)); + ip += eDataSize; + } + + uint8_t opcode = instrOpcode(instr); + + switch (opcode) { + case OP_PUSH: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)instr); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, (sljit_sw)extraData); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS3V(W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(handlePush)); + break; + case OP_PUSHLOC: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)instr); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, (sljit_sw)extraData); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS3V(W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(handlePushLoc)); + break; + case OP_PUSHGLB: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)instr); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, (sljit_sw)extraData); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS3V(W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(handlePushGlb)); + break; + case OP_PUSHBLTN: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)instr); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, (sljit_sw)extraData); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS3V(W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(handlePushBltn)); + break; + case OP_PUSHI: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)instr); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2V(W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(handlePushI)); + break; + case OP_POP: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)instr); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, (sljit_sw)extraData); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS3V(W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(handlePop)); + break; + case OP_POPZ: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(W), SLJIT_IMM, SLJIT_FUNC_ADDR(handlePopz)); + break; + case OP_ADD: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleAdd)); + break; + case OP_SUB: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleSub)); + break; + case OP_MUL: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleMul)); + break; + case OP_DIV: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleDiv)); + break; + case OP_REM: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleRem)); + break; + case OP_MOD: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleMod)); + break; + case OP_AND: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleAnd)); + break; + case OP_OR: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleOr)); + break; + case OP_XOR: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleXor)); + break; + case OP_SHL: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleShl)); + break; + case OP_SHR: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleShr)); + break; + case OP_NEG: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleNeg)); + break; + case OP_NOT: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)instr); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2V(W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleNot)); + break; + case OP_CONV: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)instr); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2V(W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleConv)); + break; + case OP_CMP: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)instr); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2V(W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleCmp)); + break; + case OP_DUP: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)instr); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2V(W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleDup)); + break; + case OP_B: { + int32_t offset = instrJumpOffset(instr); + uint32_t target_ip = (uint32_t)((int32_t)instrAddr + offset); + struct sljit_jump* jump = sljit_emit_jump(compiler, SLJIT_JUMP); + JumpFixup jf = {jump, target_ip}; + arrput(fixups, jf); + break; + } + case OP_BT: { + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1(W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(RValue_toBool_Pop)); + + sljit_emit_op2u(compiler, SLJIT_SUB | SLJIT_SET_Z, SLJIT_R0, 0, SLJIT_IMM, 0); + int32_t offset = instrJumpOffset(instr); + uint32_t target_ip = (uint32_t)((int32_t)instrAddr + offset); + struct sljit_jump* jump = sljit_emit_jump(compiler, SLJIT_NOT_EQUAL); // If true + JumpFixup jf = {jump, target_ip}; + arrput(fixups, jf); + break; + } + case OP_BF: { + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1(W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(RValue_toBool_Pop)); + + sljit_emit_op2u(compiler, SLJIT_SUB | SLJIT_SET_Z, SLJIT_R0, 0, SLJIT_IMM, 0); + int32_t offset = instrJumpOffset(instr); + uint32_t target_ip = (uint32_t)((int32_t)instrAddr + offset); + struct sljit_jump* jump = sljit_emit_jump(compiler, SLJIT_EQUAL); // If false + JumpFixup jf = {jump, target_ip}; + arrput(fixups, jf); + break; + } + case OP_CALL: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)instr); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, (sljit_sw)extraData); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS3V(W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleCall)); + break; + case OP_RET: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, SLJIT_S1, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2V(W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(VM_popAndStoreResult)); + sljit_emit_return_void(compiler); + break; + case OP_EXIT: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S1, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(W), SLJIT_IMM, SLJIT_FUNC_ADDR(VM_storeUndefined)); + sljit_emit_return_void(compiler); + break; + case OP_PUSHENV: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)instr); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, (sljit_sw)instrAddr); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS3V(W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(handlePushEnv)); + break; + case OP_POPENV: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)instr); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, (sljit_sw)instrAddr); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS3V(W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(handlePopEnv)); + break; + case OP_BREAK: + break; + default: + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_IMM, (sljit_sw)opcode); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(W), SLJIT_IMM, SLJIT_FUNC_ADDR(VM_jitAbort)); + break; + } + } + + // End of code: return undefined if we somehow fall through + labels[code->length] = sljit_emit_label(compiler); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S1, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(W), SLJIT_IMM, SLJIT_FUNC_ADDR(VM_storeUndefined)); + sljit_emit_return_void(compiler); + + // Resolve jumps + for (int i = 0; i < arrlen(fixups); i++) { + if (fixups[i].target_ip < code->length + 1 && labels[fixups[i].target_ip]) { + sljit_set_label(fixups[i].jump, labels[fixups[i].target_ip]); + } + } + + code->jitCode = sljit_generate_code(compiler, 0, NULL); + sljit_free_compiler(compiler); + free(labels); + arrfree(fixups); +} + +void VM_jitFree(CodeEntry* entry) { + if (entry->jitCode) { + sljit_free_code(entry->jitCode, NULL); + entry->jitCode = NULL; + } +} + +// ===[ Helpers ]=== +sljit_sw RValue_toBool_Pop(VMContext* ctx) { + RValue val = stackPop(ctx); + bool b = RValue_toBool(val); + RValue_free(&val); + return b ? 1 : 0; +} + +void VM_popAndStoreResult(VMContext* ctx, RValue* dest) { + *dest = stackPop(ctx); +} + +void VM_storeUndefined(RValue* dest) { + *dest = RValue_makeUndefined(); +} + +void VM_jitAbort(uint8_t opcode) { + fprintf(stderr, "VM JIT: Unsupported opcode 0x%02x\n", opcode); + abort(); +} + +#endif diff --git a/src/vm_jit.h b/src/vm_jit.h new file mode 100644 index 00000000..08f932f6 --- /dev/null +++ b/src/vm_jit.h @@ -0,0 +1,10 @@ +#pragma once + +#include "vm.h" + +#ifdef USE_JIT +typedef void (*JitFunc)(VMContext* ctx, RValue* result); + +void VM_jitCompile(VMContext* ctx, int32_t codeIndex); +void VM_jitFree(CodeEntry* entry); +#endif diff --git a/vendor/sljit b/vendor/sljit new file mode 160000 index 00000000..d9902b1b --- /dev/null +++ b/vendor/sljit @@ -0,0 +1 @@ +Subproject commit d9902b1ba06f477dcb89137dae8c8649fb58b7fb From fb2e579f1c03c3ddc3c769e88963392b8bfe400f Mon Sep 17 00:00:00 2001 From: leap0x7b Date: Sun, 22 Mar 2026 13:59:20 +0700 Subject: [PATCH 2/2] fix(jit): fix dialog boxes softlock in undertale --- src/vm_jit.c | 89 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 23 deletions(-) diff --git a/src/vm_jit.c b/src/vm_jit.c index d73cc8ce..979db2b9 100644 --- a/src/vm_jit.c +++ b/src/vm_jit.c @@ -5,12 +5,12 @@ #include "stb_ds.h" #include #include +#include #ifdef USE_JIT #include "sljitLir.h" // ===[ Forward declarations ]=== -sljit_sw RValue_toBool_Pop(VMContext* ctx); void VM_popAndStoreResult(VMContext* ctx, RValue* dest); void VM_storeUndefined(RValue* dest); void VM_jitAbort(uint8_t opcode); @@ -32,7 +32,7 @@ void VM_jitCompile(VMContext* ctx, int32_t codeIndex) { sljit_emit_enter(compiler, 0, SLJIT_ARGS2V(W, W), 3, 2, 0); uint8_t* bytecode = ctx->dataWin->bytecodeBuffer + (code->bytecodeAbsoluteOffset - ctx->dataWin->bytecodeBufferBase); - + // Labels for each instruction offset struct sljit_label** labels = safeCalloc(code->length + 1, sizeof(struct sljit_label*)); JumpFixup* fixups = NULL; @@ -52,6 +52,9 @@ void VM_jitCompile(VMContext* ctx, int32_t codeIndex) { ip += eDataSize; } + // Sync ctx->ip to the address of the NEXT instruction + sljit_emit_op1(compiler, SLJIT_MOV_U32, SLJIT_MEM1(SLJIT_S0), offsetof(VMContext, ip), SLJIT_IMM, (sljit_sw)ip); + uint8_t opcode = instrOpcode(instr); switch (opcode) { @@ -165,6 +168,7 @@ void VM_jitCompile(VMContext* ctx, int32_t codeIndex) { case OP_B: { int32_t offset = instrJumpOffset(instr); uint32_t target_ip = (uint32_t)((int32_t)instrAddr + offset); + sljit_emit_op1(compiler, SLJIT_MOV_U32, SLJIT_MEM1(SLJIT_S0), offsetof(VMContext, ip), SLJIT_IMM, (sljit_sw)target_ip); struct sljit_jump* jump = sljit_emit_jump(compiler, SLJIT_JUMP); JumpFixup jf = {jump, target_ip}; arrput(fixups, jf); @@ -172,26 +176,42 @@ void VM_jitCompile(VMContext* ctx, int32_t codeIndex) { } case OP_BT: { sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); - sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1(W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(RValue_toBool_Pop)); - - sljit_emit_op2u(compiler, SLJIT_SUB | SLJIT_SET_Z, SLJIT_R0, 0, SLJIT_IMM, 0); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)instr); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, (sljit_sw)instrAddr); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS3V(W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleBranchTrue)); + + sljit_emit_op1(compiler, SLJIT_MOV_U32, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_S0), offsetof(VMContext, ip)); int32_t offset = instrJumpOffset(instr); uint32_t target_ip = (uint32_t)((int32_t)instrAddr + offset); - struct sljit_jump* jump = sljit_emit_jump(compiler, SLJIT_NOT_EQUAL); // If true - JumpFixup jf = {jump, target_ip}; - arrput(fixups, jf); + + sljit_emit_op2u(compiler, SLJIT_SUB | SLJIT_SET_Z, SLJIT_R0, 0, SLJIT_IMM, (sljit_sw)target_ip); + struct sljit_jump* jump_to_target = sljit_emit_jump(compiler, SLJIT_EQUAL); + JumpFixup jf_target = {jump_to_target, target_ip}; + arrput(fixups, jf_target); + + struct sljit_jump* jump_to_next = sljit_emit_jump(compiler, SLJIT_JUMP); + JumpFixup jf_next = {jump_to_next, ip}; + arrput(fixups, jf_next); break; } case OP_BF: { sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); - sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1(W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(RValue_toBool_Pop)); - - sljit_emit_op2u(compiler, SLJIT_SUB | SLJIT_SET_Z, SLJIT_R0, 0, SLJIT_IMM, 0); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)instr); + sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, (sljit_sw)instrAddr); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS3V(W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(handleBranchFalse)); + + sljit_emit_op1(compiler, SLJIT_MOV_U32, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_S0), offsetof(VMContext, ip)); int32_t offset = instrJumpOffset(instr); uint32_t target_ip = (uint32_t)((int32_t)instrAddr + offset); - struct sljit_jump* jump = sljit_emit_jump(compiler, SLJIT_EQUAL); // If false - JumpFixup jf = {jump, target_ip}; - arrput(fixups, jf); + + sljit_emit_op2u(compiler, SLJIT_SUB | SLJIT_SET_Z, SLJIT_R0, 0, SLJIT_IMM, (sljit_sw)target_ip); + struct sljit_jump* jump_to_target = sljit_emit_jump(compiler, SLJIT_EQUAL); + JumpFixup jf_target = {jump_to_target, target_ip}; + arrput(fixups, jf_target); + + struct sljit_jump* jump_to_next = sljit_emit_jump(compiler, SLJIT_JUMP); + JumpFixup jf_next = {jump_to_next, ip}; + arrput(fixups, jf_next); break; } case OP_CALL: @@ -211,18 +231,48 @@ void VM_jitCompile(VMContext* ctx, int32_t codeIndex) { sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(W), SLJIT_IMM, SLJIT_FUNC_ADDR(VM_storeUndefined)); sljit_emit_return_void(compiler); break; - case OP_PUSHENV: + case OP_PUSHENV: { sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)instr); sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, (sljit_sw)instrAddr); sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS3V(W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(handlePushEnv)); + + // PushEnv can jump to the end of the with-block + sljit_emit_op1(compiler, SLJIT_MOV_U32, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_S0), offsetof(VMContext, ip)); + int32_t offset = instrJumpOffset(instr); + uint32_t target_ip = (uint32_t)((int32_t)instrAddr + offset); + + sljit_emit_op2u(compiler, SLJIT_SUB | SLJIT_SET_Z, SLJIT_R0, 0, SLJIT_IMM, (sljit_sw)target_ip); + struct sljit_jump* jump_to_target = sljit_emit_jump(compiler, SLJIT_EQUAL); + JumpFixup jf_target = {jump_to_target, target_ip}; + arrput(fixups, jf_target); + + struct sljit_jump* jump_to_next = sljit_emit_jump(compiler, SLJIT_JUMP); + JumpFixup jf_next = {jump_to_next, ip}; + arrput(fixups, jf_next); break; - case OP_POPENV: + } + case OP_POPENV: { sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0); sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)instr); sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, (sljit_sw)instrAddr); sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS3V(W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(handlePopEnv)); + + // PopEnv can jump back to the start of the with-block + sljit_emit_op1(compiler, SLJIT_MOV_U32, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_S0), offsetof(VMContext, ip)); + int32_t offset = instrJumpOffset(instr); + uint32_t target_ip = (uint32_t)((int32_t)instrAddr + offset); + + sljit_emit_op2u(compiler, SLJIT_SUB | SLJIT_SET_Z, SLJIT_R0, 0, SLJIT_IMM, (sljit_sw)target_ip); + struct sljit_jump* jump_to_target = sljit_emit_jump(compiler, SLJIT_EQUAL); + JumpFixup jf_target = {jump_to_target, target_ip}; + arrput(fixups, jf_target); + + struct sljit_jump* jump_to_next = sljit_emit_jump(compiler, SLJIT_JUMP); + JumpFixup jf_next = {jump_to_next, ip}; + arrput(fixups, jf_next); break; + } case OP_BREAK: break; default: @@ -259,13 +309,6 @@ void VM_jitFree(CodeEntry* entry) { } // ===[ Helpers ]=== -sljit_sw RValue_toBool_Pop(VMContext* ctx) { - RValue val = stackPop(ctx); - bool b = RValue_toBool(val); - RValue_free(&val); - return b ? 1 : 0; -} - void VM_popAndStoreResult(VMContext* ctx, RValue* dest) { *dest = stackPop(ctx); }