Files
spl/stage0/spl_syscall.c
T

1218 lines
42 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_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;
}
}
}
}