stage2 大量优化代码使其自举编译时间大幅缩短
This commit is contained in:
1 parent
e02b0c633a
commit
846f441e3a
38 files changed
+2418
-835
No files matched your search
@@ -33,6 +33,7 @@ typedef struct {
|
||||
int profile;
|
||||
int profile_interval;
|
||||
const char *profile_out;
|
||||
int stats;
|
||||
const char *entry;
|
||||
const char *path; /* file.sir */
|
||||
const char **args; /* spl 程序参数(file.sir 之后) */
|
||||
@@ -48,6 +49,7 @@ static void usage(void) {
|
||||
" --trace print every instruction\n"
|
||||
" --profile[=N] native sampling profiler (every N insns, default 10000)\n"
|
||||
" --profile-out <p> profile output prefix (default build/profile)\n"
|
||||
" --stats print executed instructions / call count at exit\n"
|
||||
" -h, --help show help\n");
|
||||
}
|
||||
|
||||
@@ -72,6 +74,8 @@ static int parse_args(int argc, const char **argv, cli_opts_t *o) {
|
||||
o->trace = 1;
|
||||
} else if (strcmp(a, "--disasm") == 0) {
|
||||
o->disasm = 1;
|
||||
} else if (strcmp(a, "--stats") == 0) {
|
||||
o->stats = 1;
|
||||
} else if (strcmp(a, "--profile") == 0) {
|
||||
o->profile = 1;
|
||||
} else if (strncmp(a, "--profile=", 10) == 0) {
|
||||
@@ -656,6 +660,8 @@ int main(int argc, const char **argv) {
|
||||
if (o.debug) {
|
||||
fprintf(stderr, "spl_cli: debug mode on\n");
|
||||
spl_vm_set_debug(&vm, 1);
|
||||
} else {
|
||||
spl_vm_set_debug(&vm, 0);
|
||||
}
|
||||
if (o.trace) {
|
||||
fprintf(stderr, "spl_cli: trace mode on\n");
|
||||
@@ -710,6 +716,11 @@ int main(int argc, const char **argv) {
|
||||
fprintf(stderr, "spl_cli: hit breakpoint (run with -g to debug)\n");
|
||||
}
|
||||
|
||||
if (o.stats) {
|
||||
fprintf(stderr, "spl_cli: stats steps=%llu calls=%llu\n", (unsigned long long)vm.steps,
|
||||
(unsigned long long)vm.ncalls);
|
||||
}
|
||||
|
||||
if (o.profile) {
|
||||
const char *prefix = o.profile_out ? o.profile_out : "build/profile";
|
||||
char folded[1024];
|
||||
|
||||
@@ -103,6 +103,7 @@ typedef enum {
|
||||
#define X(opcode, name, argc, pop, push, desc) opcode,
|
||||
SPL_OPCODES(X)
|
||||
#undef X
|
||||
SPL_OPCODE_COUNT,
|
||||
} spl_vm_opcode_t;
|
||||
|
||||
/*
|
||||
@@ -191,6 +192,8 @@ void spl_prog_init(spl_prog_t *prog);
|
||||
void spl_prog_drop(spl_prog_t *prog);
|
||||
void spl_prog_dump(spl_prog_t *prog);
|
||||
|
||||
|
||||
|
||||
int spl_prog_load_from_file(const char *fname, spl_prog_t *prog);
|
||||
int spl_prog_store_to_file(const char *fname, spl_prog_t *prog);
|
||||
|
||||
|
||||
@@ -348,6 +348,10 @@ static spl_vm_val_t vm_timestamp(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
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)
|
||||
@@ -493,6 +497,623 @@ SYSCALL_1(vm_run, spl_vm_t *, spl_vm_run_until(a1, 0))
|
||||
* 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 */
|
||||
@@ -516,6 +1137,26 @@ void spl_syscall_register(spl_prog_t *prog) {
|
||||
{"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) */
|
||||
|
||||
+292
-202
@@ -11,6 +11,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
static void spl_vm_profile_free(spl_vm_t *vm);
|
||||
|
||||
@@ -67,7 +68,7 @@ static int spl_type_size(spl_vm_kind_t t) {
|
||||
case SPL_USIZE:
|
||||
case SPL_PTR:
|
||||
return sizeof(void *);
|
||||
case SPL_TYPE_COUNT:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
@@ -570,6 +571,7 @@ void spl_vm_init_ex(spl_vm_t *vm, int stack_size, int call_depth) {
|
||||
|
||||
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;
|
||||
@@ -708,22 +710,29 @@ void spl_vm_set_debug(spl_vm_t *vm, int enabled) {
|
||||
static inline int spl_vm_call(spl_vm_t *vm, spl_vm_val_t addr, spl_vm_val_t nargs) {
|
||||
if (vm->cp >= vm->config.max_call_depth)
|
||||
VM_ERROR("call depth exceeded");
|
||||
// spl_vm_stackdump(vm, vm->sp);
|
||||
PUSH(SPL_STACK_CANARY);
|
||||
/* Shift args right by 1 to create gap for canary at fp-1 */
|
||||
for (usize i = vm->sp; i >= vm->sp - nargs; --i) {
|
||||
vm->stacks.data[i] = vm->stacks.data[i - 1];
|
||||
|
||||
uintptr_t saved_sp;
|
||||
if (vm->debug) {
|
||||
/* debug:在 fp-1 留金丝雀位,需要把实参右移一格。 */
|
||||
PUSH(SPL_STACK_CANARY);
|
||||
for (usize i = vm->sp; i >= vm->sp - nargs; --i)
|
||||
vm->stacks.data[i] = vm->stacks.data[i - 1];
|
||||
saved_sp = vm->sp - nargs - 1;
|
||||
} else {
|
||||
/* 非 debug:不做金丝雀搬移;实参在 sp-nargs..sp-1,fp 指向 arg0。 */
|
||||
saved_sp = vm->sp - nargs;
|
||||
}
|
||||
vm->frames.data[vm->cp].saved_sp = vm->sp - nargs - 1;
|
||||
vm->frames.data[vm->cp].saved_fp = vm->fp;
|
||||
vm->frames.data[vm->cp].saved_ip = vm->ip;
|
||||
vm->frames.data[vm->cp].nargs = nargs;
|
||||
|
||||
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->fp > 0)
|
||||
if (vm->debug && vm->fp > 0)
|
||||
STACK_CANARY(vm) = SPL_STACK_CANARY;
|
||||
// spl_vm_stackdump(vm, vm->sp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -800,6 +809,13 @@ static int func_index_by_ip(spl_prog_t *prog, spl_vm_val_t ip) {
|
||||
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 = (spl_prof_t *)vm->profile;
|
||||
spl_prog_t *prog = vm->prog;
|
||||
@@ -823,6 +839,17 @@ static void spl_vm_sample(spl_vm_t *vm) {
|
||||
}
|
||||
}
|
||||
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)) {
|
||||
@@ -853,10 +880,13 @@ void spl_vm_profile_enable(spl_vm_t *vm, usize interval) {
|
||||
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++)
|
||||
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;
|
||||
@@ -869,6 +899,7 @@ static void spl_vm_profile_free(spl_vm_t *vm) {
|
||||
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;
|
||||
}
|
||||
@@ -891,20 +922,67 @@ void spl_vm_profile_dump_table(spl_vm_t *vm, const char *path) {
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f)
|
||||
return;
|
||||
fprintf(f, "samples=%zu interval=%zu\n", p->samples, vm->profile_interval);
|
||||
fprintf(f, "func\tself_samples\tpercent\n");
|
||||
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);
|
||||
fprintf(f, "%s\t%zu\t%.2f\n", fn->name, c,
|
||||
p->samples ? (100.0 * (double)c / (double)p->samples) : 0.0);
|
||||
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);
|
||||
}
|
||||
|
||||
int spl_vm_run_once(spl_vm_t *vm) {
|
||||
/* 单步执行核心(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;
|
||||
|
||||
@@ -912,73 +990,78 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
return -1;
|
||||
prog = vm->prog;
|
||||
|
||||
if (vm->ip >= vec_size(prog->insns)) {
|
||||
fprintf(stderr, "vm: ip=%zd out of bounds\n", vm->ip);
|
||||
vm->exit_code = 1;
|
||||
return -1;
|
||||
}
|
||||
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,
|
||||
};
|
||||
|
||||
/* 断点:即将执行的指令命中 ip 断点 -> 暂停(返回 2)。
|
||||
* skip_bp 置位时执行当前指令(continue 越过当前断点)。 */
|
||||
if (limit == 0)
|
||||
return 0;
|
||||
if (ip_breakpoint_hit(vm)) {
|
||||
if (vm->skip_bp) {
|
||||
if (vm->skip_bp)
|
||||
vm->skip_bp = 0;
|
||||
} else {
|
||||
else
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
FETCH();
|
||||
goto *vm_dispatch[ins->opcode];
|
||||
|
||||
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);
|
||||
/* ========== Stack ========== */
|
||||
L_SPL_PUSH:
|
||||
PUSH(ins->imm);
|
||||
DISPATCH();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
switch (ins->opcode) {
|
||||
|
||||
/* ========== Stack ========== */
|
||||
case SPL_PUSH:
|
||||
PUSH(ins->imm);
|
||||
break;
|
||||
|
||||
case SPL_DUP: {
|
||||
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);
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case SPL_DROP:
|
||||
if (vm->sp < 1)
|
||||
VM_ERROR("DROP: stack underflow");
|
||||
vm->sp--;
|
||||
break;
|
||||
L_SPL_DROP:
|
||||
if (vm->sp < 1)
|
||||
VM_ERROR("DROP: stack underflow");
|
||||
vm->sp--;
|
||||
DISPATCH();
|
||||
|
||||
case SPL_SWAP: {
|
||||
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;
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case SPL_PICK: {
|
||||
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]);
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case SPL_ROT: {
|
||||
L_SPL_ROT:
|
||||
{
|
||||
if (vm->sp < 3)
|
||||
VM_ERROR("ROT: stack underflow");
|
||||
spl_vm_val_t _a = vm->stacks.data[vm->sp - 3];
|
||||
@@ -987,34 +1070,35 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
vm->stacks.data[vm->sp - 3] = _b;
|
||||
vm->stacks.data[vm->sp - 2] = _c;
|
||||
vm->stacks.data[vm->sp - 1] = _a;
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
/* ========== Arithmetic ========== */
|
||||
case SPL_ADD:
|
||||
ARITH_BINOP(+);
|
||||
break;
|
||||
case SPL_SUB:
|
||||
ARITH_BINOP(-);
|
||||
break;
|
||||
case SPL_MUL:
|
||||
ARITH_BINOP(*);
|
||||
break;
|
||||
/* ========== Arithmetic ========== */
|
||||
L_SPL_ADD:
|
||||
ARITH_BINOP(+);
|
||||
DISPATCH();
|
||||
L_SPL_SUB:
|
||||
ARITH_BINOP(-);
|
||||
DISPATCH();
|
||||
L_SPL_MUL:
|
||||
ARITH_BINOP(*);
|
||||
DISPATCH();
|
||||
|
||||
case SPL_DIV_S:
|
||||
DIV_REM_S(/);
|
||||
break;
|
||||
case SPL_DIV_U:
|
||||
DIV_REM_U(/);
|
||||
break;
|
||||
case SPL_REM_S:
|
||||
DIV_REM_S(%);
|
||||
break;
|
||||
case SPL_REM_U:
|
||||
DIV_REM_U(%);
|
||||
break;
|
||||
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();
|
||||
|
||||
case SPL_NEG: {
|
||||
L_SPL_NEG:
|
||||
{
|
||||
spl_vm_val_t _a = POP();
|
||||
if (spl_is_float((spl_vm_kind_t)ins->type)) {
|
||||
double _d;
|
||||
@@ -1032,115 +1116,129 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
} else {
|
||||
PUSH(-(int64_t)_a);
|
||||
}
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
/* ========== Bitwise ========== */
|
||||
case SPL_AND: {
|
||||
/* ========== Bitwise ========== */
|
||||
L_SPL_AND:
|
||||
{
|
||||
spl_vm_val_t _b = POP(), _a = POP();
|
||||
PUSH(_a & _b);
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
case SPL_OR: {
|
||||
L_SPL_OR:
|
||||
{
|
||||
spl_vm_val_t _b = POP(), _a = POP();
|
||||
PUSH(_a | _b);
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
case SPL_XOR: {
|
||||
L_SPL_XOR:
|
||||
{
|
||||
spl_vm_val_t _b = POP(), _a = POP();
|
||||
PUSH(_a ^ _b);
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
case SPL_NOT: {
|
||||
L_SPL_NOT:
|
||||
{
|
||||
PUSH(~POP());
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
case SPL_SHL: {
|
||||
L_SPL_SHL:
|
||||
{
|
||||
int _s = (int)(POP() & 63);
|
||||
PUSH(POP() << _s);
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
case SPL_SHR_U: {
|
||||
L_SPL_SHR_U:
|
||||
{
|
||||
int _s = (int)(POP() & 63);
|
||||
PUSH(POP() >> _s);
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
case SPL_SHR_S: {
|
||||
L_SPL_SHR_S:
|
||||
{
|
||||
int _s = (int)(POP() & 63);
|
||||
PUSH(POP() >> _s);
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
/* ========== Comparison ========== */
|
||||
case SPL_EQ:
|
||||
CMP_ALL(==);
|
||||
break;
|
||||
case SPL_NE:
|
||||
CMP_ALL(!=);
|
||||
break;
|
||||
case SPL_SLT:
|
||||
CMP_S(<);
|
||||
break;
|
||||
case SPL_SLE:
|
||||
CMP_S(<=);
|
||||
break;
|
||||
case SPL_SGT:
|
||||
CMP_S(>);
|
||||
break;
|
||||
case SPL_SGE:
|
||||
CMP_S(>=);
|
||||
break;
|
||||
case SPL_ULT:
|
||||
CMP_U(<);
|
||||
break;
|
||||
case SPL_ULE:
|
||||
CMP_U(<=);
|
||||
break;
|
||||
case SPL_UGT:
|
||||
CMP_U(>);
|
||||
break;
|
||||
case SPL_UGE:
|
||||
CMP_U(>=);
|
||||
break;
|
||||
/* ========== 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) ========== */
|
||||
case SPL_JMP:
|
||||
vm->ip = vm->ip + ins->imm;
|
||||
break;
|
||||
/* ========== Control Flow (relative offset) ========== */
|
||||
L_SPL_JMP:
|
||||
vm->ip = vm->ip + ins->imm;
|
||||
DISPATCH();
|
||||
|
||||
case SPL_BZ: {
|
||||
L_SPL_BZ:
|
||||
{
|
||||
if (POP() == 0)
|
||||
vm->ip = vm->ip + ins->imm;
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case SPL_BNZ: {
|
||||
L_SPL_BNZ:
|
||||
{
|
||||
if (POP() != 0)
|
||||
vm->ip = vm->ip + ins->imm;
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case SPL_CALL: {
|
||||
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; /* 已进入函数,暂停在函数入口 */
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case SPL_CALLI: {
|
||||
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;
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case SPL_RET: {
|
||||
L_SPL_RET:
|
||||
{
|
||||
spl_vm_val_t _retval = 0;
|
||||
if (ins->type != SPL_VOID)
|
||||
_retval = POP();
|
||||
@@ -1161,16 +1259,17 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
if (ins->type != SPL_VOID)
|
||||
PUSH(_retval);
|
||||
// spl_vm_stackdump(vm, vm->sp);
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case SPL_HALT:
|
||||
if (ins->imm)
|
||||
vm->exit_code = (int)ins->imm;
|
||||
return 1;
|
||||
L_SPL_HALT:
|
||||
if (ins->imm)
|
||||
vm->exit_code = (int)ins->imm;
|
||||
return 1;
|
||||
|
||||
/* ========== Stack / Frame Local Memory ========== */
|
||||
case SPL_ALLOC: {
|
||||
/* ========== 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)
|
||||
@@ -1178,14 +1277,15 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
for (uintptr_t _i = vm->sp; _i < _new_sp; _i++)
|
||||
vm->stacks.data[_i] = 0;
|
||||
vm->sp = _new_sp;
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case SPL_LADDR:
|
||||
PUSH((spl_vm_val_t)((char *)(vm->stacks.data + vm->fp) + ins->imm));
|
||||
break;
|
||||
L_SPL_LADDR:
|
||||
PUSH((spl_vm_val_t)((char *)(vm->stacks.data + vm->fp) + ins->imm));
|
||||
DISPATCH();
|
||||
|
||||
case SPL_GADDR: {
|
||||
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");
|
||||
@@ -1193,11 +1293,12 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
if (!gd->data)
|
||||
VM_ERROR("GADDR: null gdata entry");
|
||||
PUSH((spl_vm_val_t)(uintptr_t)gd->data);
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
/* ========== Indirect Memory (load/store with types) ========== */
|
||||
case SPL_LOAD: {
|
||||
/* ========== Indirect Memory (load/store with types) ========== */
|
||||
L_SPL_LOAD:
|
||||
{
|
||||
void *_addr = (void *)POP();
|
||||
CHECK_ADDR(_addr, "LOAD");
|
||||
spl_vm_val_t _v = 0;
|
||||
@@ -1209,18 +1310,20 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
_v = (spl_vm_val_t)(((isize)(_v << _shift)) >> _shift);
|
||||
}
|
||||
PUSH(_v);
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
case SPL_STORE: {
|
||||
L_SPL_STORE:
|
||||
{
|
||||
spl_vm_val_t _v = POP();
|
||||
void *_addr = (void *)POP();
|
||||
CHECK_ADDR(_addr, "STORE");
|
||||
memcpy(_addr, &_v, spl_type_size(ins->type));
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
/* ========== Type Conversion ========== */
|
||||
case SPL_TRUNC: {
|
||||
/* ========== Type Conversion ========== */
|
||||
L_SPL_TRUNC:
|
||||
{
|
||||
spl_vm_val_t _v = POP();
|
||||
intptr_t _bits = ins->imm;
|
||||
if (_bits < 1 || _bits > 64)
|
||||
@@ -1230,10 +1333,11 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
_v &= _mask;
|
||||
}
|
||||
PUSH(_v);
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case SPL_SEXT: {
|
||||
L_SPL_SEXT:
|
||||
{
|
||||
spl_vm_val_t _v = POP();
|
||||
intptr_t _bits = ins->imm;
|
||||
if (_bits < 1 || _bits > 64)
|
||||
@@ -1246,10 +1350,11 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
_v |= ~_mask;
|
||||
}
|
||||
PUSH(_v);
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case SPL_ZEXT: {
|
||||
L_SPL_ZEXT:
|
||||
{
|
||||
spl_vm_val_t _v = POP();
|
||||
intptr_t _bits = ins->imm;
|
||||
if (_bits < 1 || _bits > 64)
|
||||
@@ -1257,11 +1362,12 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
if (_bits < 64)
|
||||
_v &= ((spl_vm_val_t)1 << _bits) - 1;
|
||||
PUSH(_v);
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
/* ========== Native Interface ========== */
|
||||
case SPL_NCALL: {
|
||||
/* ========== Native Interface ========== */
|
||||
L_SPL_NCALL:
|
||||
{
|
||||
intptr_t _nargs = ins->imm;
|
||||
spl_vm_val_t _nat_idx = POP();
|
||||
spl_vm_native_t *_nat;
|
||||
@@ -1287,10 +1393,11 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
}
|
||||
vm->sp -= _nargs;
|
||||
PUSH(_result);
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case SPL_NLIB: {
|
||||
L_SPL_NLIB:
|
||||
{
|
||||
spl_vm_val_t _si = ins->imm;
|
||||
const char *_lib;
|
||||
if (_si >= vec_size(prog->strtab) || !vec_at(prog->strtab, _si))
|
||||
@@ -1303,11 +1410,12 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
} else {
|
||||
PUSH(_handle);
|
||||
}
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
/* ========== Debug ========== */
|
||||
case SPL_DBG: {
|
||||
/* ========== 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);
|
||||
@@ -1320,44 +1428,26 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
spl_vm_backtrace(vm, vm->fp);
|
||||
spl_vm_stackdump(vm, vm->sp);
|
||||
fprintf(stderr, "---DGB END ---\n");
|
||||
break;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case SPL_BK: {
|
||||
L_SPL_BK:
|
||||
{
|
||||
return 2; /* breakpoint: pause execution */
|
||||
}
|
||||
|
||||
default:
|
||||
fprintf(stderr, "vm: unknown opcode %d at ip=%zd\n", ins->opcode, vm->ip - 1);
|
||||
vm->exit_code = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* canary check in debug mode (canary is at fp-1, invisible to compiled code) */
|
||||
if (vm->debug && vm->fp > 0) {
|
||||
if (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);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
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) { return vm_loop(vm, 1); }
|
||||
|
||||
int spl_vm_run_until(spl_vm_t *vm, size_t step) {
|
||||
size_t _count = 0;
|
||||
int _ret;
|
||||
if (!vm)
|
||||
return -1;
|
||||
while (1) {
|
||||
if (step > 0 && _count >= step)
|
||||
return 0;
|
||||
_ret = spl_vm_run_once(vm);
|
||||
if (_ret != 0)
|
||||
return _ret;
|
||||
_count++;
|
||||
}
|
||||
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) {
|
||||
|
||||
+5
-1
@@ -23,6 +23,9 @@ typedef VEC(spl_callframe_t) spl_frame_vec_t;
|
||||
typedef struct {
|
||||
MAP(const char *, usize) stacks;
|
||||
VEC(usize) self_samples;
|
||||
VEC(uint64_t) self_ns; /* 每函数 time-slice 累加(纳秒) */
|
||||
uint64_t last_ns;
|
||||
uint64_t total_ns;
|
||||
usize samples;
|
||||
} spl_prof_t;
|
||||
|
||||
@@ -34,7 +37,8 @@ typedef struct spl_vm {
|
||||
uintptr_t fp; // frame pointer
|
||||
uintptr_t cp; // call pointer
|
||||
uintptr_t ip; // instr pointer
|
||||
uint64_t steps; // 已执行指令数(供性能分析参考)
|
||||
uint64_t steps; // 已执行指令数(供性能分析参考)
|
||||
uint64_t ncalls; // 已执行 CALL/CALLI 次数(供性能分析参考)
|
||||
int exit_code;
|
||||
int trace; /* non-zero to print each instruction */
|
||||
int debug; /* non-zero to enable canary checks */
|
||||
|
||||
Reference in new issue
Block a user