1218 lines
42 KiB
C
1218 lines
42 KiB
C
/* spl_syscall.c - built-in syscall implementations + registration
|
||
*
|
||
* All syscalls validate nargs, cast spl_val_t args to the expected C types,
|
||
* execute, and return the result as spl_val_t.
|
||
*
|
||
* Simple syscalls use the SYSCALL_N macros; complex ones are written
|
||
* manually but follow the same template.
|
||
*/
|
||
|
||
#include "spl_syscall.h"
|
||
#include "include/core_map.h"
|
||
#include "include/core_vec.h"
|
||
#include "spl_mcode.h"
|
||
#include "spl_vm.h"
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <time.h>
|
||
|
||
/* =================================================================
|
||
* Macro templates
|
||
*
|
||
* Each SYSCALL_N macro:
|
||
* 1. Validates nargs (prints error and returns -1 on mismatch)
|
||
* 2. Casts args[n] from spl_val_t to the specified C type via
|
||
* (type)(uintptr_t) - this handles both integer and pointer types
|
||
* 3. Evaluates the expression and returns the result as spl_val_t
|
||
* ================================================================= */
|
||
|
||
#define CHECK_NARGS(fname, expected) \
|
||
do { \
|
||
if (nargs != (expected)) { \
|
||
fprintf(stderr, fname ": expected " #expected " args, got %d\n", nargs); \
|
||
return -1; \
|
||
} \
|
||
} while (0)
|
||
|
||
#define SYSCALL_0(name, ret_expr) \
|
||
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
||
(void)_vm; \
|
||
CHECK_NARGS(#name, 0); \
|
||
(void)args; \
|
||
return (spl_vm_val_t)(uintptr_t)(ret_expr); \
|
||
}
|
||
|
||
#define SYSCALL_1(name, t1, ret_expr) \
|
||
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
||
(void)_vm; \
|
||
CHECK_NARGS(#name, 1); \
|
||
t1 a1 = (t1)(uintptr_t)args[0]; \
|
||
return (spl_vm_val_t)(uintptr_t)(ret_expr); \
|
||
}
|
||
|
||
#define SYSCALL_2(name, t1, t2, ret_expr) \
|
||
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
||
(void)_vm; \
|
||
CHECK_NARGS(#name, 2); \
|
||
t1 a1 = (t1)(uintptr_t)args[0]; \
|
||
t2 a2 = (t2)(uintptr_t)args[1]; \
|
||
return (spl_vm_val_t)(uintptr_t)(ret_expr); \
|
||
}
|
||
|
||
#define SYSCALL_3(name, t1, t2, t3, ret_expr) \
|
||
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
||
(void)_vm; \
|
||
CHECK_NARGS(#name, 3); \
|
||
t1 a1 = (t1)(uintptr_t)args[0]; \
|
||
t2 a2 = (t2)(uintptr_t)args[1]; \
|
||
t3 a3 = (t3)(uintptr_t)args[2]; \
|
||
return (spl_vm_val_t)(uintptr_t)(ret_expr); \
|
||
}
|
||
|
||
#define SYSCALL_4(name, t1, t2, t3, t4, ret_expr) \
|
||
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
||
(void)_vm; \
|
||
CHECK_NARGS(#name, 4); \
|
||
t1 a1 = (t1)(uintptr_t)args[0]; \
|
||
t2 a2 = (t2)(uintptr_t)args[1]; \
|
||
t3 a3 = (t3)(uintptr_t)args[2]; \
|
||
t4 a4 = (t4)(uintptr_t)args[3]; \
|
||
return (spl_vm_val_t)(uintptr_t)(ret_expr); \
|
||
}
|
||
|
||
/* syscall / VM 内部统一地址检查宏。
|
||
* 失败时打印 ctx + 地址 + size + ip,置 native_error,return 0。
|
||
* 要求所在函数有可用的 _vm 变量。 */
|
||
#define ADDRCHECK(vm, ptr, size, ctx) \
|
||
do { \
|
||
uintptr_t _acp = (uintptr_t)(ptr); \
|
||
usize _acs = (usize)(size); \
|
||
if (!spl_vm_addr_valid(_acp, _acs)) { \
|
||
fprintf(stderr, "%s: invalid address %p size=%zu (ip=%zu fp=%zu sp=%zu)\n", (ctx), \
|
||
(void *)_acp, _acs, (vm)->ip, (vm)->fp, (vm)->sp); \
|
||
spl_vm_backtrace((vm), (vm)->fp); \
|
||
(vm)->exit_code = 2; \
|
||
return 0; \
|
||
} \
|
||
} while (0)
|
||
|
||
/* syscall 内部:把当前栈和调用栈打印出来,供出错时定位 */
|
||
#define SYSCALL_DUMP(vm) \
|
||
do { \
|
||
fprintf(stderr, " --- syscall dump (sp=%zu fp=%zu cp=%zu ip=%zu) ---\n", (vm)->sp, \
|
||
(vm)->fp, (vm)->cp, (vm)->ip); \
|
||
spl_vm_backtrace((vm), (vm)->fp); \
|
||
spl_vm_stackdump((vm), (vm)->sp); \
|
||
} while (0)
|
||
|
||
/* syscall 内部:报告致命错误,dump 后让 VM 停在当前 ip */
|
||
#define SYSCALL_FAIL(vm, ...) \
|
||
do { \
|
||
fprintf(stderr, "syscall error: "); \
|
||
fprintf(stderr, __VA_ARGS__); \
|
||
fprintf(stderr, "\n"); \
|
||
SYSCALL_DUMP(vm); \
|
||
(vm)->exit_code = 1; \
|
||
return 0; \
|
||
} while (0)
|
||
|
||
/* =================================================================
|
||
* OS syscalls
|
||
* ================================================================= */
|
||
|
||
/* vm_exit - terminate process with the given exit code */
|
||
SYSCALL_1(vm_exit, int, (exit(a1), (spl_vm_val_t)0))
|
||
|
||
/* vm_putchar - write a single character to stdout */
|
||
SYSCALL_1(vm_putchar, int, putchar(a1))
|
||
|
||
/* vm_getchar - read a single character from stdin */
|
||
SYSCALL_0(vm_getchar, getchar())
|
||
|
||
/* vm_putint - print an integer to stdout */
|
||
SYSCALL_1(vm_putint, int, (fprintf(stdout, "%d", a1), (spl_vm_val_t)0))
|
||
|
||
/* vm_putstr - print a string to stdout (no trailing newline) */
|
||
SYSCALL_1(vm_putstr, const char *, (fputs(a1, stdout), (spl_vm_val_t)0))
|
||
|
||
/* vm_fopen - open a file, returns FILE* as spl_val_t */
|
||
SYSCALL_2(vm_fopen, const char *, const char *, (uintptr_t)fopen(a1, a2))
|
||
|
||
/* vm_fclose - close a file, returns 0 on success */
|
||
SYSCALL_1(vm_fclose, FILE *, fclose(a1))
|
||
|
||
/* vm_fread - read from file, returns number of items read */
|
||
SYSCALL_4(vm_fread, void *, size_t, size_t, FILE *, fread(a1, a2, a3, a4))
|
||
|
||
/* vm_fwrite - write to file, returns number of items written */
|
||
SYSCALL_4(vm_fwrite, const void *, size_t, size_t, FILE *, fwrite(a1, a2, a3, a4))
|
||
|
||
/* vm_fsize - get file size in bytes */
|
||
static spl_vm_val_t vm_fsize(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_fsize", 1);
|
||
FILE *f = (FILE *)(uintptr_t)args[0];
|
||
long cur = ftell(f);
|
||
fseek(f, 0, SEEK_END);
|
||
long sz = ftell(f);
|
||
fseek(f, cur, SEEK_SET);
|
||
return (spl_vm_val_t)(uintptr_t)sz;
|
||
}
|
||
|
||
SYSCALL_0(vm_stdin, stdin)
|
||
SYSCALL_0(vm_stdout, stdout)
|
||
SYSCALL_0(vm_stderr, stderr)
|
||
|
||
/* vm_read_file - read entire file into a malloc'd, null-terminated buffer */
|
||
static spl_vm_val_t vm_read_file(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_read_file", 1);
|
||
const char *path = (const char *)(uintptr_t)args[0];
|
||
if (path == nullptr) {
|
||
fprintf(stderr, "filepath can't be null");
|
||
return 0;
|
||
}
|
||
FILE *f = fopen(path, "rb");
|
||
if (!f) {
|
||
fprintf(stderr, "filepath %s can't be open", path);
|
||
return 1;
|
||
}
|
||
fseek(f, 0, SEEK_END);
|
||
long sz = ftell(f);
|
||
fseek(f, 0, SEEK_SET);
|
||
char *buf = (char *)malloc((size_t)sz + 1);
|
||
if (!buf) {
|
||
fclose(f);
|
||
return 0;
|
||
}
|
||
size_t nread = fread(buf, 1, (size_t)sz, f);
|
||
fclose(f);
|
||
buf[nread] = '\0';
|
||
return (spl_vm_val_t)(uintptr_t)buf;
|
||
}
|
||
|
||
static spl_vm_val_t vm_alloc(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
CHECK_NARGS("vm_alloc", 1);
|
||
size_t sz = (size_t)args[0];
|
||
if (sz == 0)
|
||
SYSCALL_FAIL(_vm, "vm_alloc: zero size");
|
||
if (sz > (size_t)1 << 32)
|
||
SYSCALL_FAIL(_vm, "vm_alloc: size too large (%zu)", sz);
|
||
void *p = malloc(sz);
|
||
if (!p)
|
||
SYSCALL_FAIL(_vm, "vm_alloc: malloc(%zu) failed", sz);
|
||
return (spl_vm_val_t)(uintptr_t)p;
|
||
}
|
||
|
||
static spl_vm_val_t vm_free(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_free", 1);
|
||
void *p = (void *)(uintptr_t)args[0];
|
||
if (p)
|
||
free(p);
|
||
return 0;
|
||
}
|
||
|
||
static spl_vm_val_t vm_realloc(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
CHECK_NARGS("vm_realloc", 2);
|
||
void *p = (void *)(uintptr_t)args[0];
|
||
size_t sz = (size_t)args[1];
|
||
if (!p)
|
||
SYSCALL_FAIL(_vm, "vm_realloc: NULL ptr");
|
||
if (sz == 0)
|
||
SYSCALL_FAIL(_vm, "vm_realloc: zero size");
|
||
ADDRCHECK(_vm, p, 1, "vm_realloc");
|
||
void *np = realloc(p, sz);
|
||
if (!np)
|
||
SYSCALL_FAIL(_vm, "vm_realloc: realloc(%p, %zu) failed", p, sz);
|
||
return (spl_vm_val_t)(uintptr_t)np;
|
||
}
|
||
|
||
static spl_vm_val_t vm_memcpy(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
CHECK_NARGS("vm_memcpy", 3);
|
||
void *dst = (void *)(uintptr_t)args[0];
|
||
const void *src = (const void *)(uintptr_t)args[1];
|
||
size_t size = (size_t)args[2];
|
||
if (size == 0)
|
||
return (spl_vm_val_t)(uintptr_t)dst;
|
||
ADDRCHECK(_vm, dst, size, "vm_memcpy(dst)");
|
||
ADDRCHECK(_vm, src, size, "vm_memcpy(src)");
|
||
memcpy(dst, src, size);
|
||
return (spl_vm_val_t)(uintptr_t)dst;
|
||
}
|
||
|
||
static spl_vm_val_t vm_memcmp(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
CHECK_NARGS("vm_memcmp", 3);
|
||
const void *a = (const void *)(uintptr_t)args[0];
|
||
const void *b = (const void *)(uintptr_t)args[1];
|
||
size_t size = (size_t)args[2];
|
||
if (size == 0)
|
||
return 0;
|
||
ADDRCHECK(_vm, a, size, "vm_memcmp(a)");
|
||
ADDRCHECK(_vm, b, size, "vm_memcmp(b)");
|
||
return (spl_vm_val_t)memcmp(a, b, size);
|
||
}
|
||
|
||
static spl_vm_val_t vm_printf(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
if (nargs <= 0)
|
||
return 0;
|
||
|
||
/* args[0] 是 SPL 的 []u8 format 参数。VM 里 slice 作为参数传递时,
|
||
栈上压的是"指向 slice 结构体的指针",结构为 {ptr:*u8, len:u64} */
|
||
const spl_vm_val_t *fmt_slice = (const spl_vm_val_t *)args[0];
|
||
const char *fmt = (const char *)fmt_slice[0];
|
||
usize fmt_len = (usize)fmt_slice[1];
|
||
|
||
if (nargs <= 1) {
|
||
fwrite(fmt, 1, fmt_len, stdout);
|
||
return (spl_vm_val_t)nargs;
|
||
}
|
||
|
||
typedef VEC(char) string_builder_t;
|
||
string_builder_t buffer;
|
||
vec_init(buffer);
|
||
|
||
int arg_idx = 1; /* 0 是 format */
|
||
char tmp_buf[32];
|
||
|
||
for (usize i = 0; i < fmt_len; ++i) {
|
||
if (fmt[i] != '%') {
|
||
vec_push(buffer, fmt[i]);
|
||
continue;
|
||
}
|
||
if (++i >= fmt_len)
|
||
break;
|
||
if (arg_idx >= nargs)
|
||
break;
|
||
|
||
switch (fmt[i]) {
|
||
case 'd': {
|
||
memset(tmp_buf, 0, sizeof(tmp_buf));
|
||
snprintf(tmp_buf, sizeof(tmp_buf), "%zd", (isize)args[arg_idx]);
|
||
for (usize j = 0; tmp_buf[j] != '\0'; ++j)
|
||
vec_push(buffer, tmp_buf[j]);
|
||
arg_idx++;
|
||
break;
|
||
}
|
||
case 'c':
|
||
vec_push(buffer, (char)args[arg_idx]);
|
||
arg_idx++;
|
||
break;
|
||
case 's': {
|
||
const spl_vm_val_t *s = (const spl_vm_val_t *)args[arg_idx];
|
||
const char *p = (const char *)s[0];
|
||
usize l = (usize)s[1];
|
||
for (usize j = 0; j < l; ++j)
|
||
vec_push(buffer, p[j]);
|
||
arg_idx++;
|
||
break;
|
||
}
|
||
default:
|
||
vec_push(buffer, '%');
|
||
vec_push(buffer, fmt[i]);
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (vec_size(buffer) > 0)
|
||
fwrite(buffer.data, 1, vec_size(buffer), stdout);
|
||
vec_free(buffer);
|
||
return (spl_vm_val_t)nargs;
|
||
}
|
||
|
||
/* 探测接口(syscall 风格,非必选,逐步扩展)。
|
||
* 不假设一定可用:不支持时统一返回 0。
|
||
* case 0 -> 是否存在 time:1 = 存在(则 case 1/2 有效),0 = 不存在
|
||
* case 1 -> 当前秒(time_t)
|
||
* case 2 -> 当前纳秒部分(tv_nsec)
|
||
* case 3 -> VM 已执行指令数(参考量;解码/执行恒定时可换算时间)
|
||
* case 4 -> 完整纳秒时间戳(单次调用,避免 sec/ns 跨秒竞态;
|
||
* 纪元纳秒约 1.8e18 < u64 上限,usize 足够,无需 i128)
|
||
* 新增探测点先加 case,再在 SPL 侧逐步验证。 */
|
||
static spl_vm_val_t vm_timestamp(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
CHECK_NARGS("vm_timestamp", 1);
|
||
spl_vm_val_t arg = args[0];
|
||
struct timespec ts;
|
||
int has_time = (timespec_get(&ts, TIME_UTC) == TIME_UTC);
|
||
switch (arg) {
|
||
case 0:
|
||
return has_time ? 1 : 0;
|
||
case 1:
|
||
return has_time ? (spl_vm_val_t)ts.tv_sec : 0;
|
||
case 2:
|
||
return has_time ? (spl_vm_val_t)ts.tv_nsec : 0;
|
||
case 3:
|
||
if (!_vm)
|
||
return 0;
|
||
return (spl_vm_val_t)_vm->steps;
|
||
case 5:
|
||
if (!_vm)
|
||
return 0;
|
||
return (spl_vm_val_t)_vm->ncalls;
|
||
case 4:
|
||
return has_time ? (spl_vm_val_t)((unsigned long long)ts.tv_sec * 1000000000ull +
|
||
(unsigned long long)ts.tv_nsec)
|
||
: 0;
|
||
default:
|
||
fprintf(stderr, "vm_timestamp: invalid arg %zu\n", (size_t)arg);
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/* =================================================================
|
||
* VM syscalls (for comptime - compile-time code execution)
|
||
*
|
||
* These let SPL code create isolated sub-VMs, load compiled .sir
|
||
* programs into them, push arguments, call functions, and run.
|
||
* ================================================================= */
|
||
|
||
/* vm_new - create a new sub-VM instance, returns spl_vm_t* */
|
||
static spl_vm_val_t vm_new(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_new", 0);
|
||
(void)args;
|
||
spl_vm_t *vm = (spl_vm_t *)malloc(sizeof(spl_vm_t));
|
||
if (!vm)
|
||
return 0;
|
||
spl_vm_init(vm);
|
||
return (spl_vm_val_t)(uintptr_t)vm;
|
||
}
|
||
|
||
/* vm_drop - destroy a sub-VM and its loaded program */
|
||
static spl_vm_val_t vm_drop(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_drop", 1);
|
||
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
||
if (!vm)
|
||
return 0;
|
||
if (vm->prog) {
|
||
spl_prog_drop(vm->prog);
|
||
free(vm->prog);
|
||
vm->prog = NULL;
|
||
}
|
||
spl_vm_drop(vm);
|
||
free(vm);
|
||
return 0;
|
||
}
|
||
|
||
/* vm_load - load a .sir file, register syscalls, return spl_prog_t* */
|
||
static spl_vm_val_t vm_load(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_load", 2);
|
||
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
||
const char *path = (const char *)(uintptr_t)args[1];
|
||
if (!vm || !path)
|
||
return -1;
|
||
|
||
spl_prog_t *prog = (spl_prog_t *)malloc(sizeof(spl_prog_t));
|
||
if (!prog)
|
||
return -1;
|
||
if (spl_prog_load_from_file(path, prog) != 0) {
|
||
free(prog);
|
||
return -1;
|
||
}
|
||
spl_syscall_register(prog);
|
||
if (spl_vm_load_prog(vm, prog) != 0) {
|
||
spl_prog_drop(prog);
|
||
free(prog);
|
||
return -1;
|
||
}
|
||
return (spl_vm_val_t)(uintptr_t)prog;
|
||
}
|
||
|
||
/* vm_load_from_buffer - 从内存加载字节码(comptime 内嵌 VM 用)。
|
||
* 参数 1 是 slice 结构 { ptr, len } 的地址(聚合参数按指针传递)。 */
|
||
static spl_vm_val_t vm_load_from_buffer(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_load_from_buffer", 3);
|
||
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
||
const char *buf = (const char *)(uintptr_t)args[1];
|
||
usize len = (usize)args[2];
|
||
spl_prog_t *prog;
|
||
if (!vm || !buf)
|
||
return -1;
|
||
prog = (spl_prog_t *)malloc(sizeof(spl_prog_t));
|
||
if (!prog)
|
||
return -1;
|
||
if (spl_prog_load_from_buffer(buf, len, prog) != 0) {
|
||
free(prog);
|
||
return -1;
|
||
}
|
||
spl_syscall_register(prog);
|
||
if (spl_vm_load_prog(vm, prog) != 0) {
|
||
spl_prog_drop(prog);
|
||
free(prog);
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* vm_push - push a value onto the sub-VM's stack (for passing arguments) */
|
||
static spl_vm_val_t vm_push(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_push", 2);
|
||
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
||
spl_vm_val_t val = args[1];
|
||
if (!vm)
|
||
return -1;
|
||
if (vm->sp >= vm->config.max_stack_depth) {
|
||
fprintf(stderr, "vm_push: stack overflow\n");
|
||
return -1;
|
||
}
|
||
vm->stacks.data[vm->sp++] = val;
|
||
return 0;
|
||
}
|
||
|
||
/* vm_call - call a function by name with pre-pushed arguments
|
||
*
|
||
* Args (3): vm, function_name, nargs
|
||
* The arguments should already be on the sub-VM's stack via vm_push.
|
||
*
|
||
* Mirrors the CALL instruction's frame setup:
|
||
* - Pushes a sentinel frame (saved_ip=-1) if cp==0 so RET halts properly
|
||
* - Pushes the actual call frame
|
||
* - Sets fp = sp - nargs (so arg0 = data[fp], arg1 = data[fp+1], ...)
|
||
* - Sets ip to the function address
|
||
*/
|
||
static spl_vm_val_t vm_call(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_call", 3);
|
||
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
||
const char *name = (const char *)(uintptr_t)args[1];
|
||
spl_vm_val_t call_nargs = args[2];
|
||
if (!vm || !name)
|
||
return -1;
|
||
|
||
spl_vm_func_t *func = spl_prog_get_func(vm->prog, name);
|
||
if (!func) {
|
||
fprintf(stderr, "vm_call: function '%s' not found\n", name);
|
||
return -1;
|
||
}
|
||
if (vm->cp >= vm->config.max_call_depth) {
|
||
fprintf(stderr, "vm_call: call stack overflow\n");
|
||
return -1;
|
||
}
|
||
|
||
/* 入口调用:ip 置 -1 作为 sentinel,RET 时 halt 并把返回值写入 exit_code。
|
||
* 实参须已由 vm_push 压栈(sp = nargs)。 */
|
||
vm->ip = (uintptr_t)-1;
|
||
vm->cp = 0;
|
||
vm->fp = 0;
|
||
vm->frames.data[0].saved_sp = vm->sp - call_nargs;
|
||
vm->frames.data[0].saved_fp = vm->fp;
|
||
vm->frames.data[0].saved_ip = vm->ip;
|
||
vm->frames.data[0].nargs = call_nargs;
|
||
vm->cp = 1;
|
||
vm->ip = (uintptr_t)func->address;
|
||
vm->fp = vm->sp - call_nargs;
|
||
return 0;
|
||
}
|
||
|
||
/* vm_run - run a sub-VM to completion, returns exit_code(入口 RET 的值) */
|
||
static spl_vm_val_t vm_run(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_run", 1);
|
||
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
||
if (!vm)
|
||
return -1;
|
||
spl_vm_run_until(vm, 0);
|
||
return (spl_vm_val_t)vm->exit_code;
|
||
}
|
||
|
||
/* =================================================================
|
||
* Registration
|
||
*
|
||
* Called after loading a .sir file. Matches native declarations in
|
||
* prog->natives against known syscall names and hooks up impl_fn.
|
||
* ================================================================= */
|
||
|
||
/* =================================================================
|
||
* Native std containers (spike: Vec valid/push_raw/get)
|
||
*
|
||
* 布局镜像 stage2/utils/vec.spl 的 Vec:
|
||
* raw, data, len, cap, elem_size, elem_align (均为机器字)
|
||
* 结构体按值传递/复制由 SPL 负责,native 只按指针操作。
|
||
* ================================================================= */
|
||
|
||
typedef struct {
|
||
uintptr_t raw;
|
||
uintptr_t data;
|
||
uintptr_t len;
|
||
uintptr_t cap;
|
||
uintptr_t elem_size;
|
||
uintptr_t elem_align;
|
||
} spl_vec_t;
|
||
|
||
static int spl_vec_is_valid(const spl_vec_t *v) {
|
||
if (v->elem_size == 0)
|
||
return 0;
|
||
uintptr_t a = v->elem_align;
|
||
if (a == 0 || (a & (a - 1)) != 0)
|
||
return 0;
|
||
if (v->len > v->cap)
|
||
return 0;
|
||
if (v->cap == 0)
|
||
return v->raw == 0 && v->data == 0 && v->len == 0;
|
||
if (v->raw == 0 || v->data == 0)
|
||
return 0;
|
||
return (v->data & (a - 1)) == 0;
|
||
}
|
||
|
||
static spl_vm_val_t vm_vec_valid(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_vec_valid", 1);
|
||
const spl_vec_t *v = (const spl_vec_t *)(uintptr_t)args[0];
|
||
return (spl_vm_val_t)(v ? spl_vec_is_valid(v) : 0);
|
||
}
|
||
|
||
static spl_vm_val_t vm_vec_push_raw(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_vec_push_raw", 2);
|
||
spl_vec_t *v = (spl_vec_t *)(uintptr_t)args[0];
|
||
const void *elem = (const void *)(uintptr_t)args[1];
|
||
if (!v || !elem)
|
||
return 0;
|
||
if (v->len < v->cap) {
|
||
memcpy((void *)(v->data + v->len * v->elem_size), elem, v->elem_size);
|
||
v->len++;
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* out 接收元素指针;返回 0 成功,1 无效参数,2 越界。 */
|
||
static int spl_vec_reserve_raw(spl_vec_t *v, uintptr_t need);
|
||
|
||
static spl_vm_val_t vm_vec_push(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_vec_push", 2);
|
||
spl_vec_t *v = (spl_vec_t *)(uintptr_t)args[0];
|
||
const void *elem = (const void *)(uintptr_t)args[1];
|
||
if (!v || !elem || !spl_vec_is_valid(v))
|
||
return 1;
|
||
if (v->len < v->cap) {
|
||
memcpy((void *)(v->data + v->len * v->elem_size), elem, v->elem_size);
|
||
v->len++;
|
||
return 0;
|
||
}
|
||
int rc = spl_vec_reserve_raw(v, v->len + 1);
|
||
if (rc)
|
||
return rc;
|
||
memmove((void *)(v->data + v->len * v->elem_size), elem, v->elem_size);
|
||
v->len++;
|
||
return 0;
|
||
}
|
||
|
||
static spl_vm_val_t vm_vec_get(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_vec_get", 3);
|
||
const spl_vec_t *v = (const spl_vec_t *)(uintptr_t)args[0];
|
||
uintptr_t index = (uintptr_t)args[1];
|
||
uintptr_t *out = (uintptr_t *)(uintptr_t)args[2];
|
||
if (!v || !out)
|
||
return 1;
|
||
if (!spl_vec_is_valid(v))
|
||
return 1;
|
||
if (index >= v->len)
|
||
return 2;
|
||
*out = v->data + index * v->elem_size;
|
||
return 0;
|
||
}
|
||
|
||
/* 错误码统一:0 ok / 1 InvalidArgument / 2 OutOfRange / 3 Overflow / 4 InvalidAlignment */
|
||
|
||
static int spl_align_ok(uintptr_t a) { return a != 0 && (a & (a - 1)) == 0; }
|
||
|
||
static int spl_grow_capacity(uintptr_t current, uintptr_t min_cap, uintptr_t need,
|
||
uintptr_t *out) {
|
||
uintptr_t cap = current < min_cap ? min_cap : current;
|
||
uintptr_t limit = ~(uintptr_t)0;
|
||
while (cap < need) {
|
||
if (cap > limit / 2) {
|
||
cap = need;
|
||
break;
|
||
}
|
||
cap = cap * 2;
|
||
}
|
||
*out = cap;
|
||
return 0;
|
||
}
|
||
|
||
static int spl_vec_allocate(spl_vec_t *out, uintptr_t capacity, uintptr_t elem_size,
|
||
uintptr_t elem_align) {
|
||
if (elem_size == 0)
|
||
return 1;
|
||
if (!spl_align_ok(elem_align))
|
||
return 4;
|
||
out->raw = 0;
|
||
out->data = 0;
|
||
out->len = 0;
|
||
out->cap = 0;
|
||
out->elem_size = elem_size;
|
||
out->elem_align = elem_align;
|
||
if (capacity == 0)
|
||
return 0;
|
||
if (capacity > ~(uintptr_t)0 / elem_size)
|
||
return 3;
|
||
uintptr_t bytes = capacity * elem_size;
|
||
uintptr_t mask = elem_align - 1;
|
||
if (bytes > ~(uintptr_t)0 - mask)
|
||
return 3;
|
||
uintptr_t total = bytes + mask;
|
||
void *raw = malloc(total);
|
||
if (!raw)
|
||
return 3;
|
||
uintptr_t addr = (uintptr_t)raw;
|
||
if (addr > ~(uintptr_t)0 - mask) {
|
||
free(raw);
|
||
return 3;
|
||
}
|
||
out->raw = addr;
|
||
out->data = (addr + mask) & ~mask;
|
||
out->cap = capacity;
|
||
return 0;
|
||
}
|
||
|
||
static spl_vm_val_t vm_vec_allocate(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_vec_allocate", 4);
|
||
spl_vec_t *out = (spl_vec_t *)(uintptr_t)args[0];
|
||
if (!out)
|
||
return 1;
|
||
return (spl_vm_val_t)spl_vec_allocate(out, (uintptr_t)args[1], (uintptr_t)args[2],
|
||
(uintptr_t)args[3]);
|
||
}
|
||
|
||
static int spl_vec_reserve_raw(spl_vec_t *v, uintptr_t need) {
|
||
if (need <= v->cap)
|
||
return 0;
|
||
uintptr_t new_cap = 0;
|
||
spl_grow_capacity(v->cap, 8, need, &new_cap);
|
||
spl_vec_t fresh;
|
||
int rc = spl_vec_allocate(&fresh, new_cap, v->elem_size, v->elem_align);
|
||
if (rc)
|
||
return rc;
|
||
uintptr_t used = 0;
|
||
if (v->len != 0) {
|
||
if (v->len > ~(uintptr_t)0 / v->elem_size) {
|
||
free((void *)fresh.raw);
|
||
return 3;
|
||
}
|
||
used = v->len * v->elem_size;
|
||
}
|
||
if (used != 0)
|
||
memcpy((void *)fresh.data, (void *)v->data, used);
|
||
fresh.len = v->len;
|
||
uintptr_t old = v->raw;
|
||
*v = fresh;
|
||
if (old)
|
||
free((void *)old);
|
||
return 0;
|
||
}
|
||
|
||
static spl_vm_val_t vm_vec_reserve_raw(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_vec_reserve_raw", 2);
|
||
spl_vec_t *v = (spl_vec_t *)(uintptr_t)args[0];
|
||
if (!v)
|
||
return 1;
|
||
return (spl_vm_val_t)spl_vec_reserve_raw(v, (uintptr_t)args[1]);
|
||
}
|
||
|
||
static spl_vm_val_t vm_vec_append(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_vec_append", 3);
|
||
spl_vec_t *v = (spl_vec_t *)(uintptr_t)args[0];
|
||
const void *elems = (const void *)(uintptr_t)args[1];
|
||
uintptr_t count = (uintptr_t)args[2];
|
||
if (!v || !spl_vec_is_valid(v))
|
||
return 1;
|
||
if (count == 0)
|
||
return 0;
|
||
if (!elems)
|
||
return 1;
|
||
if (count > ~(uintptr_t)0 / v->elem_size)
|
||
return 3;
|
||
uintptr_t copy_bytes = count * v->elem_size;
|
||
if (v->len > ~(uintptr_t)0 - count)
|
||
return 3;
|
||
uintptr_t new_len = v->len + count;
|
||
uintptr_t used = v->len * v->elem_size;
|
||
int source_inside = 0;
|
||
uintptr_t source_offset = 0;
|
||
if (used != 0) {
|
||
uintptr_t base = v->data;
|
||
uintptr_t source = (uintptr_t)elems;
|
||
if (source >= base && source - base < used) {
|
||
source_offset = source - base;
|
||
if (copy_bytes > used - source_offset)
|
||
return 2;
|
||
source_inside = 1;
|
||
}
|
||
}
|
||
int rc = spl_vec_reserve_raw(v, new_len);
|
||
if (rc)
|
||
return rc;
|
||
if (source_inside)
|
||
elems = (const void *)(v->data + source_offset);
|
||
memmove((void *)(v->data + used), elems, copy_bytes);
|
||
v->len = new_len;
|
||
return 0;
|
||
}
|
||
|
||
static spl_vm_val_t vm_vec_set(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_vec_set", 3);
|
||
spl_vec_t *v = (spl_vec_t *)(uintptr_t)args[0];
|
||
uintptr_t index = (uintptr_t)args[1];
|
||
const void *elem = (const void *)(uintptr_t)args[2];
|
||
if (!v || !elem || !spl_vec_is_valid(v))
|
||
return 1;
|
||
if (index >= v->len)
|
||
return 2;
|
||
memmove((void *)(v->data + index * v->elem_size), elem, v->elem_size);
|
||
return 0;
|
||
}
|
||
|
||
static spl_vm_val_t vm_vec_pop(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_vec_pop", 2);
|
||
spl_vec_t *v = (spl_vec_t *)(uintptr_t)args[0];
|
||
void *out = (void *)(uintptr_t)args[1];
|
||
if (!v || !out || !spl_vec_is_valid(v))
|
||
return 1;
|
||
if (v->len == 0)
|
||
return 2;
|
||
memmove(out, (void *)(v->data + (v->len - 1) * v->elem_size), v->elem_size);
|
||
v->len--;
|
||
return 0;
|
||
}
|
||
|
||
static spl_vm_val_t vm_vec_clone(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_vec_clone", 2);
|
||
spl_vec_t *out = (spl_vec_t *)(uintptr_t)args[0];
|
||
const spl_vec_t *src = (const spl_vec_t *)(uintptr_t)args[1];
|
||
if (!out || !src || !spl_vec_is_valid(out) || !spl_vec_is_valid(src))
|
||
return 1;
|
||
if (out->elem_size != src->elem_size || out->elem_align != src->elem_align)
|
||
return 1;
|
||
spl_vec_t fresh;
|
||
fresh.raw = 0;
|
||
fresh.data = 0;
|
||
fresh.len = 0;
|
||
fresh.cap = 0;
|
||
fresh.elem_size = src->elem_size;
|
||
fresh.elem_align = src->elem_align;
|
||
int rc = spl_vec_reserve_raw(&fresh, src->len);
|
||
if (rc)
|
||
return rc;
|
||
uintptr_t bytes = src->len * src->elem_size;
|
||
if (bytes != 0)
|
||
memcpy((void *)fresh.data, (void *)src->data, bytes);
|
||
fresh.len = src->len;
|
||
if (out->raw)
|
||
free((void *)out->raw);
|
||
*out = fresh;
|
||
return 0;
|
||
}
|
||
|
||
static spl_vm_val_t vm_vec_drop(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_vec_drop", 1);
|
||
spl_vec_t *v = (spl_vec_t *)(uintptr_t)args[0];
|
||
if (!v)
|
||
return 0;
|
||
uintptr_t es = v->elem_size;
|
||
uintptr_t ea = v->elem_align;
|
||
if (v->raw)
|
||
free((void *)v->raw);
|
||
v->raw = 0;
|
||
v->data = 0;
|
||
v->len = 0;
|
||
v->cap = 0;
|
||
v->elem_size = es;
|
||
v->elem_align = ea;
|
||
return 0;
|
||
}
|
||
|
||
static spl_vm_val_t vm_memmove(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
CHECK_NARGS("vm_memmove", 3);
|
||
void *dst = (void *)(uintptr_t)args[0];
|
||
const void *src = (const void *)(uintptr_t)args[1];
|
||
size_t size = (size_t)args[2];
|
||
if (size == 0)
|
||
return (spl_vm_val_t)(uintptr_t)dst;
|
||
ADDRCHECK(_vm, dst, size, "vm_memmove(dst)");
|
||
ADDRCHECK(_vm, src, size, "vm_memmove(src)");
|
||
memmove(dst, src, size);
|
||
return (spl_vm_val_t)(uintptr_t)dst;
|
||
}
|
||
|
||
/* =================================================================
|
||
* Native Map(开放寻址 + 墓碑),布局镜像 stage2/utils/map.spl 的 Map。
|
||
* hash_kind: 0 = usize;1 = ByteView(按内容);2 = 常量(仅测试用)。
|
||
* ================================================================= */
|
||
|
||
typedef struct {
|
||
spl_vec_t keys;
|
||
spl_vec_t vals;
|
||
spl_vec_t states;
|
||
uintptr_t size;
|
||
uintptr_t key_size;
|
||
uintptr_t val_size;
|
||
uintptr_t hash_kind;
|
||
} spl_map_t;
|
||
|
||
static void spl_vec_release(spl_vec_t *v) {
|
||
uintptr_t es = v->elem_size;
|
||
uintptr_t ea = v->elem_align;
|
||
if (v->raw)
|
||
free((void *)v->raw);
|
||
v->raw = 0;
|
||
v->data = 0;
|
||
v->len = 0;
|
||
v->cap = 0;
|
||
v->elem_size = es;
|
||
v->elem_align = ea;
|
||
}
|
||
|
||
static uintptr_t spl_hash_usize_value(uintptr_t value) {
|
||
uintptr_t prime = 1099511628211ull;
|
||
uintptr_t h = value;
|
||
h = (h ^ (h >> 30)) * prime;
|
||
h = h ^ (h >> 27);
|
||
h = h * prime;
|
||
h = h ^ (h >> 31);
|
||
if (h == 0)
|
||
return prime;
|
||
return h;
|
||
}
|
||
|
||
static uintptr_t spl_hash_bytes(const void *data, uintptr_t len) {
|
||
const unsigned char *p = (const unsigned char *)data;
|
||
uintptr_t h = 14695981039346656037ull;
|
||
uintptr_t prime = 1099511628211ull;
|
||
for (uintptr_t i = 0; i < len; i++)
|
||
h = (h ^ (uintptr_t)p[i]) * prime;
|
||
return h;
|
||
}
|
||
|
||
static uintptr_t spl_map_hash(const spl_map_t *m, const void *key) {
|
||
if (m->hash_kind == 0)
|
||
return spl_hash_usize_value(*(const uintptr_t *)key);
|
||
if (m->hash_kind == 2)
|
||
return 3;
|
||
const uintptr_t *v = (const uintptr_t *)key; /* ByteView { ptr, len } */
|
||
return spl_hash_bytes((const void *)v[0], v[1]);
|
||
}
|
||
|
||
static int spl_map_eq(const spl_map_t *m, const void *a, const void *b) {
|
||
if (m->hash_kind == 0 || m->hash_kind == 2)
|
||
return *(const uintptr_t *)a == *(const uintptr_t *)b;
|
||
const uintptr_t *x = (const uintptr_t *)a;
|
||
const uintptr_t *y = (const uintptr_t *)b;
|
||
if (x[1] != y[1])
|
||
return 0;
|
||
if (x[1] == 0)
|
||
return 1;
|
||
return memcmp((const void *)x[0], (const void *)y[0], x[1]) == 0;
|
||
}
|
||
|
||
static int spl_map_valid(const spl_map_t *m) {
|
||
if (m->key_size == 0 || m->val_size == 0)
|
||
return 0;
|
||
if (m->keys.len != m->states.len || m->vals.len != m->states.len)
|
||
return 0;
|
||
if (m->size > m->states.len)
|
||
return 0;
|
||
return 1;
|
||
}
|
||
|
||
static uintptr_t spl_map_find_slot(const spl_map_t *m, const void *key) {
|
||
if (m->states.cap == 0)
|
||
return ~(uintptr_t)0;
|
||
uintptr_t mask = m->states.cap - 1;
|
||
uintptr_t i = spl_map_hash(m, key) & mask;
|
||
unsigned char *st = (unsigned char *)m->states.data;
|
||
uintptr_t probes = 0;
|
||
while (probes < m->states.cap && st[i] != 0) {
|
||
if (st[i] == 1 && spl_map_eq(m, (const void *)(m->keys.data + i * m->key_size), key))
|
||
return i;
|
||
i = (i + 1) & mask;
|
||
probes++;
|
||
}
|
||
return ~(uintptr_t)0;
|
||
}
|
||
|
||
static void spl_map_place(spl_map_t *m, const void *key, const void *val) {
|
||
uintptr_t mask = m->states.cap - 1;
|
||
uintptr_t i = spl_map_hash(m, key) & mask;
|
||
unsigned char *st = (unsigned char *)m->states.data;
|
||
uintptr_t first_deleted = ~(uintptr_t)0;
|
||
uintptr_t probes = 0;
|
||
while (probes < m->states.cap && st[i] != 0) {
|
||
if (st[i] == 2 && first_deleted == ~(uintptr_t)0)
|
||
first_deleted = i;
|
||
i = (i + 1) & mask;
|
||
probes++;
|
||
}
|
||
if (first_deleted != ~(uintptr_t)0)
|
||
i = first_deleted;
|
||
memcpy((void *)(m->keys.data + i * m->key_size), key, m->key_size);
|
||
memcpy((void *)(m->vals.data + i * m->val_size), val, m->val_size);
|
||
st[i] = 1;
|
||
m->size++;
|
||
}
|
||
|
||
static int spl_map_reserve(spl_map_t *m, uintptr_t need) {
|
||
if (!spl_map_valid(m))
|
||
return 1;
|
||
if (need <= m->states.cap)
|
||
return 0;
|
||
uintptr_t new_cap = 0;
|
||
spl_grow_capacity(m->states.cap, 8, need, &new_cap);
|
||
|
||
spl_map_t fresh = *m;
|
||
fresh.keys.raw = 0;
|
||
fresh.keys.data = 0;
|
||
fresh.keys.len = 0;
|
||
fresh.keys.cap = 0;
|
||
fresh.vals.raw = 0;
|
||
fresh.vals.data = 0;
|
||
fresh.vals.len = 0;
|
||
fresh.vals.cap = 0;
|
||
fresh.states.raw = 0;
|
||
fresh.states.data = 0;
|
||
fresh.states.len = 0;
|
||
fresh.states.cap = 0;
|
||
fresh.size = 0;
|
||
|
||
int rc = spl_vec_reserve_raw(&fresh.keys, new_cap);
|
||
if (rc)
|
||
goto fail;
|
||
rc = spl_vec_reserve_raw(&fresh.vals, new_cap);
|
||
if (rc)
|
||
goto fail;
|
||
rc = spl_vec_reserve_raw(&fresh.states, new_cap);
|
||
if (rc)
|
||
goto fail;
|
||
fresh.keys.len = new_cap;
|
||
fresh.vals.len = new_cap;
|
||
fresh.states.len = new_cap;
|
||
memset((void *)fresh.states.data, 0, new_cap);
|
||
|
||
for (uintptr_t i = 0; i < m->states.len; i++) {
|
||
if (((unsigned char *)m->states.data)[i] == 1)
|
||
spl_map_place(&fresh, (const void *)(m->keys.data + i * m->key_size),
|
||
(const void *)(m->vals.data + i * m->val_size));
|
||
}
|
||
|
||
spl_vec_release(&m->keys);
|
||
spl_vec_release(&m->vals);
|
||
spl_vec_release(&m->states);
|
||
*m = fresh;
|
||
return 0;
|
||
fail:
|
||
spl_vec_release(&fresh.keys);
|
||
spl_vec_release(&fresh.vals);
|
||
spl_vec_release(&fresh.states);
|
||
return rc;
|
||
}
|
||
|
||
static spl_vm_val_t vm_bytes_eq(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_bytes_eq", 4);
|
||
const void *a = (const void *)(uintptr_t)args[0];
|
||
uintptr_t alen = (uintptr_t)args[1];
|
||
const void *b = (const void *)(uintptr_t)args[2];
|
||
uintptr_t blen = (uintptr_t)args[3];
|
||
if (alen != blen)
|
||
return 0;
|
||
if (alen == 0)
|
||
return 1;
|
||
if (!a || !b)
|
||
return 0;
|
||
return memcmp(a, b, alen) == 0;
|
||
}
|
||
|
||
static spl_vm_val_t vm_map_valid(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_map_valid", 1);
|
||
const spl_map_t *m = (const spl_map_t *)(uintptr_t)args[0];
|
||
return (spl_vm_val_t)(m ? spl_map_valid(m) : 0);
|
||
}
|
||
|
||
static spl_vm_val_t vm_map_reserve(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_map_reserve", 2);
|
||
spl_map_t *m = (spl_map_t *)(uintptr_t)args[0];
|
||
if (!m)
|
||
return 1;
|
||
return (spl_vm_val_t)spl_map_reserve(m, (uintptr_t)args[1]);
|
||
}
|
||
|
||
static spl_vm_val_t vm_map_put(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_map_put", 3);
|
||
spl_map_t *m = (spl_map_t *)(uintptr_t)args[0];
|
||
const void *key = (const void *)(uintptr_t)args[1];
|
||
const void *val = (const void *)(uintptr_t)args[2];
|
||
if (!m || !key || !val || !spl_map_valid(m))
|
||
return 1;
|
||
uintptr_t existing = spl_map_find_slot(m, key);
|
||
if (existing != ~(uintptr_t)0) {
|
||
memcpy((void *)(m->vals.data + existing * m->val_size), val, m->val_size);
|
||
return 0;
|
||
}
|
||
if (m->states.cap == 0) {
|
||
int rc = spl_map_reserve(m, 8);
|
||
if (rc)
|
||
return rc;
|
||
} else {
|
||
if (m->size > ~(uintptr_t)0 - 1)
|
||
return 3;
|
||
uintptr_t next_size = m->size + 1;
|
||
if (next_size > ~(uintptr_t)0 / 128)
|
||
return 3;
|
||
uintptr_t scaled = next_size * 128;
|
||
if (scaled / m->states.cap >= 70) {
|
||
if (m->states.cap > ~(uintptr_t)0 / 2)
|
||
return 3;
|
||
int rc = spl_map_reserve(m, m->states.cap * 2);
|
||
if (rc)
|
||
return rc;
|
||
}
|
||
}
|
||
spl_map_place(m, key, val);
|
||
return 0;
|
||
}
|
||
|
||
static spl_vm_val_t vm_map_get(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_map_get", 3);
|
||
spl_map_t *m = (spl_map_t *)(uintptr_t)args[0];
|
||
const void *key = (const void *)(uintptr_t)args[1];
|
||
void *out = (void *)(uintptr_t)args[2];
|
||
if (!m || !key || !out || !spl_map_valid(m))
|
||
return 1;
|
||
uintptr_t i = spl_map_find_slot(m, key);
|
||
if (i == ~(uintptr_t)0)
|
||
return 1;
|
||
memcpy(out, (void *)(m->vals.data + i * m->val_size), m->val_size);
|
||
return 0;
|
||
}
|
||
|
||
static spl_vm_val_t vm_map_del(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_map_del", 2);
|
||
spl_map_t *m = (spl_map_t *)(uintptr_t)args[0];
|
||
const void *key = (const void *)(uintptr_t)args[1];
|
||
if (!m || !key || !spl_map_valid(m))
|
||
return 1;
|
||
uintptr_t i = spl_map_find_slot(m, key);
|
||
if (i == ~(uintptr_t)0)
|
||
return 1;
|
||
((unsigned char *)m->states.data)[i] = 2;
|
||
m->size--;
|
||
return 0;
|
||
}
|
||
|
||
static spl_vm_val_t vm_map_clear(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_map_clear", 1);
|
||
spl_map_t *m = (spl_map_t *)(uintptr_t)args[0];
|
||
if (!m)
|
||
return 0;
|
||
if (m->states.data && m->states.len)
|
||
memset((void *)m->states.data, 0, m->states.len);
|
||
m->size = 0;
|
||
return 0;
|
||
}
|
||
|
||
static spl_vm_val_t vm_map_drop(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||
(void)_vm;
|
||
CHECK_NARGS("vm_map_drop", 1);
|
||
spl_map_t *m = (spl_map_t *)(uintptr_t)args[0];
|
||
if (!m)
|
||
return 0;
|
||
spl_vec_release(&m->keys);
|
||
spl_vec_release(&m->vals);
|
||
spl_vec_release(&m->states);
|
||
m->size = 0;
|
||
return 0;
|
||
}
|
||
|
||
void spl_syscall_register(spl_prog_t *prog) {
|
||
static spl_vm_native_t table[] = {
|
||
/* OS operations */
|
||
{"vm_exit", 0, vm_exit},
|
||
{"vm_putchar", 0, vm_putchar},
|
||
{"vm_getchar", 0, vm_getchar},
|
||
{"vm_putint", 0, vm_putint},
|
||
{"vm_putstr", 0, vm_putstr},
|
||
{"vm_fopen", 0, vm_fopen},
|
||
{"vm_fclose", 0, vm_fclose},
|
||
{"vm_fread", 0, vm_fread},
|
||
{"vm_fwrite", 0, vm_fwrite},
|
||
{"vm_fsize", 0, vm_fsize},
|
||
{"vm_stdin", 0, vm_stdin},
|
||
{"vm_stdout", 0, vm_stdout},
|
||
{"vm_stderr", 0, vm_stderr},
|
||
{"vm_read_file", 0, vm_read_file},
|
||
{"vm_alloc", 0, vm_alloc},
|
||
{"vm_free", 0, vm_free},
|
||
{"vm_realloc", 0, vm_realloc},
|
||
{"vm_memcpy", 0, vm_memcpy},
|
||
{"vm_memcmp", 0, vm_memcmp},
|
||
{"vm_printf", 0, vm_printf},
|
||
{"vm_vec_valid", 0, vm_vec_valid},
|
||
{"vm_vec_push_raw", 0, vm_vec_push_raw},
|
||
{"vm_vec_push", 0, vm_vec_push},
|
||
{"vm_vec_get", 0, vm_vec_get},
|
||
{"vm_vec_allocate", 0, vm_vec_allocate},
|
||
{"vm_vec_reserve_raw", 0, vm_vec_reserve_raw},
|
||
{"vm_vec_append", 0, vm_vec_append},
|
||
{"vm_vec_set", 0, vm_vec_set},
|
||
{"vm_vec_pop", 0, vm_vec_pop},
|
||
{"vm_vec_clone", 0, vm_vec_clone},
|
||
{"vm_vec_drop", 0, vm_vec_drop},
|
||
{"vm_memmove", 0, vm_memmove},
|
||
{"vm_bytes_eq", 0, vm_bytes_eq},
|
||
{"vm_map_valid", 0, vm_map_valid},
|
||
{"vm_map_reserve", 0, vm_map_reserve},
|
||
{"vm_map_put", 0, vm_map_put},
|
||
{"vm_map_get", 0, vm_map_get},
|
||
{"vm_map_del", 0, vm_map_del},
|
||
{"vm_map_clear", 0, vm_map_clear},
|
||
{"vm_map_drop", 0, vm_map_drop},
|
||
|
||
{"vm_timestamp", 0, vm_timestamp},
|
||
/* VM operations (comptime) */
|
||
{"vm_new", 0, vm_new},
|
||
{"vm_drop", 0, vm_drop},
|
||
{"vm_load", 0, vm_load},
|
||
{"vm_load_from_buffer", 0, vm_load_from_buffer},
|
||
{"vm_push", 0, vm_push},
|
||
{"vm_call", 0, vm_call},
|
||
{"vm_run", 0, vm_run},
|
||
};
|
||
int n = (int)(sizeof(table) / sizeof(table[0]));
|
||
if (!prog)
|
||
return;
|
||
vec_for(prog->natives, i) {
|
||
spl_vm_native_t *nat = &vec_at(prog->natives, i);
|
||
if (!nat->name || nat->impl_fn)
|
||
continue;
|
||
for (int j = 0; j < n; j++) {
|
||
if (strcmp(nat->name, table[j].name) == 0) {
|
||
nat->impl_fn = table[j].impl_fn;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|