/* 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 #include #include #include static void spl_vm_profile_free(spl_vm_t *vm); #if defined(_WIN32) || defined(_WIN64) #define WIN32_LEAN_AND_MEAN #include #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 #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; } 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_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) { fprintf(stderr, "vm: STORE bad off=%zu sz=%d\n", (size_t)_off, (int)spl_type_size(ins->type)); spl_vm_backtrace(vm, vm->fp); VM_ERROR("STORE: invalid address"); } memcpy(_addr, &_v, spl_type_size(ins->type)); 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; }