Compare commits
27
Commits
54425a50c7
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc2d8f8db4 | ||
|
|
76f980654c | ||
|
|
0e38858785 | ||
|
|
40cffa9cb5 | ||
|
|
0b8f649818 | ||
|
|
c0a86ea552 | ||
|
|
fae017b52d | ||
|
|
9ecb92cb03 | ||
|
|
70085355db | ||
|
|
d10578d267 | ||
|
|
51d408d524 | ||
|
|
846f441e3a | ||
|
|
e02b0c633a | ||
|
|
b5a7a811b3 | ||
|
|
bb9d0a9f18 | ||
|
|
c30143609a | ||
|
|
b3e38ba7e4 | ||
|
|
16a1f030d4 | ||
|
|
4141e7d574 | ||
|
|
fb0ff9b663 | ||
|
|
70ac7380e9 | ||
|
|
73c9a40abd | ||
|
|
b8e2bba140 | ||
|
|
1a6df012cd | ||
|
|
5de4937637 | ||
|
|
2594a81034 | ||
|
|
acfbbeee8b |
No files matched your search
+9
-2
@@ -32,7 +32,7 @@ VarDecl = { DeclHeader ~ KEYWORD_var ~ VarBody }
|
||||
ConstDecl = { DeclHeader ~ KEYWORD_const ~ VarBody }
|
||||
VarBody = { Pattern ~ (COLON ~ TypeExpr)? ~ (EQUAL ~ Expr)? }
|
||||
|
||||
TypeDecl = { DeclHeader ~ KEYWORD_type ~ IDENTIFIER ~ EQUAL ~ TypeExpr }
|
||||
TypeDecl = { DeclHeader ~ KEYWORD_type ~ Pattern ~ EQUAL ~ TypeExpr }
|
||||
|
||||
MemberDecl = { DeclHeader ~ IDENTIFIER ~ (COLON ~ TypeExpr)? ~ (EQUAL ~ Expr)? }
|
||||
|
||||
@@ -109,6 +109,10 @@ PostfixExpr = {
|
||||
|
||||
ExprList = { (Expr ~ COMMA)* ~ Expr? }
|
||||
|
||||
// 注:类型即表达式**仅限 comptime 上下文**(`comptime { ... }`、`comptime expr`、
|
||||
// `comptime fn` 的函数体)内。在这些上下文中,PrimaryExpr 额外接受
|
||||
// `AggregateTypeExpr`(如 `struct { ... }`)以及 `type` 元类型;其它上下文
|
||||
// 中类型只允许出现在 TypeExpr 位置。
|
||||
PrimaryExpr = {
|
||||
FLOAT_LITERAL
|
||||
| INTEGER_LITERAL
|
||||
@@ -214,7 +218,9 @@ TypePostfixExpr = {
|
||||
}
|
||||
|
||||
TypePrimary = {
|
||||
IDENTIFIER
|
||||
AT ~ IDENTIFIER // @self 等自引用/内置类型
|
||||
| ComptimeCallExpr // @import("...") 等导入即类型
|
||||
| IDENTIFIER
|
||||
| KEYWORD_i8
|
||||
| KEYWORD_u8
|
||||
| KEYWORD_i16
|
||||
@@ -230,6 +236,7 @@ TypePrimary = {
|
||||
| KEYWORD_bool
|
||||
| KEYWORD_void
|
||||
| KEYWORD_ptr
|
||||
| KEYWORD_type // `type` 元类型(编译期类型的值类型)
|
||||
| FnTypeExpr
|
||||
| AggregateTypeExpr
|
||||
| TypeParenExpr
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
|
||||
#define SPL_CFLAGS "-Wall", "-Wextra", "-O0", "-g", "-D_CRT_SECURE_NO_WARNINGS"
|
||||
#define COMMON_CFLAGS "-Wall", "-Wextra", "-D_CRT_SECURE_NO_WARNINGS"
|
||||
#define SPL_CFLAGS COMMON_CFLAGS, "-O0", "-g"
|
||||
#define SPL_OPTIMIZATION_FLAG COMMON_CFLAGS, "-O2"
|
||||
#define nob_cc_flags(cmd) nob_cmd_append(cmd, SPL_CFLAGS)
|
||||
#undef UNICODE
|
||||
#define NOB_IMPLEMENTATION
|
||||
#include "nob.h"
|
||||
#include "nob/nob.h"
|
||||
#undef NOB_IMPLEMENTATION
|
||||
#include "nob_popen.h"
|
||||
#include "nob/nob_popen.h"
|
||||
#ifdef _WIN32
|
||||
#include <consoleapi2.h>
|
||||
#endif
|
||||
@@ -17,52 +19,7 @@
|
||||
// object files get rebuilt.
|
||||
static const char *CFLAGS_STAMP = BUILD_FOLDER ".cflags";
|
||||
|
||||
/* clang-format off */
|
||||
#define VM_SRCS "stage0/spl_mcode.c", "stage0/spl_syscall.c", "stage0/spl_vm.c",
|
||||
static const char *spl_cli_srcs[] = {"stage0/spl_cli.c", VM_SRCS};
|
||||
static const char *test_srcs[] = {"stage0/test_spl_vm.c", VM_SRCS};
|
||||
static const char *splc0_srcs[] = {
|
||||
"stage1/splc0.c",
|
||||
"stage1/spl_diag.c",
|
||||
"stage1/spl_tok.c",
|
||||
"stage1/spl_lexer.c",
|
||||
"stage1/spl_ast.c",
|
||||
"stage1/spl_parser.c",
|
||||
"stage1/spl_dumptree.c",
|
||||
"stage1/spl_type.c",
|
||||
"stage1/spl_lang_type.c",
|
||||
"stage1/spl_attr.c",
|
||||
"stage1/spl_scope.c",
|
||||
"stage1/spl_sema.c",
|
||||
"stage1/spl_intrin.c",
|
||||
"stage1/spl_ir.c",
|
||||
"stage1/spl_ast2ir.c",
|
||||
"stage1/spl_layout.c",
|
||||
"stage1/spl_ir2vm.c",
|
||||
"stage0/spl_mcode.c",
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const char **srcs;
|
||||
size_t srcs_count;
|
||||
} target_t;
|
||||
|
||||
static target_t exe_targets[] = {
|
||||
{"spl_cli", spl_cli_srcs, NOB_ARRAY_LEN(spl_cli_srcs)},
|
||||
{"test", test_srcs, NOB_ARRAY_LEN(test_srcs)},
|
||||
{"splc0", splc0_srcs, NOB_ARRAY_LEN(splc0_srcs)},
|
||||
};
|
||||
|
||||
static const target_t *find_exe_target(const char *name) {
|
||||
for (size_t i = 0; i < NOB_ARRAY_LEN(exe_targets); ++i) {
|
||||
if (strcmp(exe_targets[i].name, name) == 0) {
|
||||
return &exe_targets[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#include "nob/nob_data.h"
|
||||
|
||||
static const char *obj_path(const char *src) {
|
||||
// "stage0/spl_vm.c" -> "build/stage0_spl_vm.o"
|
||||
@@ -78,6 +35,22 @@ static const char *obj_path(const char *src) {
|
||||
return out;
|
||||
}
|
||||
|
||||
// Windows 上 clang/gcc 会输出 build/foo.exe;这里统一带上后缀,
|
||||
// 否则 nob_needs_rebuild 找不到输出会每次重新链接。
|
||||
static const char *exe_path(const char *name) {
|
||||
#ifdef _WIN32
|
||||
return nob_temp_sprintf(BUILD_FOLDER "%s.exe", name);
|
||||
#else
|
||||
return nob_temp_sprintf(BUILD_FOLDER "%s", name);
|
||||
#endif
|
||||
}
|
||||
|
||||
static const char *sir_path(const char *name) {
|
||||
return nob_temp_sprintf(BUILD_FOLDER "%s.sir", name);
|
||||
}
|
||||
|
||||
static const char *c_path(const char *name) { return nob_temp_sprintf(BUILD_FOLDER "%s.c", name); }
|
||||
|
||||
static bool sync_cflags_stamp(void) {
|
||||
Nob_String_View cflags = nob_sv_from_cstr(CFLAGS_STR);
|
||||
Nob_String_Builder old = {0};
|
||||
@@ -98,77 +71,188 @@ static bool sync_cflags_stamp(void) {
|
||||
static bool prepare_build(void) {
|
||||
if (!nob_mkdir_if_not_exists(BUILD_FOLDER))
|
||||
return false;
|
||||
return sync_cflags_stamp();
|
||||
if (!sync_cflags_stamp())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool build_object(const char *src) {
|
||||
typedef struct {
|
||||
const char **items;
|
||||
size_t count;
|
||||
size_t capacity;
|
||||
} str_da_t;
|
||||
|
||||
bool run_target(const char *name, Nob_Cmd *args);
|
||||
const char *build_target(const char *name);
|
||||
|
||||
static const char *build_exe(build_t *target) {
|
||||
Nob_Cmd cmd = {0};
|
||||
str_da_t objs = {0};
|
||||
str_da_t deps = {0};
|
||||
for (size_t i = 0; i < target->exe.srcs_count; ++i) {
|
||||
const char *src = target->exe.srcs[i];
|
||||
const char *obj = obj_path(src);
|
||||
if (!nob_file_exists(src)) {
|
||||
nob_log(NOB_ERROR, "source file `%s` does not exist", src);
|
||||
return false;
|
||||
goto defer;
|
||||
}
|
||||
|
||||
const char *inputs[] = {src, CFLAGS_STAMP};
|
||||
int needs = nob_needs_rebuild(obj, inputs, NOB_ARRAY_LEN(inputs));
|
||||
if (needs < 0)
|
||||
return false;
|
||||
if (needs == 0)
|
||||
return true;
|
||||
da_append(&objs, obj);
|
||||
|
||||
Nob_Cmd cmd = {0};
|
||||
da_append(&deps, src);
|
||||
da_append(&deps, CFLAGS_STAMP);
|
||||
for (size_t j = 0; j < target->exe.headers_count; ++j) {
|
||||
da_append(&deps, target->exe.headers[j]);
|
||||
}
|
||||
|
||||
int needs = nob_needs_rebuild(obj, deps.items, deps.count);
|
||||
deps.count = 0; // 清空 deps,下一轮循环重新收集
|
||||
|
||||
if (needs < 0)
|
||||
goto defer;
|
||||
if (needs == 0)
|
||||
continue;
|
||||
nob_cc(&cmd);
|
||||
nob_cc_flags(&cmd);
|
||||
nob_cmd_append(&cmd, "-c");
|
||||
nob_cc_inputs(&cmd, src);
|
||||
nob_cc_output(&cmd, obj);
|
||||
return nob_cmd_run(&cmd);
|
||||
}
|
||||
|
||||
static bool link_exe(const target_t *target) {
|
||||
const char *exe = nob_temp_sprintf(BUILD_FOLDER "%s", target->name);
|
||||
const char **objs = (const char **)nob_temp_alloc(sizeof(const char *) * target->srcs_count);
|
||||
for (size_t i = 0; i < target->srcs_count; ++i) {
|
||||
objs[i] = obj_path(target->srcs[i]);
|
||||
if (!nob_cmd_run(&cmd)) {
|
||||
nob_log(NOB_ERROR, "failed to compile `%s`", src);
|
||||
goto defer;
|
||||
}
|
||||
cmd.count = 0; // 清空 cmd,下一轮循环重新构建
|
||||
}
|
||||
|
||||
int needs = nob_needs_rebuild(exe, objs, target->srcs_count);
|
||||
const char *exe = exe_path(target->name);
|
||||
|
||||
int needs = nob_needs_rebuild(exe, objs.items, objs.count);
|
||||
if (needs < 0)
|
||||
return false;
|
||||
goto defer;
|
||||
if (needs == 0)
|
||||
return true;
|
||||
return exe;
|
||||
|
||||
Nob_Cmd cmd = {0};
|
||||
nob_cc(&cmd);
|
||||
nob_cc_flags(&cmd);
|
||||
for (size_t i = 0; i < target->srcs_count; ++i) {
|
||||
nob_cc_inputs(&cmd, objs[i]);
|
||||
nob_cmd_append(&cmd, SPL_OPTIMIZATION_FLAG);
|
||||
for (size_t i = 0; i < objs.count; ++i) {
|
||||
nob_cc_inputs(&cmd, objs.items[i]);
|
||||
}
|
||||
nob_cc_output(&cmd, exe);
|
||||
return nob_cmd_run(&cmd);
|
||||
if (!nob_cmd_run(&cmd)) {
|
||||
goto defer;
|
||||
}
|
||||
|
||||
return exe;
|
||||
defer:
|
||||
nob_cmd_free(cmd);
|
||||
da_free(objs);
|
||||
da_free(deps);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool build_target(const char *name) {
|
||||
const target_t *target = find_exe_target(name);
|
||||
if (target == NULL) {
|
||||
nob_log(NOB_ERROR, "unknown target `%s`", name);
|
||||
return false;
|
||||
const char *build_target(const char *name) {
|
||||
if (!prepare_build())
|
||||
return NULL;
|
||||
for (size_t i = 0; i < NOB_ARRAY_LEN(targets); ++i) {
|
||||
if (name != NULL && strcmp(targets[i].name, name) != 0) {
|
||||
continue;
|
||||
}
|
||||
build_t *target = &targets[i];
|
||||
switch (target->kind) {
|
||||
case BUILD_EXE_WITH_OPTIMIZATION:
|
||||
case BUILD_EXE: {
|
||||
return build_exe(target);
|
||||
}
|
||||
case BUILD_SIR: {
|
||||
Nob_Cmd cmd = {0};
|
||||
const char *target_sir = sir_path(name);
|
||||
nob_cmd_append(&cmd, target->sir.src, target_sir);
|
||||
if (!run_target(target->sir.compiler, &cmd)) {
|
||||
nob_log(NOB_ERROR, "failed to build `%s`", name);
|
||||
nob_cmd_free(cmd);
|
||||
return NULL;
|
||||
}
|
||||
nob_cmd_free(cmd);
|
||||
return target_sir;
|
||||
}
|
||||
case BUILD_NATIVE: {
|
||||
Nob_Cmd cmd = {0};
|
||||
const char *target_exe = exe_path(name);
|
||||
const char *target_c = c_path(name);
|
||||
|
||||
// TODO need_rebuild
|
||||
int needs = nob_needs_rebuild(target_c, target->native.deps, target->native.deps_count);
|
||||
if (needs < 0)
|
||||
return NULL;
|
||||
if (needs != 0) {
|
||||
nob_cmd_append(&cmd, "--dump", "c", target->native.src, target_c);
|
||||
if (!run_target(target->native.compiler, &cmd)) {
|
||||
nob_log(NOB_ERROR, "failed to build `%s`", name);
|
||||
nob_cmd_free(cmd);
|
||||
return NULL;
|
||||
}
|
||||
cmd.count = 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < target->srcs_count; ++i) {
|
||||
if (!build_object(target->srcs[i])) {
|
||||
return false;
|
||||
needs = nob_needs_rebuild(target_exe, target->native.crt_deps,
|
||||
target->native.crt_deps_count);
|
||||
if (needs < 0)
|
||||
return NULL;
|
||||
if (needs == 0)
|
||||
return target_exe;
|
||||
|
||||
nob_cc(&cmd);
|
||||
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)) {
|
||||
nob_log(NOB_ERROR, "failed to build `%s`", name);
|
||||
nob_cmd_free(cmd);
|
||||
return NULL;
|
||||
}
|
||||
nob_cmd_free(cmd);
|
||||
return target_exe;
|
||||
}
|
||||
default:
|
||||
NOB_UNREACHABLE("build_t.kind");
|
||||
}
|
||||
}
|
||||
return link_exe(target);
|
||||
nob_log(NOB_ERROR, "unknown build target `%s`", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool build_all(void) {
|
||||
for (size_t i = 0; i < NOB_ARRAY_LEN(exe_targets); ++i) {
|
||||
if (!build_target(exe_targets[i].name)) {
|
||||
bool run_target_dry(const char *name, Nob_Cmd *args, Nob_Cmd *result) {
|
||||
for (size_t i = 0; i < NOB_ARRAY_LEN(runs); ++i) {
|
||||
if (strcmp(runs[i].name, name) != 0) {
|
||||
continue;
|
||||
}
|
||||
const char *exe = build_target(runs[i].name);
|
||||
const char *interpreter = runs[i].interpreter;
|
||||
if (interpreter) {
|
||||
const char *inter = build_target(interpreter);
|
||||
if (!exe) {
|
||||
nob_log(NOB_ERROR, "failed to build interpreter `%s` for `%s`", interpreter, name);
|
||||
return false;
|
||||
}
|
||||
nob_cmd_append(result, inter);
|
||||
}
|
||||
nob_cmd_append(result, exe);
|
||||
nob_cmd_extend(result, args);
|
||||
return true;
|
||||
}
|
||||
nob_log(NOB_ERROR, "unknown run target `%s`", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool run_target(const char *name, Nob_Cmd *args) {
|
||||
Nob_Cmd result = {0};
|
||||
if (!run_target_dry(name, args, &result)) {
|
||||
return false;
|
||||
}
|
||||
nob_log(NOB_INFO, "running `%s`", name);
|
||||
bool ok = nob_cmd_run(&result);
|
||||
nob_cmd_free(result);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void parse_expect(const char *name, Nob_Cmd_Result *expect) {
|
||||
@@ -225,7 +309,7 @@ static bool test_once(const char *fname, void *user_data) {
|
||||
}
|
||||
cmd.count = 0;
|
||||
|
||||
nob_cmd_append(&cmd, nob_temp_sprintf(BUILD_FOLDER "%s", compiler_path));
|
||||
nob_cmd_append(&cmd, exe_path(compiler_path));
|
||||
nob_cmd_append(&cmd, fname, target_sir);
|
||||
if (!nob_cmd_run(&cmd)) {
|
||||
return false;
|
||||
@@ -235,7 +319,7 @@ static bool test_once(const char *fname, void *user_data) {
|
||||
return 1;
|
||||
}
|
||||
cmd.count = 0;
|
||||
nob_cmd_append(&cmd, nob_temp_sprintf(BUILD_FOLDER "%s", cli_path));
|
||||
nob_cmd_append(&cmd, exe_path(cli_path));
|
||||
nob_cmd_append(&cmd, target_sir);
|
||||
// nob_cmd_run(&cmd);
|
||||
|
||||
@@ -267,36 +351,158 @@ static bool test_once_with_dump(const char *fname, void *user_data) {
|
||||
if (!build_target(target_name))
|
||||
return 1;
|
||||
Nob_Cmd cmd = {0};
|
||||
nob_cmd_append(&cmd, nob_temp_sprintf(BUILD_FOLDER "%s", target_name));
|
||||
nob_cmd_append(&cmd, exe_path(target_name));
|
||||
nob_cmd_append(&cmd, "--dump", (char *)user_data, fname, BUILD_FOLDER "t.sir");
|
||||
return nob_cmd_run(&cmd);
|
||||
}
|
||||
|
||||
typedef bool (*test_fn_t)(const char *fname, void *user_data);
|
||||
static bool run_test(Nob_Files *files, test_fn_t test_once, void *user_data) {
|
||||
const char *test_folder = "./tests/stage1/";
|
||||
// 由源路径派生构建产物路径:build/<basename>.sir
|
||||
static const char *artifact_sir(const char *src) {
|
||||
const char *base = src;
|
||||
for (const char *p = src; *p; ++p)
|
||||
if (*p == '/' || *p == '\\')
|
||||
base = p + 1;
|
||||
const char *dot = strrchr(base, '.');
|
||||
size_t len = dot ? (size_t)(dot - base) : strlen(base);
|
||||
return nob_temp_sprintf(BUILD_FOLDER "%.*s.sir", (int)len, base);
|
||||
}
|
||||
|
||||
if (!prepare_build()) {
|
||||
return 1;
|
||||
}
|
||||
static bool run_capture(Nob_Cmd *cmd, Nob_Cmd_Result *result) {
|
||||
Nob_String_Builder rendered = {0};
|
||||
nob_cmd_render(*cmd, &rendered);
|
||||
Nob_String_View sv = nob_sb_to_sv(rendered);
|
||||
bool ok = nob_cmd_capture_popen(nob_temp_sv_to_cstr(sv), result);
|
||||
nob_sb_free(rendered);
|
||||
return ok;
|
||||
}
|
||||
|
||||
Nob_File_Paths paths = {0};
|
||||
nob_read_entire_dir(test_folder, &paths);
|
||||
for (int i = 0; i < files->count; i += 1) {
|
||||
for (int j = 0; j < paths.count; j += 1) {
|
||||
String_View sv = sv_from_cstr(paths.items[j]);
|
||||
// printf("--- test " SV_Fmt " ---\n", SV_Arg(sv));
|
||||
if (!strstr(paths.items[j], files->items[i])) {
|
||||
continue;
|
||||
}
|
||||
const char *tname = nob_temp_sprintf("%s%s", test_folder, paths.items[j]);
|
||||
printf("--- test %s ---\n", tname);
|
||||
if (test_once(tname, user_data) == false)
|
||||
// diff 测试:对同一文件跑 `splc0 --dump <mode>` 与 `spl_cli splc1.sir --dump <mode>`,
|
||||
// 逐字节比较 stdout。
|
||||
static bool test_once_diff(const char *fname, void *user_data) {
|
||||
const char *mode = (const char *)user_data;
|
||||
if (!mode) {
|
||||
nob_log(NOB_ERROR, "diff test requires a mode: lex|ast|sema|ir|vm");
|
||||
return false;
|
||||
}
|
||||
if (!build_target("splc0"))
|
||||
return false;
|
||||
if (!build_target("spl_cli"))
|
||||
return false;
|
||||
if (!build_target("splc1"))
|
||||
return false;
|
||||
|
||||
Nob_Cmd c_cmd = {0};
|
||||
nob_cmd_append(&c_cmd, exe_path("splc0"), "--dump", (char *)mode, (char *)fname,
|
||||
BUILD_FOLDER "diff_c.sir");
|
||||
Nob_Cmd_Result c_res = {0};
|
||||
if (!run_capture(&c_cmd, &c_res)) {
|
||||
nob_cmd_result_free(c_res);
|
||||
return false;
|
||||
}
|
||||
|
||||
Nob_Cmd s_cmd = {0};
|
||||
nob_cmd_append(&s_cmd, exe_path("spl_cli"), BUILD_FOLDER "splc1.sir", "--dump", (char *)mode,
|
||||
(char *)fname);
|
||||
Nob_Cmd_Result s_res = {0};
|
||||
if (!run_capture(&s_cmd, &s_res)) {
|
||||
nob_cmd_result_free(c_res);
|
||||
nob_cmd_result_free(s_res);
|
||||
return false;
|
||||
}
|
||||
|
||||
Nob_String_View c_out = nob_sb_to_sv(c_res.out);
|
||||
Nob_String_View s_out = nob_sb_to_sv(s_res.out);
|
||||
bool same = nob_sv_eq(c_out, s_out);
|
||||
if (!same) {
|
||||
nob_log(NOB_ERROR, "diff mismatch: %s (c=%zu bytes, spl=%zu bytes)", fname, c_out.count,
|
||||
s_out.count);
|
||||
// 打印首个不相同行(按行比较)。
|
||||
Nob_String_View ca = c_out;
|
||||
Nob_String_View sa = s_out;
|
||||
size_t lineno = 1;
|
||||
while (ca.count > 0 || sa.count > 0) {
|
||||
Nob_String_View cl = nob_sv_chop_by_delim(&ca, '\n');
|
||||
Nob_String_View sl = nob_sv_chop_by_delim(&sa, '\n');
|
||||
if (!nob_sv_eq(cl, sl)) {
|
||||
nob_log(NOB_ERROR, " line %zu:", lineno);
|
||||
nob_log(NOB_ERROR, " c : %.*s", (int)cl.count, cl.data);
|
||||
nob_log(NOB_ERROR, " spl: %.*s", (int)sl.count, sl.data);
|
||||
break;
|
||||
}
|
||||
lineno++;
|
||||
}
|
||||
nob_log(NOB_ERROR, "--- c stderr/stdout merged above; exit=%d vs spl exit=%d ---",
|
||||
c_res.exit_code, s_res.exit_code);
|
||||
}
|
||||
|
||||
nob_cmd_result_free(c_res);
|
||||
nob_cmd_result_free(s_res);
|
||||
return same;
|
||||
}
|
||||
|
||||
static bool is_stage_name(const char *s) {
|
||||
return strcmp(s, "stage1") == 0 || strcmp(s, "stage2") == 0 || strcmp(s, "diff") == 0;
|
||||
}
|
||||
|
||||
static bool is_dump_mode(const char *s) {
|
||||
return strcmp(s, "lex") == 0 || strcmp(s, "ast") == 0 || strcmp(s, "sema") == 0 ||
|
||||
strcmp(s, "ir") == 0 || strcmp(s, "vm") == 0;
|
||||
}
|
||||
|
||||
static const char *stage_dir_of(const char *stage) {
|
||||
if (strcmp(stage, "stage2") == 0)
|
||||
return "./stage2/";
|
||||
return "./tests/stage1/";
|
||||
}
|
||||
|
||||
typedef bool (*test_fn_t)(const char *fname, void *user_data);
|
||||
typedef struct {
|
||||
Nob_Files *files;
|
||||
test_fn_t test_once;
|
||||
void *user_data;
|
||||
} test_walk_ctx_t;
|
||||
|
||||
static bool test_walk_entry(Nob_Walk_Entry entry) {
|
||||
if (entry.type != NOB_FILE_REGULAR)
|
||||
return true;
|
||||
|
||||
const char *dot = strrchr(entry.path, '.');
|
||||
if (!dot || strcmp(dot, ".spl") != 0)
|
||||
return true;
|
||||
|
||||
test_walk_ctx_t *ctx = (test_walk_ctx_t *)entry.data;
|
||||
bool run_all = (ctx->files->count == 0);
|
||||
bool matched = run_all;
|
||||
for (int i = 0; !matched && i < ctx->files->count; i += 1) {
|
||||
if (strstr(entry.path, ctx->files->items[i]))
|
||||
matched = true;
|
||||
}
|
||||
if (!matched)
|
||||
return true;
|
||||
|
||||
printf("--- test %s ---\n", entry.path);
|
||||
if (ctx->test_once(entry.path, ctx->user_data) == false)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool run_test(const char *stage, Nob_Files *files, test_fn_t test_once, void *user_data) {
|
||||
const char *test_folder = stage_dir_of(stage);
|
||||
|
||||
if (!prepare_build())
|
||||
return false;
|
||||
|
||||
if (!nob_file_exists(test_folder)) {
|
||||
nob_log(NOB_INFO, "%s does not exist, skip", test_folder);
|
||||
return true;
|
||||
}
|
||||
|
||||
test_walk_ctx_t ctx = {
|
||||
.files = files,
|
||||
.test_once = test_once,
|
||||
.user_data = user_data,
|
||||
};
|
||||
return nob_walk_dir(test_folder, test_walk_entry, .data = &ctx);
|
||||
}
|
||||
|
||||
static bool delete_walk_entry(Nob_Walk_Entry entry) { return nob_delete_file(entry.path); }
|
||||
@@ -313,22 +519,18 @@ static void print_help(const char *program_name) {
|
||||
nob_log(NOB_INFO, "Commands:");
|
||||
nob_log(NOB_INFO, " build [targets...] build all targets or the given ones");
|
||||
nob_log(NOB_INFO, " run <target> [args] build and run a target");
|
||||
nob_log(NOB_INFO, " test build and run VM unit tests");
|
||||
nob_log(NOB_INFO, " test <kind> build and run tests");
|
||||
nob_log(NOB_INFO, " clean remove build artifacts");
|
||||
nob_log(NOB_INFO, " list list all targets");
|
||||
nob_log(NOB_INFO, " help/-h/--help print this help message");
|
||||
nob_log(NOB_INFO, "%s", " ");
|
||||
nob_log(NOB_INFO, "Targets:");
|
||||
for (size_t i = 0; i < NOB_ARRAY_LEN(exe_targets); ++i) {
|
||||
nob_log(NOB_INFO, " %s", exe_targets[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
SetConsoleCP(CP_UTF8);
|
||||
#endif
|
||||
NOB_GO_REBUILD_URSELF_PLUS(argc, argv, "nob.h", "nob_popen.h");
|
||||
NOB_GO_REBUILD_URSELF_PLUS(argc, argv, "nob/nob.h", "nob/nob_popen.h", "nob/nob_data.h");
|
||||
set_log_handler(nob_cancer_log_handler);
|
||||
|
||||
const char *program_name = nob_shift(argv, argc);
|
||||
@@ -347,7 +549,7 @@ int main(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (!build_all())
|
||||
if (!build_target(NULL))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -359,44 +561,53 @@ int main(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
const char *target_name = nob_shift(argv, argc);
|
||||
if (!prepare_build())
|
||||
return 1;
|
||||
if (!build_target(target_name))
|
||||
return 1;
|
||||
Nob_Cmd cmd = {0};
|
||||
nob_cmd_append(&cmd, nob_temp_sprintf(BUILD_FOLDER "%s", target_name));
|
||||
Nob_Cmd runcmd = {0};
|
||||
while (argc > 0) {
|
||||
nob_cmd_append(&cmd, nob_shift(argv, argc));
|
||||
nob_cmd_append(&runcmd, nob_shift(argv, argc));
|
||||
}
|
||||
return nob_cmd_run(&cmd) ? 0 : 1;
|
||||
return !run_target(target_name, &runcmd);
|
||||
}
|
||||
|
||||
if (strcmp(command_name, "test") == 0) {
|
||||
Nob_Files files = {0};
|
||||
// 1. stage(可选,默认 stage1)
|
||||
const char *stage = "stage1";
|
||||
if (argc > 0 && is_stage_name(argv[0])) {
|
||||
stage = nob_shift(argv, argc);
|
||||
}
|
||||
|
||||
// 2. mode(可选)
|
||||
test_fn_t tfn = test_once;
|
||||
const char *mode_name = NULL;
|
||||
if (argc > 0) {
|
||||
mode_name = nob_shift(argv, argc);
|
||||
if (strcmp(mode_name, "lex") == 0 || strcmp(mode_name, "ast") == 0 ||
|
||||
strcmp(mode_name, "sema") == 0 || strcmp(mode_name, "ir") == 0 ||
|
||||
strcmp(mode_name, "vm") == 0) {
|
||||
if (is_dump_mode(argv[0])) {
|
||||
tfn = test_once_with_dump;
|
||||
} else if (strcmp(mode_name, "run") == 0) {
|
||||
// HACK
|
||||
mode_name = nob_shift(argv, argc);
|
||||
} else if (strcmp(argv[0], "run") == 0) {
|
||||
nob_shift(argv, argc);
|
||||
// HACK:只对 stage1 有意义,跑上次生成的 build/test.sir
|
||||
Nob_Cmd cmd = {0};
|
||||
nob_cmd_append(&cmd, BUILD_FOLDER "spl_cli", "-d");
|
||||
nob_cmd_append(&cmd, BUILD_FOLDER "test.sir");
|
||||
return nob_cmd_run(&cmd) ? 0 : 1;
|
||||
} else {
|
||||
da_append(&files, mode_name);
|
||||
}
|
||||
}
|
||||
|
||||
// diff 需要显式 mode,且使用专门的双编译器比较函数。
|
||||
if (strcmp(stage, "diff") == 0) {
|
||||
if (!mode_name) {
|
||||
nob_log(NOB_ERROR, "diff requires a mode: lex|ast|sema|ir|vm");
|
||||
return 1;
|
||||
}
|
||||
tfn = test_once_diff;
|
||||
}
|
||||
|
||||
// 3. 剩下的都当 pattern
|
||||
Nob_Files files = {0};
|
||||
while (argc > 0) {
|
||||
da_append(&files, nob_shift(argv, argc));
|
||||
}
|
||||
|
||||
return run_test(&files, tfn, (void *)mode_name) ? 0 : 1;
|
||||
return run_test(stage, &files, tfn, (void *)mode_name) ? 0 : 1;
|
||||
}
|
||||
|
||||
if (strcmp(command_name, "clean") == 0) {
|
||||
@@ -409,11 +620,8 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
if (strcmp(command_name, "list") == 0) {
|
||||
for (size_t i = 0; i < NOB_ARRAY_LEN(exe_targets); ++i) {
|
||||
nob_log(NOB_INFO, "%s:", exe_targets[i].name);
|
||||
for (size_t j = 0; j < exe_targets[i].srcs_count; ++j) {
|
||||
nob_log(NOB_INFO, " %s", exe_targets[i].srcs[j]);
|
||||
}
|
||||
for (size_t i = 0; i < NOB_ARRAY_LEN(targets); ++i) {
|
||||
nob_log(NOB_INFO, "%s:", targets[i].name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
File renamed without changes.
+298
@@ -0,0 +1,298 @@
|
||||
#ifndef __NOB_DATA_H__
|
||||
#define __NOB_DATA_H__
|
||||
|
||||
/* 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[] = {
|
||||
"stage1/splc0.c",
|
||||
"stage1/spl_diag.c",
|
||||
"stage1/spl_tok.c",
|
||||
"stage1/spl_lexer.c",
|
||||
"stage1/spl_ast.c",
|
||||
"stage1/spl_parser.c",
|
||||
"stage1/spl_dumptree.c",
|
||||
"stage1/spl_type.c",
|
||||
"stage1/spl_fold.c",
|
||||
"stage1/spl_lang_type.c",
|
||||
"stage1/spl_attr.c",
|
||||
"stage1/spl_scope.c",
|
||||
"stage1/spl_sema.c",
|
||||
"stage1/spl_intrin.c",
|
||||
"stage1/spl_ir.c",
|
||||
"stage1/spl_ast2ir.c",
|
||||
"stage1/spl_layout.c",
|
||||
"stage1/spl_ir2vm.c",
|
||||
"stage1/ir_opt.c",
|
||||
"stage1/vm_opt.c",
|
||||
"stage0/spl_mcode.c",
|
||||
};
|
||||
|
||||
static const char *splc1_crt_deps[] ={
|
||||
"stage0/spl_rt.h",
|
||||
"stage0/spl_vm_embed.h",
|
||||
// TODO
|
||||
};
|
||||
|
||||
static const char* splc1_deps[] = {
|
||||
"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_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[] = {
|
||||
"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 */
|
||||
|
||||
typedef struct build {
|
||||
const char *name;
|
||||
enum {
|
||||
BUILD_EXE,
|
||||
BUILD_EXE_WITH_OPTIMIZATION,
|
||||
BUILD_SIR,
|
||||
BUILD_NATIVE,
|
||||
} kind;
|
||||
union {
|
||||
struct {
|
||||
const char **srcs;
|
||||
size_t srcs_count;
|
||||
const char **headers;
|
||||
size_t headers_count;
|
||||
} exe;
|
||||
struct {
|
||||
const char *src;
|
||||
const char *compiler;
|
||||
const char **deps;
|
||||
size_t deps_count;
|
||||
} sir;
|
||||
struct {
|
||||
const char *src;
|
||||
const char *compiler;
|
||||
const char **deps;
|
||||
size_t deps_count;
|
||||
const char **crt_deps;
|
||||
size_t crt_deps_count;
|
||||
} native;
|
||||
};
|
||||
} build_t;
|
||||
|
||||
typedef struct run {
|
||||
const char *name;
|
||||
const char *interpreter;
|
||||
} run_t;
|
||||
|
||||
static build_t targets[] = {
|
||||
{
|
||||
"spl_cli",
|
||||
BUILD_EXE_WITH_OPTIMIZATION,
|
||||
.exe = {.srcs = spl_cli_srcs,
|
||||
.srcs_count = NOB_ARRAY_LEN(spl_cli_srcs),
|
||||
.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 = 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 = stage0_headers,
|
||||
.headers_count = NOB_ARRAY_LEN(stage0_headers)},
|
||||
},
|
||||
{
|
||||
"splc0",
|
||||
BUILD_EXE,
|
||||
.exe = {.srcs = splc0_srcs,
|
||||
.srcs_count = NOB_ARRAY_LEN(splc0_srcs),
|
||||
.headers = stage0_headers,
|
||||
.headers_count = NOB_ARRAY_LEN(stage0_headers)},
|
||||
},
|
||||
{
|
||||
"splc1",
|
||||
BUILD_SIR,
|
||||
.sir = {.src = "stage2/splc1.spl",
|
||||
.compiler = "splc0",
|
||||
.deps = splc1_deps,
|
||||
.deps_count = NOB_ARRAY_LEN(splc1_deps)},
|
||||
},
|
||||
{
|
||||
"splc1s",
|
||||
BUILD_SIR,
|
||||
.sir = {.src = "stage2/splc1.spl",
|
||||
.compiler = "splc1",
|
||||
.deps = splc1_deps,
|
||||
.deps_count = NOB_ARRAY_LEN(splc1_deps)},
|
||||
},
|
||||
{
|
||||
"splc1r",
|
||||
BUILD_SIR,
|
||||
.sir = {.src = "stage2/splc1.spl",
|
||||
.compiler = "splc1s",
|
||||
.deps = splc1_deps,
|
||||
.deps_count = NOB_ARRAY_LEN(splc1_deps)},
|
||||
},
|
||||
{
|
||||
"splc1n",
|
||||
BUILD_NATIVE,
|
||||
.native = {.src = "stage2/splc1.spl",
|
||||
.compiler = "splc1",
|
||||
.deps = splc1_deps,
|
||||
.deps_count = NOB_ARRAY_LEN(splc1_deps),
|
||||
.crt_deps = splc1_crt_deps,
|
||||
.crt_deps_count = NOB_ARRAY_LEN(splc1_crt_deps)},
|
||||
},
|
||||
{
|
||||
"splc2",
|
||||
BUILD_SIR,
|
||||
.sir = {.src = "stage3/splc2.spl",
|
||||
.compiler = "splc1n",
|
||||
.deps = splc2_deps,
|
||||
.deps_count = NOB_ARRAY_LEN(splc2_deps)},
|
||||
},
|
||||
{
|
||||
"splc2n",
|
||||
BUILD_NATIVE,
|
||||
.native = {.src = "stage3/splc2.spl",
|
||||
.compiler = "splc1n",
|
||||
.deps = splc2_deps,
|
||||
.deps_count = NOB_ARRAY_LEN(splc2_deps),
|
||||
.crt_deps = splc2_crt_deps,
|
||||
.crt_deps_count = NOB_ARRAY_LEN(splc2_crt_deps)},
|
||||
},
|
||||
};
|
||||
|
||||
static run_t runs[] = {
|
||||
{"spl_cli", NULL}, {"vm_test", NULL}, {"splc0", NULL},
|
||||
{"splc1", "spl_cli"}, {"splc1r", "spl_cli"}, {"splc1s", "spl_cli"},
|
||||
{"splc1n", NULL}, {"splc2", "spl_cli"}, {"splc2n", NULL},
|
||||
};
|
||||
|
||||
#endif /* __NOB_DATA_H__ */
|
||||
File renamed without changes.
@@ -9,8 +9,16 @@
|
||||
#ifndef nullptr
|
||||
#define nullptr NULL
|
||||
#endif
|
||||
|
||||
#ifndef SPL_USIZE_TYPEDEF
|
||||
#define SPL_USIZE_TYPEDEF
|
||||
typedef uintptr_t usize;
|
||||
#endif
|
||||
|
||||
#ifndef SPL_ISIZE_TYPEDEF
|
||||
#define SPL_ISIZE_TYPEDEF
|
||||
typedef intptr_t isize;
|
||||
#endif
|
||||
|
||||
#define MAP_TYPEOF __typeof__
|
||||
|
||||
@@ -202,33 +210,6 @@ static inline usize map_hash_str(const char *s) {
|
||||
_found; \
|
||||
}))
|
||||
|
||||
/**
|
||||
* 查询(续探):首次调用前将 *in_out_idx 置为 (usize)-1,
|
||||
* 则从键自身的 hash 桶开始查找;命中后 *out_val 被赋值、返回 1,
|
||||
* 且 *in_out_idx 更新为命中槽位,下次调用从该槽的下一个继续探测(碰撞续探)。
|
||||
* 扫描到空槽返回 0。用于 key 碰撞后需要回表二次比较的场景。
|
||||
*/
|
||||
#define map_get_continue(map, _key, out_val, in_out_idx) \
|
||||
(({ \
|
||||
int _found = 0; \
|
||||
if ((map).cap > 0) { \
|
||||
usize _mask = (map).cap - 1; \
|
||||
usize _idx = (*(in_out_idx) == (usize) - 1) ? (map).hash(_key) & _mask \
|
||||
: (*(in_out_idx) + 1) & _mask; \
|
||||
while ((map).data[_idx].state != __MAP_SLOT_EMPTY) { \
|
||||
if ((map).data[_idx].state == __MAP_SLOT_OCCUPIED && \
|
||||
(map).cmp((map).data[_idx].key, _key) == 0) { \
|
||||
*out_val = (map).data[_idx].val; \
|
||||
*(in_out_idx) = _idx; \
|
||||
_found = 1; \
|
||||
break; \
|
||||
} \
|
||||
_idx = (_idx + 1) & _mask; \
|
||||
} \
|
||||
} \
|
||||
_found; \
|
||||
}))
|
||||
|
||||
/**
|
||||
* 删除指定键
|
||||
*/
|
||||
|
||||
@@ -26,7 +26,10 @@
|
||||
#ifndef nullptr
|
||||
#define nullptr NULL
|
||||
#endif
|
||||
typedef size_t usize;
|
||||
#ifndef SPL_USIZE_TYPEDEF
|
||||
#define SPL_USIZE_TYPEDEF
|
||||
typedef uintptr_t usize;
|
||||
#endif
|
||||
#define __vec_realloc realloc
|
||||
#define __vec_free free
|
||||
#define __vec_memcpy memcpy
|
||||
|
||||
@@ -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__ */
|
||||
@@ -30,6 +30,10 @@ typedef struct {
|
||||
int debug_cli;
|
||||
int trace;
|
||||
int disasm;
|
||||
int profile;
|
||||
int profile_interval;
|
||||
const char *profile_out;
|
||||
int stats;
|
||||
const char *entry;
|
||||
const char *path; /* file.sir */
|
||||
const char **args; /* spl 程序参数(file.sir 之后) */
|
||||
@@ -43,6 +47,9 @@ static void usage(void) {
|
||||
" --disasm disasm dump sir\n"
|
||||
" --entry <name> entry function (default: main)\n"
|
||||
" --trace print every instruction\n"
|
||||
" --profile[=N] native sampling profiler (every N insns, default 10000)\n"
|
||||
" --profile-out <p> profile output prefix (default build/profile)\n"
|
||||
" --stats print executed instructions / call count at exit\n"
|
||||
" -h, --help show help\n");
|
||||
}
|
||||
|
||||
@@ -67,6 +74,19 @@ static int parse_args(int argc, const char **argv, cli_opts_t *o) {
|
||||
o->trace = 1;
|
||||
} else if (strcmp(a, "--disasm") == 0) {
|
||||
o->disasm = 1;
|
||||
} else if (strcmp(a, "--stats") == 0) {
|
||||
o->stats = 1;
|
||||
} else if (strcmp(a, "--profile") == 0) {
|
||||
o->profile = 1;
|
||||
} else if (strncmp(a, "--profile=", 10) == 0) {
|
||||
o->profile = 1;
|
||||
o->profile_interval = (int)strtol(a + 10, NULL, 10);
|
||||
} else if (strcmp(a, "--profile-out") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "spl_cli: --profile-out needs a prefix\n");
|
||||
return -1;
|
||||
}
|
||||
o->profile_out = argv[++i];
|
||||
} else if (strcmp(a, "--entry") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "spl_cli: --entry needs a name\n");
|
||||
@@ -640,6 +660,8 @@ int main(int argc, const char **argv) {
|
||||
if (o.debug) {
|
||||
fprintf(stderr, "spl_cli: debug mode on\n");
|
||||
spl_vm_set_debug(&vm, 1);
|
||||
} else {
|
||||
spl_vm_set_debug(&vm, 0);
|
||||
}
|
||||
if (o.trace) {
|
||||
fprintf(stderr, "spl_cli: trace mode on\n");
|
||||
@@ -651,6 +673,11 @@ int main(int argc, const char **argv) {
|
||||
spl_prog_drop(&prog);
|
||||
return 1;
|
||||
}
|
||||
if (o.profile) {
|
||||
usize interval = o.profile_interval > 0 ? (usize)o.profile_interval : 10000;
|
||||
spl_vm_profile_enable(&vm, interval);
|
||||
fprintf(stderr, "spl_cli: profile mode on (interval=%zu)\n", interval);
|
||||
}
|
||||
|
||||
/* spl 程序 argv:argv[0]=file.sir(程序名),argv[1..]=args */
|
||||
const char **spl_argv = (const char **)malloc(sizeof(char *) * (size_t)(o.nargs + 1));
|
||||
@@ -689,6 +716,22 @@ int main(int argc, const char **argv) {
|
||||
fprintf(stderr, "spl_cli: hit breakpoint (run with -g to debug)\n");
|
||||
}
|
||||
|
||||
if (o.stats) {
|
||||
fprintf(stderr, "spl_cli: stats steps=%llu calls=%llu\n", (unsigned long long)vm.steps,
|
||||
(unsigned long long)vm.ncalls);
|
||||
}
|
||||
|
||||
if (o.profile) {
|
||||
const char *prefix = o.profile_out ? o.profile_out : "build/profile";
|
||||
char folded[1024];
|
||||
char table[1024];
|
||||
snprintf(folded, sizeof folded, "%s.folded", prefix);
|
||||
snprintf(table, sizeof table, "%s.table", prefix);
|
||||
spl_vm_profile_dump_folded(&vm, folded);
|
||||
spl_vm_profile_dump_table(&vm, table);
|
||||
fprintf(stderr, "spl_cli: profile written to %s / %s\n", folded, table);
|
||||
}
|
||||
|
||||
free(spl_argv);
|
||||
spl_vm_drop(&vm);
|
||||
spl_prog_drop(&prog);
|
||||
|
||||
+41
-33
@@ -127,44 +127,20 @@ static inline void wr64(unsigned char **p, spl_vm_val_t v) {
|
||||
/* Round n up to next multiple of 8 */
|
||||
#define ALIGN8(n) (((n) + 7) & ~7)
|
||||
|
||||
int spl_prog_load_from_file(const char *fname, spl_prog_t *prog) {
|
||||
FILE *f;
|
||||
unsigned char *data;
|
||||
long len;
|
||||
static int spl_prog_parse(const unsigned char *data, usize len, spl_prog_t *prog) {
|
||||
const unsigned char *p;
|
||||
spl_vm_val_t nfuncs, ninsns, nnatives, nstrs, ndata;
|
||||
|
||||
if (!fname || !prog)
|
||||
if (!data || !prog)
|
||||
return -1;
|
||||
|
||||
f = fopen(fname, "rb");
|
||||
if (!f)
|
||||
if (len < 48)
|
||||
return -1;
|
||||
fseek(f, 0, SEEK_END);
|
||||
len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
if (len < 48) {
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
data = (unsigned char *)malloc((size_t)len);
|
||||
if (!data) {
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
if (fread(data, 1, (size_t)len, f) != (size_t)len) {
|
||||
free(data);
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
p = data;
|
||||
|
||||
/* magic */
|
||||
if (p[0] != 'S' || p[1] != 'P' || p[2] != 'L' || p[3] != 'B' || p[4] != 'I' || p[5] != 'N' ||
|
||||
p[6] != '\0' || p[7] != '\0') {
|
||||
free(data);
|
||||
return -1;
|
||||
}
|
||||
p += 8;
|
||||
@@ -186,7 +162,6 @@ int spl_prog_load_from_file(const char *fname, spl_prog_t *prog) {
|
||||
|
||||
func.name = (char *)malloc((usize)nlen);
|
||||
if (!func.name) {
|
||||
free(data);
|
||||
return -1;
|
||||
}
|
||||
memcpy(func.name, p, (usize)nlen);
|
||||
@@ -202,7 +177,6 @@ int spl_prog_load_from_file(const char *fname, spl_prog_t *prog) {
|
||||
/* ---- instructions ---- */
|
||||
for (spl_vm_val_t i = 0; i < ninsns; i++) {
|
||||
if ((size_t)(p - data) + 12 > (size_t)len) {
|
||||
free(data);
|
||||
return -1;
|
||||
}
|
||||
spl_vm_ins_t ins;
|
||||
@@ -221,7 +195,6 @@ int spl_prog_load_from_file(const char *fname, spl_prog_t *prog) {
|
||||
|
||||
nat.name = (char *)malloc((usize)nlen);
|
||||
if (!nat.name) {
|
||||
free(data);
|
||||
return -1;
|
||||
}
|
||||
memcpy(nat.name, p, (usize)nlen);
|
||||
@@ -238,7 +211,6 @@ int spl_prog_load_from_file(const char *fname, spl_prog_t *prog) {
|
||||
usize pad = ALIGN8((usize)slen) - (usize)slen;
|
||||
char *s = (char *)malloc((usize)slen + 1);
|
||||
if (!s) {
|
||||
free(data);
|
||||
return -1;
|
||||
}
|
||||
memcpy(s, p, (usize)slen);
|
||||
@@ -254,7 +226,6 @@ int spl_prog_load_from_file(const char *fname, spl_prog_t *prog) {
|
||||
usize pad = ALIGN8(entry.size) - entry.size;
|
||||
entry.data = (unsigned char *)malloc(entry.size);
|
||||
if (!entry.data) {
|
||||
free(data);
|
||||
return -1;
|
||||
}
|
||||
memcpy(entry.data, p, entry.size);
|
||||
@@ -279,10 +250,47 @@ int spl_prog_load_from_file(const char *fname, spl_prog_t *prog) {
|
||||
}
|
||||
}
|
||||
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spl_prog_load_from_file(const char *fname, spl_prog_t *prog) {
|
||||
FILE *f;
|
||||
unsigned char *data;
|
||||
long len;
|
||||
int rc;
|
||||
|
||||
if (!fname || !prog)
|
||||
return -1;
|
||||
f = fopen(fname, "rb");
|
||||
if (!f)
|
||||
return -1;
|
||||
fseek(f, 0, SEEK_END);
|
||||
len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
if (len < 48) {
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
data = (unsigned char *)malloc((size_t)len);
|
||||
if (!data) {
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
if (fread(data, 1, (size_t)len, f) != (size_t)len) {
|
||||
free(data);
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
fclose(f);
|
||||
rc = spl_prog_parse(data, (usize)len, prog);
|
||||
free(data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int spl_prog_load_from_buffer(const char *buffer, usize len, spl_prog_t *prog) {
|
||||
return spl_prog_parse((const unsigned char *)buffer, len, prog);
|
||||
}
|
||||
|
||||
int spl_prog_store_to_file(const char *fname, spl_prog_t *prog) {
|
||||
unsigned char *buf, *p;
|
||||
spl_vm_val_t i;
|
||||
|
||||
@@ -9,8 +9,15 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef SPL_USIZE_TYPEDEF
|
||||
#define SPL_USIZE_TYPEDEF
|
||||
typedef uintptr_t usize;
|
||||
#endif
|
||||
|
||||
#ifndef SPL_ISIZE_TYPEDEF
|
||||
#define SPL_ISIZE_TYPEDEF
|
||||
typedef intptr_t isize;
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
SPL_VOID,
|
||||
@@ -90,6 +97,7 @@ typedef enum {
|
||||
/* 间接内存访问 */ \
|
||||
X(SPL_LOAD, "load", 0, 1, 1, "load sizeof(type)-bits zero-extended") \
|
||||
X(SPL_STORE, "store", 0, 2, 0, "store low sizeof(type)-bits") \
|
||||
X(SPL_MEMCPY, "memcpy", 0, 3, 0, "copy n bytes: pop dst, src, n (VM address space)") \
|
||||
/* 原生接口 */ \
|
||||
X(SPL_NCALL, "ncall", 1, 0, 1, "call native function by index") \
|
||||
X(SPL_NLIB, "nlib", 1, 0, 0, "dlopen library (name idx)") \
|
||||
@@ -103,6 +111,7 @@ typedef enum {
|
||||
#define X(opcode, name, argc, pop, push, desc) opcode,
|
||||
SPL_OPCODES(X)
|
||||
#undef X
|
||||
SPL_OPCODE_COUNT,
|
||||
} spl_vm_opcode_t;
|
||||
|
||||
/*
|
||||
@@ -192,6 +201,7 @@ void spl_prog_drop(spl_prog_t *prog);
|
||||
void spl_prog_dump(spl_prog_t *prog);
|
||||
|
||||
int spl_prog_load_from_file(const char *fname, spl_prog_t *prog);
|
||||
int spl_prog_load_from_buffer(const char *buffer, usize len, spl_prog_t *prog);
|
||||
int spl_prog_store_to_file(const char *fname, spl_prog_t *prog);
|
||||
|
||||
int spl_prog_add_instr(spl_prog_t *prog, uint8_t opcode, uint8_t type, spl_vm_val_t imm);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#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"
|
||||
|
||||
#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__ */
|
||||
+630
-177
@@ -1,12 +1,3 @@
|
||||
/* spl_syscall.c - built-in syscall implementations + registration
|
||||
*
|
||||
* All syscalls validate nargs, cast spl_val_t args to the expected C types,
|
||||
* execute, and return the result as spl_val_t.
|
||||
*
|
||||
* Simple syscalls use the SYSCALL_N macros; complex ones are written
|
||||
* manually but follow the same template.
|
||||
*/
|
||||
|
||||
#include "spl_syscall.h"
|
||||
#include "include/core_map.h"
|
||||
#include "include/core_vec.h"
|
||||
@@ -16,16 +7,12 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
/* =================================================================
|
||||
* Macro templates
|
||||
*
|
||||
* Each SYSCALL_N macro:
|
||||
* 1. Validates nargs (prints error and returns -1 on mismatch)
|
||||
* 2. Casts args[n] from spl_val_t to the specified C type via
|
||||
* (type)(uintptr_t) - this handles both integer and pointer types
|
||||
* 3. Evaluates the expression and returns the result as spl_val_t
|
||||
* ================================================================= */
|
||||
/* VM 偏移 -> 宿主地址;size=0 表示不做边界检查。 */
|
||||
#define VM_X(_vm, off) ((void *)spl_vm_xlate((_vm), (spl_vm_val_t)(off), 0))
|
||||
#define VM_XN(_vm, off, n) ((void *)spl_vm_xlate((_vm), (spl_vm_val_t)(off), (usize)(n)))
|
||||
#define VM_ASP(_vm) spl_vm_asp(_vm)
|
||||
|
||||
#define CHECK_NARGS(fname, expected) \
|
||||
do { \
|
||||
@@ -81,9 +68,6 @@
|
||||
return (spl_vm_val_t)(uintptr_t)(ret_expr); \
|
||||
}
|
||||
|
||||
/* syscall / VM 内部统一地址检查宏。
|
||||
* 失败时打印 ctx + 地址 + size + ip,置 native_error,return 0。
|
||||
* 要求所在函数有可用的 _vm 变量。 */
|
||||
#define ADDRCHECK(vm, ptr, size, ctx) \
|
||||
do { \
|
||||
uintptr_t _acp = (uintptr_t)(ptr); \
|
||||
@@ -97,7 +81,6 @@
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* syscall 内部:把当前栈和调用栈打印出来,供出错时定位 */
|
||||
#define SYSCALL_DUMP(vm) \
|
||||
do { \
|
||||
fprintf(stderr, " --- syscall dump (sp=%zu fp=%zu cp=%zu ip=%zu) ---\n", (vm)->sp, \
|
||||
@@ -106,7 +89,6 @@
|
||||
spl_vm_stackdump((vm), (vm)->sp); \
|
||||
} while (0)
|
||||
|
||||
/* syscall 内部:报告致命错误,dump 后让 VM 停在当前 ip */
|
||||
#define SYSCALL_FAIL(vm, ...) \
|
||||
do { \
|
||||
fprintf(stderr, "syscall error: "); \
|
||||
@@ -117,153 +99,428 @@
|
||||
return 0; \
|
||||
} while (0)
|
||||
|
||||
/* =================================================================
|
||||
* 从共享核心 spl_rt_core.h 派生的 syscall 包装
|
||||
* ================================================================= */
|
||||
|
||||
#include "spl_rt_core.h"
|
||||
|
||||
#define SPL_WRAP1(name, fn, t1) \
|
||||
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
||||
(void)_vm; \
|
||||
CHECK_NARGS(#name, 1); \
|
||||
return (spl_vm_val_t)(uintptr_t)fn((t1)(uintptr_t)args[0]); \
|
||||
}
|
||||
|
||||
#define SPL_WRAP1_V(name, fn, t1) \
|
||||
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
||||
(void)_vm; \
|
||||
CHECK_NARGS(#name, 1); \
|
||||
fn((t1)(uintptr_t)args[0]); \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
#define SPL_WRAP2(name, fn, t1, t2) \
|
||||
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
||||
(void)_vm; \
|
||||
CHECK_NARGS(#name, 2); \
|
||||
return (spl_vm_val_t)(uintptr_t)fn((t1)(uintptr_t)args[0], (t2)(uintptr_t)args[1]); \
|
||||
}
|
||||
|
||||
#define SPL_WRAP3(name, fn, t1, t2, t3) \
|
||||
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
||||
(void)_vm; \
|
||||
CHECK_NARGS(#name, 3); \
|
||||
return (spl_vm_val_t)(uintptr_t)fn((t1)(uintptr_t)args[0], (t2)(uintptr_t)args[1], \
|
||||
(t3)(uintptr_t)args[2]); \
|
||||
}
|
||||
|
||||
#define SPL_WRAP4(name, fn, t1, t2, t3, t4) \
|
||||
static spl_vm_val_t name(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { \
|
||||
(void)_vm; \
|
||||
CHECK_NARGS(#name, 4); \
|
||||
return (spl_vm_val_t)(uintptr_t)fn((t1)(uintptr_t)args[0], (t2)(uintptr_t)args[1], \
|
||||
(t3)(uintptr_t)args[2], (t4)(uintptr_t)args[3]); \
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
* OS syscalls
|
||||
* ================================================================= */
|
||||
|
||||
/* vm_exit - terminate process with the given exit code */
|
||||
SYSCALL_1(vm_exit, int, (exit(a1), (spl_vm_val_t)0))
|
||||
|
||||
/* vm_putchar - write a single character to stdout */
|
||||
SYSCALL_1(vm_putchar, int, putchar(a1))
|
||||
|
||||
/* vm_getchar - read a single character from stdin */
|
||||
SYSCALL_0(vm_getchar, getchar())
|
||||
|
||||
/* vm_putint - print an integer to stdout */
|
||||
SYSCALL_1(vm_putint, int, (fprintf(stdout, "%d", a1), (spl_vm_val_t)0))
|
||||
|
||||
/* vm_putstr - print a string to stdout (no trailing newline) */
|
||||
SYSCALL_1(vm_putstr, const char *, (fputs(a1, stdout), (spl_vm_val_t)0))
|
||||
|
||||
/* vm_fopen - open a file, returns FILE* as spl_val_t */
|
||||
SYSCALL_2(vm_fopen, const char *, const char *, (uintptr_t)fopen(a1, a2))
|
||||
|
||||
/* vm_fclose - close a file, returns 0 on success */
|
||||
SYSCALL_1(vm_fclose, FILE *, fclose(a1))
|
||||
|
||||
/* vm_fread - read from file, returns number of items read */
|
||||
SYSCALL_4(vm_fread, void *, size_t, size_t, FILE *, fread(a1, a2, a3, a4))
|
||||
|
||||
/* vm_fwrite - write to file, returns number of items written */
|
||||
SYSCALL_4(vm_fwrite, const void *, size_t, size_t, FILE *, fwrite(a1, a2, a3, a4))
|
||||
|
||||
/* vm_fsize - get file size in bytes */
|
||||
static spl_vm_val_t vm_fsize(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)_vm;
|
||||
CHECK_NARGS("vm_fsize", 1);
|
||||
FILE *f = (FILE *)(uintptr_t)args[0];
|
||||
long cur = ftell(f);
|
||||
fseek(f, 0, SEEK_END);
|
||||
long sz = ftell(f);
|
||||
fseek(f, cur, SEEK_SET);
|
||||
return (spl_vm_val_t)(uintptr_t)sz;
|
||||
/* FILE* 存于 VM 空间,返回其偏移(不透明句柄)。 */
|
||||
static spl_vm_val_t vm_file_handle(spl_vm_t *vm, FILE *f) {
|
||||
spl_vm_val_t off;
|
||||
FILE **slot;
|
||||
if (!f)
|
||||
return 0;
|
||||
off = (spl_vm_val_t)(uintptr_t)spl_alloc_ex(VM_ASP(vm), sizeof(FILE *));
|
||||
if (off == 0)
|
||||
return 0;
|
||||
slot = (FILE **)VM_XN(vm, off, sizeof(FILE *));
|
||||
if (!slot)
|
||||
return 0;
|
||||
*slot = f;
|
||||
return off;
|
||||
}
|
||||
|
||||
SYSCALL_0(vm_stdin, stdin)
|
||||
SYSCALL_0(vm_stdout, stdout)
|
||||
SYSCALL_0(vm_stderr, stderr)
|
||||
static FILE *vm_file_get(spl_vm_t *vm, spl_vm_val_t off) {
|
||||
FILE **slot = (FILE **)VM_XN(vm, off, sizeof(FILE *));
|
||||
return slot ? *slot : NULL;
|
||||
}
|
||||
|
||||
static spl_vm_val_t vm_putstr(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
const char *s;
|
||||
CHECK_NARGS("vm_putstr", 1);
|
||||
s = (const char *)VM_X(_vm, args[0]);
|
||||
if (s)
|
||||
fputs(s, stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static spl_vm_val_t vm_fopen(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
const char *path, *mode;
|
||||
CHECK_NARGS("vm_fopen", 2);
|
||||
path = (const char *)VM_X(_vm, args[0]);
|
||||
mode = (const char *)VM_X(_vm, args[1]);
|
||||
return vm_file_handle(_vm, fopen(path, mode));
|
||||
}
|
||||
|
||||
static spl_vm_val_t vm_fclose(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
FILE *f;
|
||||
CHECK_NARGS("vm_fclose", 1);
|
||||
f = vm_file_get(_vm, args[0]);
|
||||
return f ? (spl_vm_val_t)fclose(f) : 0;
|
||||
}
|
||||
|
||||
static spl_vm_val_t vm_fread(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
void *buf;
|
||||
FILE *f;
|
||||
CHECK_NARGS("vm_fread", 4);
|
||||
buf = VM_X(_vm, args[0]);
|
||||
f = vm_file_get(_vm, args[3]);
|
||||
if (!buf || !f)
|
||||
return 0;
|
||||
return (spl_vm_val_t)fread(buf, (size_t)args[1], (size_t)args[2], f);
|
||||
}
|
||||
|
||||
static spl_vm_val_t vm_fwrite(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
const void *buf;
|
||||
FILE *f;
|
||||
CHECK_NARGS("vm_fwrite", 4);
|
||||
buf = VM_X(_vm, args[0]);
|
||||
f = vm_file_get(_vm, args[3]);
|
||||
if (!buf || !f)
|
||||
return 0;
|
||||
return (spl_vm_val_t)fwrite(buf, (size_t)args[1], (size_t)args[2], f);
|
||||
}
|
||||
|
||||
static spl_vm_val_t vm_fsize(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
FILE *f;
|
||||
long cur, sz;
|
||||
CHECK_NARGS("vm_fsize", 1);
|
||||
f = vm_file_get(_vm, args[0]);
|
||||
if (!f)
|
||||
return 0;
|
||||
cur = ftell(f);
|
||||
fseek(f, 0, SEEK_END);
|
||||
sz = ftell(f);
|
||||
fseek(f, cur, SEEK_SET);
|
||||
return (spl_vm_val_t)(uintptr_t)(sz < 0 ? 0 : sz);
|
||||
}
|
||||
|
||||
static spl_vm_val_t vm_std_handle(spl_vm_t *_vm, int which) {
|
||||
if (_vm->stdio_off[which] == 0) {
|
||||
FILE *f = which == 0 ? stdin : (which == 1 ? stdout : stderr);
|
||||
_vm->stdio_off[which] = vm_file_handle(_vm, f);
|
||||
}
|
||||
return _vm->stdio_off[which];
|
||||
}
|
||||
|
||||
static spl_vm_val_t vm_stdin(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)args;
|
||||
CHECK_NARGS("vm_stdin", 0);
|
||||
return vm_std_handle(_vm, 0);
|
||||
}
|
||||
static spl_vm_val_t vm_stdout(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)args;
|
||||
CHECK_NARGS("vm_stdout", 0);
|
||||
return vm_std_handle(_vm, 1);
|
||||
}
|
||||
static spl_vm_val_t vm_stderr(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)args;
|
||||
CHECK_NARGS("vm_stderr", 0);
|
||||
return vm_std_handle(_vm, 2);
|
||||
}
|
||||
|
||||
/* vm_read_file - read entire file into a malloc'd, null-terminated buffer */
|
||||
static spl_vm_val_t vm_read_file(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)_vm;
|
||||
CHECK_NARGS("vm_read_file", 1);
|
||||
const char *path = (const char *)(uintptr_t)args[0];
|
||||
const char *path = (const char *)VM_X(_vm, args[0]);
|
||||
FILE *f;
|
||||
long sz;
|
||||
spl_vm_val_t off;
|
||||
char *buf;
|
||||
if (path == nullptr) {
|
||||
fprintf(stderr, "filepath can't be null");
|
||||
return 0;
|
||||
}
|
||||
FILE *f = fopen(path, "rb");
|
||||
f = fopen(path, "rb");
|
||||
if (!f) {
|
||||
fprintf(stderr, "filepath %s can't be open", path);
|
||||
return 1;
|
||||
}
|
||||
fseek(f, 0, SEEK_END);
|
||||
long sz = ftell(f);
|
||||
sz = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
char *buf = (char *)malloc((size_t)sz + 1);
|
||||
off = (spl_vm_val_t)(uintptr_t)spl_alloc_ex(VM_ASP(_vm), (uintptr_t)sz + 1);
|
||||
if (off == 0) {
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
buf = (char *)VM_XN(_vm, off, (uintptr_t)sz + 1);
|
||||
if (!buf) {
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
{
|
||||
size_t nread = fread(buf, 1, (size_t)sz, f);
|
||||
fclose(f);
|
||||
buf[nread] = '\0';
|
||||
return (spl_vm_val_t)(uintptr_t)buf;
|
||||
}
|
||||
fclose(f);
|
||||
return off;
|
||||
}
|
||||
|
||||
static spl_vm_val_t vm_alloc(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
/* =================================================================
|
||||
* mem syscalls —— 使用 spl_rt_core.h 中的实现
|
||||
* ================================================================= */
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_alloc(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
uintptr_t sz;
|
||||
spl_vm_val_t p;
|
||||
CHECK_NARGS("vm_alloc", 1);
|
||||
size_t sz = (size_t)args[0];
|
||||
sz = (uintptr_t)args[0];
|
||||
if (sz == 0)
|
||||
SYSCALL_FAIL(_vm, "vm_alloc: zero size");
|
||||
if (sz > (size_t)1 << 32)
|
||||
SYSCALL_FAIL(_vm, "vm_alloc: size too large (%zu)", sz);
|
||||
void *p = malloc(sz);
|
||||
if (!p)
|
||||
SYSCALL_FAIL(_vm, "vm_alloc: malloc(%zu) failed", sz);
|
||||
return (spl_vm_val_t)(uintptr_t)p;
|
||||
if (sz > ((uintptr_t)1 << 32))
|
||||
SYSCALL_FAIL(_vm, "vm_alloc: size too large (%zu)", (size_t)sz);
|
||||
p = (spl_vm_val_t)(uintptr_t)spl_alloc_ex(VM_ASP(_vm), sz);
|
||||
if (p == 0)
|
||||
SYSCALL_FAIL(_vm, "vm_alloc: alloc(%zu) failed", (size_t)sz);
|
||||
return p;
|
||||
}
|
||||
|
||||
static spl_vm_val_t vm_free(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)_vm;
|
||||
static spl_vm_val_t spl_sys_vm_free(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_free", 1);
|
||||
void *p = (void *)(uintptr_t)args[0];
|
||||
if (p)
|
||||
free(p);
|
||||
spl_free_ex(VM_ASP(_vm), (void *)(uintptr_t)args[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static spl_vm_val_t vm_realloc(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
static spl_vm_val_t spl_sys_vm_realloc(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
uintptr_t sz;
|
||||
spl_vm_val_t np;
|
||||
CHECK_NARGS("vm_realloc", 2);
|
||||
void *p = (void *)(uintptr_t)args[0];
|
||||
size_t sz = (size_t)args[1];
|
||||
if (!p)
|
||||
sz = (uintptr_t)args[1];
|
||||
if (args[0] == 0)
|
||||
SYSCALL_FAIL(_vm, "vm_realloc: NULL ptr");
|
||||
if (sz == 0)
|
||||
SYSCALL_FAIL(_vm, "vm_realloc: zero size");
|
||||
ADDRCHECK(_vm, p, 1, "vm_realloc");
|
||||
void *np = realloc(p, sz);
|
||||
if (!np)
|
||||
SYSCALL_FAIL(_vm, "vm_realloc: realloc(%p, %zu) failed", p, sz);
|
||||
return (spl_vm_val_t)(uintptr_t)np;
|
||||
np = (spl_vm_val_t)(uintptr_t)spl_realloc_ex(VM_ASP(_vm), (void *)(uintptr_t)args[0], sz);
|
||||
if (np == 0)
|
||||
SYSCALL_FAIL(_vm, "vm_realloc: realloc failed");
|
||||
return np;
|
||||
}
|
||||
|
||||
static spl_vm_val_t vm_memcpy(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
static spl_vm_val_t spl_sys_vm_memcpy(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
void *dst, *src;
|
||||
uintptr_t n;
|
||||
CHECK_NARGS("vm_memcpy", 3);
|
||||
void *dst = (void *)(uintptr_t)args[0];
|
||||
const void *src = (const void *)(uintptr_t)args[1];
|
||||
size_t size = (size_t)args[2];
|
||||
if (size == 0)
|
||||
return (spl_vm_val_t)(uintptr_t)dst;
|
||||
ADDRCHECK(_vm, dst, size, "vm_memcpy(dst)");
|
||||
ADDRCHECK(_vm, src, size, "vm_memcpy(src)");
|
||||
memcpy(dst, src, size);
|
||||
return (spl_vm_val_t)(uintptr_t)dst;
|
||||
n = (uintptr_t)args[2];
|
||||
if (n == 0)
|
||||
return args[0];
|
||||
dst = VM_X(_vm, args[0]);
|
||||
src = VM_X(_vm, args[1]);
|
||||
if (!dst || !src)
|
||||
SYSCALL_FAIL(_vm, "vm_memcpy: invalid address");
|
||||
vm_memcpy(dst, src, n);
|
||||
return args[0];
|
||||
}
|
||||
|
||||
static spl_vm_val_t vm_memcmp(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_memcmp", 3);
|
||||
const void *a = (const void *)(uintptr_t)args[0];
|
||||
const void *b = (const void *)(uintptr_t)args[1];
|
||||
size_t size = (size_t)args[2];
|
||||
if (size == 0)
|
||||
return 0;
|
||||
ADDRCHECK(_vm, a, size, "vm_memcmp(a)");
|
||||
ADDRCHECK(_vm, b, size, "vm_memcmp(b)");
|
||||
return (spl_vm_val_t)memcmp(a, b, size);
|
||||
static spl_vm_val_t spl_sys_vm_memmove(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
void *dst, *src;
|
||||
uintptr_t n;
|
||||
CHECK_NARGS("vm_memmove", 3);
|
||||
n = (uintptr_t)args[2];
|
||||
if (n == 0)
|
||||
return args[0];
|
||||
dst = VM_X(_vm, args[0]);
|
||||
src = VM_X(_vm, args[1]);
|
||||
if (!dst || !src)
|
||||
SYSCALL_FAIL(_vm, "vm_memmove: invalid address");
|
||||
vm_memmove(dst, src, n);
|
||||
return args[0];
|
||||
}
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_memcmp(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
const void *a, *b;
|
||||
uintptr_t n;
|
||||
CHECK_NARGS("vm_memcmp", 3);
|
||||
a = VM_X(_vm, args[0]);
|
||||
b = VM_X(_vm, args[1]);
|
||||
n = (uintptr_t)args[2];
|
||||
if (n && (!a || !b))
|
||||
SYSCALL_FAIL(_vm, "vm_memcmp: invalid address");
|
||||
return (spl_vm_val_t)(uintptr_t)vm_memcmp(a, b, n);
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
* Vec syscalls(参数按 VM 偏移解释)
|
||||
* ================================================================= */
|
||||
|
||||
#define VM_VEC_BYTES ((uintptr_t)sizeof(spl_vec_t))
|
||||
#define VM_MAP_BYTES ((uintptr_t)sizeof(spl_map_t))
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_vec_valid(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_vec_valid", 1);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_vec_valid_ex(VM_XN(_vm, args[0], VM_VEC_BYTES));
|
||||
}
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_vec_push_raw(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_vec_push_raw", 2);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_vec_push_raw_ex(
|
||||
VM_ASP(_vm), VM_XN(_vm, args[0], VM_VEC_BYTES), VM_X(_vm, args[1]));
|
||||
}
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_vec_push(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_vec_push", 2);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_vec_push_ex(
|
||||
VM_ASP(_vm), VM_XN(_vm, args[0], VM_VEC_BYTES), VM_X(_vm, args[1]));
|
||||
}
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_vec_get(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_vec_get", 3);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_vec_get_ex(
|
||||
VM_ASP(_vm), VM_XN(_vm, args[0], VM_VEC_BYTES), (uintptr_t)args[1],
|
||||
(void **)VM_XN(_vm, args[2], sizeof(void *)));
|
||||
}
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_vec_allocate(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_vec_allocate", 4);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_vec_allocate_out_ex(
|
||||
VM_ASP(_vm), VM_XN(_vm, args[0], VM_VEC_BYTES), (uintptr_t)args[1], (uintptr_t)args[2],
|
||||
(uintptr_t)args[3]);
|
||||
}
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_vec_reserve_raw(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_vec_reserve_raw", 2);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_vec_reserve_raw_void_ex(
|
||||
VM_ASP(_vm), VM_XN(_vm, args[0], VM_VEC_BYTES), (uintptr_t)args[1]);
|
||||
}
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_vec_append(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_vec_append", 3);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_vec_append_ex(
|
||||
VM_ASP(_vm), VM_XN(_vm, args[0], VM_VEC_BYTES), VM_X(_vm, args[1]), (uintptr_t)args[2]);
|
||||
}
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_vec_set(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_vec_set", 3);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_vec_set_ex(
|
||||
VM_ASP(_vm), VM_XN(_vm, args[0], VM_VEC_BYTES), (uintptr_t)args[1], VM_X(_vm, args[2]));
|
||||
}
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_vec_pop(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_vec_pop", 2);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_vec_pop_ex(VM_ASP(_vm),
|
||||
VM_XN(_vm, args[0], VM_VEC_BYTES),
|
||||
VM_X(_vm, args[1]));
|
||||
}
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_vec_clone(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_vec_clone", 2);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_vec_clone_ex(
|
||||
VM_ASP(_vm), VM_XN(_vm, args[0], VM_VEC_BYTES), VM_XN(_vm, args[1], VM_VEC_BYTES));
|
||||
}
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_vec_drop(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_vec_drop", 1);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_vec_drop_ex(VM_ASP(_vm),
|
||||
VM_XN(_vm, args[0], VM_VEC_BYTES));
|
||||
}
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_bytes_eq(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_bytes_eq", 4);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_bytes_eq_ex(VM_ASP(_vm), VM_X(_vm, args[0]),
|
||||
(uintptr_t)args[1], VM_X(_vm, args[2]),
|
||||
(uintptr_t)args[3]);
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
* Map syscalls
|
||||
* ================================================================= */
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_map_valid(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_map_valid", 1);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_map_valid_ex(VM_XN(_vm, args[0], VM_MAP_BYTES));
|
||||
}
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_map_reserve(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_map_reserve", 2);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_map_reserve_void_ex(
|
||||
VM_ASP(_vm), VM_XN(_vm, args[0], VM_MAP_BYTES), (uintptr_t)args[1]);
|
||||
}
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_map_put(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_map_put", 3);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_map_put_ex(
|
||||
VM_ASP(_vm), VM_XN(_vm, args[0], VM_MAP_BYTES), VM_X(_vm, args[1]), VM_X(_vm, args[2]));
|
||||
}
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_map_get(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_map_get", 3);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_map_get_ex(
|
||||
VM_ASP(_vm), VM_XN(_vm, args[0], VM_MAP_BYTES), VM_X(_vm, args[1]), VM_X(_vm, args[2]));
|
||||
}
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_map_del(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_map_del", 2);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_map_del_ex(
|
||||
VM_ASP(_vm), VM_XN(_vm, args[0], VM_MAP_BYTES), VM_X(_vm, args[1]));
|
||||
}
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_map_clear(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_map_clear", 1);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_map_clear_ex(VM_ASP(_vm),
|
||||
VM_XN(_vm, args[0], VM_MAP_BYTES));
|
||||
}
|
||||
|
||||
static spl_vm_val_t spl_sys_vm_map_drop(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_map_drop", 1);
|
||||
return (spl_vm_val_t)(uintptr_t)spl_map_drop_ex(VM_ASP(_vm),
|
||||
VM_XN(_vm, args[0], VM_MAP_BYTES));
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
* printf / timestamp
|
||||
* ================================================================= */
|
||||
|
||||
static spl_vm_val_t vm_printf(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)_vm;
|
||||
if (nargs <= 0)
|
||||
return 0;
|
||||
|
||||
/* args[0] 是 SPL 的 []u8 format 参数。VM 里 slice 作为参数传递时,
|
||||
栈上压的是"指向 slice 结构体的指针",结构为 {ptr:*u8, len:u64} */
|
||||
const spl_vm_val_t *fmt_slice = (const spl_vm_val_t *)args[0];
|
||||
const char *fmt = (const char *)fmt_slice[0];
|
||||
usize fmt_len = (usize)fmt_slice[1];
|
||||
const spl_vm_val_t *fmt_slice =
|
||||
(const spl_vm_val_t *)VM_XN(_vm, args[0], 2 * sizeof(spl_vm_val_t));
|
||||
const char *fmt;
|
||||
usize fmt_len;
|
||||
if (!fmt_slice)
|
||||
return 0;
|
||||
fmt = (const char *)VM_X(_vm, fmt_slice[0]);
|
||||
fmt_len = (usize)fmt_slice[1];
|
||||
if (!fmt && fmt_len)
|
||||
return 0;
|
||||
|
||||
if (nargs <= 1) {
|
||||
fwrite(fmt, 1, fmt_len, stdout);
|
||||
@@ -274,7 +531,7 @@ static spl_vm_val_t vm_printf(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
string_builder_t buffer;
|
||||
vec_init(buffer);
|
||||
|
||||
int arg_idx = 1; /* 0 是 format */
|
||||
int arg_idx = 1;
|
||||
char tmp_buf[32];
|
||||
|
||||
for (usize i = 0; i < fmt_len; ++i) {
|
||||
@@ -301,9 +558,11 @@ static spl_vm_val_t vm_printf(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
arg_idx++;
|
||||
break;
|
||||
case 's': {
|
||||
const spl_vm_val_t *s = (const spl_vm_val_t *)args[arg_idx];
|
||||
const char *p = (const char *)s[0];
|
||||
usize l = (usize)s[1];
|
||||
const spl_vm_val_t *s =
|
||||
(const spl_vm_val_t *)VM_XN(_vm, args[arg_idx], 2 * sizeof(spl_vm_val_t));
|
||||
const char *p = s ? (const char *)VM_X(_vm, s[0]) : NULL;
|
||||
usize l = s ? (usize)s[1] : 0;
|
||||
if (p)
|
||||
for (usize j = 0; j < l; ++j)
|
||||
vec_push(buffer, p[j]);
|
||||
arg_idx++;
|
||||
@@ -322,18 +581,91 @@ static spl_vm_val_t vm_printf(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
return (spl_vm_val_t)nargs;
|
||||
}
|
||||
|
||||
static spl_vm_val_t vm_timestamp(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_timestamp", 1);
|
||||
spl_vm_val_t arg = args[0];
|
||||
struct timespec ts;
|
||||
int has_time = (timespec_get(&ts, TIME_UTC) == TIME_UTC);
|
||||
switch (arg) {
|
||||
case 0:
|
||||
return has_time ? 1 : 0;
|
||||
case 1:
|
||||
return has_time ? (spl_vm_val_t)ts.tv_sec : 0;
|
||||
case 2:
|
||||
return has_time ? (spl_vm_val_t)ts.tv_nsec : 0;
|
||||
case 3:
|
||||
if (!_vm)
|
||||
return 0;
|
||||
return (spl_vm_val_t)_vm->steps;
|
||||
case 5:
|
||||
if (!_vm)
|
||||
return 0;
|
||||
return (spl_vm_val_t)_vm->ncalls;
|
||||
case 4:
|
||||
return has_time ? (spl_vm_val_t)((unsigned long long)ts.tv_sec * 1000000000ull +
|
||||
(unsigned long long)ts.tv_nsec)
|
||||
: 0;
|
||||
default:
|
||||
fprintf(stderr, "vm_timestamp: invalid arg %zu\n", (size_t)arg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
* VM syscalls (for comptime - compile-time code execution)
|
||||
*
|
||||
* These let SPL code create isolated sub-VMs, load compiled .sir
|
||||
* programs into them, push arguments, call functions, and run.
|
||||
* nested-VM syscalls
|
||||
* ================================================================= */
|
||||
|
||||
/* vm_new - create a new sub-VM instance, returns spl_vm_t* */
|
||||
/* 通用双向桥(配置层策略,非 VM 平台 API):
|
||||
* 在子 VM 上安装「未解析 native 处理器」,把子 VM 的未解析调用按名转发到
|
||||
* 宿主(调用者)VM 中由 `entry` 指定的函数。宿主函数收到:
|
||||
* (ctx, name_off, name_len, nested_vm, arg_base, nargs)
|
||||
* 平台/C 侧**纯转发**:不复制实参、不限元数、不了解类型;聚合读写由宿主用
|
||||
* vm_stack_get / vm_mem_read / vm_mem_alloc / vm_mem_write 自行完成。
|
||||
* `entry` 由宿主显式给出,平台不预设任何函数名。 */
|
||||
typedef struct {
|
||||
spl_vm_t *host;
|
||||
void *ctx;
|
||||
char entry[128];
|
||||
} spl_bridge_t;
|
||||
|
||||
static spl_vm_val_t spl_bridge_forward(spl_vm_t *nested, const spl_vm_call_t *call, void *user) {
|
||||
spl_bridge_t *b = (spl_bridge_t *)user;
|
||||
spl_vm_val_t name_off;
|
||||
spl_vm_val_t args[6];
|
||||
spl_vm_call_t back;
|
||||
usize n;
|
||||
usize arg_base;
|
||||
if (!b || !b->host || !call || !call->name || b->entry[0] == '\0')
|
||||
return 0;
|
||||
n = strlen(call->name);
|
||||
/* 名拷进宿主 VM 内存,宿主按偏移读取。 */
|
||||
name_off = spl_vm_mem_alloc(b->host, n + 1);
|
||||
if (!name_off)
|
||||
return 0;
|
||||
if (spl_vm_mem_write(b->host, name_off, call->name, n + 1) != 0)
|
||||
return 0;
|
||||
arg_base = spl_vm_call_arg_base(nested, call);
|
||||
if (arg_base == (usize)-1)
|
||||
return 0;
|
||||
args[0] = (spl_vm_val_t)(uintptr_t)b->ctx;
|
||||
args[1] = name_off;
|
||||
args[2] = (spl_vm_val_t)n;
|
||||
args[3] = (spl_vm_val_t)(uintptr_t)nested;
|
||||
args[4] = (spl_vm_val_t)arg_base;
|
||||
args[5] = (spl_vm_val_t)call->nargs;
|
||||
back.name = b->entry;
|
||||
back.args = args;
|
||||
back.nargs = 6;
|
||||
back.ret = 0;
|
||||
if (spl_vm_invoke(b->host, &back) != 0)
|
||||
return 0;
|
||||
return back.ret;
|
||||
}
|
||||
|
||||
static spl_vm_val_t vm_new(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)_vm;
|
||||
CHECK_NARGS("vm_new", 0);
|
||||
(void)args;
|
||||
(void)_vm;
|
||||
spl_vm_t *vm = (spl_vm_t *)malloc(sizeof(spl_vm_t));
|
||||
if (!vm)
|
||||
return 0;
|
||||
@@ -341,13 +673,43 @@ static spl_vm_val_t vm_new(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
return (spl_vm_val_t)(uintptr_t)vm;
|
||||
}
|
||||
|
||||
/* vm_drop - destroy a sub-VM and its loaded program */
|
||||
/* vm_set_foreign(vm, entry, ctx):宿主显式指定处理器函数名。 */
|
||||
static spl_vm_val_t sc_vm_set_foreign(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
spl_vm_t *vm;
|
||||
spl_bridge_t *b;
|
||||
const spl_vm_val_t *entry;
|
||||
const char *eptr;
|
||||
usize elen;
|
||||
CHECK_NARGS("vm_set_foreign", 3);
|
||||
vm = (spl_vm_t *)(uintptr_t)args[0];
|
||||
if (!vm || !_vm)
|
||||
return -1;
|
||||
entry = (const spl_vm_val_t *)VM_XN(_vm, args[1], 2 * sizeof(spl_vm_val_t));
|
||||
if (!entry)
|
||||
return -1;
|
||||
eptr = (const char *)VM_X(_vm, entry[0]);
|
||||
elen = (usize)entry[1];
|
||||
if (!eptr || elen == 0 || elen >= sizeof(((spl_bridge_t *)0)->entry))
|
||||
return -1;
|
||||
b = (spl_bridge_t *)malloc(sizeof(*b));
|
||||
if (!b)
|
||||
return -1;
|
||||
b->host = _vm;
|
||||
b->ctx = (void *)(uintptr_t)args[2];
|
||||
memcpy(b->entry, eptr, elen);
|
||||
b->entry[elen] = '\0';
|
||||
spl_vm_set_foreign(vm, spl_bridge_forward, b);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static spl_vm_val_t vm_drop(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)_vm;
|
||||
CHECK_NARGS("vm_drop", 1);
|
||||
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
||||
if (!vm)
|
||||
return 0;
|
||||
if (vm->foreign == spl_bridge_forward && vm->foreign_ctx)
|
||||
free(vm->foreign_ctx);
|
||||
if (vm->prog) {
|
||||
spl_prog_drop(vm->prog);
|
||||
free(vm->prog);
|
||||
@@ -358,12 +720,10 @@ static spl_vm_val_t vm_drop(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* vm_load - load a .sir file, register syscalls, return spl_prog_t* */
|
||||
static spl_vm_val_t vm_load(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)_vm;
|
||||
CHECK_NARGS("vm_load", 2);
|
||||
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
||||
const char *path = (const char *)(uintptr_t)args[1];
|
||||
const char *path = (const char *)VM_X(_vm, args[1]);
|
||||
if (!vm || !path)
|
||||
return -1;
|
||||
|
||||
@@ -383,7 +743,30 @@ static spl_vm_val_t vm_load(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
return (spl_vm_val_t)(uintptr_t)prog;
|
||||
}
|
||||
|
||||
/* vm_push - push a value onto the sub-VM's stack (for passing arguments) */
|
||||
static spl_vm_val_t vm_load_from_buffer(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
CHECK_NARGS("vm_load_from_buffer", 3);
|
||||
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
||||
const char *buf = (const char *)VM_XN(_vm, args[1], (usize)args[2]);
|
||||
usize len = (usize)args[2];
|
||||
spl_prog_t *prog;
|
||||
if (!vm || !buf)
|
||||
return -1;
|
||||
prog = (spl_prog_t *)malloc(sizeof(spl_prog_t));
|
||||
if (!prog)
|
||||
return -1;
|
||||
if (spl_prog_load_from_buffer(buf, len, prog) != 0) {
|
||||
free(prog);
|
||||
return -1;
|
||||
}
|
||||
/* 不自动注入任何 native/syscall:解析策略由使用者配置。 */
|
||||
if (spl_vm_load_prog(vm, prog) != 0) {
|
||||
spl_prog_drop(prog);
|
||||
free(prog);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static spl_vm_val_t vm_push(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)_vm;
|
||||
CHECK_NARGS("vm_push", 2);
|
||||
@@ -399,67 +782,101 @@ static spl_vm_val_t vm_push(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* vm_call - call a function by name with pre-pushed arguments
|
||||
*
|
||||
* Args (3): vm, function_name, nargs
|
||||
* The arguments should already be on the sub-VM's stack via vm_push.
|
||||
*
|
||||
* Mirrors the CALL instruction's frame setup:
|
||||
* - Pushes a sentinel frame (saved_ip=-1) if cp==0 so RET halts properly
|
||||
* - Pushes the actual call frame
|
||||
* - Sets fp = sp - nargs (so arg0 = data[fp], arg1 = data[fp+1], ...)
|
||||
* - Sets ip to the function address
|
||||
*/
|
||||
static spl_vm_val_t vm_call(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)_vm;
|
||||
CHECK_NARGS("vm_call", 3);
|
||||
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
||||
const char *name = (const char *)(uintptr_t)args[1];
|
||||
const char *name = (const char *)VM_X(_vm, args[1]);
|
||||
spl_vm_val_t call_nargs = args[2];
|
||||
if (!vm || !name)
|
||||
return -1;
|
||||
|
||||
spl_vm_func_t *func = spl_prog_get_func(vm->prog, name);
|
||||
if (!func) {
|
||||
fprintf(stderr, "vm_call: function '%s' not found\n", name);
|
||||
if (spl_vm_call_entry(vm, name, (int)call_nargs) != 0) {
|
||||
fprintf(stderr, "vm_call: function '%s' not found or bad ABI\n", name);
|
||||
return -1;
|
||||
}
|
||||
if (vm->cp >= vm->config.max_call_depth - 1) {
|
||||
fprintf(stderr, "vm_call: call stack overflow\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Sentinel frame so the entry function's RET halts cleanly */
|
||||
if (vm->cp == 0) {
|
||||
vm->frames.data[vm->cp].saved_fp = 0;
|
||||
vm->frames.data[vm->cp].saved_ip = (uintptr_t)-1;
|
||||
vm->frames.data[vm->cp].nargs = 0;
|
||||
vm->cp++;
|
||||
}
|
||||
|
||||
/* Actual call frame (same logic as the CALL instruction) */
|
||||
vm->frames.data[vm->cp].saved_fp = vm->fp;
|
||||
vm->frames.data[vm->cp].saved_ip = vm->ip;
|
||||
vm->frames.data[vm->cp].nargs = call_nargs;
|
||||
vm->cp++;
|
||||
vm->fp = vm->sp - call_nargs;
|
||||
vm->ip = (uintptr_t)func->address;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* vm_run - run a sub-VM to completion, returns exit_code */
|
||||
SYSCALL_1(vm_run, spl_vm_t *, spl_vm_run_until(a1, 0))
|
||||
static spl_vm_val_t vm_run(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)_vm;
|
||||
CHECK_NARGS("vm_run", 1);
|
||||
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
||||
if (!vm)
|
||||
return -1;
|
||||
spl_vm_run_until(vm, 0);
|
||||
return (spl_vm_val_t)vm->ret_val;
|
||||
}
|
||||
|
||||
/* ---- 栈 / 结果 / 内存 访问(不暴露宿主指针) ---- */
|
||||
|
||||
static spl_vm_val_t sc_vm_ret_val(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)_vm;
|
||||
CHECK_NARGS("vm_ret_val", 1);
|
||||
return spl_vm_ret_val((spl_vm_t *)(uintptr_t)args[0]);
|
||||
}
|
||||
|
||||
static spl_vm_val_t sc_vm_sp(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)_vm;
|
||||
CHECK_NARGS("vm_sp", 1);
|
||||
return (spl_vm_val_t)spl_vm_sp((spl_vm_t *)(uintptr_t)args[0]);
|
||||
}
|
||||
|
||||
static spl_vm_val_t sc_vm_fp(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)_vm;
|
||||
CHECK_NARGS("vm_fp", 1);
|
||||
return (spl_vm_val_t)spl_vm_fp((spl_vm_t *)(uintptr_t)args[0]);
|
||||
}
|
||||
|
||||
static spl_vm_val_t sc_vm_stack_get(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)_vm;
|
||||
CHECK_NARGS("vm_stack_get", 2);
|
||||
return spl_vm_stack_get((spl_vm_t *)(uintptr_t)args[0], (usize)args[1]);
|
||||
}
|
||||
|
||||
static spl_vm_val_t sc_vm_stack_set(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)_vm;
|
||||
CHECK_NARGS("vm_stack_set", 3);
|
||||
return (spl_vm_val_t)spl_vm_stack_set((spl_vm_t *)(uintptr_t)args[0], (usize)args[1], args[2]);
|
||||
}
|
||||
|
||||
static spl_vm_val_t sc_vm_mem_alloc(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
(void)_vm;
|
||||
CHECK_NARGS("vm_mem_alloc", 2);
|
||||
return spl_vm_mem_alloc((spl_vm_t *)(uintptr_t)args[0], (usize)args[1]);
|
||||
}
|
||||
|
||||
static spl_vm_val_t sc_vm_mem_read(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
spl_vm_t *vm;
|
||||
void *dst;
|
||||
usize n;
|
||||
CHECK_NARGS("vm_mem_read", 4);
|
||||
vm = (spl_vm_t *)(uintptr_t)args[0];
|
||||
n = (usize)args[3];
|
||||
dst = VM_XN(_vm, args[2], n);
|
||||
if ((!dst) && n)
|
||||
return -1;
|
||||
return (spl_vm_val_t)spl_vm_mem_read(vm, (spl_vm_val_t)args[1], dst, n);
|
||||
}
|
||||
|
||||
static spl_vm_val_t sc_vm_mem_write(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||
spl_vm_t *vm;
|
||||
void *src;
|
||||
usize n;
|
||||
CHECK_NARGS("vm_mem_write", 4);
|
||||
vm = (spl_vm_t *)(uintptr_t)args[0];
|
||||
n = (usize)args[3];
|
||||
src = VM_XN(_vm, args[2], n);
|
||||
if ((!src) && n)
|
||||
return -1;
|
||||
return (spl_vm_val_t)spl_vm_mem_write(vm, (spl_vm_val_t)args[1], src, n);
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
* Registration
|
||||
*
|
||||
* Called after loading a .sir file. Matches native declarations in
|
||||
* prog->natives against known syscall names and hooks up impl_fn.
|
||||
* ================================================================= */
|
||||
|
||||
void spl_syscall_register(spl_prog_t *prog) {
|
||||
static spl_vm_native_t table[] = {
|
||||
/* OS operations */
|
||||
{"vm_exit", 0, vm_exit},
|
||||
{"vm_putchar", 0, vm_putchar},
|
||||
{"vm_getchar", 0, vm_getchar},
|
||||
@@ -474,19 +891,55 @@ void spl_syscall_register(spl_prog_t *prog) {
|
||||
{"vm_stdout", 0, vm_stdout},
|
||||
{"vm_stderr", 0, vm_stderr},
|
||||
{"vm_read_file", 0, vm_read_file},
|
||||
{"vm_alloc", 0, vm_alloc},
|
||||
{"vm_free", 0, vm_free},
|
||||
{"vm_realloc", 0, vm_realloc},
|
||||
{"vm_memcpy", 0, vm_memcpy},
|
||||
{"vm_memcmp", 0, vm_memcmp},
|
||||
|
||||
{"vm_alloc", 0, spl_sys_vm_alloc},
|
||||
{"vm_free", 0, spl_sys_vm_free},
|
||||
{"vm_realloc", 0, spl_sys_vm_realloc},
|
||||
{"vm_memcpy", 0, spl_sys_vm_memcpy},
|
||||
{"vm_memmove", 0, spl_sys_vm_memmove},
|
||||
{"vm_memcmp", 0, spl_sys_vm_memcmp},
|
||||
{"vm_printf", 0, vm_printf},
|
||||
/* VM operations (comptime) */
|
||||
|
||||
{"vm_vec_valid", 0, spl_sys_vm_vec_valid},
|
||||
{"vm_vec_push_raw", 0, spl_sys_vm_vec_push_raw},
|
||||
{"vm_vec_push", 0, spl_sys_vm_vec_push},
|
||||
{"vm_vec_get", 0, spl_sys_vm_vec_get},
|
||||
{"vm_vec_allocate", 0, spl_sys_vm_vec_allocate},
|
||||
{"vm_vec_reserve_raw", 0, spl_sys_vm_vec_reserve_raw},
|
||||
{"vm_vec_append", 0, spl_sys_vm_vec_append},
|
||||
{"vm_vec_set", 0, spl_sys_vm_vec_set},
|
||||
{"vm_vec_pop", 0, spl_sys_vm_vec_pop},
|
||||
{"vm_vec_clone", 0, spl_sys_vm_vec_clone},
|
||||
{"vm_vec_drop", 0, spl_sys_vm_vec_drop},
|
||||
{"vm_bytes_eq", 0, spl_sys_vm_bytes_eq},
|
||||
|
||||
{"vm_map_valid", 0, spl_sys_vm_map_valid},
|
||||
{"vm_map_reserve", 0, spl_sys_vm_map_reserve},
|
||||
{"vm_map_put", 0, spl_sys_vm_map_put},
|
||||
{"vm_map_get", 0, spl_sys_vm_map_get},
|
||||
{"vm_map_del", 0, spl_sys_vm_map_del},
|
||||
{"vm_map_clear", 0, spl_sys_vm_map_clear},
|
||||
{"vm_map_drop", 0, spl_sys_vm_map_drop},
|
||||
|
||||
{"vm_timestamp", 0, vm_timestamp},
|
||||
|
||||
{"vm_new", 0, vm_new},
|
||||
{"vm_set_foreign", 0, sc_vm_set_foreign},
|
||||
{"vm_drop", 0, vm_drop},
|
||||
{"vm_load", 0, vm_load},
|
||||
{"vm_load_from_buffer", 0, vm_load_from_buffer},
|
||||
{"vm_push", 0, vm_push},
|
||||
{"vm_call", 0, vm_call},
|
||||
{"vm_run", 0, vm_run},
|
||||
|
||||
{"vm_ret_val", 0, sc_vm_ret_val},
|
||||
{"vm_sp", 0, sc_vm_sp},
|
||||
{"vm_fp", 0, sc_vm_fp},
|
||||
{"vm_stack_get", 0, sc_vm_stack_get},
|
||||
{"vm_stack_set", 0, sc_vm_stack_set},
|
||||
{"vm_mem_alloc", 0, sc_vm_mem_alloc},
|
||||
{"vm_mem_read", 0, sc_vm_mem_read},
|
||||
{"vm_mem_write", 0, sc_vm_mem_write},
|
||||
};
|
||||
int n = (int)(sizeof(table) / sizeof(table[0]));
|
||||
if (!prog)
|
||||
|
||||
+814
-181
File diff suppressed because it is too large.
Load diff
@@ -3,12 +3,27 @@
|
||||
#ifndef __SPL_VM_H__
|
||||
#define __SPL_VM_H__
|
||||
|
||||
#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;
|
||||
@@ -19,6 +34,27 @@ 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;
|
||||
VEC(uint64_t) self_ns; /* 每函数 time-slice 累加(纳秒) */
|
||||
uint64_t last_ns;
|
||||
uint64_t total_ns;
|
||||
usize samples;
|
||||
} spl_prof_t;
|
||||
|
||||
typedef struct spl_vm {
|
||||
spl_stack_vec_t stacks;
|
||||
spl_frame_vec_t frames;
|
||||
@@ -27,14 +63,26 @@ typedef struct spl_vm {
|
||||
uintptr_t fp; // frame pointer
|
||||
uintptr_t cp; // call pointer
|
||||
uintptr_t ip; // instr pointer
|
||||
uint64_t steps; // 已执行指令数(供性能分析参考)
|
||||
uint64_t ncalls; // 已执行 CALL/CALLI 次数(供性能分析参考)
|
||||
int exit_code;
|
||||
int trace; /* non-zero to print each instruction */
|
||||
int debug; /* non-zero to enable canary checks */
|
||||
int debug_addr; /* non-zero to check for low-address memory access */
|
||||
int skip_bp; /* non-zero: run_once 忽略下一次断点(continue 越过当前断点指令) */
|
||||
spl_prof_t *profile; /* native 采样 profiler 状态(spl_prof_t*),NULL = 关闭 */
|
||||
usize profile_interval; /* 采样间隔(指令数) */
|
||||
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;
|
||||
@@ -54,6 +102,38 @@ 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);
|
||||
/* 本次调用实参在 VM 栈中的起始下标(外部处理器可按需用 stack_get 逐个读取,
|
||||
* 无需在 C 侧了解参数类型/元数上限)。失败返回 (usize)-1。 */
|
||||
usize spl_vm_call_arg_base(spl_vm_t *vm, const spl_vm_call_t *call);
|
||||
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);
|
||||
|
||||
@@ -73,6 +153,13 @@ void spl_vm_clear_breakpoints(spl_vm_t *vm);
|
||||
/* 让 run_once 执行当前指令(忽略一次断点命中);continue 越过当前断点用 */
|
||||
void spl_vm_skip_breakpoint(spl_vm_t *vm);
|
||||
|
||||
/* ---- native 采样 profiler ----
|
||||
* 每隔 interval 条指令记录一次当前调用栈(折叠格式),用于生成火焰图。
|
||||
* 必须在 prog 加载后调用 enable;dump 输出折叠栈/函数表,再 drop 时释放。 */
|
||||
void spl_vm_profile_enable(spl_vm_t *vm, usize interval);
|
||||
void spl_vm_profile_dump_folded(spl_vm_t *vm, const char *path);
|
||||
void spl_vm_profile_dump_table(spl_vm_t *vm, const char *path);
|
||||
|
||||
void spl_vm_dump_instr(spl_vm_t *vm, spl_vm_val_t ip);
|
||||
void spl_vm_stackdump(spl_vm_t *vm, spl_vm_val_t sp);
|
||||
int spl_vm_backtrace(spl_vm_t *vm, spl_vm_val_t fp);
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/* spl_vm_embed.h - 裸机内嵌 VM(装载/运行,不含任何内置 native/syscall)。
|
||||
*
|
||||
* 单 TU 合并式:定义 SPL_VM_EMBED_IMPLIMENTATION 后 include 本头,会把 stage0 的
|
||||
* spl_mcode.c / spl_vm.c 一并纳入当前翻译单元(不单独编译/链接)。
|
||||
*/
|
||||
|
||||
#ifndef __SPL_VM_EMBED_H__
|
||||
#define __SPL_VM_EMBED_H__
|
||||
|
||||
void *vm_new(void);
|
||||
void vm_drop(void *vm);
|
||||
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
|
||||
|
||||
/* 裸机内嵌 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));
|
||||
if (!vm)
|
||||
return 0;
|
||||
spl_vm_init(vm);
|
||||
return vm;
|
||||
}
|
||||
|
||||
void vm_drop(void *p) {
|
||||
spl_vm_t *vm = (spl_vm_t *)p;
|
||||
if (!vm)
|
||||
return;
|
||||
if (vm->prog) {
|
||||
spl_prog_drop(vm->prog);
|
||||
free(vm->prog);
|
||||
vm->prog = 0;
|
||||
}
|
||||
spl_vm_drop(vm);
|
||||
free(vm);
|
||||
}
|
||||
|
||||
int vm_load_from_buffer(void *p, const void *buf, unsigned long long len) {
|
||||
spl_vm_t *vm = (spl_vm_t *)p;
|
||||
spl_prog_t *prog;
|
||||
if (!vm || !buf)
|
||||
return -1;
|
||||
prog = (spl_prog_t *)malloc(sizeof(spl_prog_t));
|
||||
if (!prog)
|
||||
return -1;
|
||||
if (spl_prog_load_from_buffer((const char *)buf, (usize)len, prog) != 0) {
|
||||
free(prog);
|
||||
return -1;
|
||||
}
|
||||
if (spl_vm_load_prog(vm, prog) != 0) {
|
||||
spl_prog_drop(prog);
|
||||
free(prog);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vm_push(void *p, unsigned long long val) {
|
||||
spl_vm_t *vm = (spl_vm_t *)p;
|
||||
if (!vm)
|
||||
return;
|
||||
if (vm->sp >= vm->config.max_stack_depth)
|
||||
return;
|
||||
vm->stacks.data[vm->sp++] = (spl_vm_val_t)val;
|
||||
}
|
||||
|
||||
int vm_call(void *p, const char *name, unsigned long long nargs) {
|
||||
spl_vm_t *vm = (spl_vm_t *)p;
|
||||
if (!vm || !name)
|
||||
return -1;
|
||||
/* 入口调用:调用方需先按 ABI 压 2 个 pad 再压实参。 */
|
||||
return spl_vm_call_entry(vm, name, (int)nargs);
|
||||
}
|
||||
|
||||
int vm_run(void *p) {
|
||||
spl_vm_t *vm = (spl_vm_t *)p;
|
||||
if (!vm)
|
||||
return -1;
|
||||
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},
|
||||
|
||||
+612
@@ -0,0 +1,612 @@
|
||||
/* ir_opt.c - IR 层优化 pass
|
||||
*
|
||||
* 通用函数内联(任意大小/形状的 callee,不再限叶子):
|
||||
* - callee 体内允许 control.call(嵌套调用)、分支/循环、多个返回、unit 返回;
|
||||
* - 参数:克隆体内 Param 的“使用处”替换为调用实参 ref;
|
||||
* - 控制流:克隆体追加到调用者末尾,内部 br/jmp/label 整体 +shift;
|
||||
* - 返回:
|
||||
* * 有返回值:在调用者追加 ret 槽(MemAlloca)与常量 1;每个 Ret 改为
|
||||
* jmp 到“MemStore(槽, val) + jmp(join)”,join 处 MemLoad(槽) 供使用者;
|
||||
* * 无返回值(unit):每个 Ret 直接 jmp 到调用点之后。
|
||||
* - 调用点改为 jmp 到克隆体入口;其使用者改指到 join 的 MemLoad(或 0)。
|
||||
* - 防环:若 callee(传递)可达 caller,则拒绝内联,保证调用图保持无环、
|
||||
* 内联过程终止(直接/间接递归都不会被内联)。
|
||||
* - 迭代到不动点(无环调用图上轮数有上界)。
|
||||
* 默认关闭(splc0 --opt 驱动),且只内联显式 `#inline` 标记的 callee。 */
|
||||
|
||||
#include "ir_opt.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
spl_ir_t *ir;
|
||||
spl_ir_func_ref_t caller;
|
||||
spl_ir_func_ref_t callee;
|
||||
spl_ir_node_ref_t shift; /* new = old + shift */
|
||||
usize nparams;
|
||||
spl_ir_node_ref_t *param_ref;
|
||||
spl_ir_node_ref_t *param_arg;
|
||||
} inline_ctx_t;
|
||||
|
||||
static spl_ir_node_t *nd(spl_ir_t *ir, spl_ir_func_ref_t fid, spl_ir_node_ref_t ref) {
|
||||
return spl_ir_node(ir, fid, ref);
|
||||
}
|
||||
|
||||
/* ---- 引用重写(克隆体旧 ref -> 新 ref) ---- */
|
||||
|
||||
static spl_ir_node_ref_t rw(inline_ctx_t *c, spl_ir_node_ref_t r) {
|
||||
if (r == 0)
|
||||
return 0;
|
||||
for (usize i = 0; i < c->nparams; i++)
|
||||
if (c->param_ref[i] == r)
|
||||
return c->param_arg[i];
|
||||
return r + c->shift;
|
||||
}
|
||||
|
||||
static void rw_vec(inline_ctx_t *c, spl_ir_node_ref_vec_t *v) {
|
||||
for (usize i = 0; i < vec_size(*v); i++)
|
||||
vec_at(*v, i) = rw(c, vec_at(*v, i));
|
||||
}
|
||||
|
||||
static void rw_node(inline_ctx_t *c, spl_ir_node_t *n) {
|
||||
switch (n->kind) {
|
||||
case SPL_IR_ARITH_ADD: case SPL_IR_ARITH_SUB: case SPL_IR_ARITH_MUL:
|
||||
case SPL_IR_ARITH_DIV: case SPL_IR_ARITH_REM: case SPL_IR_ARITH_NEG:
|
||||
case SPL_IR_ARITH_ABS: case SPL_IR_ARITH_AND: case SPL_IR_ARITH_OR:
|
||||
case SPL_IR_ARITH_XOR: case SPL_IR_ARITH_SHL: case SPL_IR_ARITH_SHR:
|
||||
case SPL_IR_ARITH_NOT:
|
||||
n->arith.left = rw(c, n->arith.left);
|
||||
n->arith.right = rw(c, n->arith.right);
|
||||
break;
|
||||
case SPL_IR_CMP_EQ: case SPL_IR_CMP_NE: case SPL_IR_CMP_LT:
|
||||
case SPL_IR_CMP_LE: case SPL_IR_CMP_GT: case SPL_IR_CMP_GE:
|
||||
n->cmp.a = rw(c, n->cmp.a);
|
||||
n->cmp.b = rw(c, n->cmp.b);
|
||||
break;
|
||||
case SPL_IR_CAST_TRUNC: case SPL_IR_CAST_ZEXT: case SPL_IR_CAST_SEXT:
|
||||
case SPL_IR_CAST_FEXT: case SPL_IR_CAST_FTRUNC: case SPL_IR_CAST_BITCAST:
|
||||
case SPL_IR_CAST_PTR2INT: case SPL_IR_CAST_INT2PTR: case SPL_IR_CAST_BOOL2INT:
|
||||
case SPL_IR_CASE_INT2FLOAT: case SPL_IR_CASE_FLOAT2INT:
|
||||
n->cast.val = rw(c, n->cast.val);
|
||||
break;
|
||||
case SPL_IR_MEM_ALLOCA:
|
||||
n->mem_alloc.count = rw(c, n->mem_alloc.count);
|
||||
break;
|
||||
case SPL_IR_MEM_GLOBAL_ALLOC:
|
||||
n->mem_global_alloc.const_node = rw(c, n->mem_global_alloc.const_node);
|
||||
break;
|
||||
case SPL_IR_MEM_LOAD:
|
||||
n->mem_load.ptr = rw(c, n->mem_load.ptr);
|
||||
break;
|
||||
case SPL_IR_MEM_STORE:
|
||||
n->mem_store.ptr = rw(c, n->mem_store.ptr);
|
||||
n->mem_store.val = rw(c, n->mem_store.val);
|
||||
break;
|
||||
case SPL_IR_MEM_OFFSET:
|
||||
n->mem_offset.ptr = rw(c, n->mem_offset.ptr);
|
||||
n->mem_offset.offset = rw(c, n->mem_offset.offset);
|
||||
break;
|
||||
case SPL_IR_MEM_FIELD_PTR:
|
||||
n->mem_field_ptr.agg = rw(c, n->mem_field_ptr.agg);
|
||||
break;
|
||||
case SPL_IR_MEM_COPY:
|
||||
n->mem_copy.dst = rw(c, n->mem_copy.dst);
|
||||
n->mem_copy.src = rw(c, n->mem_copy.src);
|
||||
n->mem_copy.size = rw(c, n->mem_copy.size);
|
||||
break;
|
||||
case SPL_IR_MEM_SET:
|
||||
n->mem_set.dst = rw(c, n->mem_set.dst);
|
||||
n->mem_set.val = rw(c, n->mem_set.val);
|
||||
n->mem_set.size = rw(c, n->mem_set.size);
|
||||
break;
|
||||
case SPL_IR_TYPE_OFFSETOF:
|
||||
n->ir_offsetof.field_idx = rw(c, n->ir_offsetof.field_idx);
|
||||
break;
|
||||
case SPL_IR_AGG_CONSTRUCT:
|
||||
rw_vec(c, &n->agg_construct.fields);
|
||||
break;
|
||||
case SPL_IR_AGG_EXTRACT:
|
||||
n->agg_extract.val = rw(c, n->agg_extract.val);
|
||||
break;
|
||||
case SPL_IR_AGG_INSERT:
|
||||
n->agg_insert.agg = rw(c, n->agg_insert.agg);
|
||||
n->agg_insert.field = rw(c, n->agg_insert.field);
|
||||
break;
|
||||
case SPL_IR_CONTROL_SELECT:
|
||||
n->control_select.cond = rw(c, n->control_select.cond);
|
||||
n->control_select.true_val = rw(c, n->control_select.true_val);
|
||||
n->control_select.false_val = rw(c, n->control_select.false_val);
|
||||
break;
|
||||
case SPL_IR_CONTROL_BR:
|
||||
n->control_br.cond = rw(c, n->control_br.cond);
|
||||
n->control_br.true_label = rw(c, n->control_br.true_label);
|
||||
n->control_br.false_label = rw(c, n->control_br.false_label);
|
||||
break;
|
||||
case SPL_IR_CONTROL_JMP:
|
||||
n->control_jmp.label = rw(c, n->control_jmp.label);
|
||||
break;
|
||||
case SPL_IR_CONTROL_PARAM:
|
||||
n->control_param.idx = rw(c, n->control_param.idx);
|
||||
break;
|
||||
case SPL_IR_CONTROL_RET:
|
||||
n->control_ret.val = rw(c, n->control_ret.val);
|
||||
break;
|
||||
case SPL_IR_CONTROL_CALL:
|
||||
n->control_call.func = rw(c, n->control_call.func);
|
||||
rw_vec(c, &n->control_call.params);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 克隆 ---- */
|
||||
|
||||
static void clone_ref_vec(spl_ir_node_ref_vec_t *dst, const spl_ir_node_ref_vec_t *src) {
|
||||
dst->size = 0;
|
||||
dst->cap = 0;
|
||||
dst->data = NULL;
|
||||
usize n = vec_size(*src);
|
||||
if (n == 0)
|
||||
return;
|
||||
spl_ir_node_ref_t *p = (spl_ir_node_ref_t *)malloc(sizeof(spl_ir_node_ref_t) * n);
|
||||
if (!p)
|
||||
return;
|
||||
memcpy(p, src->data, sizeof(spl_ir_node_ref_t) * n);
|
||||
dst->data = p;
|
||||
dst->size = n;
|
||||
dst->cap = n;
|
||||
}
|
||||
|
||||
static void clone_node(spl_ir_t *ir, spl_ir_func_ref_t caller, spl_ir_node_ref_t dst_ref,
|
||||
const spl_ir_node_t *src) {
|
||||
spl_ir_node_t *dst = nd(ir, caller, dst_ref);
|
||||
memcpy(dst, src, sizeof(*dst));
|
||||
dst->attrs.data = NULL;
|
||||
dst->attrs.size = 0;
|
||||
dst->attrs.cap = 0;
|
||||
if (vec_size(src->attrs) != 0)
|
||||
clone_ref_vec((spl_ir_node_ref_vec_t *)&dst->attrs,
|
||||
(const spl_ir_node_ref_vec_t *)&src->attrs);
|
||||
if (src->kind == SPL_IR_AGG_CONSTRUCT)
|
||||
clone_ref_vec(&dst->agg_construct.fields, &src->agg_construct.fields);
|
||||
else if (src->kind == SPL_IR_CONTROL_CALL)
|
||||
clone_ref_vec(&dst->control_call.params, &src->control_call.params);
|
||||
}
|
||||
|
||||
/* ---- 调用图可达性(防环) ---- */
|
||||
|
||||
static spl_ir_func_ref_t call_callee(spl_ir_t *ir, spl_ir_func_ref_t fid, spl_ir_node_t *call) {
|
||||
spl_ir_node_t *fn = nd(ir, fid, call->control_call.func);
|
||||
if (!fn || fn->kind != SPL_IR_TYPE_CONST)
|
||||
return 0;
|
||||
return fn->type_const.fn;
|
||||
}
|
||||
|
||||
static bool reaches(spl_ir_t *ir, spl_ir_func_ref_t from, spl_ir_func_ref_t target,
|
||||
unsigned char *mark) {
|
||||
if (from == 0)
|
||||
return false;
|
||||
if (from == target)
|
||||
return true;
|
||||
if (mark[from])
|
||||
return false;
|
||||
mark[from] = 1;
|
||||
spl_ir_func_t *f = spl_ir_func(ir, from);
|
||||
if (!f)
|
||||
return false;
|
||||
usize n = vec_size(f->nodes);
|
||||
for (usize i = 1; i < n; i++) {
|
||||
spl_ir_node_t *x = nd(ir, from, i);
|
||||
if (x && x->kind == SPL_IR_CONTROL_CALL) {
|
||||
spl_ir_func_ref_t c = call_callee(ir, from, x);
|
||||
if (c != 0 && reaches(ir, c, target, mark))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 从 from 的直接被调者出发是否能到达 target(用于判断 from 自身是否递归/自可达)。 */
|
||||
static bool reaches_succ(spl_ir_t *ir, spl_ir_func_ref_t from, spl_ir_func_ref_t target,
|
||||
unsigned char *mark) {
|
||||
spl_ir_func_t *f = spl_ir_func(ir, from);
|
||||
if (!f)
|
||||
return false;
|
||||
usize n = vec_size(f->nodes);
|
||||
for (usize i = 1; i < n; i++) {
|
||||
spl_ir_node_t *x = nd(ir, from, i);
|
||||
if (x && x->kind == SPL_IR_CONTROL_CALL) {
|
||||
spl_ir_func_ref_t c = call_callee(ir, from, x);
|
||||
if (c != 0 && reaches(ir, c, target, mark))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ---- 内联一个调用点 ---- */
|
||||
|
||||
typedef struct {
|
||||
spl_ir_node_ref_t clone_ref;
|
||||
spl_ir_node_ref_t val;
|
||||
} ret_rec_t;
|
||||
|
||||
static bool param_index(spl_ir_t *ir, spl_ir_func_ref_t fid, spl_ir_node_t *p, usize *out) {
|
||||
spl_ir_node_t *idx = nd(ir, fid, p->control_param.idx);
|
||||
if (!idx || idx->kind != SPL_IR_TYPE_CONST)
|
||||
return false;
|
||||
*out = idx->type_const.int_lit;
|
||||
return true;
|
||||
}
|
||||
|
||||
static spl_ir_node_ref_t find_const_src(spl_ir_t *ir, spl_ir_func_ref_t fid) {
|
||||
spl_ir_func_t *f = spl_ir_func(ir, fid);
|
||||
if (!f)
|
||||
return 0;
|
||||
for (usize i = 1; i < vec_size(f->nodes); i++) {
|
||||
spl_ir_node_t *x = nd(ir, fid, i);
|
||||
if (x && x->kind == SPL_IR_TYPE_CONST)
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool try_inline(spl_ir_t *ir, spl_ir_func_ref_t caller, spl_ir_node_ref_t ci,
|
||||
spl_ir_func_ref_t callee) {
|
||||
spl_ir_func_t *cf = spl_ir_func(ir, caller);
|
||||
spl_ir_func_t *calf = spl_ir_func(ir, callee);
|
||||
if (!cf || !calf || callee == caller)
|
||||
return false;
|
||||
|
||||
spl_ir_node_t *call = nd(ir, caller, ci);
|
||||
if (!call || call->kind != SPL_IR_CONTROL_CALL)
|
||||
return false;
|
||||
|
||||
usize orig_len = vec_size(cf->nodes);
|
||||
if (ci + 1 >= orig_len)
|
||||
return false;
|
||||
|
||||
usize nargs = vec_size(call->control_call.params);
|
||||
|
||||
/* 统计参数与返回,分配动态映射。 */
|
||||
usize cnodes = vec_size(calf->nodes);
|
||||
usize in_params = 0;
|
||||
usize in_rets = 0;
|
||||
for (usize i = 1; i < cnodes; i++) {
|
||||
spl_ir_node_t *x = nd(ir, callee, i);
|
||||
if (!x)
|
||||
return false;
|
||||
if (x->kind == SPL_IR_CONTROL_PARAM)
|
||||
in_params++;
|
||||
else if (x->kind == SPL_IR_CONTROL_RET)
|
||||
in_rets++;
|
||||
}
|
||||
if (in_rets == 0 || in_params != nargs)
|
||||
return false;
|
||||
|
||||
inline_ctx_t c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
c.ir = ir;
|
||||
c.caller = caller;
|
||||
c.callee = callee;
|
||||
c.nparams = in_params;
|
||||
c.param_ref = (spl_ir_node_ref_t *)malloc(sizeof(spl_ir_node_ref_t) * (in_params ? in_params : 1));
|
||||
c.param_arg = (spl_ir_node_ref_t *)malloc(sizeof(spl_ir_node_ref_t) * (in_params ? in_params : 1));
|
||||
ret_rec_t *recs = (ret_rec_t *)malloc(sizeof(ret_rec_t) * (in_rets ? in_rets : 1));
|
||||
if (!c.param_ref || !c.param_arg || !recs) {
|
||||
free(c.param_ref);
|
||||
free(c.param_arg);
|
||||
free(recs);
|
||||
return false;
|
||||
}
|
||||
|
||||
spl_type_id_t ret_tid = call->control_call.tid;
|
||||
|
||||
/* 参数映射(旧 ref -> 实参 ref)。 */
|
||||
usize pi = 0;
|
||||
for (usize i = 1; i < cnodes; i++) {
|
||||
spl_ir_node_t *x = nd(ir, callee, i);
|
||||
if (x && x->kind == SPL_IR_CONTROL_PARAM) {
|
||||
usize idx = 0;
|
||||
if (!param_index(ir, callee, x, &idx) || idx >= nargs) {
|
||||
free(c.param_ref);
|
||||
free(c.param_arg);
|
||||
free(recs);
|
||||
return false;
|
||||
}
|
||||
c.param_ref[pi] = i;
|
||||
c.param_arg[pi] = vec_at(call->control_call.params, idx);
|
||||
pi++;
|
||||
}
|
||||
}
|
||||
|
||||
bool has_val = false;
|
||||
for (usize i = 1; i < cnodes; i++) {
|
||||
spl_ir_node_t *x = nd(ir, callee, i);
|
||||
if (x && x->kind == SPL_IR_CONTROL_RET && x->control_ret.val != 0)
|
||||
has_val = true;
|
||||
}
|
||||
spl_ir_node_ref_t const_src = find_const_src(ir, callee);
|
||||
if (has_val && const_src == 0) {
|
||||
free(c.param_ref);
|
||||
free(c.param_arg);
|
||||
free(recs);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 释放调用点原 params(随后 ci 改为 jmp) */
|
||||
vec_free(call->control_call.params);
|
||||
|
||||
/* 克隆体入口前置:常量 1 + ret 槽 */
|
||||
spl_ir_node_ref_t body_entry = 0;
|
||||
spl_ir_node_ref_t slot_ref = 0;
|
||||
if (has_val) {
|
||||
spl_ir_node_ref_t c_ref = spl_ir_alloc_node(ir, caller);
|
||||
clone_node(ir, caller, c_ref, nd(ir, callee, const_src));
|
||||
spl_ir_node_t *cn = nd(ir, caller, c_ref);
|
||||
cn->kind = SPL_IR_TYPE_CONST;
|
||||
cn->type_const.int_lit = 1;
|
||||
|
||||
slot_ref = spl_ir_alloc_node(ir, caller);
|
||||
spl_ir_node_t *sn = nd(ir, caller, slot_ref);
|
||||
memset(sn, 0, sizeof(*sn));
|
||||
sn->kind = SPL_IR_MEM_ALLOCA;
|
||||
sn->mem_alloc.tid = ret_tid;
|
||||
sn->mem_alloc.count = c_ref;
|
||||
|
||||
body_entry = c_ref;
|
||||
}
|
||||
|
||||
c.shift = vec_size(cf->nodes) - 1;
|
||||
if (!has_val)
|
||||
body_entry = vec_size(cf->nodes);
|
||||
|
||||
/* 克隆体 */
|
||||
usize nrec = 0;
|
||||
for (usize i = 1; i < cnodes; i++) {
|
||||
spl_ir_node_ref_t dst_ref = spl_ir_alloc_node(ir, caller);
|
||||
if (dst_ref == 0) {
|
||||
free(c.param_ref);
|
||||
free(c.param_arg);
|
||||
free(recs);
|
||||
return false;
|
||||
}
|
||||
spl_ir_node_t *src = nd(ir, callee, i);
|
||||
clone_node(ir, caller, dst_ref, src);
|
||||
rw_node(&c, nd(ir, caller, dst_ref));
|
||||
spl_ir_node_t *x = nd(ir, caller, dst_ref);
|
||||
/* 克隆进来的 Param 使用处已替换为实参,节点本身不能留在调用者里
|
||||
* 改变其参数个数;退化为无害常量。 */
|
||||
if (src->kind == SPL_IR_CONTROL_PARAM) {
|
||||
x->kind = SPL_IR_TYPE_CONST;
|
||||
x->type_const.tid = 0;
|
||||
x->type_const.int_lit = 0;
|
||||
continue;
|
||||
}
|
||||
if (x->kind == SPL_IR_CONTROL_RET) {
|
||||
recs[nrec].clone_ref = dst_ref;
|
||||
recs[nrec].val = x->control_ret.val;
|
||||
nrec++;
|
||||
x->kind = SPL_IR_CONTROL_JMP;
|
||||
x->control_jmp.label = 0; /* 稍后回填 */
|
||||
}
|
||||
}
|
||||
|
||||
spl_ir_node_ref_t ret_new = 0;
|
||||
if (has_val) {
|
||||
spl_ir_node_ref_t join = spl_ir_alloc_node(ir, caller);
|
||||
spl_ir_node_t *jn = nd(ir, caller, join);
|
||||
memset(jn, 0, sizeof(*jn));
|
||||
jn->kind = SPL_IR_MEM_LOAD;
|
||||
jn->mem_load.tid = ret_tid;
|
||||
jn->mem_load.ptr = slot_ref;
|
||||
|
||||
/* join 之后跳回调用点的后继(原代码此处靠 fallthrough)。 */
|
||||
spl_ir_node_ref_t jc = spl_ir_alloc_node(ir, caller);
|
||||
spl_ir_node_t *jcn = nd(ir, caller, jc);
|
||||
memset(jcn, 0, sizeof(*jcn));
|
||||
jcn->kind = SPL_IR_CONTROL_JMP;
|
||||
jcn->control_jmp.label = ci + 1;
|
||||
|
||||
for (usize k = 0; k < nrec; k++) {
|
||||
spl_ir_node_ref_t st = spl_ir_alloc_node(ir, caller);
|
||||
spl_ir_node_t *stn = nd(ir, caller, st);
|
||||
memset(stn, 0, sizeof(*stn));
|
||||
stn->kind = SPL_IR_MEM_STORE;
|
||||
stn->mem_store.tid = ret_tid;
|
||||
stn->mem_store.ptr = slot_ref;
|
||||
stn->mem_store.val = recs[k].val;
|
||||
|
||||
spl_ir_node_ref_t jr = spl_ir_alloc_node(ir, caller);
|
||||
spl_ir_node_t *jrn = nd(ir, caller, jr);
|
||||
memset(jrn, 0, sizeof(*jrn));
|
||||
jrn->kind = SPL_IR_CONTROL_JMP;
|
||||
jrn->control_jmp.label = join;
|
||||
|
||||
spl_ir_node_t *rn = nd(ir, caller, recs[k].clone_ref);
|
||||
rn->control_jmp.label = st;
|
||||
}
|
||||
ret_new = join;
|
||||
} else {
|
||||
for (usize k = 0; k < nrec; k++) {
|
||||
spl_ir_node_t *rn = nd(ir, caller, recs[k].clone_ref);
|
||||
rn->control_jmp.label = ci + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 调用点 -> jmp 到克隆体入口 */
|
||||
call = nd(ir, caller, ci);
|
||||
call->kind = SPL_IR_CONTROL_JMP;
|
||||
call->control_jmp.label = body_entry;
|
||||
|
||||
/* 使用者替换:只把等于 ci 的引用改为 ret_new(不动其它 ref) */
|
||||
for (usize j = 1; j < orig_len; j++) {
|
||||
spl_ir_node_t *x = nd(ir, caller, j);
|
||||
if (!x)
|
||||
continue;
|
||||
switch (x->kind) {
|
||||
case SPL_IR_ARITH_ADD: case SPL_IR_ARITH_SUB: case SPL_IR_ARITH_MUL:
|
||||
case SPL_IR_ARITH_DIV: case SPL_IR_ARITH_REM: case SPL_IR_ARITH_NEG:
|
||||
case SPL_IR_ARITH_ABS: case SPL_IR_ARITH_AND: case SPL_IR_ARITH_OR:
|
||||
case SPL_IR_ARITH_XOR: case SPL_IR_ARITH_SHL: case SPL_IR_ARITH_SHR:
|
||||
case SPL_IR_ARITH_NOT:
|
||||
if (x->arith.left == ci) x->arith.left = ret_new;
|
||||
if (x->arith.right == ci) x->arith.right = ret_new;
|
||||
break;
|
||||
case SPL_IR_CMP_EQ: case SPL_IR_CMP_NE: case SPL_IR_CMP_LT:
|
||||
case SPL_IR_CMP_LE: case SPL_IR_CMP_GT: case SPL_IR_CMP_GE:
|
||||
if (x->cmp.a == ci) x->cmp.a = ret_new;
|
||||
if (x->cmp.b == ci) x->cmp.b = ret_new;
|
||||
break;
|
||||
case SPL_IR_CAST_TRUNC: case SPL_IR_CAST_ZEXT: case SPL_IR_CAST_SEXT:
|
||||
case SPL_IR_CAST_FEXT: case SPL_IR_CAST_FTRUNC: case SPL_IR_CAST_BITCAST:
|
||||
case SPL_IR_CAST_PTR2INT: case SPL_IR_CAST_INT2PTR: case SPL_IR_CAST_BOOL2INT:
|
||||
case SPL_IR_CASE_INT2FLOAT: case SPL_IR_CASE_FLOAT2INT:
|
||||
if (x->cast.val == ci) x->cast.val = ret_new;
|
||||
break;
|
||||
case SPL_IR_MEM_ALLOCA:
|
||||
if (x->mem_alloc.count == ci) x->mem_alloc.count = ret_new;
|
||||
break;
|
||||
case SPL_IR_MEM_GLOBAL_ALLOC:
|
||||
if (x->mem_global_alloc.const_node == ci) x->mem_global_alloc.const_node = ret_new;
|
||||
break;
|
||||
case SPL_IR_MEM_LOAD:
|
||||
if (x->mem_load.ptr == ci) x->mem_load.ptr = ret_new;
|
||||
break;
|
||||
case SPL_IR_MEM_STORE:
|
||||
if (x->mem_store.ptr == ci) x->mem_store.ptr = ret_new;
|
||||
if (x->mem_store.val == ci) x->mem_store.val = ret_new;
|
||||
break;
|
||||
case SPL_IR_MEM_OFFSET:
|
||||
if (x->mem_offset.ptr == ci) x->mem_offset.ptr = ret_new;
|
||||
if (x->mem_offset.offset == ci) x->mem_offset.offset = ret_new;
|
||||
break;
|
||||
case SPL_IR_MEM_FIELD_PTR:
|
||||
if (x->mem_field_ptr.agg == ci) x->mem_field_ptr.agg = ret_new;
|
||||
break;
|
||||
case SPL_IR_MEM_COPY:
|
||||
if (x->mem_copy.dst == ci) x->mem_copy.dst = ret_new;
|
||||
if (x->mem_copy.src == ci) x->mem_copy.src = ret_new;
|
||||
if (x->mem_copy.size == ci) x->mem_copy.size = ret_new;
|
||||
break;
|
||||
case SPL_IR_MEM_SET:
|
||||
if (x->mem_set.dst == ci) x->mem_set.dst = ret_new;
|
||||
if (x->mem_set.val == ci) x->mem_set.val = ret_new;
|
||||
if (x->mem_set.size == ci) x->mem_set.size = ret_new;
|
||||
break;
|
||||
case SPL_IR_TYPE_OFFSETOF:
|
||||
if (x->ir_offsetof.field_idx == ci) x->ir_offsetof.field_idx = ret_new;
|
||||
break;
|
||||
case SPL_IR_AGG_CONSTRUCT:
|
||||
for (usize k = 0; k < vec_size(x->agg_construct.fields); k++)
|
||||
if (vec_at(x->agg_construct.fields, k) == ci)
|
||||
vec_at(x->agg_construct.fields, k) = ret_new;
|
||||
break;
|
||||
case SPL_IR_AGG_EXTRACT:
|
||||
if (x->agg_extract.val == ci) x->agg_extract.val = ret_new;
|
||||
break;
|
||||
case SPL_IR_AGG_INSERT:
|
||||
if (x->agg_insert.agg == ci) x->agg_insert.agg = ret_new;
|
||||
if (x->agg_insert.field == ci) x->agg_insert.field = ret_new;
|
||||
break;
|
||||
case SPL_IR_CONTROL_SELECT:
|
||||
if (x->control_select.cond == ci) x->control_select.cond = ret_new;
|
||||
if (x->control_select.true_val == ci) x->control_select.true_val = ret_new;
|
||||
if (x->control_select.false_val == ci) x->control_select.false_val = ret_new;
|
||||
break;
|
||||
case SPL_IR_CONTROL_BR:
|
||||
if (x->control_br.cond == ci) x->control_br.cond = ret_new;
|
||||
break;
|
||||
case SPL_IR_CONTROL_CALL:
|
||||
if (x->control_call.func == ci) x->control_call.func = ret_new;
|
||||
for (usize k = 0; k < vec_size(x->control_call.params); k++)
|
||||
if (vec_at(x->control_call.params, k) == ci)
|
||||
vec_at(x->control_call.params, k) = ret_new;
|
||||
break;
|
||||
case SPL_IR_CONTROL_PARAM:
|
||||
if (x->control_param.idx == ci) x->control_param.idx = ret_new;
|
||||
break;
|
||||
case SPL_IR_CONTROL_RET:
|
||||
if (x->control_ret.val == ci) x->control_ret.val = ret_new;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(c.param_ref);
|
||||
free(c.param_arg);
|
||||
free(recs);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool func_has_attr(const spl_attr_t *attr, const spl_ir_func_t *f, const char *name);
|
||||
|
||||
static int opt_function(spl_ir_t *ir, spl_ir_func_ref_t fid, const spl_attr_t *attr,
|
||||
unsigned char *mark, usize nfuncs) {
|
||||
spl_ir_func_t *f = spl_ir_func(ir, fid);
|
||||
if (!f)
|
||||
return 0;
|
||||
int done = 0;
|
||||
usize orig = vec_size(f->nodes);
|
||||
for (usize ci = 1; ci < orig; ci++) {
|
||||
spl_ir_node_t *x = nd(ir, fid, ci);
|
||||
if (!x || x->kind != SPL_IR_CONTROL_CALL)
|
||||
continue;
|
||||
spl_ir_func_ref_t callee = call_callee(ir, fid, x);
|
||||
if (callee == 0 || callee == fid || callee >= nfuncs)
|
||||
continue;
|
||||
spl_ir_func_t *calf = spl_ir_func(ir, callee);
|
||||
if (!func_has_attr(attr, calf, "inline") || func_has_attr(attr, calf, "noinline"))
|
||||
continue;
|
||||
/* extern 是占位体,绝不能内联。 */
|
||||
if (func_has_attr(attr, calf, "extern"))
|
||||
continue;
|
||||
/* 防环:callee 传递可达 caller 时拒绝(覆盖直接/间接互递归)。 */
|
||||
memset(mark, 0, nfuncs);
|
||||
if (reaches(ir, callee, fid, mark))
|
||||
continue;
|
||||
/* 防爆炸:callee 自身递归时拒绝(否则不动点会反复内联其内部调用)。 */
|
||||
memset(mark, 0, nfuncs);
|
||||
if (reaches_succ(ir, callee, callee, mark))
|
||||
continue;
|
||||
if (try_inline(ir, fid, ci, callee))
|
||||
done++;
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
/* 是否带有指定名字的属性/tag。 */
|
||||
static bool func_has_attr(const spl_attr_t *attr, const spl_ir_func_t *f, const char *name) {
|
||||
if (!attr || !f)
|
||||
return false;
|
||||
for (usize i = 0; i < vec_size(f->attrs); ++i) {
|
||||
spl_attr_node_t *a = spl_attr_node((spl_attr_t *)attr, vec_at(f->attrs, i));
|
||||
if (!a || !a->info)
|
||||
continue;
|
||||
if (strcmp(a->info->name, name) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void spl_ir_opt(spl_ir_t *ir, const spl_attr_t *attr) {
|
||||
if (!ir)
|
||||
return;
|
||||
usize nfuncs = vec_size(ir->funcs);
|
||||
unsigned char *mark = (unsigned char *)malloc(nfuncs ? nfuncs : 1);
|
||||
if (!mark)
|
||||
return;
|
||||
/* 无环调用图上,最多 nfuncs 轮即可到不动点。 */
|
||||
for (usize pass = 0; pass <= nfuncs; pass++) {
|
||||
int total = 0;
|
||||
usize n = vec_size(ir->funcs);
|
||||
for (spl_ir_func_ref_t fid = 1; fid < n && fid < nfuncs; fid++)
|
||||
total += opt_function(ir, fid, attr, mark, nfuncs);
|
||||
if (total == 0)
|
||||
break;
|
||||
}
|
||||
free(mark);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef __IR_OPT_H__
|
||||
#define __IR_OPT_H__
|
||||
|
||||
/* IR 层优化 pass:输入输出都是 spl_ir_t(节点 arena),纯函数式、可开关。
|
||||
* 由 splc0 --opt 在 ast2ir 之后、ir2vm 之前调用。 */
|
||||
|
||||
#include "spl_ir.h"
|
||||
|
||||
void spl_ir_opt(spl_ir_t *ir, const spl_attr_t *attr);
|
||||
|
||||
#endif /* __IR_OPT_H__ */
|
||||
@@ -134,6 +134,9 @@ static void dump_node(spl_ast_t *ast, spl_ast_node_ref_t node_ref, dump_stack_t
|
||||
break;
|
||||
|
||||
case SPL_AST_TYPE_DECL:
|
||||
dump_vec(ast, node->type_decl.attr_list, stack);
|
||||
if (node->type_decl.pattern)
|
||||
dump_node(ast, node->type_decl.pattern, stack, false);
|
||||
if (node->type_decl.type_expr)
|
||||
dump_node(ast, node->type_decl.type_expr, stack, true);
|
||||
break;
|
||||
@@ -421,6 +424,11 @@ static void dump_node(spl_ast_t *ast, spl_ast_node_ref_t node_ref, dump_stack_t
|
||||
dump_node(ast, node->pattern_field.pattern, stack, true);
|
||||
break;
|
||||
|
||||
case SPL_AST_PATTERN_REF:
|
||||
if (node->pattern_ref.pat)
|
||||
dump_node(ast, node->pattern_ref.pat, stack, true);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
+7
-2
@@ -73,7 +73,8 @@
|
||||
X(SPL_AST_PATTERN_TUPLE, V0, pattern_tuple) \
|
||||
X(SPL_AST_PATTERN_LIST, V0, pattern_list) \
|
||||
X(SPL_AST_PATTERN_AGGREGATE, V0, pattern_aggregate) \
|
||||
X(SPL_AST_PATTERN_FIELD, V0, pattern_field)
|
||||
X(SPL_AST_PATTERN_FIELD, V0, pattern_field) \
|
||||
X(SPL_AST_PATTERN_REF, V0, pattern_ref)
|
||||
/* clang-format on */
|
||||
|
||||
typedef enum {
|
||||
@@ -184,7 +185,7 @@ struct spl_ast_node {
|
||||
|
||||
struct {
|
||||
spl_ast_node_ref_vec_t attr_list;
|
||||
const char *name;
|
||||
spl_ast_node_ref_t pattern;
|
||||
spl_ast_node_ref_t type_expr;
|
||||
} type_decl;
|
||||
|
||||
@@ -452,6 +453,10 @@ struct spl_ast_node {
|
||||
const char *name;
|
||||
spl_ast_node_ref_t pattern;
|
||||
} pattern_field;
|
||||
|
||||
struct {
|
||||
spl_ast_node_ref_t pat;
|
||||
} pattern_ref;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+758
-92
File diff suppressed because it is too large.
Load diff
+4
-1
@@ -9,6 +9,7 @@ typedef MAP(spl_def_id_t, spl_ir_node_ref_t) spl_def2ir_map_t;
|
||||
|
||||
typedef struct {
|
||||
usize cont_blk;
|
||||
usize defer_base;
|
||||
const char *label;
|
||||
spl_ir_node_ref_vec_t breaks;
|
||||
spl_ir_node_ref_t result_slot;
|
||||
@@ -22,6 +23,7 @@ typedef struct {
|
||||
const spl_sema_t *sema;
|
||||
spl_ir_builder_t *ir;
|
||||
spl_ast_t *ast;
|
||||
spl_layout_t *layout; /* 借用 splc0 的 layout */
|
||||
spl_def2ir_map_t def2ir_map;
|
||||
spl_loop_ctx_vec_t loop_stack;
|
||||
spl_defer_stack_t defer_stack;
|
||||
@@ -31,7 +33,8 @@ typedef struct {
|
||||
spl_diag_t *diag;
|
||||
} spl_ast2ir_t;
|
||||
|
||||
void spl_ast2ir_init(spl_ast2ir_t *ast2ir, spl_ir_builder_t *ir, const spl_sema_t *sema);
|
||||
void spl_ast2ir_init(spl_ast2ir_t *ast2ir, spl_ir_builder_t *ir, spl_layout_t *layout,
|
||||
const spl_sema_t *sema);
|
||||
void spl_ast2ir_drop(spl_ast2ir_t *ast2ir);
|
||||
|
||||
void spl_ast2ir_run(spl_ast2ir_t *ast2ir, spl_ast_ptr_vec_t ast_vec);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "spl_fold.h"
|
||||
|
||||
static usize spl_fold_bucket_hash(usize k) { return MAP_HASH_INT(k); }
|
||||
static int spl_fold_bucket_cmp(usize a, usize b) { return MAP_CMP_INT(a, b); }
|
||||
|
||||
void spl_fold_init(spl_fold_t *fold, void *ctx, spl_fold_eq_fn eq, spl_fold_next_fn next,
|
||||
spl_fold_set_next_fn set_next) {
|
||||
map_init(fold->buckets, spl_fold_bucket_hash, spl_fold_bucket_cmp);
|
||||
fold->ctx = ctx;
|
||||
fold->eq = eq;
|
||||
fold->next = next;
|
||||
fold->set_next = set_next;
|
||||
}
|
||||
|
||||
void spl_fold_drop(spl_fold_t *fold) { map_free(fold->buckets); }
|
||||
|
||||
spl_fold_id_t spl_fold_find(const spl_fold_t *fold, usize hash, const void *probe) {
|
||||
spl_fold_id_t cur = 0;
|
||||
if (!map_get(fold->buckets, hash, &cur))
|
||||
return 0;
|
||||
while (cur) {
|
||||
if (fold->eq(fold->ctx, cur, probe))
|
||||
return cur;
|
||||
cur = fold->next(fold->ctx, cur);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spl_fold_insert(spl_fold_t *fold, usize hash, spl_fold_id_t id) {
|
||||
spl_fold_id_t head = 0;
|
||||
map_get(fold->buckets, hash, &head);
|
||||
fold->set_next(fold->ctx, id, head);
|
||||
map_put(fold->buckets, hash, id);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef __SPL_FOLD_H__
|
||||
#define __SPL_FOLD_H__
|
||||
|
||||
#include "../stage0/include/utils.h"
|
||||
|
||||
/* 通用 intrusive 哈希集合,对应 LLVM FoldingSet 的桶链部分。
|
||||
- 节点表由调用方持有,节点自带 next 字段;
|
||||
- hash 只用于选择桶(本实现以完整 hash 值作桶 map 的 key),
|
||||
相等始终由 eq 回调做结构比较;
|
||||
- 只增不删,适合类型 intern。 */
|
||||
typedef usize spl_fold_id_t;
|
||||
|
||||
/* 相等返回非零;probe 指向调用方的临时(尚未入表)节点。 */
|
||||
typedef int (*spl_fold_eq_fn)(void *ctx, spl_fold_id_t node, const void *probe);
|
||||
typedef spl_fold_id_t (*spl_fold_next_fn)(void *ctx, spl_fold_id_t node);
|
||||
typedef void (*spl_fold_set_next_fn)(void *ctx, spl_fold_id_t node, spl_fold_id_t next);
|
||||
|
||||
typedef struct {
|
||||
MAP(usize, spl_fold_id_t) buckets;
|
||||
void *ctx;
|
||||
spl_fold_eq_fn eq;
|
||||
spl_fold_next_fn next;
|
||||
spl_fold_set_next_fn set_next;
|
||||
} spl_fold_t;
|
||||
|
||||
void spl_fold_init(spl_fold_t *fold, void *ctx, spl_fold_eq_fn eq, spl_fold_next_fn next,
|
||||
spl_fold_set_next_fn set_next);
|
||||
void spl_fold_drop(spl_fold_t *fold);
|
||||
|
||||
/* 命中返回已存在的 id,否则返回 0。 */
|
||||
spl_fold_id_t spl_fold_find(const spl_fold_t *fold, usize hash, const void *probe);
|
||||
|
||||
/* 把已分配 id 的节点挂到对应桶头。 */
|
||||
void spl_fold_insert(spl_fold_t *fold, usize hash, spl_fold_id_t id);
|
||||
|
||||
#endif /* __SPL_FOLD_H__ */
|
||||
+31
-14
@@ -2,6 +2,8 @@
|
||||
|
||||
static const spl_intrin_info_t g_intrin_table[] = {
|
||||
[SPL_INTRIN_NONE] = {0},
|
||||
[SPL_INTRIN_BITSIZEOF] = {"@bitsizeof", SPL_INTRIN_BITSIZEOF, SPL_INTRIN_FORM_CALL, 1, 1,
|
||||
SPL_INTRIN_RESULT_USIZE},
|
||||
[SPL_INTRIN_SIZEOF] = {"@sizeof", SPL_INTRIN_SIZEOF, SPL_INTRIN_FORM_CALL, 1, 1,
|
||||
SPL_INTRIN_RESULT_USIZE},
|
||||
[SPL_INTRIN_ALIGNOF] = {"@alignof", SPL_INTRIN_ALIGNOF, SPL_INTRIN_FORM_CALL, 1, 1,
|
||||
@@ -20,18 +22,32 @@ static const spl_intrin_info_t g_intrin_table[] = {
|
||||
SPL_INTRIN_RESULT_UNIT},
|
||||
[SPL_INTRIN_FLOAT_CAST] = {"@float_cast", SPL_INTRIN_FLOAT_CAST, SPL_INTRIN_FORM_CALL, 2, 2,
|
||||
SPL_INTRIN_RESULT_UNIT},
|
||||
[SPL_INTRIN_INT_FROM_FLOAT] = {"@int_from_float", SPL_INTRIN_INT_FROM_FLOAT,
|
||||
SPL_INTRIN_FORM_CALL, 2, 2, SPL_INTRIN_RESULT_UNIT},
|
||||
[SPL_INTRIN_FLOAT_FROM_INT] = {"@float_from_int", SPL_INTRIN_FLOAT_FROM_INT,
|
||||
SPL_INTRIN_FORM_CALL, 2, 2, SPL_INTRIN_RESULT_UNIT},
|
||||
[SPL_INTRIN_INT_FROM_BOOL] = {"@int_from_bool", SPL_INTRIN_INT_FROM_BOOL, SPL_INTRIN_FORM_CALL,
|
||||
1, 2, SPL_INTRIN_RESULT_UNIT},
|
||||
[SPL_INTRIN_INT_FROM_PTR] = {"@int_from_ptr", SPL_INTRIN_INT_FROM_PTR, SPL_INTRIN_FORM_CALL, 1,
|
||||
2, SPL_INTRIN_RESULT_UNIT},
|
||||
[SPL_INTRIN_PTR_FROM_INT] = {"@ptr_from_int", SPL_INTRIN_PTR_FROM_INT, SPL_INTRIN_FORM_CALL, 2,
|
||||
2, SPL_INTRIN_RESULT_UNIT},
|
||||
[SPL_INTRIN_FLOAT2INT] = {"@float2int", SPL_INTRIN_FLOAT2INT, SPL_INTRIN_FORM_CALL, 2, 2,
|
||||
SPL_INTRIN_RESULT_UNIT},
|
||||
[SPL_INTRIN_INT2FLOAT] = {"@int2float", SPL_INTRIN_INT2FLOAT, SPL_INTRIN_FORM_CALL, 2, 2,
|
||||
SPL_INTRIN_RESULT_UNIT},
|
||||
[SPL_INTRIN_BOOL2INT] = {"@bool2int", SPL_INTRIN_BOOL2INT, SPL_INTRIN_FORM_CALL, 1, 2,
|
||||
SPL_INTRIN_RESULT_UNIT},
|
||||
[SPL_INTRIN_PTR2INT] = {"@ptr2int", SPL_INTRIN_PTR2INT, SPL_INTRIN_FORM_CALL, 1, 2,
|
||||
SPL_INTRIN_RESULT_UNIT},
|
||||
[SPL_INTRIN_INT2PTR] = {"@int2ptr", SPL_INTRIN_INT2PTR, SPL_INTRIN_FORM_CALL, 2, 2,
|
||||
SPL_INTRIN_RESULT_UNIT},
|
||||
[SPL_INTRIN_PTR_CAST] = {"@ptr_cast", SPL_INTRIN_PTR_CAST, SPL_INTRIN_FORM_CALL, 2, 2,
|
||||
SPL_INTRIN_RESULT_UNIT},
|
||||
[SPL_INTRIN_ENUM2INT] = {"@enum2int", SPL_INTRIN_ENUM2INT, SPL_INTRIN_FORM_CALL, 1, 1,
|
||||
SPL_INTRIN_RESULT_USIZE},
|
||||
[SPL_INTRIN_OFFSETOF] = {"@offsetof", SPL_INTRIN_OFFSETOF, SPL_INTRIN_FORM_CALL, 2, 2,
|
||||
SPL_INTRIN_RESULT_USIZE},
|
||||
[SPL_INTRIN_FIELD_COUNT] = {"@field_count", SPL_INTRIN_FIELD_COUNT, SPL_INTRIN_FORM_CALL, 1, 1,
|
||||
SPL_INTRIN_RESULT_USIZE},
|
||||
[SPL_INTRIN_FILE_NAME] = {"@file_name", SPL_INTRIN_FILE_NAME, SPL_INTRIN_FORM_CALL, 0, 0,
|
||||
SPL_INTRIN_RESULT_USIZE},
|
||||
[SPL_INTRIN_LINE] = {"@line", SPL_INTRIN_LINE, SPL_INTRIN_FORM_CALL, 0, 0,
|
||||
SPL_INTRIN_RESULT_USIZE},
|
||||
[SPL_INTRIN_COL] = {"@col", SPL_INTRIN_COL, SPL_INTRIN_FORM_CALL, 0, 0,
|
||||
SPL_INTRIN_RESULT_USIZE},
|
||||
[SPL_INTRIN_FN_NAME] = {"@fn_name", SPL_INTRIN_FN_NAME, SPL_INTRIN_FORM_CALL, 0, 0,
|
||||
SPL_INTRIN_RESULT_USIZE},
|
||||
};
|
||||
|
||||
static const usize g_intrin_count = sizeof(g_intrin_table) / sizeof(g_intrin_table[0]);
|
||||
@@ -86,14 +102,15 @@ spl_intrin_t spl_intrin_lookup_cast(spl_type_t *type, spl_type_id_t ft, spl_type
|
||||
if (f->kind == SPL_TYPE_FLOAT && t->kind == SPL_TYPE_FLOAT)
|
||||
return SPL_INTRIN_FLOAT_CAST;
|
||||
if (f->kind == SPL_TYPE_BOOL && t->kind == SPL_TYPE_INT)
|
||||
return SPL_INTRIN_INT_FROM_BOOL;
|
||||
return SPL_INTRIN_BOOL2INT;
|
||||
if (f->kind == SPL_TYPE_PTR && t->kind == SPL_TYPE_INT)
|
||||
return SPL_INTRIN_INT_FROM_PTR;
|
||||
return SPL_INTRIN_PTR2INT;
|
||||
if (f->kind == SPL_TYPE_INT && t->kind == SPL_TYPE_PTR)
|
||||
return SPL_INTRIN_PTR_FROM_INT;
|
||||
return SPL_INTRIN_INT2PTR;
|
||||
if (f->kind == SPL_TYPE_PTR && t->kind == SPL_TYPE_PTR)
|
||||
return SPL_INTRIN_PTR_CAST;
|
||||
|
||||
/* int float 刻意不在这里:只能通过显式 builtin 调用。 */
|
||||
/* int float 刻意不在这里:只能通过显式 builtin 调用。
|
||||
enum<->int 也刻意禁止(用 @enum2int 取 tag)。 */
|
||||
return SPL_INTRIN_NONE;
|
||||
}
|
||||
+14
-5
@@ -6,6 +6,7 @@
|
||||
|
||||
typedef enum {
|
||||
SPL_INTRIN_NONE = 0,
|
||||
SPL_INTRIN_BITSIZEOF,
|
||||
SPL_INTRIN_SIZEOF,
|
||||
SPL_INTRIN_ALIGNOF,
|
||||
SPL_INTRIN_ASSERT,
|
||||
@@ -15,12 +16,20 @@ typedef enum {
|
||||
SPL_INTRIN_TRUNCATE,
|
||||
SPL_INTRIN_BIT_CAST,
|
||||
SPL_INTRIN_FLOAT_CAST,
|
||||
SPL_INTRIN_INT_FROM_FLOAT,
|
||||
SPL_INTRIN_FLOAT_FROM_INT,
|
||||
SPL_INTRIN_INT_FROM_BOOL,
|
||||
SPL_INTRIN_INT_FROM_PTR,
|
||||
SPL_INTRIN_PTR_FROM_INT,
|
||||
SPL_INTRIN_FLOAT2INT,
|
||||
SPL_INTRIN_INT2FLOAT,
|
||||
SPL_INTRIN_BOOL2INT,
|
||||
SPL_INTRIN_PTR2INT,
|
||||
SPL_INTRIN_INT2PTR,
|
||||
SPL_INTRIN_PTR_CAST,
|
||||
SPL_INTRIN_ENUM2INT,
|
||||
SPL_INTRIN_OFFSETOF,
|
||||
SPL_INTRIN_FIELD_COUNT,
|
||||
/* 调用点位置信息(供日志/性能分析库使用) */
|
||||
SPL_INTRIN_FILE_NAME,
|
||||
SPL_INTRIN_LINE,
|
||||
SPL_INTRIN_COL,
|
||||
SPL_INTRIN_FN_NAME,
|
||||
} spl_intrin_t;
|
||||
|
||||
typedef enum {
|
||||
|
||||
+3
-2
@@ -203,8 +203,9 @@ static void ir_dump_node(const spl_type_t *ty, spl_ir_node_t *n) {
|
||||
printf(" float=%f", n->type_const.float_lit);
|
||||
} else if (t && t->kind == SPL_TYPE_FN) {
|
||||
printf(" fn#%zu", n->type_const.fn);
|
||||
// } else if (t && t->kind == SPL_TYPE_PTR) {
|
||||
// printf(" str=\"%s\"", n->type_const.cstr_lit ? n->type_const.cstr_lit : "?");
|
||||
} else if (t && t->kind == SPL_TYPE_ARRAY) {
|
||||
/* 字节常量:不打印宿主指针(不确定),只标记 bytes。 */
|
||||
printf(" bytes");
|
||||
} else if (t && t->kind == SPL_TYPE_INT && t->int_type.bits == 8) {
|
||||
printf(" char='%c'", n->type_const.ch_lit);
|
||||
} else {
|
||||
|
||||
+1
-2
@@ -2,11 +2,10 @@
|
||||
#define __SPL_IR_H__
|
||||
|
||||
#include "../stage0/include/utils.h"
|
||||
#include "spl_attr.h"
|
||||
#include "spl_dbg.h"
|
||||
#include "spl_type.h"
|
||||
|
||||
#include "spl_attr.h"
|
||||
|
||||
/* clang-format off */
|
||||
#define SPL_IR_FN_TABLE \
|
||||
X(arith.add, V0, SPL_IR_ARITH_ADD) \
|
||||
|
||||
+374
-83
@@ -3,45 +3,24 @@
|
||||
|
||||
static usize align_up(usize x, usize a);
|
||||
|
||||
/* size/align/offset 统一取自 layout(spl_layout 是唯一准确来源)。 */
|
||||
static const spl_layout_info_t *ir_layout(spl_ir2vm_t *ctx, spl_type_id_t tid) {
|
||||
return spl_layout_info_of(ctx->layout, tid);
|
||||
}
|
||||
|
||||
static usize ir_type_size(spl_ir2vm_t *ctx, spl_type_id_t tid) {
|
||||
spl_type_node_t *tn = spl_type_node(ctx->type, tid);
|
||||
if (!tn)
|
||||
return 8;
|
||||
switch (tn->kind) {
|
||||
case SPL_TYPE_UNIT:
|
||||
const spl_layout_info_t *info = ir_layout(ctx, tid);
|
||||
if (!info)
|
||||
return 0;
|
||||
case SPL_TYPE_BOOL:
|
||||
return (info->size_bits + 7) / 8;
|
||||
}
|
||||
|
||||
static usize ir_type_align(spl_ir2vm_t *ctx, spl_type_id_t tid) {
|
||||
const spl_layout_info_t *info = ir_layout(ctx, tid);
|
||||
if (!info)
|
||||
return 1;
|
||||
case SPL_TYPE_INT:
|
||||
return (tn->int_type.bits + 7) / 8;
|
||||
case SPL_TYPE_FLOAT:
|
||||
return (tn->float_type.bits + 7) / 8;
|
||||
case SPL_TYPE_PTR:
|
||||
return sizeof(void *);
|
||||
case SPL_TYPE_FN:
|
||||
return sizeof(void *);
|
||||
case SPL_TYPE_ARRAY:
|
||||
return ir_type_size(ctx, tn->array_type.element) * tn->array_type.len;
|
||||
case SPL_TYPE_ENUM: {
|
||||
usize tag_sz = ir_type_size(ctx, tn->agg_type.tag_type);
|
||||
usize max_payload = 0;
|
||||
for (usize i = 0; i < vec_size(tn->agg_type.variants); ++i) {
|
||||
usize vs = ir_type_size(ctx, vec_at(tn->agg_type.variants, i).type_id);
|
||||
if (vs > max_payload)
|
||||
max_payload = vs;
|
||||
}
|
||||
return align_up(tag_sz, 8) + max_payload;
|
||||
}
|
||||
case SPL_TYPE_STRUCT:
|
||||
case SPL_TYPE_UNION: {
|
||||
usize s = 0;
|
||||
for (usize i = 0; i < vec_size(tn->agg_type.variants); ++i)
|
||||
s += ir_type_size(ctx, vec_at(tn->agg_type.variants, i).type_id);
|
||||
return s;
|
||||
}
|
||||
default:
|
||||
return 8;
|
||||
}
|
||||
usize a = info->align_bits / 8;
|
||||
return a ? a : 1;
|
||||
}
|
||||
|
||||
static bool ir_is_aggregate(spl_ir2vm_t *ctx, spl_type_id_t tid) {
|
||||
@@ -94,15 +73,13 @@ static usize ir_agg_field_off(spl_ir2vm_t *ctx, spl_type_id_t tid, usize idx) {
|
||||
spl_type_node_t *tn = spl_type_node(ctx->type, tid);
|
||||
if (!tn)
|
||||
return 0;
|
||||
if (tn->kind == SPL_TYPE_STRUCT || tn->kind == SPL_TYPE_UNION) {
|
||||
usize off = 0;
|
||||
for (usize i = 0; i < idx && i < vec_size(tn->agg_type.variants); ++i)
|
||||
off += ir_type_size(ctx, vec_at(tn->agg_type.variants, i).type_id);
|
||||
return off;
|
||||
}
|
||||
if (tn->kind == SPL_TYPE_ARRAY)
|
||||
return idx * ir_type_size(ctx, tn->array_type.element);
|
||||
|
||||
const spl_layout_info_t *info = ir_layout(ctx, tid);
|
||||
if (!info || idx >= vec_size(info->field_bit_offsets))
|
||||
return 0;
|
||||
return vec_at(info->field_bit_offsets, idx) / 8;
|
||||
}
|
||||
|
||||
static int get_or_add_native(spl_ir2vm_t *ctx, const char *name) {
|
||||
@@ -367,6 +344,13 @@ typedef struct {
|
||||
frame_layout_t frame;
|
||||
VEC(usize) node_ip;
|
||||
fix_vec_t *fixups;
|
||||
/* 块内栈化(opt) */
|
||||
int opt;
|
||||
uint8_t *stk_loc; /* per node: 1 = 值当前在操作数栈上 */
|
||||
usize *stk_pos; /* 在虚拟栈中的位置 */
|
||||
usize *use_left; /* 剩余使用次数 */
|
||||
usize *at_pos; /* 每个栈位置对应的 node ref */
|
||||
usize tracked; /* 当前栈上被跟踪的值个数 */
|
||||
} cgen_t;
|
||||
|
||||
static void emit(cgen_t *cg, uint8_t op, uint8_t ty, spl_vm_val_t imm) {
|
||||
@@ -401,6 +385,240 @@ static void cg_store_at(cgen_t *cg, usize addr, uint8_t k) {
|
||||
CG_STORE(cg, k);
|
||||
}
|
||||
|
||||
/* ---------- 块内栈化(opt)辅助 ---------- */
|
||||
|
||||
static uint8_t node_result_kind(cgen_t *cg, usize ref) {
|
||||
const spl_ir_node_t *n = &vec_at(cg->irf->nodes, ref);
|
||||
return vm_kind_of(cg->ctx, ir_result_tid(cg->ctx, n));
|
||||
}
|
||||
|
||||
static bool node_stackable(cgen_t *cg, const spl_ir_node_t *n) {
|
||||
switch (n->kind) {
|
||||
case SPL_IR_ARITH_ADD:
|
||||
case SPL_IR_ARITH_SUB:
|
||||
case SPL_IR_ARITH_MUL:
|
||||
case SPL_IR_ARITH_DIV:
|
||||
case SPL_IR_ARITH_REM:
|
||||
case SPL_IR_ARITH_NEG:
|
||||
case SPL_IR_ARITH_AND:
|
||||
case SPL_IR_ARITH_OR:
|
||||
case SPL_IR_ARITH_XOR:
|
||||
case SPL_IR_ARITH_SHL:
|
||||
case SPL_IR_ARITH_SHR:
|
||||
case SPL_IR_ARITH_NOT:
|
||||
case SPL_IR_CMP_EQ:
|
||||
case SPL_IR_CMP_NE:
|
||||
case SPL_IR_CMP_LT:
|
||||
case SPL_IR_CMP_LE:
|
||||
case SPL_IR_CMP_GT:
|
||||
case SPL_IR_CMP_GE:
|
||||
case SPL_IR_CAST_TRUNC:
|
||||
case SPL_IR_CAST_ZEXT:
|
||||
case SPL_IR_CAST_SEXT:
|
||||
case SPL_IR_CAST_FEXT:
|
||||
case SPL_IR_CAST_FTRUNC:
|
||||
case SPL_IR_CAST_BITCAST:
|
||||
case SPL_IR_CAST_PTR2INT:
|
||||
case SPL_IR_CAST_INT2PTR:
|
||||
case SPL_IR_CAST_BOOL2INT:
|
||||
case SPL_IR_CASE_INT2FLOAT:
|
||||
case SPL_IR_CASE_FLOAT2INT:
|
||||
case SPL_IR_TYPE_CONST: {
|
||||
spl_type_node_t *tn = spl_type_node(cg->ctx->type, n->type_const.tid);
|
||||
return tn && tn->kind != SPL_TYPE_UNIT;
|
||||
}
|
||||
case SPL_IR_TYPE_BITSIZEOF:
|
||||
case SPL_IR_TYPE_SIZEOF:
|
||||
case SPL_IR_TYPE_ALIGNOF:
|
||||
case SPL_IR_TYPE_OFFSETOF:
|
||||
case SPL_IR_TYPE_FIELD_COUNT:
|
||||
case SPL_IR_MEM_OFFSET:
|
||||
case SPL_IR_MEM_FIELD_PTR:
|
||||
return true;
|
||||
case SPL_IR_MEM_LOAD:
|
||||
return !ir_is_aggregate(cg->ctx, n->mem_load.tid);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* 该节点必须使用槽(直接按槽偏移读写 / 控制转移 / 聚合数据),生成前需 flush。 */
|
||||
static bool node_needs_slots(cgen_t *cg, const spl_ir_node_t *n) {
|
||||
switch (n->kind) {
|
||||
case SPL_IR_MEM_ALLOCA:
|
||||
case SPL_IR_MEM_GLOBAL_ALLOC:
|
||||
case SPL_IR_MEM_COPY:
|
||||
case SPL_IR_MEM_SET:
|
||||
case SPL_IR_AGG_CONSTRUCT:
|
||||
case SPL_IR_AGG_EXTRACT:
|
||||
case SPL_IR_AGG_INSERT:
|
||||
case SPL_IR_CONTROL_SELECT:
|
||||
case SPL_IR_CONTROL_BR:
|
||||
case SPL_IR_CONTROL_JMP:
|
||||
case SPL_IR_CONTROL_RET:
|
||||
case SPL_IR_CONTROL_UNREACHABLE:
|
||||
case SPL_IR_CONTROL_TRAP:
|
||||
case SPL_IR_DBG_PANIC:
|
||||
case SPL_IR_DBG_BREAKPOINT:
|
||||
return true;
|
||||
case SPL_IR_MEM_LOAD:
|
||||
return ir_is_aggregate(cg->ctx, n->mem_load.tid);
|
||||
case SPL_IR_MEM_STORE:
|
||||
return ir_is_aggregate(cg->ctx, n->mem_store.tid);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* 取操作数到 TOS:若在栈上就 PICK,否则从槽 load。j = 本节点已压入的操作数个数。 */
|
||||
static void cg_operand(cgen_t *cg, usize ref, uint8_t k, usize j) {
|
||||
if (cg->opt) {
|
||||
if (cg->use_left[ref] > 0)
|
||||
cg->use_left[ref]--;
|
||||
if (cg->stk_loc[ref]) {
|
||||
/* 始终复制一份到 TOS,绝不消费栈上的“被跟踪原值”,否则位置模型失效。 */
|
||||
usize dist = cg->tracked + j - 1 - cg->stk_pos[ref];
|
||||
emit(cg, SPL_PICK, SPL_VOID, dist);
|
||||
return;
|
||||
}
|
||||
}
|
||||
cg_load_slot(cg, ref, k);
|
||||
}
|
||||
|
||||
/* 节点结果:若仍会被使用则留在栈上,否则 DROP。 */
|
||||
static void cg_result(cgen_t *cg, usize ref, uint8_t k) {
|
||||
if (cg->opt) {
|
||||
if (cg->use_left[ref] > 0) {
|
||||
cg->stk_loc[ref] = 1;
|
||||
cg->stk_pos[ref] = cg->tracked;
|
||||
cg->at_pos[cg->tracked] = ref;
|
||||
cg->tracked++;
|
||||
} else {
|
||||
emit(cg, SPL_DROP, SPL_VOID, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
cg_store_slot(cg, ref, k);
|
||||
}
|
||||
|
||||
/* 把栈上仍存活的值溢出回各自槽(TOS 起),死值 DROP;清空跟踪。 */
|
||||
static void cg_flush(cgen_t *cg) {
|
||||
if (!cg->opt)
|
||||
return;
|
||||
while (cg->tracked > 0) {
|
||||
cg->tracked--;
|
||||
usize r = cg->at_pos[cg->tracked];
|
||||
cg->stk_loc[r] = 0;
|
||||
if (cg->use_left[r] > 0) {
|
||||
uint8_t k = node_result_kind(cg, r);
|
||||
cg_store_slot(cg, r, k);
|
||||
} else {
|
||||
emit(cg, SPL_DROP, SPL_VOID, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 统计被引用次数(所有节点载荷中的 node ref)。 */
|
||||
static void count_ref(cgen_t *cg, usize r) {
|
||||
if (r != 0 && r < vec_size(cg->irf->nodes))
|
||||
cg->use_left[r]++;
|
||||
}
|
||||
static void count_refs_vec(cgen_t *cg, const spl_ir_node_ref_vec_t *v) {
|
||||
for (usize i = 0; i < vec_size(*v); i++)
|
||||
count_ref(cg, vec_at(*v, i));
|
||||
}
|
||||
static void count_refs(cgen_t *cg, const spl_ir_node_t *n) {
|
||||
switch (n->kind) {
|
||||
case SPL_IR_ARITH_ADD: case SPL_IR_ARITH_SUB: case SPL_IR_ARITH_MUL:
|
||||
case SPL_IR_ARITH_DIV: case SPL_IR_ARITH_REM: case SPL_IR_ARITH_NEG:
|
||||
case SPL_IR_ARITH_ABS: case SPL_IR_ARITH_AND: case SPL_IR_ARITH_OR:
|
||||
case SPL_IR_ARITH_XOR: case SPL_IR_ARITH_SHL: case SPL_IR_ARITH_SHR:
|
||||
case SPL_IR_ARITH_NOT:
|
||||
count_ref(cg, n->arith.left);
|
||||
count_ref(cg, n->arith.right);
|
||||
break;
|
||||
case SPL_IR_CMP_EQ: case SPL_IR_CMP_NE: case SPL_IR_CMP_LT:
|
||||
case SPL_IR_CMP_LE: case SPL_IR_CMP_GT: case SPL_IR_CMP_GE:
|
||||