Files
spl/stage0/spl_vm.c
T

1895 lines
79 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* spl_vm.c - SIR step-by-step interpreter
*
* Implements the spl_vm.h API with macro-based type dispatch to
* eliminate repetitive per-type switch cases.
*/
#include "spl_vm.h"
#include "include/core_map.h"
#include "spl_mcode.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
static void spl_vm_profile_free(spl_vm_t *vm);
#if defined(_WIN32) || defined(_WIN64)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define SPL_DLOPEN(name) ((void *)LoadLibraryA(name))
#define SPL_DLSYM(lib, fn) ((void *)GetProcAddress((HMODULE)lib, fn))
#define SPL_DLCLOSE(lib) FreeLibrary((HMODULE)lib)
#else
#include <dlfcn.h>
#define SPL_DLOPEN(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
#define SPL_DLSYM(lib, fn) dlsym(lib, fn)
#define SPL_DLCLOSE(lib) dlclose(lib)
#endif
/* ================================================================
* Type helpers
* ================================================================ */
static int spl_is_float(spl_vm_kind_t t) { return t == SPL_F32 || t == SPL_F64; }
static int spl_is_signed(spl_vm_kind_t t) {
switch (t) {
case SPL_I8:
case SPL_I16:
case SPL_I32:
case SPL_I64:
case SPL_ISIZE:
return 1;
default:
return 0;
}
}
static int spl_type_size(spl_vm_kind_t t) {
switch (t) {
case SPL_VOID:
return 0;
case SPL_BOOL:
case SPL_I8:
case SPL_U8:
return 1;
case SPL_I16:
case SPL_U16:
return 2;
case SPL_I32:
case SPL_U32:
case SPL_F32:
return 4;
case SPL_I64:
case SPL_U64:
case SPL_F64:
return 8;
case SPL_ISIZE:
case SPL_USIZE:
case SPL_PTR:
return sizeof(void *);
default:
return 0;
}
return 0;
}
/* ================================================================
* Error reporting macro
* ================================================================ */
#define VM_ERROR(msg) \
do { \
fprintf(stderr, "vm: error at ip=%zd: %s\n", vm->ip - 1, msg); \
vm->exit_code = 1; \
return -1; \
} while (0)
#define CHECK_ADDR(addr, label) \
do { \
if (vm->debug_addr && (uintptr_t)(addr) < 0x1000) { \
fprintf(stderr, "vm: %s at ip=%zd: LOW ADDR=%p sp=%zd fp=%zd\n", label, vm->ip - 1, \
(void *)(uintptr_t)(addr), vm->sp, vm->fp); \
spl_vm_stackdump(vm, vm->sp); \
spl_vm_backtrace(vm, vm->fp); \
vm->exit_code = 1; \
return -1; \
} \
} while (0)
/* ================================================================
* Stack push/pop (stacks.data is pre-allocated in init)
* ================================================================ */
#define PUSH(v) \
do { \
spl_vm_val_t _pv = (spl_vm_val_t)(v); \
if (vm->sp >= vm->config.max_stack_depth) \
VM_ERROR("stack overflow"); \
vm->stacks.data[(vm->sp)++] = _pv; \
} while (0)
#define POP() vm->stacks.data[--(vm->sp)]
/* ================================================================
* Type-dispatch macros for arithmetic / comparison
*
* ARITH_BINOP - ADD / SUB / MUL (two's complement: op same for
* signed and unsigned at the same bit-width)
* DIV_REM_S - signed division / remainder
* DIV_REM_U - unsigned division / remainder
* CMP_ALL - EQ / NE (bitwise compare, also handles floats)
* CMP_S - signed ordering (<, <=, >, >=)
* CMP_U - unsigned ordering
* ================================================================ */
#define ARITH_BINOP(OP) \
do { \
spl_vm_val_t _b = POP(), _a = POP(); \
spl_vm_val_t _r = 0; \
if (spl_is_float((spl_vm_kind_t)ins->type)) { \
double _da, _db, _dr; \
if (ins->type == SPL_F32) { \
float _fa, _fb; \
memcpy(&_fa, &_a, 4); \
memcpy(&_fb, &_b, 4); \
_da = _fa; \
_db = _fb; \
} else { \
memcpy(&_da, &_a, 8); \
memcpy(&_db, &_b, 8); \
} \
_dr = _da OP _db; \
if (ins->type == SPL_F32) { \
float _fr = (float)_dr; \
memcpy(&_r, &_fr, 4); \
} else { \
memcpy(&_r, &_dr, 8); \
} \
} else { \
switch (ins->type) { \
case SPL_I8: \
_r = (spl_vm_val_t)((int8_t)_a OP(int8_t) _b); \
break; \
case SPL_U8: \
_r = (spl_vm_val_t)((uint8_t)_a OP(uint8_t) _b); \
break; \
case SPL_I16: \
_r = (spl_vm_val_t)((int16_t)_a OP(int16_t) _b); \
break; \
case SPL_U16: \
_r = (spl_vm_val_t)((uint16_t)_a OP(uint16_t) _b); \
break; \
case SPL_I32: \
_r = (spl_vm_val_t)((int32_t)_a OP(int32_t) _b); \
break; \
case SPL_U32: \
_r = (spl_vm_val_t)((uint32_t)_a OP(uint32_t) _b); \
break; \
case SPL_I64: \
_r = (spl_vm_val_t)((int64_t)_a OP(int64_t) _b); \
break; \
case SPL_U64: \
case SPL_PTR: \
case SPL_USIZE: \
case SPL_ISIZE: \
_r = _a OP _b; \
break; \
default: \
VM_ERROR("bad type for arithmetic"); \
} \
} \
PUSH(_r); \
} while (0)
/* signed division / remainder - all types cast to signed */
#define DIV_REM_S(OP) \
do { \
spl_vm_val_t _b = POP(), _a = POP(); \
if (_b == 0) \
VM_ERROR("division by zero"); \
spl_vm_val_t _r = 0; \
if (spl_is_float((spl_vm_kind_t)ins->type)) { \
double _da, _db, _dr; \
if (ins->type == SPL_F32) { \
float _fa, _fb; \
memcpy(&_fa, &_a, 4); \
memcpy(&_fb, &_b, 4); \
_da = _fa; \
_db = _fb; \
} else { \
memcpy(&_da, &_a, 8); \
memcpy(&_db, &_b, 8); \
} \
_dr = _da / _db; \
if (ins->type == SPL_F32) { \
float _fr = (float)_dr; \
memcpy(&_r, &_fr, 4); \
} else { \
memcpy(&_r, &_dr, 8); \
} \
} else { \
switch (ins->type) { \
case SPL_I8: \
_r = (spl_vm_val_t)((int8_t)_a OP(int8_t) _b); \
break; \
case SPL_U8: \
_r = (spl_vm_val_t)((int8_t)_a OP(int8_t) _b); \
break; \
case SPL_I16: \
_r = (spl_vm_val_t)((int16_t)_a OP(int16_t) _b); \
break; \
case SPL_U16: \
_r = (spl_vm_val_t)((int16_t)_a OP(int16_t) _b); \
break; \
case SPL_I32: \
_r = (spl_vm_val_t)((int32_t)_a OP(int32_t) _b); \
break; \
case SPL_U32: \
_r = (spl_vm_val_t)((int32_t)_a OP(int32_t) _b); \
break; \
case SPL_I64: \
_r = (spl_vm_val_t)((int64_t)_a OP(int64_t) _b); \
break; \
case SPL_U64: \
case SPL_PTR: \
case SPL_USIZE: \
case SPL_ISIZE: \
_r = (spl_vm_val_t)((int64_t)_a OP(int64_t) _b); \
break; \
default: \
VM_ERROR("bad type for signed division"); \
} \
} \
PUSH(_r); \
} while (0)
/* unsigned division / remainder */
#define DIV_REM_U(OP) \
do { \
spl_vm_val_t _b = POP(), _a = POP(); \
if (_b == 0) \
VM_ERROR("division by zero"); \
spl_vm_val_t _r = 0; \
switch (ins->type) { \
case SPL_I8: \
_r = (spl_vm_val_t)((uint8_t)_a OP(uint8_t) _b); \
break; \
case SPL_U8: \
_r = (spl_vm_val_t)((uint8_t)_a OP(uint8_t) _b); \
break; \
case SPL_I16: \
_r = (spl_vm_val_t)((uint16_t)_a OP(uint16_t) _b); \
break; \
case SPL_U16: \
_r = (spl_vm_val_t)((uint16_t)_a OP(uint16_t) _b); \
break; \
case SPL_I32: \
_r = (spl_vm_val_t)((uint32_t)_a OP(uint32_t) _b); \
break; \
case SPL_U32: \
_r = (spl_vm_val_t)((uint32_t)_a OP(uint32_t) _b); \
break; \
case SPL_I64: \
_r = (spl_vm_val_t)((uint64_t)_a OP(uint64_t) _b); \
break; \
case SPL_U64: \
case SPL_PTR: \
case SPL_USIZE: \
case SPL_ISIZE: \
_r = _a OP _b; \
break; \
default: \
VM_ERROR("bad type for unsigned division"); \
} \
PUSH(_r); \
} while (0)
/* CMP_ALL - equality comparisons (all types, floats via memcpy) */
#define CMP_ALL(OP) \
do { \
spl_vm_val_t _b = POP(), _a = POP(); \
intptr_t _r = 0; \
switch (ins->type) { \
case SPL_I8: \
_r = (int8_t)_a OP(int8_t) _b; \
break; \
case SPL_U8: \
_r = (uint8_t)_a OP(uint8_t) _b; \
break; \
case SPL_I16: \
_r = (int16_t)_a OP(int16_t) _b; \
break; \
case SPL_U16: \
_r = (uint16_t)_a OP(uint16_t) _b; \
break; \
case SPL_I32: \
_r = (int32_t)_a OP(int32_t) _b; \
break; \
case SPL_U32: \
_r = (uint32_t)_a OP(uint32_t) _b; \
break; \
case SPL_I64: \
_r = (int64_t)_a OP(int64_t) _b; \
break; \
case SPL_U64: \
case SPL_USIZE: \
case SPL_ISIZE: \
case SPL_PTR: \
_r = _a OP _b; \
break; \
case SPL_F32: { \
float _fa, _fb; \
memcpy(&_fa, &_a, 4); \
memcpy(&_fb, &_b, 4); \
_r = _fa OP _fb; \
break; \
} \
case SPL_F64: { \
double _da, _db; \
memcpy(&_da, &_a, 8); \
memcpy(&_db, &_b, 8); \
_r = _da OP _db; \
break; \
} \
case SPL_BOOL: \
_r = (uint8_t)_a OP(uint8_t) _b; \
break; \
default: \
_r = 0; \
break; \
} \
PUSH(_r); \
} while (0)
/* CMP_S - signed ordering (all ints cast to signed, floats OK) */
#define CMP_S(OP) \
do { \
spl_vm_val_t _b = POP(), _a = POP(); \
intptr_t _r = 0; \
switch (ins->type) { \
case SPL_I8: \
_r = (int8_t)_a OP(int8_t) _b; \
break; \
case SPL_U8: \
_r = (int8_t)_a OP(int8_t) _b; \
break; \
case SPL_I16: \
_r = (int16_t)_a OP(int16_t) _b; \
break; \
case SPL_U16: \
_r = (int16_t)_a OP(int16_t) _b; \
break; \
case SPL_I32: \
_r = (int32_t)_a OP(int32_t) _b; \
break; \
case SPL_U32: \
_r = (int32_t)_a OP(int32_t) _b; \
break; \
case SPL_I64: \
_r = (int64_t)_a OP(int64_t) _b; \
break; \
case SPL_U64: \
case SPL_PTR: \
case SPL_USIZE: \
case SPL_ISIZE: \
_r = _a OP _b; \
break; \
case SPL_F32: { \
float _fa, _fb; \
memcpy(&_fa, &_a, 4); \
memcpy(&_fb, &_b, 4); \
_r = _fa OP _fb; \
break; \
} \
case SPL_F64: { \
double _da, _db; \
memcpy(&_da, &_a, 8); \
memcpy(&_db, &_b, 8); \
_r = _da OP _db; \
break; \
} \
default: \
_r = 0; \
break; \
} \
PUSH(_r); \
} while (0)
/* CMP_U - unsigned ordering (all ints cast to unsigned, no float) */
#define CMP_U(OP) \
do { \
spl_vm_val_t _b = POP(), _a = POP(); \
intptr_t _r = 0; \
switch (ins->type) { \
case SPL_I8: \
_r = (uint8_t)_a OP(uint8_t) _b; \
break; \
case SPL_U8: \
_r = (uint8_t)_a OP(uint8_t) _b; \
break; \
case SPL_I16: \
_r = (uint16_t)_a OP(uint16_t) _b; \
break; \
case SPL_U16: \
_r = (uint16_t)_a OP(uint16_t) _b; \
break; \
case SPL_I32: \
_r = (uint32_t)_a OP(uint32_t) _b; \
break; \
case SPL_U32: \
_r = (uint32_t)_a OP(uint32_t) _b; \
break; \
case SPL_I64: \
_r = (uint64_t)_a OP(uint64_t) _b; \
break; \
case SPL_U64: \
case SPL_PTR: \
case SPL_USIZE: \
case SPL_ISIZE: \
_r = _a OP _b; \
break; \
default: \
_r = 0; \
break; \
} \
PUSH(_r); \
} while (0)
#ifdef _WIN32
// 辅助函数:将异常代码转为可读字符串
const char *ExceptionCodeToString(DWORD code) {
switch (code) {
case EXCEPTION_ACCESS_VIOLATION:
return "ACCESS_VIOLATION";
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
return "ARRAY_BOUNDS_EXCEEDED";
case EXCEPTION_BREAKPOINT:
return "BREAKPOINT";
case EXCEPTION_DATATYPE_MISALIGNMENT:
return "DATATYPE_MISALIGNMENT";
case EXCEPTION_FLT_DENORMAL_OPERAND:
return "FLT_DENORMAL_OPERAND";
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
return "FLT_DIVIDE_BY_ZERO";
case EXCEPTION_FLT_INEXACT_RESULT:
return "FLT_INEXACT_RESULT";
case EXCEPTION_FLT_INVALID_OPERATION:
return "FLT_INVALID_OPERATION";
case EXCEPTION_FLT_OVERFLOW:
return "FLT_OVERFLOW";
case EXCEPTION_FLT_STACK_CHECK:
return "FLT_STACK_CHECK";
case EXCEPTION_FLT_UNDERFLOW:
return "FLT_UNDERFLOW";
case EXCEPTION_ILLEGAL_INSTRUCTION:
return "ILLEGAL_INSTRUCTION";
case EXCEPTION_IN_PAGE_ERROR:
return "IN_PAGE_ERROR";
case EXCEPTION_INT_DIVIDE_BY_ZERO:
return "INT_DIVIDE_BY_ZERO";
case EXCEPTION_INT_OVERFLOW:
return "INT_OVERFLOW";
case EXCEPTION_INVALID_DISPOSITION:
return "INVALID_DISPOSITION";
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
return "NONCONTINUABLE_EXCEPTION";
case EXCEPTION_PRIV_INSTRUCTION:
return "PRIV_INSTRUCTION";
case EXCEPTION_SINGLE_STEP:
return "SINGLE_STEP";
case EXCEPTION_STACK_OVERFLOW:
return "STACK_OVERFLOW";
default:
return "UNKNOWN_EXCEPTION";
}
}
// 全局未处理异常过滤器
LONG WINAPI UnhandledExceptionFilterImpl(EXCEPTION_POINTERS *pExceptionInfo) {
// 获取异常记录和上下文
PEXCEPTION_RECORD record = pExceptionInfo->ExceptionRecord;
PCONTEXT context = pExceptionInfo->ContextRecord;
// 打印基础信息
fprintf(stderr, "========================================\n");
fprintf(stderr, " Unhandled Exception Caught!\n");
fprintf(stderr, " Exception Code: 0x%08lX (%s)\n", record->ExceptionCode,
ExceptionCodeToString(record->ExceptionCode));
fprintf(stderr, " Exception Address: 0x%p\n", record->ExceptionAddress);
fprintf(stderr, " Exception Flags: %ld\n", record->ExceptionFlags);
// 针对访问违例,打印更多细节
if (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
// ExceptionInformation[0]: 0=读, 1=写, 8=执行
// ExceptionInformation[1]: 违例的目标地址
if (record->NumberParameters >= 2) {
const char *operation;
switch (record->ExceptionInformation[0]) {
case 0:
operation = "Read";
break;
case 1:
operation = "Write";
break;
case 8:
operation = "Execute";
break;
default:
operation = "Unknown";
break;
}
fprintf(stderr, " Access Violation: %s at address 0x%p\n", operation,
(void *)(ULONG_PTR)record->ExceptionInformation[1]);
}
}
// 可选:打印发生异常时的部分寄存器(例如 EIP/RIP, EAX/RAX 等)
#ifdef _M_X64
fprintf(stderr, " Registers:\n");
fprintf(stderr, " RIP: 0x%p RSP: 0x%p RAX: 0x%p\n", (void *)context->Rip,
(void *)context->Rsp, (void *)context->Rax);
#else
fprintf(stderr, " Registers:\n");
fprintf(stderr, " EIP: 0x%p ESP: 0x%p EAX: 0x%p\n", (void *)context->Eip,
(void *)context->Esp, (void *)context->Eax);
#endif
fprintf(stderr, "========================================\n");
fflush(stderr);
// 如果附加了调试器,同步输出到调试器窗口
OutputDebugStringA("Unhandled exception occurred, check stderr.\n");
// 返回 EXCEPTION_EXECUTE_HANDLER 会终止进程
// 你可以在这里调用 exit(1) 或直接返回,进程会被终止
return EXCEPTION_EXECUTE_HANDLER;
}
#endif
/* ================================================================
* spl_vm_init / spl_vm_drop
* ================================================================ */
void spl_vm_init_ex(spl_vm_t *vm, int stack_size, int call_depth) {
#ifdef _WIN32
SetErrorMode(SEM_FAILCRITICALERRORS);
SetUnhandledExceptionFilter(UnhandledExceptionFilterImpl);
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
#endif
if (!vm)
return;
vm->config.max_stack_depth = stack_size > 0 ? stack_size : 1024 * 1024 * 64;
vm->config.max_call_depth = call_depth > 0 ? call_depth : 65536;
vec_init(vm->stacks);
vec_realloc(vm->stacks, (usize)vm->config.max_stack_depth);
vec_init(vm->frames);
vec_realloc(vm->frames, (usize)vm->config.max_call_depth);
vec_init(vm->breakpoints);
vec_init(vm->fn_breakpoints);
vec_init(vm->gdata_off);
vec_init(vm->regions);
/* regions[0] 保留;regions[1] = 栈区。 */
vec_push(vm->regions, ((spl_vm_region_t){NULL, 0, 0}));
vec_push(vm->regions, ((spl_vm_region_t){
(unsigned char *)vm->stacks.data,
(usize)vm->config.max_stack_depth * sizeof(spl_vm_val_t), 1}));
vm->stdio_off[0] = vm->stdio_off[1] = vm->stdio_off[2] = 0;
vm->ret_val = 0;
vm->foreign = NULL;
vm->foreign_ctx = NULL;
vm->sp = vm->fp = vm->ip = vm->cp = vm->gp = 0;
vm->steps = 0;
vm->ncalls = 0;
vm->profile = NULL;
vm->profile_interval = 0;
vm->prog = NULL;
vm->trace = 0;
vm->debug = 1;
vm->debug_addr = 0;
vm->skip_bp = 0;
vm->exit_code = 0;
}
void spl_vm_init(spl_vm_t *vm) { spl_vm_init_ex(vm, 0, 0); }
void spl_vm_drop(spl_vm_t *vm) {
if (!vm)
return;
vec_free(vm->stacks);
vec_free(vm->frames);
for (usize i = 0; i < vec_size(vm->fn_breakpoints); i++)
free(vm->fn_breakpoints.data[i]);
vec_free(vm->fn_breakpoints);
vec_free(vm->breakpoints);
vec_free(vm->gdata_off);
for (usize i = 2; i < vec_size(vm->regions); i++) {
spl_vm_region_t *r = &vec_at(vm->regions, i);
if (r->used && r->base)
free(r->base);
}
vec_free(vm->regions);
spl_vm_profile_free(vm);
}
/* ================================================================
* 统一偏移地址空间:offset = (handle<<32) | 区内字节偏移
* ================================================================ */
#define SPL_ASP_HANDLE_OF(off) ((usize)((spl_vm_val_t)(off) >> 32))
#define SPL_ASP_BYTE_OF(off) ((usize)((spl_vm_val_t)(off) & 0xFFFFFFFFull))
static spl_vm_region_t *spl_vm_region_at(spl_vm_t *vm, usize handle) {
if (handle == 0 || handle >= vec_size(vm->regions))
return NULL;
return &vec_at(vm->regions, handle);
}
void *spl_vm_xlate(spl_vm_t *vm, spl_vm_val_t off, usize size) {
usize handle, byte;
spl_vm_region_t *r;
if (!vm || off == SPL_OFF_NULL)
return NULL;
handle = SPL_ASP_HANDLE_OF(off);
byte = SPL_ASP_BYTE_OF(off);
r = spl_vm_region_at(vm, handle);
if (!r || !r->used)
return NULL;
if (byte > r->size || (size > 0 && byte + size > r->size))
return NULL;
return (void *)(r->base + byte);
}
spl_vm_val_t spl_vm_to_off(spl_vm_t *vm, const void *ptr) {
if (!vm || !ptr)
return SPL_OFF_NULL;
{
unsigned char *p = (unsigned char *)ptr;
usize i;
for (i = 1; i < vec_size(vm->regions); i++) {
spl_vm_region_t *r = &vec_at(vm->regions, i);
if (r->used && r->base && p >= r->base && p < r->base + r->size)
return SPL_ASP_OFF(i, (usize)(p - r->base));
}
}
return SPL_OFF_NULL;
}
/* 返回用于存放该偏移的 handle 槽;找不到空闲/空间不足返回 0。 */
static usize spl_vm_region_new(spl_vm_t *vm, void *base, usize size) {
usize i;
for (i = 2; i < vec_size(vm->regions); i++) {
spl_vm_region_t *r = &vec_at(vm->regions, i);
if (!r->used) {
r->base = (unsigned char *)base;
r->size = size;
r->used = 1;
return i;
}
}
if (vec_size(vm->regions) >= (usize)1 << 32)
return 0;
vec_push(vm->regions, ((spl_vm_region_t){(unsigned char *)base, size, 1}));
return vec_size(vm->regions) - 1;
}
/* ---- spl_asp_t 实现 ---- */
static unsigned char *spl_vm_asp_xlate(void *ctx, uintptr_t off, uintptr_t size) {
return (unsigned char *)spl_vm_xlate((spl_vm_t *)ctx, (spl_vm_val_t)off, (usize)size);
}
static uintptr_t spl_vm_asp_alloc(void *ctx, uintptr_t size) {
spl_vm_t *vm = (spl_vm_t *)ctx;
void *p;
usize h;
if (size == 0 || size > 0xFFFFFFFFull)
return 0;
p = malloc((size_t)size);
if (!p)
return 0;
h = spl_vm_region_new(vm, p, (usize)size);
if (h == 0) {
free(p);
return 0;
}
return (uintptr_t)SPL_ASP_OFF(h, 0);
}
static uintptr_t spl_vm_asp_realloc(void *ctx, uintptr_t off, uintptr_t size) {
spl_vm_t *vm = (spl_vm_t *)ctx;
spl_vm_region_t *r;
void *np;
usize h;
if (size == 0 || size > 0xFFFFFFFFull)
return 0;
if (off == 0)
return spl_vm_asp_alloc(ctx, size);
h = SPL_ASP_HANDLE_OF(off);
r = spl_vm_region_at(vm, h);
if (!r || !r->used)
return 0;
np = realloc(r->base, (size_t)size);
if (!np)
return 0;
r->base = (unsigned char *)np;
r->size = (usize)size;
return (uintptr_t)SPL_ASP_OFF(h, SPL_ASP_BYTE_OF(off) & 0);
}
static void spl_vm_asp_free(void *ctx, uintptr_t off) {
spl_vm_t *vm = (spl_vm_t *)ctx;
usize h;
spl_vm_region_t *r;
if (off == 0 || !vm)
return;
h = SPL_ASP_HANDLE_OF(off);
if (h < 2) /* 0/1 不可释放 */
return;
r = spl_vm_region_at(vm, h);
if (!r || !r->used)
return;
free(r->base);
r->base = NULL;
r->size = 0;
r->used = 0;
}
static uintptr_t spl_vm_asp_to_off(void *ctx, const void *host, uintptr_t size) {
(void)size;
return (uintptr_t)spl_vm_to_off((spl_vm_t *)ctx, host);
}
spl_asp_t *spl_vm_asp(spl_vm_t *vm) {
if (!vm)
return NULL;
vm->asp.ctx = vm;
vm->asp.xlate = spl_vm_asp_xlate;
vm->asp.alloc = spl_vm_asp_alloc;
vm->asp.realloc = spl_vm_asp_realloc;
vm->asp.free = spl_vm_asp_free;
vm->asp.to_off = spl_vm_asp_to_off;
return &vm->asp;
}
/* ================================================================
* 动态外部回调 + 栈/结果/内存访问
* ================================================================ */
void spl_vm_set_foreign(spl_vm_t *vm, spl_vm_foreign_fn fn, void *ctx) {
if (!vm)
return;
vm->foreign = fn;
vm->foreign_ctx = ctx;
}
spl_vm_val_t spl_vm_ret_val(spl_vm_t *vm) { return vm ? vm->ret_val : 0; }
usize spl_vm_sp(spl_vm_t *vm) { return vm ? (usize)vm->sp : 0; }
usize spl_vm_fp(spl_vm_t *vm) { return vm ? (usize)vm->fp : 0; }
spl_vm_val_t spl_vm_stack_get(spl_vm_t *vm, usize index) {
if (!vm || index >= (usize)vm->config.max_stack_depth || !vm->stacks.data)
return 0;
return vm->stacks.data[index];
}
int spl_vm_stack_set(spl_vm_t *vm, usize index, spl_vm_val_t val) {
if (!vm || index >= (usize)vm->config.max_stack_depth || !vm->stacks.data)
return -1;
vm->stacks.data[index] = val;
return 0;
}
usize spl_vm_call_arg_base(spl_vm_t *vm, const spl_vm_call_t *call) {
if (!vm || !call || !call->args || !vm->stacks.data)
return (usize)-1;
if (call->args < vm->stacks.data)
return (usize)-1;
return (usize)(call->args - vm->stacks.data);
}
spl_vm_val_t spl_vm_mem_alloc(spl_vm_t *vm, usize size) { return spl_vm_asp_alloc(vm, size); }
int spl_vm_mem_read(spl_vm_t *vm, spl_vm_val_t off, void *dst, usize n) {
void *src = spl_vm_xlate(vm, off, n);
if ((!src || !dst) && n)
return -1;
if (n)
memcpy(dst, src, n);
return 0;
}
int spl_vm_mem_write(spl_vm_t *vm, spl_vm_val_t off, const void *src, usize n) {
void *dst = spl_vm_xlate(vm, off, n);
if ((!dst || !src) && n)
return -1;
if (n)
memcpy(dst, src, n);
return 0;
}
// FIXME
int spl_vm_addr_valid(uintptr_t p, usize size) {
if (p == 0)
return 0;
if (p < 0x1000)
return 0;
uintptr_t max =
(sizeof(void *) == 8) ? (uintptr_t)0x00007FFFFFFFFFFFULL : (uintptr_t)0x7FFFFFFF;
if (p > max)
return 0;
if (size > 0) {
if (p + size < p)
return 0;
if (p + size > max)
return 0;
}
return 1;
}
void spl_vm_add_breakpoint(spl_vm_t *vm, usize ip) {
if (!vm)
return;
for (usize i = 0; i < vec_size(vm->breakpoints); i++)
if (vm->breakpoints.data[i] == ip)
return;
vec_push(vm->breakpoints, ip);
}
void spl_vm_add_breakpoint_fn(spl_vm_t *vm, const char *name) {
if (!vm || !name)
return;
for (usize i = 0; i < vec_size(vm->fn_breakpoints); i++)
if (strcmp(vm->fn_breakpoints.data[i], name) == 0)
return;
char *cpname = malloc(strlen(name) + 1);
Assert(cpname != NULL);
strcpy(cpname, name);
vec_push(vm->fn_breakpoints, cpname);
}
void spl_vm_clear_breakpoints(spl_vm_t *vm) {
if (!vm)
return;
vm->breakpoints.size = 0;
for (usize i = 0; i < vec_size(vm->fn_breakpoints); i++)
free(vm->fn_breakpoints.data[i]);
vm->fn_breakpoints.size = 0;
}
void spl_vm_skip_breakpoint(spl_vm_t *vm) {
if (vm)
vm->skip_bp = 1;
}
/* 前向声明(定义在文件尾部 dump 区域) */
static const char *func_name_by_ip(spl_prog_t *prog, spl_vm_val_t ip);
static void spl_vm_profile_free(spl_vm_t *vm);
/* 函数名断点命中:addr 所在函数名是否在断点表 */
static int fn_breakpoint_hit(spl_vm_t *vm, spl_vm_val_t addr) {
if (!vm || !vm->prog || !vec_size(vm->fn_breakpoints))
return 0;
const char *fn = func_name_by_ip(vm->prog, addr);
if (!fn)
return 0;
for (usize i = 0; i < vec_size(vm->fn_breakpoints); i++)
if (strcmp(vm->fn_breakpoints.data[i], fn) == 0)
return 1;
return 0;
}
/* ip 断点命中:vm->ip(即将执行的指令)在断点表 */
static int ip_breakpoint_hit(spl_vm_t *vm) {
if (!vm || !vec_size(vm->breakpoints))
return 0;
for (usize i = 0; i < vec_size(vm->breakpoints); i++)
if (vm->breakpoints.data[i] == (usize)vm->ip)
return 1;
return 0;
}
/* ================================================================
* spl_vm_load_prog
* ================================================================ */
int spl_vm_load_prog(spl_vm_t *vm, spl_prog_t *prog) {
if (!vm || !prog)
return -1;
vm->prog = prog;
/* 释放上一次加载的堆分配(保留 regions[0]/[1])。 */
for (usize i = 2; i < vec_size(vm->regions); i++) {
spl_vm_region_t *r = &vec_at(vm->regions, i);
if (r->used && r->base)
free(r->base);
r->base = NULL;
r->size = 0;
r->used = 0;
}
/* 把 prog 的常量段搬进本 VM 空间,并记录每个 gdata 的偏移;
* gdata 索引 0 为哨兵,偏移 0(null)。 */
vm->gdata_off.size = 0;
vec_push(vm->gdata_off, (usize)SPL_OFF_NULL);
for (usize i = 1; i < vec_size(prog->gdata); i++) {
spl_vm_gdata_t *gd = &vec_at(prog->gdata, i);
usize sz = gd->size ? gd->size : 1;
spl_vm_val_t off = spl_vm_asp_alloc(vm, sz);
unsigned char *dst;
if (off == SPL_OFF_NULL)
return -1;
dst = (unsigned char *)spl_vm_xlate(vm, off, sz);
if (!dst)
return -1;
if (gd->data && gd->size)
memcpy(dst, gd->data, gd->size);
vec_push(vm->gdata_off, (usize)off);
}
return 0;
}
/* ================================================================
* spl_vm_set_trace
* ================================================================ */
void spl_vm_set_trace(spl_vm_t *vm, int enabled) {
if (!vm)
return;
vm->trace = enabled ? 1 : 0;
}
void spl_vm_set_debug(spl_vm_t *vm, int enabled) {
if (!vm)
return;
vm->debug = enabled ? 1 : 0;
vm->debug_addr = enabled ? 1 : 0;
}
#define STACK_CANARY(vm) (vm)->stacks.data[(vm)->fp - 1]
/* 把 C 字符串复制进 VM 空间,返回其偏移。 */
static spl_vm_val_t spl_vm_marshal_cstr(spl_vm_t *vm, const char *s) {
usize n = s ? strlen(s) + 1 : 1;
spl_vm_val_t off = spl_vm_asp_alloc(vm, n);
unsigned char *d;
if (off == 0)
return 0;
d = (unsigned char *)spl_vm_xlate(vm, off, n);
if (!d)
return 0;
if (s)
memcpy(d, s, n);
else
d[0] = 0;
return off;
}
/* 把 argv/envp 指针数组整体编组进 VM 空间,返回数组偏移。 */
static spl_vm_val_t spl_vm_marshal_argv(spl_vm_t *vm, int argc, const char **argv) {
usize bytes = (usize)(argc + 1) * sizeof(spl_vm_val_t);
spl_vm_val_t arr = spl_vm_asp_alloc(vm, bytes);
spl_vm_val_t *slots;
int i;
if (arr == 0)
return 0;
slots = (spl_vm_val_t *)spl_vm_xlate(vm, arr, bytes);
if (!slots)
return 0;
for (i = 0; i < argc; i++)
slots[i] = spl_vm_marshal_cstr(vm, argv ? argv[i] : NULL);
slots[argc] = 0;
return arr;
}
/* ABI:调用者在实参下方预留 2 个空闲 slot(PAD0/PAD1)。
* fp 指向 arg0;fp-1 为栈金丝雀槽,fp-2 为空闲槽。
* saved_sp = fp-2(调用者压参数前的 sp)。 */
#define SPL_CALL_PADS 2
static inline int spl_vm_call(spl_vm_t *vm, spl_vm_val_t addr, spl_vm_val_t nargs) {
uintptr_t saved_sp;
if (vm->cp >= vm->config.max_call_depth)
VM_ERROR("call depth exceeded");
if (vm->sp < nargs + SPL_CALL_PADS)
VM_ERROR("call: stack underflow (missing pads)");
saved_sp = vm->sp - nargs - SPL_CALL_PADS;
spl_callframe_t *fr = &vm->frames.data[vm->cp];
fr->saved_sp = saved_sp;
fr->saved_fp = vm->fp;
fr->saved_ip = vm->ip;
fr->nargs = nargs;
vm->cp++;
vm->ip = addr;
vm->fp = vm->sp - nargs;
if (vm->debug && vm->fp > 0)
STACK_CANARY(vm) = SPL_STACK_CANARY;
return 0;
}
/* 入口调用:按名字调用顶层函数(同 CALL 的 ABI,实参+2 pads 已由调用方压好)。 */
int spl_vm_call_entry(spl_vm_t *vm, const char *name, int nargs) {
spl_vm_func_t *func;
if (!vm || !vm->prog || !name)
return -1;
func = spl_prog_get_func(vm->prog, name);
if (!func)
return -1;
if (vm->cp >= vm->config.max_call_depth)
return -1;
if (vm->sp < (usize)nargs + SPL_CALL_PADS)
return -1;
vm->ip = (usize)-1;
vm->cp = 0;
vm->fp = 0;
vm->frames.data[0].saved_sp = vm->sp - (usize)nargs - SPL_CALL_PADS;
vm->frames.data[0].saved_fp = vm->fp;
vm->frames.data[0].saved_ip = vm->ip;
vm->frames.data[0].nargs = (spl_vm_val_t)nargs;
vm->cp = 1;
vm->ip = (usize)func->address;
vm->fp = vm->sp - (usize)nargs;
if (vm->debug && vm->fp > 0)
STACK_CANARY(vm) = SPL_STACK_CANARY;
return 0;
}
int spl_vm_prepare(spl_vm_t *vm, const char *entry, int argc, const char **argv,
const char **envp) {
if (!vm || !vm->prog) {
return -1;
}
spl_vm_func_t *fn = NULL;
const char *entry_point = "_start";
fn = spl_prog_get_func(vm->prog, entry_point);
if (fn == NULL) {
entry_point = entry ? entry : "main";
fn = spl_prog_get_func(vm->prog, entry_point);
}
if (!fn) {
fprintf(stderr, "vm: entry point '%s' not found\n", entry_point);
return -1;
}
vm->fp = 0;
vm->sp = 0;
vm->cp = 0;
vm->ip = -1;
vm->exit_code = 0;
vm->ret_val = 0;
/* ABI:入口也在实参下方预留 2 个空闲 slot。 */
PUSH(0);
PUSH(0);
if (fn->nargs >= 1) {
PUSH(argc);
}
if (fn->nargs >= 2) {
PUSH(spl_vm_marshal_argv(vm, argc, argv));
}
if (fn->nargs >= 3) {
int envc = 0;
while (envp && envp[envc])
envc++;
PUSH(spl_vm_marshal_argv(vm, envc, envp));
}
if (fn->nargs >= 4) {
printf("the start symbol can't using more than 3 args");
return -1;
}
/* push sentinel frame so RET knows this is the entry return */
spl_vm_call(vm, fn->address, fn->nargs);
return 0;
}
/* 外部 -> VM 的调用:保存外层状态,按当前深度建帧跑到边界,读结果后恢复。
* 可在 VM 运行中(如外部回调内)安全调用。 */
int spl_vm_invoke(spl_vm_t *vm, spl_vm_call_t *call) {
spl_vm_func_t *fn;
uintptr_t s_ip, s_fp, s_sp, s_cp;
int s_exit, i, r;
spl_vm_val_t s_ret;
if (!vm || !vm->prog || !call || !call->name)
return -1;
fn = spl_prog_get_func(vm->prog, call->name);
if (!fn)
return -1;
s_ip = vm->ip;
s_fp = vm->fp;
s_sp = vm->sp;
s_cp = vm->cp;
s_exit = vm->exit_code;
s_ret = vm->ret_val;
PUSH(0);
PUSH(0);
for (i = 0; i < (int)call->nargs; i++)
PUSH(call->args ? call->args[i] : 0);
/* sentinel:本次调用的 RET 视为边界(与入口调用一致)。 */
vm->ip = (spl_vm_val_t)-1;
if (spl_vm_call(vm, fn->address, (spl_vm_val_t)call->nargs) != 0) {
vm->ip = s_ip;
vm->fp = s_fp;
vm->sp = s_sp;
vm->cp = s_cp;
vm->exit_code = s_exit;
vm->ret_val = s_ret;
return -1;
}
r = spl_vm_run_until(vm, 0);
call->ret = vm->ret_val;
vm->ip = s_ip;
vm->fp = s_fp;
vm->sp = s_sp;
vm->cp = s_cp;
vm->exit_code = s_exit;
vm->ret_val = s_ret;
return r < 0 ? -1 : 0;
}
/* ================================================================
* spl_vm_run_once - execute one instruction
*
* Returns: 0 = still running, 1 = halted, 2 = breakpoint (SPL_DBG), -1 = error
* ================================================================ */
/* ================= native 采样 profiler =================
* 每隔 interval 条指令采样一次当前调用栈(root;...;leaf),累积计数。
* 输出折叠格式(folded)供火焰图工具使用,以及每函数 self 采样表。 */
static int func_index_by_ip(spl_prog_t *prog, spl_vm_val_t ip) {
if (!prog)
return -1;
isize lo = 0, hi = (isize)vec_size(prog->funcs) - 1;
while (lo <= hi) {
isize mid = (lo + hi) / 2;
spl_vm_func_t *f = &vec_at(prog->funcs, (usize)mid);
if (ip < f->address)
hi = mid - 1;
else if (ip >= f->address + f->ninsns)
lo = mid + 1;
else
return (int)mid;
}
for (usize i = 0; i < vec_size(prog->funcs); i++) {
spl_vm_func_t *f = &vec_at(prog->funcs, i);
if (ip >= f->address && ip < f->address + f->ninsns)
return (int)i;
}
return -1;
}
static uint64_t prof_now_ns(void) {
struct timespec ts;
if (timespec_get(&ts, TIME_UTC) != TIME_UTC)
return 0;
return (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec;
}
static void spl_vm_sample(spl_vm_t *vm) {
spl_prof_t *p = vm->profile;
spl_prog_t *prog = vm->prog;
if (!p || !prog)
return;
char buf[8192];
usize len = 0;
for (usize i = 0; i < vm->cp; i++) {
intptr_t s = (intptr_t)vm->frames.data[i].saved_ip;
if (s <= 0)
continue;
int fi = func_index_by_ip(prog, (spl_vm_val_t)(s - 1));
const char *n = fi >= 0 ? vec_at(prog->funcs, (usize)fi).name : "?";
size_t l = strlen(n);
if (len + l + 2 < sizeof(buf)) {
if (len)
buf[len++] = ';';
memcpy(buf + len, n, l);
len += l;
}
}
int leaf = func_index_by_ip(prog, vm->ip > 0 ? vm->ip - 1 : 0);
/* time-slice:把两次采样之间的时间记到当前活跃函数(leaf)。 */
uint64_t now = prof_now_ns();
if (p->last_ns != 0 && now > p->last_ns) {
uint64_t d = now - p->last_ns;
p->total_ns += d;
if (leaf >= 0 && (usize)leaf < vec_size(p->self_ns))
p->self_ns.data[leaf] += d;
}
p->last_ns = now;
const char *ln = leaf >= 0 ? vec_at(prog->funcs, (usize)leaf).name : "?";
size_t ll = strlen(ln);
if (len + ll + 1 < sizeof(buf)) {
if (len)
buf[len++] = ';';
memcpy(buf + len, ln, ll);
len += ll;
}
buf[len] = 0;
usize old = 0;
if (map_get(p->stacks, buf, &old)) {
map_put(p->stacks, strdup(buf), old + 1);
} else {
map_put(p->stacks, strdup(buf), (usize)1);
}
if (leaf >= 0 && (usize)leaf < vec_size(p->self_samples))
p->self_samples.data[leaf]++;
p->samples++;
}
void spl_vm_profile_enable(spl_vm_t *vm, usize interval) {
if (!vm)
return;
spl_prof_t *p = (spl_prof_t *)malloc(sizeof(*p));
if (!p)
return;
memset(p, 0, sizeof(*p));
map_init(p->stacks, MAP_HASH_STR, MAP_CMP_STR);
vec_init(p->self_samples);
vec_init(p->self_ns);
if (vm->prog) {
usize n = vec_size(vm->prog->funcs);
for (usize i = 0; i < n; i++) {
vec_push(p->self_samples, (usize)0);
vec_push(p->self_ns, (uint64_t)0);
}
}
vm->profile = p;
vm->profile_interval = interval ? interval : 100000;
}
static void spl_vm_profile_free(spl_vm_t *vm) {
spl_prof_t *p = (spl_prof_t *)vm->profile;
if (!p)
return;
map_for(p->stacks, i) { free((void *)p->stacks.data[i].key); }
map_free(p->stacks);
vec_free(p->self_samples);
vec_free(p->self_ns);
free(p);
vm->profile = NULL;
}
void spl_vm_profile_dump_folded(spl_vm_t *vm, const char *path) {
spl_prof_t *p = vm ? (spl_prof_t *)vm->profile : NULL;
if (!p || !path)
return;
FILE *f = fopen(path, "wb");
if (!f)
return;
map_for(p->stacks, i) { fprintf(f, "%s %zu\n", p->stacks.data[i].key, p->stacks.data[i].val); }
fclose(f);
}
void spl_vm_profile_dump_table(spl_vm_t *vm, const char *path) {
spl_prof_t *p = vm ? (spl_prof_t *)vm->profile : NULL;
if (!p || !path || !vm->prog)
return;
FILE *f = fopen(path, "wb");
if (!f)
return;
fprintf(f, "samples=%zu interval=%zu total_ns=%llu\n", p->samples, vm->profile_interval,
(unsigned long long)p->total_ns);
fprintf(f, "func\tself_samples\tpct_samples\tself_ns\tpct_ns\tns_per_step\n");
for (usize i = 0; i < vec_size(vm->prog->funcs); i++) {
usize c = i < vec_size(p->self_samples) ? p->self_samples.data[i] : 0;
if (c == 0)
continue;
uint64_t t = i < vec_size(p->self_ns) ? p->self_ns.data[i] : 0;
spl_vm_func_t *fn = &vec_at(vm->prog->funcs, i);
double ps = p->samples ? (100.0 * (double)c / (double)p->samples) : 0.0;
double pt = p->total_ns ? (100.0 * (double)t / (double)p->total_ns) : 0.0;
double nps = c ? ((double)t / (double)c) : 0.0;
fprintf(f, "%s\t%zu\t%.2f\t%llu\t%.2f\t%.1f\n", fn->name, c, ps, (unsigned long long)t, pt,
nps);
}
fclose(f);
}
/* 单步执行核心(static,便于内联进热循环)。 */
/* computed-goto 调度:每个 opcode 直接跳到对应标签;limit=SIZE_MAX 表示不限步。 */
#define DT(op) [op] = &&L_##op
#define FETCH() \
do { \
if (vm->ip >= (spl_vm_val_t)vec_size(prog->insns)) { \
fprintf(stderr, "vm: ip=%zd out of bounds\n", vm->ip); \
vm->exit_code = 1; \
return -1; \
} \
ins = &vec_at(prog->insns, vm->ip); \
vm->ip++; \
vm->steps++; \
if (vm->profile && vm->profile_interval && (vm->steps % vm->profile_interval) == 0) \
spl_vm_sample(vm); \
if (vm->trace) { \
fprintf(stderr, "vm: ip=%zd op=%s type=%s imm=%zu sp=%zd fp=%zd\n", vm->ip - 1, \
spl_vm_opcode_name(ins->opcode), spl_vm_type_kind_name(ins->type), ins->imm, \
vm->sp, vm->fp); \
} \
} while (0)
#define DISPATCH() \
do { \
if (vm->debug && vm->fp > 0 && STACK_CANARY(vm) != SPL_STACK_CANARY) { \
snprintf(vm->error_msg, sizeof(vm->error_msg), \
"STACK CANARY CORRUPTED at ip=%zd, fp=%zd\n", vm->ip - 1, vm->fp); \
VM_ERROR(vm->error_msg); \
} \
if (limit && --limit == 0) \
return 0; \
if (ip_breakpoint_hit(vm)) { \
if (vm->skip_bp) \
vm->skip_bp = 0; \
else \
return 2; \
} \
FETCH(); \
goto *vm_dispatch[ins->opcode]; \
} while (0)
static int vm_loop(spl_vm_t *vm, size_t limit) {
const spl_vm_ins_t *ins;
spl_prog_t *prog;
if (!vm || !vm->prog)
return -1;
prog = vm->prog;
static void *vm_dispatch[SPL_OPCODE_COUNT] = {
DT(SPL_PUSH), DT(SPL_DUP), DT(SPL_DROP),
DT(SPL_SWAP), DT(SPL_PICK), DT(SPL_ROT),
DT(SPL_ADD), DT(SPL_SUB), DT(SPL_MUL),
DT(SPL_DIV_S), DT(SPL_DIV_U), DT(SPL_REM_S),
DT(SPL_REM_U), DT(SPL_NEG), DT(SPL_AND),
DT(SPL_OR), DT(SPL_XOR), DT(SPL_NOT),
DT(SPL_SHL), DT(SPL_SHR_U), DT(SPL_SHR_S),
DT(SPL_EQ), DT(SPL_NE), DT(SPL_SLT),
DT(SPL_SLE), DT(SPL_ULT), DT(SPL_ULE),
DT(SPL_SGT), DT(SPL_SGE), DT(SPL_UGT),
DT(SPL_UGE), DT(SPL_TRUNC), DT(SPL_SEXT),
DT(SPL_ZEXT), DT(SPL_JMP), DT(SPL_BZ),
DT(SPL_BNZ), DT(SPL_HALT), DT(SPL_CALL),
DT(SPL_CALLI), DT(SPL_RET), DT(SPL_ALLOC),
DT(SPL_LADDR), DT(SPL_GADDR), DT(SPL_LOAD),
DT(SPL_STORE), DT(SPL_NCALL), DT(SPL_NLIB),
DT(SPL_MEMCPY),
DT(SPL_BK), DT(SPL_DBG), [SPL_ERROR] = &&L_default,
};
if (limit == 0)
return 0;
if (ip_breakpoint_hit(vm)) {
if (vm->skip_bp)
vm->skip_bp = 0;
else
return 2;
}
FETCH();
goto *vm_dispatch[ins->opcode];
/* ========== Stack ========== */
L_SPL_PUSH:
PUSH(ins->imm);
DISPATCH();
L_SPL_DUP:
{
if (vm->sp < 1)
VM_ERROR("DUP: stack underflow");
spl_vm_val_t _v = vm->stacks.data[vm->sp - 1];
PUSH(_v);
DISPATCH();
}
L_SPL_DROP:
if (vm->sp < 1)
VM_ERROR("DROP: stack underflow");
vm->sp--;
DISPATCH();
L_SPL_SWAP:
{
if (vm->sp < 2)
VM_ERROR("SWAP: stack underflow");
spl_vm_val_t _t = vm->stacks.data[vm->sp - 1];
vm->stacks.data[vm->sp - 1] = vm->stacks.data[vm->sp - 2];
vm->stacks.data[vm->sp - 2] = _t;
DISPATCH();
}
L_SPL_PICK:
{
isize _idx = ins->imm;
if ((usize)_idx >= vm->sp)
VM_ERROR("PICK: index out of range");
PUSH(vm->stacks.data[vm->sp - 1 - _idx]);
DISPATCH();
}
L_SPL_ROT:
{
if (vm->sp < 3)
VM_ERROR("ROT: stack underflow");
spl_vm_val_t _a = vm->stacks.data[vm->sp - 3];
spl_vm_val_t _b = vm->stacks.data[vm->sp - 2];
spl_vm_val_t _c = vm->stacks.data[vm->sp - 1];
vm->stacks.data[vm->sp - 3] = _b;
vm->stacks.data[vm->sp - 2] = _c;
vm->stacks.data[vm->sp - 1] = _a;
DISPATCH();
}
/* ========== Arithmetic ========== */
L_SPL_ADD:
ARITH_BINOP(+);
DISPATCH();
L_SPL_SUB:
ARITH_BINOP(-);
DISPATCH();
L_SPL_MUL:
ARITH_BINOP(*);
DISPATCH();
L_SPL_DIV_S:
DIV_REM_S(/);
DISPATCH();
L_SPL_DIV_U:
DIV_REM_U(/);
DISPATCH();
L_SPL_REM_S:
DIV_REM_S(%);
DISPATCH();
L_SPL_REM_U:
DIV_REM_U(%);
DISPATCH();
L_SPL_NEG:
{
spl_vm_val_t _a = POP();
if (spl_is_float((spl_vm_kind_t)ins->type)) {
double _d;
if (ins->type == SPL_F32) {
float _f;
memcpy(&_f, &_a, 4);
_f = -_f;
memcpy(&_a, &_f, 4);
} else {
memcpy(&_d, &_a, 8);
_d = -_d;
memcpy(&_a, &_d, 8);
}
PUSH(_a);
} else {
PUSH(-(int64_t)_a);
}
DISPATCH();
}
/* ========== Bitwise ========== */
L_SPL_AND:
{
spl_vm_val_t _b = POP(), _a = POP();
PUSH(_a & _b);
DISPATCH();
}
L_SPL_OR:
{
spl_vm_val_t _b = POP(), _a = POP();
PUSH(_a | _b);
DISPATCH();
}
L_SPL_XOR:
{
spl_vm_val_t _b = POP(), _a = POP();
PUSH(_a ^ _b);
DISPATCH();
}
L_SPL_NOT:
{
PUSH(~POP());
DISPATCH();
}
L_SPL_SHL:
{
int _s = (int)(POP() & 63);
PUSH(POP() << _s);
DISPATCH();
}
L_SPL_SHR_U:
{
int _s = (int)(POP() & 63);
PUSH(POP() >> _s);
DISPATCH();
}
L_SPL_SHR_S:
{
int _s = (int)(POP() & 63);
PUSH(POP() >> _s);
DISPATCH();
}
/* ========== Comparison ========== */
L_SPL_EQ:
CMP_ALL(==);
DISPATCH();
L_SPL_NE:
CMP_ALL(!=);
DISPATCH();
L_SPL_SLT:
CMP_S(<);
DISPATCH();
L_SPL_SLE:
CMP_S(<=);
DISPATCH();
L_SPL_SGT:
CMP_S(>);
DISPATCH();
L_SPL_SGE:
CMP_S(>=);
DISPATCH();
L_SPL_ULT:
CMP_U(<);
DISPATCH();
L_SPL_ULE:
CMP_U(<=);
DISPATCH();
L_SPL_UGT:
CMP_U(>);
DISPATCH();
L_SPL_UGE:
CMP_U(>=);
DISPATCH();
/* ========== Control Flow (relative offset) ========== */
L_SPL_JMP:
vm->ip = vm->ip + ins->imm;
DISPATCH();
L_SPL_BZ:
{
if (POP() == 0)
vm->ip = vm->ip + ins->imm;
DISPATCH();
}
L_SPL_BNZ:
{
if (POP() != 0)
vm->ip = vm->ip + ins->imm;
DISPATCH();
}
L_SPL_CALL:
{
spl_vm_val_t _nargs = ins->imm;
spl_vm_val_t _addr = POP();
vm->ncalls++;
int _hit = fn_breakpoint_hit(vm, _addr);
spl_vm_call(vm, _addr, _nargs);
if (_hit)
return 2; /* 已进入函数,暂停在函数入口 */
DISPATCH();
}
L_SPL_CALLI:
{
spl_vm_val_t _addr = POP();
spl_vm_val_t _nargs = POP();
vm->ncalls++;
int _hit = fn_breakpoint_hit(vm, _addr);
spl_vm_call(vm, _addr, _nargs);
if (_hit)
return 2;
DISPATCH();
}
L_SPL_RET:
{
spl_vm_val_t _retval = 0;
if (ins->type != SPL_VOID)
_retval = POP();
if (vm->cp <= 0)
VM_ERROR("RET: call stack underflow");
vm->cp--;
intptr_t _saved_sp = vm->frames.data[vm->cp].saved_sp;
intptr_t _saved_fp = vm->frames.data[vm->cp].saved_fp;
intptr_t _saved_ip = vm->frames.data[vm->cp].saved_ip;
/* entry return -> halt */
if (_saved_ip < 0) {
PUSH(_retval);
vm->ret_val = _retval;
vm->exit_code = (int)_retval;
return 1;
}
vm->sp = _saved_sp;
vm->fp = _saved_fp;
vm->ip = _saved_ip;
if (ins->type != SPL_VOID)
PUSH(_retval);
// spl_vm_stackdump(vm, vm->sp);
DISPATCH();
}
L_SPL_HALT:
vm->ret_val = ins->imm;
if (ins->imm)
vm->exit_code = (int)ins->imm;
return 1;
/* ========== Stack / Frame Local Memory ========== */
L_SPL_ALLOC:
{
spl_vm_val_t _k = ins->imm;
uintptr_t _new_sp = vm->sp + _k;
if (_new_sp > vm->config.max_stack_depth)
VM_ERROR("ALLOC: stack overflow");
for (uintptr_t _i = vm->sp; _i < _new_sp; _i++)
vm->stacks.data[_i] = 0;
vm->sp = _new_sp;
DISPATCH();
}
L_SPL_LADDR:
PUSH(SPL_ASP_OFF(SPL_ASP_HANDLE_STACK,
(spl_vm_val_t)vm->fp * sizeof(spl_vm_val_t) + ins->imm));
DISPATCH();
L_SPL_GADDR:
{
spl_vm_val_t _idx = ins->imm;
if (_idx == 0 || _idx >= vec_size(prog->gdata))
VM_ERROR("GADDR: global data index out of range");
if (_idx >= vec_size(vm->gdata_off))
VM_ERROR("GADDR: global data not mapped");
PUSH((spl_vm_val_t)vec_at(vm->gdata_off, _idx));
DISPATCH();
}
/* ========== Indirect Memory (load/store with types) ========== */
L_SPL_LOAD:
{
spl_vm_val_t _off = POP();
void *_addr;
spl_vm_val_t _v = 0;
usize _sz = spl_type_size(ins->type);
_addr = spl_vm_xlate(vm, _off, _sz);
if (!_addr)
VM_ERROR("LOAD: invalid address");
memcpy(&_v, _addr, _sz);
/* Sign-extend signed integer types smaller than 64 bits */
if (_sz > 0 && _sz < sizeof(spl_vm_val_t) && spl_is_signed(ins->type)) {
usize _shift = (sizeof(spl_vm_val_t) - _sz) * 8;
_v = (spl_vm_val_t)(((isize)(_v << _shift)) >> _shift);
}
PUSH(_v);
DISPATCH();
}
L_SPL_STORE:
{
spl_vm_val_t _v = POP();
spl_vm_val_t _off = POP();
void *_addr = spl_vm_xlate(vm, _off, spl_type_size(ins->type));
if (!_addr)
VM_ERROR("STORE: invalid address");
memcpy(_addr, &_v, spl_type_size(ins->type));
DISPATCH();
}
L_SPL_MEMCPY:
{
spl_vm_val_t _n = POP();
spl_vm_val_t _src = POP();
spl_vm_val_t _dst = POP();
if (_n) {
void *_d = spl_vm_xlate(vm, _dst, (usize)_n);
void *_s = spl_vm_xlate(vm, _src, (usize)_n);
if (!_d || !_s)
VM_ERROR("MEMCPY: invalid address");
memmove(_d, _s, (usize)_n);
}
DISPATCH();
}
/* ========== Type Conversion ========== */
L_SPL_TRUNC:
{
spl_vm_val_t _v = POP();
intptr_t _bits = ins->imm;
if (_bits < 1 || _bits > 64)
VM_ERROR("TRUNC: bad bit-width");
if (_bits < 64) {
spl_vm_val_t _mask = ((spl_vm_val_t)1 << _bits) - 1;
_v &= _mask;
}
PUSH(_v);
DISPATCH();
}
L_SPL_SEXT:
{
spl_vm_val_t _v = POP();
intptr_t _bits = ins->imm;
if (_bits < 1 || _bits > 64)
VM_ERROR("SEXT: bad bit-width");
if (_bits < 64) {
spl_vm_val_t _sign = (spl_vm_val_t)1 << (_bits - 1);
spl_vm_val_t _mask = ((spl_vm_val_t)1 << _bits) - 1;
_v &= _mask;
if (_v & _sign)
_v |= ~_mask;
}
PUSH(_v);
DISPATCH();
}
L_SPL_ZEXT:
{
spl_vm_val_t _v = POP();
intptr_t _bits = ins->imm;
if (_bits < 1 || _bits > 64)
VM_ERROR("ZEXT: bad bit-width");
if (_bits < 64)
_v &= ((spl_vm_val_t)1 << _bits) - 1;
PUSH(_v);
DISPATCH();
}
/* ========== Native Interface ========== */
L_SPL_NCALL:
{
intptr_t _nargs = ins->imm;
spl_vm_val_t _nat_idx = POP();
spl_vm_native_t *_nat;
if (_nat_idx >= vec_size(prog->natives))
VM_ERROR("NCALL: native index out of range");
_nat = &vec_at(prog->natives, _nat_idx);
spl_vm_val_t *_arg_base = vm->stacks.data + vm->sp - _nargs;
spl_vm_val_t _result;
if (_nat->impl_fn) {
_result = _nat->impl_fn(vm, _nargs, _arg_base);
} else if (vm->foreign) {
/* 线 1 动态:交给按名的外部函数处理器。 */
spl_vm_call_t _call;
_call.name = _nat->name ? _nat->name : "";
_call.args = _arg_base;
_call.nargs = (usize)_nargs;
_call.ret = 0;
_result = vm->foreign(vm, &_call, vm->foreign_ctx);
} else {
snprintf(vm->error_msg, sizeof(vm->error_msg),
"NCALL: NULL native function pointer expect %s", _nat->name);
VM_ERROR(vm->error_msg);
}
if (vm->exit_code) {
snprintf(vm->error_msg, sizeof(vm->error_msg), "NCALL %s failed (ip=%zd)",
_nat->name ? _nat->name : "?", vm->ip - 1);
fprintf(stderr, "native call: %s\n", _nat->name ? _nat->name : "?");
fprintf(stderr, " nargs=%zd arg_base=%p sp=%zu\n", _nargs, (void *)_arg_base,
vm->sp);
spl_vm_backtrace(vm, vm->fp);
spl_vm_stackdump(vm, vm->sp);
return -1;
}
vm->sp -= _nargs;
PUSH(_result);
DISPATCH();
}
L_SPL_NLIB:
{
spl_vm_val_t _si = ins->imm;
const char *_lib;
if (_si >= vec_size(prog->strtab) || !vec_at(prog->strtab, _si))
VM_ERROR("NLIB: invalid string index");
_lib = vec_at(prog->strtab, _si);
void *_handle = SPL_DLOPEN(_lib);
if (!_handle) {
fprintf(stderr, "vm: NLIB: dlopen(%s) failed\n", _lib);
PUSH(0);
} else {
PUSH(_handle);
}
DISPATCH();
}
/* ========== Debug ========== */
L_SPL_DBG:
{
fprintf(stderr, "---DGB: current ip %zu---\n", vm->ip);
spl_vm_dump_instr(vm, vm->ip - 6);
spl_vm_dump_instr(vm, vm->ip - 5);
spl_vm_dump_instr(vm, vm->ip - 4);
spl_vm_dump_instr(vm, vm->ip - 3);
spl_vm_dump_instr(vm, vm->ip - 2);
spl_vm_dump_instr(vm, vm->ip - 1);
spl_vm_dump_instr(vm, vm->ip);
spl_vm_dump_instr(vm, vm->ip + 1);
spl_vm_backtrace(vm, vm->fp);
spl_vm_stackdump(vm, vm->sp);
fprintf(stderr, "---DGB END ---\n");
DISPATCH();
}
L_SPL_BK:
{
return 2; /* breakpoint: pause execution */
}
L_default:
fprintf(stderr, "vm: unknown opcode %d at ip=%zd\n", ins->opcode, vm->ip - 1);
vm->exit_code = 1;
return -1;
}
int spl_vm_run_once(spl_vm_t *vm) {
if (!vm)
return -1;
return vm_loop(vm, 1);
}
int spl_vm_run_until(spl_vm_t *vm, size_t step) {
if (!vm)
return -1;
return vm_loop(vm, step ? step : (size_t)-1);
}
static const char *func_name_by_ip(spl_prog_t *prog, spl_vm_val_t ip) {
vec_for(prog->funcs, i) {
spl_vm_func_t *f = &vec_at(prog->funcs, i);
if (ip >= f->address && ip < (f->address + f->ninsns))
return f->name;
}
return "?";
}
void spl_vm_dump_instr(spl_vm_t *vm, spl_vm_val_t ip) {
if (!vm || !vm->prog)
return;
if (ip >= vec_size(vm->prog->insns))
return;
spl_vm_ins_t *ins = &vec_at(vm->prog->insns, ip);
fprintf(stderr, " instr at ip=%zd: op=%s type=%s imm=%zu\n", ip,
spl_vm_opcode_name(ins->opcode), spl_vm_type_kind_name(ins->type), ins->imm);
}
void spl_vm_stackdump(spl_vm_t *vm, spl_vm_val_t sp) {
if (!vm)
return;
fprintf(stderr, "stack dump (sp=%zd, fp=%zd):\n", sp, vm->fp);
spl_vm_val_t start = sp > 32 ? sp - 32 : 0;
for (spl_vm_val_t i = start; i <= sp; i++) {
fprintf(stderr, " [%3zd] = [addr 0x%p] 0x%016zx (%zd)\n", i, &vm->stacks.data[i],
vm->stacks.data[i], vm->stacks.data[i]);
}
}
int spl_vm_backtrace(spl_vm_t *vm, spl_vm_val_t fp) {
if (!vm || !vm->prog)
return -1;
(void)fp;
fprintf(stderr, "backtrace: \n");
for (isize i = vm->cp - 1; i >= 0; i--) {
spl_vm_val_t _saved_ip = vm->frames.data[i].saved_ip;
spl_vm_val_t _saved_fp = vm->frames.data[i].saved_fp;
const char *_fn = func_name_by_ip(vm->prog, _saved_ip - 1);
fprintf(stderr, " [%3zd] %s (fp=%zd, ip=%zd, args=%zd)\n", i, _fn, _saved_fp, _saved_ip,
vm->frames.data[i].nargs);
}
const char *_cur = func_name_by_ip(vm->prog, vm->ip);
fprintf(stderr, " => %s (fp=%zd, ip=%zd, sp=%zd)\n", _cur, vm->fp, vm->ip, vm->sp);
return 0;
}