Compare commits
2
Commits
fae017b52d
...
0b8f649818
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b8f649818 | ||
|
|
c0a86ea552 |
No files matched your search
@@ -202,7 +202,7 @@ const char *build_target(const char *name) {
|
||||
return target_exe;
|
||||
|
||||
nob_cc(&cmd);
|
||||
nob_cmd_append(&cmd, "-I./stage2/sys", "-O2", "-D_CRT_SECURE_NO_WARNINGS");
|
||||
nob_cmd_append(&cmd, "-I./stage0", "-O2", "-D_CRT_SECURE_NO_WARNINGS");
|
||||
nob_cc_inputs(&cmd, target_c);
|
||||
nob_cc_output(&cmd, target_exe);
|
||||
if (!nob_cmd_run(&cmd)) {
|
||||
|
||||
+82
-12
@@ -3,6 +3,13 @@
|
||||
|
||||
/* clang-format off */
|
||||
#define VM_SRCS "stage0/spl_mcode.c", "stage0/spl_syscall.c", "stage0/spl_vm.c",
|
||||
|
||||
static const char *stage0_headers[] = {
|
||||
"stage0/spl_asp.h", "stage0/spl_mcode.h", "stage0/spl_syscall.h",
|
||||
"stage0/spl_vm.h", "stage0/spl_rt_core.h", "stage0/spl_rt.h",
|
||||
"stage0/spl_vm_embed.h",
|
||||
};
|
||||
|
||||
static const char *spl_cli_srcs[] = {"stage0/spl_cli.c", VM_SRCS};
|
||||
static const char *vm_test_srcs[] = {"stage0/test_spl_vm.c", VM_SRCS};
|
||||
static const char *splc0_srcs[] = {
|
||||
@@ -30,8 +37,8 @@ static const char *splc0_srcs[] = {
|
||||
};
|
||||
|
||||
static const char *splc1_crt_deps[] ={
|
||||
"stage2/sys/spl_rt.h",
|
||||
"stage2/sys/spl_vm_embed.h",
|
||||
"stage0/spl_rt.h",
|
||||
"stage0/spl_vm_embed.h",
|
||||
// TODO
|
||||
};
|
||||
|
||||
@@ -88,10 +95,73 @@ static const char* splc1_deps[] = {
|
||||
};
|
||||
|
||||
static const char* splc2_deps[] = {
|
||||
|
||||
/* stage3 */
|
||||
"stage3/builtin.spl",
|
||||
"stage3/hir.spl",
|
||||
"stage3/hir2ir.spl",
|
||||
"stage3/lang_type.spl",
|
||||
"stage3/lower.spl",
|
||||
"stage3/scope.spl",
|
||||
"stage3/sema.spl",
|
||||
"stage3/splc2.spl",
|
||||
"stage3/tag.spl",
|
||||
/* stage2 */
|
||||
"stage2/sys/exit.spl",
|
||||
"stage2/sys/file.spl",
|
||||
"stage2/sys/mem.spl",
|
||||
"stage2/sys/time.spl",
|
||||
"stage2/sys/vm.spl",
|
||||
"stage2/utils/algorithm.spl",
|
||||
"stage2/utils/arena.spl",
|
||||
"stage2/utils/ascii.spl",
|
||||
"stage2/utils/byte_buffer.spl",
|
||||
"stage2/utils/byte_view.spl",
|
||||
"stage2/utils/error.spl",
|
||||
"stage2/utils/fmt.spl",
|
||||
"stage2/utils/hash.spl",
|
||||
"stage2/utils/io.spl",
|
||||
"stage2/utils/json.spl",
|
||||
"stage2/utils/log.spl",
|
||||
"stage2/utils/lsp_frame.spl",
|
||||
"stage2/utils/map.spl",
|
||||
"stage2/utils/memory.spl",
|
||||
"stage2/utils/mp.spl",
|
||||
"stage2/utils/path.spl",
|
||||
"stage2/utils/profile.spl",
|
||||
"stage2/utils/set.spl",
|
||||
"stage2/utils/source.spl",
|
||||
"stage2/utils/str.spl",
|
||||
"stage2/utils/utf8.spl",
|
||||
"stage2/utils/value.spl",
|
||||
"stage2/utils/vec.spl",
|
||||
"stage2/ast.spl",
|
||||
"stage2/ast2ir.spl",
|
||||
"stage2/attr.spl",
|
||||
"stage2/def.spl",
|
||||
"stage2/diag.spl",
|
||||
"stage2/eval.spl",
|
||||
"stage2/intrin.spl",
|
||||
"stage2/ir.spl",
|
||||
"stage2/ir2c.spl",
|
||||
"stage2/ir2vm.spl",
|
||||
"stage2/ir_builder.spl",
|
||||
"stage2/lang_type.spl",
|
||||
"stage2/layout.spl",
|
||||
"stage2/lexer.spl",
|
||||
"stage2/mcode.spl",
|
||||
"stage2/parser.spl",
|
||||
"stage2/scope.spl",
|
||||
"stage2/sema.spl",
|
||||
"stage2/splc1.spl",
|
||||
"stage2/token.spl",
|
||||
"stage2/type_table.spl",
|
||||
};
|
||||
|
||||
static const char *splc2_crt_deps[] = {};
|
||||
static const char *splc2_crt_deps[] = {
|
||||
"stage0/spl_rt.h", "stage0/spl_rt_core.h", "stage0/spl_asp.h",
|
||||
"stage0/spl_mcode.h", "stage0/spl_vm.h", "stage0/spl_syscall.h",
|
||||
"stage0/spl_vm_embed.h",
|
||||
};
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
@@ -138,32 +208,32 @@ static build_t targets[] = {
|
||||
BUILD_EXE_WITH_OPTIMIZATION,
|
||||
.exe = {.srcs = spl_cli_srcs,
|
||||
.srcs_count = NOB_ARRAY_LEN(spl_cli_srcs),
|
||||
.headers = NULL,
|
||||
.headers_count = 0},
|
||||
.headers = stage0_headers,
|
||||
.headers_count = NOB_ARRAY_LEN(stage0_headers)},
|
||||
},
|
||||
{
|
||||
"spl_cli_g",
|
||||
BUILD_EXE,
|
||||
.exe = {.srcs = spl_cli_srcs,
|
||||
.srcs_count = NOB_ARRAY_LEN(spl_cli_srcs),
|
||||
.headers = NULL,
|
||||
.headers_count = 0},
|
||||
.headers = stage0_headers,
|
||||
.headers_count = NOB_ARRAY_LEN(stage0_headers)},
|
||||
},
|
||||
{
|
||||
"vm_test",
|
||||
BUILD_EXE,
|
||||
.exe = {.srcs = vm_test_srcs,
|
||||
.srcs_count = NOB_ARRAY_LEN(vm_test_srcs),
|
||||
.headers = NULL,
|
||||
.headers_count = 0},
|
||||
.headers = stage0_headers,
|
||||
.headers_count = NOB_ARRAY_LEN(stage0_headers)},
|
||||
},
|
||||
{
|
||||
"splc0",
|
||||
BUILD_EXE,
|
||||
.exe = {.srcs = splc0_srcs,
|
||||
.srcs_count = NOB_ARRAY_LEN(splc0_srcs),
|
||||
.headers = NULL,
|
||||
.headers_count = 0},
|
||||
.headers = stage0_headers,
|
||||
.headers_count = NOB_ARRAY_LEN(stage0_headers)},
|
||||
},
|
||||
{
|
||||
"splc1",
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef __SPL_ASP_H__
|
||||
#define __SPL_ASP_H__
|
||||
|
||||
/* spl_asp.h - 地址空间抽象(Address SPace)
|
||||
*
|
||||
* SPL 侧指针语义由 spl_asp_t 统一描述:
|
||||
* - native(编译器自身):identity,指针就是宿主地址。
|
||||
* - VM:句柄/偏移,offset = (handle<<32) | 区内字节偏移,0 为 null。
|
||||
*
|
||||
* runtime(spl_rt_core.h)的解引用/分配一律经此接口,从而同一份 SPL
|
||||
* 源的 Vec/Map/mem 在两种后端下都自洽。
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifndef SPL_USIZE_TYPEDEF
|
||||
#define SPL_USIZE_TYPEDEF
|
||||
typedef uintptr_t usize;
|
||||
#endif
|
||||
|
||||
typedef struct spl_asp spl_asp_t;
|
||||
|
||||
struct spl_asp {
|
||||
void *ctx;
|
||||
/* offset -> 宿主地址;size>0 时做边界检查;失败返回 NULL。 */
|
||||
unsigned char *(*xlate)(void *ctx, uintptr_t off, uintptr_t size);
|
||||
/* 分配 size 字节,返回 offset(0=失败)。 */
|
||||
uintptr_t (*alloc)(void *ctx, uintptr_t size);
|
||||
/* 重新分配 offset@old,返回新 offset(0=失败)。 */
|
||||
uintptr_t (*realloc)(void *ctx, uintptr_t off, uintptr_t size);
|
||||
/* 释放 offset。 */
|
||||
void (*free)(void *ctx, uintptr_t off);
|
||||
/* 宿主地址 -> offset;size>0 时校验范围。不属于该空间返回 0。 */
|
||||
uintptr_t (*to_off)(void *ctx, const void *host, uintptr_t size);
|
||||
};
|
||||
|
||||
#endif /* __SPL_ASP_H__ */
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
#ifndef __SPL_RT_H__
|
||||
#define __SPL_RT_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||
#define SPL_FN static inline
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#define SPL_FN static __inline__
|
||||
#else
|
||||
#define SPL_FN static
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
typedef uint8_t u8;
|
||||
typedef int8_t i8;
|
||||
typedef uint16_t u16;
|
||||
typedef int16_t i16;
|
||||
typedef uint32_t u32;
|
||||
typedef int32_t i32;
|
||||
typedef uint64_t u64;
|
||||
typedef int64_t i64;
|
||||
|
||||
#ifndef SPL_USIZE_TYPEDEF
|
||||
#define SPL_USIZE_TYPEDEF
|
||||
typedef u64 usize;
|
||||
#endif
|
||||
|
||||
#ifndef SPL_ISIZE_TYPEDEF
|
||||
#define SPL_ISIZE_TYPEDEF
|
||||
typedef i64 isize;
|
||||
#endif
|
||||
|
||||
SPL_FN void spl_panic(const char *msg) {
|
||||
fprintf(stderr, "%s\n", msg ? msg : "panic");
|
||||
exit(1);
|
||||
}
|
||||
SPL_FN void spl_trap(void) { abort(); }
|
||||
|
||||
#include "spl_rt_core.h"
|
||||
|
||||
SPL_FN void *vm_fopen(void *path, void *mode) {
|
||||
return (void *)fopen((const char *)path, (const char *)mode);
|
||||
}
|
||||
SPL_FN i32 vm_fclose(void *f) { return f ? fclose((FILE *)f) : 0; }
|
||||
SPL_FN u64 vm_fread(void *buf, u64 sz, u64 n, void *f) {
|
||||
return f ? (u64)fread(buf, (size_t)sz, (size_t)n, (FILE *)f) : 0;
|
||||
}
|
||||
SPL_FN u64 vm_fwrite(void *buf, u64 sz, u64 n, void *f) {
|
||||
return f ? (u64)fwrite(buf, (size_t)sz, (size_t)n, (FILE *)f) : 0;
|
||||
}
|
||||
SPL_FN u64 vm_fsize(void *f) {
|
||||
long cur, end;
|
||||
if (!f)
|
||||
return 0;
|
||||
cur = ftell((FILE *)f);
|
||||
if (cur < 0)
|
||||
return 0;
|
||||
if (fseek((FILE *)f, 0, SEEK_END) != 0)
|
||||
return 0;
|
||||
end = ftell((FILE *)f);
|
||||
fseek((FILE *)f, cur, SEEK_SET);
|
||||
return end < 0 ? 0 : (u64)end;
|
||||
}
|
||||
SPL_FN void *vm_stdin(void) { return (void *)stdin; }
|
||||
SPL_FN void *vm_stdout(void) { return (void *)stdout; }
|
||||
SPL_FN void *vm_stderr(void) { return (void *)stderr; }
|
||||
SPL_FN i32 vm_putchar(i32 c) { return fputc(c, stdout); }
|
||||
SPL_FN i32 vm_getchar(void) { return fgetc(stdin); }
|
||||
|
||||
SPL_FN void vm_exit(i32 code) { exit(code); }
|
||||
SPL_FN u64 vm_timestamp(u64 kind) {
|
||||
time_t sec = time(NULL);
|
||||
if (kind == 1)
|
||||
return (u64)sec;
|
||||
if (kind == 2)
|
||||
return 0;
|
||||
if (kind == 4)
|
||||
return (u64)sec * 1000000000ull;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SPL_VM_EMBED_IMPLIMENTATION
|
||||
#include "spl_vm_embed.h"
|
||||
|
||||
/* 配置层外部函数处理器(native 端):把 nested VM 未解析的 native 按名分发到
|
||||
* 编译器生成的 `ct_*` 函数(表由 ir2c 生成,见 stage2/ir2c.spl)。 */
|
||||
extern void *spl_ct_lookup(const char *name);
|
||||
|
||||
typedef spl_vm_val_t (*spl_native_ct_fn_t)(void *, spl_vm_val_t, spl_vm_val_t, spl_vm_val_t);
|
||||
|
||||
SPL_FN spl_vm_val_t spl_native_foreign(spl_vm_t *vm, const spl_vm_call_t *call, void *user) {
|
||||
void *fn;
|
||||
spl_vm_val_t a0 = 0, a1 = 0;
|
||||
(void)vm;
|
||||
if (!call)
|
||||
return 0;
|
||||
fn = spl_ct_lookup(call->name);
|
||||
if (!fn)
|
||||
return 0;
|
||||
if (call->nargs > 0)
|
||||
a0 = call->args[0];
|
||||
if (call->nargs > 1)
|
||||
a1 = call->args[1];
|
||||
/* ct_* 统一形状:返回 u64,参数 (ctx, u64, u64);多余参数被忽略。 */
|
||||
return ((spl_native_ct_fn_t)fn)(user, a0, a1, 0);
|
||||
}
|
||||
|
||||
SPL_FN void vm_set_foreign(void *p, unsigned long long handler, void *ctx) {
|
||||
(void)handler;
|
||||
spl_vm_set_foreign((spl_vm_t *)p, spl_native_foreign, ctx);
|
||||
}
|
||||
|
||||
#endif /* __SPL_RT_H__ */
|
||||
@@ -0,0 +1,691 @@
|
||||
#ifndef __SPL_RT_CORE_H__
|
||||
#define __SPL_RT_CORE_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "spl_asp.h"
|
||||
|
||||
#ifndef SPL_CORE_FN
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||
#define SPL_CORE_FN static inline
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#define SPL_CORE_FN static __inline__
|
||||
#else
|
||||
#define SPL_CORE_FN static
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* ================================================================
|
||||
* 地址空间:identity(native/编译器自身)
|
||||
* ================================================================ */
|
||||
|
||||
SPL_CORE_FN unsigned char *spl_asp_id_xlate(void *ctx, uintptr_t off, uintptr_t size) {
|
||||
(void)ctx;
|
||||
(void)size;
|
||||
return (unsigned char *)off;
|
||||
}
|
||||
SPL_CORE_FN uintptr_t spl_asp_id_alloc(void *ctx, uintptr_t size) {
|
||||
(void)ctx;
|
||||
return (uintptr_t)malloc((size_t)size);
|
||||
}
|
||||
SPL_CORE_FN uintptr_t spl_asp_id_realloc(void *ctx, uintptr_t off, uintptr_t size) {
|
||||
(void)ctx;
|
||||
return (uintptr_t)realloc((void *)off, (size_t)size);
|
||||
}
|
||||
SPL_CORE_FN void spl_asp_id_free(void *ctx, uintptr_t off) {
|
||||
(void)ctx;
|
||||
free((void *)off);
|
||||
}
|
||||
SPL_CORE_FN uintptr_t spl_asp_id_to_off(void *ctx, const void *host, uintptr_t size) {
|
||||
(void)ctx;
|
||||
(void)size;
|
||||
return (uintptr_t)host;
|
||||
}
|
||||
|
||||
static const spl_asp_t spl_asp_identity = {
|
||||
NULL, spl_asp_id_xlate, spl_asp_id_alloc, spl_asp_id_realloc, spl_asp_id_free,
|
||||
spl_asp_id_to_off,
|
||||
};
|
||||
#define SPL_ASP_ID ((spl_asp_t *)&spl_asp_identity)
|
||||
|
||||
/* 偏移 -> 宿主(不做边界检查)。 */
|
||||
SPL_CORE_FN unsigned char *spl_x(spl_asp_t *asp, uintptr_t off) {
|
||||
return asp->xlate(asp->ctx, off, 0);
|
||||
}
|
||||
SPL_CORE_FN void *spl_xp(spl_asp_t *asp, uintptr_t off, uintptr_t size) {
|
||||
return asp->xlate(asp->ctx, off, size);
|
||||
}
|
||||
|
||||
/* ---------------- mem ---------------- */
|
||||
|
||||
SPL_CORE_FN void *spl_alloc_ex(spl_asp_t *asp, uintptr_t sz) {
|
||||
if (sz == 0)
|
||||
return NULL;
|
||||
return (void *)asp->alloc(asp->ctx, sz);
|
||||
}
|
||||
|
||||
SPL_CORE_FN void spl_free_ex(spl_asp_t *asp, void *p) {
|
||||
if (p)
|
||||
asp->free(asp->ctx, (uintptr_t)p);
|
||||
}
|
||||
|
||||
SPL_CORE_FN void *spl_realloc_ex(spl_asp_t *asp, void *p, uintptr_t sz) {
|
||||
if (!p)
|
||||
return spl_alloc_ex(asp, sz);
|
||||
if (sz == 0) {
|
||||
asp->free(asp->ctx, (uintptr_t)p);
|
||||
return NULL;
|
||||
}
|
||||
return (void *)asp->realloc(asp->ctx, (uintptr_t)p, sz);
|
||||
}
|
||||
|
||||
SPL_CORE_FN void *vm_memcpy(void *dst, const void *src, uintptr_t n) {
|
||||
if (n)
|
||||
memcpy(dst, src, (size_t)n);
|
||||
return dst;
|
||||
}
|
||||
|
||||
SPL_CORE_FN void *vm_memmove(void *dst, const void *src, uintptr_t n) {
|
||||
if (n)
|
||||
memmove(dst, src, (size_t)n);
|
||||
return dst;
|
||||
}
|
||||
|
||||
SPL_CORE_FN intptr_t vm_memcmp(const void *a, const void *b, uintptr_t n) {
|
||||
if (n == 0)
|
||||
return 0;
|
||||
return (intptr_t)memcmp(a, b, (size_t)n);
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_bytes_eq_ex(spl_asp_t *asp, const void *a, uintptr_t alen, const void *b,
|
||||
uintptr_t blen) {
|
||||
(void)asp;
|
||||
if (alen != blen)
|
||||
return 0;
|
||||
if (alen == 0)
|
||||
return 1;
|
||||
if (!a || !b)
|
||||
return 0;
|
||||
return memcmp(a, b, (size_t)alen) == 0;
|
||||
}
|
||||
|
||||
/* ---------------- Vec ---------------- */
|
||||
|
||||
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;
|
||||
|
||||
SPL_CORE_FN int spl_align_ok(uintptr_t a) { return a != 0 && (a & (a - 1)) == 0; }
|
||||
|
||||
SPL_CORE_FN int spl_vec_is_valid(const spl_vec_t *v) {
|
||||
uintptr_t a;
|
||||
if (v->elem_size == 0)
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
|
||||
SPL_CORE_FN void 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;
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_vec_allocate_ex(spl_asp_t *asp, spl_vec_t *out, uintptr_t capacity,
|
||||
uintptr_t elem_size, uintptr_t elem_align) {
|
||||
uintptr_t bytes, mask, addr;
|
||||
uintptr_t raw;
|
||||
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;
|
||||
bytes = capacity * elem_size;
|
||||
mask = elem_align - 1;
|
||||
if (bytes > ~(uintptr_t)0 - mask)
|
||||
return 3;
|
||||
raw = asp->alloc(asp->ctx, bytes + mask);
|
||||
if (!raw)
|
||||
return 3;
|
||||
addr = raw;
|
||||
if (addr > ~(uintptr_t)0 - mask) {
|
||||
asp->free(asp->ctx, raw);
|
||||
return 3;
|
||||
}
|
||||
out->raw = raw;
|
||||
out->data = (addr + mask) & ~mask;
|
||||
out->cap = capacity;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_vec_reserve_raw_ex(spl_asp_t *asp, spl_vec_t *v, uintptr_t need) {
|
||||
uintptr_t new_cap = 0, used = 0, old;
|
||||
spl_vec_t fresh;
|
||||
int rc;
|
||||
if (need <= v->cap)
|
||||
return 0;
|
||||
spl_grow_capacity(v->cap, 8, need, &new_cap);
|
||||
rc = spl_vec_allocate_ex(asp, &fresh, new_cap, v->elem_size, v->elem_align);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (v->len != 0) {
|
||||
if (v->len > ~(uintptr_t)0 / v->elem_size) {
|
||||
asp->free(asp->ctx, fresh.raw);
|
||||
return 3;
|
||||
}
|
||||
used = v->len * v->elem_size;
|
||||
}
|
||||
if (used)
|
||||
memcpy(spl_x(asp, fresh.data), spl_x(asp, v->data), used);
|
||||
fresh.len = v->len;
|
||||
old = v->raw;
|
||||
*v = fresh;
|
||||
if (old)
|
||||
asp->free(asp->ctx, old);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPL_CORE_FN void spl_vec_release_ex(spl_asp_t *asp, spl_vec_t *v) {
|
||||
uintptr_t es = v->elem_size, ea = v->elem_align;
|
||||
if (v->raw)
|
||||
asp->free(asp->ctx, v->raw);
|
||||
v->raw = 0;
|
||||
v->data = 0;
|
||||
v->len = 0;
|
||||
v->cap = 0;
|
||||
v->elem_size = es;
|
||||
v->elem_align = ea;
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_vec_valid_ex(const void *vv) {
|
||||
return vv ? spl_vec_is_valid((const spl_vec_t *)vv) : 0;
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_vec_push_raw_ex(spl_asp_t *asp, void *vv, const void *elem) {
|
||||
spl_vec_t *v = (spl_vec_t *)vv;
|
||||
if (!v || !elem)
|
||||
return 0;
|
||||
if (v->len < v->cap) {
|
||||
memcpy(spl_x(asp, v->data + v->len * v->elem_size), elem, v->elem_size);
|
||||
v->len++;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_vec_push_ex(spl_asp_t *asp, void *vv, const void *elem) {
|
||||
spl_vec_t *v = (spl_vec_t *)vv;
|
||||
int rc;
|
||||
if (!v || !elem || !spl_vec_is_valid(v))
|
||||
return 1;
|
||||
if (v->len < v->cap) {
|
||||
memcpy(spl_x(asp, v->data + v->len * v->elem_size), elem, v->elem_size);
|
||||
v->len++;
|
||||
return 0;
|
||||
}
|
||||
rc = spl_vec_reserve_raw_ex(asp, v, v->len + 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
memmove(spl_x(asp, v->data + v->len * v->elem_size), elem, v->elem_size);
|
||||
v->len++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_vec_get_ex(spl_asp_t *asp, const void *vv, uintptr_t index, void **out) {
|
||||
const spl_vec_t *v = (const spl_vec_t *)vv;
|
||||
unsigned char *p;
|
||||
if (!v || !out)
|
||||
return 1;
|
||||
if (!spl_vec_is_valid(v))
|
||||
return 1;
|
||||
if (index >= v->len)
|
||||
return 2;
|
||||
p = spl_x(asp, v->data) + index * v->elem_size;
|
||||
*out = (void *)(uintptr_t)asp->to_off(asp->ctx, p, v->elem_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_vec_allocate_out_ex(spl_asp_t *asp, void *out, uintptr_t capacity,
|
||||
uintptr_t elem_size, uintptr_t elem_align) {
|
||||
if (!out)
|
||||
return 1;
|
||||
return spl_vec_allocate_ex(asp, (spl_vec_t *)out, capacity, elem_size, elem_align);
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_vec_reserve_raw_void_ex(spl_asp_t *asp, void *vv, uintptr_t need) {
|
||||
if (!vv)
|
||||
return 1;
|
||||
return spl_vec_reserve_raw_ex(asp, (spl_vec_t *)vv, need);
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_vec_append_ex(spl_asp_t *asp, void *vv, const void *elems, uintptr_t count) {
|
||||
spl_vec_t *v = (spl_vec_t *)vv;
|
||||
uintptr_t copy_bytes, new_len, used, source_offset = 0;
|
||||
int source_inside = 0, rc;
|
||||
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;
|
||||
copy_bytes = count * v->elem_size;
|
||||
if (v->len > ~(uintptr_t)0 - count)
|
||||
return 3;
|
||||
new_len = v->len + count;
|
||||
used = v->len * v->elem_size;
|
||||
if (used) {
|
||||
unsigned char *base = spl_x(asp, v->data);
|
||||
uintptr_t source = (uintptr_t)elems;
|
||||
if (source >= (uintptr_t)base && source - (uintptr_t)base < used) {
|
||||
source_offset = source - (uintptr_t)base;
|
||||
if (copy_bytes > used - source_offset)
|
||||
return 2;
|
||||
source_inside = 1;
|
||||
}
|
||||
}
|
||||
rc = spl_vec_reserve_raw_ex(asp, v, new_len);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (source_inside)
|
||||
elems = (const void *)(spl_x(asp, v->data) + source_offset);
|
||||
memmove(spl_x(asp, v->data + used), elems, copy_bytes);
|
||||
v->len = new_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_vec_set_ex(spl_asp_t *asp, void *vv, uintptr_t index, const void *elem) {
|
||||
spl_vec_t *v = (spl_vec_t *)vv;
|
||||
if (!v || !elem || !spl_vec_is_valid(v))
|
||||
return 1;
|
||||
if (index >= v->len)
|
||||
return 2;
|
||||
memmove(spl_x(asp, v->data + index * v->elem_size), elem, v->elem_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_vec_pop_ex(spl_asp_t *asp, void *vv, void *out) {
|
||||
spl_vec_t *v = (spl_vec_t *)vv;
|
||||
if (!v || !out || !spl_vec_is_valid(v))
|
||||
return 1;
|
||||
if (v->len == 0)
|
||||
return 2;
|
||||
memmove(out, spl_x(asp, v->data + (v->len - 1) * v->elem_size), v->elem_size);
|
||||
v->len--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_vec_clone_ex(spl_asp_t *asp, void *outp, const void *srcp) {
|
||||
spl_vec_t *out = (spl_vec_t *)outp;
|
||||
const spl_vec_t *src = (const spl_vec_t *)srcp;
|
||||
spl_vec_t fresh;
|
||||
uintptr_t bytes;
|
||||
int rc;
|
||||
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;
|
||||
fresh.raw = 0;
|
||||
fresh.data = 0;
|
||||
fresh.len = 0;
|
||||
fresh.cap = 0;
|
||||
fresh.elem_size = src->elem_size;
|
||||
fresh.elem_align = src->elem_align;
|
||||
rc = spl_vec_reserve_raw_ex(asp, &fresh, src->len);
|
||||
if (rc)
|
||||
return rc;
|
||||
bytes = src->len * src->elem_size;
|
||||
if (bytes)
|
||||
memcpy(spl_x(asp, fresh.data), spl_x(asp, src->data), bytes);
|
||||
fresh.len = src->len;
|
||||
if (out->raw)
|
||||
asp->free(asp->ctx, out->raw);
|
||||
*out = fresh;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_vec_drop_ex(spl_asp_t *asp, void *vv) {
|
||||
if (vv)
|
||||
spl_vec_release_ex(asp, (spl_vec_t *)vv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------- Map ---------------- */
|
||||
|
||||
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;
|
||||
|
||||
SPL_CORE_FN 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;
|
||||
}
|
||||
|
||||
SPL_CORE_FN 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;
|
||||
uintptr_t i;
|
||||
for (i = 0; i < len; i++)
|
||||
h = (h ^ (uintptr_t)p[i]) * prime;
|
||||
return h;
|
||||
}
|
||||
|
||||
SPL_CORE_FN uintptr_t spl_hash_bytes_ex(spl_asp_t *asp, const void *data, uintptr_t len) {
|
||||
return spl_hash_bytes(spl_x(asp, (uintptr_t)data), len);
|
||||
}
|
||||
|
||||
SPL_CORE_FN uintptr_t spl_map_hash_ex(spl_asp_t *asp, const spl_map_t *m, const void *key) {
|
||||
const uintptr_t *v;
|
||||
if (m->hash_kind == 0)
|
||||
return spl_hash_usize_value(*(const uintptr_t *)key);
|
||||
if (m->hash_kind == 2)
|
||||
return 3;
|
||||
v = (const uintptr_t *)key;
|
||||
return spl_hash_bytes_ex(asp, (const void *)v[0], v[1]);
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_map_eq_ex(spl_asp_t *asp, const spl_map_t *m, const void *a, const void *b) {
|
||||
const uintptr_t *x;
|
||||
const uintptr_t *y;
|
||||
if (m->hash_kind == 0 || m->hash_kind == 2)
|
||||
return *(const uintptr_t *)a == *(const uintptr_t *)b;
|
||||
x = (const uintptr_t *)a;
|
||||
y = (const uintptr_t *)b;
|
||||
if (x[1] != y[1])
|
||||
return 0;
|
||||
if (x[1] == 0)
|
||||
return 1;
|
||||
return memcmp(spl_x(asp, x[0]), spl_x(asp, y[0]), (size_t)x[1]) == 0;
|
||||
}
|
||||
|
||||
SPL_CORE_FN 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;
|
||||
}
|
||||
|
||||
SPL_CORE_FN uintptr_t spl_map_find_slot_ex(spl_asp_t *asp, const spl_map_t *m, const void *key) {
|
||||
uintptr_t mask, i, probes;
|
||||
unsigned char *st;
|
||||
if (m->states.cap == 0)
|
||||
return ~(uintptr_t)0;
|
||||
mask = m->states.cap - 1;
|
||||
i = spl_map_hash_ex(asp, m, key) & mask;
|
||||
st = spl_x(asp, m->states.data);
|
||||
probes = 0;
|
||||
while (probes < m->states.cap && st[i] != 0) {
|
||||
if (st[i] == 1 &&
|
||||
spl_map_eq_ex(asp, m, spl_x(asp, m->keys.data + i * m->key_size), key))
|
||||
return i;
|
||||
i = (i + 1) & mask;
|
||||
probes++;
|
||||
}
|
||||
return ~(uintptr_t)0;
|
||||
}
|
||||
|
||||
SPL_CORE_FN void spl_map_place_ex(spl_asp_t *asp, spl_map_t *m, const void *key, const void *val) {
|
||||
uintptr_t mask = m->states.cap - 1;
|
||||
uintptr_t i = spl_map_hash_ex(asp, m, key) & mask;
|
||||
unsigned char *st = spl_x(asp, 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(spl_x(asp, m->keys.data + i * m->key_size), key, m->key_size);
|
||||
memcpy(spl_x(asp, m->vals.data + i * m->val_size), val, m->val_size);
|
||||
st[i] = 1;
|
||||
m->size++;
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_map_reserve_ex(spl_asp_t *asp, spl_map_t *m, uintptr_t need) {
|
||||
uintptr_t new_cap = 0, i;
|
||||
spl_map_t fresh;
|
||||
int rc;
|
||||
if (!spl_map_valid(m))
|
||||
return 1;
|
||||
if (need <= m->states.cap)
|
||||
return 0;
|
||||
spl_grow_capacity(m->states.cap, 8, need, &new_cap);
|
||||
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;
|
||||
rc = spl_vec_reserve_raw_ex(asp, &fresh.keys, new_cap);
|
||||
if (rc)
|
||||
goto fail;
|
||||
rc = spl_vec_reserve_raw_ex(asp, &fresh.vals, new_cap);
|
||||
if (rc)
|
||||
goto fail;
|
||||
rc = spl_vec_reserve_raw_ex(asp, &fresh.states, new_cap);
|
||||
if (rc)
|
||||
goto fail;
|
||||
fresh.keys.len = new_cap;
|
||||
fresh.vals.len = new_cap;
|
||||
fresh.states.len = new_cap;
|
||||
memset(spl_x(asp, fresh.states.data), 0, new_cap);
|
||||
for (i = 0; i < m->states.len; i++) {
|
||||
if (spl_x(asp, m->states.data)[i] == 1)
|
||||
spl_map_place_ex(asp, &fresh, spl_x(asp, m->keys.data + i * m->key_size),
|
||||
spl_x(asp, m->vals.data + i * m->val_size));
|
||||
}
|
||||
spl_vec_release_ex(asp, &m->keys);
|
||||
spl_vec_release_ex(asp, &m->vals);
|
||||
spl_vec_release_ex(asp, &m->states);
|
||||
*m = fresh;
|
||||
return 0;
|
||||
fail:
|
||||
spl_vec_release_ex(asp, &fresh.keys);
|
||||
spl_vec_release_ex(asp, &fresh.vals);
|
||||
spl_vec_release_ex(asp, &fresh.states);
|
||||
return rc;
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_map_valid_ex(const void *mm) {
|
||||
return mm ? spl_map_valid((const spl_map_t *)mm) : 0;
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_map_reserve_void_ex(spl_asp_t *asp, void *mm, uintptr_t need) {
|
||||
if (!mm)
|
||||
return 1;
|
||||
return spl_map_reserve_ex(asp, (spl_map_t *)mm, need);
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_map_put_ex(spl_asp_t *asp, void *mm, const void *key, const void *val) {
|
||||
spl_map_t *m = (spl_map_t *)mm;
|
||||
uintptr_t existing;
|
||||
if (!m || !key || !val || !spl_map_valid(m))
|
||||
return 1;
|
||||
existing = spl_map_find_slot_ex(asp, m, key);
|
||||
if (existing != ~(uintptr_t)0) {
|
||||
memcpy(spl_x(asp, m->vals.data + existing * m->val_size), val, m->val_size);
|
||||
return 0;
|
||||
}
|
||||
if (m->states.cap == 0) {
|
||||
int rc = spl_map_reserve_ex(asp, m, 8);
|
||||
if (rc)
|
||||
return rc;
|
||||
} else {
|
||||
uintptr_t next_size, scaled;
|
||||
if (m->size > ~(uintptr_t)0 - 1)
|
||||
return 3;
|
||||
next_size = m->size + 1;
|
||||
if (next_size > ~(uintptr_t)0 / 128)
|
||||
return 3;
|
||||
scaled = next_size * 128;
|
||||
if (scaled / m->states.cap >= 70) {
|
||||
int rc;
|
||||
if (m->states.cap > ~(uintptr_t)0 / 2)
|
||||
return 3;
|
||||
rc = spl_map_reserve_ex(asp, m, m->states.cap * 2);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
spl_map_place_ex(asp, m, key, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_map_get_ex(spl_asp_t *asp, void *mm, const void *key, void *out) {
|
||||
spl_map_t *m = (spl_map_t *)mm;
|
||||
uintptr_t i;
|
||||
if (!m || !key || !out || !spl_map_valid(m))
|
||||
return 1;
|
||||
i = spl_map_find_slot_ex(asp, m, key);
|
||||
if (i == ~(uintptr_t)0)
|
||||
return 1;
|
||||
memcpy(out, spl_x(asp, m->vals.data + i * m->val_size), m->val_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_map_del_ex(spl_asp_t *asp, void *mm, const void *key) {
|
||||
spl_map_t *m = (spl_map_t *)mm;
|
||||
uintptr_t i;
|
||||
if (!m || !key || !spl_map_valid(m))
|
||||
return 1;
|
||||
i = spl_map_find_slot_ex(asp, m, key);
|
||||
if (i == ~(uintptr_t)0)
|
||||
return 1;
|
||||
spl_x(asp, m->states.data)[i] = 2;
|
||||
m->size--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_map_clear_ex(spl_asp_t *asp, void *mm) {
|
||||
spl_map_t *m = (spl_map_t *)mm;
|
||||
if (!m)
|
||||
return 0;
|
||||
if (m->states.data && m->states.len)
|
||||
memset(spl_x(asp, m->states.data), 0, m->states.len);
|
||||
m->size = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPL_CORE_FN int spl_map_drop_ex(spl_asp_t *asp, void *mm) {
|
||||
spl_map_t *m = (spl_map_t *)mm;
|
||||
if (!m)
|
||||
return 0;
|
||||
spl_vec_release_ex(asp, &m->keys);
|
||||
spl_vec_release_ex(asp, &m->vals);
|
||||
spl_vec_release_ex(asp, &m->states);
|
||||
m->size = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* 公开 C 符号(identity):供 native 编译器直接调用
|
||||
* ================================================================ */
|
||||
|
||||
SPL_CORE_FN void *vm_alloc(uintptr_t sz) { return spl_alloc_ex(SPL_ASP_ID, sz); }
|
||||
SPL_CORE_FN void vm_free(void *p) { spl_free_ex(SPL_ASP_ID, p); }
|
||||
SPL_CORE_FN void *vm_realloc(void *p, uintptr_t sz) { return spl_realloc_ex(SPL_ASP_ID, p, sz); }
|
||||
SPL_CORE_FN int vm_bytes_eq(const void *a, uintptr_t alen, const void *b, uintptr_t blen) {
|
||||
return spl_bytes_eq_ex(SPL_ASP_ID, a, alen, b, blen);
|
||||
}
|
||||
|
||||
SPL_CORE_FN int vm_vec_valid(const void *v) { return spl_vec_valid_ex(v); }
|
||||
SPL_CORE_FN int vm_vec_push_raw(void *v, const void *e) {
|
||||
return spl_vec_push_raw_ex(SPL_ASP_ID, v, e);
|
||||
}
|
||||
SPL_CORE_FN int vm_vec_push(void *v, const void *e) { return spl_vec_push_ex(SPL_ASP_ID, v, e); }
|
||||
SPL_CORE_FN int vm_vec_get(const void *v, uintptr_t i, void **o) {
|
||||
return spl_vec_get_ex(SPL_ASP_ID, v, i, o);
|
||||
}
|
||||
SPL_CORE_FN int vm_vec_allocate(void *o, uintptr_t c, uintptr_t es, uintptr_t ea) {
|
||||
return spl_vec_allocate_out_ex(SPL_ASP_ID, o, c, es, ea);
|
||||
}
|
||||
SPL_CORE_FN int vm_vec_reserve_raw(void *v, uintptr_t n) {
|
||||
return spl_vec_reserve_raw_void_ex(SPL_ASP_ID, v, n);
|
||||
}
|
||||
SPL_CORE_FN int vm_vec_append(void *v, const void *e, uintptr_t n) {
|
||||
return spl_vec_append_ex(SPL_ASP_ID, v, e, n);
|
||||
}
|
||||
SPL_CORE_FN int vm_vec_set(void *v, uintptr_t i, const void *e) {
|
||||
return spl_vec_set_ex(SPL_ASP_ID, v, i, e);
|
||||
}
|
||||
SPL_CORE_FN int vm_vec_pop(void *v, void *o) { return spl_vec_pop_ex(SPL_ASP_ID, v, o); }
|
||||
SPL_CORE_FN int vm_vec_clone(void *o, const void *s) { return spl_vec_clone_ex(SPL_ASP_ID, o, s); }
|
||||
SPL_CORE_FN int vm_vec_drop(void *v) { return spl_vec_drop_ex(SPL_ASP_ID, v); }
|
||||
|
||||
SPL_CORE_FN int vm_map_valid(const void *m) { return spl_map_valid_ex(m); }
|
||||
SPL_CORE_FN int vm_map_reserve(void *m, uintptr_t n) {
|
||||
return spl_map_reserve_void_ex(SPL_ASP_ID, m, n);
|
||||
}
|
||||
SPL_CORE_FN int vm_map_put(void *m, const void *k, const void *v) {
|
||||
return spl_map_put_ex(SPL_ASP_ID, m, k, v);
|
||||
}
|
||||
SPL_CORE_FN int vm_map_get(void *m, const void *k, void *o) {
|
||||
return spl_map_get_ex(SPL_ASP_ID, m, k, o);
|
||||
}
|
||||
SPL_CORE_FN int vm_map_del(void *m, const void *k) { return spl_map_del_ex(SPL_ASP_ID, m, k); }
|
||||
SPL_CORE_FN int vm_map_clear(void *m) { return spl_map_clear_ex(SPL_ASP_ID, m); }
|
||||
SPL_CORE_FN int vm_map_drop(void *m) { return spl_map_drop_ex(SPL_ASP_ID, m); }
|
||||
|
||||
#endif /* __SPL_RT_CORE_H__ */
|
||||
+541
-825
File diff suppressed because it is too large.
Load diff
+401
-28
@@ -568,6 +568,18 @@ void spl_vm_init_ex(spl_vm_t *vm, int stack_size, int call_depth) {
|
||||
vec_realloc(vm->frames, (usize)vm->config.max_call_depth);
|
||||
vec_init(vm->breakpoints);
|
||||
vec_init(vm->fn_breakpoints);
|
||||
vec_init(vm->gdata_off);
|
||||
vec_init(vm->regions);
|
||||
|
||||
/* regions[0] 保留;regions[1] = 栈区。 */
|
||||
vec_push(vm->regions, ((spl_vm_region_t){NULL, 0, 0}));
|
||||
vec_push(vm->regions, ((spl_vm_region_t){
|
||||
(unsigned char *)vm->stacks.data,
|
||||
(usize)vm->config.max_stack_depth * sizeof(spl_vm_val_t), 1}));
|
||||
vm->stdio_off[0] = vm->stdio_off[1] = vm->stdio_off[2] = 0;
|
||||
vm->ret_val = 0;
|
||||
vm->foreign = NULL;
|
||||
vm->foreign_ctx = NULL;
|
||||
|
||||
vm->sp = vm->fp = vm->ip = vm->cp = vm->gp = 0;
|
||||
vm->steps = 0;
|
||||
@@ -593,9 +605,206 @@ void spl_vm_drop(spl_vm_t *vm) {
|
||||
free(vm->fn_breakpoints.data[i]);
|
||||
vec_free(vm->fn_breakpoints);
|
||||
vec_free(vm->breakpoints);
|
||||
vec_free(vm->gdata_off);
|
||||
for (usize i = 2; i < vec_size(vm->regions); i++) {
|
||||
spl_vm_region_t *r = &vec_at(vm->regions, i);
|
||||
if (r->used && r->base)
|
||||
free(r->base);
|
||||
}
|
||||
vec_free(vm->regions);
|
||||
spl_vm_profile_free(vm);
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* 统一偏移地址空间:offset = (handle<<32) | 区内字节偏移
|
||||
* ================================================================ */
|
||||
|
||||
#define SPL_ASP_HANDLE_OF(off) ((usize)((spl_vm_val_t)(off) >> 32))
|
||||
#define SPL_ASP_BYTE_OF(off) ((usize)((spl_vm_val_t)(off) & 0xFFFFFFFFull))
|
||||
|
||||
static spl_vm_region_t *spl_vm_region_at(spl_vm_t *vm, usize handle) {
|
||||
if (handle == 0 || handle >= vec_size(vm->regions))
|
||||
return NULL;
|
||||
return &vec_at(vm->regions, handle);
|
||||
}
|
||||
|
||||
void *spl_vm_xlate(spl_vm_t *vm, spl_vm_val_t off, usize size) {
|
||||
usize handle, byte;
|
||||
spl_vm_region_t *r;
|
||||
if (!vm || off == SPL_OFF_NULL)
|
||||
return NULL;
|
||||
handle = SPL_ASP_HANDLE_OF(off);
|
||||
byte = SPL_ASP_BYTE_OF(off);
|
||||
r = spl_vm_region_at(vm, handle);
|
||||
if (!r || !r->used)
|
||||
return NULL;
|
||||
if (byte > r->size || (size > 0 && byte + size > r->size))
|
||||
return NULL;
|
||||
return (void *)(r->base + byte);
|
||||
}
|
||||
|
||||
spl_vm_val_t spl_vm_to_off(spl_vm_t *vm, const void *ptr) {
|
||||
if (!vm || !ptr)
|
||||
return SPL_OFF_NULL;
|
||||
{
|
||||
unsigned char *p = (unsigned char *)ptr;
|
||||
usize i;
|
||||
for (i = 1; i < vec_size(vm->regions); i++) {
|
||||
spl_vm_region_t *r = &vec_at(vm->regions, i);
|
||||
if (r->used && r->base && p >= r->base && p < r->base + r->size)
|
||||
return SPL_ASP_OFF(i, (usize)(p - r->base));
|
||||
}
|
||||
}
|
||||
return SPL_OFF_NULL;
|
||||
}
|
||||
|
||||
/* 返回用于存放该偏移的 handle 槽;找不到空闲/空间不足返回 0。 */
|
||||
static usize spl_vm_region_new(spl_vm_t *vm, void *base, usize size) {
|
||||
usize i;
|
||||
for (i = 2; i < vec_size(vm->regions); i++) {
|
||||
spl_vm_region_t *r = &vec_at(vm->regions, i);
|
||||
if (!r->used) {
|
||||
r->base = (unsigned char *)base;
|
||||
r->size = size;
|
||||
r->used = 1;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
if (vec_size(vm->regions) >= (usize)1 << 32)
|
||||
return 0;
|
||||
vec_push(vm->regions, ((spl_vm_region_t){(unsigned char *)base, size, 1}));
|
||||
return vec_size(vm->regions) - 1;
|
||||
}
|
||||
|
||||
/* ---- spl_asp_t 实现 ---- */
|
||||
|
||||
static unsigned char *spl_vm_asp_xlate(void *ctx, uintptr_t off, uintptr_t size) {
|
||||
return (unsigned char *)spl_vm_xlate((spl_vm_t *)ctx, (spl_vm_val_t)off, (usize)size);
|
||||
}
|
||||
|
||||
static uintptr_t spl_vm_asp_alloc(void *ctx, uintptr_t size) {
|
||||
spl_vm_t *vm = (spl_vm_t *)ctx;
|
||||
void *p;
|
||||
usize h;
|
||||
if (size == 0 || size > 0xFFFFFFFFull)
|
||||
return 0;
|
||||
p = malloc((size_t)size);
|
||||
if (!p)
|
||||
return 0;
|
||||
h = spl_vm_region_new(vm, p, (usize)size);
|
||||
if (h == 0) {
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
||||
return (uintptr_t)SPL_ASP_OFF(h, 0);
|
||||
}
|
||||
|
||||
static uintptr_t spl_vm_asp_realloc(void *ctx, uintptr_t off, uintptr_t size) {
|
||||
spl_vm_t *vm = (spl_vm_t *)ctx;
|
||||
spl_vm_region_t *r;
|
||||
void *np;
|
||||
usize h;
|
||||
if (size == 0 || size > 0xFFFFFFFFull)
|
||||
return 0;
|
||||
if (off == 0)
|
||||
return spl_vm_asp_alloc(ctx, size);
|
||||
h = SPL_ASP_HANDLE_OF(off);
|
||||
r = spl_vm_region_at(vm, h);
|
||||
if (!r || !r->used)
|
||||
return 0;
|
||||
np = realloc(r->base, (size_t)size);
|
||||
if (!np)
|
||||
return 0;
|
||||
r->base = (unsigned char *)np;
|
||||
r->size = (usize)size;
|
||||
return (uintptr_t)SPL_ASP_OFF(h, SPL_ASP_BYTE_OF(off) & 0);
|
||||
}
|
||||
|
||||
static void spl_vm_asp_free(void *ctx, uintptr_t off) {
|
||||
spl_vm_t *vm = (spl_vm_t *)ctx;
|
||||
usize h;
|
||||
spl_vm_region_t *r;
|
||||
if (off == 0 || !vm)
|
||||
return;
|
||||
h = SPL_ASP_HANDLE_OF(off);
|
||||
if (h < 2) /* 0/1 不可释放 */
|
||||
return;
|
||||
r = spl_vm_region_at(vm, h);
|
||||
if (!r || !r->used)
|
||||
return;
|
||||
free(r->base);
|
||||
r->base = NULL;
|
||||
r->size = 0;
|
||||
r->used = 0;
|
||||
}
|
||||
|
||||
static uintptr_t spl_vm_asp_to_off(void *ctx, const void *host, uintptr_t size) {
|
||||
(void)size;
|
||||
return (uintptr_t)spl_vm_to_off((spl_vm_t *)ctx, host);
|
||||
}
|
||||
|
||||
spl_asp_t *spl_vm_asp(spl_vm_t *vm) {
|
||||
if (!vm)
|
||||
return NULL;
|
||||
vm->asp.ctx = vm;
|
||||
vm->asp.xlate = spl_vm_asp_xlate;
|
||||
vm->asp.alloc = spl_vm_asp_alloc;
|
||||
vm->asp.realloc = spl_vm_asp_realloc;
|
||||
vm->asp.free = spl_vm_asp_free;
|
||||
vm->asp.to_off = spl_vm_asp_to_off;
|
||||
return &vm->asp;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* 动态外部回调 + 栈/结果/内存访问
|
||||
* ================================================================ */
|
||||
|
||||
void spl_vm_set_foreign(spl_vm_t *vm, spl_vm_foreign_fn fn, void *ctx) {
|
||||
if (!vm)
|
||||
return;
|
||||
vm->foreign = fn;
|
||||
vm->foreign_ctx = ctx;
|
||||
}
|
||||
|
||||
spl_vm_val_t spl_vm_ret_val(spl_vm_t *vm) { return vm ? vm->ret_val : 0; }
|
||||
|
||||
usize spl_vm_sp(spl_vm_t *vm) { return vm ? (usize)vm->sp : 0; }
|
||||
|
||||
usize spl_vm_fp(spl_vm_t *vm) { return vm ? (usize)vm->fp : 0; }
|
||||
|
||||
spl_vm_val_t spl_vm_stack_get(spl_vm_t *vm, usize index) {
|
||||
if (!vm || index >= (usize)vm->config.max_stack_depth || !vm->stacks.data)
|
||||
return 0;
|
||||
return vm->stacks.data[index];
|
||||
}
|
||||
|
||||
int spl_vm_stack_set(spl_vm_t *vm, usize index, spl_vm_val_t val) {
|
||||
if (!vm || index >= (usize)vm->config.max_stack_depth || !vm->stacks.data)
|
||||
return -1;
|
||||
vm->stacks.data[index] = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
spl_vm_val_t spl_vm_mem_alloc(spl_vm_t *vm, usize size) { return spl_vm_asp_alloc(vm, size); }
|
||||
|
||||
int spl_vm_mem_read(spl_vm_t *vm, spl_vm_val_t off, void *dst, usize n) {
|
||||
void *src = spl_vm_xlate(vm, off, n);
|
||||
if ((!src || !dst) && n)
|
||||
return -1;
|
||||
if (n)
|
||||
memcpy(dst, src, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spl_vm_mem_write(spl_vm_t *vm, spl_vm_val_t off, const void *src, usize n) {
|
||||
void *dst = spl_vm_xlate(vm, off, n);
|
||||
if ((!dst || !src) && n)
|
||||
return -1;
|
||||
if (n)
|
||||
memcpy(dst, src, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// FIXME
|
||||
int spl_vm_addr_valid(uintptr_t p, usize size) {
|
||||
if (p == 0)
|
||||
@@ -685,6 +894,35 @@ int spl_vm_load_prog(spl_vm_t *vm, spl_prog_t *prog) {
|
||||
if (!vm || !prog)
|
||||
return -1;
|
||||
vm->prog = prog;
|
||||
|
||||
/* 释放上一次加载的堆分配(保留 regions[0]/[1])。 */
|
||||
for (usize i = 2; i < vec_size(vm->regions); i++) {
|
||||
spl_vm_region_t *r = &vec_at(vm->regions, i);
|
||||
if (r->used && r->base)
|
||||
free(r->base);
|
||||
r->base = NULL;
|
||||
r->size = 0;
|
||||
r->used = 0;
|
||||
}
|
||||
|
||||
/* 把 prog 的常量段搬进本 VM 空间,并记录每个 gdata 的偏移;
|
||||
* gdata 索引 0 为哨兵,偏移 0(null)。 */
|
||||
vm->gdata_off.size = 0;
|
||||
vec_push(vm->gdata_off, (usize)SPL_OFF_NULL);
|
||||
for (usize i = 1; i < vec_size(prog->gdata); i++) {
|
||||
spl_vm_gdata_t *gd = &vec_at(prog->gdata, i);
|
||||
usize sz = gd->size ? gd->size : 1;
|
||||
spl_vm_val_t off = spl_vm_asp_alloc(vm, sz);
|
||||
unsigned char *dst;
|
||||
if (off == SPL_OFF_NULL)
|
||||
return -1;
|
||||
dst = (unsigned char *)spl_vm_xlate(vm, off, sz);
|
||||
if (!dst)
|
||||
return -1;
|
||||
if (gd->data && gd->size)
|
||||
memcpy(dst, gd->data, gd->size);
|
||||
vec_push(vm->gdata_off, (usize)off);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -707,21 +945,53 @@ void spl_vm_set_debug(spl_vm_t *vm, int enabled) {
|
||||
|
||||
#define STACK_CANARY(vm) (vm)->stacks.data[(vm)->fp - 1]
|
||||
|
||||
/* 把 C 字符串复制进 VM 空间,返回其偏移。 */
|
||||
static spl_vm_val_t spl_vm_marshal_cstr(spl_vm_t *vm, const char *s) {
|
||||
usize n = s ? strlen(s) + 1 : 1;
|
||||
spl_vm_val_t off = spl_vm_asp_alloc(vm, n);
|
||||
unsigned char *d;
|
||||
if (off == 0)
|
||||
return 0;
|
||||
d = (unsigned char *)spl_vm_xlate(vm, off, n);
|
||||
if (!d)
|
||||
return 0;
|
||||
if (s)
|
||||
memcpy(d, s, n);
|
||||
else
|
||||
d[0] = 0;
|
||||
return off;
|
||||
}
|
||||
|
||||
/* 把 argv/envp 指针数组整体编组进 VM 空间,返回数组偏移。 */
|
||||
static spl_vm_val_t spl_vm_marshal_argv(spl_vm_t *vm, int argc, const char **argv) {
|
||||
usize bytes = (usize)(argc + 1) * sizeof(spl_vm_val_t);
|
||||
spl_vm_val_t arr = spl_vm_asp_alloc(vm, bytes);
|
||||
spl_vm_val_t *slots;
|
||||
int i;
|
||||
if (arr == 0)
|
||||
return 0;
|
||||
slots = (spl_vm_val_t *)spl_vm_xlate(vm, arr, bytes);
|
||||
if (!slots)
|
||||
return 0;
|
||||
for (i = 0; i < argc; i++)
|
||||
slots[i] = spl_vm_marshal_cstr(vm, argv ? argv[i] : NULL);
|
||||
slots[argc] = 0;
|
||||
return arr;
|
||||
}
|
||||
|
||||
/* ABI:调用者在实参下方预留 2 个空闲 slot(PAD0/PAD1)。
|
||||
* fp 指向 arg0;fp-1 为栈金丝雀槽,fp-2 为空闲槽。
|
||||
* saved_sp = fp-2(调用者压参数前的 sp)。 */
|
||||
#define SPL_CALL_PADS 2
|
||||
|
||||
static inline int spl_vm_call(spl_vm_t *vm, spl_vm_val_t addr, spl_vm_val_t nargs) {
|
||||
uintptr_t saved_sp;
|
||||
if (vm->cp >= vm->config.max_call_depth)
|
||||
VM_ERROR("call depth exceeded");
|
||||
if (vm->sp < nargs + SPL_CALL_PADS)
|
||||
VM_ERROR("call: stack underflow (missing pads)");
|
||||
|
||||
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;
|
||||
}
|
||||
saved_sp = vm->sp - nargs - SPL_CALL_PADS;
|
||||
|
||||
spl_callframe_t *fr = &vm->frames.data[vm->cp];
|
||||
fr->saved_sp = saved_sp;
|
||||
@@ -736,6 +1006,33 @@ static inline int spl_vm_call(spl_vm_t *vm, spl_vm_val_t addr, spl_vm_val_t narg
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 入口调用:按名字调用顶层函数(同 CALL 的 ABI,实参+2 pads 已由调用方压好)。 */
|
||||
int spl_vm_call_entry(spl_vm_t *vm, const char *name, int nargs) {
|
||||
spl_vm_func_t *func;
|
||||
if (!vm || !vm->prog || !name)
|
||||
return -1;
|
||||
func = spl_prog_get_func(vm->prog, name);
|
||||
if (!func)
|
||||
return -1;
|
||||
if (vm->cp >= vm->config.max_call_depth)
|
||||
return -1;
|
||||
if (vm->sp < (usize)nargs + SPL_CALL_PADS)
|
||||
return -1;
|
||||
vm->ip = (usize)-1;
|
||||
vm->cp = 0;
|
||||
vm->fp = 0;
|
||||
vm->frames.data[0].saved_sp = vm->sp - (usize)nargs - SPL_CALL_PADS;
|
||||
vm->frames.data[0].saved_fp = vm->fp;
|
||||
vm->frames.data[0].saved_ip = vm->ip;
|
||||
vm->frames.data[0].nargs = (spl_vm_val_t)nargs;
|
||||
vm->cp = 1;
|
||||
vm->ip = (usize)func->address;
|
||||
vm->fp = vm->sp - (usize)nargs;
|
||||
if (vm->debug && vm->fp > 0)
|
||||
STACK_CANARY(vm) = SPL_STACK_CANARY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spl_vm_prepare(spl_vm_t *vm, const char *entry, int argc, const char **argv,
|
||||
const char **envp) {
|
||||
if (!vm || !vm->prog) {
|
||||
@@ -757,15 +1054,22 @@ int spl_vm_prepare(spl_vm_t *vm, const char *entry, int argc, const char **argv,
|
||||
vm->cp = 0;
|
||||
vm->ip = -1;
|
||||
vm->exit_code = 0;
|
||||
vm->ret_val = 0;
|
||||
|
||||
/* ABI:入口也在实参下方预留 2 个空闲 slot。 */
|
||||
PUSH(0);
|
||||
PUSH(0);
|
||||
if (fn->nargs >= 1) {
|
||||
PUSH(argc);
|
||||
}
|
||||
if (fn->nargs >= 2) {
|
||||
PUSH(argv);
|
||||
PUSH(spl_vm_marshal_argv(vm, argc, argv));
|
||||
}
|
||||
if (fn->nargs >= 3) {
|
||||
PUSH(envp);
|
||||
int envc = 0;
|
||||
while (envp && envp[envc])
|
||||
envc++;
|
||||
PUSH(spl_vm_marshal_argv(vm, envc, envp));
|
||||
}
|
||||
if (fn->nargs >= 4) {
|
||||
printf("the start symbol can't using more than 3 args");
|
||||
@@ -777,6 +1081,53 @@ int spl_vm_prepare(spl_vm_t *vm, const char *entry, int argc, const char **argv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 外部 -> VM 的调用:保存外层状态,按当前深度建帧跑到边界,读结果后恢复。
|
||||
* 可在 VM 运行中(如外部回调内)安全调用。 */
|
||||
int spl_vm_invoke(spl_vm_t *vm, spl_vm_call_t *call) {
|
||||
spl_vm_func_t *fn;
|
||||
uintptr_t s_ip, s_fp, s_sp, s_cp;
|
||||
int s_exit, i, r;
|
||||
spl_vm_val_t s_ret;
|
||||
if (!vm || !vm->prog || !call || !call->name)
|
||||
return -1;
|
||||
fn = spl_prog_get_func(vm->prog, call->name);
|
||||
if (!fn)
|
||||
return -1;
|
||||
|
||||
s_ip = vm->ip;
|
||||
s_fp = vm->fp;
|
||||
s_sp = vm->sp;
|
||||
s_cp = vm->cp;
|
||||
s_exit = vm->exit_code;
|
||||
s_ret = vm->ret_val;
|
||||
|
||||
PUSH(0);
|
||||
PUSH(0);
|
||||
for (i = 0; i < (int)call->nargs; i++)
|
||||
PUSH(call->args ? call->args[i] : 0);
|
||||
/* sentinel:本次调用的 RET 视为边界(与入口调用一致)。 */
|
||||
vm->ip = (spl_vm_val_t)-1;
|
||||
if (spl_vm_call(vm, fn->address, (spl_vm_val_t)call->nargs) != 0) {
|
||||
vm->ip = s_ip;
|
||||
vm->fp = s_fp;
|
||||
vm->sp = s_sp;
|
||||
vm->cp = s_cp;
|
||||
vm->exit_code = s_exit;
|
||||
vm->ret_val = s_ret;
|
||||
return -1;
|
||||
}
|
||||
r = spl_vm_run_until(vm, 0);
|
||||
call->ret = vm->ret_val;
|
||||
|
||||
vm->ip = s_ip;
|
||||
vm->fp = s_fp;
|
||||
vm->sp = s_sp;
|
||||
vm->cp = s_cp;
|
||||
vm->exit_code = s_exit;
|
||||
vm->ret_val = s_ret;
|
||||
return r < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* spl_vm_run_once - execute one instruction
|
||||
*
|
||||
@@ -817,7 +1168,7 @@ static uint64_t prof_now_ns(void) {
|
||||
}
|
||||
|
||||
static void spl_vm_sample(spl_vm_t *vm) {
|
||||
spl_prof_t *p = (spl_prof_t *)vm->profile;
|
||||
spl_prof_t *p = vm->profile;
|
||||
spl_prog_t *prog = vm->prog;
|
||||
if (!p || !prog)
|
||||
return;
|
||||
@@ -862,7 +1213,7 @@ static void spl_vm_sample(spl_vm_t *vm) {
|
||||
|
||||
usize old = 0;
|
||||
if (map_get(p->stacks, buf, &old)) {
|
||||
map_put(p->stacks, buf, old + 1);
|
||||
map_put(p->stacks, strdup(buf), old + 1);
|
||||
} else {
|
||||
map_put(p->stacks, strdup(buf), (usize)1);
|
||||
}
|
||||
@@ -1250,6 +1601,8 @@ L_SPL_RET:
|
||||
intptr_t _saved_ip = vm->frames.data[vm->cp].saved_ip;
|
||||
/* entry return -> halt */
|
||||
if (_saved_ip < 0) {
|
||||
PUSH(_retval);
|
||||
vm->ret_val = _retval;
|
||||
vm->exit_code = (int)_retval;
|
||||
return 1;
|
||||
}
|
||||
@@ -1263,6 +1616,7 @@ L_SPL_RET:
|
||||
}
|
||||
|
||||
L_SPL_HALT:
|
||||
vm->ret_val = ins->imm;
|
||||
if (ins->imm)
|
||||
vm->exit_code = (int)ins->imm;
|
||||
return 1;
|
||||
@@ -1281,7 +1635,8 @@ L_SPL_ALLOC:
|
||||
}
|
||||
|
||||
L_SPL_LADDR:
|
||||
PUSH((spl_vm_val_t)((char *)(vm->stacks.data + vm->fp) + ins->imm));
|
||||
PUSH(SPL_ASP_OFF(SPL_ASP_HANDLE_STACK,
|
||||
(spl_vm_val_t)vm->fp * sizeof(spl_vm_val_t) + ins->imm));
|
||||
DISPATCH();
|
||||
|
||||
L_SPL_GADDR:
|
||||
@@ -1289,20 +1644,22 @@ 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");
|
||||
spl_vm_gdata_t *gd = &vec_at(prog->gdata, _idx);
|
||||
if (!gd->data)
|
||||
VM_ERROR("GADDR: null gdata entry");
|
||||
PUSH((spl_vm_val_t)(uintptr_t)gd->data);
|
||||
if (_idx >= vec_size(vm->gdata_off))
|
||||
VM_ERROR("GADDR: global data not mapped");
|
||||
PUSH((spl_vm_val_t)vec_at(vm->gdata_off, _idx));
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
/* ========== Indirect Memory (load/store with types) ========== */
|
||||
L_SPL_LOAD:
|
||||
{
|
||||
void *_addr = (void *)POP();
|
||||
CHECK_ADDR(_addr, "LOAD");
|
||||
spl_vm_val_t _off = POP();
|
||||
void *_addr;
|
||||
spl_vm_val_t _v = 0;
|
||||
usize _sz = spl_type_size(ins->type);
|
||||
_addr = spl_vm_xlate(vm, _off, _sz);
|
||||
if (!_addr)
|
||||
VM_ERROR("LOAD: invalid address");
|
||||
memcpy(&_v, _addr, _sz);
|
||||
/* Sign-extend signed integer types smaller than 64 bits */
|
||||
if (_sz > 0 && _sz < sizeof(spl_vm_val_t) && spl_is_signed(ins->type)) {
|
||||
@@ -1315,8 +1672,10 @@ L_SPL_LOAD:
|
||||
L_SPL_STORE:
|
||||
{
|
||||
spl_vm_val_t _v = POP();
|
||||
void *_addr = (void *)POP();
|
||||
CHECK_ADDR(_addr, "STORE");
|
||||
spl_vm_val_t _off = POP();
|
||||
void *_addr = spl_vm_xlate(vm, _off, spl_type_size(ins->type));
|
||||
if (!_addr)
|
||||
VM_ERROR("STORE: invalid address");
|
||||
memcpy(_addr, &_v, spl_type_size(ins->type));
|
||||
DISPATCH();
|
||||
}
|
||||
@@ -1374,13 +1733,23 @@ L_SPL_NCALL:
|
||||
if (_nat_idx >= vec_size(prog->natives))
|
||||
VM_ERROR("NCALL: native index out of range");
|
||||
_nat = &vec_at(prog->natives, _nat_idx);
|
||||
if (!_nat->impl_fn) {
|
||||
spl_vm_val_t *_arg_base = vm->stacks.data + vm->sp - _nargs;
|
||||
spl_vm_val_t _result;
|
||||
if (_nat->impl_fn) {
|
||||
_result = _nat->impl_fn(vm, _nargs, _arg_base);
|
||||
} else if (vm->foreign) {
|
||||
/* 线 1 动态:交给按名的外部函数处理器。 */
|
||||
spl_vm_call_t _call;
|
||||
_call.name = _nat->name ? _nat->name : "";
|
||||
_call.args = _arg_base;
|
||||
_call.nargs = (usize)_nargs;
|
||||
_call.ret = 0;
|
||||
_result = vm->foreign(vm, &_call, vm->foreign_ctx);
|
||||
} else {
|
||||
snprintf(vm->error_msg, sizeof(vm->error_msg),
|
||||
"NCALL: NULL native function pointer expect %s", _nat->name);
|
||||
VM_ERROR(vm->error_msg);
|
||||
}
|
||||
spl_vm_val_t *_arg_base = vm->stacks.data + vm->sp - _nargs;
|
||||
spl_vm_val_t _result = _nat->impl_fn(vm, _nargs, _arg_base);
|
||||
if (vm->exit_code) {
|
||||
snprintf(vm->error_msg, sizeof(vm->error_msg), "NCALL %s failed (ip=%zd)",
|
||||
_nat->name ? _nat->name : "?", vm->ip - 1);
|
||||
@@ -1442,7 +1811,11 @@ L_default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int spl_vm_run_once(spl_vm_t *vm) { return vm_loop(vm, 1); }
|
||||
int spl_vm_run_once(spl_vm_t *vm) {
|
||||
if (!vm)
|
||||
return -1;
|
||||
return vm_loop(vm, 1);
|
||||
}
|
||||
|
||||
int spl_vm_run_until(spl_vm_t *vm, size_t step) {
|
||||
if (!vm)
|
||||
|
||||
@@ -5,11 +5,25 @@
|
||||
|
||||
#include "include/core_map.h"
|
||||
#include "include/core_vec.h"
|
||||
#include "spl_asp.h"
|
||||
#include "spl_mcode.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define SPL_STACK_CANARY ((spl_vm_val_t)0xDEADBEEFCAFEBABEull)
|
||||
|
||||
/* 统一偏移地址空间:SPL 侧指针一律是句柄/偏移,0 保留为 null。
|
||||
* offset = (handle<<32) | 区内字节偏移;handle 1 = 栈,handle>=2 = 堆分配。 */
|
||||
#define SPL_OFF_NULL ((spl_vm_val_t)0)
|
||||
#define SPL_ASP_HANDLE_STACK ((spl_vm_val_t)1)
|
||||
#define SPL_ASP_OFF(handle, byte) ((((spl_vm_val_t)(handle)) << 32) | (spl_vm_val_t)(byte))
|
||||
|
||||
typedef struct {
|
||||
unsigned char *base;
|
||||
usize size;
|
||||
int used;
|
||||
} spl_vm_region_t;
|
||||
typedef VEC(spl_vm_region_t) spl_vm_region_vec_t;
|
||||
|
||||
typedef struct {
|
||||
uintptr_t saved_sp;
|
||||
uintptr_t saved_fp;
|
||||
@@ -20,6 +34,18 @@ typedef struct {
|
||||
typedef VEC(spl_vm_val_t) spl_stack_vec_t;
|
||||
typedef VEC(spl_callframe_t) spl_frame_vec_t;
|
||||
|
||||
/* 一次调用描述(VM <-> 外部两侧统一形状)。 */
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const spl_vm_val_t *args;
|
||||
usize nargs;
|
||||
spl_vm_val_t ret;
|
||||
} spl_vm_call_t;
|
||||
|
||||
/* 线 1 动态形态:VM 调“外部(非 VM)函数”的按名处理器。
|
||||
* 当 NCALL 的 native 无 impl_fn 时调用;返回被调函数的返回值。 */
|
||||
typedef spl_vm_val_t (*spl_vm_foreign_fn)(spl_vm_t *vm, const spl_vm_call_t *call, void *ctx);
|
||||
|
||||
typedef struct {
|
||||
MAP(const char *, usize) stacks;
|
||||
VEC(usize) self_samples;
|
||||
@@ -49,6 +75,14 @@ typedef struct spl_vm {
|
||||
VEC(usize) breakpoints; /* ip 断点(执行到该指令前暂停,run_once 返回 2) */
|
||||
VEC(char *) fn_breakpoints; /* 函数名断点(CALL/CALLI 目标函数入口暂停) */
|
||||
spl_prog_t *prog;
|
||||
/* 统一偏移地址空间:regions[1]=栈,regions[i>=2]=堆分配。 */
|
||||
spl_vm_region_vec_t regions;
|
||||
VEC(usize) gdata_off; /* prog->gdata[i] 在本 VM 空间内的偏移 */
|
||||
spl_asp_t asp; /* 本 VM 的地址空间描述符(按值持有,避免共享静态) */
|
||||
spl_vm_val_t stdio_off[3]; /* stdin/stdout/stderr 的 VM 句柄(懒建) */
|
||||
spl_vm_val_t ret_val; /* 最近一次完成的调用返回值(64 位) */
|
||||
spl_vm_foreign_fn foreign; /* 线 1 动态处理器(可为 NULL) */
|
||||
void *foreign_ctx;
|
||||
char error_msg[1024];
|
||||
struct {
|
||||
uintptr_t max_stack_depth;
|
||||
@@ -68,6 +102,35 @@ void spl_vm_drop(spl_vm_t *vm);
|
||||
|
||||
int spl_vm_addr_valid(uintptr_t p, usize size);
|
||||
|
||||
/* 偏移地址空间:偏移 -> 宿主地址;越界/ null 返回 NULL。 */
|
||||
void *spl_vm_xlate(spl_vm_t *vm, spl_vm_val_t off, usize size);
|
||||
/* 宿主地址 -> 偏移;不属于本 VM 地址空间返回 0。 */
|
||||
spl_vm_val_t spl_vm_to_off(spl_vm_t *vm, const void *ptr);
|
||||
/* 本 VM 的地址空间描述符(供 runtime 解引用)。 */
|
||||
spl_asp_t *spl_vm_asp(spl_vm_t *vm);
|
||||
|
||||
/* ---- 线 1:VM -> 外部函数 ---- */
|
||||
/* 安装“外部函数”按名处理器(NCALL 的 native 无 impl_fn 时使用)。 */
|
||||
void spl_vm_set_foreign(spl_vm_t *vm, spl_vm_foreign_fn fn, void *ctx);
|
||||
|
||||
/* ---- 线 2:外部 -> VM 函数 ---- */
|
||||
/* 按 call->name 调用 VM 内函数(call->args/nargs 为实参),结果写入 call->ret。
|
||||
* 可在 VM 运行中重入调用。返回 0 成功,-1 失败。 */
|
||||
int spl_vm_invoke(spl_vm_t *vm, spl_vm_call_t *call);
|
||||
|
||||
/* 入口调用:调用方先按 ABI 压 2 个 pad 再压实参。 */
|
||||
int spl_vm_call_entry(spl_vm_t *vm, const char *name, int nargs);
|
||||
|
||||
/* ---- 栈 / 结果 / 内存 访问(不暴露宿主指针) ---- */
|
||||
spl_vm_val_t spl_vm_ret_val(spl_vm_t *vm);
|
||||
usize spl_vm_sp(spl_vm_t *vm);
|
||||
usize spl_vm_fp(spl_vm_t *vm);
|
||||
spl_vm_val_t spl_vm_stack_get(spl_vm_t *vm, usize index);
|
||||
int spl_vm_stack_set(spl_vm_t *vm, usize index, spl_vm_val_t val);
|
||||
spl_vm_val_t spl_vm_mem_alloc(spl_vm_t *vm, usize size);
|
||||
int spl_vm_mem_read(spl_vm_t *vm, spl_vm_val_t off, void *dst, usize n);
|
||||
int spl_vm_mem_write(spl_vm_t *vm, spl_vm_val_t off, const void *src, usize n);
|
||||
|
||||
int spl_vm_load_prog(spl_vm_t *vm, spl_prog_t *prog);
|
||||
int spl_vm_prepare(spl_vm_t *vm, const char *entry, int argc, const char **argv, const char **envp);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* spl_vm_embed.h - 原生编译器内嵌 VM(comptime 执行)。
|
||||
/* spl_vm_embed.h - 裸机内嵌 VM(装载/运行,不含任何内置 native/syscall)。
|
||||
*
|
||||
* 单 TU 合并式:定义 SPL_VM_EMBED_IMPLIMENTATION 后 include 本头,会把 stage0 的
|
||||
* spl_mcode.c / spl_syscall.c / spl_vm.c 一并纳入当前翻译单元(不单独编译/链接)。
|
||||
* spl_mcode.c / spl_vm.c 一并纳入当前翻译单元(不单独编译/链接)。
|
||||
*/
|
||||
|
||||
#ifndef __SPL_VM_EMBED_H__
|
||||
@@ -13,14 +13,17 @@ int vm_load_from_buffer(void *vm, const void *buf, unsigned long long len);
|
||||
void vm_push(void *vm, unsigned long long val);
|
||||
int vm_call(void *vm, const char *name, unsigned long long nargs);
|
||||
int vm_run(void *vm);
|
||||
unsigned long long vm_ret_val(void *vm);
|
||||
unsigned long long vm_mem_alloc(void *vm, unsigned long long size);
|
||||
int vm_mem_read(void *vm, unsigned long long off, void *dst, unsigned long long n);
|
||||
int vm_mem_write(void *vm, unsigned long long off, const void *src, unsigned long long n);
|
||||
|
||||
#ifdef SPL_VM_EMBED_IMPLIMENTATION
|
||||
|
||||
#include "../../stage0/spl_mcode.h"
|
||||
void spl_syscall_register(spl_prog_t *prog) { (void)prog; /* stub */ }
|
||||
|
||||
#include "../../stage0/spl_mcode.c"
|
||||
#include "../../stage0/spl_vm.c"
|
||||
/* 裸机内嵌 VM:只提供装载/运行 VM 的能力,不内置任何 native/syscall。
|
||||
* 外部函数解析与注册属于配置端(调用方自行处理)。 */
|
||||
#include "spl_mcode.c"
|
||||
#include "spl_vm.c"
|
||||
|
||||
void *vm_new(void) {
|
||||
spl_vm_t *vm = (spl_vm_t *)malloc(sizeof(spl_vm_t));
|
||||
@@ -55,7 +58,6 @@ int vm_load_from_buffer(void *p, const void *buf, unsigned long long len) {
|
||||
free(prog);
|
||||
return -1;
|
||||
}
|
||||
spl_syscall_register(prog);
|
||||
if (spl_vm_load_prog(vm, prog) != 0) {
|
||||
spl_prog_drop(prog);
|
||||
free(prog);
|
||||
@@ -75,26 +77,10 @@ void vm_push(void *p, unsigned long long val) {
|
||||
|
||||
int vm_call(void *p, const char *name, unsigned long long nargs) {
|
||||
spl_vm_t *vm = (spl_vm_t *)p;
|
||||
spl_vm_func_t *func;
|
||||
if (!vm || !name)
|
||||
return -1;
|
||||
func = spl_prog_get_func(vm->prog, name);
|
||||
if (!func)
|
||||
return -1;
|
||||
if (vm->cp >= vm->config.max_call_depth)
|
||||
return -1;
|
||||
/* 入口调用:ip=-1 作 sentinel;实参由 vm_push 压栈。 */
|
||||
vm->ip = (usize)-1;
|
||||
vm->cp = 0;
|
||||
vm->fp = 0;
|
||||
vm->frames.data[0].saved_sp = vm->sp - (usize)nargs;
|
||||
vm->frames.data[0].saved_fp = vm->fp;
|
||||
vm->frames.data[0].saved_ip = vm->ip;
|
||||
vm->frames.data[0].nargs = (spl_vm_val_t)nargs;
|
||||
vm->cp = 1;
|
||||
vm->ip = (usize)func->address;
|
||||
vm->fp = vm->sp - (usize)nargs;
|
||||
return 0;
|
||||
/* 入口调用:调用方需先按 ABI 压 2 个 pad 再压实参。 */
|
||||
return spl_vm_call_entry(vm, name, (int)nargs);
|
||||
}
|
||||
|
||||
int vm_run(void *p) {
|
||||
@@ -104,6 +90,27 @@ int vm_run(void *p) {
|
||||
spl_vm_run_until(vm, 0);
|
||||
return (int)vm->exit_code;
|
||||
}
|
||||
|
||||
unsigned long long vm_ret_val(void *p) {
|
||||
spl_vm_t *vm = (spl_vm_t *)p;
|
||||
return vm ? (unsigned long long)vm->ret_val : 0;
|
||||
}
|
||||
|
||||
unsigned long long vm_mem_alloc(void *p, unsigned long long size) {
|
||||
spl_vm_t *vm = (spl_vm_t *)p;
|
||||
return (unsigned long long)spl_vm_mem_alloc(vm, (usize)size);
|
||||
}
|
||||
|
||||
int vm_mem_read(void *p, unsigned long long off, void *dst, unsigned long long n) {
|
||||
spl_vm_t *vm = (spl_vm_t *)p;
|
||||
return spl_vm_mem_read(vm, (spl_vm_val_t)off, dst, (usize)n);
|
||||
}
|
||||
|
||||
int vm_mem_write(void *p, unsigned long long off, const void *src, unsigned long long n) {
|
||||
spl_vm_t *vm = (spl_vm_t *)p;
|
||||
return spl_vm_mem_write(vm, (spl_vm_val_t)off, src, (usize)n);
|
||||
}
|
||||
|
||||
#endif /* SPL_VM_EMBED_IMPLIMENTATION */
|
||||
|
||||
#endif /* __SPL_VM_EMBED_H__ */
|
||||
+140
-10
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "include/acutest.h"
|
||||
#include "spl_mcode.h"
|
||||
#include "spl_syscall.h"
|
||||
#include "spl_vm.h"
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -159,7 +160,8 @@ static int run_with_natives(const spl_vm_ins_t *instns, int ninsns, spl_vm_nativ
|
||||
* Native function for NCALL tests
|
||||
* ================================================================ */
|
||||
|
||||
static spl_vm_val_t native_add_impl(int nargs, spl_vm_val_t *args) {
|
||||
static spl_vm_val_t native_add_impl(spl_vm_t *vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)vm;
|
||||
return (nargs >= 2) ? args[0] + args[1] : 0;
|
||||
}
|
||||
|
||||
@@ -479,7 +481,8 @@ void test_call_add(void) {
|
||||
spl_vm_ins_t add_insns[] = {ins(SPL_LADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_I64, 0),
|
||||
ins(SPL_LADDR, SPL_VOID, 8), ins(SPL_LOAD, SPL_I64, 0),
|
||||
ins(SPL_ADD, SPL_I64, 0), ins(SPL_RET, SPL_I64, 0)};
|
||||
spl_vm_ins_t main_insns[] = {ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 20),
|
||||
spl_vm_ins_t main_insns[] = {ins(SPL_PUSH, SPL_VOID, 0), ins(SPL_PUSH, SPL_VOID, 0),
|
||||
ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 20),
|
||||
ins(SPL_PUSH, SPL_VOID, 0), ins(SPL_CALL, SPL_VOID, 2),
|
||||
ins(SPL_RET, SPL_I64, 0)};
|
||||
|
||||
@@ -586,11 +589,12 @@ void test_calli(void) {
|
||||
spl_vm_ins_t add_insns[] = {ins(SPL_LADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_I64, 0),
|
||||
ins(SPL_LADDR, SPL_VOID, 8), ins(SPL_LOAD, SPL_I64, 0),
|
||||
ins(SPL_ADD, SPL_I64, 0), ins(SPL_RET, SPL_I64, 0)};
|
||||
spl_vm_ins_t main_insns[] = {ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 20),
|
||||
spl_vm_ins_t main_insns[] = {ins(SPL_PUSH, SPL_VOID, 0), ins(SPL_PUSH, SPL_VOID, 0),
|
||||
ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 20),
|
||||
ins(SPL_PUSH, SPL_VOID, 2), ins(SPL_PUSH, SPL_VOID, 0),
|
||||
ins(SPL_CALLI, SPL_VOID, 0), ins(SPL_RET, SPL_I64, 0)};
|
||||
|
||||
int ninsns_t = 6 + 6;
|
||||
int ninsns_t = 6 + 8;
|
||||
size_t nlen0 = strlen("add") + 1;
|
||||
size_t npad0 = ((nlen0 + 7) / 8) * 8 - nlen0;
|
||||
size_t nlen1 = strlen("main") + 1;
|
||||
@@ -627,7 +631,7 @@ void test_calli(void) {
|
||||
p += npad1;
|
||||
LE64(p, 0);
|
||||
LE64(p, 0);
|
||||
LE64(p, 6);
|
||||
LE64(p, 8);
|
||||
LE64(p, 6);
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
@@ -637,7 +641,7 @@ void test_calli(void) {
|
||||
*p++ = (unsigned char)(add_insns[i].type >> 8);
|
||||
LE64(p, add_insns[i].imm);
|
||||
}
|
||||
for (int i = 0; i < 6; i++) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
*p++ = (unsigned char)(main_insns[i].opcode);
|
||||
*p++ = (unsigned char)(main_insns[i].opcode >> 8);
|
||||
*p++ = (unsigned char)(main_insns[i].type);
|
||||
@@ -669,8 +673,9 @@ void test_calli(void) {
|
||||
* ================================================================ */
|
||||
|
||||
void test_gaddr_ld32(void) {
|
||||
/* Load program, add global data, GADDR + LD32 to read it back */
|
||||
spl_vm_ins_t p[] = {ins(SPL_GADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_U32, 0),
|
||||
/* Load program, add global data, GADDR + LD32 to read it back.
|
||||
* gdata 索引 0 为哨兵;首次 add_data 得到索引 1。 */
|
||||
spl_vm_ins_t p[] = {ins(SPL_GADDR, SPL_VOID, 1), ins(SPL_LOAD, SPL_U32, 0),
|
||||
ins(SPL_RET, SPL_U32, 0)};
|
||||
size_t len;
|
||||
unsigned char *bin = build_binary("main", 0, p, 3, &len);
|
||||
@@ -697,8 +702,8 @@ void test_gaddr_ld32(void) {
|
||||
}
|
||||
|
||||
void test_gaddr_multi(void) {
|
||||
/* Two global data entries: read second one via GADDR 1 */
|
||||
spl_vm_ins_t p[] = {ins(SPL_GADDR, SPL_VOID, 1), ins(SPL_LOAD, SPL_U32, 0),
|
||||
/* Two global data entries: read second one via GADDR 2 (0 为哨兵) */
|
||||
spl_vm_ins_t p[] = {ins(SPL_GADDR, SPL_VOID, 2), ins(SPL_LOAD, SPL_U32, 0),
|
||||
ins(SPL_RET, SPL_U32, 0)};
|
||||
size_t len;
|
||||
unsigned char *bin = build_binary("main", 0, p, 3, &len);
|
||||
@@ -818,6 +823,127 @@ void test_empty(void) {
|
||||
TEST_CHECK(spl_prog_load_from_file("nonexistent_file_xyz.bin", NULL) != 0);
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* 动态回调 / 重入 / 栈访问 API
|
||||
* ================================================================ */
|
||||
|
||||
static spl_vm_val_t foreign_add_cb(spl_vm_t *vm, const spl_vm_call_t *call, void *ctx) {
|
||||
(void)vm;
|
||||
(void)ctx;
|
||||
if (!call || call->nargs < 2)
|
||||
return 0;
|
||||
return call->args[0] + call->args[1];
|
||||
}
|
||||
|
||||
/* 线 1 动态:NCALL 的 native 无 impl_fn 时走 vm->foreign。 */
|
||||
void test_foreign_fallback(void) {
|
||||
spl_vm_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 30), ins(SPL_PUSH, SPL_I32, 12),
|
||||
ins(SPL_PUSH, SPL_VOID, 0), ins(SPL_NCALL, SPL_I32, 2),
|
||||
ins(SPL_RET, SPL_I32, 0)};
|
||||
size_t len;
|
||||
unsigned char *bin = build_binary("main", 0, p, 5, &len);
|
||||
spl_prog_t prog;
|
||||
spl_vm_t vm;
|
||||
spl_vm_native_t nat[] = {{"host_add", 0, NULL}};
|
||||
int rc = -1;
|
||||
|
||||
spl_vm_init(&vm);
|
||||
if (write_temp(bin, len) == 0 && spl_prog_load_from_file(TMPFILE, &prog) == 0) {
|
||||
for (int i = 0; i < 1; i++)
|
||||
spl_prog_add_native(&prog, &nat[i]);
|
||||
if (spl_vm_load_prog(&vm, &prog) == 0) {
|
||||
spl_vm_set_foreign(&vm, foreign_add_cb, NULL);
|
||||
if (spl_vm_prepare(&vm, "main", 0, NULL, NULL) == 0) {
|
||||
int ret = spl_vm_run_until(&vm, 0);
|
||||
if (ret == 1)
|
||||
rc = (int)vm.ret_val;
|
||||
}
|
||||
}
|
||||
}
|
||||
spl_vm_drop(&vm);
|
||||
remove(TMPFILE);
|
||||
free(bin);
|
||||
TEST_CHECK(rc == 42);
|
||||
}
|
||||
|
||||
/* 线 2:外部 -> VM 调用。 */
|
||||
void test_invoke(void) {
|
||||
spl_vm_ins_t add_insns[] = {ins(SPL_LADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_I64, 0),
|
||||
ins(SPL_LADDR, SPL_VOID, 8), ins(SPL_LOAD, SPL_I64, 0),
|
||||
ins(SPL_ADD, SPL_I64, 0), ins(SPL_RET, SPL_I64, 0)};
|
||||
size_t len;
|
||||
unsigned char *bin = build_binary("add", 2, add_insns, 6, &len);
|
||||
spl_prog_t prog;
|
||||
spl_vm_t vm;
|
||||
spl_vm_val_t args[2];
|
||||
spl_vm_call_t call;
|
||||
int ok = 0;
|
||||
|
||||
spl_vm_init(&vm);
|
||||
if (write_temp(bin, len) == 0 && spl_prog_load_from_file(TMPFILE, &prog) == 0) {
|
||||
if (spl_vm_load_prog(&vm, &prog) == 0) {
|
||||
args[0] = 40;
|
||||
args[1] = 2;
|
||||
call.name = "add";
|
||||
call.args = args;
|
||||
call.nargs = 2;
|
||||
call.ret = 0;
|
||||
ok = (spl_vm_invoke(&vm, &call) == 0) && (call.ret == 42);
|
||||
}
|
||||
}
|
||||
spl_vm_drop(&vm);
|
||||
remove(TMPFILE);
|
||||
free(bin);
|
||||
TEST_CHECK(ok);
|
||||
}
|
||||
|
||||
/* 栈 / 结果访问 API 不依赖宿主指针。 */
|
||||
void test_stack_api(void) {
|
||||
spl_vm_t vm;
|
||||
spl_vm_init(&vm);
|
||||
TEST_CHECK(spl_vm_stack_set(&vm, 0, 123) == 0);
|
||||
TEST_CHECK(spl_vm_stack_get(&vm, 0) == 123);
|
||||
TEST_CHECK(spl_vm_sp(&vm) == 0);
|
||||
TEST_CHECK(spl_vm_fp(&vm) == 0);
|
||||
TEST_CHECK(spl_vm_stack_get(&vm, (usize)-1) == 0);
|
||||
spl_vm_drop(&vm);
|
||||
}
|
||||
|
||||
/* syscall 注册 + 内存 API:NCALL vm_alloc 得到 VM 偏移,并经 mem_read/write 往返。 */
|
||||
void test_syscall_registration(void) {
|
||||
spl_vm_ins_t p[] = {ins(SPL_PUSH, SPL_I64, 16), ins(SPL_PUSH, SPL_VOID, 0),
|
||||
ins(SPL_NCALL, SPL_I64, 1), ins(SPL_RET, SPL_I64, 0)};
|
||||
size_t len;
|
||||
unsigned char *bin = build_binary("main", 0, p, 4, &len);
|
||||
spl_prog_t prog;
|
||||
spl_vm_t vm;
|
||||
spl_vm_native_t nat[] = {{"vm_alloc", 0, NULL}};
|
||||
spl_vm_val_t off = 0;
|
||||
int ok = 0;
|
||||
char w[4] = {1, 2, 3, 0};
|
||||
char r[4] = {0};
|
||||
|
||||
spl_vm_init(&vm);
|
||||
if (write_temp(bin, len) == 0 && spl_prog_load_from_file(TMPFILE, &prog) == 0) {
|
||||
for (int i = 0; i < 1; i++)
|
||||
spl_prog_add_native(&prog, &nat[i]);
|
||||
spl_syscall_register(&prog);
|
||||
if (spl_vm_load_prog(&vm, &prog) == 0 && spl_vm_prepare(&vm, "main", 0, NULL, NULL) == 0) {
|
||||
if (spl_vm_run_until(&vm, 0) == 1) {
|
||||
off = vm.ret_val;
|
||||
if (off != 0 && spl_vm_mem_write(&vm, off, w, 4) == 0 &&
|
||||
spl_vm_mem_read(&vm, off, r, 4) == 0 && memcmp(w, r, 4) == 0)
|
||||
ok = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
spl_vm_drop(&vm);
|
||||
remove(TMPFILE);
|
||||
free(bin);
|
||||
TEST_CHECK(off != 0);
|
||||
TEST_CHECK(ok);
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Test list
|
||||
* ================================================================ */
|
||||
@@ -872,6 +998,10 @@ TEST_LIST = {
|
||||
{"ld_st64", test_ld_st64},
|
||||
{"many_ops", test_many_ops},
|
||||
{"halt", test_halt},
|
||||
{"foreign_fallback", test_foreign_fallback},
|
||||
{"invoke", test_invoke},
|
||||
{"stack_api", test_stack_api},
|
||||
{"syscall_registration", test_syscall_registration},
|
||||
{"bad_magic", test_bad_magic},
|
||||
{"empty", test_empty},
|
||||
{NULL, NULL},
|
||||
|
||||
@@ -1086,6 +1086,10 @@ static void cg_control(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
||||
usize sret_off = 0;
|
||||
usize j = 0;
|
||||
|
||||
/* ABI:实参下方预留 2 个空闲 slot(PAD0/PAD1)。 */
|
||||
CG_PUSH(cg, SPL_U64, 0);
|
||||
CG_PUSH(cg, SPL_U64, 0);
|
||||
|
||||
if (ret_is_sret) {
|
||||
sret_off = vec_at(cg->frame.slots, ref).data_off;
|
||||
CG_LADDR(cg, sret_off);
|
||||
|
||||
@@ -50,6 +50,7 @@ type Body = enum {
|
||||
StatementBreak: struct { label: usize, expr: usize },
|
||||
StatementContinue: struct { label: usize },
|
||||
StatementDefer: struct { body: usize },
|
||||
StatementErrdefer: struct { body: usize }, // errdefer:仅错误退出时执行
|
||||
StatementComptime: struct { body: usize },
|
||||
StatementDecl: struct { decl: usize },
|
||||
StatementExpr: struct { expr: usize },
|
||||
@@ -87,6 +88,8 @@ type Body = enum {
|
||||
},
|
||||
ExprAggregateLit: struct { name: ByteView, fields: Vec },
|
||||
ExprComptimeCall: struct { name: ByteView, args: Vec },
|
||||
ExprTry: struct { value: usize }, // `try expr`
|
||||
ExprCatch: struct { value: usize, pattern: usize, handler: usize }, // `expr catch <pat> { }`
|
||||
ExprBlock: struct { stmts: Vec, tail_expr: usize },
|
||||
ExprIf: struct { cond: usize, then_block: usize, else_branch: usize },
|
||||
ExprIfvar: struct { var_decl: usize, then_block: usize, else_branch: usize },
|
||||
@@ -144,6 +147,7 @@ fn kind_name(body: Body): []u8 {
|
||||
.StatementBreak(_) => { ret "statement_break"; },
|
||||
.StatementContinue(_) => { ret "statement_continue"; },
|
||||
.StatementDefer(_) => { ret "statement_defer"; },
|
||||
.StatementErrdefer(_) => { ret "statement_errdefer"; },
|
||||
.StatementComptime(_) => { ret "statement_comptime"; },
|
||||
.ExprComptime(_) => { ret "expr_comptime"; },
|
||||
.StatementDecl(_) => { ret "statement_decl"; },
|
||||
@@ -171,6 +175,8 @@ fn kind_name(body: Body): []u8 {
|
||||
.ExprArrayLit(_) => { ret "array_literal"; },
|
||||
.ExprAggregateLit(_) => { ret "aggregate_literal"; },
|
||||
.ExprComptimeCall(_) => { ret "comptime_call"; },
|
||||
.ExprTry(_) => { ret "try_expr"; },
|
||||
.ExprCatch(_) => { ret "catch_expr"; },
|
||||
.ExprComptimeInit(_) => { ret "comptime_init"; },
|
||||
.ExprBlock(_) => { ret "block"; },
|
||||
.ExprIf(_) => { ret "if_expr"; },
|
||||
@@ -466,6 +472,7 @@ fn dump_node(ast: *Ast, buf: *ByteBuffer, stack: *Vec, ref: usize, last: bool):
|
||||
},
|
||||
.StatementContinue(x) => { ok = dump_opt(ast, buf, stack, x.label, true); },
|
||||
.StatementDefer(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
||||
.StatementErrdefer(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
||||
.StatementComptime(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
||||
.ExprComptime(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
||||
.StatementDecl(d) => { ok = dump_opt(ast, buf, stack, d.decl, true); },
|
||||
@@ -519,6 +526,12 @@ fn dump_node(ast: *Ast, buf: *ByteBuffer, stack: *Vec, ref: usize, last: bool):
|
||||
},
|
||||
.ExprAggregateLit(a) => { ok = dump_list(ast, buf, stack, a.fields); },
|
||||
.ExprComptimeCall(c) => { ok = dump_list(ast, buf, stack, c.args); },
|
||||
.ExprTry(t2) => { ok = dump_opt(ast, buf, stack, t2.value, true); },
|
||||
.ExprCatch(c2) => {
|
||||
ok = dump_opt(ast, buf, stack, c2.value, false) &&
|
||||
dump_opt(ast, buf, stack, c2.pattern, false) &&
|
||||
dump_opt(ast, buf, stack, c2.handler, true);
|
||||
},
|
||||
.ExprComptimeInit(x) => { ok = dump_list(ast, buf, stack, x.fields); },
|
||||
.ExprBlock(b) => {
|
||||
ok = dump_list(ast, buf, stack, b.stmts) &&
|
||||
|
||||
@@ -44,6 +44,7 @@ sema: *Sema,
|
||||
ir: *IrBuilder,
|
||||
ast: *Ast,
|
||||
def2ir_map: Map,
|
||||
glob_gidx: Map, // def_id -> gidx+1(全局变量)
|
||||
loop_stack: Vec, // LoopCtx
|
||||
defer_stack: Vec, // ast node ref
|
||||
cur_ret_lang: usize,
|
||||
@@ -105,6 +106,62 @@ fn ir_map_put(self: *Ast2Ir, def_id: usize, val: usize) {
|
||||
match Map.put(&self.def2ir_map, &def_id as *u8, &val as *u8) { .Ok => {}, .Err(e) => {} }
|
||||
}
|
||||
|
||||
fn glob_get(self: *Ast2Ir, def_id: usize, out: *usize): bool {
|
||||
match Map.get(&self.glob_gidx, &def_id as *u8, out as *u8) {
|
||||
.Found => { ret true; },
|
||||
.NotFound => { ret false; },
|
||||
}
|
||||
ret false;
|
||||
}
|
||||
|
||||
fn glob_put(self: *Ast2Ir, def_id: usize, val: usize) {
|
||||
match Map.put(&self.glob_gidx, &def_id as *u8, &val as *u8) { .Ok => {}, .Err(e) => {} }
|
||||
}
|
||||
|
||||
// 顶层 var/const → 模块全局(alloc 于静态存储;引用是 *T 指针常量)。
|
||||
fn emit_global_var(self: *Ast2Ir, d: *Node) {
|
||||
if d == null || d.resolved_def_id == 0 { ret; }
|
||||
var def_id = d.resolved_def_id;
|
||||
var existing: usize = 0;
|
||||
if glob_get(self, def_id, &existing) { ret; }
|
||||
var lang = LangType.def_lang_id(&self.sema.types, def_id);
|
||||
var ir = to_ir_type(self, lang);
|
||||
if ir == 0 { ret; }
|
||||
var init_ref: usize = 0;
|
||||
match d.body {
|
||||
.VarDecl(x) => { init_ref = x.init_expr; },
|
||||
.ConstDecl(x) => { init_ref = x.init_expr; },
|
||||
_ => {},
|
||||
}
|
||||
var zero = true;
|
||||
var bytes = Vec.blank(1, 1);
|
||||
if init_ref != 0 {
|
||||
var inode = Ast.node(self.ast, init_ref);
|
||||
if inode != null {
|
||||
match inode.body {
|
||||
.ExprIntLit(bi) => {
|
||||
var v: u64 = 0;
|
||||
match BigInt.to_u64(&v, bi.value) { .Ok => {}, .Err(e) => {} }
|
||||
var bits = LangType.int_bits(&self.sema.types, ir);
|
||||
var size = bits / 8;
|
||||
if size != 0 {
|
||||
zero = false;
|
||||
var i: usize = 0;
|
||||
while i < size {
|
||||
var b = ((v >> (8 * i)) & 255) as u8;
|
||||
match Vec.push(&bytes, &b as *u8) { .Ok => {}, .Err(e) => {} }
|
||||
i = i + 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
var gidx = IrBuilder.add_global(self.ir, ir, zero, bytes);
|
||||
glob_put(self, def_id, gidx + 1);
|
||||
}
|
||||
|
||||
fn kind_is(self: *Ast2Ir, lang_id: usize, want: LangKind): bool {
|
||||
var e = ltn(self, lang_id);
|
||||
if e == null { ret false; }
|
||||
@@ -608,6 +665,13 @@ fn emit_lvalue(self: *Ast2Ir, ref: usize): usize {
|
||||
.ExprIdent(x) => {
|
||||
var d = defn(self, n.resolved_def_id);
|
||||
if d != null && @enum2int(d.kind) == @enum2int(DefKind.Fn) { ret 0; }
|
||||
var g: usize = 0;
|
||||
if glob_get(self, n.resolved_def_id, &g) {
|
||||
var lang = node_lang(n);
|
||||
var ir = to_ir_type(self, lang);
|
||||
var ptr_ir = TypeTable.make_ptr(&self.sema.types.ir, ir);
|
||||
ret IrBuilder.type_const_global(self.ir, ptr_ir, g - 1);
|
||||
}
|
||||
var slot: usize = 0;
|
||||
if !ir_map_get(self, n.resolved_def_id, &slot) { ret 0; }
|
||||
ret slot;
|
||||
@@ -935,6 +999,13 @@ fn emit_ident(self: *Ast2Ir, n: *Node): usize {
|
||||
if !ir_map_get(self, n.resolved_def_id, &fn_ref) { ret 0; }
|
||||
ret IrBuilder.type_const_fn(self.ir, node_ir(self, n), fn_ref);
|
||||
}
|
||||
var g: usize = 0;
|
||||
if glob_get(self, n.resolved_def_id, &g) {
|
||||
var ir = node_ir(self, n);
|
||||
var ptr_ir = TypeTable.make_ptr(&self.sema.types.ir, ir);
|
||||
var ptr = IrBuilder.type_const_global(self.ir, ptr_ir, g - 1);
|
||||
ret IrBuilder.mem_load(self.ir, ir, ptr);
|
||||
}
|
||||
var slot: usize = 0;
|
||||
if !ir_map_get(self, n.resolved_def_id, &slot) { ret 0; }
|
||||
ret IrBuilder.mem_load(self.ir, node_ir(self, n), slot);
|
||||
@@ -1961,6 +2032,8 @@ fn emit_container_reserve(self: *Ast2Ir, ref: usize) {
|
||||
}
|
||||
},
|
||||
.TopLevelBlock(x) => { emit_container_reserve(self, dref); },
|
||||
.VarDecl(_) => { emit_global_var(self, d); },
|
||||
.ConstDecl(_) => { emit_global_var(self, d); },
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
@@ -1997,6 +2070,8 @@ fn emit_container_body(self: *Ast2Ir, ref: usize) {
|
||||
}
|
||||
},
|
||||
.TopLevelBlock(x) => { emit_container_body(self, dref); },
|
||||
.VarDecl(_) => {},
|
||||
.ConstDecl(_) => {},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
@@ -2070,6 +2145,8 @@ fn new(builder: *IrBuilder, sema: *Sema, diag: *Diag): Ast2Ir {
|
||||
.ast = sema.ast,
|
||||
.def2ir_map = Map.new(@sizeof(usize), @alignof(usize), @sizeof(usize), @alignof(usize),
|
||||
hash_kind_usize()),
|
||||
.glob_gidx = Map.new(@sizeof(usize), @alignof(usize), @sizeof(usize), @alignof(usize),
|
||||
hash_kind_usize()),
|
||||
.loop_stack = Vec.blank(@sizeof(LoopCtx), @alignof(LoopCtx)),
|
||||
.defer_stack = Vec.blank(@sizeof(usize), @alignof(usize)),
|
||||
.cur_ret_lang = 0,
|
||||
@@ -2089,6 +2166,7 @@ fn drop(self: *Ast2Ir) {
|
||||
Vec.drop(&self.loop_stack);
|
||||
Vec.drop(&self.defer_stack);
|
||||
Map.drop(&self.def2ir_map);
|
||||
Map.drop(&self.glob_gidx);
|
||||
var k: usize = 0;
|
||||
while k < self.owned.len {
|
||||
match Vec.get(&self.owned, k) {
|
||||
|
||||
+37
-1
@@ -82,6 +82,14 @@ type ConstLit = enum {
|
||||
Float: f64,
|
||||
Char: u8,
|
||||
Fn: usize,
|
||||
Global: usize, // 全局变量下标(Ir.globals);值 = 指向该全局的 *T
|
||||
}
|
||||
|
||||
// 模块级全局变量:编译期分配的静态存储,引用时是一个 *T 指针常量(同 LLVM GlobalVariable)。
|
||||
type Global = struct {
|
||||
tid: usize,
|
||||
zero: bool, // true = 零初始化(BSS,不携带字节)
|
||||
data: Vec, // u8 初始字节(zero=false 时使用)
|
||||
}
|
||||
|
||||
type IrBody = enum {
|
||||
@@ -128,6 +136,7 @@ type IrFunc = struct {
|
||||
type Ir = @self;
|
||||
|
||||
funcs: Vec, // IrFunc
|
||||
globals: Vec, // Global
|
||||
|
||||
// ---- 生命周期 ----
|
||||
|
||||
@@ -141,7 +150,8 @@ fn new_entry(): IrEntry {
|
||||
}
|
||||
|
||||
fn new(): Ir {
|
||||
var ir = Ir { .funcs = Vec.blank(@sizeof(IrFunc), @alignof(IrFunc)) };
|
||||
var ir = Ir { .funcs = Vec.blank(@sizeof(IrFunc), @alignof(IrFunc)),
|
||||
.globals = Vec.blank(@sizeof(Global), @alignof(Global)) };
|
||||
var sentinel = IrFunc {
|
||||
.name = ByteView.empty(),
|
||||
.attrs = Vec.blank(@sizeof(usize), @alignof(usize)),
|
||||
@@ -180,6 +190,25 @@ fn drop(self: *Ir) {
|
||||
i = i + 1;
|
||||
}
|
||||
Vec.drop(&self.funcs);
|
||||
var g: usize = 0;
|
||||
while g < self.globals.len {
|
||||
var ge = (self.globals.data as *Global) + g;
|
||||
Vec.drop(&ge.data);
|
||||
g = g + 1;
|
||||
}
|
||||
Vec.drop(&self.globals);
|
||||
}
|
||||
|
||||
// 追加一个全局变量;返回其下标(gidx)。bytes 的所有权转移进表。
|
||||
fn add_global(self: *Ir, tid: usize, zero: bool, bytes: Vec): usize {
|
||||
var g = Global { .tid = tid, .zero = zero, .data = bytes };
|
||||
match Vec.push(&self.globals, &g as *u8) { .Ok => {}, .Err(error) => { ret 0; } }
|
||||
ret self.globals.len - 1;
|
||||
}
|
||||
|
||||
fn global_at(self: *Ir, gidx: usize): *Global {
|
||||
if gidx >= self.globals.len { ret null; }
|
||||
ret (self.globals.data as *Global) + gidx;
|
||||
}
|
||||
|
||||
fn alloc_fn(self: *Ir): usize {
|
||||
@@ -394,6 +423,13 @@ fn dump_body(out: *ByteBuffer, ty: *TypeTable, e: *IrEntry): bool {
|
||||
},
|
||||
.Const(c) => {
|
||||
if !dump_type(out, ty, c.tid) { ret false; }
|
||||
match c.lit {
|
||||
.Global(g) => {
|
||||
if !out_str(out, " global#") { ret false; }
|
||||
ret out_u(out, g);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
var te = TypeTable.node(ty, c.tid);
|
||||
var is_float = false;
|
||||
var is_fn = false;
|
||||
|
||||
+57
-1
@@ -18,7 +18,7 @@ type .{ .Layout } = @import("./layout.spl");
|
||||
type AttrM = @import("./attr.spl");
|
||||
type .{ .Attr } = @import("./attr.spl");
|
||||
type IrM = @import("./ir.spl");
|
||||
type .{ .Ir, .IrFunc, .IrEntry } = @import("./ir.spl");
|
||||
type .{ .Ir, .IrFunc, .IrEntry, .Global } = @import("./ir.spl");
|
||||
|
||||
type Ir2C = @self;
|
||||
|
||||
@@ -570,6 +570,7 @@ fn emit_const(self: *Ir2C, ref: usize, n: *IrEntry) {
|
||||
}
|
||||
},
|
||||
.Ptr(v) => { put(self, "(void*)"); put_u(self, v); },
|
||||
.Global(g) => { put(self, "(void*)spl_g_"); put_u(self, g); },
|
||||
.Float(v) => { put(self, "0.0"); },
|
||||
.Fn(fid) => { put(self, "(void*)&"); c_fn_name(self, fid); },
|
||||
}
|
||||
@@ -1240,6 +1241,32 @@ fn find_main(self: *Ir2C): usize {
|
||||
fn run(self: *Ir2C) {
|
||||
put(self, "#include \"spl_rt.h\"\n");
|
||||
|
||||
// 模块级全局变量(Zero → BSS:无初始化式)。
|
||||
var gi: usize = 0;
|
||||
while gi < self.ir.globals.len {
|
||||
var g = (self.ir.globals.data as *Global) + gi;
|
||||
var sz = type_size(self, g.tid);
|
||||
if sz == 0 { sz = 1; }
|
||||
put(self, "static unsigned char spl_g_");
|
||||
put_u(self, gi);
|
||||
put(self, "[");
|
||||
put_u(self, sz);
|
||||
put(self, "]");
|
||||
if !g.zero {
|
||||
put(self, " = {");
|
||||
var k: usize = 0;
|
||||
while k < g.data.len {
|
||||
if k > 0 { put(self, ", "); }
|
||||
put_u(self, ((g.data.data as *u8) + k).* as usize);
|
||||
k = k + 1;
|
||||
}
|
||||
if g.data.len == 0 { put(self, "0"); }
|
||||
put(self, "}");
|
||||
}
|
||||
put(self, ";\n");
|
||||
gi = gi + 1;
|
||||
}
|
||||
|
||||
var i: usize = 1;
|
||||
while i < self.ir.funcs.len {
|
||||
var f = IrM.func(self.ir, i);
|
||||
@@ -1256,6 +1283,7 @@ fn run(self: *Ir2C) {
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
emit_ct_table(self);
|
||||
var m = find_main(self);
|
||||
if m != 0 {
|
||||
var f = IrM.func(self.ir, m);
|
||||
@@ -1283,6 +1311,34 @@ fn run(self: *Ir2C) {
|
||||
}
|
||||
}
|
||||
|
||||
// compiler function(`ct_` 前缀)回调表:供 native 端 vm_set_foreign 按名分发。
|
||||
fn emit_ct_table(self: *Ir2C) {
|
||||
put(self, "static const struct { const char *name; void *fn; } spl_ct_tab[] = {\n");
|
||||
var n: usize = 0;
|
||||
var i: usize = 1;
|
||||
while i < self.ir.funcs.len {
|
||||
var f = IrM.func(self.ir, i);
|
||||
if f != null && !fn_is_extern(self, f) &&
|
||||
ByteView.starts_with(f.name, ByteView.from_slice("ct_")) {
|
||||
put(self, " {\"");
|
||||
put_bv(self, f.name);
|
||||
put(self, "\", (void*)f");
|
||||
put_u(self, i);
|
||||
put(self, "},\n");
|
||||
n = n + 1;
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
if n == 0 { put(self, " {0, 0},\n"); }
|
||||
put(self, "};\n");
|
||||
put(self, "void *spl_ct_lookup(const char *name) {\n");
|
||||
put(self, " unsigned long i;\n");
|
||||
put(self, " for (i = 0; i < sizeof(spl_ct_tab)/sizeof(spl_ct_tab[0]); i++)\n");
|
||||
put(self, " if (spl_ct_tab[i].name && strcmp(spl_ct_tab[i].name, name) == 0) return spl_ct_tab[i].fn;\n");
|
||||
put(self, " return 0;\n");
|
||||
put(self, "}\n");
|
||||
}
|
||||
|
||||
fn new(ir: *Ir, ty: *TypeTable, layout: *Layout, attr: *Attr, out: *ByteBuffer): Ir2C {
|
||||
ret Ir2C {
|
||||
.ir = ir,
|
||||
|
||||
+49
-1
@@ -16,7 +16,7 @@ type .{ .Layout } = @import("./layout.spl");
|
||||
type AttrM = @import("./attr.spl");
|
||||
type .{ .Attr, .AttrArg } = @import("./attr.spl");
|
||||
type IrM = @import("./ir.spl");
|
||||
type .{ .Ir, .IrFunc, .IrEntry } = @import("./ir.spl");
|
||||
type .{ .Ir, .IrFunc, .IrEntry, .Global } = @import("./ir.spl");
|
||||
type Mcode = @import("./mcode.spl");
|
||||
type .{ .Prog, .Op, .VmKind, .VmIns, .VmFunc } = @import("./mcode.spl");
|
||||
type Mem = @import("sys/mem.spl");
|
||||
@@ -28,6 +28,7 @@ ty: *TypeTable,
|
||||
attr: *Attr,
|
||||
layout: *Layout,
|
||||
extern_native: Vec, // isize
|
||||
global_gaddr: Vec, // usize:每个全局变量在 prog.gdata 的下标
|
||||
prog: Prog,
|
||||
|
||||
// ---- 类型/布局查询 ----
|
||||
@@ -589,6 +590,23 @@ fn cg_const(cg: *Cgen, ref: usize, n: *IrEntry) {
|
||||
var te = TypeTable.node(ctx.ty, tid);
|
||||
if te == null { ret; }
|
||||
|
||||
// 全局变量引用:Const{ lit = Global(gidx) } → GADDR(该全局的运行时地址)。
|
||||
match n.body {
|
||||
.Const(c) => {
|
||||
match c.lit {
|
||||
.Global(g) => {
|
||||
var di: usize = 0;
|
||||
if g < ctx.global_gaddr.len { di = (ctx.global_gaddr.data as *usize)[g]; }
|
||||
emit(cg, .Gaddr, .Void, di);
|
||||
cg_store_slot(cg, ref, .Ptr);
|
||||
ret;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
match te.node {
|
||||
.Fn(f) => {
|
||||
var cref = const_fn(n);
|
||||
@@ -952,6 +970,9 @@ fn cg_control(cg: *Cgen, ref: usize, n: *IrEntry) {
|
||||
cg_store_slot(cg, ref, rk);
|
||||
}
|
||||
} else {
|
||||
/* ABI:实参下方预留 2 个空闲 slot(PAD0/PAD1)。 */
|
||||
cg_push(cg, .U64, 0);
|
||||
cg_push(cg, .U64, 0);
|
||||
var ret_t = cl.tid;
|
||||
var ret_is_sret = ir_is_aggregate(ctx, ret_t);
|
||||
var sret_off: usize = 0;
|
||||
@@ -1210,6 +1231,7 @@ fn init(ctx: *Ir2Vm, ir: *Ir, ty: *TypeTable, layout: *Layout, attr: *Attr) {
|
||||
ctx.attr = attr;
|
||||
ctx.layout = layout;
|
||||
ctx.extern_native = Vec.blank(@sizeof(isize), @alignof(isize));
|
||||
ctx.global_gaddr = Vec.blank(@sizeof(usize), @alignof(usize));
|
||||
|
||||
match Mcode.new() {
|
||||
.Ok(v) => { ctx.prog = v; },
|
||||
@@ -1220,6 +1242,7 @@ fn init(ctx: *Ir2Vm, ir: *Ir, ty: *TypeTable, layout: *Layout, attr: *Attr) {
|
||||
fn drop(ctx: *Ir2Vm) {
|
||||
Mcode.drop(&ctx.prog);
|
||||
Vec.drop(&ctx.extern_native);
|
||||
Vec.drop(&ctx.global_gaddr);
|
||||
}
|
||||
|
||||
fn run_func(ctx: *Ir2Vm, ref: usize): i32 {
|
||||
@@ -1250,6 +1273,22 @@ fn run(ctx: *Ir2Vm): i32 {
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
// 登记全局变量到 prog.gdata(Zero→零填/BSS,Bytes→拷贝),记下运行时下标。
|
||||
var gi: usize = 0;
|
||||
while gi < ctx.ir.globals.len {
|
||||
var g = (ctx.ir.globals.data as *Global) + gi;
|
||||
var gsize = ir_type_size(ctx, g.tid);
|
||||
var didx: usize = 0;
|
||||
if g.zero {
|
||||
didx = Mcode.add_zero_data(&ctx.prog, gsize);
|
||||
} else {
|
||||
didx = Mcode.add_data(&ctx.prog, g.data.data as usize, gsize);
|
||||
}
|
||||
var dv = didx;
|
||||
match Vec.push(&ctx.global_gaddr, &dv as *u8) { .Ok => {}, .Err(error) => {} }
|
||||
gi = gi + 1;
|
||||
}
|
||||
|
||||
// 哨兵:保证真实函数入口地址非零。
|
||||
ignore_usize(Mcode.add_instr(&ctx.prog, .Halt, .Void, 0));
|
||||
|
||||
@@ -1327,6 +1366,15 @@ fn dump(ctx: *Ir2Vm, out: *ByteBuffer): bool {
|
||||
ret Mcode.dump(&ctx.prog, out);
|
||||
}
|
||||
|
||||
// 序列化到内存 ByteBuffer(不落盘),供内嵌 VM 直接加载。
|
||||
fn encode(ctx: *Ir2Vm, out: *ByteBuffer): bool {
|
||||
match Mcode.encode(&ctx.prog, out) {
|
||||
.Ok => { ret true; },
|
||||
.Err(error) => { ret false; },
|
||||
}
|
||||
ret false;
|
||||
}
|
||||
|
||||
// 把翻译结果写成 .sir(二进制)。
|
||||
fn store(ctx: *Ir2Vm, path: ByteView): bool {
|
||||
match Mcode.store(&ctx.prog, path) {
|
||||
|
||||
@@ -285,6 +285,12 @@ fn type_const_float(self: *IrBuilder, tid: usize, val: f64): usize { ret const_n
|
||||
fn type_const_char(self: *IrBuilder, tid: usize, val: u8): usize { ret const_node(self, tid, .Char(val)); }
|
||||
fn type_const_fn(self: *IrBuilder, tid: usize, val: usize): usize { ret const_node(self, tid, .Fn(val)); }
|
||||
fn type_const_bytes(self: *IrBuilder, tid: usize, data: usize): usize { ret const_node(self, tid, .Ptr(data)); }
|
||||
fn type_const_global(self: *IrBuilder, tid: usize, gidx: usize): usize { ret const_node(self, tid, .Global(gidx)); }
|
||||
|
||||
// 追加模块级全局变量;返回 gidx。bytes 所有权转移进 Ir。
|
||||
fn add_global(self: *IrBuilder, tid: usize, zero: bool, bytes: Vec): usize {
|
||||
ret Ir.add_global(&self.ir, tid, zero, bytes);
|
||||
}
|
||||
|
||||
// ---- type ops ----
|
||||
|
||||
|
||||
+54
-25
@@ -314,6 +314,26 @@ fn add_data(self: *Prog, ptr: usize, size: usize): usize {
|
||||
ret self.gdata.len - 1;
|
||||
}
|
||||
|
||||
// 追加一条 size 字节的零初始化全局数据(BSS 语义);返回其索引。
|
||||
fn add_zero_data(self: *Prog, size: usize): usize {
|
||||
var entry = ByteBuffer.new();
|
||||
match ByteBuffer.reserve(&entry, size) {
|
||||
.Ok => {},
|
||||
.Err(error) => { ByteBuffer.drop(&entry); ret 0; },
|
||||
}
|
||||
if size != 0 {
|
||||
entry.storage.len = size;
|
||||
var p = entry.storage.data as *u8;
|
||||
var i: usize = 0;
|
||||
while i < size { p[i] = 0; i = i + 1; }
|
||||
}
|
||||
match Vec.push(&self.gdata, &entry as *u8) {
|
||||
.Ok => {},
|
||||
.Err(error) => { ByteBuffer.drop(&entry); ret 0; },
|
||||
}
|
||||
ret self.gdata.len - 1;
|
||||
}
|
||||
|
||||
// 返回 name 对应的 native 索引;不存在则登记。name 为空指针时返回 -1。
|
||||
fn get_or_add_native(self: *Prog, name: ByteView): isize {
|
||||
if name.ptr == null { ret -1; }
|
||||
@@ -545,10 +565,8 @@ fn put_name_field(out: *ByteBuffer, name: ByteView): bool {
|
||||
ret put_zeros(out, align8(nlen) - nlen);
|
||||
}
|
||||
|
||||
fn store(self: *Prog, path: ByteView): Status {
|
||||
var out = ByteBuffer.new();
|
||||
defer { ByteBuffer.drop(&out); }
|
||||
|
||||
// 把 prog 序列化为二进制写入 out(不落盘,供内嵌 VM 直接用)。
|
||||
fn encode(self: *Prog, out: *ByteBuffer): Status {
|
||||
// 精确预估输出大小并一次性预留,避免大文件逐字节增长导致的重复拷贝。
|
||||
var total: usize = 48;
|
||||
var fi: usize = 0;
|
||||
@@ -581,11 +599,11 @@ fn store(self: *Prog, path: ByteView): Status {
|
||||
if e != null { total = total + 8 + align8(e.storage.len); }
|
||||
fi = fi + 1;
|
||||
}
|
||||
match ByteBuffer.reserve(&out, total) { .Ok => {}, .Err(error) => {} }
|
||||
match ByteBuffer.reserve(out, total) { .Ok => {}, .Err(error) => {} }
|
||||
if out.storage.cap < total || out.storage.data == null { ret .Err(make(.Io)); }
|
||||
|
||||
if !put_view(&out, ByteView.from_slice("SPLBIN")) { ret .Err(make(.Io)); }
|
||||
if !put_zeros(&out, 2) { ret .Err(make(.Io)); }
|
||||
if !put_view(out, ByteView.from_slice("SPLBIN")) { ret .Err(make(.Io)); }
|
||||
if !put_zeros(out, 2) { ret .Err(make(.Io)); }
|
||||
|
||||
var nfuncs = self.funcs.len;
|
||||
var ninsns = self.insns.len;
|
||||
@@ -594,21 +612,21 @@ fn store(self: *Prog, path: ByteView): Status {
|
||||
var ndata: usize = 0;
|
||||
if self.gdata.len > 0 { ndata = self.gdata.len - 1; }
|
||||
|
||||
if !put_u64(&out, nfuncs) { ret .Err(make(.Io)); }
|
||||
if !put_u64(&out, ninsns) { ret .Err(make(.Io)); }
|
||||
if !put_u64(&out, nnatives) { ret .Err(make(.Io)); }
|
||||
if !put_u64(&out, nstrs) { ret .Err(make(.Io)); }
|
||||
if !put_u64(&out, ndata) { ret .Err(make(.Io)); }
|
||||
if !put_u64(out, nfuncs) { ret .Err(make(.Io)); }
|
||||
if !put_u64(out, ninsns) { ret .Err(make(.Io)); }
|
||||
if !put_u64(out, nnatives) { ret .Err(make(.Io)); }
|
||||
if !put_u64(out, nstrs) { ret .Err(make(.Io)); }
|
||||
if !put_u64(out, ndata) { ret .Err(make(.Io)); }
|
||||
|
||||
var i: usize = 0;
|
||||
while i < nfuncs {
|
||||
var f = func_at(self, i);
|
||||
if f == null { ret .Err(make(.Io)); }
|
||||
if !put_name_field(&out, f.name) { ret .Err(make(.Io)); }
|
||||
if !put_u64(&out, f.idx_of_strtab) { ret .Err(make(.Io)); }
|
||||
if !put_u64(&out, f.nargs) { ret .Err(make(.Io)); }
|
||||
if !put_u64(&out, f.ninsns) { ret .Err(make(.Io)); }
|
||||
if !put_u64(&out, f.address) { ret .Err(make(.Io)); }
|
||||
if !put_name_field(out, f.name) { ret .Err(make(.Io)); }
|
||||
if !put_u64(out, f.idx_of_strtab) { ret .Err(make(.Io)); }
|
||||
if !put_u64(out, f.nargs) { ret .Err(make(.Io)); }
|
||||
if !put_u64(out, f.ninsns) { ret .Err(make(.Io)); }
|
||||
if !put_u64(out, f.address) { ret .Err(make(.Io)); }
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
@@ -627,17 +645,17 @@ fn store(self: *Prog, path: ByteView): Status {
|
||||
while i < nnatives {
|
||||
var n = native_at(self, i);
|
||||
if n == null { ret .Err(make(.Io)); }
|
||||
if !put_name_field(&out, n.name) { ret .Err(make(.Io)); }
|
||||
if !put_u64(&out, n.idx_of_strtab) { ret .Err(make(.Io)); }
|
||||
if !put_name_field(out, n.name) { ret .Err(make(.Io)); }
|
||||
if !put_u64(out, n.idx_of_strtab) { ret .Err(make(.Io)); }
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while i < nstrs {
|
||||
var s = str_at(self, i);
|
||||
if !put_u64(&out, s.len) { ret .Err(make(.Io)); }
|
||||
if !put_view(&out, s) { ret .Err(make(.Io)); }
|
||||
if !put_zeros(&out, align8(s.len) - s.len) { ret .Err(make(.Io)); }
|
||||
if !put_u64(out, s.len) { ret .Err(make(.Io)); }
|
||||
if !put_view(out, s) { ret .Err(make(.Io)); }
|
||||
if !put_zeros(out, align8(s.len) - s.len) { ret .Err(make(.Io)); }
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
@@ -646,11 +664,22 @@ fn store(self: *Prog, path: ByteView): Status {
|
||||
var e = gdata_at(self, i);
|
||||
if e == null { ret .Err(make(.Io)); }
|
||||
var dsize = e.storage.len;
|
||||
if !put_u64(&out, dsize) { ret .Err(make(.Io)); }
|
||||
if !put_view(&out, ByteView.from_slice(ByteBuffer.as_slice(e.*))) { ret .Err(make(.Io)); }
|
||||
if !put_zeros(&out, align8(dsize) - dsize) { ret .Err(make(.Io)); }
|
||||
if !put_u64(out, dsize) { ret .Err(make(.Io)); }
|
||||
if !put_view(out, ByteView.from_slice(ByteBuffer.as_slice(e.*))) { ret .Err(make(.Io)); }
|
||||
if !put_zeros(out, align8(dsize) - dsize) { ret .Err(make(.Io)); }
|
||||
i = i + 1;
|
||||
}
|
||||
ret .Ok;
|
||||
}
|
||||
|
||||
fn store(self: *Prog, path: ByteView): Status {
|
||||
var out = ByteBuffer.new();
|
||||
defer { ByteBuffer.drop(&out); }
|
||||
|
||||
match encode(self, &out) {
|
||||
.Ok => {},
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
|
||||
var f = open_cstr(path, ByteView.from_slice("wb"));
|
||||
if !File.valid(&f) { ret .Err(make(.Io)); }
|
||||
|
||||
+38
-1
@@ -282,6 +282,7 @@ fn can_start_expr(kind: TokenKind): bool {
|
||||
.For => { ret true; },
|
||||
.Match => { ret true; },
|
||||
.Comptime => { ret true; },
|
||||
.Try => { ret true; },
|
||||
_ => { ret false; },
|
||||
}
|
||||
},
|
||||
@@ -321,6 +322,7 @@ fn can_start_statement(kind: TokenKind): bool {
|
||||
.Break => { ret true; },
|
||||
.Continue => { ret true; },
|
||||
.Defer => { ret true; },
|
||||
.Errdefer => { ret true; },
|
||||
.Var => { ret true; },
|
||||
.Const => { ret true; },
|
||||
.Type => { ret true; },
|
||||
@@ -1237,7 +1239,19 @@ fn parse_expr_prec(self: *Parser, min_prec: i32): usize {
|
||||
}
|
||||
|
||||
fn parse_expr(self: *Parser): usize {
|
||||
ret parse_expr_prec(self, 1);
|
||||
var e = parse_expr_prec(self, 1);
|
||||
// `expr catch <pattern> <block>`:catch 用解包语法绑定错误。
|
||||
while !self.failed {
|
||||
var t = peek(self);
|
||||
if t == null { break; }
|
||||
if !kind_is_keyword(t.kind, .Catch) { break; }
|
||||
advance(self);
|
||||
var pat = parse_pattern(self);
|
||||
if pat == 0 { ret e; }
|
||||
var handler = parse_block(self);
|
||||
e = new_node(self, .ExprCatch(.{ .value = e, .pattern = pat, .handler = handler }), t);
|
||||
}
|
||||
ret e;
|
||||
}
|
||||
|
||||
fn parse_cast(self: *Parser): usize {
|
||||
@@ -1444,6 +1458,12 @@ fn parse_primary(self: *Parser): usize {
|
||||
if @enum2int(k) == @enum2int(Keyword.Loop) { ret parse_loop_expr(self); }
|
||||
if @enum2int(k) == @enum2int(Keyword.For) { ret parse_for_expr(self); }
|
||||
if @enum2int(k) == @enum2int(Keyword.Match) { ret parse_match_expr(self); }
|
||||
// `try <expr>`:错误上抛前缀。
|
||||
if @enum2int(k) == @enum2int(Keyword.Try) {
|
||||
advance(self);
|
||||
var tv = parse_expr(self);
|
||||
ret new_node(self, .ExprTry(.{ .value = tv }), t);
|
||||
}
|
||||
// `comptime <expr>`:编译期表达式(可调用普通函数、返回类型等)。
|
||||
if @enum2int(k) == @enum2int(Keyword.Comptime) {
|
||||
advance(self);
|
||||
@@ -1958,6 +1978,22 @@ fn parse_defer_stmt(self: *Parser): usize {
|
||||
ret new_node(self, .StatementDefer(.{ .body = body }), kw);
|
||||
}
|
||||
|
||||
fn parse_errdefer_stmt(self: *Parser): usize {
|
||||
var kw = peek(self);
|
||||
if kw == null || !kind_is_keyword(kw.kind, .Errdefer) { ret 0; }
|
||||
advance(self);
|
||||
var body: usize = 0;
|
||||
var t = peek(self);
|
||||
if t != null && kind_is_punct(t.kind, .LBrace) { body = parse_block(self); }
|
||||
else if t != null && can_start_statement(t.kind) { body = parse_statement(self); }
|
||||
else {
|
||||
body = parse_expr(self);
|
||||
t = peek(self);
|
||||
if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); }
|
||||
}
|
||||
ret new_node(self, .StatementErrdefer(.{ .body = body }), kw);
|
||||
}
|
||||
|
||||
fn parse_comptime_stmt(self: *Parser): usize {
|
||||
var kw = peek(self);
|
||||
if kw == null || !kind_is_keyword(kw.kind, .Comptime) { ret 0; }
|
||||
@@ -1979,6 +2015,7 @@ fn parse_statement(self: *Parser): usize {
|
||||
if @enum2int(k) == @enum2int(Keyword.Break) { ret parse_break_stmt(self); }
|
||||
if @enum2int(k) == @enum2int(Keyword.Continue) { ret parse_continue_stmt(self); }
|
||||
if @enum2int(k) == @enum2int(Keyword.Defer) { ret parse_defer_stmt(self); }
|
||||
if @enum2int(k) == @enum2int(Keyword.Errdefer) { ret parse_errdefer_stmt(self); }
|
||||
if @enum2int(k) == @enum2int(Keyword.Comptime) { ret parse_comptime_stmt(self); }
|
||||
if @enum2int(k) == @enum2int(Keyword.Var) ||
|
||||
@enum2int(k) == @enum2int(Keyword.Const) ||
|
||||
|
||||
@@ -1,637 +0,0 @@
|
||||
/* spl_rt.h - SPL native runtime (header-only, C89-friendly)
|
||||
*
|
||||
* 提供 ir2c 生成代码所需的所有 `#extern(vm)` 符号,以及 SPL 基础类型别名。
|
||||
* 目标:生成代码可用 C89 语法通过(不依赖 stdint/stdbool)。
|
||||
*/
|
||||
|
||||
#ifndef __SPL_RT_H__
|
||||
#define __SPL_RT_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
/* 语义清晰的 inline:C99 用 static inline,GNU 用 __inline__,C89 退化为 static */
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||
#define SPL_INLINE static inline
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#define SPL_INLINE static __inline__
|
||||
#else
|
||||
#define SPL_INLINE static
|
||||
#endif
|
||||
|
||||
/* ---- SPL 基础类型别名 ---- */
|
||||
typedef unsigned char u8;
|
||||
typedef signed char i8;
|
||||
typedef unsigned short u16;
|
||||
typedef signed short i16;
|
||||
typedef unsigned int u32;
|
||||
typedef signed int i32;
|
||||
typedef unsigned long long u64;
|
||||
typedef signed long long i64;
|
||||
|
||||
#ifndef SPL_USIZE_TYPEDEF
|
||||
#define SPL_USIZE_TYPEDEF
|
||||
typedef u64 usize;
|
||||
#endif
|
||||
|
||||
#ifndef SPL_ISIZE_TYPEDEF
|
||||
#define SPL_ISIZE_TYPEDEF
|
||||
typedef i64 isize;
|
||||
#endif
|
||||
/* 注:不定义 bool/true/false,避免与 C23 关键字冲突;生成代码用 u8 表示布尔。 */
|
||||
|
||||
/* ---- panic / trap ---- */
|
||||
SPL_INLINE void spl_panic(const char *msg) {
|
||||
fprintf(stderr, "%s\n", msg ? msg : "panic");
|
||||
exit(1);
|
||||
}
|
||||
SPL_INLINE void spl_trap(void) { abort(); }
|
||||
|
||||
/* ---- mem ---- */
|
||||
SPL_INLINE void *vm_alloc(u64 sz) {
|
||||
if (sz == 0)
|
||||
return NULL;
|
||||
return malloc((size_t)sz);
|
||||
}
|
||||
SPL_INLINE void vm_free(void *p) {
|
||||
if (p)
|
||||
free(p);
|
||||
}
|
||||
SPL_INLINE void *vm_realloc(void *p, u64 sz) {
|
||||
if (!p)
|
||||
return vm_alloc(sz);
|
||||
if (sz == 0) {
|
||||
free(p);
|
||||
return NULL;
|
||||
}
|
||||
return realloc(p, (size_t)sz);
|
||||
}
|
||||
SPL_INLINE void *vm_memcpy(void *dst, const void *src, u64 n) {
|
||||
if (n)
|
||||
memcpy(dst, src, (size_t)n);
|
||||
return dst;
|
||||
}
|
||||
SPL_INLINE void *vm_memmove(void *dst, const void *src, u64 n) {
|
||||
if (n)
|
||||
memmove(dst, src, (size_t)n);
|
||||
return dst;
|
||||
}
|
||||
SPL_INLINE i64 vm_memcmp(const void *a, const void *b, u64 n) {
|
||||
if (n == 0)
|
||||
return 0;
|
||||
return (i64)memcmp(a, b, (size_t)n);
|
||||
}
|
||||
SPL_INLINE i32 vm_bytes_eq(const void *a, u64 alen, const void *b, u64 blen) {
|
||||
if (alen != blen)
|
||||
return 0;
|
||||
if (alen == 0)
|
||||
return 1;
|
||||
if (!a || !b)
|
||||
return 0;
|
||||
return memcmp(a, b, (size_t)alen) == 0;
|
||||
}
|
||||
|
||||
/* ---- Vec ---- */
|
||||
typedef struct {
|
||||
usize raw;
|
||||
usize data;
|
||||
usize len;
|
||||
usize cap;
|
||||
usize elem_size;
|
||||
usize elem_align;
|
||||
} spl_vec_t;
|
||||
|
||||
SPL_INLINE int spl_vec_is_valid(const spl_vec_t *v) {
|
||||
usize a;
|
||||
if (v->elem_size == 0)
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
|
||||
SPL_INLINE int spl_align_ok(usize a) { return a != 0 && (a & (a - 1)) == 0; }
|
||||
|
||||
SPL_INLINE void spl_grow(usize current, usize need, usize *out) {
|
||||
usize cap = current < 8 ? 8 : current;
|
||||
usize limit = ~(usize)0;
|
||||
while (cap < need) {
|
||||
if (cap > limit / 2) {
|
||||
cap = need;
|
||||
break;
|
||||
}
|
||||
cap = cap * 2;
|
||||
}
|
||||
*out = cap;
|
||||
}
|
||||
|
||||
SPL_INLINE int spl_vec_allocate(spl_vec_t *out, usize capacity, usize elem_size, usize elem_align) {
|
||||
usize bytes, mask, addr;
|
||||
void *raw;
|
||||
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 > ~(usize)0 / elem_size)
|
||||
return 3;
|
||||
bytes = capacity * elem_size;
|
||||
mask = elem_align - 1;
|
||||
if (bytes > ~(usize)0 - mask)
|
||||
return 3;
|
||||
raw = malloc(bytes + mask);
|
||||
if (!raw)
|
||||
return 3;
|
||||
addr = (usize)raw;
|
||||
if (addr > ~(usize)0 - mask) {
|
||||
free(raw);
|
||||
return 3;
|
||||
}
|
||||
out->raw = addr;
|
||||
out->data = (addr + mask) & ~mask;
|
||||
out->cap = capacity;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPL_INLINE int spl_vec_reserve_raw(spl_vec_t *v, usize need) {
|
||||
usize new_cap = 0, used = 0, old;
|
||||
spl_vec_t fresh;
|
||||
int rc;
|
||||
if (need <= v->cap)
|
||||
return 0;
|
||||
spl_grow(v->cap, need, &new_cap);
|
||||
rc = spl_vec_allocate(&fresh, new_cap, v->elem_size, v->elem_align);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (v->len != 0) {
|
||||
if (v->len > ~(usize)0 / v->elem_size) {
|
||||
free((void *)fresh.raw);
|
||||
return 3;
|
||||
}
|
||||
used = v->len * v->elem_size;
|
||||
}
|
||||
if (used)
|
||||
memcpy((void *)fresh.data, (void *)v->data, used);
|
||||
fresh.len = v->len;
|
||||
old = v->raw;
|
||||
*v = fresh;
|
||||
if (old)
|
||||
free((void *)old);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SPL_INLINE void spl_vec_release(spl_vec_t *v) {
|
||||
usize es = v->elem_size, 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;
|
||||
}
|
||||
|
||||
SPL_INLINE i32 vm_vec_valid(void *vv) {
|
||||
const spl_vec_t *v = (const spl_vec_t *)vv;
|
||||
return v ? spl_vec_is_valid(v) : 0;
|
||||
}
|
||||
SPL_INLINE i32 vm_vec_push_raw(void *vv, const void *elem) {
|
||||
spl_vec_t *v = (spl_vec_t *)vv;
|
||||
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;
|
||||
}
|
||||
SPL_INLINE i32 vm_vec_get(void *vv, u64 index, void **out) {
|
||||
const spl_vec_t *v = (const spl_vec_t *)vv;
|
||||
if (!v || !out)
|
||||
return 1;
|
||||
if (!spl_vec_is_valid(v))
|
||||
return 1;
|
||||
if (index >= v->len)
|
||||
return 2;
|
||||
*out = (void *)(v->data + index * v->elem_size);
|
||||
return 0;
|
||||
}
|
||||
SPL_INLINE i32 vm_vec_push(void *vv, const void *elem) {
|
||||
spl_vec_t *v = (spl_vec_t *)vv;
|
||||
int rc;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
SPL_INLINE i32 vm_vec_allocate(void *out, u64 capacity, u64 elem_size, u64 elem_align) {
|
||||
return spl_vec_allocate((spl_vec_t *)out, capacity, elem_size, elem_align);
|
||||
}
|
||||
SPL_INLINE i32 vm_vec_reserve_raw(void *vv, u64 need) {
|
||||
spl_vec_t *v = (spl_vec_t *)vv;
|
||||
if (!v)
|
||||
return 1;
|
||||
return spl_vec_reserve_raw(v, need);
|
||||
}
|
||||
SPL_INLINE i32 vm_vec_append(void *vv, const void *elems, u64 count) {
|
||||
spl_vec_t *v = (spl_vec_t *)vv;
|
||||
usize copy_bytes, new_len, used, source_offset = 0;
|
||||
int source_inside = 0, rc;
|
||||
if (!v || !spl_vec_is_valid(v))
|
||||
return 1;
|
||||
if (count == 0)
|
||||
return 0;
|
||||
if (!elems)
|
||||
return 1;
|
||||
if (count > ~(usize)0 / v->elem_size)
|
||||
return 3;
|
||||
copy_bytes = count * v->elem_size;
|
||||
if (v->len > ~(usize)0 - count)
|
||||
return 3;
|
||||
new_len = v->len + count;
|
||||
used = v->len * v->elem_size;
|
||||
if (used) {
|
||||
usize base = v->data;
|
||||
usize source = (usize)elems;
|
||||
if (source >= base && source - base < used) {
|
||||
source_offset = source - base;
|
||||
if (copy_bytes > used - source_offset)
|
||||
return 2;
|
||||
source_inside = 1;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
SPL_INLINE i32 vm_vec_set(void *vv, u64 index, const void *elem) {
|
||||
spl_vec_t *v = (spl_vec_t *)vv;
|
||||
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;
|
||||
}
|
||||
SPL_INLINE i32 vm_vec_pop(void *vv, void *out) {
|
||||
spl_vec_t *v = (spl_vec_t *)vv;
|
||||
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;
|
||||
}
|
||||
SPL_INLINE i32 vm_vec_clone(void *outp, const void *srcp) {
|
||||
spl_vec_t *out = (spl_vec_t *)outp;
|
||||
const spl_vec_t *src = (const spl_vec_t *)srcp;
|
||||
spl_vec_t fresh;
|
||||
usize bytes;
|
||||
int rc;
|
||||
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;
|
||||
fresh.raw = 0;
|
||||
fresh.data = 0;
|
||||
fresh.len = 0;
|
||||
fresh.cap = 0;
|
||||
fresh.elem_size = src->elem_size;
|
||||
fresh.elem_align = src->elem_align;
|
||||
rc = spl_vec_reserve_raw(&fresh, src->len);
|
||||
if (rc)
|
||||
return rc;
|
||||
bytes = src->len * src->elem_size;
|
||||
if (bytes)
|
||||
memcpy((void *)fresh.data, (void *)src->data, bytes);
|
||||
fresh.len = src->len;
|
||||
if (out->raw)
|
||||
free((void *)out->raw);
|
||||
*out = fresh;
|
||||
return 0;
|
||||
}
|
||||
SPL_INLINE void vm_vec_drop(void *vv) {
|
||||
spl_vec_t *v = (spl_vec_t *)vv;
|
||||
if (v)
|
||||
spl_vec_release(v);
|
||||
}
|
||||
|
||||
/* ---- Map ---- */
|
||||
typedef struct {
|
||||
spl_vec_t keys;
|
||||
spl_vec_t vals;
|
||||
spl_vec_t states;
|
||||
usize size;
|
||||
usize key_size;
|
||||
usize val_size;
|
||||
usize hash_kind;
|
||||
} spl_map_t;
|
||||
|
||||
SPL_INLINE usize spl_hash_usize_value(usize value) {
|
||||
usize prime = 1099511628211ull;
|
||||
usize 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;
|
||||
}
|
||||
SPL_INLINE usize spl_hash_bytes(const void *data, usize len) {
|
||||
const unsigned char *p = (const unsigned char *)data;
|
||||
usize h = 14695981039346656037ull;
|
||||
usize prime = 1099511628211ull;
|
||||
usize i;
|
||||
for (i = 0; i < len; i++)
|
||||
h = (h ^ (usize)p[i]) * prime;
|
||||
return h;
|
||||
}
|
||||
SPL_INLINE usize spl_map_hash(const spl_map_t *m, const void *key) {
|
||||
const usize *v;
|
||||
if (m->hash_kind == 0)
|
||||
return spl_hash_usize_value(*(const usize *)key);
|
||||
if (m->hash_kind == 2)
|
||||
return 3;
|
||||
v = (const usize *)key;
|
||||
return spl_hash_bytes((const void *)v[0], v[1]);
|
||||
}
|
||||
SPL_INLINE int spl_map_eq(const spl_map_t *m, const void *a, const void *b) {
|
||||
const usize *x;
|
||||
const usize *y;
|
||||
if (m->hash_kind == 0 || m->hash_kind == 2)
|
||||
return *(const usize *)a == *(const usize *)b;
|
||||
x = (const usize *)a;
|
||||
y = (const usize *)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;
|
||||
}
|
||||
SPL_INLINE 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;
|
||||
}
|
||||
SPL_INLINE usize spl_map_find_slot(const spl_map_t *m, const void *key) {
|
||||
usize mask, i, probes;
|
||||
unsigned char *st;
|
||||
if (m->states.cap == 0)
|
||||
return ~(usize)0;
|
||||
mask = m->states.cap - 1;
|
||||
i = spl_map_hash(m, key) & mask;
|
||||
st = (unsigned char *)m->states.data;
|
||||
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 ~(usize)0;
|
||||
}
|
||||
SPL_INLINE void spl_map_place(spl_map_t *m, const void *key, const void *val) {
|
||||
usize mask, i, first_deleted, probes;
|
||||
unsigned char *st;
|
||||
mask = m->states.cap - 1;
|
||||
i = spl_map_hash(m, key) & mask;
|
||||
st = (unsigned char *)m->states.data;
|
||||
first_deleted = ~(usize)0;
|
||||
probes = 0;
|
||||
while (probes < m->states.cap && st[i] != 0) {
|
||||
if (st[i] == 2 && first_deleted == ~(usize)0)
|
||||
first_deleted = i;
|
||||
i = (i + 1) & mask;
|
||||
probes++;
|
||||
}
|
||||
if (first_deleted != ~(usize)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++;
|
||||
}
|
||||
SPL_INLINE int spl_map_reserve(spl_map_t *m, usize need) {
|
||||
usize new_cap = 0, i;
|
||||
spl_map_t fresh;
|
||||
int rc;
|
||||
if (!spl_map_valid(m))
|
||||
return 1;
|
||||
if (need <= m->states.cap)
|
||||
return 0;
|
||||
spl_grow(m->states.cap, need, &new_cap);
|
||||
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;
|
||||
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 (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;
|
||||
}
|
||||
SPL_INLINE i32 vm_map_valid(void *mm) {
|
||||
const spl_map_t *m = (const spl_map_t *)mm;
|
||||
return m ? spl_map_valid(m) : 0;
|
||||
}
|
||||
SPL_INLINE i32 vm_map_reserve(void *mm, u64 need) {
|
||||
spl_map_t *m = (spl_map_t *)mm;
|
||||
if (!m)
|
||||
return 1;
|
||||
return spl_map_reserve(m, need);
|
||||
}
|
||||
SPL_INLINE i32 vm_map_put(void *mm, const void *key, const void *val) {
|
||||
spl_map_t *m = (spl_map_t *)mm;
|
||||
usize existing;
|
||||
if (!m || !key || !val || !spl_map_valid(m))
|
||||
return 1;
|
||||
existing = spl_map_find_slot(m, key);
|
||||
if (existing != ~(usize)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 {
|
||||
usize next_size, scaled;
|
||||
if (m->size > ~(usize)0 - 1)
|
||||
return 3;
|
||||
next_size = m->size + 1;
|
||||
if (next_size > ~(usize)0 / 128)
|
||||
return 3;
|
||||
scaled = next_size * 128;
|
||||
if (scaled / m->states.cap >= 70) {
|
||||
int rc;
|
||||
if (m->states.cap > ~(usize)0 / 2)
|
||||
return 3;
|
||||
rc = spl_map_reserve(m, m->states.cap * 2);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
spl_map_place(m, key, val);
|
||||
return 0;
|
||||
}
|
||||
SPL_INLINE i32 vm_map_get(void *mm, const void *key, void *out) {
|
||||
spl_map_t *m = (spl_map_t *)mm;
|
||||
usize i;
|
||||
if (!m || !key || !out || !spl_map_valid(m))
|
||||
return 1;
|
||||
i = spl_map_find_slot(m, key);
|
||||
if (i == ~(usize)0)
|
||||
return 1;
|
||||
memcpy(out, (void *)(m->vals.data + i * m->val_size), m->val_size);
|
||||
return 0;
|
||||
}
|
||||
SPL_INLINE i32 vm_map_del(void *mm, const void *key) {
|
||||
spl_map_t *m = (spl_map_t *)mm;
|
||||
usize i;
|
||||
if (!m || !key || !spl_map_valid(m))
|
||||
return 1;
|
||||
i = spl_map_find_slot(m, key);
|
||||
if (i == ~(usize)0)
|
||||
return 1;
|
||||
((unsigned char *)m->states.data)[i] = 2;
|
||||
m->size--;
|
||||
return 0;
|
||||
}
|
||||
SPL_INLINE void vm_map_clear(void *mm) {
|
||||
spl_map_t *m = (spl_map_t *)mm;
|
||||
if (!m)
|
||||
return;
|
||||
if (m->states.data && m->states.len)
|
||||
memset((void *)m->states.data, 0, m->states.len);
|
||||
m->size = 0;
|
||||
}
|
||||
SPL_INLINE void vm_map_drop(void *mm) {
|
||||
spl_map_t *m = (spl_map_t *)mm;
|
||||
if (!m)
|
||||
return;
|
||||
spl_vec_release(&m->keys);
|
||||
spl_vec_release(&m->vals);
|
||||
spl_vec_release(&m->states);
|
||||
m->size = 0;
|
||||
}
|
||||
|
||||
/* ---- file ---- */
|
||||
SPL_INLINE void *vm_fopen(void *path, void *mode) {
|
||||
return (void *)fopen((const char *)path, (const char *)mode);
|
||||
}
|
||||
SPL_INLINE i32 vm_fclose(void *f) { return f ? fclose((FILE *)f) : 0; }
|
||||
SPL_INLINE u64 vm_fread(void *buf, u64 sz, u64 n, void *f) {
|
||||
return f ? (u64)fread(buf, (size_t)sz, (size_t)n, (FILE *)f) : 0;
|
||||
}
|
||||
SPL_INLINE u64 vm_fwrite(void *buf, u64 sz, u64 n, void *f) {
|
||||
return f ? (u64)fwrite(buf, (size_t)sz, (size_t)n, (FILE *)f) : 0;
|
||||
}
|
||||
SPL_INLINE u64 vm_fsize(void *f) {
|
||||
long cur, end;
|
||||
if (!f)
|
||||
return 0;
|
||||
cur = ftell((FILE *)f);
|
||||
if (cur < 0)
|
||||
return 0;
|
||||
if (fseek((FILE *)f, 0, SEEK_END) != 0)
|
||||
return 0;
|
||||
end = ftell((FILE *)f);
|
||||
fseek((FILE *)f, cur, SEEK_SET);
|
||||
return end < 0 ? 0 : (u64)end;
|
||||
}
|
||||
SPL_INLINE void *vm_stdin(void) { return (void *)stdin; }
|
||||
SPL_INLINE void *vm_stdout(void) { return (void *)stdout; }
|
||||
SPL_INLINE void *vm_stderr(void) { return (void *)stderr; }
|
||||
SPL_INLINE i32 vm_putchar(i32 c) { return fputc(c, stdout); }
|
||||
SPL_INLINE i32 vm_getchar(void) { return fgetc(stdin); }
|
||||
|
||||
/* ---- misc ---- */
|
||||
SPL_INLINE void vm_exit(i32 code) { exit(code); }
|
||||
SPL_INLINE u64 vm_timestamp(u64 kind) {
|
||||
time_t sec = time(NULL);
|
||||
if (kind == 1)
|
||||
return (u64)sec;
|
||||
if (kind == 2)
|
||||
return 0;
|
||||
if (kind == 4)
|
||||
return (u64)sec * 1000000000ull;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- 内嵌 VM(comptime):实现在 spl_vm_embed.c,链接 stage0 VM ---- */
|
||||
#define SPL_VM_EMBED_IMPLIMENTATION
|
||||
#include "spl_vm_embed.h"
|
||||
|
||||
#endif /* __SPL_RT_H__ */
|
||||
Reference in new issue
Block a user