Compare commits
13
Commits
b5a7a811b3
..
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc2d8f8db4 | ||
|
|
76f980654c | ||
|
|
0e38858785 | ||
|
|
40cffa9cb5 | ||
|
|
0b8f649818 | ||
|
|
c0a86ea552 | ||
|
|
fae017b52d | ||
|
|
9ecb92cb03 | ||
|
|
70085355db | ||
|
|
d10578d267 | ||
|
|
51d408d524 | ||
|
|
846f441e3a | ||
|
|
e02b0c633a |
No files matched your search
@@ -109,6 +109,10 @@ PostfixExpr = {
|
|||||||
|
|
||||||
ExprList = { (Expr ~ COMMA)* ~ Expr? }
|
ExprList = { (Expr ~ COMMA)* ~ Expr? }
|
||||||
|
|
||||||
|
// 注:类型即表达式**仅限 comptime 上下文**(`comptime { ... }`、`comptime expr`、
|
||||||
|
// `comptime fn` 的函数体)内。在这些上下文中,PrimaryExpr 额外接受
|
||||||
|
// `AggregateTypeExpr`(如 `struct { ... }`)以及 `type` 元类型;其它上下文
|
||||||
|
// 中类型只允许出现在 TypeExpr 位置。
|
||||||
PrimaryExpr = {
|
PrimaryExpr = {
|
||||||
FLOAT_LITERAL
|
FLOAT_LITERAL
|
||||||
| INTEGER_LITERAL
|
| INTEGER_LITERAL
|
||||||
@@ -232,6 +236,7 @@ TypePrimary = {
|
|||||||
| KEYWORD_bool
|
| KEYWORD_bool
|
||||||
| KEYWORD_void
|
| KEYWORD_void
|
||||||
| KEYWORD_ptr
|
| KEYWORD_ptr
|
||||||
|
| KEYWORD_type // `type` 元类型(编译期类型的值类型)
|
||||||
| FnTypeExpr
|
| FnTypeExpr
|
||||||
| AggregateTypeExpr
|
| AggregateTypeExpr
|
||||||
| TypeParenExpr
|
| 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)
|
#define nob_cc_flags(cmd) nob_cmd_append(cmd, SPL_CFLAGS)
|
||||||
#undef UNICODE
|
#undef UNICODE
|
||||||
#define NOB_IMPLEMENTATION
|
#define NOB_IMPLEMENTATION
|
||||||
#include "nob.h"
|
#include "nob/nob.h"
|
||||||
#undef NOB_IMPLEMENTATION
|
#undef NOB_IMPLEMENTATION
|
||||||
#include "nob_popen.h"
|
#include "nob/nob_popen.h"
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <consoleapi2.h>
|
#include <consoleapi2.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -17,53 +19,7 @@
|
|||||||
// object files get rebuilt.
|
// object files get rebuilt.
|
||||||
static const char *CFLAGS_STAMP = BUILD_FOLDER ".cflags";
|
static const char *CFLAGS_STAMP = BUILD_FOLDER ".cflags";
|
||||||
|
|
||||||
/* clang-format off */
|
#include "nob/nob_data.h"
|
||||||
#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_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",
|
|
||||||
"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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *obj_path(const char *src) {
|
static const char *obj_path(const char *src) {
|
||||||
// "stage0/spl_vm.c" -> "build/stage0_spl_vm.o"
|
// "stage0/spl_vm.c" -> "build/stage0_spl_vm.o"
|
||||||
@@ -89,6 +45,12 @@ static const char *exe_path(const char *name) {
|
|||||||
#endif
|
#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) {
|
static bool sync_cflags_stamp(void) {
|
||||||
Nob_String_View cflags = nob_sv_from_cstr(CFLAGS_STR);
|
Nob_String_View cflags = nob_sv_from_cstr(CFLAGS_STR);
|
||||||
Nob_String_Builder old = {0};
|
Nob_String_Builder old = {0};
|
||||||
@@ -106,138 +68,191 @@ static bool sync_cflags_stamp(void) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 头文件依赖:收集 stage0/stage1 下的所有 .h,作为每个 object 的输入。
|
|
||||||
// nob 不解析 #include,这里用“任一头文件变化则重建全部 object”的保守策略,
|
|
||||||
// 避免头文件改动后各 .o 结构体布局/常量不一致导致的隐蔽错误。
|
|
||||||
static Nob_Files g_headers = {0};
|
|
||||||
|
|
||||||
static bool collect_header_entry(Nob_Walk_Entry entry) {
|
|
||||||
if (entry.type != NOB_FILE_REGULAR)
|
|
||||||
return true;
|
|
||||||
const char *dot = strrchr(entry.path, '.');
|
|
||||||
if (!dot || strcmp(dot, ".h") != 0)
|
|
||||||
return true;
|
|
||||||
if (strstr(entry.path, ".old") != NULL)
|
|
||||||
return true;
|
|
||||||
da_append(&g_headers, nob_temp_strdup(entry.path));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool collect_headers(void) {
|
|
||||||
if (g_headers.count > 0)
|
|
||||||
return true;
|
|
||||||
if (!nob_walk_dir("stage0", collect_header_entry))
|
|
||||||
return false;
|
|
||||||
if (!nob_walk_dir("stage1", collect_header_entry))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// stage2 的所有 .spl:作为 splc1.sir 的依赖(不解析 @import,保守全量重建)。
|
|
||||||
static Nob_Files g_spl2 = {0};
|
|
||||||
|
|
||||||
static bool collect_spl2_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;
|
|
||||||
da_append(&g_spl2, nob_temp_strdup(entry.path));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool collect_spl2(void) {
|
|
||||||
if (g_spl2.count > 0)
|
|
||||||
return true;
|
|
||||||
return nob_walk_dir("stage2", collect_spl2_entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool prepare_build(void) {
|
static bool prepare_build(void) {
|
||||||
if (!nob_mkdir_if_not_exists(BUILD_FOLDER))
|
if (!nob_mkdir_if_not_exists(BUILD_FOLDER))
|
||||||
return false;
|
return false;
|
||||||
if (!sync_cflags_stamp())
|
if (!sync_cflags_stamp())
|
||||||
return false;
|
return false;
|
||||||
if (!collect_headers())
|
return true;
|
||||||
return false;
|
|
||||||
return collect_spl2();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
const char *obj = obj_path(src);
|
||||||
if (!nob_file_exists(src)) {
|
if (!nob_file_exists(src)) {
|
||||||
nob_log(NOB_ERROR, "source file `%s` does not exist", src);
|
nob_log(NOB_ERROR, "source file `%s` does not exist", src);
|
||||||
return false;
|
goto defer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 依赖:源文件 + 所有头文件 + cflags 戳。
|
da_append(&objs, obj);
|
||||||
size_t ninputs = g_headers.count + 2;
|
|
||||||
const char **inputs = (const char **)nob_temp_alloc(sizeof(const char *) * ninputs);
|
da_append(&deps, src);
|
||||||
inputs[0] = src;
|
da_append(&deps, CFLAGS_STAMP);
|
||||||
for (size_t i = 0; i < g_headers.count; ++i) {
|
for (size_t j = 0; j < target->exe.headers_count; ++j) {
|
||||||
inputs[i + 1] = g_headers.items[i];
|
da_append(&deps, target->exe.headers[j]);
|
||||||
}
|
}
|
||||||
inputs[ninputs - 1] = CFLAGS_STAMP;
|
|
||||||
|
int needs = nob_needs_rebuild(obj, deps.items, deps.count);
|
||||||
|
deps.count = 0; // 清空 deps,下一轮循环重新收集
|
||||||
|
|
||||||
int needs = nob_needs_rebuild(obj, inputs, ninputs);
|
|
||||||
if (needs < 0)
|
if (needs < 0)
|
||||||
return false;
|
goto defer;
|
||||||
if (needs == 0)
|
if (needs == 0)
|
||||||
return true;
|
continue;
|
||||||
|
|
||||||
Nob_Cmd cmd = {0};
|
|
||||||
nob_cc(&cmd);
|
nob_cc(&cmd);
|
||||||
nob_cc_flags(&cmd);
|
nob_cc_flags(&cmd);
|
||||||
nob_cmd_append(&cmd, "-c");
|
nob_cmd_append(&cmd, "-c");
|
||||||
nob_cc_inputs(&cmd, src);
|
nob_cc_inputs(&cmd, src);
|
||||||
nob_cc_output(&cmd, obj);
|
nob_cc_output(&cmd, obj);
|
||||||
return nob_cmd_run(&cmd);
|
if (!nob_cmd_run(&cmd)) {
|
||||||
}
|
nob_log(NOB_ERROR, "failed to compile `%s`", src);
|
||||||
|
goto defer;
|
||||||
static bool link_exe(const target_t *target) {
|
}
|
||||||
const char *exe = exe_path(target->name);
|
cmd.count = 0; // 清空 cmd,下一轮循环重新构建
|
||||||
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]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
if (needs < 0)
|
||||||
return false;
|
goto defer;
|
||||||
if (needs == 0)
|
if (needs == 0)
|
||||||
return true;
|
return exe;
|
||||||
|
|
||||||
Nob_Cmd cmd = {0};
|
|
||||||
nob_cc(&cmd);
|
nob_cc(&cmd);
|
||||||
nob_cc_flags(&cmd);
|
nob_cmd_append(&cmd, SPL_OPTIMIZATION_FLAG);
|
||||||
for (size_t i = 0; i < target->srcs_count; ++i) {
|
for (size_t i = 0; i < objs.count; ++i) {
|
||||||
nob_cc_inputs(&cmd, objs[i]);
|
nob_cc_inputs(&cmd, objs.items[i]);
|
||||||
}
|
}
|
||||||
nob_cc_output(&cmd, exe);
|
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 char *build_target(const char *name) {
|
||||||
const target_t *target = find_exe_target(name);
|
if (!prepare_build())
|
||||||
if (target == NULL) {
|
return NULL;
|
||||||
nob_log(NOB_ERROR, "unknown target `%s`", name);
|
for (size_t i = 0; i < NOB_ARRAY_LEN(targets); ++i) {
|
||||||
return false;
|
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) {
|
needs = nob_needs_rebuild(target_exe, target->native.crt_deps,
|
||||||
if (!build_object(target->srcs[i])) {
|
target->native.crt_deps_count);
|
||||||
return false;
|
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) {
|
bool run_target_dry(const char *name, Nob_Cmd *args, Nob_Cmd *result) {
|
||||||
for (size_t i = 0; i < NOB_ARRAY_LEN(exe_targets); ++i) {
|
for (size_t i = 0; i < NOB_ARRAY_LEN(runs); ++i) {
|
||||||
if (!build_target(exe_targets[i].name)) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
nob_cmd_append(result, inter);
|
||||||
}
|
}
|
||||||
|
nob_cmd_append(result, exe);
|
||||||
|
nob_cmd_extend(result, args);
|
||||||
return true;
|
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) {
|
static void parse_expect(const char *name, Nob_Cmd_Result *expect) {
|
||||||
@@ -341,30 +356,15 @@ static bool test_once_with_dump(const char *fname, void *user_data) {
|
|||||||
return nob_cmd_run(&cmd);
|
return nob_cmd_run(&cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用 splc0 编译 stage2/splc1.spl -> build/splc1.sir(依赖 stage2 全部 .spl)。
|
// 由源路径派生构建产物路径:build/<basename>.sir
|
||||||
static bool build_splc1(void) {
|
static const char *artifact_sir(const char *src) {
|
||||||
if (!build_target("splc0"))
|
const char *base = src;
|
||||||
return false;
|
for (const char *p = src; *p; ++p)
|
||||||
|
if (*p == '/' || *p == '\\')
|
||||||
const char *src = "stage2/splc1.spl";
|
base = p + 1;
|
||||||
const char *sir = BUILD_FOLDER "splc1.sir";
|
const char *dot = strrchr(base, '.');
|
||||||
|
size_t len = dot ? (size_t)(dot - base) : strlen(base);
|
||||||
size_t ninputs = g_spl2.count + 2;
|
return nob_temp_sprintf(BUILD_FOLDER "%.*s.sir", (int)len, base);
|
||||||
const char **inputs = (const char **)nob_temp_alloc(sizeof(const char *) * ninputs);
|
|
||||||
inputs[0] = src;
|
|
||||||
for (size_t i = 0; i < g_spl2.count; ++i)
|
|
||||||
inputs[i + 1] = g_spl2.items[i];
|
|
||||||
inputs[ninputs - 1] = CFLAGS_STAMP;
|
|
||||||
|
|
||||||
int needs = nob_needs_rebuild(sir, inputs, ninputs);
|
|
||||||
if (needs < 0)
|
|
||||||
return false;
|
|
||||||
if (needs == 0)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
Nob_Cmd cmd = {0};
|
|
||||||
nob_cmd_append(&cmd, exe_path("splc0"), src, sir);
|
|
||||||
return nob_cmd_run(&cmd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool run_capture(Nob_Cmd *cmd, Nob_Cmd_Result *result) {
|
static bool run_capture(Nob_Cmd *cmd, Nob_Cmd_Result *result) {
|
||||||
@@ -388,7 +388,7 @@ static bool test_once_diff(const char *fname, void *user_data) {
|
|||||||
return false;
|
return false;
|
||||||
if (!build_target("spl_cli"))
|
if (!build_target("spl_cli"))
|
||||||
return false;
|
return false;
|
||||||
if (!build_splc1())
|
if (!build_target("splc1"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Nob_Cmd c_cmd = {0};
|
Nob_Cmd c_cmd = {0};
|
||||||
@@ -519,22 +519,18 @@ static void print_help(const char *program_name) {
|
|||||||
nob_log(NOB_INFO, "Commands:");
|
nob_log(NOB_INFO, "Commands:");
|
||||||
nob_log(NOB_INFO, " build [targets...] build all targets or the given ones");
|
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, " 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, " clean remove build artifacts");
|
||||||
nob_log(NOB_INFO, " list list all targets");
|
nob_log(NOB_INFO, " list list all targets");
|
||||||
nob_log(NOB_INFO, " help/-h/--help print this help message");
|
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) {
|
int main(int argc, char **argv) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SetConsoleOutputCP(CP_UTF8);
|
SetConsoleOutputCP(CP_UTF8);
|
||||||
|
SetConsoleCP(CP_UTF8);
|
||||||
#endif
|
#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);
|
set_log_handler(nob_cancer_log_handler);
|
||||||
|
|
||||||
const char *program_name = nob_shift(argv, argc);
|
const char *program_name = nob_shift(argv, argc);
|
||||||
@@ -553,7 +549,7 @@ int main(int argc, char **argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!build_all())
|
if (!build_target(NULL))
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -565,16 +561,11 @@ int main(int argc, char **argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
const char *target_name = nob_shift(argv, argc);
|
const char *target_name = nob_shift(argv, argc);
|
||||||
if (!prepare_build())
|
Nob_Cmd runcmd = {0};
|
||||||
return 1;
|
|
||||||
if (!build_target(target_name))
|
|
||||||
return 1;
|
|
||||||
Nob_Cmd cmd = {0};
|
|
||||||
nob_cmd_append(&cmd, exe_path(target_name));
|
|
||||||
while (argc > 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) {
|
if (strcmp(command_name, "test") == 0) {
|
||||||
@@ -629,11 +620,8 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(command_name, "list") == 0) {
|
if (strcmp(command_name, "list") == 0) {
|
||||||
for (size_t i = 0; i < NOB_ARRAY_LEN(exe_targets); ++i) {
|
for (size_t i = 0; i < NOB_ARRAY_LEN(targets); ++i) {
|
||||||
nob_log(NOB_INFO, "%s:", exe_targets[i].name);
|
nob_log(NOB_INFO, "%s:", 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]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
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
|
#ifndef nullptr
|
||||||
#define nullptr NULL
|
#define nullptr NULL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef SPL_USIZE_TYPEDEF
|
||||||
|
#define SPL_USIZE_TYPEDEF
|
||||||
typedef uintptr_t usize;
|
typedef uintptr_t usize;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SPL_ISIZE_TYPEDEF
|
||||||
|
#define SPL_ISIZE_TYPEDEF
|
||||||
typedef intptr_t isize;
|
typedef intptr_t isize;
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MAP_TYPEOF __typeof__
|
#define MAP_TYPEOF __typeof__
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,10 @@
|
|||||||
#ifndef nullptr
|
#ifndef nullptr
|
||||||
#define nullptr NULL
|
#define nullptr NULL
|
||||||
#endif
|
#endif
|
||||||
typedef size_t usize;
|
#ifndef SPL_USIZE_TYPEDEF
|
||||||
|
#define SPL_USIZE_TYPEDEF
|
||||||
|
typedef uintptr_t usize;
|
||||||
|
#endif
|
||||||
#define __vec_realloc realloc
|
#define __vec_realloc realloc
|
||||||
#define __vec_free free
|
#define __vec_free free
|
||||||
#define __vec_memcpy memcpy
|
#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 debug_cli;
|
||||||
int trace;
|
int trace;
|
||||||
int disasm;
|
int disasm;
|
||||||
|
int profile;
|
||||||
|
int profile_interval;
|
||||||
|
const char *profile_out;
|
||||||
|
int stats;
|
||||||
const char *entry;
|
const char *entry;
|
||||||
const char *path; /* file.sir */
|
const char *path; /* file.sir */
|
||||||
const char **args; /* spl 程序参数(file.sir 之后) */
|
const char **args; /* spl 程序参数(file.sir 之后) */
|
||||||
@@ -43,6 +47,9 @@ static void usage(void) {
|
|||||||
" --disasm disasm dump sir\n"
|
" --disasm disasm dump sir\n"
|
||||||
" --entry <name> entry function (default: main)\n"
|
" --entry <name> entry function (default: main)\n"
|
||||||
" --trace print every instruction\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");
|
" -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;
|
o->trace = 1;
|
||||||
} else if (strcmp(a, "--disasm") == 0) {
|
} else if (strcmp(a, "--disasm") == 0) {
|
||||||
o->disasm = 1;
|
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) {
|
} else if (strcmp(a, "--entry") == 0) {
|
||||||
if (i + 1 >= argc) {
|
if (i + 1 >= argc) {
|
||||||
fprintf(stderr, "spl_cli: --entry needs a name\n");
|
fprintf(stderr, "spl_cli: --entry needs a name\n");
|
||||||
@@ -640,6 +660,8 @@ int main(int argc, const char **argv) {
|
|||||||
if (o.debug) {
|
if (o.debug) {
|
||||||
fprintf(stderr, "spl_cli: debug mode on\n");
|
fprintf(stderr, "spl_cli: debug mode on\n");
|
||||||
spl_vm_set_debug(&vm, 1);
|
spl_vm_set_debug(&vm, 1);
|
||||||
|
} else {
|
||||||
|
spl_vm_set_debug(&vm, 0);
|
||||||
}
|
}
|
||||||
if (o.trace) {
|
if (o.trace) {
|
||||||
fprintf(stderr, "spl_cli: trace mode on\n");
|
fprintf(stderr, "spl_cli: trace mode on\n");
|
||||||
@@ -651,6 +673,11 @@ int main(int argc, const char **argv) {
|
|||||||
spl_prog_drop(&prog);
|
spl_prog_drop(&prog);
|
||||||
return 1;
|
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 */
|
/* spl 程序 argv:argv[0]=file.sir(程序名),argv[1..]=args */
|
||||||
const char **spl_argv = (const char **)malloc(sizeof(char *) * (size_t)(o.nargs + 1));
|
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");
|
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);
|
free(spl_argv);
|
||||||
spl_vm_drop(&vm);
|
spl_vm_drop(&vm);
|
||||||
spl_prog_drop(&prog);
|
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 */
|
/* Round n up to next multiple of 8 */
|
||||||
#define ALIGN8(n) (((n) + 7) & ~7)
|
#define ALIGN8(n) (((n) + 7) & ~7)
|
||||||
|
|
||||||
int spl_prog_load_from_file(const char *fname, spl_prog_t *prog) {
|
static int spl_prog_parse(const unsigned char *data, usize len, spl_prog_t *prog) {
|
||||||
FILE *f;
|
|
||||||
unsigned char *data;
|
|
||||||
long len;
|
|
||||||
const unsigned char *p;
|
const unsigned char *p;
|
||||||
spl_vm_val_t nfuncs, ninsns, nnatives, nstrs, ndata;
|
spl_vm_val_t nfuncs, ninsns, nnatives, nstrs, ndata;
|
||||||
|
|
||||||
if (!fname || !prog)
|
if (!data || !prog)
|
||||||
return -1;
|
return -1;
|
||||||
|
if (len < 48)
|
||||||
f = fopen(fname, "rb");
|
|
||||||
if (!f)
|
|
||||||
return -1;
|
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;
|
p = data;
|
||||||
|
|
||||||
/* magic */
|
/* magic */
|
||||||
if (p[0] != 'S' || p[1] != 'P' || p[2] != 'L' || p[3] != 'B' || p[4] != 'I' || p[5] != 'N' ||
|
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') {
|
p[6] != '\0' || p[7] != '\0') {
|
||||||
free(data);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
p += 8;
|
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);
|
func.name = (char *)malloc((usize)nlen);
|
||||||
if (!func.name) {
|
if (!func.name) {
|
||||||
free(data);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
memcpy(func.name, p, (usize)nlen);
|
memcpy(func.name, p, (usize)nlen);
|
||||||
@@ -202,7 +177,6 @@ int spl_prog_load_from_file(const char *fname, spl_prog_t *prog) {
|
|||||||
/* ---- instructions ---- */
|
/* ---- instructions ---- */
|
||||||
for (spl_vm_val_t i = 0; i < ninsns; i++) {
|
for (spl_vm_val_t i = 0; i < ninsns; i++) {
|
||||||
if ((size_t)(p - data) + 12 > (size_t)len) {
|
if ((size_t)(p - data) + 12 > (size_t)len) {
|
||||||
free(data);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
spl_vm_ins_t ins;
|
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);
|
nat.name = (char *)malloc((usize)nlen);
|
||||||
if (!nat.name) {
|
if (!nat.name) {
|
||||||
free(data);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
memcpy(nat.name, p, (usize)nlen);
|
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;
|
usize pad = ALIGN8((usize)slen) - (usize)slen;
|
||||||
char *s = (char *)malloc((usize)slen + 1);
|
char *s = (char *)malloc((usize)slen + 1);
|
||||||
if (!s) {
|
if (!s) {
|
||||||
free(data);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
memcpy(s, p, (usize)slen);
|
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;
|
usize pad = ALIGN8(entry.size) - entry.size;
|
||||||
entry.data = (unsigned char *)malloc(entry.size);
|
entry.data = (unsigned char *)malloc(entry.size);
|
||||||
if (!entry.data) {
|
if (!entry.data) {
|
||||||
free(data);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
memcpy(entry.data, p, entry.size);
|
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;
|
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) {
|
int spl_prog_store_to_file(const char *fname, spl_prog_t *prog) {
|
||||||
unsigned char *buf, *p;
|
unsigned char *buf, *p;
|
||||||
spl_vm_val_t i;
|
spl_vm_val_t i;
|
||||||
|
|||||||
@@ -9,8 +9,15 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifndef SPL_USIZE_TYPEDEF
|
||||||
|
#define SPL_USIZE_TYPEDEF
|
||||||
typedef uintptr_t usize;
|
typedef uintptr_t usize;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SPL_ISIZE_TYPEDEF
|
||||||
|
#define SPL_ISIZE_TYPEDEF
|
||||||
typedef intptr_t isize;
|
typedef intptr_t isize;
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SPL_VOID,
|
SPL_VOID,
|
||||||
@@ -90,6 +97,7 @@ typedef enum {
|
|||||||
/* 间接内存访问 */ \
|
/* 间接内存访问 */ \
|
||||||
X(SPL_LOAD, "load", 0, 1, 1, "load sizeof(type)-bits zero-extended") \
|
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_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_NCALL, "ncall", 1, 0, 1, "call native function by index") \
|
||||||
X(SPL_NLIB, "nlib", 1, 0, 0, "dlopen library (name idx)") \
|
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,
|
#define X(opcode, name, argc, pop, push, desc) opcode,
|
||||||
SPL_OPCODES(X)
|
SPL_OPCODES(X)
|
||||||
#undef X
|
#undef X
|
||||||
|
SPL_OPCODE_COUNT,
|
||||||
} spl_vm_opcode_t;
|
} spl_vm_opcode_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -192,6 +201,7 @@ void spl_prog_drop(spl_prog_t *prog);
|
|||||||
void spl_prog_dump(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_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_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);
|
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 "spl_syscall.h"
|
||||||
#include "include/core_map.h"
|
#include "include/core_map.h"
|
||||||
#include "include/core_vec.h"
|
#include "include/core_vec.h"
|
||||||
@@ -16,16 +7,12 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
/* =================================================================
|
/* VM 偏移 -> 宿主地址;size=0 表示不做边界检查。 */
|
||||||
* Macro templates
|
#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)))
|
||||||
* Each SYSCALL_N macro:
|
#define VM_ASP(_vm) spl_vm_asp(_vm)
|
||||||
* 1. Validates nargs (prints error and returns -1 on mismatch)
|
|
||||||
* 2. Casts args[n] from spl_val_t to the specified C type via
|
|
||||||
* (type)(uintptr_t) - this handles both integer and pointer types
|
|
||||||
* 3. Evaluates the expression and returns the result as spl_val_t
|
|
||||||
* ================================================================= */
|
|
||||||
|
|
||||||
#define CHECK_NARGS(fname, expected) \
|
#define CHECK_NARGS(fname, expected) \
|
||||||
do { \
|
do { \
|
||||||
@@ -81,9 +68,6 @@
|
|||||||
return (spl_vm_val_t)(uintptr_t)(ret_expr); \
|
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) \
|
#define ADDRCHECK(vm, ptr, size, ctx) \
|
||||||
do { \
|
do { \
|
||||||
uintptr_t _acp = (uintptr_t)(ptr); \
|
uintptr_t _acp = (uintptr_t)(ptr); \
|
||||||
@@ -97,7 +81,6 @@
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* syscall 内部:把当前栈和调用栈打印出来,供出错时定位 */
|
|
||||||
#define SYSCALL_DUMP(vm) \
|
#define SYSCALL_DUMP(vm) \
|
||||||
do { \
|
do { \
|
||||||
fprintf(stderr, " --- syscall dump (sp=%zu fp=%zu cp=%zu ip=%zu) ---\n", (vm)->sp, \
|
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); \
|
spl_vm_stackdump((vm), (vm)->sp); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* syscall 内部:报告致命错误,dump 后让 VM 停在当前 ip */
|
|
||||||
#define SYSCALL_FAIL(vm, ...) \
|
#define SYSCALL_FAIL(vm, ...) \
|
||||||
do { \
|
do { \
|
||||||
fprintf(stderr, "syscall error: "); \
|
fprintf(stderr, "syscall error: "); \
|
||||||
@@ -117,153 +99,428 @@
|
|||||||
return 0; \
|
return 0; \
|
||||||
} while (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
|
* OS syscalls
|
||||||
* ================================================================= */
|
* ================================================================= */
|
||||||
|
|
||||||
/* vm_exit - terminate process with the given exit code */
|
|
||||||
SYSCALL_1(vm_exit, int, (exit(a1), (spl_vm_val_t)0))
|
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))
|
SYSCALL_1(vm_putchar, int, putchar(a1))
|
||||||
|
|
||||||
/* vm_getchar - read a single character from stdin */
|
|
||||||
SYSCALL_0(vm_getchar, getchar())
|
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))
|
SYSCALL_1(vm_putint, int, (fprintf(stdout, "%d", a1), (spl_vm_val_t)0))
|
||||||
|
|
||||||
/* vm_putstr - print a string to stdout (no trailing newline) */
|
/* FILE* 存于 VM 空间,返回其偏移(不透明句柄)。 */
|
||||||
SYSCALL_1(vm_putstr, const char *, (fputs(a1, stdout), (spl_vm_val_t)0))
|
static spl_vm_val_t vm_file_handle(spl_vm_t *vm, FILE *f) {
|
||||||
|
spl_vm_val_t off;
|
||||||
/* vm_fopen - open a file, returns FILE* as spl_val_t */
|
FILE **slot;
|
||||||
SYSCALL_2(vm_fopen, const char *, const char *, (uintptr_t)fopen(a1, a2))
|
if (!f)
|
||||||
|
return 0;
|
||||||
/* vm_fclose - close a file, returns 0 on success */
|
off = (spl_vm_val_t)(uintptr_t)spl_alloc_ex(VM_ASP(vm), sizeof(FILE *));
|
||||||
SYSCALL_1(vm_fclose, FILE *, fclose(a1))
|
if (off == 0)
|
||||||
|
return 0;
|
||||||
/* vm_fread - read from file, returns number of items read */
|
slot = (FILE **)VM_XN(vm, off, sizeof(FILE *));
|
||||||
SYSCALL_4(vm_fread, void *, size_t, size_t, FILE *, fread(a1, a2, a3, a4))
|
if (!slot)
|
||||||
|
return 0;
|
||||||
/* vm_fwrite - write to file, returns number of items written */
|
*slot = f;
|
||||||
SYSCALL_4(vm_fwrite, const void *, size_t, size_t, FILE *, fwrite(a1, a2, a3, a4))
|
return off;
|
||||||
|
|
||||||
/* vm_fsize - get file size in bytes */
|
|
||||||
static spl_vm_val_t vm_fsize(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
|
||||||
(void)_vm;
|
|
||||||
CHECK_NARGS("vm_fsize", 1);
|
|
||||||
FILE *f = (FILE *)(uintptr_t)args[0];
|
|
||||||
long cur = ftell(f);
|
|
||||||
fseek(f, 0, SEEK_END);
|
|
||||||
long sz = ftell(f);
|
|
||||||
fseek(f, cur, SEEK_SET);
|
|
||||||
return (spl_vm_val_t)(uintptr_t)sz;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SYSCALL_0(vm_stdin, stdin)
|
static FILE *vm_file_get(spl_vm_t *vm, spl_vm_val_t off) {
|
||||||
SYSCALL_0(vm_stdout, stdout)
|
FILE **slot = (FILE **)VM_XN(vm, off, sizeof(FILE *));
|
||||||
SYSCALL_0(vm_stderr, stderr)
|
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) {
|
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);
|
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) {
|
if (path == nullptr) {
|
||||||
fprintf(stderr, "filepath can't be null");
|
fprintf(stderr, "filepath can't be null");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
FILE *f = fopen(path, "rb");
|
f = fopen(path, "rb");
|
||||||
if (!f) {
|
if (!f) {
|
||||||
fprintf(stderr, "filepath %s can't be open", path);
|
fprintf(stderr, "filepath %s can't be open", path);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
fseek(f, 0, SEEK_END);
|
fseek(f, 0, SEEK_END);
|
||||||
long sz = ftell(f);
|
sz = ftell(f);
|
||||||
fseek(f, 0, SEEK_SET);
|
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) {
|
if (!buf) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
{
|
||||||
size_t nread = fread(buf, 1, (size_t)sz, f);
|
size_t nread = fread(buf, 1, (size_t)sz, f);
|
||||||
fclose(f);
|
|
||||||
buf[nread] = '\0';
|
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);
|
CHECK_NARGS("vm_alloc", 1);
|
||||||
size_t sz = (size_t)args[0];
|
sz = (uintptr_t)args[0];
|
||||||
if (sz == 0)
|
if (sz == 0)
|
||||||
SYSCALL_FAIL(_vm, "vm_alloc: zero size");
|
SYSCALL_FAIL(_vm, "vm_alloc: zero size");
|
||||||
if (sz > (size_t)1 << 32)
|
if (sz > ((uintptr_t)1 << 32))
|
||||||
SYSCALL_FAIL(_vm, "vm_alloc: size too large (%zu)", sz);
|
SYSCALL_FAIL(_vm, "vm_alloc: size too large (%zu)", (size_t)sz);
|
||||||
void *p = malloc(sz);
|
p = (spl_vm_val_t)(uintptr_t)spl_alloc_ex(VM_ASP(_vm), sz);
|
||||||
if (!p)
|
if (p == 0)
|
||||||
SYSCALL_FAIL(_vm, "vm_alloc: malloc(%zu) failed", sz);
|
SYSCALL_FAIL(_vm, "vm_alloc: alloc(%zu) failed", (size_t)sz);
|
||||||
return (spl_vm_val_t)(uintptr_t)p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static spl_vm_val_t vm_free(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
static spl_vm_val_t spl_sys_vm_free(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||||
(void)_vm;
|
|
||||||
CHECK_NARGS("vm_free", 1);
|
CHECK_NARGS("vm_free", 1);
|
||||||
void *p = (void *)(uintptr_t)args[0];
|
spl_free_ex(VM_ASP(_vm), (void *)(uintptr_t)args[0]);
|
||||||
if (p)
|
|
||||||
free(p);
|
|
||||||
return 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);
|
CHECK_NARGS("vm_realloc", 2);
|
||||||
void *p = (void *)(uintptr_t)args[0];
|
sz = (uintptr_t)args[1];
|
||||||
size_t sz = (size_t)args[1];
|
if (args[0] == 0)
|
||||||
if (!p)
|
|
||||||
SYSCALL_FAIL(_vm, "vm_realloc: NULL ptr");
|
SYSCALL_FAIL(_vm, "vm_realloc: NULL ptr");
|
||||||
if (sz == 0)
|
if (sz == 0)
|
||||||
SYSCALL_FAIL(_vm, "vm_realloc: zero size");
|
SYSCALL_FAIL(_vm, "vm_realloc: zero size");
|
||||||
ADDRCHECK(_vm, p, 1, "vm_realloc");
|
np = (spl_vm_val_t)(uintptr_t)spl_realloc_ex(VM_ASP(_vm), (void *)(uintptr_t)args[0], sz);
|
||||||
void *np = realloc(p, sz);
|
if (np == 0)
|
||||||
if (!np)
|
SYSCALL_FAIL(_vm, "vm_realloc: realloc failed");
|
||||||
SYSCALL_FAIL(_vm, "vm_realloc: realloc(%p, %zu) failed", p, sz);
|
return np;
|
||||||
return (spl_vm_val_t)(uintptr_t)np;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static spl_vm_val_t vm_memcpy(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
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);
|
CHECK_NARGS("vm_memcpy", 3);
|
||||||
void *dst = (void *)(uintptr_t)args[0];
|
n = (uintptr_t)args[2];
|
||||||
const void *src = (const void *)(uintptr_t)args[1];
|
if (n == 0)
|
||||||
size_t size = (size_t)args[2];
|
return args[0];
|
||||||
if (size == 0)
|
dst = VM_X(_vm, args[0]);
|
||||||
return (spl_vm_val_t)(uintptr_t)dst;
|
src = VM_X(_vm, args[1]);
|
||||||
ADDRCHECK(_vm, dst, size, "vm_memcpy(dst)");
|
if (!dst || !src)
|
||||||
ADDRCHECK(_vm, src, size, "vm_memcpy(src)");
|
SYSCALL_FAIL(_vm, "vm_memcpy: invalid address");
|
||||||
memcpy(dst, src, size);
|
vm_memcpy(dst, src, n);
|
||||||
return (spl_vm_val_t)(uintptr_t)dst;
|
return args[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
static spl_vm_val_t vm_memcmp(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
static spl_vm_val_t spl_sys_vm_memmove(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||||
CHECK_NARGS("vm_memcmp", 3);
|
void *dst, *src;
|
||||||
const void *a = (const void *)(uintptr_t)args[0];
|
uintptr_t n;
|
||||||
const void *b = (const void *)(uintptr_t)args[1];
|
CHECK_NARGS("vm_memmove", 3);
|
||||||
size_t size = (size_t)args[2];
|
n = (uintptr_t)args[2];
|
||||||
if (size == 0)
|
if (n == 0)
|
||||||
return 0;
|
return args[0];
|
||||||
ADDRCHECK(_vm, a, size, "vm_memcmp(a)");
|
dst = VM_X(_vm, args[0]);
|
||||||
ADDRCHECK(_vm, b, size, "vm_memcmp(b)");
|
src = VM_X(_vm, args[1]);
|
||||||
return (spl_vm_val_t)memcmp(a, b, size);
|
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) {
|
static spl_vm_val_t vm_printf(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||||
(void)_vm;
|
|
||||||
if (nargs <= 0)
|
if (nargs <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* args[0] 是 SPL 的 []u8 format 参数。VM 里 slice 作为参数传递时,
|
const spl_vm_val_t *fmt_slice =
|
||||||
栈上压的是"指向 slice 结构体的指针",结构为 {ptr:*u8, len:u64} */
|
(const spl_vm_val_t *)VM_XN(_vm, args[0], 2 * sizeof(spl_vm_val_t));
|
||||||
const spl_vm_val_t *fmt_slice = (const spl_vm_val_t *)args[0];
|
const char *fmt;
|
||||||
const char *fmt = (const char *)fmt_slice[0];
|
usize fmt_len;
|
||||||
usize fmt_len = (usize)fmt_slice[1];
|
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) {
|
if (nargs <= 1) {
|
||||||
fwrite(fmt, 1, fmt_len, stdout);
|
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;
|
string_builder_t buffer;
|
||||||
vec_init(buffer);
|
vec_init(buffer);
|
||||||
|
|
||||||
int arg_idx = 1; /* 0 是 format */
|
int arg_idx = 1;
|
||||||
char tmp_buf[32];
|
char tmp_buf[32];
|
||||||
|
|
||||||
for (usize i = 0; i < fmt_len; ++i) {
|
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++;
|
arg_idx++;
|
||||||
break;
|
break;
|
||||||
case 's': {
|
case 's': {
|
||||||
const spl_vm_val_t *s = (const spl_vm_val_t *)args[arg_idx];
|
const spl_vm_val_t *s =
|
||||||
const char *p = (const char *)s[0];
|
(const spl_vm_val_t *)VM_XN(_vm, args[arg_idx], 2 * sizeof(spl_vm_val_t));
|
||||||
usize l = (usize)s[1];
|
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)
|
for (usize j = 0; j < l; ++j)
|
||||||
vec_push(buffer, p[j]);
|
vec_push(buffer, p[j]);
|
||||||
arg_idx++;
|
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;
|
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)
|
* nested-VM syscalls
|
||||||
*
|
|
||||||
* These let SPL code create isolated sub-VMs, load compiled .sir
|
|
||||||
* programs into them, push arguments, call functions, and run.
|
|
||||||
* ================================================================= */
|
* ================================================================= */
|
||||||
|
|
||||||
/* vm_new - create a new sub-VM instance, returns spl_vm_t* */
|
/* 通用双向桥(配置层策略,非 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) {
|
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);
|
CHECK_NARGS("vm_new", 0);
|
||||||
(void)args;
|
(void)args;
|
||||||
|
(void)_vm;
|
||||||
spl_vm_t *vm = (spl_vm_t *)malloc(sizeof(spl_vm_t));
|
spl_vm_t *vm = (spl_vm_t *)malloc(sizeof(spl_vm_t));
|
||||||
if (!vm)
|
if (!vm)
|
||||||
return 0;
|
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;
|
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) {
|
static spl_vm_val_t vm_drop(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||||
(void)_vm;
|
(void)_vm;
|
||||||
CHECK_NARGS("vm_drop", 1);
|
CHECK_NARGS("vm_drop", 1);
|
||||||
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
||||||
if (!vm)
|
if (!vm)
|
||||||
return 0;
|
return 0;
|
||||||
|
if (vm->foreign == spl_bridge_forward && vm->foreign_ctx)
|
||||||
|
free(vm->foreign_ctx);
|
||||||
if (vm->prog) {
|
if (vm->prog) {
|
||||||
spl_prog_drop(vm->prog);
|
spl_prog_drop(vm->prog);
|
||||||
free(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;
|
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) {
|
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);
|
CHECK_NARGS("vm_load", 2);
|
||||||
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
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)
|
if (!vm || !path)
|
||||||
return -1;
|
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;
|
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) {
|
static spl_vm_val_t vm_push(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||||
(void)_vm;
|
(void)_vm;
|
||||||
CHECK_NARGS("vm_push", 2);
|
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;
|
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) {
|
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);
|
CHECK_NARGS("vm_call", 3);
|
||||||
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
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];
|
spl_vm_val_t call_nargs = args[2];
|
||||||
if (!vm || !name)
|
if (!vm || !name)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
spl_vm_func_t *func = spl_prog_get_func(vm->prog, name);
|
if (spl_vm_call_entry(vm, name, (int)call_nargs) != 0) {
|
||||||
if (!func) {
|
fprintf(stderr, "vm_call: function '%s' not found or bad ABI\n", name);
|
||||||
fprintf(stderr, "vm_call: function '%s' not found\n", name);
|
|
||||||
return -1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vm_run - run a sub-VM to completion, returns exit_code */
|
static spl_vm_val_t vm_run(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
||||||
SYSCALL_1(vm_run, spl_vm_t *, spl_vm_run_until(a1, 0))
|
(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
|
* 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) {
|
void spl_syscall_register(spl_prog_t *prog) {
|
||||||
static spl_vm_native_t table[] = {
|
static spl_vm_native_t table[] = {
|
||||||
/* OS operations */
|
|
||||||
{"vm_exit", 0, vm_exit},
|
{"vm_exit", 0, vm_exit},
|
||||||
{"vm_putchar", 0, vm_putchar},
|
{"vm_putchar", 0, vm_putchar},
|
||||||
{"vm_getchar", 0, vm_getchar},
|
{"vm_getchar", 0, vm_getchar},
|
||||||
@@ -474,19 +891,55 @@ void spl_syscall_register(spl_prog_t *prog) {
|
|||||||
{"vm_stdout", 0, vm_stdout},
|
{"vm_stdout", 0, vm_stdout},
|
||||||
{"vm_stderr", 0, vm_stderr},
|
{"vm_stderr", 0, vm_stderr},
|
||||||
{"vm_read_file", 0, vm_read_file},
|
{"vm_read_file", 0, vm_read_file},
|
||||||
{"vm_alloc", 0, vm_alloc},
|
|
||||||
{"vm_free", 0, vm_free},
|
{"vm_alloc", 0, spl_sys_vm_alloc},
|
||||||
{"vm_realloc", 0, vm_realloc},
|
{"vm_free", 0, spl_sys_vm_free},
|
||||||
{"vm_memcpy", 0, vm_memcpy},
|
{"vm_realloc", 0, spl_sys_vm_realloc},
|
||||||
{"vm_memcmp", 0, vm_memcmp},
|
{"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_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_new", 0, vm_new},
|
||||||
|
{"vm_set_foreign", 0, sc_vm_set_foreign},
|
||||||
{"vm_drop", 0, vm_drop},
|
{"vm_drop", 0, vm_drop},
|
||||||
{"vm_load", 0, vm_load},
|
{"vm_load", 0, vm_load},
|
||||||
|
{"vm_load_from_buffer", 0, vm_load_from_buffer},
|
||||||
{"vm_push", 0, vm_push},
|
{"vm_push", 0, vm_push},
|
||||||
{"vm_call", 0, vm_call},
|
{"vm_call", 0, vm_call},
|
||||||
{"vm_run", 0, vm_run},
|
{"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]));
|
int n = (int)(sizeof(table) / sizeof(table[0]));
|
||||||
if (!prog)
|
if (!prog)
|
||||||
|
|||||||
+806
-179
File diff suppressed because it is too large.
Load diff
@@ -3,12 +3,27 @@
|
|||||||
#ifndef __SPL_VM_H__
|
#ifndef __SPL_VM_H__
|
||||||
#define __SPL_VM_H__
|
#define __SPL_VM_H__
|
||||||
|
|
||||||
|
#include "include/core_map.h"
|
||||||
#include "include/core_vec.h"
|
#include "include/core_vec.h"
|
||||||
|
#include "spl_asp.h"
|
||||||
#include "spl_mcode.h"
|
#include "spl_mcode.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define SPL_STACK_CANARY ((spl_vm_val_t)0xDEADBEEFCAFEBABEull)
|
#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 {
|
typedef struct {
|
||||||
uintptr_t saved_sp;
|
uintptr_t saved_sp;
|
||||||
uintptr_t saved_fp;
|
uintptr_t saved_fp;
|
||||||
@@ -19,6 +34,27 @@ typedef struct {
|
|||||||
typedef VEC(spl_vm_val_t) spl_stack_vec_t;
|
typedef VEC(spl_vm_val_t) spl_stack_vec_t;
|
||||||
typedef VEC(spl_callframe_t) spl_frame_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 {
|
typedef struct spl_vm {
|
||||||
spl_stack_vec_t stacks;
|
spl_stack_vec_t stacks;
|
||||||
spl_frame_vec_t frames;
|
spl_frame_vec_t frames;
|
||||||
@@ -27,14 +63,26 @@ typedef struct spl_vm {
|
|||||||
uintptr_t fp; // frame pointer
|
uintptr_t fp; // frame pointer
|
||||||
uintptr_t cp; // call pointer
|
uintptr_t cp; // call pointer
|
||||||
uintptr_t ip; // instr pointer
|
uintptr_t ip; // instr pointer
|
||||||
|
uint64_t steps; // 已执行指令数(供性能分析参考)
|
||||||
|
uint64_t ncalls; // 已执行 CALL/CALLI 次数(供性能分析参考)
|
||||||
int exit_code;
|
int exit_code;
|
||||||
int trace; /* non-zero to print each instruction */
|
int trace; /* non-zero to print each instruction */
|
||||||
int debug; /* non-zero to enable canary checks */
|
int debug; /* non-zero to enable canary checks */
|
||||||
int debug_addr; /* non-zero to check for low-address memory access */
|
int debug_addr; /* non-zero to check for low-address memory access */
|
||||||
int skip_bp; /* non-zero: run_once 忽略下一次断点(continue 越过当前断点指令) */
|
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(usize) breakpoints; /* ip 断点(执行到该指令前暂停,run_once 返回 2) */
|
||||||
VEC(char *) fn_breakpoints; /* 函数名断点(CALL/CALLI 目标函数入口暂停) */
|
VEC(char *) fn_breakpoints; /* 函数名断点(CALL/CALLI 目标函数入口暂停) */
|
||||||
spl_prog_t *prog;
|
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];
|
char error_msg[1024];
|
||||||
struct {
|
struct {
|
||||||
uintptr_t max_stack_depth;
|
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);
|
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_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);
|
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 越过当前断点用 */
|
/* 让 run_once 执行当前指令(忽略一次断点命中);continue 越过当前断点用 */
|
||||||
void spl_vm_skip_breakpoint(spl_vm_t *vm);
|
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_dump_instr(spl_vm_t *vm, spl_vm_val_t ip);
|
||||||
void spl_vm_stackdump(spl_vm_t *vm, spl_vm_val_t sp);
|
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);
|
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 "include/acutest.h"
|
||||||
#include "spl_mcode.h"
|
#include "spl_mcode.h"
|
||||||
|
#include "spl_syscall.h"
|
||||||
#include "spl_vm.h"
|
#include "spl_vm.h"
|
||||||
|
|
||||||
#include <stdio.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
|
* 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;
|
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),
|
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_LADDR, SPL_VOID, 8), ins(SPL_LOAD, SPL_I64, 0),
|
||||||
ins(SPL_ADD, SPL_I64, 0), ins(SPL_RET, 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_PUSH, SPL_VOID, 0), ins(SPL_CALL, SPL_VOID, 2),
|
||||||
ins(SPL_RET, SPL_I64, 0)};
|
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),
|
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_LADDR, SPL_VOID, 8), ins(SPL_LOAD, SPL_I64, 0),
|
||||||
ins(SPL_ADD, SPL_I64, 0), ins(SPL_RET, 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_PUSH, SPL_VOID, 2), ins(SPL_PUSH, SPL_VOID, 0),
|
||||||
ins(SPL_CALLI, SPL_VOID, 0), ins(SPL_RET, SPL_I64, 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 nlen0 = strlen("add") + 1;
|
||||||
size_t npad0 = ((nlen0 + 7) / 8) * 8 - nlen0;
|
size_t npad0 = ((nlen0 + 7) / 8) * 8 - nlen0;
|
||||||
size_t nlen1 = strlen("main") + 1;
|
size_t nlen1 = strlen("main") + 1;
|
||||||
@@ -627,7 +631,7 @@ void test_calli(void) {
|
|||||||
p += npad1;
|
p += npad1;
|
||||||
LE64(p, 0);
|
LE64(p, 0);
|
||||||
LE64(p, 0);
|
LE64(p, 0);
|
||||||
LE64(p, 6);
|
LE64(p, 8);
|
||||||
LE64(p, 6);
|
LE64(p, 6);
|
||||||
|
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
@@ -637,7 +641,7 @@ void test_calli(void) {
|
|||||||
*p++ = (unsigned char)(add_insns[i].type >> 8);
|
*p++ = (unsigned char)(add_insns[i].type >> 8);
|
||||||
LE64(p, add_insns[i].imm);
|
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);
|
||||||
*p++ = (unsigned char)(main_insns[i].opcode >> 8);
|
*p++ = (unsigned char)(main_insns[i].opcode >> 8);
|
||||||
*p++ = (unsigned char)(main_insns[i].type);
|
*p++ = (unsigned char)(main_insns[i].type);
|
||||||
@@ -669,8 +673,9 @@ void test_calli(void) {
|
|||||||
* ================================================================ */
|
* ================================================================ */
|
||||||
|
|
||||||
void test_gaddr_ld32(void) {
|
void test_gaddr_ld32(void) {
|
||||||
/* Load program, add global data, GADDR + LD32 to read it back */
|
/* 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),
|
* 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)};
|
ins(SPL_RET, SPL_U32, 0)};
|
||||||
size_t len;
|
size_t len;
|
||||||
unsigned char *bin = build_binary("main", 0, p, 3, &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) {
|
void test_gaddr_multi(void) {
|
||||||
/* Two global data entries: read second one via GADDR 1 */
|
/* Two global data entries: read second one via GADDR 2 (0 为哨兵) */
|
||||||
spl_vm_ins_t p[] = {ins(SPL_GADDR, SPL_VOID, 1), ins(SPL_LOAD, SPL_U32, 0),
|
spl_vm_ins_t p[] = {ins(SPL_GADDR, SPL_VOID, 2), ins(SPL_LOAD, SPL_U32, 0),
|
||||||
ins(SPL_RET, SPL_U32, 0)};
|
ins(SPL_RET, SPL_U32, 0)};
|
||||||
size_t len;
|
size_t len;
|
||||||
unsigned char *bin = build_binary("main", 0, p, 3, &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);
|
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
|
* Test list
|
||||||
* ================================================================ */
|
* ================================================================ */
|
||||||
@@ -872,6 +998,10 @@ TEST_LIST = {
|
|||||||
{"ld_st64", test_ld_st64},
|
{"ld_st64", test_ld_st64},
|
||||||
{"many_ops", test_many_ops},
|
{"many_ops", test_many_ops},
|
||||||
{"halt", test_halt},
|
{"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},
|
{"bad_magic", test_bad_magic},
|
||||||
{"empty", test_empty},
|
{"empty", test_empty},
|
||||||
{NULL, NULL},
|
{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__ */
|
||||||
@@ -424,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);
|
dump_node(ast, node->pattern_field.pattern, stack, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SPL_AST_PATTERN_REF:
|
||||||
|
if (node->pattern_ref.pat)
|
||||||
|
dump_node(ast, node->pattern_ref.pat, stack, true);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -73,7 +73,8 @@
|
|||||||
X(SPL_AST_PATTERN_TUPLE, V0, pattern_tuple) \
|
X(SPL_AST_PATTERN_TUPLE, V0, pattern_tuple) \
|
||||||
X(SPL_AST_PATTERN_LIST, V0, pattern_list) \
|
X(SPL_AST_PATTERN_LIST, V0, pattern_list) \
|
||||||
X(SPL_AST_PATTERN_AGGREGATE, V0, pattern_aggregate) \
|
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 */
|
/* clang-format on */
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -452,6 +453,10 @@ struct spl_ast_node {
|
|||||||
const char *name;
|
const char *name;
|
||||||
spl_ast_node_ref_t pattern;
|
spl_ast_node_ref_t pattern;
|
||||||
} pattern_field;
|
} pattern_field;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_t pat;
|
||||||
|
} pattern_ref;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+187
-13
@@ -51,7 +51,8 @@ static void emit_deferred_until(spl_ast2ir_t *a, usize base) {
|
|||||||
static void emit_decl(spl_ast2ir_t *a, spl_ast_node_ref_t ref);
|
static void emit_decl(spl_ast2ir_t *a, spl_ast_node_ref_t ref);
|
||||||
static spl_ir_node_ref_t emit_var_decl(spl_ast2ir_t *a, spl_ast_node_t *n);
|
static spl_ir_node_ref_t emit_var_decl(spl_ast2ir_t *a, spl_ast_node_t *n);
|
||||||
static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref,
|
static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref,
|
||||||
spl_ir_node_ref_t src_ptr, spl_type_id_t tid);
|
spl_ir_node_ref_t src_ptr, spl_type_id_t tid, int place_mode);
|
||||||
|
static int pattern_has_ref(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref);
|
||||||
static spl_ir_node_ref_t emit_match_check(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref,
|
static spl_ir_node_ref_t emit_match_check(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref,
|
||||||
spl_type_id_t scrut_ir, spl_ir_node_ref_t scrut_slot);
|
spl_type_id_t scrut_ir, spl_ir_node_ref_t scrut_slot);
|
||||||
|
|
||||||
@@ -72,6 +73,9 @@ static spl_ir_node_ref_t ast2ir_intrin_call(spl_ast2ir_t *a, spl_ast_node_t *n,
|
|||||||
spl_ast_node_ref_vec_t *args);
|
spl_ast_node_ref_vec_t *args);
|
||||||
static spl_ir_node_ref_t ast2ir_intrin_field(spl_ast2ir_t *a, spl_ast_node_t *n, spl_intrin_t in);
|
static spl_ir_node_ref_t ast2ir_intrin_field(spl_ast2ir_t *a, spl_ast_node_t *n, spl_intrin_t in);
|
||||||
static spl_ir_node_ref_t ast2ir_intrin_len(spl_ast2ir_t *a, spl_ast_node_t *n);
|
static spl_ir_node_ref_t ast2ir_intrin_len(spl_ast2ir_t *a, spl_ast_node_t *n);
|
||||||
|
static spl_ir_node_ref_t ast2ir_intrin_pos(spl_ast2ir_t *a, spl_ast_node_t *n, int which);
|
||||||
|
static spl_ir_node_ref_t ast2ir_intrin_file_name(spl_ast2ir_t *a, spl_ast_node_t *n);
|
||||||
|
static spl_ir_node_ref_t ast2ir_intrin_fn_name(spl_ast2ir_t *a, spl_ast_node_t *n);
|
||||||
|
|
||||||
static spl_ir_node_ref_t emit_zero_value(spl_ast2ir_t *a, spl_type_id_t tid) {
|
static spl_ir_node_ref_t emit_zero_value(spl_ast2ir_t *a, spl_type_id_t tid) {
|
||||||
spl_type_node_t *tn = spl_type_node(&a->sema->type->ir, tid);
|
spl_type_node_t *tn = spl_type_node(&a->sema->type->ir, tid);
|
||||||
@@ -137,14 +141,52 @@ static spl_ir_node_ref_t emit_zero_value(spl_ast2ir_t *a, spl_type_id_t tid) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 判断 pattern 是否包含 `&` 引用绑定(PATTERN_REF)。 */
|
||||||
|
static int pattern_has_ref(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref) {
|
||||||
|
if (!pat_ref)
|
||||||
|
return 0;
|
||||||
|
spl_ast_node_t *pat = spl_ast_node(a->ast, pat_ref);
|
||||||
|
if (!pat)
|
||||||
|
return 0;
|
||||||
|
switch (pat->kind) {
|
||||||
|
case SPL_AST_PATTERN_REF:
|
||||||
|
return 1;
|
||||||
|
case SPL_AST_PATTERN_LIST:
|
||||||
|
for (usize i = 0; i < vec_size(pat->pattern_list.patterns); ++i)
|
||||||
|
if (pattern_has_ref(a, vec_at(pat->pattern_list.patterns, i)))
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
case SPL_AST_PATTERN_TUPLE:
|
||||||
|
for (usize i = 0; i < vec_size(pat->pattern_tuple.patterns); ++i)
|
||||||
|
if (pattern_has_ref(a, vec_at(pat->pattern_tuple.patterns, i)))
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
case SPL_AST_PATTERN_DOT:
|
||||||
|
return pattern_has_ref(a, pat->pattern_dot.sub_pattern);
|
||||||
|
case SPL_AST_PATTERN_AGGREGATE:
|
||||||
|
for (usize i = 0; i < vec_size(pat->pattern_aggregate.fields); ++i) {
|
||||||
|
spl_ast_node_t *f =
|
||||||
|
spl_ast_node(a->ast, vec_at(pat->pattern_aggregate.fields, i));
|
||||||
|
if (f && f->kind == SPL_AST_PATTERN_FIELD &&
|
||||||
|
pattern_has_ref(a, f->pattern_field.pattern))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 把 pattern 里的 ident 绑到 src_ptr 指向内存的对应位置。
|
/* 把 pattern 里的 ident 绑到 src_ptr 指向内存的对应位置。
|
||||||
- PATTERN_IDENT: def2ir_map[def_id] = src_ptr
|
- PATTERN_IDENT: def2ir_map[def_id] = src_ptr(place_mode=0,值绑定别名源);
|
||||||
|
place_mode=1 时强制拷贝到新 slot(源为真实左值,值绑定必须拷贝)。
|
||||||
|
- PATTERN_REF: src_ptr 是该子位置的地址;分配 *T slot 存该地址,内层名绑到 slot。
|
||||||
- PATTERN_DOT: src_ptr 指向 enum,payload 在 mem_field(tid, src_ptr, vidx+1)
|
- PATTERN_DOT: src_ptr 指向 enum,payload 在 mem_field(tid, src_ptr, vidx+1)
|
||||||
- PATTERN_TUPLE / PATTERN_AGGREGATE: 递归 mem_field
|
- PATTERN_TUPLE / PATTERN_AGGREGATE: 递归 mem_field
|
||||||
UB 语义:不做 tag 检查,调用者保证变体正确。
|
UB 语义:不做 tag 检查,调用者保证变体正确。
|
||||||
*/
|
*/
|
||||||
static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref,
|
static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref,
|
||||||
spl_ir_node_ref_t src_ptr, spl_type_id_t tid) {
|
spl_ir_node_ref_t src_ptr, spl_type_id_t tid, int place_mode) {
|
||||||
if (!pat_ref || !src_ptr)
|
if (!pat_ref || !src_ptr)
|
||||||
return;
|
return;
|
||||||
spl_ast_node_t *pat = spl_ast_node(a->ast, pat_ref);
|
spl_ast_node_t *pat = spl_ast_node(a->ast, pat_ref);
|
||||||
@@ -153,8 +195,30 @@ static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref,
|
|||||||
|
|
||||||
switch (pat->kind) {
|
switch (pat->kind) {
|
||||||
case SPL_AST_PATTERN_IDENT: {
|
case SPL_AST_PATTERN_IDENT: {
|
||||||
if (pat->resolved_def_id)
|
if (!pat->resolved_def_id)
|
||||||
|
return;
|
||||||
|
if (place_mode) {
|
||||||
|
spl_ir_node_ref_t slot =
|
||||||
|
spl_ir_builder_mem_alloca(a->ir, tid, const_usize(a, 1));
|
||||||
|
spl_ir_node_ref_t v = spl_ir_builder_mem_load(a->ir, tid, src_ptr);
|
||||||
|
spl_ir_builder_mem_store(a->ir, tid, slot, v);
|
||||||
|
map_put(a->def2ir_map, pat->resolved_def_id, slot);
|
||||||
|
} else {
|
||||||
map_put(a->def2ir_map, pat->resolved_def_id, src_ptr);
|
map_put(a->def2ir_map, pat->resolved_def_id, src_ptr);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SPL_AST_PATTERN_REF: {
|
||||||
|
spl_ast_node_ref_t inner = pat->pattern_ref.pat;
|
||||||
|
spl_ast_node_t *in = spl_ast_node(a->ast, inner);
|
||||||
|
spl_type_id_t ptr_tid = in ? node_ir(a, in) : 0;
|
||||||
|
if (!ptr_tid)
|
||||||
|
ptr_tid = spl_type_builder_ptr(&a->sema->type->ir, tid);
|
||||||
|
spl_ir_node_ref_t slot =
|
||||||
|
spl_ir_builder_mem_alloca(a->ir, ptr_tid, const_usize(a, 1));
|
||||||
|
spl_ir_builder_mem_store(a->ir, ptr_tid, slot, src_ptr);
|
||||||
|
emit_pattern_bind(a, inner, slot, ptr_tid, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +235,7 @@ static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref,
|
|||||||
|
|
||||||
case SPL_AST_PATTERN_LIST:
|
case SPL_AST_PATTERN_LIST:
|
||||||
for (usize i = 0; i < vec_size(pat->pattern_list.patterns); ++i)
|
for (usize i = 0; i < vec_size(pat->pattern_list.patterns); ++i)
|
||||||
emit_pattern_bind(a, vec_at(pat->pattern_list.patterns, i), src_ptr, tid);
|
emit_pattern_bind(a, vec_at(pat->pattern_list.patterns, i), src_ptr, tid, place_mode);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case SPL_AST_PATTERN_TUPLE: {
|
case SPL_AST_PATTERN_TUPLE: {
|
||||||
@@ -185,7 +249,7 @@ static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref,
|
|||||||
for (usize i = 0; i < n; ++i) {
|
for (usize i = 0; i < n; ++i) {
|
||||||
spl_type_id_t ft = vec_at(tn->agg_type.variants, i).type_id;
|
spl_type_id_t ft = vec_at(tn->agg_type.variants, i).type_id;
|
||||||
spl_ir_node_ref_t fp = spl_ir_builder_mem_field(a->ir, tid, src_ptr, i);
|
spl_ir_node_ref_t fp = spl_ir_builder_mem_field(a->ir, tid, src_ptr, i);
|
||||||
emit_pattern_bind(a, vec_at(pat->pattern_tuple.patterns, i), fp, ft);
|
emit_pattern_bind(a, vec_at(pat->pattern_tuple.patterns, i), fp, ft, place_mode);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -203,7 +267,7 @@ static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref,
|
|||||||
spl_type_id_t payload_ir = vec_at(tn->agg_type.variants, vidx).type_id;
|
spl_type_id_t payload_ir = vec_at(tn->agg_type.variants, vidx).type_id;
|
||||||
spl_ir_node_ref_t pptr = spl_ir_builder_mem_field(a->ir, tid, src_ptr, vidx + 1);
|
spl_ir_node_ref_t pptr = spl_ir_builder_mem_field(a->ir, tid, src_ptr, vidx + 1);
|
||||||
if (pat->pattern_dot.sub_pattern)
|
if (pat->pattern_dot.sub_pattern)
|
||||||
emit_pattern_bind(a, pat->pattern_dot.sub_pattern, pptr, payload_ir);
|
emit_pattern_bind(a, pat->pattern_dot.sub_pattern, pptr, payload_ir, place_mode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,7 +285,7 @@ static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref,
|
|||||||
spl_type_id_t ft = vec_at(tn->agg_type.variants, idx).type_id;
|
spl_type_id_t ft = vec_at(tn->agg_type.variants, idx).type_id;
|
||||||
spl_ir_node_ref_t fp = spl_ir_builder_mem_field(a->ir, tid, src_ptr, idx);
|
spl_ir_node_ref_t fp = spl_ir_builder_mem_field(a->ir, tid, src_ptr, idx);
|
||||||
if (f->pattern_field.pattern)
|
if (f->pattern_field.pattern)
|
||||||
emit_pattern_bind(a, f->pattern_field.pattern, fp, ft);
|
emit_pattern_bind(a, f->pattern_field.pattern, fp, ft, place_mode);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -294,12 +358,32 @@ static spl_ir_node_ref_t emit_match_check(spl_ast2ir_t *a, spl_ast_node_ref_t pa
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 分配 slot → store init 值 → bind pattern。
|
/* 分配 slot → store init 值 → bind pattern。
|
||||||
返回指向变量内存的指针(*tid),调用者可用于读取初值。 */
|
返回指向变量内存的指针(*tid),调用者可用于读取初值。
|
||||||
|
若 pattern 含 `&` 引用绑定:源必须是可寻址左值,直接在源 place 上绑定
|
||||||
|
(值绑定拷贝子值,引用绑定取子位置地址),返回该 place。 */
|
||||||
static spl_ir_node_ref_t emit_var_decl(spl_ast2ir_t *a, spl_ast_node_t *n) {
|
static spl_ir_node_ref_t emit_var_decl(spl_ast2ir_t *a, spl_ast_node_t *n) {
|
||||||
spl_type_id_t var_ir = node_ir(a, n);
|
spl_type_id_t var_ir = node_ir(a, n);
|
||||||
if (!var_ir)
|
if (!var_ir)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
spl_ast_node_ref_t pat_ref = n->var_const_decl.pattern;
|
||||||
|
if (pattern_has_ref(a, pat_ref)) {
|
||||||
|
if (!n->var_const_decl.init_expr) {
|
||||||
|
spl_diag_error(a->diag, SPL_DIAG_KIND_NO_LVALUE, &n->dbg,
|
||||||
|
"reference binding requires an initialized place");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
spl_ir_node_ref_t place = emit_lvalue(a, n->var_const_decl.init_expr);
|
||||||
|
if (!place) {
|
||||||
|
spl_ast_node_t *in = spl_ast_node(a->ast, n->var_const_decl.init_expr);
|
||||||
|
spl_diag_error(a->diag, SPL_DIAG_KIND_NO_LVALUE, in ? &in->dbg : &n->dbg,
|
||||||
|
"cannot bind by reference: initializer is not an lvalue");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
emit_pattern_bind(a, pat_ref, place, var_ir, 1);
|
||||||
|
return place;
|
||||||
|
}
|
||||||
|
|
||||||
spl_ir_node_ref_t slot = spl_ir_builder_mem_alloca(a->ir, var_ir, const_usize(a, 1));
|
spl_ir_node_ref_t slot = spl_ir_builder_mem_alloca(a->ir, var_ir, const_usize(a, 1));
|
||||||
|
|
||||||
if (n->var_const_decl.init_expr) {
|
if (n->var_const_decl.init_expr) {
|
||||||
@@ -308,7 +392,7 @@ static spl_ir_node_ref_t emit_var_decl(spl_ast2ir_t *a, spl_ast_node_t *n) {
|
|||||||
spl_ir_builder_mem_store(a->ir, var_ir, slot, v);
|
spl_ir_builder_mem_store(a->ir, var_ir, slot, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit_pattern_bind(a, n->var_const_decl.pattern, slot, var_ir);
|
emit_pattern_bind(a, pat_ref, slot, var_ir, 0);
|
||||||
return slot;
|
return slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,6 +545,14 @@ static spl_ir_node_ref_t ast2ir_intrin_call(spl_ast2ir_t *a, spl_ast_node_t *n,
|
|||||||
case SPL_INTRIN_INT2PTR:
|
case SPL_INTRIN_INT2PTR:
|
||||||
case SPL_INTRIN_PTR_CAST:
|
case SPL_INTRIN_PTR_CAST:
|
||||||
return ast2ir_intrin_cast(a, n, in, args);
|
return ast2ir_intrin_cast(a, n, in, args);
|
||||||
|
case SPL_INTRIN_LINE:
|
||||||
|
return ast2ir_intrin_pos(a, n, 0);
|
||||||
|
case SPL_INTRIN_COL:
|
||||||
|
return ast2ir_intrin_pos(a, n, 1);
|
||||||
|
case SPL_INTRIN_FILE_NAME:
|
||||||
|
return ast2ir_intrin_file_name(a, n);
|
||||||
|
case SPL_INTRIN_FN_NAME:
|
||||||
|
return ast2ir_intrin_fn_name(a, n);
|
||||||
case SPL_INTRIN_BREAKPOINT:
|
case SPL_INTRIN_BREAKPOINT:
|
||||||
return spl_ir_builder_dbg_breakpoint(a->ir);
|
return spl_ir_builder_dbg_breakpoint(a->ir);
|
||||||
default:
|
default:
|
||||||
@@ -469,6 +561,67 @@ static spl_ir_node_ref_t ast2ir_intrin_call(spl_ast2ir_t *a, spl_ast_node_t *n,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* @line() / @col():把调用点位置降为 usize 常量。which: 0=line, 1=col。 */
|
||||||
|
static spl_ir_node_ref_t ast2ir_intrin_pos(spl_ast2ir_t *a, spl_ast_node_t *n, int which) {
|
||||||
|
spl_lang_type_id_t usize_lang = spl_lang_make_int(a->sema->type, sizeof(void *) * 8, 0);
|
||||||
|
spl_type_id_t usize_ir = to_ir_type(a, usize_lang);
|
||||||
|
usize v = which == 0 ? (usize)n->dbg.line : (usize)n->dbg.col;
|
||||||
|
return spl_ir_builder_type_const_int(a->ir, usize_ir, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @file_name():把调用点所在文件路径降为 []u8(同字符串字面量的构造方式)。 */
|
||||||
|
static spl_ir_node_ref_t ast2ir_intrin_file_name(spl_ast2ir_t *a, spl_ast_node_t *n) {
|
||||||
|
const char *fname = n->dbg.fname ? n->dbg.fname : "?";
|
||||||
|
usize len = strlen(fname);
|
||||||
|
|
||||||
|
spl_lang_type_id_t u8_lang = spl_lang_make_int(a->sema->type, 8, 0);
|
||||||
|
spl_type_id_t u8_ir = to_ir_type(a, u8_lang);
|
||||||
|
spl_type_id_t ptr_u8_ir = spl_type_builder_ptr(&a->sema->type->ir, u8_ir);
|
||||||
|
spl_lang_type_id_t usize_lang = spl_lang_make_int(a->sema->type, sizeof(void *) * 8, 0);
|
||||||
|
spl_type_id_t usize_ir = to_ir_type(a, usize_lang);
|
||||||
|
spl_lang_type_id_t arr_lang = spl_lang_make_array(a->sema->type, u8_lang, len);
|
||||||
|
spl_type_id_t arr_ir = to_ir_type(a, arr_lang);
|
||||||
|
|
||||||
|
spl_ir_node_ref_t raw =
|
||||||
|
spl_ir_builder_type_const_bytes(a->ir, arr_ir, (void *)fname, len);
|
||||||
|
spl_ir_node_ref_t ptr = spl_ir_builder_mem_global_alloc(a->ir, ptr_u8_ir, raw);
|
||||||
|
spl_ir_node_ref_t len_c = spl_ir_builder_type_const_int(a->ir, usize_ir, len);
|
||||||
|
|
||||||
|
spl_type_id_t slice_ir = node_ir(a, n);
|
||||||
|
spl_ir_node_ref_t node = spl_ir_builder_agg_construct(a->ir, slice_ir);
|
||||||
|
spl_ir_builder_agg_construct_field(a->ir, node, ptr);
|
||||||
|
spl_ir_builder_agg_construct_field(a->ir, node, len_c);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @fn_name():把当前函数名降为 []u8(同字符串字面量的构造方式)。 */
|
||||||
|
static spl_ir_node_ref_t ast2ir_intrin_fn_name(spl_ast2ir_t *a, spl_ast_node_t *n) {
|
||||||
|
const char *name = "?";
|
||||||
|
spl_ir_func_t *f = spl_ir_func(&a->ir->ir, a->ir->current_fn);
|
||||||
|
if (f && f->name)
|
||||||
|
name = f->name;
|
||||||
|
usize len = strlen(name);
|
||||||
|
|
||||||
|
spl_lang_type_id_t u8_lang = spl_lang_make_int(a->sema->type, 8, 0);
|
||||||
|
spl_type_id_t u8_ir = to_ir_type(a, u8_lang);
|
||||||
|
spl_type_id_t ptr_u8_ir = spl_type_builder_ptr(&a->sema->type->ir, u8_ir);
|
||||||
|
spl_lang_type_id_t usize_lang = spl_lang_make_int(a->sema->type, sizeof(void *) * 8, 0);
|
||||||
|
spl_type_id_t usize_ir = to_ir_type(a, usize_lang);
|
||||||
|
spl_lang_type_id_t arr_lang = spl_lang_make_array(a->sema->type, u8_lang, len);
|
||||||
|
spl_type_id_t arr_ir = to_ir_type(a, arr_lang);
|
||||||
|
|
||||||
|
spl_ir_node_ref_t raw =
|
||||||
|
spl_ir_builder_type_const_bytes(a->ir, arr_ir, (void *)name, len);
|
||||||
|
spl_ir_node_ref_t ptr = spl_ir_builder_mem_global_alloc(a->ir, ptr_u8_ir, raw);
|
||||||
|
spl_ir_node_ref_t len_c = spl_ir_builder_type_const_int(a->ir, usize_ir, len);
|
||||||
|
|
||||||
|
spl_type_id_t slice_ir = node_ir(a, n);
|
||||||
|
spl_ir_node_ref_t node = spl_ir_builder_agg_construct(a->ir, slice_ir);
|
||||||
|
spl_ir_builder_agg_construct_field(a->ir, node, ptr);
|
||||||
|
spl_ir_builder_agg_construct_field(a->ir, node, len_c);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
static spl_ir_node_ref_t ast2ir_intrin_field(spl_ast2ir_t *a, spl_ast_node_t *n, spl_intrin_t in) {
|
static spl_ir_node_ref_t ast2ir_intrin_field(spl_ast2ir_t *a, spl_ast_node_t *n, spl_intrin_t in) {
|
||||||
switch (in) {
|
switch (in) {
|
||||||
case SPL_INTRIN_LEN:
|
case SPL_INTRIN_LEN:
|
||||||
@@ -1324,12 +1477,32 @@ static spl_ir_node_ref_t emit_expr_raw(spl_ast2ir_t *a, spl_ast_node_ref_t ref)
|
|||||||
if (!matched_ir)
|
if (!matched_ir)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
spl_ir_node_ref_t scrut_slot =
|
spl_ir_node_ref_t scrut_slot = 0;
|
||||||
spl_ir_builder_mem_alloca(a->ir, matched_ir, const_usize(a, 1));
|
int matched_place_mode = 0;
|
||||||
|
for (usize i = 0; i < vec_size(n->match_expr.arms); ++i) {
|
||||||
|
spl_ast_node_t *arm = spl_ast_node(a->ast, vec_at(n->match_expr.arms, i));
|
||||||
|
if (arm && arm->kind == SPL_AST_MATCH_ARM &&
|
||||||
|
pattern_has_ref(a, arm->match_arm.pattern)) {
|
||||||
|
matched_place_mode = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matched_place_mode) {
|
||||||
|
scrut_slot = emit_lvalue(a, matched_ref);
|
||||||
|
if (!scrut_slot) {
|
||||||
|
spl_diag_error(a->diag, SPL_DIAG_KIND_NO_LVALUE,
|
||||||
|
matched_node ? &matched_node->dbg : &n->dbg,
|
||||||
|
"cannot bind by reference: matched value is not an lvalue");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scrut_slot = spl_ir_builder_mem_alloca(a->ir, matched_ir, const_usize(a, 1));
|
||||||
spl_ir_node_ref_t scrut_val = emit_expr(a, matched_ref);
|
spl_ir_node_ref_t scrut_val = emit_expr(a, matched_ref);
|
||||||
if (!scrut_val)
|
if (!scrut_val)
|
||||||
return 0;
|
return 0;
|
||||||
spl_ir_builder_mem_store(a->ir, matched_ir, scrut_slot, scrut_val);
|
spl_ir_builder_mem_store(a->ir, matched_ir, scrut_slot, scrut_val);
|
||||||
|
}
|
||||||
|
|
||||||
usize narms = vec_size(n->match_expr.arms);
|
usize narms = vec_size(n->match_expr.arms);
|
||||||
spl_ir_node_ref_vec_t arm_branches;
|
spl_ir_node_ref_vec_t arm_branches;
|
||||||
@@ -1376,7 +1549,8 @@ static spl_ir_node_ref_t emit_expr_raw(spl_ast2ir_t *a, spl_ast_node_ref_t ref)
|
|||||||
|
|
||||||
spl_ast_node_t *arm = spl_ast_node(a->ast, vec_at(n->match_expr.arms, i));
|
spl_ast_node_t *arm = spl_ast_node(a->ast, vec_at(n->match_expr.arms, i));
|
||||||
if (arm && arm->kind == SPL_AST_MATCH_ARM) {
|
if (arm && arm->kind == SPL_AST_MATCH_ARM) {
|
||||||
emit_pattern_bind(a, arm->match_arm.pattern, scrut_slot, matched_ir);
|
emit_pattern_bind(a, arm->match_arm.pattern, scrut_slot, matched_ir,
|
||||||
|
matched_place_mode);
|
||||||
if (arm->match_arm.body)
|
if (arm->match_arm.body)
|
||||||
emit_expr(a, arm->match_arm.body);
|
emit_expr(a, arm->match_arm.body);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,14 @@ static const spl_intrin_info_t g_intrin_table[] = {
|
|||||||
SPL_INTRIN_RESULT_USIZE},
|
SPL_INTRIN_RESULT_USIZE},
|
||||||
[SPL_INTRIN_FIELD_COUNT] = {"@field_count", SPL_INTRIN_FIELD_COUNT, SPL_INTRIN_FORM_CALL, 1, 1,
|
[SPL_INTRIN_FIELD_COUNT] = {"@field_count", SPL_INTRIN_FIELD_COUNT, SPL_INTRIN_FORM_CALL, 1, 1,
|
||||||
SPL_INTRIN_RESULT_USIZE},
|
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]);
|
static const usize g_intrin_count = sizeof(g_intrin_table) / sizeof(g_intrin_table[0]);
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ typedef enum {
|
|||||||
SPL_INTRIN_ENUM2INT,
|
SPL_INTRIN_ENUM2INT,
|
||||||
SPL_INTRIN_OFFSETOF,
|
SPL_INTRIN_OFFSETOF,
|
||||||
SPL_INTRIN_FIELD_COUNT,
|
SPL_INTRIN_FIELD_COUNT,
|
||||||
|
/* 调用点位置信息(供日志/性能分析库使用) */
|
||||||
|
SPL_INTRIN_FILE_NAME,
|
||||||
|
SPL_INTRIN_LINE,
|
||||||
|
SPL_INTRIN_COL,
|
||||||
|
SPL_INTRIN_FN_NAME,
|
||||||
} spl_intrin_t;
|
} spl_intrin_t;
|
||||||
|
|
||||||
typedef enum {
|
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);
|
printf(" float=%f", n->type_const.float_lit);
|
||||||
} else if (t && t->kind == SPL_TYPE_FN) {
|
} else if (t && t->kind == SPL_TYPE_FN) {
|
||||||
printf(" fn#%zu", n->type_const.fn);
|
printf(" fn#%zu", n->type_const.fn);
|
||||||
// } else if (t && t->kind == SPL_TYPE_PTR) {
|
} else if (t && t->kind == SPL_TYPE_ARRAY) {
|
||||||
// printf(" str=\"%s\"", n->type_const.cstr_lit ? n->type_const.cstr_lit : "?");
|
/* 字节常量:不打印宿主指针(不确定),只标记 bytes。 */
|
||||||
|
printf(" bytes");
|
||||||
} else if (t && t->kind == SPL_TYPE_INT && t->int_type.bits == 8) {
|
} else if (t && t->kind == SPL_TYPE_INT && t->int_type.bits == 8) {
|
||||||
printf(" char='%c'", n->type_const.ch_lit);
|
printf(" char='%c'", n->type_const.ch_lit);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+326
-38
@@ -344,6 +344,13 @@ typedef struct {
|
|||||||
frame_layout_t frame;
|
frame_layout_t frame;
|
||||||
VEC(usize) node_ip;
|
VEC(usize) node_ip;
|
||||||
fix_vec_t *fixups;
|
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;
|
} cgen_t;
|
||||||
|
|
||||||
static void emit(cgen_t *cg, uint8_t op, uint8_t ty, spl_vm_val_t imm) {
|
static void emit(cgen_t *cg, uint8_t op, uint8_t ty, spl_vm_val_t imm) {
|
||||||
@@ -378,6 +385,240 @@ static void cg_store_at(cgen_t *cg, usize addr, uint8_t k) {
|
|||||||
CG_STORE(cg, 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:
|
||||||
|
count_ref(cg, n->cmp.a);
|
||||||
|
count_ref(cg, 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:
|
||||||
|
count_ref(cg, n->cast.val);
|
||||||
|
break;
|
||||||
|
case SPL_IR_MEM_ALLOCA:
|
||||||
|
count_ref(cg, n->mem_alloc.count);
|
||||||
|
break;
|
||||||
|
case SPL_IR_MEM_GLOBAL_ALLOC:
|
||||||
|
count_ref(cg, n->mem_global_alloc.const_node);
|
||||||
|
break;
|
||||||
|
case SPL_IR_MEM_LOAD:
|
||||||
|
count_ref(cg, n->mem_load.ptr);
|
||||||
|
break;
|
||||||
|
case SPL_IR_MEM_STORE:
|
||||||
|
count_ref(cg, n->mem_store.ptr);
|
||||||
|
count_ref(cg, n->mem_store.val);
|
||||||
|
break;
|
||||||
|
case SPL_IR_MEM_OFFSET:
|
||||||
|
count_ref(cg, n->mem_offset.ptr);
|
||||||
|
count_ref(cg, n->mem_offset.offset);
|
||||||
|
break;
|
||||||
|
case SPL_IR_MEM_FIELD_PTR:
|
||||||
|
count_ref(cg, n->mem_field_ptr.agg);
|
||||||
|
break;
|
||||||
|
case SPL_IR_MEM_COPY:
|
||||||
|
count_ref(cg, n->mem_copy.dst);
|
||||||
|
count_ref(cg, n->mem_copy.src);
|
||||||
|
count_ref(cg, n->mem_copy.size);
|
||||||
|
break;
|
||||||
|
case SPL_IR_MEM_SET:
|
||||||
|
count_ref(cg, n->mem_set.dst);
|
||||||
|
count_ref(cg, n->mem_set.val);
|
||||||
|
count_ref(cg, n->mem_set.size);
|
||||||
|
break;
|
||||||
|
case SPL_IR_TYPE_OFFSETOF:
|
||||||
|
count_ref(cg, n->ir_offsetof.field_idx);
|
||||||
|
break;
|
||||||
|
case SPL_IR_AGG_CONSTRUCT:
|
||||||
|
count_refs_vec(cg, &n->agg_construct.fields);
|
||||||
|
break;
|
||||||
|
case SPL_IR_AGG_EXTRACT:
|
||||||
|
count_ref(cg, n->agg_extract.val);
|
||||||
|
break;
|
||||||
|
case SPL_IR_AGG_INSERT:
|
||||||
|
count_ref(cg, n->agg_insert.agg);
|
||||||
|
count_ref(cg, n->agg_insert.field);
|
||||||
|
break;
|
||||||
|
case SPL_IR_CONTROL_SELECT:
|
||||||
|
count_ref(cg, n->control_select.cond);
|
||||||
|
count_ref(cg, n->control_select.true_val);
|
||||||
|
count_ref(cg, n->control_select.false_val);
|
||||||
|
break;
|
||||||
|
case SPL_IR_CONTROL_BR:
|
||||||
|
count_ref(cg, n->control_br.cond);
|
||||||
|
count_ref(cg, n->control_br.true_label);
|
||||||
|
count_ref(cg, n->control_br.false_label);
|
||||||
|
break;
|
||||||
|
case SPL_IR_CONTROL_JMP:
|
||||||
|
count_ref(cg, n->control_jmp.label);
|
||||||
|
break;
|
||||||
|
case SPL_IR_CONTROL_CALL:
|
||||||
|
count_ref(cg, n->control_call.func);
|
||||||
|
count_refs_vec(cg, &n->control_call.params);
|
||||||
|
break;
|
||||||
|
case SPL_IR_CONTROL_PARAM:
|
||||||
|
count_ref(cg, n->control_param.idx);
|
||||||
|
break;
|
||||||
|
case SPL_IR_CONTROL_RET:
|
||||||
|
count_ref(cg, n->control_ret.val);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool indirect;
|
bool indirect;
|
||||||
usize off;
|
usize off;
|
||||||
@@ -439,25 +680,25 @@ static void cg_add_fix(cgen_t *cg, usize ins_idx, fix_kind_t kind, spl_ir_node_r
|
|||||||
|
|
||||||
static void cg_binop(cgen_t *cg, usize ref, const spl_ir_node_t *n, uint8_t op) {
|
static void cg_binop(cgen_t *cg, usize ref, const spl_ir_node_t *n, uint8_t op) {
|
||||||
uint8_t k = vm_kind_of(cg->ctx, n->arith.tid);
|
uint8_t k = vm_kind_of(cg->ctx, n->arith.tid);
|
||||||
cg_load_slot(cg, n->arith.left, k);
|
cg_operand(cg, n->arith.left, k, 0);
|
||||||
cg_load_slot(cg, n->arith.right, k);
|
cg_operand(cg, n->arith.right, k, 1);
|
||||||
CG_OP(cg, op, k);
|
CG_OP(cg, op, k);
|
||||||
cg_store_slot(cg, ref, k);
|
cg_result(cg, ref, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cg_unop(cgen_t *cg, usize ref, const spl_ir_node_t *n, uint8_t op) {
|
static void cg_unop(cgen_t *cg, usize ref, const spl_ir_node_t *n, uint8_t op) {
|
||||||
uint8_t k = vm_kind_of(cg->ctx, n->arith.tid);
|
uint8_t k = vm_kind_of(cg->ctx, n->arith.tid);
|
||||||
cg_load_slot(cg, n->arith.left, k);
|
cg_operand(cg, n->arith.left, k, 0);
|
||||||
CG_OP(cg, op, k);
|
CG_OP(cg, op, k);
|
||||||
cg_store_slot(cg, ref, k);
|
cg_result(cg, ref, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cg_cmp(cgen_t *cg, usize ref, const spl_ir_node_t *n, uint8_t op) {
|
static void cg_cmp(cgen_t *cg, usize ref, const spl_ir_node_t *n, uint8_t op) {
|
||||||
uint8_t k = vm_kind_of(cg->ctx, n->cmp.tid);
|
uint8_t k = vm_kind_of(cg->ctx, n->cmp.tid);
|
||||||
cg_load_slot(cg, n->cmp.a, k);
|
cg_operand(cg, n->cmp.a, k, 0);
|
||||||
cg_load_slot(cg, n->cmp.b, k);
|
cg_operand(cg, n->cmp.b, k, 1);
|
||||||
CG_OP(cg, op, k);
|
CG_OP(cg, op, k);
|
||||||
cg_store_slot(cg, ref, SPL_BOOL);
|
cg_result(cg, ref, SPL_BOOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cg_const(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
static void cg_const(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
||||||
@@ -479,7 +720,7 @@ static void cg_const(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|||||||
CG_PUSH(cg, SPL_PTR, 0);
|
CG_PUSH(cg, SPL_PTR, 0);
|
||||||
cg_add_fix(cg, idx, FIX_FNADDR, 0, n->type_const.fn);
|
cg_add_fix(cg, idx, FIX_FNADDR, 0, n->type_const.fn);
|
||||||
}
|
}
|
||||||
cg_store_slot(cg, ref, SPL_PTR);
|
cg_result(cg, ref, SPL_PTR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,7 +748,7 @@ static void cg_const(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
CG_PUSH(cg, k, imm);
|
CG_PUSH(cg, k, imm);
|
||||||
cg_store_slot(cg, ref, k);
|
cg_result(cg, ref, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cg_cast(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
static void cg_cast(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
||||||
@@ -520,7 +761,7 @@ static void cg_cast(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|||||||
uint8_t fk = vm_kind_of(ctx, n->cast.from_tid);
|
uint8_t fk = vm_kind_of(ctx, n->cast.from_tid);
|
||||||
uint8_t tk = vm_kind_of(ctx, n->cast.to_tid);
|
uint8_t tk = vm_kind_of(ctx, n->cast.to_tid);
|
||||||
|
|
||||||
cg_load_slot(cg, n->cast.val, fk);
|
cg_operand(cg, n->cast.val, fk, 0);
|
||||||
|
|
||||||
if (ft->kind == SPL_TYPE_INT && tt->kind == SPL_TYPE_INT) {
|
if (ft->kind == SPL_TYPE_INT && tt->kind == SPL_TYPE_INT) {
|
||||||
usize fb = ft->int_type.bits;
|
usize fb = ft->int_type.bits;
|
||||||
@@ -533,7 +774,7 @@ static void cg_cast(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|||||||
emit(cg, SPL_ZEXT, SPL_VOID, 1);
|
emit(cg, SPL_ZEXT, SPL_VOID, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
cg_store_slot(cg, ref, tk);
|
cg_result(cg, ref, tk);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cg_mem(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
static void cg_mem(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
||||||
@@ -571,9 +812,9 @@ static void cg_mem(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|||||||
cg_store_slot(cg, ref, SPL_PTR);
|
cg_store_slot(cg, ref, SPL_PTR);
|
||||||
} else {
|
} else {
|
||||||
uint8_t k = vm_kind_of(ctx, tid);
|
uint8_t k = vm_kind_of(ctx, tid);
|
||||||
cg_load_slot(cg, n->mem_load.ptr, SPL_PTR);
|
cg_operand(cg, n->mem_load.ptr, SPL_PTR, 0);
|
||||||
CG_LOAD(cg, k);
|
CG_LOAD(cg, k);
|
||||||
cg_store_slot(cg, ref, k);
|
cg_result(cg, ref, k);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -585,8 +826,8 @@ static void cg_mem(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|||||||
ptr_slot(vec_at(cg->frame.slots, n->mem_store.ptr).result_off), tid);
|
ptr_slot(vec_at(cg->frame.slots, n->mem_store.ptr).result_off), tid);
|
||||||
} else {
|
} else {
|
||||||
uint8_t k = vm_kind_of(ctx, tid);
|
uint8_t k = vm_kind_of(ctx, tid);
|
||||||
cg_load_slot(cg, n->mem_store.ptr, SPL_PTR);
|
cg_operand(cg, n->mem_store.ptr, SPL_PTR, 0);
|
||||||
cg_load_slot(cg, n->mem_store.val, k);
|
cg_operand(cg, n->mem_store.val, k, 1);
|
||||||
CG_STORE(cg, k);
|
CG_STORE(cg, k);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -596,24 +837,24 @@ static void cg_mem(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|||||||
usize elem_size = ir_type_size(ctx, n->mem_offset.tid);
|
usize elem_size = ir_type_size(ctx, n->mem_offset.tid);
|
||||||
if (elem_size == 0)
|
if (elem_size == 0)
|
||||||
elem_size = 1;
|
elem_size = 1;
|
||||||
cg_load_slot(cg, n->mem_offset.ptr, SPL_PTR);
|
cg_operand(cg, n->mem_offset.ptr, SPL_PTR, 0);
|
||||||
cg_load_slot(cg, n->mem_offset.offset, SPL_U64);
|
cg_operand(cg, n->mem_offset.offset, SPL_U64, 1);
|
||||||
CG_PUSH(cg, SPL_U64, elem_size);
|
CG_PUSH(cg, SPL_U64, elem_size);
|
||||||
CG_OP(cg, SPL_MUL, SPL_U64);
|
CG_OP(cg, SPL_MUL, SPL_U64);
|
||||||
CG_OP(cg, SPL_ADD, SPL_PTR);
|
CG_OP(cg, SPL_ADD, SPL_PTR);
|
||||||
cg_store_slot(cg, ref, SPL_PTR);
|
cg_result(cg, ref, SPL_PTR);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SPL_IR_MEM_FIELD_PTR: {
|
case SPL_IR_MEM_FIELD_PTR: {
|
||||||
spl_type_id_t agg_tid = n->mem_field_ptr.tid;
|
spl_type_id_t agg_tid = n->mem_field_ptr.tid;
|
||||||
usize off = ir_agg_field_off(ctx, agg_tid, n->mem_field_ptr.field_idx);
|
usize off = ir_agg_field_off(ctx, agg_tid, n->mem_field_ptr.field_idx);
|
||||||
cg_load_slot(cg, n->mem_field_ptr.agg, SPL_PTR);
|
cg_operand(cg, n->mem_field_ptr.agg, SPL_PTR, 0);
|
||||||
if (off) {
|
if (off) {
|
||||||
CG_PUSH(cg, SPL_U64, off);
|
CG_PUSH(cg, SPL_U64, off);
|
||||||
CG_OP(cg, SPL_ADD, SPL_PTR);
|
CG_OP(cg, SPL_ADD, SPL_PTR);
|
||||||
}
|
}
|
||||||
cg_store_slot(cg, ref, SPL_PTR);
|
cg_result(cg, ref, SPL_PTR);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -742,7 +983,7 @@ static void cg_typeop(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CG_PUSH(cg, SPL_U64, v);
|
CG_PUSH(cg, SPL_U64, v);
|
||||||
cg_store_slot(cg, ref, SPL_U64);
|
cg_result(cg, ref, SPL_U64);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cg_control(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
static void cg_control(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
||||||
@@ -775,10 +1016,7 @@ static void cg_control(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|||||||
CG_LOAD(cg, SPL_PTR);
|
CG_LOAD(cg, SPL_PTR);
|
||||||
cg_load_slot(cg, n->control_ret.val, SPL_PTR);
|
cg_load_slot(cg, n->control_ret.val, SPL_PTR);
|
||||||
CG_PUSH(cg, SPL_U64, sz);
|
CG_PUSH(cg, SPL_U64, sz);
|
||||||
int nat = get_or_add_native(ctx, "vm_memcpy");
|
emit(cg, SPL_MEMCPY, SPL_VOID, 0);
|
||||||
CG_PUSH(cg, SPL_U64, (spl_vm_val_t)nat);
|
|
||||||
emit(cg, SPL_NCALL, SPL_VOID, 3);
|
|
||||||
emit(cg, SPL_DROP, SPL_VOID, 0);
|
|
||||||
}
|
}
|
||||||
emit(cg, SPL_RET, SPL_VOID, 0);
|
emit(cg, SPL_RET, SPL_VOID, 0);
|
||||||
break;
|
break;
|
||||||
@@ -829,44 +1067,53 @@ static void cg_control(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|||||||
const spl_ir_node_t *pn = &vec_at(cg->irf->nodes, pref);
|
const spl_ir_node_t *pn = &vec_at(cg->irf->nodes, pref);
|
||||||
spl_type_id_t ptid = ir_result_tid(ctx, pn);
|
spl_type_id_t ptid = ir_result_tid(ctx, pn);
|
||||||
uint8_t pk = vm_kind_of(ctx, ptid);
|
uint8_t pk = vm_kind_of(ctx, ptid);
|
||||||
cg_load_slot(cg, pref, pk);
|
cg_operand(cg, pref, pk, i);
|
||||||
}
|
}
|
||||||
CG_PUSH(cg, SPL_U64, (spl_vm_val_t)nat_idx);
|
CG_PUSH(cg, SPL_U64, (spl_vm_val_t)nat_idx);
|
||||||
emit(cg, SPL_NCALL, SPL_VOID, (spl_vm_val_t)nargs);
|
emit(cg, SPL_NCALL, SPL_VOID, (spl_vm_val_t)nargs);
|
||||||
|
|
||||||
uint8_t rk = vm_kind_of(ctx, n->control_call.tid);
|
uint8_t rk = vm_kind_of(ctx, n->control_call.tid);
|
||||||
if (rk == SPL_VOID)
|
if (rk == SPL_VOID)
|
||||||
emit(cg, SPL_DROP, SPL_VOID, 0); /* NCALL 总是 push 一个结果 */
|
emit(cg, SPL_DROP, SPL_VOID, 0); /* NCALL always push result */
|
||||||
else
|
else
|
||||||
cg_store_slot(cg, ref, rk);
|
cg_result(cg, ref, rk);
|
||||||
} else {
|
} else {
|
||||||
spl_type_id_t ret_t = n->control_call.tid;
|
spl_type_id_t ret_t = n->control_call.tid;
|
||||||
bool ret_is_sret = ir_is_aggregate(ctx, ret_t);
|
bool ret_is_sret = ir_is_aggregate(ctx, ret_t);
|
||||||
usize sret_off = 0;
|
usize sret_off = 0;
|
||||||
|
/* j = 本节点已压入 tracked 块之上的值的个数(用于栈化 PICK 距离)。
|
||||||
|
先压 2 个 ABI 空闲 slot,故从 2 起算。 */
|
||||||
|
usize j = 2;
|
||||||
|
|
||||||
|
/* ABI:实参下方预留 2 个空闲 slot(PAD0/PAD1)。 */
|
||||||
|
CG_PUSH(cg, SPL_U64, 0);
|
||||||
|
CG_PUSH(cg, SPL_U64, 0);
|
||||||
|
|
||||||
if (ret_is_sret) {
|
if (ret_is_sret) {
|
||||||
sret_off = vec_at(cg->frame.slots, ref).data_off;
|
sret_off = vec_at(cg->frame.slots, ref).data_off;
|
||||||
CG_LADDR(cg, sret_off);
|
CG_LADDR(cg, sret_off);
|
||||||
|
j = 3;
|
||||||
}
|
}
|
||||||
for (usize i = 0; i < nargs; ++i) {
|
for (usize i = 0; i < nargs; ++i) {
|
||||||
spl_ir_node_ref_t pref = vec_at(n->control_call.params, i);
|
spl_ir_node_ref_t pref = vec_at(n->control_call.params, i);
|
||||||
const spl_ir_node_t *pn = &vec_at(cg->irf->nodes, pref);
|
const spl_ir_node_t *pn = &vec_at(cg->irf->nodes, pref);
|
||||||
spl_type_id_t ptid = ir_result_tid(ctx, pn);
|
spl_type_id_t ptid = ir_result_tid(ctx, pn);
|
||||||
uint8_t pk = vm_kind_of(ctx, ptid);
|
uint8_t pk = vm_kind_of(ctx, ptid);
|
||||||
cg_load_slot(cg, pref, pk);
|
cg_operand(cg, pref, pk, j);
|
||||||
|
j++;
|
||||||
}
|
}
|
||||||
cg_load_slot(cg, fref, SPL_PTR);
|
cg_operand(cg, fref, SPL_PTR, j);
|
||||||
spl_vm_val_t actual_nargs = nargs + (ret_is_sret ? 1 : 0);
|
spl_vm_val_t actual_nargs = nargs + (ret_is_sret ? 1 : 0);
|
||||||
emit(cg, SPL_CALL, SPL_VOID, actual_nargs);
|
emit(cg, SPL_CALL, SPL_VOID, actual_nargs);
|
||||||
|
|
||||||
if (ret_is_sret) {
|
if (ret_is_sret) {
|
||||||
/* 结果槽保存 sret 指针,后续 agg_extract 把它当聚合首地址 */
|
/* result slot saves sret pointer */
|
||||||
CG_LADDR(cg, sret_off);
|
CG_LADDR(cg, sret_off);
|
||||||
cg_store_slot(cg, ref, SPL_PTR);
|
cg_result(cg, ref, SPL_PTR);
|
||||||
} else {
|
} else {
|
||||||
uint8_t rk = vm_kind_of(ctx, ret_t);
|
uint8_t rk = vm_kind_of(ctx, ret_t);
|
||||||
if (rk != SPL_VOID)
|
if (rk != SPL_VOID)
|
||||||
cg_store_slot(cg, ref, rk);
|
cg_result(cg, ref, rk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -933,10 +1180,10 @@ static void cg_node(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|||||||
case SPL_IR_ARITH_NOT: {
|
case SPL_IR_ARITH_NOT: {
|
||||||
spl_type_node_t *tn = spl_type_node(cg->ctx->type, n->arith.tid);
|
spl_type_node_t *tn = spl_type_node(cg->ctx->type, n->arith.tid);
|
||||||
if (tn && tn->kind == SPL_TYPE_BOOL) {
|
if (tn && tn->kind == SPL_TYPE_BOOL) {
|
||||||
cg_load_slot(cg, n->arith.left, SPL_BOOL);
|
cg_operand(cg, n->arith.left, SPL_BOOL, 0);
|
||||||
CG_PUSH(cg, SPL_BOOL, 0);
|
CG_PUSH(cg, SPL_BOOL, 0);
|
||||||
CG_OP(cg, SPL_EQ, SPL_BOOL);
|
CG_OP(cg, SPL_EQ, SPL_BOOL);
|
||||||
cg_store_slot(cg, ref, SPL_BOOL);
|
cg_result(cg, ref, SPL_BOOL);
|
||||||
} else {
|
} else {
|
||||||
cg_unop(cg, ref, n, SPL_NOT);
|
cg_unop(cg, ref, n, SPL_NOT);
|
||||||
}
|
}
|
||||||
@@ -1098,9 +1345,49 @@ static usize translate_function(spl_ir2vm_t *ctx, spl_ir_func_ref_t fid, fix_vec
|
|||||||
for (usize i = 0; i < nnodes; ++i)
|
for (usize i = 0; i < nnodes; ++i)
|
||||||
vec_push(cg.node_ip, 0);
|
vec_push(cg.node_ip, 0);
|
||||||
|
|
||||||
|
unsigned char *leader = NULL;
|
||||||
|
cg.opt = ctx->opt;
|
||||||
|
if (ctx->opt) {
|
||||||
|
cg.stk_loc = (uint8_t *)calloc(nnodes, 1);
|
||||||
|
cg.stk_pos = (usize *)calloc(nnodes, sizeof(usize));
|
||||||
|
cg.use_left = (usize *)calloc(nnodes, sizeof(usize));
|
||||||
|
cg.at_pos = (usize *)calloc(nnodes, sizeof(usize));
|
||||||
|
leader = (unsigned char *)calloc(nnodes, 1);
|
||||||
|
cg.tracked = 0;
|
||||||
|
for (usize i = 1; i < nnodes; ++i)
|
||||||
|
count_refs(&cg, &vec_at(irf->nodes, i));
|
||||||
|
if (nnodes > 1)
|
||||||
|
leader[1] = 1;
|
||||||
for (usize i = 1; i < nnodes; ++i) {
|
for (usize i = 1; i < nnodes; ++i) {
|
||||||
|
const spl_ir_node_t *n = &vec_at(irf->nodes, i);
|
||||||
|
if (n->kind == SPL_IR_CONTROL_BR) {
|
||||||
|
if (n->control_br.true_label < nnodes)
|
||||||
|
leader[n->control_br.true_label] = 1;
|
||||||
|
if (n->control_br.false_label < nnodes)
|
||||||
|
leader[n->control_br.false_label] = 1;
|
||||||
|
} else if (n->kind == SPL_IR_CONTROL_JMP) {
|
||||||
|
if (n->control_jmp.label < nnodes)
|
||||||
|
leader[n->control_jmp.label] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (usize i = 1; i < nnodes; ++i) {
|
||||||
|
const spl_ir_node_t *n = &vec_at(irf->nodes, i);
|
||||||
|
if (cg.opt) {
|
||||||
|
if (leader[i] || node_needs_slots(&cg, n))
|
||||||
|
cg_flush(&cg);
|
||||||
|
}
|
||||||
vec_at(cg.node_ip, i) = ip_of(&cg);
|
vec_at(cg.node_ip, i) = ip_of(&cg);
|
||||||
cg_node(&cg, i, &vec_at(irf->nodes, i));
|
cg_node(&cg, i, n);
|
||||||
|
}
|
||||||
|
if (cg.opt) {
|
||||||
|
cg_flush(&cg);
|
||||||
|
free(cg.stk_loc);
|
||||||
|
free(cg.stk_pos);
|
||||||
|
free(cg.use_left);
|
||||||
|
free(cg.at_pos);
|
||||||
|
free(leader);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (usize i = 0; i < vec_size(local_fixups); ++i) {
|
for (usize i = 0; i < vec_size(local_fixups); ++i) {
|
||||||
@@ -1124,12 +1411,13 @@ static usize spl_map_hash(usize k) { return MAP_HASH_INT(k); }
|
|||||||
static int spl_map_cmp(usize a, usize b) { return MAP_CMP_INT(a, b); }
|
static int spl_map_cmp(usize a, usize b) { return MAP_CMP_INT(a, b); }
|
||||||
|
|
||||||
void spl_ir2vm_init(spl_ir2vm_t *ctx, const spl_ir_t *ir, const spl_type_t *type,
|
void spl_ir2vm_init(spl_ir2vm_t *ctx, const spl_ir_t *ir, const spl_type_t *type,
|
||||||
spl_layout_t *layout, const spl_attr_t *attr) {
|
spl_layout_t *layout, const spl_attr_t *attr, int opt) {
|
||||||
Assert(ir != NULL && type != NULL && attr != NULL);
|
Assert(ir != NULL && type != NULL && attr != NULL);
|
||||||
ctx->ir = ir;
|
ctx->ir = ir;
|
||||||
ctx->type = type;
|
ctx->type = type;
|
||||||
ctx->attr = attr;
|
ctx->attr = attr;
|
||||||
ctx->layout = layout;
|
ctx->layout = layout;
|
||||||
|
ctx->opt = opt;
|
||||||
map_init(ctx->extern_native, spl_map_hash, spl_map_cmp);
|
map_init(ctx->extern_native, spl_map_hash, spl_map_cmp);
|
||||||
spl_prog_init(&ctx->prog);
|
spl_prog_init(&ctx->prog);
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -25,10 +25,11 @@ typedef struct {
|
|||||||
spl_layout_t *layout; /* 借用 splc0 的 layout(size/align/offset 唯一来源) */
|
spl_layout_t *layout; /* 借用 splc0 的 layout(size/align/offset 唯一来源) */
|
||||||
|
|
||||||
spl_diag_t *diag;
|
spl_diag_t *diag;
|
||||||
|
int opt; /* 非 0:块内操作数栈化(表达式临时值留栈,块边界溢出回槽) */
|
||||||
} spl_ir2vm_t;
|
} spl_ir2vm_t;
|
||||||
|
|
||||||
void spl_ir2vm_init(spl_ir2vm_t *ctx, const spl_ir_t *ir, const spl_type_t *type,
|
void spl_ir2vm_init(spl_ir2vm_t *ctx, const spl_ir_t *ir, const spl_type_t *type,
|
||||||
spl_layout_t *layout, const spl_attr_t *attr);
|
spl_layout_t *layout, const spl_attr_t *attr, int opt);
|
||||||
void spl_ir2vm_drop(spl_ir2vm_t *ctx);
|
void spl_ir2vm_drop(spl_ir2vm_t *ctx);
|
||||||
|
|
||||||
int spl_ir2vm_run_func(spl_ir2vm_t *ctx, spl_ir_func_ref_t ref);
|
int spl_ir2vm_run_func(spl_ir2vm_t *ctx, spl_ir_func_ref_t ref);
|
||||||
|
|||||||
@@ -417,6 +417,20 @@ static spl_ast_node_ref_t parse_pattern_atom(spl_parser_t *p) {
|
|||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case TOK_AND: {
|
||||||
|
/* `&name`:解包左值引用绑定。仅允许作用于绑定名。 */
|
||||||
|
advance(p);
|
||||||
|
const spl_tok_t *nx = peek(p);
|
||||||
|
if (!nx || nx->type != TOK_IDENT) {
|
||||||
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, nx, "expected binding name after '&'");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
spl_ast_node_ref_t inner = parse_pattern_atom(p);
|
||||||
|
spl_ast_node_ref_t ref = new_node(p, SPL_AST_PATTERN_REF, t);
|
||||||
|
node_at(p->ast, ref)->pattern_ref.pat = inner;
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_UNRECOGNIZED, t, "unexpected token in pattern");
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_UNRECOGNIZED, t, "unexpected token in pattern");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+92
-29
@@ -302,27 +302,23 @@ static spl_lang_type_id_t sema_build_aggregate(spl_sema_t *sema, spl_ast_node_t
|
|||||||
|
|
||||||
spl_lang_type_id_t saved_agg = sema->current_aggregate_lang;
|
spl_lang_type_id_t saved_agg = sema->current_aggregate_lang;
|
||||||
sema->current_aggregate_lang = agg_lang;
|
sema->current_aggregate_lang = agg_lang;
|
||||||
|
/* 聚合体自成命名空间:成员类型可引用同体声明的嵌套类型(裸名)。 */
|
||||||
|
sema_push_scope(sema);
|
||||||
|
|
||||||
spl_type_member_vec_t members;
|
spl_type_member_vec_t members;
|
||||||
vec_init(members);
|
vec_init(members);
|
||||||
spl_lang_method_vec_t methods;
|
spl_lang_method_vec_t methods;
|
||||||
vec_init(methods);
|
vec_init(methods);
|
||||||
|
|
||||||
|
/* 第一遍:登记嵌套类型/函数的 def,挂进 methods 与聚合作用域。成员类型可能引用它们,
|
||||||
|
必须早于成员类型推断,否则限定名(经 methods)与裸名(经作用域)都解析不到。 */
|
||||||
for (usize i = 0; i < vec_size(te->type_aggregate.members); ++i) {
|
for (usize i = 0; i < vec_size(te->type_aggregate.members); ++i) {
|
||||||
spl_ast_node_ref_t mref = vec_at(te->type_aggregate.members, i);
|
spl_ast_node_ref_t mref = vec_at(te->type_aggregate.members, i);
|
||||||
spl_ast_node_t *mn = spl_ast_node(sema->ast, mref);
|
spl_ast_node_t *mn = spl_ast_node(sema->ast, mref);
|
||||||
if (!mn)
|
if (!mn)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (mn->kind == SPL_AST_MEMBER_DECL) {
|
if (mn->kind == SPL_AST_FN_DECL) {
|
||||||
spl_lang_type_id_t mtid = 0;
|
|
||||||
if (mn->member_decl.type_expr)
|
|
||||||
mtid = sema_infer(sema, mn->member_decl.type_expr);
|
|
||||||
spl_type_member_t m = {.name = mn->member_decl.name,
|
|
||||||
.type_id = spl_lang_type_get_ir_type(sema->type, mtid),
|
|
||||||
.ast_node_ref = mref};
|
|
||||||
vec_push(members, m);
|
|
||||||
} else if (mn->kind == SPL_AST_FN_DECL) {
|
|
||||||
spl_def_id_t fn_def = mn->resolved_def_id;
|
spl_def_id_t fn_def = mn->resolved_def_id;
|
||||||
if (!fn_def) {
|
if (!fn_def) {
|
||||||
fn_def = spl_def_alloc(sema->type, SPL_DEF_FN, mn->fn_decl.name, mn->dbg, sema->ast,
|
fn_def = spl_def_alloc(sema->type, SPL_DEF_FN, mn->fn_decl.name, mn->dbg, sema->ast,
|
||||||
@@ -334,6 +330,9 @@ static spl_lang_type_id_t sema_build_aggregate(spl_sema_t *sema, spl_ast_node_t
|
|||||||
fn_d->owner_def = owner_def_id;
|
fn_d->owner_def = owner_def_id;
|
||||||
spl_lang_method_t meth = {.name = mn->fn_decl.name, .def_id = fn_def};
|
spl_lang_method_t meth = {.name = mn->fn_decl.name, .def_id = fn_def};
|
||||||
vec_push(methods, meth);
|
vec_push(methods, meth);
|
||||||
|
spl_symbol_t fsym = {.name = mn->fn_decl.name, .kind = SPL_SYMBOL_KIND_FN};
|
||||||
|
fsym.node = fn_def;
|
||||||
|
spl_scope_insert(sema->scope, sema->scope->current_scope, fsym);
|
||||||
} else if (mn->kind == SPL_AST_TYPE_DECL) {
|
} else if (mn->kind == SPL_AST_TYPE_DECL) {
|
||||||
spl_ast_node_t *tpat = spl_ast_node(sema->ast, mn->type_decl.pattern);
|
spl_ast_node_t *tpat = spl_ast_node(sema->ast, mn->type_decl.pattern);
|
||||||
if (!tpat || tpat->kind != SPL_AST_PATTERN_IDENT)
|
if (!tpat || tpat->kind != SPL_AST_PATTERN_IDENT)
|
||||||
@@ -352,9 +351,37 @@ static spl_lang_type_id_t sema_build_aggregate(spl_sema_t *sema, spl_ast_node_t
|
|||||||
}
|
}
|
||||||
spl_lang_method_t meth = {.name = tname, .def_id = tdef};
|
spl_lang_method_t meth = {.name = tname, .def_id = tdef};
|
||||||
vec_push(methods, meth);
|
vec_push(methods, meth);
|
||||||
|
spl_symbol_t tsym = {.name = tname, .kind = SPL_SYMBOL_KIND_TYPE};
|
||||||
|
tsym.node = tdef;
|
||||||
|
spl_scope_insert(sema->scope, sema->scope->current_scope, tsym);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spl_lang_type_node_t *lslot = spl_lang_type_node(sema->type, agg_lang);
|
||||||
|
if (lslot) {
|
||||||
|
vec_free(lslot->methods);
|
||||||
|
lslot->methods = methods;
|
||||||
|
vec_init(methods);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 第二遍:成员(字段/变体)类型推断;此时嵌套名字已可解析。 */
|
||||||
|
for (usize i = 0; i < vec_size(te->type_aggregate.members); ++i) {
|
||||||
|
spl_ast_node_ref_t mref = vec_at(te->type_aggregate.members, i);
|
||||||
|
spl_ast_node_t *mn = spl_ast_node(sema->ast, mref);
|
||||||
|
if (!mn)
|
||||||
|
continue;
|
||||||
|
if (mn->kind == SPL_AST_MEMBER_DECL) {
|
||||||
|
spl_lang_type_id_t mtid = 0;
|
||||||
|
if (mn->member_decl.type_expr)
|
||||||
|
mtid = sema_infer(sema, mn->member_decl.type_expr);
|
||||||
|
spl_type_member_t m = {.name = mn->member_decl.name,
|
||||||
|
.type_id = spl_lang_type_get_ir_type(sema->type, mtid),
|
||||||
|
.ast_node_ref = mref};
|
||||||
|
vec_push(members, m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sema_pop_scope(sema);
|
||||||
sema->current_aggregate_lang = saved_agg;
|
sema->current_aggregate_lang = saved_agg;
|
||||||
|
|
||||||
spl_type_node_t tn = {0};
|
spl_type_node_t tn = {0};
|
||||||
@@ -366,13 +393,6 @@ static spl_lang_type_id_t sema_build_aggregate(spl_sema_t *sema, spl_ast_node_t
|
|||||||
vec_init(members);
|
vec_init(members);
|
||||||
spl_lang_define_ir(sema->type, agg_ir, &tn);
|
spl_lang_define_ir(sema->type, agg_ir, &tn);
|
||||||
|
|
||||||
spl_lang_type_node_t *lslot = spl_lang_type_node(sema->type, agg_lang);
|
|
||||||
if (lslot) {
|
|
||||||
vec_free(lslot->methods);
|
|
||||||
lslot->methods = methods;
|
|
||||||
vec_init(methods);
|
|
||||||
}
|
|
||||||
|
|
||||||
return agg_lang;
|
return agg_lang;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -764,8 +784,7 @@ static void sema_collect_signatures(spl_sema_t *sema) {
|
|||||||
spl_def_node_t *d = &vec_at(sema->type->def_table, i);
|
spl_def_node_t *d = &vec_at(sema->type->def_table, i);
|
||||||
/* Nested imports share def_table, but each module has its own scope.
|
/* Nested imports share def_table, but each module has its own scope.
|
||||||
* Resolve other modules' signatures only when processing their AST. */
|
* Resolve other modules' signatures only when processing their AST. */
|
||||||
if (d->owner_ast == sema->ast && d->kind == SPL_DEF_FN &&
|
if (d->owner_ast == sema->ast && d->kind == SPL_DEF_FN && d->state == SPL_DECL_UNRESOLVED)
|
||||||
d->state == SPL_DECL_UNRESOLVED)
|
|
||||||
sema_eval_decl(sema, i);
|
sema_eval_decl(sema, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -830,14 +849,14 @@ static spl_lang_type_id_t sema_infer_type_context(spl_sema_t *sema, spl_ast_node
|
|||||||
spl_symbol_t symbol;
|
spl_symbol_t symbol;
|
||||||
if (!spl_scope_find(sema->scope, n->ident.name, &symbol) ||
|
if (!spl_scope_find(sema->scope, n->ident.name, &symbol) ||
|
||||||
symbol.kind != SPL_SYMBOL_KIND_TYPE) {
|
symbol.kind != SPL_SYMBOL_KIND_TYPE) {
|
||||||
spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg,
|
spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "`%s` is not a type",
|
||||||
"`%s` is not a type", n->ident.name);
|
n->ident.name);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
spl_def_node_t *def = spl_def_node(sema->type, symbol.node);
|
spl_def_node_t *def = spl_def_node(sema->type, symbol.node);
|
||||||
if (!sema_def_is_type(def)) {
|
if (!sema_def_is_type(def)) {
|
||||||
spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg,
|
spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "`%s` is not a type",
|
||||||
"`%s` is not a type", n->ident.name);
|
n->ident.name);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
n->resolved_def_id = symbol.node;
|
n->resolved_def_id = symbol.node;
|
||||||
@@ -880,16 +899,15 @@ static spl_lang_type_id_t sema_infer_type_context(spl_sema_t *sema, spl_ast_node
|
|||||||
spl_lang_type_id_vec_t elements;
|
spl_lang_type_id_vec_t elements;
|
||||||
vec_init(elements);
|
vec_init(elements);
|
||||||
for (usize i = 0; i < vec_size(n->tuple.elems); ++i) {
|
for (usize i = 0; i < vec_size(n->tuple.elems); ++i) {
|
||||||
spl_lang_type_id_t element =
|
spl_lang_type_id_t element = sema_infer_type_context(sema, vec_at(n->tuple.elems, i));
|
||||||
sema_infer_type_context(sema, vec_at(n->tuple.elems, i));
|
|
||||||
if (!element) {
|
if (!element) {
|
||||||
vec_free(elements);
|
vec_free(elements);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
vec_push(elements, element);
|
vec_push(elements, element);
|
||||||
}
|
}
|
||||||
spl_lang_type_id_t result = spl_lang_make_tuple(
|
spl_lang_type_id_t result =
|
||||||
sema->type, vec_unsafe_get_data(elements), vec_size(elements));
|
spl_lang_make_tuple(sema->type, vec_unsafe_get_data(elements), vec_size(elements));
|
||||||
vec_free(elements);
|
vec_free(elements);
|
||||||
n->resolved_lang_tid = result;
|
n->resolved_lang_tid = result;
|
||||||
return result;
|
return result;
|
||||||
@@ -1020,6 +1038,16 @@ static spl_lang_type_id_t sema_intrin_call(spl_sema_t *sema, spl_ast_node_t *n,
|
|||||||
}
|
}
|
||||||
return spl_lang_make_int(sema->type, sizeof(void *) * 8, 0);
|
return spl_lang_make_int(sema->type, sizeof(void *) * 8, 0);
|
||||||
}
|
}
|
||||||
|
case SPL_INTRIN_FILE_NAME:
|
||||||
|
case SPL_INTRIN_FN_NAME: {
|
||||||
|
spl_lang_type_id_t u8 = spl_lang_make_int(sema->type, 8, 0);
|
||||||
|
spl_lang_type_id_t slice = spl_lang_make_slice(sema->type, u8);
|
||||||
|
n->resolved_lang_tid = slice;
|
||||||
|
return slice;
|
||||||
|
}
|
||||||
|
case SPL_INTRIN_LINE:
|
||||||
|
case SPL_INTRIN_COL:
|
||||||
|
return spl_lang_make_int(sema->type, sizeof(void *) * 8, 0);
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
@@ -1128,6 +1156,18 @@ static void sema_bind_pattern(spl_sema_t *sema, spl_ast_node_ref_t pat_ref,
|
|||||||
case SPL_AST_PATTERN_UNDERSCORE:
|
case SPL_AST_PATTERN_UNDERSCORE:
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case SPL_AST_PATTERN_REF: {
|
||||||
|
if (!matched_tid) {
|
||||||
|
spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &pat->dbg,
|
||||||
|
"ref pattern requires a concrete type");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
spl_lang_type_id_t ptr = spl_lang_make_ptr(sema->type, matched_tid);
|
||||||
|
sema_bind_pattern(sema, pat->pattern_ref.pat, ptr);
|
||||||
|
pat->resolved_lang_tid = ptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case SPL_AST_PATTERN_LIST: {
|
case SPL_AST_PATTERN_LIST: {
|
||||||
for (usize i = 0; i < vec_size(pat->pattern_list.patterns); ++i)
|
for (usize i = 0; i < vec_size(pat->pattern_list.patterns); ++i)
|
||||||
sema_bind_pattern(sema, vec_at(pat->pattern_list.patterns, i), matched_tid);
|
sema_bind_pattern(sema, vec_at(pat->pattern_list.patterns, i), matched_tid);
|
||||||
@@ -2843,16 +2883,39 @@ void spl_sema_run(spl_sema_t *sema) {
|
|||||||
spl_sema_run_module(sema, root->root.members);
|
spl_sema_run_module(sema, root->root.members);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *key;
|
||||||
|
spl_symbol_t sym;
|
||||||
|
} spl_sema_sym_t;
|
||||||
|
|
||||||
|
static int spl_sema_sym_cmp(const void *a, const void *b) {
|
||||||
|
return strcmp(((const spl_sema_sym_t *)a)->key, ((const spl_sema_sym_t *)b)->key);
|
||||||
|
}
|
||||||
|
|
||||||
void spl_sema_dump(spl_sema_t *sema) {
|
void spl_sema_dump(spl_sema_t *sema) {
|
||||||
printf("Scopes:\n");
|
printf("Scopes:\n");
|
||||||
for (usize i = 1; i < vec_size(sema->scope->scopes); ++i) {
|
for (usize i = 1; i < vec_size(sema->scope->scopes); ++i) {
|
||||||
spl_scope_node_t *scope = &vec_at(sema->scope->scopes, i);
|
spl_scope_node_t *scope = &vec_at(sema->scope->scopes, i);
|
||||||
|
Assert(scope != NULL);
|
||||||
printf("scope[%zu] parent=%zu\n", i, scope->parent);
|
printf("scope[%zu] parent=%zu\n", i, scope->parent);
|
||||||
|
|
||||||
|
/* 按符号名排序输出:与哈希表内部布局解耦,保证确定顺序。 */
|
||||||
|
usize nsyms = scope->symbols.size;
|
||||||
|
spl_sema_sym_t *arr = nsyms ? malloc(nsyms * sizeof(*arr)) : NULL;
|
||||||
|
usize k = 0;
|
||||||
map_for(scope->symbols, j) {
|
map_for(scope->symbols, j) {
|
||||||
spl_symbol_t *symbol = &unsafe_map_at(scope->symbols, j).val;
|
Assert(arr != NULL);
|
||||||
printf(" %s -> def`%s`#%zu\n", unsafe_map_at(scope->symbols, j).key, symbol->name,
|
arr[k].key = unsafe_map_at(scope->symbols, j).key;
|
||||||
symbol->node);
|
arr[k].sym = unsafe_map_at(scope->symbols, j).val;
|
||||||
|
k++;
|
||||||
}
|
}
|
||||||
|
if (arr && nsyms > 1)
|
||||||
|
qsort(arr, nsyms, sizeof(*arr), spl_sema_sym_cmp);
|
||||||
|
for (k = 0; k < nsyms; ++k) {
|
||||||
|
Assert(arr != NULL);
|
||||||
|
printf(" %s -> def`%s`#%zu\n", arr[k].key, arr[k].sym.name, arr[k].sym.node);
|
||||||
|
}
|
||||||
|
free(arr);
|
||||||
}
|
}
|
||||||
printf("LangTypeTable:\n");
|
printf("LangTypeTable:\n");
|
||||||
for (usize i = 1; i < vec_size(sema->type->lang_table); ++i) {
|
for (usize i = 1; i < vec_size(sema->type->lang_table); ++i) {
|
||||||
|
|||||||
+58
-8
@@ -10,6 +10,9 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "ir_opt.h"
|
||||||
|
#include "vm_opt.h"
|
||||||
|
|
||||||
#include "spl_ast.h"
|
#include "spl_ast.h"
|
||||||
#include "spl_ast2ir.h"
|
#include "spl_ast2ir.h"
|
||||||
#include "spl_diag.h"
|
#include "spl_diag.h"
|
||||||
@@ -81,15 +84,18 @@ static void dump_tokens(spl_tok_vec_t toks) {
|
|||||||
vec_free(toks);
|
vec_free(toks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 折叠重复分隔符并去掉 "." 路径分量,使同一模块的 import 解析结果唯一。
|
/* 折叠重复分隔符、解析 "." 与 ".." 路径分量,使同一模块的 import 解析结果唯一。
|
||||||
就地重写,'\' 统一成 '/';保留前导分隔符(绝对路径)。 */
|
就地重写,'\' 统一成 '/';保留前导分隔符(绝对路径)。
|
||||||
|
".." 会弹出上一个分量;无分量可弹时按相对语义保留(绝对根则忽略)。 */
|
||||||
static void normalize_import_path(char *p) {
|
static void normalize_import_path(char *p) {
|
||||||
usize n = strlen(p);
|
usize n = strlen(p);
|
||||||
char *out = malloc(n + 1);
|
char *out = malloc(n + 1);
|
||||||
if (!out)
|
if (!out)
|
||||||
return;
|
return;
|
||||||
|
char *base = out;
|
||||||
char *w = out;
|
char *w = out;
|
||||||
if (p[0] == '/' || p[0] == '\\')
|
int has_root = (p[0] == '/' || p[0] == '\\');
|
||||||
|
if (has_root)
|
||||||
*w++ = '/';
|
*w++ = '/';
|
||||||
|
|
||||||
char *r = p;
|
char *r = p;
|
||||||
@@ -104,7 +110,31 @@ static void normalize_import_path(char *p) {
|
|||||||
usize len = (usize)(r - seg);
|
usize len = (usize)(r - seg);
|
||||||
if (len == 1 && seg[0] == '.')
|
if (len == 1 && seg[0] == '.')
|
||||||
continue;
|
continue;
|
||||||
if (w != out && w[-1] != '/')
|
if (len == 2 && seg[0] == '.' && seg[1] == '.') {
|
||||||
|
if (w == base + (has_root ? 1 : 0)) {
|
||||||
|
if (!has_root) {
|
||||||
|
*w++ = '.';
|
||||||
|
*w++ = '.';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
char *q = w;
|
||||||
|
while (q > base && q[-1] != '/')
|
||||||
|
q--;
|
||||||
|
usize sl = (usize)(w - q);
|
||||||
|
if (sl == 2 && q[0] == '.' && q[1] == '.') {
|
||||||
|
*w++ = '.';
|
||||||
|
*w++ = '.';
|
||||||
|
} else {
|
||||||
|
if (q > base && q[-1] == '/')
|
||||||
|
q--;
|
||||||
|
w = q;
|
||||||
|
if (w == base && has_root)
|
||||||
|
w = base + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (w != base && w[-1] != '/')
|
||||||
*w++ = '/';
|
*w++ = '/';
|
||||||
memcpy(w, seg, len);
|
memcpy(w, seg, len);
|
||||||
w += len;
|
w += len;
|
||||||
@@ -190,7 +220,7 @@ static spl_ast_t *driver_load(void *user, const char *from_fname, const char *pa
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int compile_spl(const char *src, const char *fname, const char *outpath, int gen_debug,
|
static int compile_spl(const char *src, const char *fname, const char *outpath, int gen_debug,
|
||||||
dump_t dump) {
|
dump_t dump, int opt) {
|
||||||
(void)gen_debug;
|
(void)gen_debug;
|
||||||
|
|
||||||
spl_diag_t diag;
|
spl_diag_t diag;
|
||||||
@@ -299,6 +329,10 @@ static int compile_spl(const char *src, const char *fname, const char *outpath,
|
|||||||
rc = 1;
|
rc = 1;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
/* IR 层优化 pass(默认关闭,--opt 开启)。 */
|
||||||
|
if (opt)
|
||||||
|
spl_ir_opt(&ir_builder.ir, &attr);
|
||||||
|
|
||||||
if (dump == DUMP_IR) {
|
if (dump == DUMP_IR) {
|
||||||
spl_ir_dump(&ir_builder.ir, &lang_type.ir, &attr);
|
spl_ir_dump(&ir_builder.ir, &lang_type.ir, &attr);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@@ -307,7 +341,7 @@ static int compile_spl(const char *src, const char *fname, const char *outpath,
|
|||||||
/* 类型已全部就绪:解析 layout,供 ir2vm 的 size/align/offset 使用。 */
|
/* 类型已全部就绪:解析 layout,供 ir2vm 的 size/align/offset 使用。 */
|
||||||
spl_layout_resolve(&layout, &lang_type.ir);
|
spl_layout_resolve(&layout, &lang_type.ir);
|
||||||
|
|
||||||
spl_ir2vm_init(&ir2vm, &a2ir.ir->ir, &lang_type.ir, &layout, &attr);
|
spl_ir2vm_init(&ir2vm, &a2ir.ir->ir, &lang_type.ir, &layout, &attr, opt);
|
||||||
ir2vm_ok = 1;
|
ir2vm_ok = 1;
|
||||||
spl_diag_set_stage(&diag, SPL_STAGE_IR2VM);
|
spl_diag_set_stage(&diag, SPL_STAGE_IR2VM);
|
||||||
ir2vm.diag = &diag;
|
ir2vm.diag = &diag;
|
||||||
@@ -321,6 +355,10 @@ static int compile_spl(const char *src, const char *fname, const char *outpath,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ir2vm 之后的字节码优化 pass(默认关闭,--opt 开启)。 */
|
||||||
|
if (opt)
|
||||||
|
spl_prog_opt(&ir2vm.prog);
|
||||||
|
|
||||||
spl_prog_store_to_file(outpath, &ir2vm.prog);
|
spl_prog_store_to_file(outpath, &ir2vm.prog);
|
||||||
rc = 0;
|
rc = 0;
|
||||||
|
|
||||||
@@ -370,6 +408,11 @@ int main(int argc, char **argv) {
|
|||||||
LOG_INFO("splc0 --dump <flags> <file> dump: lex,ast,sema,ir,vm,none");
|
LOG_INFO("splc0 --dump <flags> <file> dump: lex,ast,sema,ir,vm,none");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
int opt = 0;
|
||||||
|
for (int ai = 1; ai < argc; ai++)
|
||||||
|
if (strcmp(argv[ai], "--opt") == 0)
|
||||||
|
opt = 1;
|
||||||
|
|
||||||
int argi = 1;
|
int argi = 1;
|
||||||
if (argi >= argc) {
|
if (argi >= argc) {
|
||||||
LOG_FATAL("Usage: splc0 [--dump <flags>] <in> [out]");
|
LOG_FATAL("Usage: splc0 [--dump <flags>] <in> [out]");
|
||||||
@@ -408,19 +451,26 @@ int main(int argc, char **argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
int gen_debug = 0;
|
int gen_debug = 0;
|
||||||
|
while (argi < argc - 1) {
|
||||||
if (strcmp(argv[argi], "-g") == 0) {
|
if (strcmp(argv[argi], "-g") == 0) {
|
||||||
gen_debug = 1;
|
gen_debug = 1;
|
||||||
argi++;
|
argi++;
|
||||||
|
} else if (strcmp(argv[argi], "--opt") == 0) {
|
||||||
|
opt = 1;
|
||||||
|
argi++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (argc < argi + 2) {
|
if (argc < argi + 2) {
|
||||||
LOG_FATAL("Usage: splc0 <in> <out> [-g] or splc0 --dump <flags> <file>\n");
|
LOG_FATAL("Usage: splc0 <in> <out> [-g] [--opt] or splc0 --dump <flags> <file>\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
long len;
|
long len;
|
||||||
char *src = read_file(argv[argi], &len);
|
char *src = read_file(argv[argi], &len);
|
||||||
if (!src)
|
if (!src)
|
||||||
return 1;
|
return 1;
|
||||||
int rc = compile_spl(src, argv[argi], argv[argi + 1], gen_debug, dump);
|
int rc = compile_spl(src, argv[argi], argv[argi + 1], gen_debug, dump, opt);
|
||||||
free(src);
|
free(src);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
+165
@@ -0,0 +1,165 @@
|
|||||||
|
/* vm_opt.c - 字节码(VM 层)优化 pass
|
||||||
|
*
|
||||||
|
* 从 stage0/spl_mcode.c 迁出:对已生成的 spl_prog_t 做 prog->prog 优化。
|
||||||
|
* 默认不启用(由 splc0 --opt 驱动)。 */
|
||||||
|
|
||||||
|
#include "vm_opt.h"
|
||||||
|
|
||||||
|
#include "../stage0/include/core_map.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* 消除“store 后立刻 load 同一槽位”的往返。
|
||||||
|
*
|
||||||
|
* 模式(5 条):laddr X; swap; store k; laddr X; load k
|
||||||
|
* 变换(3 条):laddr X; pick 1; store k
|
||||||
|
* [.., v] -> laddr X => [.., v, X]
|
||||||
|
* -> pick 1 => [.., v, X, v]
|
||||||
|
* -> store k => [.., v] (存 v 到 X,同时栈上保留 v)
|
||||||
|
* 与原来 store 后再 load 的效果等价,但去掉 2 条指令与一次内存读。
|
||||||
|
*
|
||||||
|
* 会真的删除指令,因此必须重定位:相对跳转、函数表 address、
|
||||||
|
* 以及作为函数指针的 `push ptr <addr>`。仅当被删/被改写的指令不是
|
||||||
|
* 跳转目标或函数入口时才变换(已收集 mark)。 */
|
||||||
|
static usize peep_hash_int(usize k) { return MAP_HASH_INT(k); }
|
||||||
|
static int peep_cmp_int(usize a, usize b) { return MAP_CMP_INT(a, b); }
|
||||||
|
|
||||||
|
void spl_prog_peephole(spl_prog_t *prog) {
|
||||||
|
if (!prog)
|
||||||
|
return;
|
||||||
|
usize n = vec_size(prog->insns);
|
||||||
|
if (n < 5)
|
||||||
|
return;
|
||||||
|
spl_vm_ins_t *ins = prog->insns.data;
|
||||||
|
|
||||||
|
unsigned char *mark = (unsigned char *)calloc(n, 1); /* 跳转目标/函数入口,受保护 */
|
||||||
|
unsigned char *rem = (unsigned char *)calloc(n, 1);
|
||||||
|
if (!mark || !rem) {
|
||||||
|
free(mark);
|
||||||
|
free(rem);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (usize i = 0; i < n; i++) {
|
||||||
|
spl_vm_ins_t *x = &ins[i];
|
||||||
|
if (x->opcode == SPL_JMP || x->opcode == SPL_BZ || x->opcode == SPL_BNZ) {
|
||||||
|
isize t = (isize)i + 1 + (isize)x->imm;
|
||||||
|
if (t >= 0 && (usize)t < n)
|
||||||
|
mark[(usize)t] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vec_for(prog->funcs, fi) {
|
||||||
|
spl_vm_func_t *f = &vec_at(prog->funcs, fi);
|
||||||
|
if (f->address < n)
|
||||||
|
mark[f->address] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (usize i = 0; i + 4 < n; i++) {
|
||||||
|
spl_vm_ins_t *a = &ins[i];
|
||||||
|
spl_vm_ins_t *b = &ins[i + 1];
|
||||||
|
spl_vm_ins_t *c = &ins[i + 2];
|
||||||
|
spl_vm_ins_t *d = &ins[i + 3];
|
||||||
|
spl_vm_ins_t *e = &ins[i + 4];
|
||||||
|
if (a->opcode == SPL_LADDR && b->opcode == SPL_SWAP && c->opcode == SPL_STORE &&
|
||||||
|
d->opcode == SPL_LADDR && e->opcode == SPL_LOAD && a->imm == d->imm &&
|
||||||
|
c->type == e->type) {
|
||||||
|
if (mark[i + 1] || mark[i + 3] || mark[i + 4])
|
||||||
|
continue;
|
||||||
|
b->opcode = SPL_PICK;
|
||||||
|
b->type = SPL_VOID;
|
||||||
|
b->imm = 1;
|
||||||
|
rem[i + 3] = 1;
|
||||||
|
rem[i + 4] = 1;
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
usize kept = 0;
|
||||||
|
for (usize i = 0; i < n; i++)
|
||||||
|
if (!rem[i])
|
||||||
|
kept++;
|
||||||
|
if (kept == n) {
|
||||||
|
free(mark);
|
||||||
|
free(rem);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_vm_ins_t *fresh = (spl_vm_ins_t *)malloc(sizeof(spl_vm_ins_t) * (kept ? kept : 1));
|
||||||
|
usize *map = (usize *)malloc(sizeof(usize) * (n + 1)); /* old index -> new index */
|
||||||
|
usize *old_of_new = (usize *)malloc(sizeof(usize) * (kept ? kept : 1));
|
||||||
|
if (!fresh || !map || !old_of_new) {
|
||||||
|
free(mark);
|
||||||
|
free(rem);
|
||||||
|
free(fresh);
|
||||||
|
free(map);
|
||||||
|
free(old_of_new);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
usize out = 0;
|
||||||
|
for (usize i = 0; i < n; i++) {
|
||||||
|
if (!rem[i]) {
|
||||||
|
map[i] = out;
|
||||||
|
old_of_new[out] = i;
|
||||||
|
fresh[out] = ins[i];
|
||||||
|
out++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map[n] = kept;
|
||||||
|
for (usize i = n; i-- > 0;)
|
||||||
|
if (rem[i])
|
||||||
|
map[i] = map[i + 1];
|
||||||
|
|
||||||
|
/* 函数地址 old->new */
|
||||||
|
MAP(usize, usize) addr_map;
|
||||||
|
map_init(addr_map, peep_hash_int, peep_cmp_int);
|
||||||
|
vec_for(prog->funcs, fi) {
|
||||||
|
spl_vm_func_t *f = &vec_at(prog->funcs, fi);
|
||||||
|
usize old_a = f->address;
|
||||||
|
if (old_a < n) {
|
||||||
|
usize new_a = map[old_a];
|
||||||
|
map_put(addr_map, old_a, new_a);
|
||||||
|
f->address = new_a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 修正相对跳转与函数指针常量 */
|
||||||
|
for (usize j = 0; j < kept; j++) {
|
||||||
|
spl_vm_ins_t *x = &fresh[j];
|
||||||
|
if (x->opcode == SPL_JMP || x->opcode == SPL_BZ || x->opcode == SPL_BNZ) {
|
||||||
|
isize t = (isize)old_of_new[j] + 1 + (isize)x->imm;
|
||||||
|
if (t >= 0 && (usize)t <= n) {
|
||||||
|
usize nt = map[(usize)t];
|
||||||
|
x->imm = (spl_vm_val_t)(isize)((isize)nt - (isize)(j + 1));
|
||||||
|
}
|
||||||
|
} else if (x->opcode == SPL_PUSH && x->type == SPL_PTR && x->imm != 0) {
|
||||||
|
usize nv = 0;
|
||||||
|
if (map_get(addr_map, (usize)x->imm, &nv))
|
||||||
|
x->imm = nv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map_free(addr_map);
|
||||||
|
|
||||||
|
/* 指令删除后函数长度变化,重算 ninsns(函数按地址升序排列)。 */
|
||||||
|
{
|
||||||
|
usize nf = vec_size(prog->funcs);
|
||||||
|
for (usize i = 0; i < nf; i++) {
|
||||||
|
spl_vm_func_t *f = &vec_at(prog->funcs, i);
|
||||||
|
usize next = (i + 1 < nf) ? vec_at(prog->funcs, i + 1).address : kept;
|
||||||
|
f->ninsns = next > f->address ? (spl_vm_val_t)(next - f->address) : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(prog->insns.data);
|
||||||
|
prog->insns.data = fresh;
|
||||||
|
prog->insns.size = kept;
|
||||||
|
prog->insns.cap = kept;
|
||||||
|
|
||||||
|
free(mark);
|
||||||
|
free(rem);
|
||||||
|
free(map);
|
||||||
|
free(old_of_new);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 优化 pass 流水线(纯 prog->prog):当前 P1,后续可追加。 */
|
||||||
|
void spl_prog_opt(spl_prog_t *prog) { spl_prog_peephole(prog); }
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef __VM_OPT_H__
|
||||||
|
#define __VM_OPT_H__
|
||||||
|
|
||||||
|
/* 字节码(VM 层)优化 pass:输入输出都是 spl_prog_t,纯函数式、可开关。 */
|
||||||
|
|
||||||
|
#include "../stage0/spl_mcode.h"
|
||||||
|
|
||||||
|
void spl_prog_peephole(spl_prog_t *prog);
|
||||||
|
void spl_prog_opt(spl_prog_t *prog);
|
||||||
|
|
||||||
|
#endif /* __VM_OPT_H__ */
|
||||||
+58
-13
@@ -1,9 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// AST:用 ADT(enum 带载荷)表达节点,节点引用是 arena 序号 usize。
|
// AST:节点为带载荷 ADT,节点引用是 arena 序号(0 为 NONE 哨兵)。最终 dump/语义须与
|
||||||
// stage1 的 C 结构只是「手写 union 模拟 ADT」;这里直接用 SPL 的 enum 载荷,
|
// splc0 一致。
|
||||||
// 不照抄 union 布局,只要求最终 dump / 语义与 stage1 一致。
|
|
||||||
// 索引 0 为 NONE 哨兵(与 stage1 一致)。
|
|
||||||
|
|
||||||
type .{ .Error } = @import("utils/error.spl");
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
type .{ .Vec } = @import("utils/vec.spl");
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
@@ -18,7 +16,6 @@ type .{ .Sources, .Source, .set_at } = @import("utils/source.spl");
|
|||||||
// 算子不另立 enum:AST 直接存词法 Punct。一元/二元由节点 kind 区分
|
// 算子不另立 enum:AST 直接存词法 Punct。一元/二元由节点 kind 区分
|
||||||
// (一元:Sub=负 / Not=! / BitNot=~ / And=& / Mul=.*;二元即对应 Punct)。
|
// (一元:Sub=负 / Not=! / BitNot=~ / And=& / Mul=.*;二元即对应 Punct)。
|
||||||
|
|
||||||
// ---- 节点体(ADT;variant 名 = stage1 的 kind 名;载荷用内联匿名 struct)----
|
|
||||||
|
|
||||||
type Body = enum {
|
type Body = enum {
|
||||||
None,
|
None,
|
||||||
@@ -45,11 +42,12 @@ type Body = enum {
|
|||||||
type_expr: usize,
|
type_expr: usize,
|
||||||
init_expr: usize,
|
init_expr: usize,
|
||||||
},
|
},
|
||||||
ParamDecl: struct { attr_list: Vec, name: ByteView, type_expr: usize },
|
ParamDecl: struct { attr_list: Vec, is_comptime: bool, name: ByteView, type_expr: usize },
|
||||||
StatementRet: struct { expr: usize },
|
StatementRet: struct { expr: usize },
|
||||||
StatementBreak: struct { label: usize, expr: usize },
|
StatementBreak: struct { label: usize, expr: usize },
|
||||||
StatementContinue: struct { label: usize },
|
StatementContinue: struct { label: usize },
|
||||||
StatementDefer: struct { body: usize },
|
StatementDefer: struct { body: usize },
|
||||||
|
StatementErrdefer: struct { body: usize }, // errdefer:仅错误退出时执行
|
||||||
StatementComptime: struct { body: usize },
|
StatementComptime: struct { body: usize },
|
||||||
StatementDecl: struct { decl: usize },
|
StatementDecl: struct { decl: usize },
|
||||||
StatementExpr: struct { expr: usize },
|
StatementExpr: struct { expr: usize },
|
||||||
@@ -87,6 +85,8 @@ type Body = enum {
|
|||||||
},
|
},
|
||||||
ExprAggregateLit: struct { name: ByteView, fields: Vec },
|
ExprAggregateLit: struct { name: ByteView, fields: Vec },
|
||||||
ExprComptimeCall: struct { name: ByteView, args: Vec },
|
ExprComptimeCall: struct { name: ByteView, args: Vec },
|
||||||
|
ExprTry: struct { value: usize }, // `try expr`
|
||||||
|
ExprCatch: struct { value: usize, pattern: usize, handler: usize }, // `expr catch <pat> { }`
|
||||||
ExprBlock: struct { stmts: Vec, tail_expr: usize },
|
ExprBlock: struct { stmts: Vec, tail_expr: usize },
|
||||||
ExprIf: struct { cond: usize, then_block: usize, else_branch: usize },
|
ExprIf: struct { cond: usize, then_block: usize, else_branch: usize },
|
||||||
ExprIfvar: struct { var_decl: usize, then_block: usize, else_branch: usize },
|
ExprIfvar: struct { var_decl: usize, then_block: usize, else_branch: usize },
|
||||||
@@ -121,6 +121,9 @@ type Body = enum {
|
|||||||
PatternList: struct { items: Vec },
|
PatternList: struct { items: Vec },
|
||||||
PatternAggregate: struct { items: Vec },
|
PatternAggregate: struct { items: Vec },
|
||||||
PatternField: struct { name: ByteView, pattern: usize },
|
PatternField: struct { name: ByteView, pattern: usize },
|
||||||
|
PatternRef: struct { pat: usize },
|
||||||
|
ExprComptime: struct { body: usize },
|
||||||
|
ExprComptimeInit: struct { name: ByteView, args: Vec, fields: Vec },
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kind_name(body: Body): []u8 {
|
fn kind_name(body: Body): []u8 {
|
||||||
@@ -142,7 +145,9 @@ fn kind_name(body: Body): []u8 {
|
|||||||
.StatementBreak(_) => { ret "statement_break"; },
|
.StatementBreak(_) => { ret "statement_break"; },
|
||||||
.StatementContinue(_) => { ret "statement_continue"; },
|
.StatementContinue(_) => { ret "statement_continue"; },
|
||||||
.StatementDefer(_) => { ret "statement_defer"; },
|
.StatementDefer(_) => { ret "statement_defer"; },
|
||||||
|
.StatementErrdefer(_) => { ret "statement_errdefer"; },
|
||||||
.StatementComptime(_) => { ret "statement_comptime"; },
|
.StatementComptime(_) => { ret "statement_comptime"; },
|
||||||
|
.ExprComptime(_) => { ret "expr_comptime"; },
|
||||||
.StatementDecl(_) => { ret "statement_decl"; },
|
.StatementDecl(_) => { ret "statement_decl"; },
|
||||||
.StatementExpr(_) => { ret "statement_expr"; },
|
.StatementExpr(_) => { ret "statement_expr"; },
|
||||||
.AggregateMember(_) => { ret "aggregate_member"; },
|
.AggregateMember(_) => { ret "aggregate_member"; },
|
||||||
@@ -168,6 +173,9 @@ fn kind_name(body: Body): []u8 {
|
|||||||
.ExprArrayLit(_) => { ret "array_literal"; },
|
.ExprArrayLit(_) => { ret "array_literal"; },
|
||||||
.ExprAggregateLit(_) => { ret "aggregate_literal"; },
|
.ExprAggregateLit(_) => { ret "aggregate_literal"; },
|
||||||
.ExprComptimeCall(_) => { ret "comptime_call"; },
|
.ExprComptimeCall(_) => { ret "comptime_call"; },
|
||||||
|
.ExprTry(_) => { ret "try_expr"; },
|
||||||
|
.ExprCatch(_) => { ret "catch_expr"; },
|
||||||
|
.ExprComptimeInit(_) => { ret "comptime_init"; },
|
||||||
.ExprBlock(_) => { ret "block"; },
|
.ExprBlock(_) => { ret "block"; },
|
||||||
.ExprIf(_) => { ret "if_expr"; },
|
.ExprIf(_) => { ret "if_expr"; },
|
||||||
.ExprIfvar(_) => { ret "ifvar_expr"; },
|
.ExprIfvar(_) => { ret "ifvar_expr"; },
|
||||||
@@ -191,6 +199,7 @@ fn kind_name(body: Body): []u8 {
|
|||||||
.PatternList(_) => { ret "pattern_list"; },
|
.PatternList(_) => { ret "pattern_list"; },
|
||||||
.PatternAggregate(_) => { ret "pattern_aggregate"; },
|
.PatternAggregate(_) => { ret "pattern_aggregate"; },
|
||||||
.PatternField(_) => { ret "pattern_field"; },
|
.PatternField(_) => { ret "pattern_field"; },
|
||||||
|
.PatternRef(_) => { ret "pattern_ref"; },
|
||||||
}
|
}
|
||||||
ret "?";
|
ret "?";
|
||||||
}
|
}
|
||||||
@@ -209,6 +218,7 @@ type Ast = @self;
|
|||||||
nodes: Vec, // Node
|
nodes: Vec, // Node
|
||||||
root: usize,
|
root: usize,
|
||||||
sources: *Sources, // 借用;可为 null(dump 时不解析行列)
|
sources: *Sources, // 借用;可为 null(dump 时不解析行列)
|
||||||
|
strings: Vec, // ByteBuffer:已解码的字符串字面量字节(ByteView 指向其中)
|
||||||
|
|
||||||
type AstResult = enum {
|
type AstResult = enum {
|
||||||
Ok: Ast,
|
Ok: Ast,
|
||||||
@@ -231,6 +241,7 @@ fn new(sources: *Sources): AstResult {
|
|||||||
.nodes = Vec.blank(@sizeof(Node), @alignof(Node)),
|
.nodes = Vec.blank(@sizeof(Node), @alignof(Node)),
|
||||||
.root = 0,
|
.root = 0,
|
||||||
.sources = sources,
|
.sources = sources,
|
||||||
|
.strings = Vec.blank(@sizeof(ByteBuffer), @alignof(ByteBuffer)),
|
||||||
};
|
};
|
||||||
var sentinel = new_node(.None, span_none());
|
var sentinel = new_node(.None, span_none());
|
||||||
match Vec.push(&ast.nodes, &sentinel as *u8) {
|
match Vec.push(&ast.nodes, &sentinel as *u8) {
|
||||||
@@ -257,15 +268,21 @@ fn drop(self: *Ast) {
|
|||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
Vec.drop(&self.nodes);
|
Vec.drop(&self.nodes);
|
||||||
|
var k: usize = 0;
|
||||||
|
while k < self.strings.len {
|
||||||
|
match Vec.get(&self.strings, k) {
|
||||||
|
.Ok(ptr) => { ByteBuffer.drop(ptr as *ByteBuffer); },
|
||||||
|
.Err(error) => {},
|
||||||
|
}
|
||||||
|
k = k + 1;
|
||||||
|
}
|
||||||
|
Vec.drop(&self.strings);
|
||||||
self.root = 0;
|
self.root = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node(self: *Ast, ref: usize): *Node {
|
fn node(self: *Ast, ref: usize): *Node {
|
||||||
match Vec.get(&self.nodes, ref) {
|
if ref >= self.nodes.len { ret null; }
|
||||||
.Ok(ptr) => { ret ptr as *Node; },
|
ret (self.nodes.data as *Node) + ref;
|
||||||
.Err(error) => { ret null; },
|
|
||||||
}
|
|
||||||
ret null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 追加节点,返回其引用(失败返回 0)。
|
// 追加节点,返回其引用(失败返回 0)。
|
||||||
@@ -281,6 +298,26 @@ fn alloc(self: *Ast, body: Body, span: Span): usize {
|
|||||||
ret self.nodes.len - 1;
|
ret self.nodes.len - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 注册一段已解码的字符串字节(Ast 拥有;ByteView 指向其中,生命周期同 Ast)。
|
||||||
|
// 空字符串也保证非空 data 指针(空字面量仍有稳定非空地址)。
|
||||||
|
fn push_string(self: *Ast, bytes: ByteBuffer): ByteView {
|
||||||
|
var b = bytes;
|
||||||
|
if b.storage.len == 0 {
|
||||||
|
ByteBuffer.reserve(&b, 1);
|
||||||
|
}
|
||||||
|
@assert(b.storage.data != null, "ast string pool must be non-null");
|
||||||
|
// 直接构造,避免 from_slice 把空串规范成 null 视图。
|
||||||
|
var view = ByteView { .ptr = b.storage.data, .len = b.storage.len };
|
||||||
|
match Vec.push(&self.strings, &b as *u8) {
|
||||||
|
.Ok => { ret view; },
|
||||||
|
.Err(error) => {
|
||||||
|
ByteBuffer.drop(&b);
|
||||||
|
ret ByteView.empty();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ret ByteView.empty();
|
||||||
|
}
|
||||||
|
|
||||||
fn count(self: *Ast): usize {
|
fn count(self: *Ast): usize {
|
||||||
ret self.nodes.len;
|
ret self.nodes.len;
|
||||||
}
|
}
|
||||||
@@ -289,7 +326,6 @@ fn ref_vec(): Vec {
|
|||||||
ret Vec.blank(@sizeof(usize), @alignof(usize));
|
ret Vec.blank(@sizeof(usize), @alignof(usize));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- dump(镜像 stage1 spl_ast_dump)----
|
|
||||||
//
|
//
|
||||||
// AST:
|
// AST:
|
||||||
// <indent>`-/|-kind #ref [line:col]
|
// <indent>`-/|-kind #ref [line:col]
|
||||||
@@ -434,7 +470,9 @@ fn dump_node(ast: *Ast, buf: *ByteBuffer, stack: *Vec, ref: usize, last: bool):
|
|||||||
},
|
},
|
||||||
.StatementContinue(x) => { ok = dump_opt(ast, buf, stack, x.label, true); },
|
.StatementContinue(x) => { ok = dump_opt(ast, buf, stack, x.label, true); },
|
||||||
.StatementDefer(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
.StatementDefer(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
||||||
|
.StatementErrdefer(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
||||||
.StatementComptime(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
.StatementComptime(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
||||||
|
.ExprComptime(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
||||||
.StatementDecl(d) => { ok = dump_opt(ast, buf, stack, d.decl, true); },
|
.StatementDecl(d) => { ok = dump_opt(ast, buf, stack, d.decl, true); },
|
||||||
.StatementExpr(e) => { ok = dump_opt(ast, buf, stack, e.expr, true); },
|
.StatementExpr(e) => { ok = dump_opt(ast, buf, stack, e.expr, true); },
|
||||||
.AggregateMember(d) => {
|
.AggregateMember(d) => {
|
||||||
@@ -486,6 +524,13 @@ fn dump_node(ast: *Ast, buf: *ByteBuffer, stack: *Vec, ref: usize, last: bool):
|
|||||||
},
|
},
|
||||||
.ExprAggregateLit(a) => { ok = dump_list(ast, buf, stack, a.fields); },
|
.ExprAggregateLit(a) => { ok = dump_list(ast, buf, stack, a.fields); },
|
||||||
.ExprComptimeCall(c) => { ok = dump_list(ast, buf, stack, c.args); },
|
.ExprComptimeCall(c) => { ok = dump_list(ast, buf, stack, c.args); },
|
||||||
|
.ExprTry(t2) => { ok = dump_opt(ast, buf, stack, t2.value, true); },
|
||||||
|
.ExprCatch(c2) => {
|
||||||
|
ok = dump_opt(ast, buf, stack, c2.value, false) &&
|
||||||
|
dump_opt(ast, buf, stack, c2.pattern, false) &&
|
||||||
|
dump_opt(ast, buf, stack, c2.handler, true);
|
||||||
|
},
|
||||||
|
.ExprComptimeInit(x) => { ok = dump_list(ast, buf, stack, x.fields); },
|
||||||
.ExprBlock(b) => {
|
.ExprBlock(b) => {
|
||||||
ok = dump_list(ast, buf, stack, b.stmts) &&
|
ok = dump_list(ast, buf, stack, b.stmts) &&
|
||||||
dump_opt(ast, buf, stack, b.tail_expr, true);
|
dump_opt(ast, buf, stack, b.tail_expr, true);
|
||||||
@@ -543,6 +588,7 @@ fn dump_node(ast: *Ast, buf: *ByteBuffer, stack: *Vec, ref: usize, last: bool):
|
|||||||
.PatternList(v) => { ok = dump_list(ast, buf, stack, v.items); },
|
.PatternList(v) => { ok = dump_list(ast, buf, stack, v.items); },
|
||||||
.PatternAggregate(v) => { ok = dump_list(ast, buf, stack, v.items); },
|
.PatternAggregate(v) => { ok = dump_list(ast, buf, stack, v.items); },
|
||||||
.PatternField(p) => { ok = dump_opt(ast, buf, stack, p.pattern, true); },
|
.PatternField(p) => { ok = dump_opt(ast, buf, stack, p.pattern, true); },
|
||||||
|
.PatternRef(p) => { ok = dump_opt(ast, buf, stack, p.pat, true); },
|
||||||
}
|
}
|
||||||
|
|
||||||
stack.len = stack.len - 1;
|
stack.len = stack.len - 1;
|
||||||
@@ -557,7 +603,6 @@ fn dump(ast: *Ast, out: *ByteBuffer): bool {
|
|||||||
ret dump_node(ast, out, &stack, ast.root, true);
|
ret dump_node(ast, out, &stack, ast.root, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn test_kind_names() {
|
fn test_kind_names() {
|
||||||
@assert(ByteView.eq(ByteView.from_slice(kind_name(.None)), ByteView.from_slice("none")),
|
@assert(ByteView.eq(ByteView.from_slice(kind_name(.None)), ByteView.from_slice("none")),
|
||||||
|
|||||||
+2312
File diff suppressed because it is too large.
Load diff
+417
@@ -0,0 +1,417 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
// 属性注册表:内置属性在 register_builtins 登记,按 (ns, name) 查找。新增属性只加
|
||||||
|
// 注册项,不改 sema 逻辑(SPL.md 的 @/# 扩展点)。
|
||||||
|
|
||||||
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||||
|
type .{ .ByteBuffer } = @import("utils/byte_buffer.spl");
|
||||||
|
type .{ .append_usize, .append_i64 } = @import("utils/fmt.spl");
|
||||||
|
|
||||||
|
type AttrStage = enum {
|
||||||
|
Sema,
|
||||||
|
Layout,
|
||||||
|
Ir,
|
||||||
|
Opt,
|
||||||
|
Backend,
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttrTarget = enum {
|
||||||
|
Decl,
|
||||||
|
Type,
|
||||||
|
Expr,
|
||||||
|
Stmt,
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttrDemand = enum {
|
||||||
|
Optional,
|
||||||
|
Required,
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttrInfo = struct {
|
||||||
|
ns: ByteView,
|
||||||
|
name: ByteView,
|
||||||
|
stage: AttrStage,
|
||||||
|
target: AttrTarget,
|
||||||
|
demand: AttrDemand,
|
||||||
|
min_args: i32,
|
||||||
|
max_args: i32, // -1 = variadic
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttrArgKind = enum {
|
||||||
|
Int,
|
||||||
|
Str,
|
||||||
|
Def,
|
||||||
|
Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttrArg = enum {
|
||||||
|
Int: i64,
|
||||||
|
Str: ByteView,
|
||||||
|
Def: usize,
|
||||||
|
Type: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttrNode = struct {
|
||||||
|
info_index: usize, // 注册表下标;0 = 无
|
||||||
|
args: Vec, // AttrArg
|
||||||
|
consumed_mask: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Attr = @self;
|
||||||
|
|
||||||
|
table: Vec, // AttrNode
|
||||||
|
registry: Vec, // AttrInfo
|
||||||
|
|
||||||
|
type AttrResult = enum {
|
||||||
|
Ok: Attr,
|
||||||
|
Err: Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn empty_info(): AttrInfo {
|
||||||
|
ret AttrInfo {
|
||||||
|
.ns = ByteView.empty(),
|
||||||
|
.name = ByteView.empty(),
|
||||||
|
.stage = .Sema,
|
||||||
|
.target = .Decl,
|
||||||
|
.demand = .Optional,
|
||||||
|
.min_args = 0,
|
||||||
|
.max_args = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_node(): AttrNode {
|
||||||
|
ret AttrNode {
|
||||||
|
.info_index = 0,
|
||||||
|
.args = Vec.blank(@sizeof(AttrArg), @alignof(AttrArg)),
|
||||||
|
.consumed_mask = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(): AttrResult {
|
||||||
|
var at = Attr {
|
||||||
|
.table = Vec.blank(@sizeof(AttrNode), @alignof(AttrNode)),
|
||||||
|
.registry = Vec.blank(@sizeof(AttrInfo), @alignof(AttrInfo)),
|
||||||
|
};
|
||||||
|
var sentinel = new_node();
|
||||||
|
match Vec.push(&at.table, &sentinel as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => {
|
||||||
|
Vec.drop(&at.table);
|
||||||
|
Vec.drop(&at.registry);
|
||||||
|
ret .Err(error);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var reg_sentinel = empty_info();
|
||||||
|
match Vec.push(&at.registry, ®_sentinel as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => {
|
||||||
|
Vec.drop(&at.table);
|
||||||
|
Vec.drop(&at.registry);
|
||||||
|
ret .Err(error);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
register_builtins(&at);
|
||||||
|
ret .Ok(at);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drop(self: *Attr) {
|
||||||
|
var i: usize = 1;
|
||||||
|
while i < self.table.len {
|
||||||
|
var a = node(self, i);
|
||||||
|
if a != null {
|
||||||
|
Vec.drop(&a.args);
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
Vec.drop(&self.table);
|
||||||
|
Vec.drop(&self.registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node(self: *Attr, id: usize): *AttrNode {
|
||||||
|
if id == 0 || id >= self.table.len { ret null; }
|
||||||
|
ret (self.table.data as *AttrNode) + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn info_at(self: *Attr, index: usize): *AttrInfo {
|
||||||
|
if index == 0 || index >= self.registry.len { ret null; }
|
||||||
|
match Vec.get(&self.registry, index) {
|
||||||
|
.Ok(ptr) => { ret ptr as *AttrInfo; },
|
||||||
|
.Err(error) => { ret null; },
|
||||||
|
}
|
||||||
|
ret null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node_info(self: *Attr, id: usize): *AttrInfo {
|
||||||
|
var a = node(self, id);
|
||||||
|
if a == null { ret null; }
|
||||||
|
ret info_at(self, a.info_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 追加属性节点(args 所有权转移)。
|
||||||
|
fn alloc(self: *Attr, info_index: usize, args: Vec): usize {
|
||||||
|
var a = new_node();
|
||||||
|
a.info_index = info_index;
|
||||||
|
a.args = args;
|
||||||
|
match Vec.push(&self.table, &a as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ret 0; },
|
||||||
|
}
|
||||||
|
ret self.table.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn registry_add(self: *Attr, info: AttrInfo) {
|
||||||
|
Vec.push(&self.registry, &info as *u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回注册表下标(>=1)或 0。
|
||||||
|
fn lookup(self: *Attr, ns: ByteView, name: ByteView): usize {
|
||||||
|
var i: usize = 1;
|
||||||
|
while i < self.registry.len {
|
||||||
|
var info = info_at(self, i);
|
||||||
|
if info != null && ByteView.eq(info.ns, ns) && ByteView.eq(info.name, name) {
|
||||||
|
ret i;
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_info(ns: []u8, name: []u8, stage: AttrStage, target: AttrTarget, demand: AttrDemand,
|
||||||
|
min_args: i32, max_args: i32): AttrInfo {
|
||||||
|
ret AttrInfo {
|
||||||
|
.ns = ByteView.from_slice(ns),
|
||||||
|
.name = ByteView.from_slice(name),
|
||||||
|
.stage = stage,
|
||||||
|
.target = target,
|
||||||
|
.demand = demand,
|
||||||
|
.min_args = min_args,
|
||||||
|
.max_args = max_args,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_builtins(self: *Attr) {
|
||||||
|
registry_add(self, make_info("", "extern", .Ir, .Decl, .Required, 1, 1));
|
||||||
|
registry_add(self, make_info("", "inline", .Opt, .Decl, .Optional, 0, 0));
|
||||||
|
registry_add(self, make_info("", "noinline", .Opt, .Decl, .Optional, 0, 0));
|
||||||
|
registry_add(self, make_info("", "align", .Layout, .Type, .Required, 1, 1));
|
||||||
|
registry_add(self, make_info("", "volatile", .Ir, .Expr, .Required, 0, 0));
|
||||||
|
registry_add(self, make_info("", "atomic", .Ir, .Expr, .Required, 0, 1));
|
||||||
|
registry_add(self, make_info("", "export", .Ir, .Decl, .Optional, 1, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn push_out(buf: *ByteBuffer, bytes: []u8): bool {
|
||||||
|
match ByteBuffer.append(buf, bytes) {
|
||||||
|
.Ok => { ret true; },
|
||||||
|
.Err(error) => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_usize(buf: *ByteBuffer, value: usize): bool {
|
||||||
|
match append_usize(buf, value, 10, false) {
|
||||||
|
.Ok => { ret true; },
|
||||||
|
.Err(error) => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_hex(buf: *ByteBuffer, value: u32): bool {
|
||||||
|
if !push_out(buf, "0x") { ret false; }
|
||||||
|
match append_usize(buf, value as usize, 16, false) {
|
||||||
|
.Ok => { ret true; },
|
||||||
|
.Err(error) => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stage_name(s: AttrStage): []u8 {
|
||||||
|
match s {
|
||||||
|
.Sema => { ret "SEMA"; },
|
||||||
|
.Layout => { ret "LAYOUT"; },
|
||||||
|
.Ir => { ret "IR"; },
|
||||||
|
.Opt => { ret "OPT"; },
|
||||||
|
.Backend => { ret "BACKEND"; },
|
||||||
|
}
|
||||||
|
ret "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target_name(t: AttrTarget): []u8 {
|
||||||
|
match t {
|
||||||
|
.Decl => { ret "DECL"; },
|
||||||
|
.Type => { ret "TYPE"; },
|
||||||
|
.Expr => { ret "EXPR"; },
|
||||||
|
.Stmt => { ret "STMT"; },
|
||||||
|
}
|
||||||
|
ret "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
fn demand_name(d: AttrDemand): []u8 {
|
||||||
|
match d {
|
||||||
|
.Required => { ret "REQUIRED"; },
|
||||||
|
.Optional => { ret "OPTIONAL"; },
|
||||||
|
}
|
||||||
|
ret "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_id(self: *Attr, id: usize, out: *ByteBuffer): bool {
|
||||||
|
if id == 0 {
|
||||||
|
ret push_out(out, "?");
|
||||||
|
}
|
||||||
|
var a = node(self, id);
|
||||||
|
if a == null { ret push_out(out, "?"); }
|
||||||
|
var info = info_at(self, a.info_index);
|
||||||
|
if info == null { ret push_out(out, "?"); }
|
||||||
|
|
||||||
|
if !push_out(out, ByteView.as_slice(info.name)) { ret false; }
|
||||||
|
if a.args.len == 0 { ret true; }
|
||||||
|
if !push_out(out, "(") { ret false; }
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < a.args.len {
|
||||||
|
if i > 0 && !push_out(out, ", ") { ret false; }
|
||||||
|
var arg = arg_at(&a.args, i);
|
||||||
|
if arg == null { ret false; }
|
||||||
|
match arg.* {
|
||||||
|
.Int(v) => {
|
||||||
|
match append_i64(out, v, 10, false) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ret false; },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Str(v) => {
|
||||||
|
if !push_out(out, "\"") { ret false; }
|
||||||
|
if !push_out(out, ByteView.as_slice(v)) { ret false; }
|
||||||
|
if !push_out(out, "\"") { ret false; }
|
||||||
|
},
|
||||||
|
.Def(v) => {
|
||||||
|
if !push_out(out, "def#") { ret false; }
|
||||||
|
if !push_usize(out, v) { ret false; }
|
||||||
|
},
|
||||||
|
.Type(v) => {
|
||||||
|
if !push_out(out, "type#") { ret false; }
|
||||||
|
if !push_usize(out, v) { ret false; }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret push_out(out, ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arg_at(v: *Vec, i: usize): *AttrArg {
|
||||||
|
match Vec.get(v, i) {
|
||||||
|
.Ok(ptr) => { ret ptr as *AttrArg; },
|
||||||
|
.Err(error) => { ret null; },
|
||||||
|
}
|
||||||
|
ret null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump(self: *Attr, out: *ByteBuffer): bool {
|
||||||
|
if !push_out(out, "AttrTable:\n") { ret false; }
|
||||||
|
var i: usize = 1;
|
||||||
|
while i < self.table.len {
|
||||||
|
var a = node(self, i);
|
||||||
|
if !push_out(out, " #") { ret false; }
|
||||||
|
if !push_usize(out, i) { ret false; }
|
||||||
|
if !push_out(out, " ") { ret false; }
|
||||||
|
if !dump_id(self, i, out) { ret false; }
|
||||||
|
if a != null {
|
||||||
|
var info = info_at(self, a.info_index);
|
||||||
|
if info != null {
|
||||||
|
if !push_out(out, " [stage=") { ret false; }
|
||||||
|
if !push_out(out, stage_name(info.stage)) { ret false; }
|
||||||
|
if !push_out(out, " target=") { ret false; }
|
||||||
|
if !push_out(out, target_name(info.target)) { ret false; }
|
||||||
|
if !push_out(out, " demand=") { ret false; }
|
||||||
|
if !push_out(out, demand_name(info.demand)) { ret false; }
|
||||||
|
if !push_out(out, " consumed=") { ret false; }
|
||||||
|
if !push_hex(out, a.consumed_mask) { ret false; }
|
||||||
|
if !push_out(out, "]") { ret false; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !push_out(out, "\n") { ret false; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn expect_text(buf: ByteBuffer, expected: []u8) {
|
||||||
|
var got = ByteBuffer.as_slice(buf);
|
||||||
|
@assert(got.len == expected.len, "dump length");
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < expected.len {
|
||||||
|
@assert(got.ptr[i] == expected.ptr[i], "dump byte");
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_registry() {
|
||||||
|
var at: Attr = undefined;
|
||||||
|
match new() {
|
||||||
|
.Ok(v) => { at = v; },
|
||||||
|
.Err(error) => { @assert(false, "attr new failed"); },
|
||||||
|
}
|
||||||
|
defer { drop(&at); }
|
||||||
|
|
||||||
|
@assert(at.registry.len == 8, "1 sentinel + 7 builtins");
|
||||||
|
|
||||||
|
var i = lookup(&at, ByteView.empty(), ByteView.from_slice("extern"));
|
||||||
|
@assert(i != 0, "extern found");
|
||||||
|
var info = info_at(&at, i);
|
||||||
|
@assert(info != null, "extern info");
|
||||||
|
@assert(info.min_args == 1 && info.max_args == 1, "extern arity");
|
||||||
|
|
||||||
|
var j = lookup(&at, ByteView.empty(), ByteView.from_slice("atomic"));
|
||||||
|
@assert(j != 0, "atomic found");
|
||||||
|
var jinfo = info_at(&at, j);
|
||||||
|
@assert(jinfo.min_args == 0 && jinfo.max_args == 1, "atomic arity");
|
||||||
|
|
||||||
|
@assert(lookup(&at, ByteView.empty(), ByteView.from_slice("nope")) == 0, "unknown attr");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_alloc_and_dump_id() {
|
||||||
|
var at: Attr = undefined;
|
||||||
|
match new() {
|
||||||
|
.Ok(v) => { at = v; },
|
||||||
|
.Err(error) => { @assert(false, "attr new failed"); },
|
||||||
|
}
|
||||||
|
defer { drop(&at); }
|
||||||
|
|
||||||
|
var idx = lookup(&at, ByteView.empty(), ByteView.from_slice("extern"));
|
||||||
|
var args = Vec.blank(@sizeof(AttrArg), @alignof(AttrArg));
|
||||||
|
var s: AttrArg = .Str(ByteView.from_slice("vm"));
|
||||||
|
match Vec.push(&args, &s as *u8) { .Ok => {}, .Err(e) => { @assert(false, "push str"); } }
|
||||||
|
var n: AttrArg = .Int(3);
|
||||||
|
match Vec.push(&args, &n as *u8) { .Ok => {}, .Err(e) => { @assert(false, "push int"); } }
|
||||||
|
var d: AttrArg = .Def(9);
|
||||||
|
match Vec.push(&args, &d as *u8) { .Ok => {}, .Err(e) => { @assert(false, "push def"); } }
|
||||||
|
|
||||||
|
var id = alloc(&at, idx, args);
|
||||||
|
@assert(id == 1, "first attr id");
|
||||||
|
|
||||||
|
var out = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&out); }
|
||||||
|
if !dump_id(&at, id, &out) { @assert(false, "dump_id ok"); }
|
||||||
|
expect_text(out, "extern(\"vm\", 3, def#9)");
|
||||||
|
|
||||||
|
var out2 = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&out2); }
|
||||||
|
if !dump_id(&at, 0, &out2) { @assert(false, "dump_id none ok"); }
|
||||||
|
expect_text(out2, "?");
|
||||||
|
|
||||||
|
var out3 = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&out3); }
|
||||||
|
var l = lookup(&at, ByteView.empty(), ByteView.from_slice("inline"));
|
||||||
|
var id2 = alloc(&at, l, Vec.blank(@sizeof(AttrArg), @alignof(AttrArg)));
|
||||||
|
if !dump(&at, &out3) { @assert(false, "dump ok"); }
|
||||||
|
// 只校验首尾关键片段,避免依赖完整表格排版
|
||||||
|
var got = ByteBuffer.as_slice(out3);
|
||||||
|
@assert(got.len > 0, "dump nonempty");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_registry();
|
||||||
|
test_alloc_and_dump_id();
|
||||||
|
}
|
||||||
+304
@@ -0,0 +1,304 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
// 声明句柄表。DefId 是声明的稳定身份:作用域符号、节点 resolved_def_id、comptime
|
||||||
|
// 生成的声明都用它;前向/环形引用由 state 状态机表达。owner_ast 为不透明 *any。
|
||||||
|
|
||||||
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||||
|
type .{ .ByteBuffer } = @import("utils/byte_buffer.spl");
|
||||||
|
type .{ .append_usize } = @import("utils/fmt.spl");
|
||||||
|
type .{ .Span, .span_none } = @import("./diag.spl");
|
||||||
|
|
||||||
|
type DefKind = enum {
|
||||||
|
Error,
|
||||||
|
Scalar,
|
||||||
|
Member,
|
||||||
|
Var,
|
||||||
|
Fn,
|
||||||
|
Agg,
|
||||||
|
Distinct,
|
||||||
|
Alias,
|
||||||
|
Builtin,
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeclState = enum {
|
||||||
|
Unresolved,
|
||||||
|
Resolving,
|
||||||
|
Resolved,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefNode = struct {
|
||||||
|
kind: DefKind,
|
||||||
|
name: ByteView,
|
||||||
|
span: Span,
|
||||||
|
lang_id: usize,
|
||||||
|
owner: *any, // 借用;不透明,sema 侧 cast 回 *Ast
|
||||||
|
ast_node_ref: usize,
|
||||||
|
state: DeclState,
|
||||||
|
owner_def: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefTable = @self;
|
||||||
|
|
||||||
|
table: Vec, // DefNode
|
||||||
|
|
||||||
|
type DefTableResult = enum {
|
||||||
|
Ok: DefTable,
|
||||||
|
Err: Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_node(): DefNode {
|
||||||
|
ret DefNode {
|
||||||
|
.kind = .Error,
|
||||||
|
.name = ByteView.empty(),
|
||||||
|
.span = span_none(),
|
||||||
|
.lang_id = 0,
|
||||||
|
.owner = null,
|
||||||
|
.ast_node_ref = 0,
|
||||||
|
.state = .Unresolved,
|
||||||
|
.owner_def = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(): DefTableResult {
|
||||||
|
var dt = DefTable { .table = Vec.blank(@sizeof(DefNode), @alignof(DefNode)) };
|
||||||
|
var sentinel = new_node();
|
||||||
|
match Vec.push(&dt.table, &sentinel as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => {
|
||||||
|
Vec.drop(&dt.table);
|
||||||
|
ret .Err(error);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ret .Ok(dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drop(self: *DefTable) {
|
||||||
|
Vec.drop(&self.table);
|
||||||
|
}
|
||||||
|
|
||||||
|
// id 0 与越界均返回 null。
|
||||||
|
fn node(self: *DefTable, id: usize): *DefNode {
|
||||||
|
if id == 0 || id >= self.table.len { ret null; }
|
||||||
|
ret (self.table.data as *DefNode) + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn count(self: *DefTable): usize {
|
||||||
|
ret self.table.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 追加一条声明,返回其 DefId(失败返回 0)。
|
||||||
|
fn alloc(self: *DefTable, kind: DefKind, name: ByteView, span: Span, owner: *any,
|
||||||
|
ast_node_ref: usize): usize {
|
||||||
|
var n = new_node();
|
||||||
|
n.kind = kind;
|
||||||
|
n.name = name;
|
||||||
|
n.span = span;
|
||||||
|
n.owner = owner;
|
||||||
|
n.ast_node_ref = ast_node_ref;
|
||||||
|
match Vec.push(&self.table, &n as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ret 0; },
|
||||||
|
}
|
||||||
|
ret self.table.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve(self: *DefTable, id: usize, lang_id: usize) {
|
||||||
|
var d = node(self, id);
|
||||||
|
if d == null { ret; }
|
||||||
|
d.lang_id = lang_id;
|
||||||
|
d.state = .Resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_state(self: *DefTable, id: usize, state: DeclState) {
|
||||||
|
var d = node(self, id);
|
||||||
|
if d == null { ret; }
|
||||||
|
d.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_lang_id(self: *DefTable, id: usize): usize {
|
||||||
|
var d = node(self, id);
|
||||||
|
if d == null { ret 0; }
|
||||||
|
ret d.lang_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn kind_name(kind: DefKind): []u8 {
|
||||||
|
match kind {
|
||||||
|
.Error => { ret "error"; },
|
||||||
|
.Scalar => { ret "scalar"; },
|
||||||
|
.Member => { ret "member"; },
|
||||||
|
.Var => { ret "var"; },
|
||||||
|
.Fn => { ret "fn"; },
|
||||||
|
.Agg => { ret "agg"; },
|
||||||
|
.Distinct => { ret "distinct"; },
|
||||||
|
.Alias => { ret "alias"; },
|
||||||
|
.Builtin => { ret "builtin"; },
|
||||||
|
}
|
||||||
|
ret "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state_name(state: DeclState): []u8 {
|
||||||
|
match state {
|
||||||
|
.Unresolved => { ret "unresolved"; },
|
||||||
|
.Resolving => { ret "resolving"; },
|
||||||
|
.Resolved => { ret "resolved"; },
|
||||||
|
.Error => { ret "error"; },
|
||||||
|
}
|
||||||
|
ret "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 能作为类型使用的 def。
|
||||||
|
fn is_type_kind(kind: DefKind): bool {
|
||||||
|
match kind {
|
||||||
|
.Scalar => { ret true; },
|
||||||
|
.Agg => { ret true; },
|
||||||
|
.Distinct => { ret true; },
|
||||||
|
.Alias => { ret true; },
|
||||||
|
.Builtin => { ret true; },
|
||||||
|
_ => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn push_out(buf: *ByteBuffer, bytes: []u8): bool {
|
||||||
|
match ByteBuffer.append(buf, bytes) {
|
||||||
|
.Ok => { ret true; },
|
||||||
|
.Err(error) => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_usize(buf: *ByteBuffer, value: usize): bool {
|
||||||
|
match append_usize(buf, value, 10, false) {
|
||||||
|
.Ok => { ret true; },
|
||||||
|
.Err(error) => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump(self: *DefTable, id: usize, out: *ByteBuffer): bool {
|
||||||
|
var d = node(self, id);
|
||||||
|
if d == null {
|
||||||
|
ret push_out(out, "none");
|
||||||
|
}
|
||||||
|
if !push_out(out, "kind=") { ret false; }
|
||||||
|
if !push_out(out, kind_name(d.kind)) { ret false; }
|
||||||
|
if !push_out(out, " lang_id=") { ret false; }
|
||||||
|
if !push_usize(out, d.lang_id) { ret false; }
|
||||||
|
if !push_out(out, " name=") { ret false; }
|
||||||
|
if d.name.len == 0 {
|
||||||
|
ret push_out(out, "?");
|
||||||
|
}
|
||||||
|
ret push_out(out, ByteView.as_slice(d.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn test_alloc_and_state() {
|
||||||
|
var dt: DefTable = undefined;
|
||||||
|
match new() {
|
||||||
|
.Ok(v) => { dt = v; },
|
||||||
|
.Err(error) => { @assert(false, "def table new failed"); },
|
||||||
|
}
|
||||||
|
defer { drop(&dt); }
|
||||||
|
|
||||||
|
@assert(count(&dt) == 1, "sentinel only");
|
||||||
|
@assert(node(&dt, 0) == null, "id 0 is null");
|
||||||
|
|
||||||
|
var span = span_none();
|
||||||
|
var a = alloc(&dt, .Fn, ByteView.from_slice("foo"), span, null, 7);
|
||||||
|
var b = alloc(&dt, .Agg, ByteView.from_slice("Bar"), span, null, 9);
|
||||||
|
@assert(a == 1 && b == 2, "ids increase from 1");
|
||||||
|
|
||||||
|
var na = node(&dt, a);
|
||||||
|
@assert(na != null, "node a");
|
||||||
|
@assert(ByteView.eq(na.name, ByteView.from_slice("foo")), "name stored");
|
||||||
|
@assert(na.ast_node_ref == 7, "ast_node_ref stored");
|
||||||
|
@assert(na.owner == null, "owner stored");
|
||||||
|
match na.state {
|
||||||
|
.Unresolved => {},
|
||||||
|
_ => { @assert(false, "initial state unresolved"); },
|
||||||
|
}
|
||||||
|
match na.kind {
|
||||||
|
.Fn => {},
|
||||||
|
_ => { @assert(false, "kind fn"); },
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(&dt, a, 42);
|
||||||
|
@assert(get_lang_id(&dt, a) == 42, "lang id resolved");
|
||||||
|
var na2 = node(&dt, a);
|
||||||
|
match na2.state {
|
||||||
|
.Resolved => {},
|
||||||
|
_ => { @assert(false, "state resolved"); },
|
||||||
|
}
|
||||||
|
|
||||||
|
set_state(&dt, b, .Resolving);
|
||||||
|
var nb = node(&dt, b);
|
||||||
|
match nb.state {
|
||||||
|
.Resolving => {},
|
||||||
|
_ => { @assert(false, "state resolving"); },
|
||||||
|
}
|
||||||
|
|
||||||
|
@assert(get_lang_id(&dt, 999) == 0, "missing id");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_kind_helpers() {
|
||||||
|
@assert(is_type_kind(.Scalar), "scalar is type");
|
||||||
|
@assert(is_type_kind(.Agg), "agg is type");
|
||||||
|
@assert(is_type_kind(.Alias), "alias is type");
|
||||||
|
@assert(!is_type_kind(.Fn), "fn not type");
|
||||||
|
@assert(!is_type_kind(.Var), "var not type");
|
||||||
|
@assert(!is_type_kind(.Member), "member not type");
|
||||||
|
|
||||||
|
@assert(ByteView.eq(ByteView.from_slice(kind_name(.Agg)), ByteView.from_slice("agg")), "agg name");
|
||||||
|
@assert(ByteView.eq(ByteView.from_slice(state_name(.Resolving)),
|
||||||
|
ByteView.from_slice("resolving")),
|
||||||
|
"resolving name");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_dump() {
|
||||||
|
var dt: DefTable = undefined;
|
||||||
|
match new() {
|
||||||
|
.Ok(v) => { dt = v; },
|
||||||
|
.Err(error) => { @assert(false, "def table new failed"); },
|
||||||
|
}
|
||||||
|
defer { drop(&dt); }
|
||||||
|
|
||||||
|
var a = alloc(&dt, .Var, ByteView.from_slice("x"), span_none(), null, 0);
|
||||||
|
resolve(&dt, a, 5);
|
||||||
|
|
||||||
|
var out = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&out); }
|
||||||
|
if !dump(&dt, a, &out) { @assert(false, "dump ok"); }
|
||||||
|
expect_text(out, "kind=var lang_id=5 name=x");
|
||||||
|
|
||||||
|
var out2 = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&out2); }
|
||||||
|
if !dump(&dt, 0, &out2) { @assert(false, "dump none ok"); }
|
||||||
|
expect_text(out2, "none");
|
||||||
|
|
||||||
|
var out3 = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&out3); }
|
||||||
|
var c = alloc(&dt, .Alias, ByteView.empty(), span_none(), null, 0);
|
||||||
|
if !dump(&dt, c, &out3) { @assert(false, "dump empty name ok"); }
|
||||||
|
expect_text(out3, "kind=alias lang_id=0 name=?");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expect_text(buf: ByteBuffer, expected: []u8) {
|
||||||
|
var got = ByteBuffer.as_slice(buf);
|
||||||
|
@assert(got.len == expected.len, "dump length");
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < expected.len {
|
||||||
|
@assert(got.ptr[i] == expected.ptr[i], "dump byte");
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_alloc_and_state();
|
||||||
|
test_kind_helpers();
|
||||||
|
test_dump();
|
||||||
|
}
|
||||||
+2
-15
@@ -1,14 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// 丰富诊断(参考 rustc / Clang / Swift 的设计,不照搬 stage1 C 实现):
|
// 丰富诊断:Severity / 稳定 E 码 / Span / Label / Child,渲染为人类可读或 JSON。
|
||||||
// Severity Note/Help/Warning/Error/Fatal
|
// 位置只存下标,渲染时经 Sources 解析行列;条目拥有其字符串与 labels/children。
|
||||||
// Code 固定语义枚举 + 稳定 E 码;集中管理,利于 diff
|
|
||||||
// Span 字节区间 {file,start,end};file 是 Sources 的下标,NO_FILE 表示无位置
|
|
||||||
// Label 主/次标签(span + 文本),可多个
|
|
||||||
// Child 子诊断(note/help,可带 span),挂在条目上
|
|
||||||
// 渲染:人类可读(源码行 + caret 下划线)与 JSON(字段稳定)双格式。
|
|
||||||
// 位置只存下标,渲染时经 Sources 解析行列/取行,避免复制文件名。
|
|
||||||
// 消息按拥有型 Str 保存;条目拥有其 labels/children;clear/drop 释放全部内部资源。
|
|
||||||
|
|
||||||
type .{ .Error, .Status, .make } = @import("./utils/error.spl");
|
type .{ .Error, .Status, .make } = @import("./utils/error.spl");
|
||||||
type .{ .Vec } = @import("./utils/vec.spl");
|
type .{ .Vec } = @import("./utils/vec.spl");
|
||||||
@@ -181,7 +174,6 @@ fn has_error(self: Diag): bool { ret self.errors > 0; }
|
|||||||
|
|
||||||
fn has_fatal(self: Diag): bool { ret self.fatals > 0; }
|
fn has_fatal(self: Diag): bool { ret self.fatals > 0; }
|
||||||
|
|
||||||
// ---- 采集 ----
|
|
||||||
|
|
||||||
// 追加一条诊断,返回其下标(失败返回 no_file())。span 为主位置。
|
// 追加一条诊断,返回其下标(失败返回 no_file())。span 为主位置。
|
||||||
fn emit(self: *Diag, severity: Severity, stage: Stage, code: Code, span: Span,
|
fn emit(self: *Diag, severity: Severity, stage: Stage, code: Code, span: Span,
|
||||||
@@ -265,7 +257,6 @@ fn attach_child(self: *Diag, severity: Severity, span: Span, msg: []u8): Status
|
|||||||
ret .Ok;
|
ret .Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 名称 ----
|
|
||||||
|
|
||||||
fn severity_name(severity: Severity): []u8 {
|
fn severity_name(severity: Severity): []u8 {
|
||||||
match severity {
|
match severity {
|
||||||
@@ -370,7 +361,6 @@ fn code_name(code: Code): []u8 {
|
|||||||
ret "none";
|
ret "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 渲染:公共小工具 ----
|
|
||||||
|
|
||||||
fn append_num(out: *ByteBuffer, value: usize): Status {
|
fn append_num(out: *ByteBuffer, value: usize): Status {
|
||||||
ret append_usize(out, value, 10, false);
|
ret append_usize(out, value, 10, false);
|
||||||
@@ -519,7 +509,6 @@ fn render_human(self: *Diag, out: *ByteBuffer): Status {
|
|||||||
ret .Ok;
|
ret .Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- JSON 渲染(字段稳定,供 LSP / diff 工具)----
|
|
||||||
|
|
||||||
fn append_json_hex4(out: *ByteBuffer, b: u8): Status {
|
fn append_json_hex4(out: *ByteBuffer, b: u8): Status {
|
||||||
var hex: []u8 = "0123456789abcdef";
|
var hex: []u8 = "0123456789abcdef";
|
||||||
@@ -661,7 +650,6 @@ fn render_json(self: *Diag, out: *ByteBuffer): Status {
|
|||||||
ret ByteBuffer.append_byte(out, ']');
|
ret ByteBuffer.append_byte(out, ']');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 生命周期 ----
|
|
||||||
|
|
||||||
fn drop_item(it: *Item) {
|
fn drop_item(it: *Item) {
|
||||||
Str.drop(&it.message);
|
Str.drop(&it.message);
|
||||||
@@ -700,7 +688,6 @@ fn drop(self: *Diag) {
|
|||||||
self.sources = null;
|
self.sources = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn view(text: []u8): ByteView {
|
fn view(text: []u8): ByteView {
|
||||||
ret ByteView.from_slice(text);
|
ret ByteView.from_slice(text);
|
||||||
|
|||||||
+130
@@ -0,0 +1,130 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
// comptime/const 求值的接口 seam:sema 只在此调用,未实现时返回 NotConst(放弃折叠、
|
||||||
|
// 不报错)。编译期生成的声明经 DeclSink 登记,不改 sema 表。
|
||||||
|
|
||||||
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
|
type Ast = @import("./ast.spl");
|
||||||
|
type Diag = @import("./diag.spl");
|
||||||
|
|
||||||
|
// 编译期值(当前仅标量)。
|
||||||
|
type EvalValue = enum {
|
||||||
|
Int: usize,
|
||||||
|
Bool: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
type EvalOutcome = enum {
|
||||||
|
Value: EvalValue,
|
||||||
|
NotConst, // 不是编译期常量(正常放弃,不产生诊断)
|
||||||
|
Err: Error, // 求值出错(诊断由 eval 侧发出)
|
||||||
|
}
|
||||||
|
|
||||||
|
// comptime 生成声明的出口:eval 只登记,插入/重跑由驱动负责。
|
||||||
|
type DeclSink = struct {
|
||||||
|
items: Vec, // usize:待插入的 AST 节点引用
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeclSinkResult = enum {
|
||||||
|
Ok: DeclSink,
|
||||||
|
Err: Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 求值上下文:sink/diag 均借用,不持有所有权。
|
||||||
|
type Eval = @self;
|
||||||
|
|
||||||
|
sink: *DeclSink, // 借用,可为 null
|
||||||
|
diag: *Diag, // 借用,可为 null
|
||||||
|
enabled: bool, // false = 完全不求值
|
||||||
|
|
||||||
|
|
||||||
|
fn sink_new(): DeclSinkResult {
|
||||||
|
var s = DeclSink { .items = Vec.blank(@sizeof(usize), @alignof(usize)) };
|
||||||
|
ret .Ok(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sink_drop(self: *DeclSink) {
|
||||||
|
Vec.drop(&self.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sink_push(self: *DeclSink, node_ref: usize): bool {
|
||||||
|
match Vec.push(&self.items, &node_ref as *u8) {
|
||||||
|
.Ok => { ret true; },
|
||||||
|
.Err(error) => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sink_len(self: *DeclSink): usize {
|
||||||
|
ret self.items.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn new(sink: *DeclSink, diag: *Diag, enabled: bool): Eval {
|
||||||
|
ret Eval {
|
||||||
|
.sink = sink,
|
||||||
|
.diag = diag,
|
||||||
|
.enabled = enabled,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_enabled(self: *Eval): bool {
|
||||||
|
ret self.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 求值表达式:当前一律 NotConst(调用方按“放弃折叠”处理)。
|
||||||
|
fn eval_expr(self: *Eval, ast: *Ast, ref: usize): EvalOutcome {
|
||||||
|
if !self.enabled { ret .NotConst; }
|
||||||
|
ret .NotConst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行 comptime 块;当前不实现,直接 NotConst。
|
||||||
|
fn run_block(self: *Eval, ast: *Ast, ref: usize): EvalOutcome {
|
||||||
|
if !self.enabled { ret .NotConst; }
|
||||||
|
ret .NotConst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 顶层 const 初始化求值;当前不实现。
|
||||||
|
fn eval_const_init(self: *Eval, ast: *Ast, ref: usize): EvalOutcome {
|
||||||
|
if !self.enabled { ret .NotConst; }
|
||||||
|
ret .NotConst;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn test_sink() {
|
||||||
|
var sink: DeclSink = undefined;
|
||||||
|
match sink_new() {
|
||||||
|
.Ok(v) => { sink = v; },
|
||||||
|
.Err(error) => { @assert(false, "sink new failed"); },
|
||||||
|
}
|
||||||
|
defer { sink_drop(&sink); }
|
||||||
|
|
||||||
|
@assert(sink_len(&sink) == 0, "empty sink");
|
||||||
|
@assert(sink_push(&sink, 42), "push");
|
||||||
|
@assert(sink_len(&sink) == 1, "one item");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_stub_outcome() {
|
||||||
|
var sink: DeclSink = undefined;
|
||||||
|
match sink_new() {
|
||||||
|
.Ok(v) => { sink = v; },
|
||||||
|
.Err(error) => { @assert(false, "sink new failed"); },
|
||||||
|
}
|
||||||
|
defer { sink_drop(&sink); }
|
||||||
|
|
||||||
|
var ev = new(&sink, null, true);
|
||||||
|
@assert(is_enabled(&ev), "enabled");
|
||||||
|
var out = eval_expr(&ev, null, 0);
|
||||||
|
match out {
|
||||||
|
.NotConst => {},
|
||||||
|
_ => { @assert(false, "stub returns NotConst"); },
|
||||||
|
}
|
||||||
|
|
||||||
|
var ev2 = new(null, null, false);
|
||||||
|
@assert(!is_enabled(&ev2), "disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_sink();
|
||||||
|
test_stub_outcome();
|
||||||
|
}
|
||||||
@@ -0,0 +1,326 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
// 内建注册表:按 Intrin 查描述信息,按名字/字段/`as` 规则反查 Intrin。新增内建只改
|
||||||
|
// info_at,不改 sema 逻辑(SPL.md 的 @/# 扩展点)。
|
||||||
|
|
||||||
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||||
|
type TypeTable = @import("./type_table.spl");
|
||||||
|
type .{ .TypeNode, .TypeEntry } = @import("./type_table.spl");
|
||||||
|
type .{ .LangKind } = @import("./lang_type.spl");
|
||||||
|
|
||||||
|
// 文件命名空间(供跨模块用 IntrinOps.foo 调用)。
|
||||||
|
type IntrinOps = @self;
|
||||||
|
|
||||||
|
type Intrin = enum {
|
||||||
|
None,
|
||||||
|
BitSizeof,
|
||||||
|
Sizeof,
|
||||||
|
Alignof,
|
||||||
|
Assert,
|
||||||
|
Breakpoint,
|
||||||
|
Len,
|
||||||
|
IntCast,
|
||||||
|
Truncate,
|
||||||
|
BitCast,
|
||||||
|
FloatCast,
|
||||||
|
Float2Int,
|
||||||
|
Int2Float,
|
||||||
|
Bool2Int,
|
||||||
|
Ptr2Int,
|
||||||
|
Int2Ptr,
|
||||||
|
PtrCast,
|
||||||
|
Enum2Int,
|
||||||
|
OffsetOf,
|
||||||
|
FieldCount,
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntrinForm = enum {
|
||||||
|
Call,
|
||||||
|
Field,
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntrinResult = enum {
|
||||||
|
Unit,
|
||||||
|
Bool,
|
||||||
|
Usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntrinInfo = struct {
|
||||||
|
name: ByteView,
|
||||||
|
kind: Intrin,
|
||||||
|
form: IntrinForm,
|
||||||
|
min_args: i32,
|
||||||
|
max_args: i32, // -1 = variadic
|
||||||
|
result: IntrinResult,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn form_is(a: IntrinForm, b: IntrinForm): bool {
|
||||||
|
ret @enum2int(a) == @enum2int(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kind_is(a: LangKind, b: LangKind): bool {
|
||||||
|
ret @enum2int(a) == @enum2int(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 枚举顺序即 kind 数值(与 splc0 表一致)。
|
||||||
|
fn info_at(index: usize): IntrinInfo {
|
||||||
|
match index {
|
||||||
|
1 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("@bitsizeof"), .kind = .BitSizeof,
|
||||||
|
.form = .Call, .min_args = 1, .max_args = 1, .result = .Usize };
|
||||||
|
},
|
||||||
|
2 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("@sizeof"), .kind = .Sizeof,
|
||||||
|
.form = .Call, .min_args = 1, .max_args = 1, .result = .Usize };
|
||||||
|
},
|
||||||
|
3 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("@alignof"), .kind = .Alignof,
|
||||||
|
.form = .Call, .min_args = 1, .max_args = 1, .result = .Usize };
|
||||||
|
},
|
||||||
|
4 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("@assert"), .kind = .Assert,
|
||||||
|
.form = .Call, .min_args = 1, .max_args = 2, .result = .Unit };
|
||||||
|
},
|
||||||
|
5 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("@breakpoint"), .kind = .Breakpoint,
|
||||||
|
.form = .Call, .min_args = 0, .max_args = 0, .result = .Unit };
|
||||||
|
},
|
||||||
|
6 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("len"), .kind = .Len,
|
||||||
|
.form = .Field, .min_args = 0, .max_args = 0, .result = .Usize };
|
||||||
|
},
|
||||||
|
7 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("@int_cast"), .kind = .IntCast,
|
||||||
|
.form = .Call, .min_args = 2, .max_args = 2, .result = .Unit };
|
||||||
|
},
|
||||||
|
8 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("@truncate"), .kind = .Truncate,
|
||||||
|
.form = .Call, .min_args = 2, .max_args = 2, .result = .Unit };
|
||||||
|
},
|
||||||
|
9 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("@bit_cast"), .kind = .BitCast,
|
||||||
|
.form = .Call, .min_args = 2, .max_args = 2, .result = .Unit };
|
||||||
|
},
|
||||||
|
10 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("@float_cast"), .kind = .FloatCast,
|
||||||
|
.form = .Call, .min_args = 2, .max_args = 2, .result = .Unit };
|
||||||
|
},
|
||||||
|
11 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("@float2int"), .kind = .Float2Int,
|
||||||
|
.form = .Call, .min_args = 2, .max_args = 2, .result = .Unit };
|
||||||
|
},
|
||||||
|
12 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("@int2float"), .kind = .Int2Float,
|
||||||
|
.form = .Call, .min_args = 2, .max_args = 2, .result = .Unit };
|
||||||
|
},
|
||||||
|
13 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("@bool2int"), .kind = .Bool2Int,
|
||||||
|
.form = .Call, .min_args = 1, .max_args = 2, .result = .Unit };
|
||||||
|
},
|
||||||
|
14 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("@ptr2int"), .kind = .Ptr2Int,
|
||||||
|
.form = .Call, .min_args = 1, .max_args = 2, .result = .Unit };
|
||||||
|
},
|
||||||
|
15 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("@int2ptr"), .kind = .Int2Ptr,
|
||||||
|
.form = .Call, .min_args = 2, .max_args = 2, .result = .Unit };
|
||||||
|
},
|
||||||
|
16 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("@ptr_cast"), .kind = .PtrCast,
|
||||||
|
.form = .Call, .min_args = 2, .max_args = 2, .result = .Unit };
|
||||||
|
},
|
||||||
|
17 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("@enum2int"), .kind = .Enum2Int,
|
||||||
|
.form = .Call, .min_args = 1, .max_args = 1, .result = .Usize };
|
||||||
|
},
|
||||||
|
18 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("@offsetof"), .kind = .OffsetOf,
|
||||||
|
.form = .Call, .min_args = 2, .max_args = 2, .result = .Usize };
|
||||||
|
},
|
||||||
|
19 => {
|
||||||
|
ret IntrinInfo { .name = ByteView.from_slice("@field_count"), .kind = .FieldCount,
|
||||||
|
.form = .Call, .min_args = 1, .max_args = 1, .result = .Usize };
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
ret IntrinInfo { .name = ByteView.empty(), .kind = .None, .form = .Call,
|
||||||
|
.min_args = 0, .max_args = 0, .result = .Unit };
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ret IntrinInfo { .name = ByteView.empty(), .kind = .None, .form = .Call,
|
||||||
|
.min_args = 0, .max_args = 0, .result = .Unit };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn info(kind: Intrin): IntrinInfo {
|
||||||
|
ret info_at(@enum2int(kind) as usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn table_count(): usize {
|
||||||
|
ret 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn strip_at(name: ByteView): ByteView {
|
||||||
|
if name.len > 0 && name.ptr[0] == '@' {
|
||||||
|
ret ByteView { .ptr = name.ptr + 1, .len = name.len - 1 };
|
||||||
|
}
|
||||||
|
ret name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按名查找 CALL 形态的内建;忽略前导 '@'。
|
||||||
|
fn lookup_call(name: ByteView): Intrin {
|
||||||
|
var want = strip_at(name);
|
||||||
|
var i: usize = 1;
|
||||||
|
while i < table_count() {
|
||||||
|
var d = info_at(i);
|
||||||
|
if form_is(d.form, .Call) {
|
||||||
|
var dn = strip_at(d.name);
|
||||||
|
if ByteView.eq(dn, want) { ret d.kind; }
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret .None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按接收者类型 + 字段名查找 FIELD 形态内建。
|
||||||
|
fn lookup_field(recv: LangKind, field: ByteView): Intrin {
|
||||||
|
if ByteView.eq(field, ByteView.from_slice("len")) {
|
||||||
|
if kind_is(recv, .Array) || kind_is(recv, .Slice) { ret .Len; }
|
||||||
|
}
|
||||||
|
ret .None;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_int(e: *TypeEntry): bool {
|
||||||
|
match e.node {
|
||||||
|
.Int(x) => { ret true; },
|
||||||
|
_ => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_float(e: *TypeEntry): bool {
|
||||||
|
match e.node {
|
||||||
|
.Float(x) => { ret true; },
|
||||||
|
_ => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_bool(e: *TypeEntry): bool {
|
||||||
|
match e.node {
|
||||||
|
.Bool => { ret true; },
|
||||||
|
_ => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ptr(e: *TypeEntry): bool {
|
||||||
|
match e.node {
|
||||||
|
.Ptr(x) => { ret true; },
|
||||||
|
_ => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int_bits(e: *TypeEntry): usize {
|
||||||
|
match e.node {
|
||||||
|
.Int(x) => { ret x.bits; },
|
||||||
|
_ => { ret 0; },
|
||||||
|
}
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// `as` 的展开规则表(改 as 语义只改这里)。
|
||||||
|
fn lookup_cast(ir: *TypeTable, ft: usize, tt: usize): Intrin {
|
||||||
|
var f = TypeTable.node(ir, ft);
|
||||||
|
var t = TypeTable.node(ir, tt);
|
||||||
|
if f == null || t == null { ret .None; }
|
||||||
|
|
||||||
|
if is_int(f) && is_int(t) {
|
||||||
|
var fb = int_bits(f);
|
||||||
|
var tb = int_bits(t);
|
||||||
|
if fb == tb { ret .BitCast; }
|
||||||
|
if fb > tb { ret .Truncate; }
|
||||||
|
ret .IntCast;
|
||||||
|
}
|
||||||
|
if is_float(f) && is_float(t) { ret .FloatCast; }
|
||||||
|
if is_bool(f) && is_int(t) { ret .Bool2Int; }
|
||||||
|
if is_ptr(f) && is_int(t) { ret .Ptr2Int; }
|
||||||
|
if is_int(f) && is_ptr(t) { ret .Int2Ptr; }
|
||||||
|
if is_ptr(f) && is_ptr(t) { ret .PtrCast; }
|
||||||
|
ret .None;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn test_info_table() {
|
||||||
|
var d = info(.Sizeof);
|
||||||
|
@assert(ByteView.eq(d.name, ByteView.from_slice("@sizeof")), "sizeof name");
|
||||||
|
@assert(d.min_args == 1 && d.max_args == 1, "sizeof arity");
|
||||||
|
@assert(form_is(d.form, .Call), "sizeof form");
|
||||||
|
|
||||||
|
var l = info(.Len);
|
||||||
|
@assert(ByteView.eq(l.name, ByteView.from_slice("len")), "len name");
|
||||||
|
@assert(form_is(l.form, .Field), "len form");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_lookup_call() {
|
||||||
|
@assert(@enum2int(lookup_call(ByteView.from_slice("sizeof"))) == @enum2int(Intrin.Sizeof),
|
||||||
|
"sizeof by bare name");
|
||||||
|
@assert(@enum2int(lookup_call(ByteView.from_slice("@sizeof"))) == @enum2int(Intrin.Sizeof),
|
||||||
|
"sizeof by @name");
|
||||||
|
@assert(@enum2int(lookup_call(ByteView.from_slice("assert"))) == @enum2int(Intrin.Assert),
|
||||||
|
"assert");
|
||||||
|
@assert(@enum2int(lookup_call(ByteView.from_slice("len"))) == @enum2int(Intrin.None),
|
||||||
|
"len is field, not call");
|
||||||
|
@assert(@enum2int(lookup_call(ByteView.from_slice("nope"))) == @enum2int(Intrin.None),
|
||||||
|
"unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_lookup_field() {
|
||||||
|
@assert(@enum2int(lookup_field(.Slice, ByteView.from_slice("len"))) ==
|
||||||
|
@enum2int(Intrin.Len),
|
||||||
|
"slice len");
|
||||||
|
@assert(@enum2int(lookup_field(.Array, ByteView.from_slice("len"))) ==
|
||||||
|
@enum2int(Intrin.Len),
|
||||||
|
"array len");
|
||||||
|
@assert(@enum2int(lookup_field(.Struct, ByteView.from_slice("len"))) ==
|
||||||
|
@enum2int(Intrin.None),
|
||||||
|
"struct len");
|
||||||
|
@assert(@enum2int(lookup_field(.Slice, ByteView.from_slice("ptr"))) ==
|
||||||
|
@enum2int(Intrin.None),
|
||||||
|
"slice ptr");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_lookup_cast() {
|
||||||
|
var ir: TypeTable = undefined;
|
||||||
|
match TypeTable.new() {
|
||||||
|
.Ok(v) => { ir = v; },
|
||||||
|
.Err(error) => { @assert(false, "type table new failed"); },
|
||||||
|
}
|
||||||
|
defer { TypeTable.drop(&ir); }
|
||||||
|
|
||||||
|
var i32t = TypeTable.make_int(&ir, 32, true);
|
||||||
|
var i64t = TypeTable.make_int(&ir, 64, true);
|
||||||
|
var f64t = TypeTable.make_float(&ir, 64);
|
||||||
|
var boolt = TypeTable.make_bool(&ir);
|
||||||
|
var pt = TypeTable.make_ptr(&ir, i32t);
|
||||||
|
|
||||||
|
@assert(@enum2int(lookup_cast(&ir, i64t, i32t)) == @enum2int(Intrin.Truncate),
|
||||||
|
"truncate");
|
||||||
|
@assert(@enum2int(lookup_cast(&ir, i32t, i64t)) == @enum2int(Intrin.IntCast), "int cast");
|
||||||
|
@assert(@enum2int(lookup_cast(&ir, i32t, i32t)) == @enum2int(Intrin.BitCast), "bit cast");
|
||||||
|
@assert(@enum2int(lookup_cast(&ir, f64t, f64t)) == @enum2int(Intrin.FloatCast),
|
||||||
|
"float cast");
|
||||||
|
@assert(@enum2int(lookup_cast(&ir, boolt, i32t)) == @enum2int(Intrin.Bool2Int),
|
||||||
|
"bool2int");
|
||||||
|
@assert(@enum2int(lookup_cast(&ir, pt, i32t)) == @enum2int(Intrin.Ptr2Int), "ptr2int");
|
||||||
|
@assert(@enum2int(lookup_cast(&ir, i32t, pt)) == @enum2int(Intrin.Int2Ptr), "int2ptr");
|
||||||
|
@assert(@enum2int(lookup_cast(&ir, pt, pt)) == @enum2int(Intrin.PtrCast), "ptr cast");
|
||||||
|
@assert(@enum2int(lookup_cast(&ir, i32t, f64t)) == @enum2int(Intrin.None),
|
||||||
|
"int->float forbidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_info_table();
|
||||||
|
test_lookup_call();
|
||||||
|
test_lookup_field();
|
||||||
|
test_lookup_cast();
|
||||||
|
}
|
||||||
+610
@@ -0,0 +1,610 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
// SIR 中间表示:节点用 arena 序号引用(0=error),按函数分桶;公共字段在 IrEntry,
|
||||||
|
// kind 专属载荷在 IrBody ADT(kind 与载荷一一对应)。builder 负责发射与跳转回填。
|
||||||
|
|
||||||
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||||
|
type .{ .ByteBuffer } = @import("utils/byte_buffer.spl");
|
||||||
|
type .{ .append_usize, .append_i64 } = @import("utils/fmt.spl");
|
||||||
|
type .{ .Span, .span_none } = @import("./diag.spl");
|
||||||
|
type TypeTable = @import("./type_table.spl");
|
||||||
|
type Attr = @import("./attr.spl");
|
||||||
|
|
||||||
|
type IrKind = enum {
|
||||||
|
ArithAdd,
|
||||||
|
ArithSub,
|
||||||
|
ArithMul,
|
||||||
|
ArithDiv,
|
||||||
|
ArithRem,
|
||||||
|
ArithNeg,
|
||||||
|
ArithAbs,
|
||||||
|
ArithAnd,
|
||||||
|
ArithOr,
|
||||||
|
ArithXor,
|
||||||
|
ArithShl,
|
||||||
|
ArithShr,
|
||||||
|
ArithNot,
|
||||||
|
CmpEq,
|
||||||
|
CmpNe,
|
||||||
|
CmpLt,
|
||||||
|
CmpLe,
|
||||||
|
CmpGt,
|
||||||
|
CmpGe,
|
||||||
|
CastTrunc,
|
||||||
|
CastZext,
|
||||||
|
CastSext,
|
||||||
|
CastFext,
|
||||||
|
CastFtrunc,
|
||||||
|
CastBitcast,
|
||||||
|
CastPtr2int,
|
||||||
|
CastInt2ptr,
|
||||||
|
CastBool2int,
|
||||||
|
CaseInt2float,
|
||||||
|
CaseFloat2int,
|
||||||
|
MemAlloca,
|
||||||
|
MemGlobalAlloc,
|
||||||
|
MemLoad,
|
||||||
|
MemStore,
|
||||||
|
MemOffset,
|
||||||
|
MemFieldPtr,
|
||||||
|
MemCopy,
|
||||||
|
MemSet,
|
||||||
|
TypeConst,
|
||||||
|
TypeBitsizeof,
|
||||||
|
TypeSizeof,
|
||||||
|
TypeAlignof,
|
||||||
|
TypeOffsetof,
|
||||||
|
TypeFieldCount,
|
||||||
|
AggConstruct,
|
||||||
|
AggExtract,
|
||||||
|
AggInsert,
|
||||||
|
ControlSelect,
|
||||||
|
ControlBr,
|
||||||
|
ControlJmp,
|
||||||
|
ControlCall,
|
||||||
|
ControlParam,
|
||||||
|
ControlRet,
|
||||||
|
ControlUnreachable,
|
||||||
|
ControlTrap,
|
||||||
|
DbgBreakpoint,
|
||||||
|
DbgPanic,
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConstLit = enum {
|
||||||
|
Bool: bool,
|
||||||
|
Ptr: usize,
|
||||||
|
Int: usize,
|
||||||
|
Float: f64,
|
||||||
|
Char: u8,
|
||||||
|
Fn: usize,
|
||||||
|
Global: usize, // 全局变量下标(Ir.globals);值 = 指向该全局的 *T
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模块级全局变量:编译期分配的静态存储,引用时是一个 *T 指针常量(同 LLVM GlobalVariable)。
|
||||||
|
type Global = struct {
|
||||||
|
tid: usize,
|
||||||
|
zero: bool, // true = 零初始化(BSS,不携带字节)
|
||||||
|
data: Vec, // u8 初始字节(zero=false 时使用)
|
||||||
|
}
|
||||||
|
|
||||||
|
type IrBody = enum {
|
||||||
|
None,
|
||||||
|
Arith: struct { tid: usize, left: usize, right: usize },
|
||||||
|
Cmp: struct { tid: usize, a: usize, b: usize },
|
||||||
|
Cast: struct { from_tid: usize, to_tid: usize, val: usize },
|
||||||
|
MemAlloc: struct { tid: usize, count: usize },
|
||||||
|
MemGlobalAlloc: struct { tid: usize, const_node: usize },
|
||||||
|
MemLoad: struct { tid: usize, ptr: usize },
|
||||||
|
MemStore: struct { tid: usize, ptr: usize, val: usize },
|
||||||
|
MemOffset: struct { tid: usize, ptr: usize, offset: usize },
|
||||||
|
MemFieldPtr: struct { tid: usize, agg: usize, field_idx: usize },
|
||||||
|
MemCopy: struct { tid: usize, dst: usize, src: usize, size: usize },
|
||||||
|
MemSet: struct { tid: usize, dst: usize, val: usize, size: usize },
|
||||||
|
Const: struct { tid: usize, lit: ConstLit },
|
||||||
|
TypeOp: struct { tid: usize, field_idx: usize },
|
||||||
|
AggConstruct: struct { tid: usize, fields: Vec },
|
||||||
|
AggExtract: struct { tid: usize, field_tid: usize, field_idx: usize, val: usize },
|
||||||
|
AggInsert: struct { tid: usize, field_idx: usize, agg: usize, field: usize },
|
||||||
|
Select: struct { tid: usize, cond: usize, true_val: usize, false_val: usize },
|
||||||
|
Br: struct { cond: usize, true_label: usize, false_label: usize },
|
||||||
|
Jmp: struct { label: usize },
|
||||||
|
Call: struct { tid: usize, func: usize, params: Vec },
|
||||||
|
Param: struct { tid: usize, idx: usize },
|
||||||
|
Ret: struct { tid: usize, val: usize },
|
||||||
|
Panic: struct { msg: ByteView },
|
||||||
|
}
|
||||||
|
|
||||||
|
type IrEntry = struct {
|
||||||
|
kind: IrKind,
|
||||||
|
dbg: Span,
|
||||||
|
attrs: Vec, // attr id
|
||||||
|
body: IrBody,
|
||||||
|
}
|
||||||
|
|
||||||
|
type IrFunc = struct {
|
||||||
|
name: ByteView,
|
||||||
|
attrs: Vec, // attr id
|
||||||
|
fn_tid: usize,
|
||||||
|
nodes: Vec, // IrEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
type Ir = @self;
|
||||||
|
|
||||||
|
funcs: Vec, // IrFunc
|
||||||
|
globals: Vec, // Global
|
||||||
|
|
||||||
|
|
||||||
|
fn new_entry(): IrEntry {
|
||||||
|
ret IrEntry {
|
||||||
|
.kind = .ArithAdd,
|
||||||
|
.dbg = span_none(),
|
||||||
|
.attrs = Vec.blank(@sizeof(usize), @alignof(usize)),
|
||||||
|
.body = .None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(): Ir {
|
||||||
|
var ir = Ir { .funcs = Vec.blank(@sizeof(IrFunc), @alignof(IrFunc)),
|
||||||
|
.globals = Vec.blank(@sizeof(Global), @alignof(Global)) };
|
||||||
|
var sentinel = IrFunc {
|
||||||
|
.name = ByteView.empty(),
|
||||||
|
.attrs = Vec.blank(@sizeof(usize), @alignof(usize)),
|
||||||
|
.fn_tid = 0,
|
||||||
|
.nodes = Vec.blank(@sizeof(IrEntry), @alignof(IrEntry)),
|
||||||
|
};
|
||||||
|
var sn = new_entry();
|
||||||
|
Vec.push(&sentinel.nodes, &sn as *u8);
|
||||||
|
Vec.push(&ir.funcs, &sentinel as *u8);
|
||||||
|
ret ir;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn drop_body(body: IrBody) {
|
||||||
|
match body {
|
||||||
|
.AggConstruct(a) => { Vec.drop(&a.fields); },
|
||||||
|
.Call(c) => { Vec.drop(&c.params); },
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drop(self: *Ir) {
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < self.funcs.len {
|
||||||
|
var f = (self.funcs.data as *IrFunc) + i;
|
||||||
|
var j: usize = 0;
|
||||||
|
while j < f.nodes.len {
|
||||||
|
var e = (f.nodes.data as *IrEntry) + j;
|
||||||
|
Vec.drop(&e.attrs);
|
||||||
|
drop_body(e.body);
|
||||||
|
j = j + 1;
|
||||||
|
}
|
||||||
|
Vec.drop(&f.attrs);
|
||||||
|
Vec.drop(&f.nodes);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
Vec.drop(&self.funcs);
|
||||||
|
var g: usize = 0;
|
||||||
|
while g < self.globals.len {
|
||||||
|
var ge = (self.globals.data as *Global) + g;
|
||||||
|
Vec.drop(&ge.data);
|
||||||
|
g = g + 1;
|
||||||
|
}
|
||||||
|
Vec.drop(&self.globals);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 追加一个全局变量;返回其下标(gidx)。bytes 的所有权转移进表。
|
||||||
|
fn add_global(self: *Ir, tid: usize, zero: bool, bytes: Vec): usize {
|
||||||
|
var g = Global { .tid = tid, .zero = zero, .data = bytes };
|
||||||
|
match Vec.push(&self.globals, &g as *u8) { .Ok => {}, .Err(error) => { ret 0; } }
|
||||||
|
ret self.globals.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn global_at(self: *Ir, gidx: usize): *Global {
|
||||||
|
if gidx >= self.globals.len { ret null; }
|
||||||
|
ret (self.globals.data as *Global) + gidx;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_fn(self: *Ir): usize {
|
||||||
|
var f = IrFunc {
|
||||||
|
.name = ByteView.empty(),
|
||||||
|
.attrs = Vec.blank(@sizeof(usize), @alignof(usize)),
|
||||||
|
.fn_tid = 0,
|
||||||
|
.nodes = Vec.blank(@sizeof(IrEntry), @alignof(IrEntry)),
|
||||||
|
};
|
||||||
|
var sn = new_entry();
|
||||||
|
match Vec.push(&f.nodes, &sn as *u8) { .Ok => {}, .Err(error) => { ret 0; } }
|
||||||
|
match Vec.push(&self.funcs, &f as *u8) { .Ok => {}, .Err(error) => { ret 0; } }
|
||||||
|
ret self.funcs.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_node(self: *Ir, fn_id: usize): usize {
|
||||||
|
if fn_id == 0 || fn_id >= self.funcs.len { ret 0; }
|
||||||
|
var f = (self.funcs.data as *IrFunc) + fn_id;
|
||||||
|
var n = new_entry();
|
||||||
|
match Vec.push(&f.nodes, &n as *u8) { .Ok => {}, .Err(error) => { ret 0; } }
|
||||||
|
ret f.nodes.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func(self: *Ir, fn_id: usize): *IrFunc {
|
||||||
|
if fn_id == 0 || fn_id >= self.funcs.len { ret null; }
|
||||||
|
ret (self.funcs.data as *IrFunc) + fn_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node(self: *Ir, fn_id: usize, node_id: usize): *IrEntry {
|
||||||
|
var f = func(self, fn_id);
|
||||||
|
if f == null { ret null; }
|
||||||
|
if node_id == 0 || node_id >= f.nodes.len { ret null; }
|
||||||
|
ret (f.nodes.data as *IrEntry) + node_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kind_name(kind: IrKind): []u8 {
|
||||||
|
match kind {
|
||||||
|
.ArithAdd => { ret "arith.add"; },
|
||||||
|
.ArithSub => { ret "arith.sub"; },
|
||||||
|
.ArithMul => { ret "arith.mul"; },
|
||||||
|
.ArithDiv => { ret "arith.div"; },
|
||||||
|
.ArithRem => { ret "arith.rem"; },
|
||||||
|
.ArithNeg => { ret "arith.neg"; },
|
||||||
|
.ArithAbs => { ret "arith.abs"; },
|
||||||
|
.ArithAnd => { ret "arith.and"; },
|
||||||
|
.ArithOr => { ret "arith.or"; },
|
||||||
|
.ArithXor => { ret "arith.xor"; },
|
||||||
|
.ArithShl => { ret "arith.shl"; },
|
||||||
|
.ArithShr => { ret "arith.shr"; },
|
||||||
|
.ArithNot => { ret "arith.not"; },
|
||||||
|
.CmpEq => { ret "cmp.eq"; },
|
||||||
|
.CmpNe => { ret "cmp.ne"; },
|
||||||
|
.CmpLt => { ret "cmp.lt"; },
|
||||||
|
.CmpLe => { ret "cmp.le"; },
|
||||||
|
.CmpGt => { ret "cmp.gt"; },
|
||||||
|
.CmpGe => { ret "cmp.ge"; },
|
||||||
|
.CastTrunc => { ret "cast.trunc"; },
|
||||||
|
.CastZext => { ret "cast.zext"; },
|
||||||
|
.CastSext => { ret "cast.sext"; },
|
||||||
|
.CastFext => { ret "cast.fext"; },
|
||||||
|
.CastFtrunc => { ret "cast.ftrunc"; },
|
||||||
|
.CastBitcast => { ret "cast.bitcast"; },
|
||||||
|
.CastPtr2int => { ret "cast.ptr2int"; },
|
||||||
|
.CastInt2ptr => { ret "cast.int2ptr"; },
|
||||||
|
.CastBool2int => { ret "cast.bool2int"; },
|
||||||
|
.CaseInt2float => { ret "case.int2float"; },
|
||||||
|
.CaseFloat2int => { ret "case.float2int"; },
|
||||||
|
.MemAlloca => { ret "mem.alloca"; },
|
||||||
|
.MemGlobalAlloc => { ret "mem.global_alloc"; },
|
||||||
|
.MemLoad => { ret "mem.load"; },
|
||||||
|
.MemStore => { ret "mem.store"; },
|
||||||
|
.MemOffset => { ret "mem.offset"; },
|
||||||
|
.MemFieldPtr => { ret "mem.field"; },
|
||||||
|
.MemCopy => { ret "mem.copy"; },
|
||||||
|
.MemSet => { ret "mem.set"; },
|
||||||
|
.TypeConst => { ret "type.const"; },
|
||||||
|
.TypeBitsizeof => { ret "type.bitsizeof"; },
|
||||||
|
.TypeSizeof => { ret "type.sizeof"; },
|
||||||
|
.TypeAlignof => { ret "type.alignof"; },
|
||||||
|
.TypeOffsetof => { ret "type.offsetof"; },
|
||||||
|
.TypeFieldCount => { ret "type.field_count"; },
|
||||||
|
.AggConstruct => { ret "agg.construct"; },
|
||||||
|
.AggExtract => { ret "agg.extract"; },
|
||||||
|
.AggInsert => { ret "agg.insert"; },
|
||||||
|
.ControlSelect => { ret "control.select"; },
|
||||||
|
.ControlBr => { ret "control.br"; },
|
||||||
|
.ControlJmp => { ret "control.jmp"; },
|
||||||
|
.ControlCall => { ret "control.call"; },
|
||||||
|
.ControlParam => { ret "control.param"; },
|
||||||
|
.ControlRet => { ret "control.ret"; },
|
||||||
|
.ControlUnreachable => { ret "control.unreachable"; },
|
||||||
|
.ControlTrap => { ret "control.trap"; },
|
||||||
|
.DbgBreakpoint => { ret "dbg.breakpoint"; },
|
||||||
|
.DbgPanic => { ret "dbg.panic"; },
|
||||||
|
}
|
||||||
|
ret "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn out_str(out: *ByteBuffer, s: []u8): bool {
|
||||||
|
match ByteBuffer.append(out, s) { .Ok => { ret true; }, .Err(error) => { ret false; } }
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn out_u(out: *ByteBuffer, v: usize): bool {
|
||||||
|
match append_usize(out, v, 10, false) { .Ok => { ret true; }, .Err(error) => { ret false; } }
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn out_i(out: *ByteBuffer, v: i64): bool {
|
||||||
|
match append_i64(out, v, 10, false) { .Ok => { ret true; }, .Err(error) => { ret false; } }
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_type(out: *ByteBuffer, ty: *TypeTable, tid: usize): bool {
|
||||||
|
if !out_str(out, "(") { ret false; }
|
||||||
|
if !TypeTable.pure_dump(ty, tid, out) { ret false; }
|
||||||
|
ret out_str(out, ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_ref(out: *ByteBuffer, ref: usize): bool {
|
||||||
|
if ref == 0 { ret out_str(out, " _"); }
|
||||||
|
if !out_str(out, " %") { ret false; }
|
||||||
|
ret out_u(out, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_attrs(out: *ByteBuffer, attr: *Attr, ids: Vec): bool {
|
||||||
|
if ids.len == 0 { ret true; }
|
||||||
|
if !out_str(out, " attrs=[") { ret false; }
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < ids.len {
|
||||||
|
if i > 0 && !out_str(out, ", ") { ret false; }
|
||||||
|
var id = list_at(ids, i);
|
||||||
|
if !Attr.dump_id(attr, id, out) { ret false; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret out_str(out, "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_at(v: Vec, i: usize): usize {
|
||||||
|
if i >= v.len { ret 0; }
|
||||||
|
ret ((v.data as *usize) + i).*;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_body(out: *ByteBuffer, ty: *TypeTable, e: *IrEntry): bool {
|
||||||
|
match e.body {
|
||||||
|
.None => { ret true; },
|
||||||
|
.Arith(a) => {
|
||||||
|
if !dump_type(out, ty, a.tid) { ret false; }
|
||||||
|
if !dump_ref(out, a.left) { ret false; }
|
||||||
|
// 一元 arith(neg/abs/not)不打印 right。
|
||||||
|
match e.kind {
|
||||||
|
.ArithNeg, .ArithAbs, .ArithNot => { ret true; },
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
ret dump_ref(out, a.right);
|
||||||
|
},
|
||||||
|
.Cmp(c) => {
|
||||||
|
if !dump_type(out, ty, c.tid) { ret false; }
|
||||||
|
if !dump_ref(out, c.a) { ret false; }
|
||||||
|
ret dump_ref(out, c.b);
|
||||||
|
},
|
||||||
|
.Cast(c) => {
|
||||||
|
if !out_str(out, " ") { ret false; }
|
||||||
|
if !TypeTable.pure_dump(ty, c.from_tid, out) { ret false; }
|
||||||
|
if !out_str(out, " -> ") { ret false; }
|
||||||
|
if !TypeTable.pure_dump(ty, c.to_tid, out) { ret false; }
|
||||||
|
ret dump_ref(out, c.val);
|
||||||
|
},
|
||||||
|
.MemAlloc(m) => {
|
||||||
|
if !dump_type(out, ty, m.tid) { ret false; }
|
||||||
|
ret dump_ref(out, m.count);
|
||||||
|
},
|
||||||
|
.MemGlobalAlloc(m) => {
|
||||||
|
if !dump_type(out, ty, m.tid) { ret false; }
|
||||||
|
if !out_str(out, " g#") { ret false; }
|
||||||
|
ret out_u(out, m.const_node);
|
||||||
|
},
|
||||||
|
.MemLoad(m) => {
|
||||||
|
if !dump_type(out, ty, m.tid) { ret false; }
|
||||||
|
ret dump_ref(out, m.ptr);
|
||||||
|
},
|
||||||
|
.MemStore(m) => {
|
||||||
|
if !dump_type(out, ty, m.tid) { ret false; }
|
||||||
|
if !dump_ref(out, m.ptr) { ret false; }
|
||||||
|
ret dump_ref(out, m.val);
|
||||||
|
},
|
||||||
|
.MemOffset(m) => {
|
||||||
|
if !dump_type(out, ty, m.tid) { ret false; }
|
||||||
|
if !dump_ref(out, m.ptr) { ret false; }
|
||||||
|
ret dump_ref(out, m.offset);
|
||||||
|
},
|
||||||
|
.MemFieldPtr(m) => {
|
||||||
|
if !dump_type(out, ty, m.tid) { ret false; }
|
||||||
|
if !dump_ref(out, m.agg) { ret false; }
|
||||||
|
if !out_str(out, " #") { ret false; }
|
||||||
|
ret out_u(out, m.field_idx);
|
||||||
|
},
|
||||||
|
.MemCopy(m) => {
|
||||||
|
if !dump_type(out, ty, m.tid) { ret false; }
|
||||||
|
if !dump_ref(out, m.dst) { ret false; }
|
||||||
|
if !dump_ref(out, m.src) { ret false; }
|
||||||
|
ret dump_ref(out, m.size);
|
||||||
|
},
|
||||||
|
.MemSet(m) => {
|
||||||
|
if !dump_type(out, ty, m.tid) { ret false; }
|
||||||
|
if !dump_ref(out, m.dst) { ret false; }
|
||||||
|
if !dump_ref(out, m.val) { ret false; }
|
||||||
|
ret dump_ref(out, m.size);
|
||||||
|
},
|
||||||
|
.Const(c) => {
|
||||||
|
if !dump_type(out, ty, c.tid) { ret false; }
|
||||||
|
match c.lit {
|
||||||
|
.Global(g) => {
|
||||||
|
if !out_str(out, " global#") { ret false; }
|
||||||
|
ret out_u(out, g);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
var te = TypeTable.node(ty, c.tid);
|
||||||
|
var is_float = false;
|
||||||
|
var is_fn = false;
|
||||||
|
var is_u8 = false;
|
||||||
|
var is_arr = false;
|
||||||
|
if te != null {
|
||||||
|
match te.node {
|
||||||
|
.Float(x) => { is_float = true; },
|
||||||
|
.Fn(x) => { is_fn = true; },
|
||||||
|
.Array(x) => { is_arr = true; },
|
||||||
|
.Int(x) => { if x.bits == 8 { is_u8 = true; } },
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if is_float {
|
||||||
|
ret out_str(out, " float=?");
|
||||||
|
}
|
||||||
|
if is_arr {
|
||||||
|
ret out_str(out, " bytes");
|
||||||
|
}
|
||||||
|
if is_fn {
|
||||||
|
if !out_str(out, " fn#") { ret false; }
|
||||||
|
match c.lit { .Fn(v) => { ret out_u(out, v); }, _ => { ret out_str(out, "?"); } }
|
||||||
|
}
|
||||||
|
if is_u8 {
|
||||||
|
var cv: usize = 0;
|
||||||
|
match c.lit {
|
||||||
|
.Char(v) => { cv = v as usize; },
|
||||||
|
.Int(v) => { cv = v; },
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
if !out_str(out, " char='") { ret false; }
|
||||||
|
match ByteBuffer.append_byte(out, cv as u8) { .Ok => {}, .Err(error) => { ret false; } }
|
||||||
|
ret out_str(out, "'");
|
||||||
|
}
|
||||||
|
// 其他常量统一按 int_lit 打印(union 别名)。
|
||||||
|
var iv: usize = 0;
|
||||||
|
match c.lit {
|
||||||
|
.Int(v) => { iv = v; },
|
||||||
|
.Bool(v) => { if v { iv = 1; } },
|
||||||
|
.Ptr(v) => { iv = v; },
|
||||||
|
.Char(v) => { iv = v as usize; },
|
||||||
|
.Fn(v) => { iv = v; },
|
||||||
|
.Float(v) => {},
|
||||||
|
}
|
||||||
|
if !out_str(out, " int=") { ret false; }
|
||||||
|
ret out_u(out, iv);
|
||||||
|
},
|
||||||
|
.TypeOp(t) => {
|
||||||
|
if !dump_type(out, ty, t.tid) { ret false; }
|
||||||
|
if @enum2int(e.kind) == @enum2int(IrKind.TypeOffsetof) {
|
||||||
|
ret dump_ref(out, t.field_idx);
|
||||||
|
}
|
||||||
|
ret true;
|
||||||
|
},
|
||||||
|
.AggConstruct(a) => {
|
||||||
|
if !dump_type(out, ty, a.tid) { ret false; }
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < a.fields.len {
|
||||||
|
if !dump_ref(out, list_at(a.fields, i)) { ret false; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret true;
|
||||||
|
},
|
||||||
|
.AggExtract(a) => {
|
||||||
|
if !dump_type(out, ty, a.tid) { ret false; }
|
||||||
|
if !out_str(out, " #") { ret false; }
|
||||||
|
if !out_u(out, a.field_idx) { ret false; }
|
||||||
|
ret dump_ref(out, a.val);
|
||||||
|
},
|
||||||
|
.AggInsert(a) => {
|
||||||
|
if !dump_type(out, ty, a.tid) { ret false; }
|
||||||
|
if !out_str(out, " #") { ret false; }
|
||||||
|
if !out_u(out, a.field_idx) { ret false; }
|
||||||
|
if !dump_ref(out, a.agg) { ret false; }
|
||||||
|
ret dump_ref(out, a.field);
|
||||||
|
},
|
||||||
|
.Select(s) => {
|
||||||
|
if !dump_type(out, ty, s.tid) { ret false; }
|
||||||
|
if !dump_ref(out, s.cond) { ret false; }
|
||||||
|
if !dump_ref(out, s.true_val) { ret false; }
|
||||||
|
ret dump_ref(out, s.false_val);
|
||||||
|
},
|
||||||
|
.Br(b) => {
|
||||||
|
if !dump_ref(out, b.cond) { ret false; }
|
||||||
|
if !dump_ref(out, b.true_label) { ret false; }
|
||||||
|
ret dump_ref(out, b.false_label);
|
||||||
|
},
|
||||||
|
.Jmp(j) => { ret dump_ref(out, j.label); },
|
||||||
|
.Call(c) => {
|
||||||
|
if !dump_type(out, ty, c.tid) { ret false; }
|
||||||
|
if !dump_ref(out, c.func) { ret false; }
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < c.params.len {
|
||||||
|
if !dump_ref(out, list_at(c.params, i)) { ret false; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret true;
|
||||||
|
},
|
||||||
|
.Param(p) => {
|
||||||
|
if !dump_type(out, ty, p.tid) { ret false; }
|
||||||
|
ret dump_ref(out, p.idx);
|
||||||
|
},
|
||||||
|
.Ret(r) => {
|
||||||
|
if !dump_type(out, ty, r.tid) { ret false; }
|
||||||
|
ret dump_ref(out, r.val);
|
||||||
|
},
|
||||||
|
.Panic(p) => {
|
||||||
|
if !out_str(out, " \"") { ret false; }
|
||||||
|
if !out_str(out, ByteView.as_slice(p.msg)) { ret false; }
|
||||||
|
ret out_str(out, "\"");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump(self: *Ir, ty: *TypeTable, attr: *Attr, out: *ByteBuffer): bool {
|
||||||
|
var i: usize = 1;
|
||||||
|
while i < self.funcs.len {
|
||||||
|
var f = func(self, i);
|
||||||
|
if f == null { i = i + 1; continue; }
|
||||||
|
if !out_str(out, "func ") { ret false; }
|
||||||
|
if f.name.len == 0 {
|
||||||
|
if !out_str(out, "?") { ret false; }
|
||||||
|
} else {
|
||||||
|
if !out_str(out, ByteView.as_slice(f.name)) { ret false; }
|
||||||
|
}
|
||||||
|
if !out_str(out, " %") { ret false; }
|
||||||
|
if !out_u(out, i) { ret false; }
|
||||||
|
if !out_str(out, " tid = ") { ret false; }
|
||||||
|
if !TypeTable.pure_dump(ty, f.fn_tid, out) { ret false; }
|
||||||
|
if !dump_attrs(out, attr, f.attrs) { ret false; }
|
||||||
|
if !out_str(out, "\n") { ret false; }
|
||||||
|
|
||||||
|
var j: usize = 1;
|
||||||
|
while j < f.nodes.len {
|
||||||
|
var e = node(self, i, j);
|
||||||
|
if e == null { j = j + 1; continue; }
|
||||||
|
if !out_str(out, " #") { ret false; }
|
||||||
|
if !out_u(out, j) { ret false; }
|
||||||
|
if !out_str(out, " ") { ret false; }
|
||||||
|
if !out_str(out, kind_name(e.kind)) { ret false; }
|
||||||
|
if !dump_body(out, ty, e) { ret false; }
|
||||||
|
if !dump_attrs(out, attr, e.attrs) { ret false; }
|
||||||
|
if !out_str(out, "\n") { ret false; }
|
||||||
|
j = j + 1;
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn test_kinds() {
|
||||||
|
@assert(ByteView.eq(ByteView.from_slice(kind_name(.ArithAdd)), ByteView.from_slice("arith.add")),
|
||||||
|
"arith.add name");
|
||||||
|
@assert(ByteView.eq(ByteView.from_slice(kind_name(.ControlCall)),
|
||||||
|
ByteView.from_slice("control.call")),
|
||||||
|
"control.call name");
|
||||||
|
@assert(ByteView.eq(ByteView.from_slice(kind_name(.DbgPanic)), ByteView.from_slice("dbg.panic")),
|
||||||
|
"dbg.panic name");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_alloc_and_node() {
|
||||||
|
var ir = new();
|
||||||
|
defer { drop(&ir); }
|
||||||
|
|
||||||
|
var fid = alloc_fn(&ir);
|
||||||
|
@assert(fid == 1, "first fn is 1");
|
||||||
|
var nid = alloc_node(&ir, fid);
|
||||||
|
@assert(nid == 1, "first node is 1");
|
||||||
|
var e = node(&ir, fid, nid);
|
||||||
|
@assert(e != null, "node lookup");
|
||||||
|
e.kind = .ArithAdd;
|
||||||
|
e.body = .Arith(.{ .tid = 2, .left = 1, .right = 1 });
|
||||||
|
var f = func(&ir, fid);
|
||||||
|
@assert(f != null && f.nodes.len == 2, "fn nodes");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_kinds();
|
||||||
|
test_alloc_and_node();
|
||||||
|
}
|
||||||
+1320
File diff suppressed because it is too large.
Load diff
+1405
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,475 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
// IR 构建器:单独成文件,以免 ast2ir 在同文件 @self 嵌套时无限递归。
|
||||||
|
|
||||||
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||||
|
type .{ .ByteBuffer } = @import("utils/byte_buffer.spl");
|
||||||
|
type .{ .Span, .span_none } = @import("./diag.spl");
|
||||||
|
type TypeTable = @import("./type_table.spl");
|
||||||
|
type Attr = @import("./attr.spl");
|
||||||
|
type Ir = @import("./ir.spl");
|
||||||
|
type .{ .IrKind, .ConstLit, .IrEntry, .IrFunc, .IrBody } = @import("./ir.spl");
|
||||||
|
|
||||||
|
fn list_at(v: Vec, i: usize): usize {
|
||||||
|
if i >= v.len { ret 0; }
|
||||||
|
ret ((v.data as *usize) + i).*;
|
||||||
|
}
|
||||||
|
|
||||||
|
type FixKind = enum { BrTrue, BrFalse, Jmp }
|
||||||
|
|
||||||
|
type Fix = struct {
|
||||||
|
node: usize,
|
||||||
|
slot: FixKind,
|
||||||
|
block: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
type IrBuilder = @self;
|
||||||
|
|
||||||
|
ir: Ir,
|
||||||
|
dbg: Span,
|
||||||
|
current_fn: usize,
|
||||||
|
current_block: usize,
|
||||||
|
blocks: Vec, // usize
|
||||||
|
fixups: Vec, // Fix
|
||||||
|
terminated: bool,
|
||||||
|
|
||||||
|
fn builder_alloc(self: *IrBuilder, kind: IrKind): usize {
|
||||||
|
var ref = Ir.alloc_node(&self.ir, self.current_fn);
|
||||||
|
if ref == 0 { ret 0; }
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null {
|
||||||
|
e.kind = kind;
|
||||||
|
e.dbg = self.dbg;
|
||||||
|
}
|
||||||
|
if self.current_block < self.blocks.len {
|
||||||
|
var slot = (self.blocks.data as *usize) + self.current_block;
|
||||||
|
if slot.* == 0 { slot.* = ref; }
|
||||||
|
}
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn builder_new(): IrBuilder {
|
||||||
|
ret IrBuilder {
|
||||||
|
.ir = Ir.new(),
|
||||||
|
.dbg = span_none(),
|
||||||
|
.current_fn = 0,
|
||||||
|
.current_block = 0,
|
||||||
|
.blocks = Vec.blank(@sizeof(usize), @alignof(usize)),
|
||||||
|
.fixups = Vec.blank(@sizeof(Fix), @alignof(Fix)),
|
||||||
|
.terminated = false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn builder_drop(self: *IrBuilder) {
|
||||||
|
Ir.drop(&self.ir);
|
||||||
|
Vec.drop(&self.blocks);
|
||||||
|
Vec.drop(&self.fixups);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fn_begin(self: *IrBuilder, name: ByteView, fn_tid: usize): usize {
|
||||||
|
if self.current_fn != 0 { fn_end(self); }
|
||||||
|
var fid = Ir.alloc_fn(&self.ir);
|
||||||
|
var f = Ir.func(&self.ir, fid);
|
||||||
|
if f != null {
|
||||||
|
f.name = name;
|
||||||
|
f.fn_tid = fn_tid;
|
||||||
|
}
|
||||||
|
self.current_fn = fid;
|
||||||
|
self.current_block = 0;
|
||||||
|
self.blocks.len = 0;
|
||||||
|
self.fixups.len = 0;
|
||||||
|
self.terminated = false;
|
||||||
|
ret fid;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fn_reserve(self: *IrBuilder, name: ByteView, fn_tid: usize): usize {
|
||||||
|
var fid = Ir.alloc_fn(&self.ir);
|
||||||
|
var f = Ir.func(&self.ir, fid);
|
||||||
|
if f != null {
|
||||||
|
f.name = name;
|
||||||
|
f.fn_tid = fn_tid;
|
||||||
|
}
|
||||||
|
ret fid;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fn_begin_at(self: *IrBuilder, fid: usize) {
|
||||||
|
if self.current_fn != 0 && self.current_fn != fid { fn_end(self); }
|
||||||
|
self.current_fn = fid;
|
||||||
|
self.current_block = 0;
|
||||||
|
self.blocks.len = 0;
|
||||||
|
self.fixups.len = 0;
|
||||||
|
self.terminated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fn_end(self: *IrBuilder) {
|
||||||
|
var k: usize = 0;
|
||||||
|
while k < self.fixups.len {
|
||||||
|
var fx = (self.fixups.data as *Fix) + k;
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, fx.node);
|
||||||
|
if e != null {
|
||||||
|
var target: usize = 0;
|
||||||
|
if fx.block < self.blocks.len { target = list_at(self.blocks, fx.block); }
|
||||||
|
if target != 0 {
|
||||||
|
match fx.slot {
|
||||||
|
.BrTrue => { match e.body { .Br(&b) => { b.true_label = target; }, _ => {} } },
|
||||||
|
.BrFalse => { match e.body { .Br(&b) => { b.false_label = target; }, _ => {} } },
|
||||||
|
.Jmp => { match e.body { .Jmp(&j) => { j.label = target; }, _ => {} } },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
k = k + 1;
|
||||||
|
}
|
||||||
|
self.current_block = 0;
|
||||||
|
self.blocks.len = 0;
|
||||||
|
self.fixups.len = 0;
|
||||||
|
self.terminated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_begin(self: *IrBuilder): usize {
|
||||||
|
var zero: usize = 0;
|
||||||
|
Vec.push(&self.blocks, &zero as *u8);
|
||||||
|
var block = self.blocks.len - 1;
|
||||||
|
self.current_block = block;
|
||||||
|
self.terminated = false;
|
||||||
|
ret block;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_end(self: *IrBuilder) {
|
||||||
|
// 空实现:本实现不做 terminated/空块校验(不终止进程)。
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn set_arith(self: *IrBuilder, kind: IrKind, tid: usize, left: usize, right: usize): usize {
|
||||||
|
var ref = builder_alloc(self, kind);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .Arith(.{ .tid = tid, .left = left, .right = right }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_cmp(self: *IrBuilder, kind: IrKind, tid: usize, a: usize, b: usize): usize {
|
||||||
|
var ref = builder_alloc(self, kind);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .Cmp(.{ .tid = tid, .a = a, .b = b }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_cast(self: *IrBuilder, kind: IrKind, from_tid: usize, to_tid: usize, val: usize): usize {
|
||||||
|
var ref = builder_alloc(self, kind);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .Cast(.{ .from_tid = from_tid, .to_tid = to_tid, .val = val }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arith_add(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithAdd, t, l, r); }
|
||||||
|
fn arith_sub(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithSub, t, l, r); }
|
||||||
|
fn arith_mul(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithMul, t, l, r); }
|
||||||
|
fn arith_div(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithDiv, t, l, r); }
|
||||||
|
fn arith_rem(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithRem, t, l, r); }
|
||||||
|
fn arith_and(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithAnd, t, l, r); }
|
||||||
|
fn arith_or(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithOr, t, l, r); }
|
||||||
|
fn arith_xor(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithXor, t, l, r); }
|
||||||
|
fn arith_shl(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithShl, t, l, r); }
|
||||||
|
fn arith_shr(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithShr, t, l, r); }
|
||||||
|
|
||||||
|
fn arith_neg(self: *IrBuilder, t: usize, v: usize): usize { ret set_arith(self, .ArithNeg, t, v, 0); }
|
||||||
|
fn arith_abs(self: *IrBuilder, t: usize, v: usize): usize { ret set_arith(self, .ArithAbs, t, v, 0); }
|
||||||
|
fn arith_not(self: *IrBuilder, t: usize, v: usize): usize { ret set_arith(self, .ArithNot, t, v, 0); }
|
||||||
|
|
||||||
|
fn cmp_eq(self: *IrBuilder, t: usize, a: usize, b: usize): usize { ret set_cmp(self, .CmpEq, t, a, b); }
|
||||||
|
fn cmp_ne(self: *IrBuilder, t: usize, a: usize, b: usize): usize { ret set_cmp(self, .CmpNe, t, a, b); }
|
||||||
|
fn cmp_lt(self: *IrBuilder, t: usize, a: usize, b: usize): usize { ret set_cmp(self, .CmpLt, t, a, b); }
|
||||||
|
fn cmp_le(self: *IrBuilder, t: usize, a: usize, b: usize): usize { ret set_cmp(self, .CmpLe, t, a, b); }
|
||||||
|
fn cmp_gt(self: *IrBuilder, t: usize, a: usize, b: usize): usize { ret set_cmp(self, .CmpGt, t, a, b); }
|
||||||
|
fn cmp_ge(self: *IrBuilder, t: usize, a: usize, b: usize): usize { ret set_cmp(self, .CmpGe, t, a, b); }
|
||||||
|
|
||||||
|
fn cast_trunc(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CastTrunc, f, t, v); }
|
||||||
|
fn cast_zext(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CastZext, f, t, v); }
|
||||||
|
fn cast_sext(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CastSext, f, t, v); }
|
||||||
|
fn cast_fext(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CastFext, f, t, v); }
|
||||||
|
fn cast_ftrunc(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CastFtrunc, f, t, v); }
|
||||||
|
fn cast_bitcast(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CastBitcast, f, t, v); }
|
||||||
|
fn cast_ptr2int(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CastPtr2int, f, t, v); }
|
||||||
|
fn cast_int2ptr(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CastInt2ptr, f, t, v); }
|
||||||
|
fn cast_bool2int(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CastBool2int, f, t, v); }
|
||||||
|
fn case_int2float(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CaseInt2float, f, t, v); }
|
||||||
|
fn case_float2int(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CaseFloat2int, f, t, v); }
|
||||||
|
|
||||||
|
|
||||||
|
fn mem_alloca(self: *IrBuilder, tid: usize, count: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .MemAlloca);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .MemAlloc(.{ .tid = tid, .count = count }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mem_global_alloc(self: *IrBuilder, tid: usize, const_node: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .MemGlobalAlloc);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .MemGlobalAlloc(.{ .tid = tid, .const_node = const_node }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mem_load(self: *IrBuilder, tid: usize, ptr: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .MemLoad);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .MemLoad(.{ .tid = tid, .ptr = ptr }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mem_store(self: *IrBuilder, tid: usize, ptr: usize, val: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .MemStore);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .MemStore(.{ .tid = tid, .ptr = ptr, .val = val }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mem_offset(self: *IrBuilder, tid: usize, ptr: usize, offset: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .MemOffset);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .MemOffset(.{ .tid = tid, .ptr = ptr, .offset = offset }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mem_field(self: *IrBuilder, tid: usize, agg: usize, field_idx: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .MemFieldPtr);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .MemFieldPtr(.{ .tid = tid, .agg = agg, .field_idx = field_idx }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mem_copy(self: *IrBuilder, tid: usize, dst: usize, src: usize, size: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .MemCopy);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .MemCopy(.{ .tid = tid, .dst = dst, .src = src, .size = size }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mem_set(self: *IrBuilder, tid: usize, dst: usize, val: usize, size: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .MemSet);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .MemSet(.{ .tid = tid, .dst = dst, .val = val, .size = size }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn const_node(self: *IrBuilder, tid: usize, lit: ConstLit): usize {
|
||||||
|
var ref = builder_alloc(self, .TypeConst);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .Const(.{ .tid = tid, .lit = lit }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_const_ptr(self: *IrBuilder, tid: usize, val: usize): usize { ret const_node(self, tid, .Ptr(val)); }
|
||||||
|
fn type_const_bool(self: *IrBuilder, tid: usize, val: bool): usize { ret const_node(self, tid, .Bool(val)); }
|
||||||
|
fn type_const_int(self: *IrBuilder, tid: usize, val: usize): usize { ret const_node(self, tid, .Int(val)); }
|
||||||
|
fn type_const_float(self: *IrBuilder, tid: usize, val: f64): usize { ret const_node(self, tid, .Float(val)); }
|
||||||
|
fn type_const_char(self: *IrBuilder, tid: usize, val: u8): usize { ret const_node(self, tid, .Char(val)); }
|
||||||
|
fn type_const_fn(self: *IrBuilder, tid: usize, val: usize): usize { ret const_node(self, tid, .Fn(val)); }
|
||||||
|
fn type_const_bytes(self: *IrBuilder, tid: usize, data: usize): usize { ret const_node(self, tid, .Ptr(data)); }
|
||||||
|
fn type_const_global(self: *IrBuilder, tid: usize, gidx: usize): usize { ret const_node(self, tid, .Global(gidx)); }
|
||||||
|
|
||||||
|
// 追加模块级全局变量;返回 gidx。bytes 所有权转移进 Ir。
|
||||||
|
fn add_global(self: *IrBuilder, tid: usize, zero: bool, bytes: Vec): usize {
|
||||||
|
ret Ir.add_global(&self.ir, tid, zero, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn type_bitsizeof(self: *IrBuilder, tid: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .TypeBitsizeof);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .TypeOp(.{ .tid = tid, .field_idx = 0 }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_sizeof(self: *IrBuilder, tid: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .TypeSizeof);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .TypeOp(.{ .tid = tid, .field_idx = 0 }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_alignof(self: *IrBuilder, tid: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .TypeAlignof);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .TypeOp(.{ .tid = tid, .field_idx = 0 }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_offsetof(self: *IrBuilder, tid: usize, field_idx: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .TypeOffsetof);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .TypeOp(.{ .tid = tid, .field_idx = field_idx }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_field_count(self: *IrBuilder, tid: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .TypeFieldCount);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .TypeOp(.{ .tid = tid, .field_idx = 0 }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn agg_construct(self: *IrBuilder, tid: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .AggConstruct);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null {
|
||||||
|
e.body = .AggConstruct(.{ .tid = tid, .fields = Vec.blank(@sizeof(usize), @alignof(usize)) });
|
||||||
|
}
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn agg_construct_field(self: *IrBuilder, node_ref: usize, field: usize) {
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, node_ref);
|
||||||
|
if e == null { ret; }
|
||||||
|
match e.body {
|
||||||
|
.AggConstruct(&a) => { Vec.push(&a.fields, &field as *u8); },
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn agg_extract(self: *IrBuilder, tid: usize, field_tid: usize, field_idx: usize, val: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .AggExtract);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null {
|
||||||
|
e.body = .AggExtract(.{ .tid = tid, .field_tid = field_tid, .field_idx = field_idx, .val = val });
|
||||||
|
}
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn agg_insert(self: *IrBuilder, tid: usize, field_idx: usize, agg: usize, field: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .AggInsert);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .AggInsert(.{ .tid = tid, .field_idx = field_idx, .agg = agg, .field = field }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn control_select(self: *IrBuilder, tid: usize, cond: usize, tv: usize, fv: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .ControlSelect);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .Select(.{ .tid = tid, .cond = cond, .true_val = tv, .false_val = fv }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn control_br(self: *IrBuilder, cond: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .ControlBr);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .Br(.{ .cond = cond, .true_label = 0, .false_label = 0 }); }
|
||||||
|
self.terminated = true;
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn control_br_fixup(self: *IrBuilder, ref: usize, true_block: usize, false_block: usize) {
|
||||||
|
var f1 = Fix { .node = ref, .slot = .BrTrue, .block = true_block };
|
||||||
|
Vec.push(&self.fixups, &f1 as *u8);
|
||||||
|
var f2 = Fix { .node = ref, .slot = .BrFalse, .block = false_block };
|
||||||
|
Vec.push(&self.fixups, &f2 as *u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn control_jmp(self: *IrBuilder): usize {
|
||||||
|
var ref = builder_alloc(self, .ControlJmp);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .Jmp(.{ .label = 0 }); }
|
||||||
|
self.terminated = true;
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn control_jmp_fixup(self: *IrBuilder, ref: usize, target_block: usize) {
|
||||||
|
var fx = Fix { .node = ref, .slot = .Jmp, .block = target_block };
|
||||||
|
Vec.push(&self.fixups, &fx as *u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn control_call(self: *IrBuilder, tid: usize, fn_ref: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .ControlCall);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null {
|
||||||
|
e.body = .Call(.{ .tid = tid, .func = fn_ref,
|
||||||
|
.params = Vec.blank(@sizeof(usize), @alignof(usize)) });
|
||||||
|
}
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn control_call_param(self: *IrBuilder, call_node: usize, arg: usize) {
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, call_node);
|
||||||
|
if e == null { ret; }
|
||||||
|
match e.body {
|
||||||
|
.Call(&c) => { Vec.push(&c.params, &arg as *u8); },
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn control_param(self: *IrBuilder, tid: usize, idx: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .ControlParam);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .Param(.{ .tid = tid, .idx = idx }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn control_ret(self: *IrBuilder, tid: usize, val: usize): usize {
|
||||||
|
var ref = builder_alloc(self, .ControlRet);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .Ret(.{ .tid = tid, .val = val }); }
|
||||||
|
self.terminated = true;
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn control_unreachable(self: *IrBuilder): usize {
|
||||||
|
var ref = builder_alloc(self, .ControlUnreachable);
|
||||||
|
self.terminated = true;
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn control_trap(self: *IrBuilder): usize {
|
||||||
|
var ref = builder_alloc(self, .ControlTrap);
|
||||||
|
self.terminated = true;
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dbg_breakpoint(self: *IrBuilder): usize {
|
||||||
|
ret builder_alloc(self, .DbgBreakpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dbg_panic(self: *IrBuilder, msg: ByteView): usize {
|
||||||
|
var ref = builder_alloc(self, .DbgPanic);
|
||||||
|
var e = Ir.node(&self.ir, self.current_fn, ref);
|
||||||
|
if e != null { e.body = .Panic(.{ .msg = msg }); }
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn test_builder_emit_and_dump() {
|
||||||
|
var b = builder_new();
|
||||||
|
defer { builder_drop(&b); }
|
||||||
|
|
||||||
|
b.dbg = span_none();
|
||||||
|
var fid = fn_begin(&b, ByteView.from_slice("f"), 3);
|
||||||
|
var blk = block_begin(&b);
|
||||||
|
var p = control_param(&b, 2, 0);
|
||||||
|
var c = type_const_int(&b, 2, 42);
|
||||||
|
var add = arith_add(&b, 2, p, c);
|
||||||
|
control_ret(&b, 2, add);
|
||||||
|
block_end(&b);
|
||||||
|
fn_end(&b);
|
||||||
|
|
||||||
|
var ty = TypeTable.new();
|
||||||
|
var t: TypeTable = undefined;
|
||||||
|
match ty { .Ok(v) => { t = v; }, .Err(e) => { @assert(false, "tt"); } }
|
||||||
|
defer { TypeTable.drop(&t); }
|
||||||
|
var at = Attr.new();
|
||||||
|
var a: Attr = undefined;
|
||||||
|
match at { .Ok(v) => { a = v; }, .Err(e) => { @assert(false, "attr"); } }
|
||||||
|
defer { Attr.drop(&a); }
|
||||||
|
|
||||||
|
var out = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&out); }
|
||||||
|
@assert(Ir.dump(&b.ir, &t, &a, &out), "dump ok");
|
||||||
|
var got = ByteBuffer.as_slice(out);
|
||||||
|
@assert(got.len > 0, "dump nonempty");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_builder_emit_and_dump();
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,349 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
// 目标布局:由 IR 类型表计算 size/align/字段位偏移,供 ir2vm 使用。默认 64 位平台,
|
||||||
|
// 聚合按最大字段对齐且不重排。
|
||||||
|
|
||||||
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
|
type TypeTable = @import("./type_table.spl");
|
||||||
|
type .{ .TypeNode, .Agg, .AggKind, .Member } = @import("./type_table.spl");
|
||||||
|
type .{ .node, .member_at, .make_int, .make_array, .make_agg, .push_member } = @import("./type_table.spl");
|
||||||
|
|
||||||
|
type LayoutInfo = struct {
|
||||||
|
size_bits: usize,
|
||||||
|
align_bits: usize,
|
||||||
|
field_bit_offsets: Vec, // usize
|
||||||
|
field_bit_sizes: Vec, // usize
|
||||||
|
resolved: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Layout = @self;
|
||||||
|
|
||||||
|
infos: Vec, // LayoutInfo
|
||||||
|
ptr_size: usize,
|
||||||
|
ptr_align: usize,
|
||||||
|
|
||||||
|
type LayoutResult = enum {
|
||||||
|
Ok: Layout,
|
||||||
|
Err: Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blank_usizes(): Vec {
|
||||||
|
ret Vec.blank(@sizeof(usize), @alignof(usize));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(): LayoutResult {
|
||||||
|
ret .Ok(Layout {
|
||||||
|
.infos = Vec.blank(@sizeof(LayoutInfo), @alignof(LayoutInfo)),
|
||||||
|
.ptr_size = @sizeof(usize),
|
||||||
|
.ptr_align = @alignof(usize),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn info_at(self: *Layout, id: usize): *LayoutInfo {
|
||||||
|
if id == 0 || id >= self.infos.len { ret null; }
|
||||||
|
ret (self.infos.data as *LayoutInfo) + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 未解析或越界返回 null。
|
||||||
|
fn info_of(self: *Layout, id: usize): *LayoutInfo {
|
||||||
|
var info = info_at(self, id);
|
||||||
|
if info == null || !info.resolved { ret null; }
|
||||||
|
ret info;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drop(self: *Layout) {
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < self.infos.len {
|
||||||
|
var info = info_at(self, i);
|
||||||
|
if info != null {
|
||||||
|
Vec.drop(&info.field_bit_offsets);
|
||||||
|
Vec.drop(&info.field_bit_sizes);
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
Vec.drop(&self.infos);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure(self: *Layout, n: usize) {
|
||||||
|
while self.infos.len < n {
|
||||||
|
var info = LayoutInfo {
|
||||||
|
.size_bits = 0,
|
||||||
|
.align_bits = 0,
|
||||||
|
.field_bit_offsets = blank_usizes(),
|
||||||
|
.field_bit_sizes = blank_usizes(),
|
||||||
|
.resolved = false,
|
||||||
|
};
|
||||||
|
match Vec.push(&self.infos, &info as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ret; },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_usize(v: *Vec, x: usize) {
|
||||||
|
Vec.push(v, &x as *u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usize_at(v: *Vec, i: usize): usize {
|
||||||
|
match Vec.get(v, i) {
|
||||||
|
.Ok(ptr) => { ret (ptr as *usize).*; },
|
||||||
|
.Err(error) => { ret 0; },
|
||||||
|
}
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标量对齐(字节);0 表示没有匹配的标量(即未落地)。
|
||||||
|
fn scalar_align(size_bytes: usize): usize {
|
||||||
|
if size_bytes == 1 { ret 1; }
|
||||||
|
if size_bytes == 2 { ret 2; }
|
||||||
|
if size_bytes == 4 { ret 4; }
|
||||||
|
if size_bytes == 8 { ret 8; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn round_up_bits(x: usize, align_bits: usize): usize {
|
||||||
|
if align_bits <= 1 { ret x; }
|
||||||
|
ret (x + align_bits - 1) / align_bits * align_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout_scalar(info: *LayoutInfo, size_bits: usize) {
|
||||||
|
var size_bytes = (size_bits + 7) / 8;
|
||||||
|
var a = scalar_align(size_bytes);
|
||||||
|
if a == 0 { ret; }
|
||||||
|
info.size_bits = size_bytes * 8;
|
||||||
|
info.align_bits = a * 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout_ptr(self: *Layout, info: *LayoutInfo) {
|
||||||
|
if self.ptr_size == 0 { ret; }
|
||||||
|
info.size_bits = self.ptr_size * 8;
|
||||||
|
info.align_bits = self.ptr_align * 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// enum 与 struct/union/shape 的布局。字段索引约定(与 ir2vm 一致):
|
||||||
|
// enum: 0 = tag,1..n = 各变体载荷(同一偏移);其余按声明顺序。
|
||||||
|
fn layout_agg(self: *Layout, is_enum: bool, is_union: bool, agg: Agg, info: *LayoutInfo) {
|
||||||
|
var n = agg.variants.len;
|
||||||
|
|
||||||
|
if is_enum {
|
||||||
|
if agg.tag_type == 0 { ret; }
|
||||||
|
var tag_info = info_at(self, agg.tag_type);
|
||||||
|
if tag_info == null || !tag_info.resolved { ret; }
|
||||||
|
|
||||||
|
var tag_align_bits = tag_info.align_bits;
|
||||||
|
if tag_align_bits == 0 { tag_align_bits = 8; }
|
||||||
|
var payload_align_bits: usize = 8;
|
||||||
|
var payload_size_bits: usize = 0;
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < n {
|
||||||
|
var m = member_at(&agg.variants, i);
|
||||||
|
if m != null && m.type_id != 0 {
|
||||||
|
var fi = info_at(self, m.type_id);
|
||||||
|
if fi == null || !fi.resolved { ret; }
|
||||||
|
var fa = fi.align_bits;
|
||||||
|
if fa == 0 { fa = 8; }
|
||||||
|
if fa > payload_align_bits { payload_align_bits = fa; }
|
||||||
|
if fi.size_bits > payload_size_bits { payload_size_bits = fi.size_bits; }
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var payload_off = round_up_bits(tag_info.size_bits, payload_align_bits);
|
||||||
|
var final_align = tag_align_bits;
|
||||||
|
if payload_align_bits > final_align { final_align = payload_align_bits; }
|
||||||
|
info.size_bits = round_up_bits(payload_off + payload_size_bits, final_align);
|
||||||
|
info.align_bits = final_align;
|
||||||
|
|
||||||
|
push_usize(&info.field_bit_offsets, 0);
|
||||||
|
push_usize(&info.field_bit_sizes, tag_info.size_bits);
|
||||||
|
i = 0;
|
||||||
|
while i < n {
|
||||||
|
var m = member_at(&agg.variants, i);
|
||||||
|
var fsize: usize = 0;
|
||||||
|
if m != null && m.type_id != 0 {
|
||||||
|
var fi = info_at(self, m.type_id);
|
||||||
|
if fi != null { fsize = fi.size_bits; }
|
||||||
|
}
|
||||||
|
push_usize(&info.field_bit_offsets, payload_off);
|
||||||
|
push_usize(&info.field_bit_sizes, fsize);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
info.size_bits = 0;
|
||||||
|
info.align_bits = 8;
|
||||||
|
ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
var max_align_bits: usize = 8;
|
||||||
|
var offset_bits: usize = 0;
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < n {
|
||||||
|
var m = member_at(&agg.variants, i);
|
||||||
|
var fsize_bits: usize = 0;
|
||||||
|
var fsize_align: usize = 8;
|
||||||
|
if m != null && m.type_id != 0 {
|
||||||
|
var fi = info_at(self, m.type_id);
|
||||||
|
if fi == null || !fi.resolved { ret; }
|
||||||
|
fsize_bits = fi.size_bits;
|
||||||
|
fsize_align = fi.align_bits;
|
||||||
|
if fsize_align == 0 { fsize_align = 8; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_union {
|
||||||
|
push_usize(&info.field_bit_offsets, 0);
|
||||||
|
push_usize(&info.field_bit_sizes, fsize_bits);
|
||||||
|
if fsize_bits > info.size_bits { info.size_bits = fsize_bits; }
|
||||||
|
} else {
|
||||||
|
offset_bits = round_up_bits(offset_bits, fsize_align);
|
||||||
|
push_usize(&info.field_bit_offsets, offset_bits);
|
||||||
|
push_usize(&info.field_bit_sizes, fsize_bits);
|
||||||
|
offset_bits = offset_bits + fsize_bits;
|
||||||
|
}
|
||||||
|
if fsize_align > max_align_bits { max_align_bits = fsize_align; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_union {
|
||||||
|
info.size_bits = round_up_bits(info.size_bits, max_align_bits);
|
||||||
|
} else {
|
||||||
|
info.size_bits = round_up_bits(offset_bits, max_align_bits);
|
||||||
|
}
|
||||||
|
info.align_bits = max_align_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_members(self: *Layout, tt: *TypeTable, agg: Agg) {
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < agg.variants.len {
|
||||||
|
var m = member_at(&agg.variants, i);
|
||||||
|
if m != null && m.type_id != 0 { resolve_one(self, tt, m.type_id); }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_one(self: *Layout, tt: *TypeTable, id: usize) {
|
||||||
|
var info = info_at(self, id);
|
||||||
|
if info == null || info.resolved { ret; }
|
||||||
|
var e = node(tt, id);
|
||||||
|
if e == null { ret; }
|
||||||
|
|
||||||
|
info.resolved = true;
|
||||||
|
|
||||||
|
match e.node {
|
||||||
|
.Error => { info.size_bits = 0; info.align_bits = 8; },
|
||||||
|
.Infer => { info.size_bits = 0; info.align_bits = 8; },
|
||||||
|
.Forall => { info.size_bits = 0; info.align_bits = 8; },
|
||||||
|
.Exists => { info.size_bits = 0; info.align_bits = 8; },
|
||||||
|
.Never => { info.size_bits = 0; info.align_bits = 8; },
|
||||||
|
.Unit => { info.size_bits = 0; info.align_bits = 8; },
|
||||||
|
.Any => { layout_ptr(self, info); },
|
||||||
|
.Bool => { layout_scalar(info, 8); },
|
||||||
|
.Int(x) => { layout_scalar(info, x.bits); },
|
||||||
|
.Float(x) => { layout_scalar(info, x.bits); },
|
||||||
|
.Ptr(x) => { layout_ptr(self, info); },
|
||||||
|
.Array(a) => {
|
||||||
|
resolve_one(self, tt, a.element);
|
||||||
|
var elem = info_at(self, a.element);
|
||||||
|
if elem == null || !elem.resolved { ret; }
|
||||||
|
info.size_bits = elem.size_bits * a.len;
|
||||||
|
info.align_bits = elem.align_bits;
|
||||||
|
},
|
||||||
|
.Struct(agg) => {
|
||||||
|
resolve_members(self, tt, agg);
|
||||||
|
layout_agg(self, false, false, agg, info);
|
||||||
|
},
|
||||||
|
.Union(agg) => {
|
||||||
|
resolve_members(self, tt, agg);
|
||||||
|
layout_agg(self, false, true, agg, info);
|
||||||
|
},
|
||||||
|
.Enum(agg) => {
|
||||||
|
if agg.tag_type != 0 { resolve_one(self, tt, agg.tag_type); }
|
||||||
|
resolve_members(self, tt, agg);
|
||||||
|
layout_agg(self, true, false, agg, info);
|
||||||
|
},
|
||||||
|
.Shape(agg) => {
|
||||||
|
resolve_members(self, tt, agg);
|
||||||
|
layout_agg(self, false, false, agg, info);
|
||||||
|
},
|
||||||
|
.Fn(f) => {
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < f.params.len {
|
||||||
|
resolve_one(self, tt, usize_at(&f.params, i));
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
resolve_one(self, tt, f.ret_type);
|
||||||
|
layout_ptr(self, info);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve(self: *Layout, tt: *TypeTable) {
|
||||||
|
ensure(self, tt.table.len);
|
||||||
|
var i: usize = 1;
|
||||||
|
while i < tt.table.len {
|
||||||
|
resolve_one(self, tt, i);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
var tt: TypeTable = undefined;
|
||||||
|
match TypeTable.new() {
|
||||||
|
.Ok(v) => { tt = v; },
|
||||||
|
.Err(error) => { @assert(false, "layout test: tt init"); ret; },
|
||||||
|
}
|
||||||
|
defer { TypeTable.drop(&tt); }
|
||||||
|
|
||||||
|
var i32t = make_int(&tt, 32, true);
|
||||||
|
var u8t = make_int(&tt, 8, false);
|
||||||
|
|
||||||
|
var members = Vec.blank(@sizeof(Member), @alignof(Member));
|
||||||
|
push_member(&members, "a", i32t);
|
||||||
|
push_member(&members, "b", u8t);
|
||||||
|
var agg = Agg { .mode = .Auto, .fixed_align_bits = 0, .tag_type = 0, .variants = members };
|
||||||
|
var sid = make_agg(&tt, .Struct, agg);
|
||||||
|
|
||||||
|
var layout: Layout = undefined;
|
||||||
|
match new() {
|
||||||
|
.Ok(v) => { layout = v; },
|
||||||
|
.Err(error) => { @assert(false, "layout test: layout init"); ret; },
|
||||||
|
}
|
||||||
|
defer { drop(&layout); }
|
||||||
|
|
||||||
|
resolve(&layout, &tt);
|
||||||
|
var info = info_of(&layout, sid);
|
||||||
|
if info == null {
|
||||||
|
@assert(false, "struct layout missing");
|
||||||
|
ret;
|
||||||
|
}
|
||||||
|
@assert(info.size_bits == 64, "struct size_bits");
|
||||||
|
@assert(info.align_bits == 32, "struct align_bits");
|
||||||
|
@assert(usize_at(&info.field_bit_offsets, 0) == 0, "field0 off");
|
||||||
|
@assert(usize_at(&info.field_bit_offsets, 1) == 32, "field1 off");
|
||||||
|
|
||||||
|
var arr = make_array(&tt, i32t, 3);
|
||||||
|
resolve(&layout, &tt);
|
||||||
|
var ainfo = info_of(&layout, arr);
|
||||||
|
if ainfo == null {
|
||||||
|
@assert(false, "array layout missing");
|
||||||
|
ret;
|
||||||
|
}
|
||||||
|
@assert(ainfo.size_bits == 96, "array size_bits");
|
||||||
|
@assert(ainfo.align_bits == 32, "array align_bits");
|
||||||
|
|
||||||
|
var pt = TypeTable.make_ptr(&tt, u8t);
|
||||||
|
resolve(&layout, &tt);
|
||||||
|
var pinfo = info_of(&layout, pt);
|
||||||
|
if pinfo == null {
|
||||||
|
@assert(false, "ptr layout missing");
|
||||||
|
ret;
|
||||||
|
}
|
||||||
|
@assert(pinfo.size_bits == @sizeof(usize) * 8, "ptr size_bits");
|
||||||
|
|
||||||
|
ret;
|
||||||
|
}
|
||||||
+4
-12
@@ -1,9 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// 词法分析器:字节流 -> Vec<Token>。镜像 stage1/spl_lexer.c 的行为:
|
// 字节流 -> Vec<Token>。行为镜像 splc0(保证 diff):行注释作为 token 产出,块注释
|
||||||
// 行注释作为 token 产出;块注释(可嵌套)整体跳过;换行产出 Endline;EOF 产出 Eof。
|
//(可嵌套)跳过,换行产 Endline,EOF 产 Eof。Token.text 借用输入,不复制。
|
||||||
// 复用 utils/ascii 做字符分类;整型/字符在词法期解码后放进 TokenKind 载荷。
|
|
||||||
// Token.text 借用输入字节,不复制。
|
|
||||||
|
|
||||||
type .{ .Vec, .VecResult } = @import("utils/vec.spl");
|
type .{ .Vec, .VecResult } = @import("utils/vec.spl");
|
||||||
type .{ .ByteView } = @import("utils/byte_view.spl");
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||||
@@ -80,7 +78,6 @@ fn decode_char(text: ByteView): u8 {
|
|||||||
ret e;
|
ret e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 读取辅助 ----
|
|
||||||
|
|
||||||
fn has2(self: *Lexer, ch: u8): bool {
|
fn has2(self: *Lexer, ch: u8): bool {
|
||||||
ret self.pos + 1 < self.src.len && self.src.ptr[self.pos + 1] == ch;
|
ret self.pos + 1 < self.src.len && self.src.ptr[self.pos + 1] == ch;
|
||||||
@@ -92,11 +89,8 @@ fn has3(self: *Lexer, ch1: u8, ch2: u8): bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sub(self: *Lexer, start: usize, end: usize): ByteView {
|
fn sub(self: *Lexer, start: usize, end: usize): ByteView {
|
||||||
match ByteView.slice(self.src, start, end) {
|
if start >= end { ret ByteView.empty(); }
|
||||||
.Ok(v) => { ret v; },
|
ret ByteView { .ptr = self.src.ptr + start, .len = end - start };
|
||||||
.Err(error) => { ret ByteView.empty(); },
|
|
||||||
}
|
|
||||||
ret ByteView.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 产出 token 并把 pos/col 推进到 end。Vec.push 失败视为致命(同 vec.make_vec 风格)。
|
// 产出 token 并把 pos/col 推进到 end。Vec.push 失败视为致命(同 vec.make_vec 风格)。
|
||||||
@@ -123,7 +117,6 @@ fn op3(self: *Lexer, out: *Vec, punct: Punct, start: usize, line: usize, col: us
|
|||||||
emit_to(self, out, .Punct(punct), start, start + 3, line, col);
|
emit_to(self, out, .Punct(punct), start, start + 3, line, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 复合扫描 ----
|
|
||||||
|
|
||||||
fn scan_int(self: *Lexer, out: *Vec, start: usize, line: usize, col: usize) {
|
fn scan_int(self: *Lexer, out: *Vec, start: usize, line: usize, col: usize) {
|
||||||
var p = self.pos;
|
var p = self.pos;
|
||||||
@@ -456,7 +449,6 @@ fn lex(src: ByteView, diag: *Diag, file: usize): VecResult {
|
|||||||
ret .Ok(out);
|
ret .Ok(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn view(text: []u8): ByteView {
|
fn view(text: []u8): ByteView {
|
||||||
ret ByteView.from_slice(text);
|
ret ByteView.from_slice(text);
|
||||||
|
|||||||
@@ -0,0 +1,788 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
// VM 字节码容器:内存中的 Prog 与 .sir 序列化(反序列化在 C 侧 spl_cli)。
|
||||||
|
// 字符串用 ByteView 借用、不拥有;gdata 条目为 ByteBuffer,由本模块拥有释放。
|
||||||
|
|
||||||
|
type .{ .Error, .Status, .make } = @import("utils/error.spl");
|
||||||
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||||
|
type .{ .ByteBuffer } = @import("utils/byte_buffer.spl");
|
||||||
|
type .{ .append_usize, .append_i64 } = @import("utils/fmt.spl");
|
||||||
|
type Mem = @import("utils/memory.spl");
|
||||||
|
type .{ .open_cstr, .write_all, .read_all, .File } = @import("utils/io.spl");
|
||||||
|
|
||||||
|
type Op = enum {
|
||||||
|
Error,
|
||||||
|
Push,
|
||||||
|
Dup,
|
||||||
|
Drop,
|
||||||
|
Swap,
|
||||||
|
Pick,
|
||||||
|
Rot,
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
DivS,
|
||||||
|
DivU,
|
||||||
|
RemS,
|
||||||
|
RemU,
|
||||||
|
Neg,
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
Xor,
|
||||||
|
Not,
|
||||||
|
Shl,
|
||||||
|
ShrU,
|
||||||
|
ShrS,
|
||||||
|
Eq,
|
||||||
|
Ne,
|
||||||
|
Slt,
|
||||||
|
Sle,
|
||||||
|
Ult,
|
||||||
|
Ule,
|
||||||
|
Sgt,
|
||||||
|
Sge,
|
||||||
|
Ugt,
|
||||||
|
Uge,
|
||||||
|
Trunc,
|
||||||
|
Sext,
|
||||||
|
Zext,
|
||||||
|
Jmp,
|
||||||
|
Bz,
|
||||||
|
Bnz,
|
||||||
|
Halt,
|
||||||
|
Call,
|
||||||
|
Calli,
|
||||||
|
Ret,
|
||||||
|
Alloc,
|
||||||
|
Laddr,
|
||||||
|
Gaddr,
|
||||||
|
Load,
|
||||||
|
Store,
|
||||||
|
Memcpy,
|
||||||
|
Ncall,
|
||||||
|
Nlib,
|
||||||
|
Bk,
|
||||||
|
Dbg,
|
||||||
|
}
|
||||||
|
|
||||||
|
type VmKind = enum {
|
||||||
|
Void,
|
||||||
|
Bool,
|
||||||
|
I8,
|
||||||
|
U8,
|
||||||
|
I16,
|
||||||
|
U16,
|
||||||
|
I32,
|
||||||
|
U32,
|
||||||
|
I64,
|
||||||
|
U64,
|
||||||
|
Isize,
|
||||||
|
Usize,
|
||||||
|
F32,
|
||||||
|
F64,
|
||||||
|
Ptr,
|
||||||
|
}
|
||||||
|
|
||||||
|
type VmIns = struct {
|
||||||
|
opcode: Op,
|
||||||
|
ty: VmKind,
|
||||||
|
imm: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
type VmFunc = struct {
|
||||||
|
name: ByteView,
|
||||||
|
idx_of_strtab: usize,
|
||||||
|
nargs: usize,
|
||||||
|
ninsns: usize,
|
||||||
|
address: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
type VmNative = struct {
|
||||||
|
name: ByteView,
|
||||||
|
idx_of_strtab: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Prog = @self;
|
||||||
|
|
||||||
|
insns: Vec, // VmIns
|
||||||
|
funcs: Vec, // VmFunc
|
||||||
|
natives: Vec, // VmNative
|
||||||
|
strtab: Vec, // ByteView
|
||||||
|
gdata: Vec, // ByteBuffer(索引 0 为占位项)
|
||||||
|
|
||||||
|
type ProgResult = enum {
|
||||||
|
Ok: Prog,
|
||||||
|
Err: Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn op_name(op: Op): []u8 {
|
||||||
|
match op {
|
||||||
|
.Error => { ret "error"; },
|
||||||
|
.Push => { ret "push"; },
|
||||||
|
.Dup => { ret "dup"; },
|
||||||
|
.Drop => { ret "drop"; },
|
||||||
|
.Swap => { ret "swap"; },
|
||||||
|
.Pick => { ret "pick"; },
|
||||||
|
.Rot => { ret "rot"; },
|
||||||
|
.Add => { ret "add"; },
|
||||||
|
.Sub => { ret "sub"; },
|
||||||
|
.Mul => { ret "mul"; },
|
||||||
|
.DivS => { ret "div_s"; },
|
||||||
|
.DivU => { ret "div_u"; },
|
||||||
|
.RemS => { ret "rem_s"; },
|
||||||
|
.RemU => { ret "rem_u"; },
|
||||||
|
.Neg => { ret "neg"; },
|
||||||
|
.And => { ret "and"; },
|
||||||
|
.Or => { ret "or"; },
|
||||||
|
.Xor => { ret "xor"; },
|
||||||
|
.Not => { ret "not"; },
|
||||||
|
.Shl => { ret "shl"; },
|
||||||
|
.ShrU => { ret "shr_u"; },
|
||||||
|
.ShrS => { ret "shr_s"; },
|
||||||
|
.Eq => { ret "eq"; },
|
||||||
|
.Ne => { ret "ne"; },
|
||||||
|
.Slt => { ret "slt"; },
|
||||||
|
.Sle => { ret "sle"; },
|
||||||
|
.Ult => { ret "ult"; },
|
||||||
|
.Ule => { ret "ule"; },
|
||||||
|
.Sgt => { ret "sgt"; },
|
||||||
|
.Sge => { ret "sge"; },
|
||||||
|
.Ugt => { ret "ugt"; },
|
||||||
|
.Uge => { ret "uge"; },
|
||||||
|
.Trunc => { ret "trunc"; },
|
||||||
|
.Sext => { ret "sext"; },
|
||||||
|
.Zext => { ret "zext"; },
|
||||||
|
.Jmp => { ret "jmp"; },
|
||||||
|
.Bz => { ret "bz"; },
|
||||||
|
.Bnz => { ret "bnz"; },
|
||||||
|
.Halt => { ret "halt"; },
|
||||||
|
.Call => { ret "call"; },
|
||||||
|
.Calli => { ret "calli"; },
|
||||||
|
.Ret => { ret "ret"; },
|
||||||
|
.Alloc => { ret "alloc"; },
|
||||||
|
.Laddr => { ret "laddr"; },
|
||||||
|
.Gaddr => { ret "gaddr"; },
|
||||||
|
.Load => { ret "load"; },
|
||||||
|
.Store => { ret "store"; },
|
||||||
|
.Memcpy => { ret "memcpy"; },
|
||||||
|
.Ncall => { ret "ncall"; },
|
||||||
|
.Nlib => { ret "nlib"; },
|
||||||
|
.Bk => { ret "breakpoint"; },
|
||||||
|
.Dbg => { ret "dbg"; },
|
||||||
|
}
|
||||||
|
ret "error";
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kind_name(k: VmKind): []u8 {
|
||||||
|
match k {
|
||||||
|
.Void => { ret "void"; },
|
||||||
|
.Bool => { ret "bool"; },
|
||||||
|
.I8 => { ret "i8"; },
|
||||||
|
.U8 => { ret "u8"; },
|
||||||
|
.I16 => { ret "i16"; },
|
||||||
|
.U16 => { ret "u16"; },
|
||||||
|
.I32 => { ret "i32"; },
|
||||||
|
.U32 => { ret "u32"; },
|
||||||
|
.I64 => { ret "i64"; },
|
||||||
|
.U64 => { ret "u64"; },
|
||||||
|
.Isize => { ret "isize"; },
|
||||||
|
.Usize => { ret "usize"; },
|
||||||
|
.F32 => { ret "f32"; },
|
||||||
|
.F64 => { ret "f64"; },
|
||||||
|
.Ptr => { ret "ptr"; },
|
||||||
|
}
|
||||||
|
ret "???";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn new(): ProgResult {
|
||||||
|
var prog = Prog {
|
||||||
|
.insns = Vec.blank(@sizeof(VmIns), @alignof(VmIns)),
|
||||||
|
.funcs = Vec.blank(@sizeof(VmFunc), @alignof(VmFunc)),
|
||||||
|
.natives = Vec.blank(@sizeof(VmNative), @alignof(VmNative)),
|
||||||
|
.strtab = Vec.blank(@sizeof(ByteView), @alignof(ByteView)),
|
||||||
|
.gdata = Vec.blank(@sizeof(ByteBuffer), @alignof(ByteBuffer)),
|
||||||
|
};
|
||||||
|
// 索引 0 占位,使真实数据地址非零。
|
||||||
|
var placeholder = ByteBuffer.new();
|
||||||
|
match Vec.push(&prog.gdata, &placeholder as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => {
|
||||||
|
Vec.drop(&prog.gdata);
|
||||||
|
Vec.drop(&prog.strtab);
|
||||||
|
Vec.drop(&prog.natives);
|
||||||
|
Vec.drop(&prog.funcs);
|
||||||
|
Vec.drop(&prog.insns);
|
||||||
|
ret .Err(error);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ret .Ok(prog);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gdata_at(self: *Prog, i: usize): *ByteBuffer {
|
||||||
|
match Vec.get(&self.gdata, i) {
|
||||||
|
.Ok(ptr) => { ret ptr as *ByteBuffer; },
|
||||||
|
.Err(error) => { ret null; },
|
||||||
|
}
|
||||||
|
ret null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn native_at(self: *Prog, i: usize): *VmNative {
|
||||||
|
match Vec.get(&self.natives, i) {
|
||||||
|
.Ok(ptr) => { ret ptr as *VmNative; },
|
||||||
|
.Err(error) => { ret null; },
|
||||||
|
}
|
||||||
|
ret null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drop(self: *Prog) {
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < self.gdata.len {
|
||||||
|
var e = gdata_at(self, i);
|
||||||
|
if e != null { ByteBuffer.drop(e); }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
Vec.drop(&self.gdata);
|
||||||
|
Vec.drop(&self.strtab);
|
||||||
|
Vec.drop(&self.natives);
|
||||||
|
Vec.drop(&self.funcs);
|
||||||
|
Vec.drop(&self.insns);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn add_instr(self: *Prog, op: Op, ty: VmKind, imm: usize): usize {
|
||||||
|
var i = self.insns.len;
|
||||||
|
if i < self.insns.cap {
|
||||||
|
var p = (self.insns.data as *VmIns) + i;
|
||||||
|
p.opcode = op;
|
||||||
|
p.ty = ty;
|
||||||
|
p.imm = imm;
|
||||||
|
self.insns.len = i + 1;
|
||||||
|
ret i + 1;
|
||||||
|
}
|
||||||
|
var ins = VmIns { .opcode = op, .ty = ty, .imm = imm };
|
||||||
|
if !Vec.push_raw(&self.insns, &ins as *u8) { ret 0; }
|
||||||
|
ret self.insns.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_func(self: *Prog, name: ByteView, nargs: usize, address: usize): usize {
|
||||||
|
var f = VmFunc {
|
||||||
|
.name = name,
|
||||||
|
.idx_of_strtab = 0,
|
||||||
|
.nargs = nargs,
|
||||||
|
.ninsns = 0,
|
||||||
|
.address = address,
|
||||||
|
};
|
||||||
|
match Vec.push(&self.funcs, &f as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ret 0; },
|
||||||
|
}
|
||||||
|
ret self.funcs.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制 size 字节到新的 gdata 条目;返回其索引(0 表示失败)。
|
||||||
|
// ptr 是上层(Ast/Ast2Ir)拥有的稳定字节,调用期间保持有效。
|
||||||
|
fn add_data(self: *Prog, ptr: usize, size: usize): usize {
|
||||||
|
var entry = ByteBuffer.new();
|
||||||
|
match ByteBuffer.reserve(&entry, size) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ByteBuffer.drop(&entry); ret 0; },
|
||||||
|
}
|
||||||
|
if size != 0 {
|
||||||
|
if ptr == 0 { ByteBuffer.drop(&entry); ret 0; }
|
||||||
|
Mem.copy(entry.storage.data as *any, ptr as *any, size);
|
||||||
|
entry.storage.len = size;
|
||||||
|
}
|
||||||
|
match Vec.push(&self.gdata, &entry as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ByteBuffer.drop(&entry); ret 0; },
|
||||||
|
}
|
||||||
|
ret self.gdata.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 追加一条 size 字节的零初始化全局数据(BSS 语义);返回其索引。
|
||||||
|
fn add_zero_data(self: *Prog, size: usize): usize {
|
||||||
|
var entry = ByteBuffer.new();
|
||||||
|
match ByteBuffer.reserve(&entry, size) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ByteBuffer.drop(&entry); ret 0; },
|
||||||
|
}
|
||||||
|
if size != 0 {
|
||||||
|
entry.storage.len = size;
|
||||||
|
var p = entry.storage.data as *u8;
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < size { p[i] = 0; i = i + 1; }
|
||||||
|
}
|
||||||
|
match Vec.push(&self.gdata, &entry as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ByteBuffer.drop(&entry); ret 0; },
|
||||||
|
}
|
||||||
|
ret self.gdata.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回 name 对应的 native 索引;不存在则登记。name 为空指针时返回 -1。
|
||||||
|
fn get_or_add_native(self: *Prog, name: ByteView): isize {
|
||||||
|
if name.ptr == null { ret -1; }
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < self.natives.len {
|
||||||
|
var n = native_at(self, i);
|
||||||
|
if n != null && ByteView.eq(n.name, name) { ret i as isize; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
var nat = VmNative { .name = name, .idx_of_strtab = 0 };
|
||||||
|
match Vec.push(&self.natives, &nat as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ret -1; },
|
||||||
|
}
|
||||||
|
ret (self.natives.len - 1) as isize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn out_s(out: *ByteBuffer, s: []u8): bool {
|
||||||
|
match ByteBuffer.append(out, s) { .Ok => { ret true; }, .Err(error) => { ret false; } }
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn out_u(out: *ByteBuffer, v: usize): bool {
|
||||||
|
match append_usize(out, v, 10, false) { .Ok => { ret true; }, .Err(error) => { ret false; } }
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn out_i(out: *ByteBuffer, v: i64): bool {
|
||||||
|
match append_i64(out, v, 10, false) { .Ok => { ret true; }, .Err(error) => { ret false; } }
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// %4zd:右对齐、最小宽度 4 的有符号十进制。
|
||||||
|
fn out_width4_i(out: *ByteBuffer, v: i64): bool {
|
||||||
|
var width: usize = 0;
|
||||||
|
if v < 0 {
|
||||||
|
if v >= -9 { width = 2; }
|
||||||
|
else if v >= -99 { width = 3; }
|
||||||
|
else { width = 4; }
|
||||||
|
} else {
|
||||||
|
if v < 10 { width = 1; }
|
||||||
|
else if v < 100 { width = 2; }
|
||||||
|
else if v < 1000 { width = 3; }
|
||||||
|
else { width = 4; }
|
||||||
|
}
|
||||||
|
var pad: usize = 0;
|
||||||
|
if width < 4 { pad = 4 - width; }
|
||||||
|
if !out_s(out, " "[0..pad]) { ret false; }
|
||||||
|
ret out_i(out, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump(self: *Prog, out: *ByteBuffer): bool {
|
||||||
|
if !out_s(out, ";; SPL bytecode dump: \n") { ret false; }
|
||||||
|
if !out_s(out, ";; ninsns=") { ret false; }
|
||||||
|
if !out_u(out, self.insns.len) { ret false; }
|
||||||
|
if !out_s(out, " nfuncs=") { ret false; }
|
||||||
|
if !out_u(out, self.funcs.len) { ret false; }
|
||||||
|
if !out_s(out, " nnatives=") { ret false; }
|
||||||
|
if !out_u(out, self.natives.len) { ret false; }
|
||||||
|
if !out_s(out, " nstrs=") { ret false; }
|
||||||
|
if !out_u(out, self.strtab.len) { ret false; }
|
||||||
|
if !out_s(out, "\n\n") { ret false; }
|
||||||
|
|
||||||
|
if self.funcs.len > 0 {
|
||||||
|
if !out_s(out, ";; --- functions ---\n") { ret false; }
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < self.funcs.len {
|
||||||
|
var f = func_at(self, i);
|
||||||
|
if f == null {
|
||||||
|
@assert(false, "func missing in dump");
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
if !out_s(out, " ") { ret false; }
|
||||||
|
if !out_s(out, ByteView.as_slice(f.name)) { ret false; }
|
||||||
|
if !out_s(out, " nargs=") { ret false; }
|
||||||
|
if !out_u(out, f.nargs) { ret false; }
|
||||||
|
if !out_s(out, " ninsns=") { ret false; }
|
||||||
|
if !out_u(out, f.ninsns) { ret false; }
|
||||||
|
if !out_s(out, " addr=") { ret false; }
|
||||||
|
if !out_u(out, f.address) { ret false; }
|
||||||
|
if !out_s(out, "\n") { ret false; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
if !out_s(out, "\n") { ret false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.natives.len > 0 {
|
||||||
|
if !out_s(out, ";; --- natives ---\n") { ret false; }
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < self.natives.len {
|
||||||
|
var n = native_at(self, i);
|
||||||
|
if n == null {
|
||||||
|
@assert(false, "native missing in dump");
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
if !out_s(out, " ") { ret false; }
|
||||||
|
if !out_s(out, ByteView.as_slice(n.name)) { ret false; }
|
||||||
|
if !out_s(out, "\n") { ret false; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
if !out_s(out, "\n") { ret false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.strtab.len > 0 {
|
||||||
|
if !out_s(out, ";; --- string table ---\n") { ret false; }
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < self.strtab.len {
|
||||||
|
var s = str_at(self, i);
|
||||||
|
if !out_s(out, " ") { ret false; }
|
||||||
|
if !out_i(out, i as i64) { ret false; }
|
||||||
|
if !out_s(out, ": \"") { ret false; }
|
||||||
|
if !out_s(out, ByteView.as_slice(s)) { ret false; }
|
||||||
|
if !out_s(out, "\"\n") { ret false; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
if !out_s(out, "\n") { ret false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if !out_s(out, ";; --- instructions ---\n") { ret false; }
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < self.insns.len {
|
||||||
|
var ins = ins_at(self, i);
|
||||||
|
if !out_width4_i(out, i as i64) { ret false; }
|
||||||
|
if !out_s(out, ": ") { ret false; }
|
||||||
|
if !out_s(out, op_name(ins.opcode)) { ret false; }
|
||||||
|
if @enum2int(ins.ty) != @enum2int(VmKind.Void) {
|
||||||
|
if !out_s(out, " ") { ret false; }
|
||||||
|
if !out_s(out, kind_name(ins.ty)) { ret false; }
|
||||||
|
}
|
||||||
|
if !out_s(out, " ") { ret false; }
|
||||||
|
if !out_i(out, ins.imm as i64) { ret false; }
|
||||||
|
|
||||||
|
match ins.opcode {
|
||||||
|
.Jmp, .Bz, .Bnz => {
|
||||||
|
if !out_s(out, " ; -> ") { ret false; }
|
||||||
|
if !out_u(out, i + 1 + ins.imm) { ret false; }
|
||||||
|
},
|
||||||
|
.Call => {
|
||||||
|
if !out_s(out, " ; nargs=") { ret false; }
|
||||||
|
if !out_u(out, ins.imm) { ret false; }
|
||||||
|
if !out_s(out, ", from stack") { ret false; }
|
||||||
|
},
|
||||||
|
.Calli => { if !out_s(out, " ; indirect call") { ret false; } },
|
||||||
|
.Gaddr => {
|
||||||
|
if !out_s(out, " ; gdata[") { ret false; }
|
||||||
|
if !out_u(out, ins.imm) { ret false; }
|
||||||
|
if !out_s(out, "]") { ret false; }
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
if !out_s(out, "\n") { ret false; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ins_at(self: *Prog, i: usize): VmIns {
|
||||||
|
if i >= self.insns.len { ret VmIns { .opcode = .Error, .ty = .Void, .imm = 0 }; }
|
||||||
|
ret ((self.insns.data as *VmIns) + i).*;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func_at(self: *Prog, i: usize): *VmFunc {
|
||||||
|
match Vec.get(&self.funcs, i) {
|
||||||
|
.Ok(ptr) => { ret ptr as *VmFunc; },
|
||||||
|
.Err(error) => { ret null; },
|
||||||
|
}
|
||||||
|
ret null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn str_at(self: *Prog, i: usize): ByteView {
|
||||||
|
match Vec.get(&self.strtab, i) {
|
||||||
|
.Ok(ptr) => { ret (ptr as *ByteView).*; },
|
||||||
|
.Err(error) => { ret ByteView.empty(); },
|
||||||
|
}
|
||||||
|
ret ByteView.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn align8(n: usize): usize {
|
||||||
|
ret (n + 7) & (~(7 as usize));
|
||||||
|
}
|
||||||
|
|
||||||
|
// put_* 直接写入已预留容量的 ByteBuffer 存储(store 已按精确总大小 reserve),跳过
|
||||||
|
// append 的校验/拷贝;容量不足会越界,故 store 先校验容量。
|
||||||
|
|
||||||
|
fn put_u8(out: *ByteBuffer, b: u8): bool {
|
||||||
|
out.storage.data[out.storage.len] = b;
|
||||||
|
out.storage.len = out.storage.len + 1;
|
||||||
|
ret true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接按小端整型写(x86 小端;VM 的 STORE 用 memcpy,未对齐也安全)。
|
||||||
|
fn put_u16(out: *ByteBuffer, v: usize): bool {
|
||||||
|
var d = out.storage.data + out.storage.len;
|
||||||
|
(d as *u16).* = v as u16;
|
||||||
|
out.storage.len = out.storage.len + 2;
|
||||||
|
ret true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn put_u64(out: *ByteBuffer, v: usize): bool {
|
||||||
|
var d = out.storage.data + out.storage.len;
|
||||||
|
(d as *u64).* = v as u64;
|
||||||
|
out.storage.len = out.storage.len + 8;
|
||||||
|
ret true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn put_zeros(out: *ByteBuffer, n: usize): bool {
|
||||||
|
if n != 0 {
|
||||||
|
Mem.set((out.storage.data + out.storage.len) as *any, 0, n);
|
||||||
|
out.storage.len = out.storage.len + n;
|
||||||
|
}
|
||||||
|
ret true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn put_view(out: *ByteBuffer, v: ByteView): bool {
|
||||||
|
if v.len == 0 { ret true; }
|
||||||
|
Mem.copy((out.storage.data + out.storage.len) as *any, v.ptr as *any, v.len);
|
||||||
|
out.storage.len = out.storage.len + v.len;
|
||||||
|
ret true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入「带结尾 NUL、再按 8 对齐」的名称字段(对应 C 侧 nlen = strlen+1)。
|
||||||
|
fn put_name_field(out: *ByteBuffer, name: ByteView): bool {
|
||||||
|
var nlen = name.len + 1;
|
||||||
|
if !put_u64(out, nlen) { ret false; }
|
||||||
|
if !put_view(out, name) { ret false; }
|
||||||
|
if !put_u8(out, 0) { ret false; }
|
||||||
|
ret put_zeros(out, align8(nlen) - nlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 把 prog 序列化为二进制写入 out(不落盘,供内嵌 VM 直接用)。
|
||||||
|
fn encode(self: *Prog, out: *ByteBuffer): Status {
|
||||||
|
// 精确预估输出大小并一次性预留,避免大文件逐字节增长导致的重复拷贝。
|
||||||
|
var total: usize = 48;
|
||||||
|
var fi: usize = 0;
|
||||||
|
var func_base = self.funcs.data as *VmFunc;
|
||||||
|
var nat_base = self.natives.data as *VmNative;
|
||||||
|
var str_base = self.strtab.data as *ByteView;
|
||||||
|
var gd_base = self.gdata.data as *ByteBuffer;
|
||||||
|
while fi < self.funcs.len {
|
||||||
|
var f = func_base + fi;
|
||||||
|
var nlen = f.name.len + 1;
|
||||||
|
total = total + 8 + align8(nlen) + 32;
|
||||||
|
fi = fi + 1;
|
||||||
|
}
|
||||||
|
total = total + self.insns.len * 12;
|
||||||
|
fi = 0;
|
||||||
|
while fi < self.natives.len {
|
||||||
|
var n = nat_base + fi;
|
||||||
|
var nlen = n.name.len + 1;
|
||||||
|
total = total + 8 + align8(nlen) + 8;
|
||||||
|
fi = fi + 1;
|
||||||
|
}
|
||||||
|
fi = 0;
|
||||||
|
while fi < self.strtab.len {
|
||||||
|
var s = str_base + fi;
|
||||||
|
total = total + 8 + align8(s.len);
|
||||||
|
fi = fi + 1;
|
||||||
|
}
|
||||||
|
fi = 1;
|
||||||
|
while fi < self.gdata.len {
|
||||||
|
var e = gd_base + fi;
|
||||||
|
total = total + 8 + align8(e.storage.len);
|
||||||
|
fi = fi + 1;
|
||||||
|
}
|
||||||
|
ByteBuffer.reserve(out, total);
|
||||||
|
if out.storage.cap < total || out.storage.data == null { ret .Err(make(.Io)); }
|
||||||
|
|
||||||
|
if !put_view(out, ByteView.from_slice("SPLBIN")) { ret .Err(make(.Io)); }
|
||||||
|
if !put_zeros(out, 2) { ret .Err(make(.Io)); }
|
||||||
|
|
||||||
|
var nfuncs = self.funcs.len;
|
||||||
|
var ninsns = self.insns.len;
|
||||||
|
var nnatives = self.natives.len;
|
||||||
|
var nstrs = self.strtab.len;
|
||||||
|
var ndata: usize = 0;
|
||||||
|
if self.gdata.len > 0 { ndata = self.gdata.len - 1; }
|
||||||
|
|
||||||
|
if !put_u64(out, nfuncs) { ret .Err(make(.Io)); }
|
||||||
|
if !put_u64(out, ninsns) { ret .Err(make(.Io)); }
|
||||||
|
if !put_u64(out, nnatives) { ret .Err(make(.Io)); }
|
||||||
|
if !put_u64(out, nstrs) { ret .Err(make(.Io)); }
|
||||||
|
if !put_u64(out, ndata) { ret .Err(make(.Io)); }
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < nfuncs {
|
||||||
|
var f = func_base + i;
|
||||||
|
if !put_name_field(out, f.name) { ret .Err(make(.Io)); }
|
||||||
|
if !put_u64(out, f.idx_of_strtab) { ret .Err(make(.Io)); }
|
||||||
|
if !put_u64(out, f.nargs) { ret .Err(make(.Io)); }
|
||||||
|
if !put_u64(out, f.ninsns) { ret .Err(make(.Io)); }
|
||||||
|
if !put_u64(out, f.address) { ret .Err(make(.Io)); }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
var ins_base = self.insns.data as *VmIns;
|
||||||
|
var out_base = out.storage.data;
|
||||||
|
var out_len = out.storage.len;
|
||||||
|
while i < ninsns {
|
||||||
|
var p = ins_base + i;
|
||||||
|
var d = out_base + out_len;
|
||||||
|
(d as *u16).* = @enum2int(p.opcode) as u16;
|
||||||
|
((d + 2) as *u16).* = @enum2int(p.ty) as u16;
|
||||||
|
((d + 4) as *u64).* = p.imm as u64;
|
||||||
|
out_len = out_len + 12;
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
out.storage.len = out_len;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while i < nnatives {
|
||||||
|
var n = nat_base + i;
|
||||||
|
if !put_name_field(out, n.name) { ret .Err(make(.Io)); }
|
||||||
|
if !put_u64(out, n.idx_of_strtab) { ret .Err(make(.Io)); }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while i < nstrs {
|
||||||
|
var s = str_base + i;
|
||||||
|
if !put_u64(out, s.len) { ret .Err(make(.Io)); }
|
||||||
|
if !put_view(out, s.*) { ret .Err(make(.Io)); }
|
||||||
|
if !put_zeros(out, align8(s.len) - s.len) { ret .Err(make(.Io)); }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 1;
|
||||||
|
while i < self.gdata.len {
|
||||||
|
var e = gd_base + i;
|
||||||
|
var dsize = e.storage.len;
|
||||||
|
if !put_u64(out, dsize) { ret .Err(make(.Io)); }
|
||||||
|
if !put_view(out, ByteView.from_slice(ByteBuffer.as_slice(e.*))) { ret .Err(make(.Io)); }
|
||||||
|
if !put_zeros(out, align8(dsize) - dsize) { ret .Err(make(.Io)); }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret .Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store(self: *Prog, path: ByteView): Status {
|
||||||
|
var out = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&out); }
|
||||||
|
|
||||||
|
match encode(self, &out) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ret .Err(error); },
|
||||||
|
}
|
||||||
|
|
||||||
|
var f = open_cstr(path, ByteView.from_slice("wb"));
|
||||||
|
if !File.valid(&f) { ret .Err(make(.Io)); }
|
||||||
|
var w = write_all(&f, ByteBuffer.as_slice(out));
|
||||||
|
var closed = File.close(&f);
|
||||||
|
match w {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ret .Err(error); },
|
||||||
|
}
|
||||||
|
if closed != 0 { ret .Err(make(.Io)); }
|
||||||
|
ret .Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 序列化到内存缓冲(供内嵌 VM 直接装载)。
|
||||||
|
fn serialize(self: *Prog, out: *ByteBuffer): Status {
|
||||||
|
ret encode(self, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn test_dump_basic() {
|
||||||
|
var pr: Prog = undefined;
|
||||||
|
match new() {
|
||||||
|
.Ok(v) => { pr = v; },
|
||||||
|
.Err(error) => { @assert(false, "prog init"); ret; },
|
||||||
|
}
|
||||||
|
defer { drop(&pr); }
|
||||||
|
|
||||||
|
add_instr(&pr, .Halt, .Void, 0);
|
||||||
|
|
||||||
|
var out = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&out); }
|
||||||
|
if !dump(&pr, &out) { @assert(false, "dump"); ret; }
|
||||||
|
|
||||||
|
var expected: [69]u8 = .[
|
||||||
|
';', ';', ' ', 'S', 'P', 'L', ' ', 'b',
|
||||||
|
'y', 't', 'e', 'c', 'o', 'd', 'e', ' ',
|
||||||
|
'd', 'u', 'm', 'p', ':', ' ', 10,
|
||||||
|
';', ';', ' ', 'n', 'i', 'n', 's', 'n',
|
||||||
|
's', '=', '1', ' ', 'n', 'f', 'u', 'n',
|
||||||
|
'c', 's', '=', '0', ' ', 'n', 'n', 'a',
|
||||||
|
't', 'i', 'v', 'e', 's', '=', '0', ' ',
|
||||||
|
'n', 's', 't', 'r', 's', '=', '0', 10, 10,
|
||||||
|
';', ';', ' ', '-', '-', '-',
|
||||||
|
];
|
||||||
|
var got = ByteBuffer.as_slice(out);
|
||||||
|
@assert(got.len > expected.len, "dump longer than header prefix");
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < expected.len {
|
||||||
|
@assert(got.ptr[i] == expected[i], "dump header bytes");
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_native_dedup() {
|
||||||
|
var pr: Prog = undefined;
|
||||||
|
match new() {
|
||||||
|
.Ok(v) => { pr = v; },
|
||||||
|
.Err(error) => { @assert(false, "prog init"); ret; },
|
||||||
|
}
|
||||||
|
defer { drop(&pr); }
|
||||||
|
|
||||||
|
var vm = ByteView.from_slice("vm_printf");
|
||||||
|
@assert(get_or_add_native(&pr, vm) == 0, "first native idx");
|
||||||
|
@assert(get_or_add_native(&pr, vm) == 0, "dedup native idx");
|
||||||
|
@assert(pr.natives.len == 1, "native count");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_store_roundtrip() {
|
||||||
|
var pr: Prog = undefined;
|
||||||
|
match new() {
|
||||||
|
.Ok(v) => { pr = v; },
|
||||||
|
.Err(error) => { @assert(false, "prog init"); ret; },
|
||||||
|
}
|
||||||
|
defer { drop(&pr); }
|
||||||
|
|
||||||
|
add_func(&pr, ByteView.from_slice("main"), 0, 0);
|
||||||
|
add_instr(&pr, .Halt, .Void, 0);
|
||||||
|
|
||||||
|
var path = ByteView.from_slice("build/_mcode_roundtrip.tmp");
|
||||||
|
match store(&pr, path) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { @assert(false, "store to file"); ret; },
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&data); }
|
||||||
|
match read_all(path, &data) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { @assert(false, "read back"); ret; },
|
||||||
|
}
|
||||||
|
var b = ByteBuffer.as_slice(data);
|
||||||
|
// header(48) + 一个函数项(8 + 8 + 4*8 = 48) + 一条指令(12) = 108
|
||||||
|
@assert(b.len == 108, "serialized size");
|
||||||
|
@assert(b.ptr[0] == 'S' && b.ptr[1] == 'P' && b.ptr[2] == 'L' && b.ptr[3] == 'B' &&
|
||||||
|
b.ptr[4] == 'I' && b.ptr[5] == 'N' && b.ptr[6] == 0 && b.ptr[7] == 0,
|
||||||
|
"magic");
|
||||||
|
@assert(b.ptr[8] == 1, "nfuncs");
|
||||||
|
@assert(b.ptr[16] == 1, "ninsns");
|
||||||
|
@assert(b.ptr[24] == 0 && b.ptr[32] == 0 && b.ptr[40] == 0, "nnatives/nstrs/ndata");
|
||||||
|
// 函数项起始:name_len(5) + "main\0" + pad(3)
|
||||||
|
@assert(b.ptr[48] == 5, "name_len");
|
||||||
|
@assert(b.ptr[56] == 'm' && b.ptr[57] == 'a' && b.ptr[58] == 'i' && b.ptr[59] == 'n' &&
|
||||||
|
b.ptr[60] == 0, "name bytes");
|
||||||
|
@assert(b.ptr[61] == 0 && b.ptr[62] == 0 && b.ptr[63] == 0, "name padding");
|
||||||
|
// 指令:opcode(2)=halt(38) type(2)=void(0) imm(8)=0
|
||||||
|
var ins_off: usize = 96;
|
||||||
|
@assert(b.ptr[ins_off + 0] == 38 && b.ptr[ins_off + 1] == 0, "opcode bytes");
|
||||||
|
@assert(b.ptr[ins_off + 2] == 0 && b.ptr[ins_off + 3] == 0, "type bytes");
|
||||||
|
@assert(b.ptr[ins_off + 4] == 0 && b.ptr[ins_off + 11] == 0, "imm bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_dump_basic();
|
||||||
|
test_native_dedup();
|
||||||
|
test_store_roundtrip();
|
||||||
|
}
|
||||||
+389
-133
@@ -1,7 +1,6 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// Parser:token 流 -> Ast(ADT 节点)。C 版只作控制流参考,节点分配顺序需与 stage1
|
// token 流 -> Ast。节点分配顺序必须与 splc0 一致,否则 dump 的 #ref 无法逐字节对比。
|
||||||
// 一致,以保证 dump 的 #ref 可对比。pos 在 token 向量上移动,自动跳过 trivia。
|
|
||||||
|
|
||||||
type .{ .Vec } = @import("utils/vec.spl");
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
type .{ .ByteView } = @import("utils/byte_view.spl");
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||||
@@ -25,6 +24,7 @@ failed: bool,
|
|||||||
stop_agg: i32,
|
stop_agg: i32,
|
||||||
error_count: usize,
|
error_count: usize,
|
||||||
file: usize, // 当前源文件在 Sources 中的下标
|
file: usize, // 当前源文件在 Sources 中的下标
|
||||||
|
comptime_depth: usize, // >0 表示处于 comptime 上下文(类型可作为表达式)
|
||||||
|
|
||||||
fn new(ast: *Ast, toks: Vec, diag: *Diag): Parser {
|
fn new(ast: *Ast, toks: Vec, diag: *Diag): Parser {
|
||||||
ret Parser {
|
ret Parser {
|
||||||
@@ -36,17 +36,14 @@ fn new(ast: *Ast, toks: Vec, diag: *Diag): Parser {
|
|||||||
.stop_agg = 0,
|
.stop_agg = 0,
|
||||||
.error_count = 0,
|
.error_count = 0,
|
||||||
.file = 0,
|
.file = 0,
|
||||||
|
.comptime_depth = 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- token 访问 ----
|
|
||||||
|
|
||||||
fn token_ptr(self: *Parser, index: usize): *Token {
|
fn token_ptr(self: *Parser, index: usize): *Token {
|
||||||
match Vec.get(&self.toks, index) {
|
if index >= self.toks.len { ret null; }
|
||||||
.Ok(ptr) => { ret ptr as *Token; },
|
ret (self.toks.data as *Token) + index;
|
||||||
.Err(error) => { ret null; },
|
|
||||||
}
|
|
||||||
ret null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_trivia(kind: TokenKind): bool {
|
fn is_trivia(kind: TokenKind): bool {
|
||||||
@@ -61,16 +58,32 @@ fn is_trivia(kind: TokenKind): bool {
|
|||||||
// 跳过 trivia;返回当前 token(含 EOF)。越界返回 null。
|
// 跳过 trivia;返回当前 token(含 EOF)。越界返回 null。
|
||||||
fn peek(self: *Parser): *Token {
|
fn peek(self: *Parser): *Token {
|
||||||
var n = self.toks.len;
|
var n = self.toks.len;
|
||||||
|
var base = self.toks.data as *Token;
|
||||||
var i = self.pos;
|
var i = self.pos;
|
||||||
while i < n {
|
while i < n {
|
||||||
var t = token_ptr(self, i);
|
if base == null { break; }
|
||||||
if t == null { break; }
|
var t = base + i;
|
||||||
if !is_trivia(t.kind) { break; }
|
if !is_trivia(t.kind) { break; }
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
self.pos = i;
|
self.pos = i;
|
||||||
if i >= n { ret null; }
|
if i >= n || base == null { ret null; }
|
||||||
ret token_ptr(self, i);
|
ret base + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳过 trivia,返回当前 token 之后的下一个非 trivia token(不移动 pos)。
|
||||||
|
fn peek_next(self: *Parser): *Token {
|
||||||
|
var n = self.toks.len;
|
||||||
|
var base = self.toks.data as *Token;
|
||||||
|
var i = self.pos + 1;
|
||||||
|
while i < n {
|
||||||
|
if base == null { break; }
|
||||||
|
var t = base + i;
|
||||||
|
if !is_trivia(t.kind) { break; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
if i >= n || base == null { ret null; }
|
||||||
|
ret base + i;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn advance(self: *Parser): *Token {
|
fn advance(self: *Parser): *Token {
|
||||||
@@ -123,7 +136,6 @@ fn at_eof(self: *Parser): bool {
|
|||||||
ret false;
|
ret false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 错误 ----
|
|
||||||
|
|
||||||
fn span_of(self: *Parser, tok: *Token): Span {
|
fn span_of(self: *Parser, tok: *Token): Span {
|
||||||
if tok == null { ret span_none(); }
|
if tok == null { ret span_none(); }
|
||||||
@@ -136,6 +148,46 @@ fn parse_error(self: *Parser, tok: *Token, what: []u8) {
|
|||||||
Diag.err(self.diag, span_of(self, tok), .ParseExpected, what);
|
Diag.err(self.diag, span_of(self, tok), .ParseExpected, what);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 转义解码:去掉引号,处理 \n \t \r \0 \\ \' \" \xHH。
|
||||||
|
fn hex_digit(c: u8): u8 {
|
||||||
|
if c >= '0' && c <= '9' { ret c - '0'; }
|
||||||
|
if c >= 'a' && c <= 'f' { ret (c - 'a') + 10; }
|
||||||
|
if c >= 'A' && c <= 'F' { ret (c - 'A') + 10; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_str(t: ByteView): ByteBuffer {
|
||||||
|
@assert(t.len >= 2 && t.ptr != null, "string literal token must include quotes");
|
||||||
|
var out = ByteBuffer.new();
|
||||||
|
if t.len < 2 { ret out; }
|
||||||
|
var i: usize = 1;
|
||||||
|
var end = t.len - 1;
|
||||||
|
while i < end {
|
||||||
|
var c = t.ptr[i];
|
||||||
|
if c == '\\' && i + 1 < end {
|
||||||
|
var e = t.ptr[i + 1];
|
||||||
|
if e == 'x' && i + 3 < end {
|
||||||
|
var hi = hex_digit(t.ptr[i + 2]);
|
||||||
|
var lo = hex_digit(t.ptr[i + 3]);
|
||||||
|
ByteBuffer.append_byte(&out, (hi * 16 + lo) as u8);
|
||||||
|
i = i + 4;
|
||||||
|
} else {
|
||||||
|
var d: u8 = e;
|
||||||
|
if e == 'n' { d = '\n'; }
|
||||||
|
else if e == 't' { d = '\t'; }
|
||||||
|
else if e == 'r' { d = '\r'; }
|
||||||
|
else if e == '0' { d = 0; }
|
||||||
|
ByteBuffer.append_byte(&out, d);
|
||||||
|
i = i + 2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ByteBuffer.append_byte(&out, c);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret out;
|
||||||
|
}
|
||||||
|
|
||||||
fn expect_punct(self: *Parser, punct: Punct, what: []u8): bool {
|
fn expect_punct(self: *Parser, punct: Punct, what: []u8): bool {
|
||||||
var t = peek(self);
|
var t = peek(self);
|
||||||
if t == null || !kind_is_punct(t.kind, punct) {
|
if t == null || !kind_is_punct(t.kind, punct) {
|
||||||
@@ -146,7 +198,6 @@ fn expect_punct(self: *Parser, punct: Punct, what: []u8): bool {
|
|||||||
ret true;
|
ret true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 节点 ----
|
|
||||||
|
|
||||||
fn new_node(self: *Parser, body: Body, tok: *Token): usize {
|
fn new_node(self: *Parser, body: Body, tok: *Token): usize {
|
||||||
ret Ast.alloc(self.ast, body, span_of(self, tok));
|
ret Ast.alloc(self.ast, body, span_of(self, tok));
|
||||||
@@ -205,7 +256,6 @@ fn int_token_u64(self: *Parser, tok: *Token, radix: Radix): u64 {
|
|||||||
ret v;
|
ret v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 判定(stage1 can_start_*)----
|
|
||||||
|
|
||||||
fn can_start_expr(kind: TokenKind): bool {
|
fn can_start_expr(kind: TokenKind): bool {
|
||||||
match kind {
|
match kind {
|
||||||
@@ -224,6 +274,8 @@ fn can_start_expr(kind: TokenKind): bool {
|
|||||||
.Loop => { ret true; },
|
.Loop => { ret true; },
|
||||||
.For => { ret true; },
|
.For => { ret true; },
|
||||||
.Match => { ret true; },
|
.Match => { ret true; },
|
||||||
|
.Comptime => { ret true; },
|
||||||
|
.Try => { ret true; },
|
||||||
_ => { ret false; },
|
_ => { ret false; },
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -263,6 +315,7 @@ fn can_start_statement(kind: TokenKind): bool {
|
|||||||
.Break => { ret true; },
|
.Break => { ret true; },
|
||||||
.Continue => { ret true; },
|
.Continue => { ret true; },
|
||||||
.Defer => { ret true; },
|
.Defer => { ret true; },
|
||||||
|
.Errdefer => { ret true; },
|
||||||
.Var => { ret true; },
|
.Var => { ret true; },
|
||||||
.Const => { ret true; },
|
.Const => { ret true; },
|
||||||
.Type => { ret true; },
|
.Type => { ret true; },
|
||||||
@@ -275,11 +328,10 @@ fn can_start_statement(kind: TokenKind): bool {
|
|||||||
ret false;
|
ret false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 模式(M3.2 子集:ident / _ / .variant / tuple / .{ } )----
|
|
||||||
|
|
||||||
fn parse_pattern_aggregate(self: *Parser, start: *Token): usize {
|
fn parse_pattern_aggregate(self: *Parser, start: *Token): usize {
|
||||||
if !expect_punct(self, .LBrace, "'{' after '.'") { ret 0; }
|
if !expect_punct(self, .LBrace, "'{' after '.'") { ret 0; }
|
||||||
// stage1 先分配 PATTERN_AGGREGATE,再解析字段。
|
// 先分配 PATTERN_AGGREGATE,再解析字段。
|
||||||
var ref = new_node(self, .PatternAggregate(.{ .items = ref_vec() }), start);
|
var ref = new_node(self, .PatternAggregate(.{ .items = ref_vec() }), start);
|
||||||
var fields = ref_vec();
|
var fields = ref_vec();
|
||||||
while !self.failed {
|
while !self.failed {
|
||||||
@@ -294,7 +346,7 @@ fn parse_pattern_aggregate(self: *Parser, start: *Token): usize {
|
|||||||
if name == null { break; }
|
if name == null { break; }
|
||||||
match name.kind { .Ident => {}, _ => { break; } }
|
match name.kind { .Ident => {}, _ => { break; } }
|
||||||
advance(self);
|
advance(self);
|
||||||
// stage1 先分配 PATTERN_FIELD,再解析可选子模式。
|
// 先分配 PATTERN_FIELD,再解析可选子模式。
|
||||||
var fre = new_node(self, .PatternField(.{
|
var fre = new_node(self, .PatternField(.{
|
||||||
.name = name.text,
|
.name = name.text,
|
||||||
.pattern = 0,
|
.pattern = 0,
|
||||||
@@ -355,7 +407,7 @@ fn parse_pattern_atom(self: *Parser): usize {
|
|||||||
},
|
},
|
||||||
.Str => {
|
.Str => {
|
||||||
advance(self);
|
advance(self);
|
||||||
ret new_node(self, .ExprStringLit(.{ .data = t.text }), t);
|
ret new_node(self, .ExprStringLit(.{ .data = Ast.push_string(self.ast, decode_str(t.text)) }), t);
|
||||||
},
|
},
|
||||||
.Keyword(k) => {
|
.Keyword(k) => {
|
||||||
match k {
|
match k {
|
||||||
@@ -389,7 +441,7 @@ fn parse_pattern_atom(self: *Parser): usize {
|
|||||||
if nx == null { ret 0; }
|
if nx == null { ret 0; }
|
||||||
match nx.kind { .Ident => {}, _ => { ret 0; } }
|
match nx.kind { .Ident => {}, _ => { ret 0; } }
|
||||||
advance(self);
|
advance(self);
|
||||||
// stage1 先分配 PATTERN_DOT,再解析子模式。
|
// 先分配 PATTERN_DOT,再解析子模式。
|
||||||
var ref = new_node(self, .PatternDot(.{
|
var ref = new_node(self, .PatternDot(.{
|
||||||
.name = nx.text,
|
.name = nx.text,
|
||||||
.sub_pattern = 0,
|
.sub_pattern = 0,
|
||||||
@@ -400,9 +452,8 @@ fn parse_pattern_atom(self: *Parser): usize {
|
|||||||
var sub = parse_pattern(self);
|
var sub = parse_pattern(self);
|
||||||
var pn = Ast.node(self.ast, ref);
|
var pn = Ast.node(self.ast, ref);
|
||||||
match pn.body {
|
match pn.body {
|
||||||
.PatternDot(pd) => {
|
.PatternDot(&pd) => {
|
||||||
pd.sub_pattern = sub;
|
pd.sub_pattern = sub;
|
||||||
pn.body = .PatternDot(pd);
|
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
@@ -413,7 +464,7 @@ fn parse_pattern_atom(self: *Parser): usize {
|
|||||||
},
|
},
|
||||||
.LParen => {
|
.LParen => {
|
||||||
advance(self);
|
advance(self);
|
||||||
// stage1 先分配 PATTERN_TUPLE,再解析内部模式。
|
// 先分配 PATTERN_TUPLE,再解析内部模式。
|
||||||
var ref = new_node(self, .PatternTuple(.{ .items = ref_vec() }), t);
|
var ref = new_node(self, .PatternTuple(.{ .items = ref_vec() }), t);
|
||||||
if at_punct(self, .RParen) {
|
if at_punct(self, .RParen) {
|
||||||
advance(self);
|
advance(self);
|
||||||
@@ -448,6 +499,24 @@ fn parse_pattern_atom(self: *Parser): usize {
|
|||||||
}
|
}
|
||||||
ret ref;
|
ret ref;
|
||||||
},
|
},
|
||||||
|
.And => {
|
||||||
|
// `&name`:解包左值引用绑定。仅允许作用于绑定名。
|
||||||
|
advance(self);
|
||||||
|
var nx = peek(self);
|
||||||
|
if nx == null {
|
||||||
|
parse_error(self, nx, "expected binding name after '&'");
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
match nx.kind {
|
||||||
|
.Ident => {},
|
||||||
|
_ => {
|
||||||
|
parse_error(self, nx, "expected binding name after '&'");
|
||||||
|
ret 0;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var inner = parse_pattern_atom(self);
|
||||||
|
ret new_node(self, .PatternRef(.{ .pat = inner }), t);
|
||||||
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -485,9 +554,8 @@ fn parse_pattern(self: *Parser): usize {
|
|||||||
ret new_node(self, .PatternList(.{ .items = atoms }), peek(self));
|
ret new_node(self, .PatternList(.{ .items = atoms }), peek(self));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 声明 ----
|
|
||||||
|
|
||||||
fn parse_tag_list(self: *Parser, out: *Vec, is_external: bool, allow_at: bool) {
|
fn parse_tag_list(self: *Parser, out: *Vec, is_external: bool, allow_at: bool, bang_only: bool) {
|
||||||
while !self.failed {
|
while !self.failed {
|
||||||
var t = peek(self);
|
var t = peek(self);
|
||||||
if t == null { ret; }
|
if t == null { ret; }
|
||||||
@@ -496,30 +564,37 @@ fn parse_tag_list(self: *Parser, out: *Vec, is_external: bool, allow_at: bool) {
|
|||||||
else if kind_is_punct(t.kind, .Sharp) { is_at = false; }
|
else if kind_is_punct(t.kind, .Sharp) { is_at = false; }
|
||||||
else { ret; }
|
else { ret; }
|
||||||
if !allow_at && is_at { ret; }
|
if !allow_at && is_at { ret; }
|
||||||
|
if bang_only {
|
||||||
|
// 仅消费 `#!`/`@!` 形式的当前层 tag;遇到普通 `#name` 即停。
|
||||||
|
var save = self.pos;
|
||||||
advance(self);
|
advance(self);
|
||||||
if is_external {
|
var nt = peek(self);
|
||||||
t = peek(self);
|
var has_bang = nt != null && kind_is_punct(nt.kind, .Not);
|
||||||
if t == null || !kind_is_punct(t.kind, .Not) {
|
self.pos = save;
|
||||||
parse_error(self, t, "expected '!' in external tag");
|
if !has_bang { ret; }
|
||||||
ret;
|
|
||||||
}
|
}
|
||||||
advance(self);
|
advance(self);
|
||||||
|
t = peek(self);
|
||||||
|
var ext_here = is_external;
|
||||||
|
if t != null && kind_is_punct(t.kind, .Not) {
|
||||||
|
advance(self);
|
||||||
|
ext_here = true;
|
||||||
}
|
}
|
||||||
t = peek(self);
|
t = peek(self);
|
||||||
if t == null { ret; }
|
if t == null { ret; }
|
||||||
match t.kind { .Ident => {}, _ => { parse_error(self, t, "expected tag name"); ret; } }
|
match t.kind { .Ident => {}, _ => { parse_error(self, t, "expected tag name"); ret; } }
|
||||||
advance(self);
|
advance(self);
|
||||||
// stage1 先分配 tag 节点,再解析参数。
|
// 先分配 tag 节点,再解析参数。
|
||||||
var body: Body = .TagItem(.{
|
var body: Body = .TagItem(.{
|
||||||
.is_at = is_at,
|
.is_at = is_at,
|
||||||
.is_external = is_external,
|
.is_external = ext_here,
|
||||||
.name = t.text,
|
.name = t.text,
|
||||||
.args = ref_vec(),
|
.args = ref_vec(),
|
||||||
});
|
});
|
||||||
if is_external {
|
if ext_here {
|
||||||
body = .ExternalTagItem(.{
|
body = .ExternalTagItem(.{
|
||||||
.is_at = is_at,
|
.is_at = is_at,
|
||||||
.is_external = is_external,
|
.is_external = ext_here,
|
||||||
.name = t.text,
|
.name = t.text,
|
||||||
.args = ref_vec(),
|
.args = ref_vec(),
|
||||||
});
|
});
|
||||||
@@ -548,7 +623,13 @@ fn parse_tag_list(self: *Parser, out: *Vec, is_external: bool, allow_at: bool) {
|
|||||||
|
|
||||||
fn parse_param_decl_impl(self: *Parser): usize {
|
fn parse_param_decl_impl(self: *Parser): usize {
|
||||||
var attrs = ref_vec();
|
var attrs = ref_vec();
|
||||||
parse_tag_list(self, &attrs, false, true);
|
var is_comptime = false;
|
||||||
|
var c0 = peek(self);
|
||||||
|
if c0 != null && kind_is_keyword(c0.kind, .Comptime) {
|
||||||
|
advance(self);
|
||||||
|
is_comptime = true;
|
||||||
|
}
|
||||||
|
parse_tag_list(self, &attrs, false, true, false);
|
||||||
var name = peek(self);
|
var name = peek(self);
|
||||||
if name == null { ret 0; }
|
if name == null { ret 0; }
|
||||||
var ok = false;
|
var ok = false;
|
||||||
@@ -560,10 +641,10 @@ fn parse_param_decl_impl(self: *Parser): usize {
|
|||||||
if !ok { ret 0; }
|
if !ok { ret 0; }
|
||||||
advance(self);
|
advance(self);
|
||||||
if !expect_punct(self, .Colon, "':' in parameter declaration") { ret 0; }
|
if !expect_punct(self, .Colon, "':' in parameter declaration") { ret 0; }
|
||||||
parse_tag_list(self, &attrs, false, false);
|
parse_tag_list(self, &attrs, false, false, false);
|
||||||
var ty = parse_type_expr(self);
|
var ty = parse_type_expr(self);
|
||||||
ret new_node(self,
|
ret new_node(self,
|
||||||
.ParamDecl(.{ .attr_list = attrs, .name = name.text, .type_expr = ty }),
|
.ParamDecl(.{ .attr_list = attrs, .is_comptime = is_comptime, .name = name.text, .type_expr = ty }),
|
||||||
name);
|
name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -591,11 +672,24 @@ fn parse_param_list(self: *Parser): Vec {
|
|||||||
if at_punct(self, .Comma) { advance(self); continue; }
|
if at_punct(self, .Comma) { advance(self); continue; }
|
||||||
break;
|
break;
|
||||||
},
|
},
|
||||||
|
.Keyword(k) => {
|
||||||
|
if @enum2int(k) == @enum2int(Keyword.Comptime) {
|
||||||
|
var pd2 = parse_param_decl_impl(self);
|
||||||
|
if pd2 != 0 {
|
||||||
|
match Vec.push(&out, &pd2 as *u8) { .Ok => {}, .Err(e) => { ret out; } }
|
||||||
|
}
|
||||||
|
if at_punct(self, .Comma) { advance(self); continue; }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
parse_error(self, t, "unexpected token in parameter list");
|
||||||
|
break;
|
||||||
|
},
|
||||||
.Punct(p) => {
|
.Punct(p) => {
|
||||||
if @enum2int(p) == @enum2int(Punct.Ellipsis) {
|
if @enum2int(p) == @enum2int(Punct.Ellipsis) {
|
||||||
advance(self);
|
advance(self);
|
||||||
var pd = new_node(self, .ParamDecl(.{
|
var pd = new_node(self, .ParamDecl(.{
|
||||||
.attr_list = ref_vec(),
|
.attr_list = ref_vec(),
|
||||||
|
.is_comptime = false,
|
||||||
.name = ByteView.empty(),
|
.name = ByteView.empty(),
|
||||||
.type_expr = 0,
|
.type_expr = 0,
|
||||||
}), t);
|
}), t);
|
||||||
@@ -615,7 +709,29 @@ fn parse_param_list(self: *Parser): Vec {
|
|||||||
ret out;
|
ret out;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_fn_decl(self: *Parser, tags: Vec): usize {
|
// 该 token 是否可能开始一个类型表达式(用于省略冒号的返回类型)。
|
||||||
|
fn token_starts_type(t: *Token): bool {
|
||||||
|
if t == null { ret false; }
|
||||||
|
match t.kind {
|
||||||
|
.Ident => { ret true; },
|
||||||
|
.Keyword(k) => {
|
||||||
|
match k {
|
||||||
|
.Type, .Bool, .Any, .Fn, .Struct, .Union, .Enum, .Shape => { ret true; },
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Punct(p) => {
|
||||||
|
match p {
|
||||||
|
.Mul, .LBracket, .At, .Sharp => { ret true; },
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_fn_decl(self: *Parser, tags: Vec, is_comptime: bool): usize {
|
||||||
var kw = peek(self);
|
var kw = peek(self);
|
||||||
if kw == null || !kind_is_keyword(kw.kind, .Fn) { ret 0; }
|
if kw == null || !kind_is_keyword(kw.kind, .Fn) { ret 0; }
|
||||||
advance(self);
|
advance(self);
|
||||||
@@ -632,19 +748,26 @@ fn parse_fn_decl(self: *Parser, tags: Vec): usize {
|
|||||||
advance(self);
|
advance(self);
|
||||||
ret_type = parse_type_expr(self);
|
ret_type = parse_type_expr(self);
|
||||||
t = peek(self);
|
t = peek(self);
|
||||||
|
} else if token_starts_type(t) {
|
||||||
|
// 省略冒号:`fn f() T { ... }`
|
||||||
|
ret_type = parse_type_expr(self);
|
||||||
|
t = peek(self);
|
||||||
}
|
}
|
||||||
var body: usize = 0;
|
var body: usize = 0;
|
||||||
|
if is_comptime { self.comptime_depth = self.comptime_depth + 1; }
|
||||||
if t != null && kind_is_punct(t.kind, .LBrace) {
|
if t != null && kind_is_punct(t.kind, .LBrace) {
|
||||||
body = parse_block(self);
|
body = parse_block(self);
|
||||||
} else if t != null && kind_is_punct(t.kind, .Semicolon) {
|
} else if t != null && kind_is_punct(t.kind, .Semicolon) {
|
||||||
advance(self);
|
advance(self);
|
||||||
} else {
|
} else {
|
||||||
|
if is_comptime { self.comptime_depth = self.comptime_depth - 1; }
|
||||||
parse_error(self, t, "function declaration must end with ';' or block");
|
parse_error(self, t, "function declaration must end with ';' or block");
|
||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
if is_comptime { self.comptime_depth = self.comptime_depth - 1; }
|
||||||
ret new_node(self, .FnDecl(.{
|
ret new_node(self, .FnDecl(.{
|
||||||
.tag_list = tags,
|
.tag_list = tags,
|
||||||
.is_comptime = false,
|
.is_comptime = is_comptime,
|
||||||
.name = name.text,
|
.name = name.text,
|
||||||
.params = params,
|
.params = params,
|
||||||
.ret_type = ret_type,
|
.ret_type = ret_type,
|
||||||
@@ -659,7 +782,7 @@ fn parse_type_decl(self: *Parser, tags: Vec): usize {
|
|||||||
var pattern = parse_pattern(self);
|
var pattern = parse_pattern(self);
|
||||||
if pattern == 0 { ret 0; }
|
if pattern == 0 { ret 0; }
|
||||||
if !expect_punct(self, .Assign, "'=' in type declaration") { ret 0; }
|
if !expect_punct(self, .Assign, "'=' in type declaration") { ret 0; }
|
||||||
// stage1 先分配 TYPE_DECL 再解析 type_expr(影响 #ref 顺序)。
|
// 先分配 TYPE_DECL 再解析 type_expr(影响 #ref 顺序)。
|
||||||
var ref = new_node(self, .TypeDecl(.{
|
var ref = new_node(self, .TypeDecl(.{
|
||||||
.attr_list = tags,
|
.attr_list = tags,
|
||||||
.pattern = pattern,
|
.pattern = pattern,
|
||||||
@@ -732,7 +855,7 @@ fn parse_member_decl(self: *Parser, tags: Vec): usize {
|
|||||||
var t = peek(self);
|
var t = peek(self);
|
||||||
if t != null && kind_is_punct(t.kind, .Colon) {
|
if t != null && kind_is_punct(t.kind, .Colon) {
|
||||||
advance(self);
|
advance(self);
|
||||||
parse_tag_list(self, &tags, false, false);
|
parse_tag_list(self, &tags, false, false, false);
|
||||||
te = parse_type_expr(self);
|
te = parse_type_expr(self);
|
||||||
t = peek(self);
|
t = peek(self);
|
||||||
if t != null && kind_is_punct(t.kind, .Assign) {
|
if t != null && kind_is_punct(t.kind, .Assign) {
|
||||||
@@ -762,37 +885,48 @@ fn parse_comptime_decl(self: *Parser): usize {
|
|||||||
advance(self);
|
advance(self);
|
||||||
var body: usize = 0;
|
var body: usize = 0;
|
||||||
var t = peek(self);
|
var t = peek(self);
|
||||||
|
self.comptime_depth = self.comptime_depth + 1;
|
||||||
if t != null && kind_is_punct(t.kind, .LBrace) { body = parse_block(self); }
|
if t != null && kind_is_punct(t.kind, .LBrace) { body = parse_block(self); }
|
||||||
else { body = parse_expr(self); }
|
else { body = parse_expr(self); }
|
||||||
|
self.comptime_depth = self.comptime_depth - 1;
|
||||||
ret new_node(self, .ComptimeDecl(.{ .body = body }), kw);
|
ret new_node(self, .ComptimeDecl(.{ .body = body }), kw);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_container_declaration(self: *Parser): usize {
|
fn parse_container_declaration(self: *Parser): usize {
|
||||||
var tags = ref_vec();
|
var tags = ref_vec();
|
||||||
parse_tag_list(self, &tags, false, false);
|
parse_tag_list(self, &tags, false, false, false);
|
||||||
var t = peek(self);
|
var t = peek(self);
|
||||||
if t == null { ret 0; }
|
if t == null { ret 0; }
|
||||||
match t.kind {
|
match t.kind {
|
||||||
.Eof => { ret 0; },
|
.Eof => { ret 0; },
|
||||||
.Keyword(k) => {
|
.Keyword(k) => {
|
||||||
if @enum2int(k) == @enum2int(Keyword.Fn) { ret parse_fn_decl(self, tags); }
|
match k {
|
||||||
if @enum2int(k) == @enum2int(Keyword.Type) { ret parse_type_decl(self, tags); }
|
.Fn => { ret parse_fn_decl(self, tags, false); },
|
||||||
if @enum2int(k) == @enum2int(Keyword.Var) {
|
.Type => { ret parse_type_decl(self, tags); },
|
||||||
ret parse_var_const_decl(self, tags, false);
|
.Var => { ret parse_var_const_decl(self, tags, false); },
|
||||||
|
.Const => { ret parse_var_const_decl(self, tags, true); },
|
||||||
|
.Comptime => {
|
||||||
|
var nxt = peek_next(self);
|
||||||
|
if nxt != null && kind_is_keyword(nxt.kind, .Fn) {
|
||||||
|
advance(self);
|
||||||
|
ret parse_fn_decl(self, tags, true);
|
||||||
}
|
}
|
||||||
if @enum2int(k) == @enum2int(Keyword.Const) {
|
ret parse_comptime_decl(self);
|
||||||
ret parse_var_const_decl(self, tags, true);
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
if @enum2int(k) == @enum2int(Keyword.Comptime) { ret parse_comptime_decl(self); }
|
|
||||||
},
|
},
|
||||||
.Ident => { ret parse_member_decl(self, tags); },
|
.Ident => { ret parse_member_decl(self, tags); },
|
||||||
.Punct(p) => {
|
.Punct(p) => {
|
||||||
if @enum2int(p) == @enum2int(Punct.LBrace) {
|
match p {
|
||||||
|
.LBrace => {
|
||||||
var block = parse_block(self);
|
var block = parse_block(self);
|
||||||
ret new_node(self, .TopLevelBlock(.{
|
ret new_node(self, .TopLevelBlock(.{
|
||||||
.tag_list = tags,
|
.tag_list = tags,
|
||||||
.block = block,
|
.block = block,
|
||||||
}), t);
|
}), t);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
@@ -802,7 +936,6 @@ fn parse_container_declaration(self: *Parser): usize {
|
|||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 类型 ----
|
|
||||||
|
|
||||||
fn parse_type_comptime_call(self: *Parser): usize {
|
fn parse_type_comptime_call(self: *Parser): usize {
|
||||||
var start = peek(self);
|
var start = peek(self);
|
||||||
@@ -831,7 +964,7 @@ fn parse_type_base(self: *Parser): usize {
|
|||||||
if kind_is_keyword(bt.kind, .Fn) {
|
if kind_is_keyword(bt.kind, .Fn) {
|
||||||
advance(self);
|
advance(self);
|
||||||
if !expect_punct(self, .LParen, "'(' in function type") { ret 0; }
|
if !expect_punct(self, .LParen, "'(' in function type") { ret 0; }
|
||||||
// stage1 先分配 TYPE_FN,再解析参数与返回类型。
|
// 先分配 TYPE_FN,再解析参数与返回类型。
|
||||||
var ref = new_node(self, .TypeFn(.{
|
var ref = new_node(self, .TypeFn(.{
|
||||||
.is_comptime = false,
|
.is_comptime = false,
|
||||||
.params = ref_vec(),
|
.params = ref_vec(),
|
||||||
@@ -865,10 +998,13 @@ fn parse_type_base(self: *Parser): usize {
|
|||||||
match bt.kind {
|
match bt.kind {
|
||||||
.Keyword(k) => {
|
.Keyword(k) => {
|
||||||
var kind_name: []u8 = "";
|
var kind_name: []u8 = "";
|
||||||
if @enum2int(k) == @enum2int(Keyword.Struct) { kind_name = "struct"; }
|
match k {
|
||||||
else if @enum2int(k) == @enum2int(Keyword.Union) { kind_name = "union"; }
|
.Struct => { kind_name = "struct"; },
|
||||||
else if @enum2int(k) == @enum2int(Keyword.Enum) { kind_name = "enum"; }
|
.Union => { kind_name = "union"; },
|
||||||
else if @enum2int(k) == @enum2int(Keyword.Shape) { kind_name = "shape"; }
|
.Enum => { kind_name = "enum"; },
|
||||||
|
.Shape => { kind_name = "shape"; },
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
if kind_name.len != 0 {
|
if kind_name.len != 0 {
|
||||||
advance(self);
|
advance(self);
|
||||||
if !expect_punct(self, .LBrace, "'{' in aggregate type") { ret 0; }
|
if !expect_punct(self, .LBrace, "'{' in aggregate type") { ret 0; }
|
||||||
@@ -911,25 +1047,52 @@ fn parse_type_base(self: *Parser): usize {
|
|||||||
match bt.kind {
|
match bt.kind {
|
||||||
.Ident => {
|
.Ident => {
|
||||||
advance(self);
|
advance(self);
|
||||||
|
// 类型调用 `Name(T, ...)`(comptime 类型构造,如 `vec(i32)`)。
|
||||||
|
if at_punct(self, .LParen) {
|
||||||
|
advance(self);
|
||||||
|
var ref = new_node(self, .TypePath(.{
|
||||||
|
.name = bt.text,
|
||||||
|
.generic_args = ref_vec(),
|
||||||
|
}), bt);
|
||||||
|
var args = ref_vec();
|
||||||
|
while !self.failed && !at_punct(self, .RParen) {
|
||||||
|
var a = parse_type_expr(self);
|
||||||
|
match Vec.push(&args, &a as *u8) { .Ok => {}, .Err(e) => { ret 0; } }
|
||||||
|
if at_punct(self, .Comma) { advance(self); continue; }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
expect_punct(self, .RParen, "')' in type call");
|
||||||
|
var tn = Ast.node(self.ast, ref);
|
||||||
|
match tn.body {
|
||||||
|
.TypePath(tp) => {
|
||||||
|
tp.generic_args = args;
|
||||||
|
tn.body = .TypePath(tp);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
ret ref;
|
||||||
|
}
|
||||||
ret new_node(self, .TypePath(.{
|
ret new_node(self, .TypePath(.{
|
||||||
.name = bt.text,
|
.name = bt.text,
|
||||||
.generic_args = ref_vec(),
|
.generic_args = ref_vec(),
|
||||||
}), bt);
|
}), bt);
|
||||||
},
|
},
|
||||||
.Keyword(k) => {
|
.Keyword(k) => {
|
||||||
// stage1 is_builtin_type_kw:bool / any 作为类型路径。
|
match k {
|
||||||
if @enum2int(k) == @enum2int(Keyword.Bool) ||
|
.Bool, .Any, .Type => {
|
||||||
@enum2int(k) == @enum2int(Keyword.Any) {
|
|
||||||
advance(self);
|
advance(self);
|
||||||
ret new_node(self, .TypePath(.{
|
ret new_node(self, .TypePath(.{
|
||||||
.name = bt.text,
|
.name = bt.text,
|
||||||
.generic_args = ref_vec(),
|
.generic_args = ref_vec(),
|
||||||
}), bt);
|
}), bt);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.Punct(p) => {
|
.Punct(p) => {
|
||||||
if @enum2int(p) == @enum2int(Punct.At) || @enum2int(p) == @enum2int(Punct.Sharp) {
|
match p {
|
||||||
ret parse_type_comptime_call(self);
|
.At, .Sharp => { ret parse_type_comptime_call(self); },
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
@@ -944,7 +1107,7 @@ fn parse_type_expr(self: *Parser): usize {
|
|||||||
|
|
||||||
if kind_is_punct(t.kind, .Mul) {
|
if kind_is_punct(t.kind, .Mul) {
|
||||||
advance(self);
|
advance(self);
|
||||||
// stage1 先分配 POINTER,再解析 pointee。
|
// 先分配 POINTER,再解析 pointee。
|
||||||
var ref = new_node(self, .TypePointer(.{ .pointee = 0 }), t);
|
var ref = new_node(self, .TypePointer(.{ .pointee = 0 }), t);
|
||||||
var pointee = parse_type_expr(self);
|
var pointee = parse_type_expr(self);
|
||||||
var pn = Ast.node(self.ast, ref);
|
var pn = Ast.node(self.ast, ref);
|
||||||
@@ -1028,7 +1191,6 @@ fn parse_type_expr(self: *Parser): usize {
|
|||||||
ret base;
|
ret base;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 表达式 ----
|
|
||||||
|
|
||||||
type BinInfo = struct { op: Punct, prec: i32, right: bool }
|
type BinInfo = struct { op: Punct, prec: i32, right: bool }
|
||||||
type BinResult = enum { No, Yes: BinInfo }
|
type BinResult = enum { No, Yes: BinInfo }
|
||||||
@@ -1110,7 +1272,19 @@ fn parse_expr_prec(self: *Parser, min_prec: i32): usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_expr(self: *Parser): usize {
|
fn parse_expr(self: *Parser): usize {
|
||||||
ret parse_expr_prec(self, 1);
|
var e = parse_expr_prec(self, 1);
|
||||||
|
// `expr catch <pattern> <block>`:catch 用解包语法绑定错误。
|
||||||
|
while !self.failed {
|
||||||
|
var t = peek(self);
|
||||||
|
if t == null { break; }
|
||||||
|
if !kind_is_keyword(t.kind, .Catch) { break; }
|
||||||
|
advance(self);
|
||||||
|
var pat = parse_pattern(self);
|
||||||
|
if pat == 0 { ret e; }
|
||||||
|
var handler = parse_block(self);
|
||||||
|
e = new_node(self, .ExprCatch(.{ .value = e, .pattern = pat, .handler = handler }), t);
|
||||||
|
}
|
||||||
|
ret e;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_cast(self: *Parser): usize {
|
fn parse_cast(self: *Parser): usize {
|
||||||
@@ -1141,9 +1315,9 @@ fn parse_prefix(self: *Parser): usize {
|
|||||||
if ref == 0 { ret 0; }
|
if ref == 0 { ret 0; }
|
||||||
var n = Ast.node(self.ast, ref);
|
var n = Ast.node(self.ast, ref);
|
||||||
match n.body {
|
match n.body {
|
||||||
.ExprWhile(w) => { w.label = label; n.body = .ExprWhile(w); },
|
.ExprWhile(&w) => { w.label = label; },
|
||||||
.ExprLoop(l) => { l.label = label; n.body = .ExprLoop(l); },
|
.ExprLoop(&l) => { l.label = label; },
|
||||||
.ExprFor(f) => { f.label = label; n.body = .ExprFor(f); },
|
.ExprFor(&f) => { f.label = label; },
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
ret ref;
|
ret ref;
|
||||||
@@ -1183,7 +1357,7 @@ fn parse_postfix(self: *Parser): usize {
|
|||||||
|
|
||||||
if kind_is_punct(t.kind, .LParen) {
|
if kind_is_punct(t.kind, .LParen) {
|
||||||
advance(self);
|
advance(self);
|
||||||
// stage1 先分配 CALL,再解析实参(影响 #ref 顺序)。
|
// 先分配 CALL,再解析实参(影响 #ref 顺序)。
|
||||||
var ref = new_node(self, .ExprCall(.{ .callee = expr, .args = ref_vec() }), t);
|
var ref = new_node(self, .ExprCall(.{ .callee = expr, .args = ref_vec() }), t);
|
||||||
var args = parse_expr_list(self);
|
var args = parse_expr_list(self);
|
||||||
var cn = Ast.node(self.ast, ref);
|
var cn = Ast.node(self.ast, ref);
|
||||||
@@ -1293,37 +1467,42 @@ fn parse_primary(self: *Parser): usize {
|
|||||||
},
|
},
|
||||||
.Str => {
|
.Str => {
|
||||||
advance(self);
|
advance(self);
|
||||||
ret new_node(self, .ExprStringLit(.{ .data = t.text }), t);
|
ret new_node(self, .ExprStringLit(.{ .data = Ast.push_string(self.ast, decode_str(t.text)) }), t);
|
||||||
},
|
},
|
||||||
.Keyword(k) => {
|
.Keyword(k) => {
|
||||||
if @enum2int(k) == @enum2int(Keyword.True) {
|
match k {
|
||||||
|
.True => { advance(self); ret new_node(self, .ExprBoolLit(.{ .value = true }), t); },
|
||||||
|
.False => { advance(self); ret new_node(self, .ExprBoolLit(.{ .value = false }), t); },
|
||||||
|
.Null => { advance(self); ret new_node(self, .ExprNullLit, t); },
|
||||||
|
.Undefined => { advance(self); ret new_node(self, .ExprUndefinedLit, t); },
|
||||||
|
.If => { ret parse_if_expr(self); },
|
||||||
|
.While => { ret parse_while_expr(self); },
|
||||||
|
.Loop => { ret parse_loop_expr(self); },
|
||||||
|
.For => { ret parse_for_expr(self); },
|
||||||
|
.Match => { ret parse_match_expr(self); },
|
||||||
|
.Try => {
|
||||||
advance(self);
|
advance(self);
|
||||||
ret new_node(self, .ExprBoolLit(.{ .value = true }), t);
|
var tv = parse_expr(self);
|
||||||
}
|
ret new_node(self, .ExprTry(.{ .value = tv }), t);
|
||||||
if @enum2int(k) == @enum2int(Keyword.False) {
|
},
|
||||||
|
.Comptime => {
|
||||||
advance(self);
|
advance(self);
|
||||||
ret new_node(self, .ExprBoolLit(.{ .value = false }), t);
|
var cbody = parse_expr(self);
|
||||||
|
ret new_node(self, .ExprComptime(.{ .body = cbody }), t);
|
||||||
|
},
|
||||||
|
.Struct, .Union, .Enum, .Shape => {
|
||||||
|
// 类型即表达式仅限 comptime 上下文。
|
||||||
|
if self.comptime_depth > 0 { ret parse_type_base(self); }
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
if @enum2int(k) == @enum2int(Keyword.Null) {
|
|
||||||
advance(self);
|
|
||||||
ret new_node(self, .ExprNullLit, t);
|
|
||||||
}
|
|
||||||
if @enum2int(k) == @enum2int(Keyword.Undefined) {
|
|
||||||
advance(self);
|
|
||||||
ret new_node(self, .ExprUndefinedLit, t);
|
|
||||||
}
|
|
||||||
if @enum2int(k) == @enum2int(Keyword.If) { ret parse_if_expr(self); }
|
|
||||||
if @enum2int(k) == @enum2int(Keyword.While) { ret parse_while_expr(self); }
|
|
||||||
if @enum2int(k) == @enum2int(Keyword.Loop) { ret parse_loop_expr(self); }
|
|
||||||
if @enum2int(k) == @enum2int(Keyword.For) { ret parse_for_expr(self); }
|
|
||||||
if @enum2int(k) == @enum2int(Keyword.Match) { ret parse_match_expr(self); }
|
|
||||||
parse_error(self, t, "unrecognized expression");
|
parse_error(self, t, "unrecognized expression");
|
||||||
ret 0;
|
ret 0;
|
||||||
},
|
},
|
||||||
.Ident => {
|
.Ident => {
|
||||||
advance(self);
|
advance(self);
|
||||||
if at_punct(self, .LBrace) && self.stop_agg == 0 {
|
if at_punct(self, .LBrace) && self.stop_agg == 0 {
|
||||||
// stage1 先分配 AGGREGATE_LIT,再解析字段。
|
// 先分配 AGGREGATE_LIT,再解析字段。
|
||||||
var ref = new_node(self, .ExprAggregateLit(.{
|
var ref = new_node(self, .ExprAggregateLit(.{
|
||||||
.name = t.text,
|
.name = t.text,
|
||||||
.fields = ref_vec(),
|
.fields = ref_vec(),
|
||||||
@@ -1331,10 +1510,7 @@ fn parse_primary(self: *Parser): usize {
|
|||||||
var fields = parse_aggregate_fields(self);
|
var fields = parse_aggregate_fields(self);
|
||||||
var an = Ast.node(self.ast, ref);
|
var an = Ast.node(self.ast, ref);
|
||||||
match an.body {
|
match an.body {
|
||||||
.ExprAggregateLit(a) => {
|
.ExprAggregateLit(&a) => { a.fields = fields; },
|
||||||
a.fields = fields;
|
|
||||||
an.body = .ExprAggregateLit(a);
|
|
||||||
},
|
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
ret ref;
|
ret ref;
|
||||||
@@ -1342,7 +1518,8 @@ fn parse_primary(self: *Parser): usize {
|
|||||||
ret new_node(self, .ExprIdent(.{ .name = t.text }), t);
|
ret new_node(self, .ExprIdent(.{ .name = t.text }), t);
|
||||||
},
|
},
|
||||||
.Punct(p) => {
|
.Punct(p) => {
|
||||||
if @enum2int(p) == @enum2int(Punct.Dot) {
|
match p {
|
||||||
|
.Dot => {
|
||||||
advance(self);
|
advance(self);
|
||||||
var nx = peek(self);
|
var nx = peek(self);
|
||||||
if nx == null { parse_error(self, t, "unexpected '.'"); ret 0; }
|
if nx == null { parse_error(self, t, "unexpected '.'"); ret 0; }
|
||||||
@@ -1354,10 +1531,7 @@ fn parse_primary(self: *Parser): usize {
|
|||||||
var fields = parse_aggregate_fields(self);
|
var fields = parse_aggregate_fields(self);
|
||||||
var an = Ast.node(self.ast, ref);
|
var an = Ast.node(self.ast, ref);
|
||||||
match an.body {
|
match an.body {
|
||||||
.ExprAggregateLit(a) => {
|
.ExprAggregateLit(&a) => { a.fields = fields; },
|
||||||
a.fields = fields;
|
|
||||||
an.body = .ExprAggregateLit(a);
|
|
||||||
},
|
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
ret ref;
|
ret ref;
|
||||||
@@ -1379,13 +1553,11 @@ fn parse_primary(self: *Parser): usize {
|
|||||||
if is_ident { ret parse_variant_construct(self, t); }
|
if is_ident { ret parse_variant_construct(self, t); }
|
||||||
parse_error(self, nx, "expected '{', '[' or identifier after '.'");
|
parse_error(self, nx, "expected '{', '[' or identifier after '.'");
|
||||||
ret 0;
|
ret 0;
|
||||||
}
|
},
|
||||||
if @enum2int(p) == @enum2int(Punct.LBracket) { ret parse_array_literal(self); }
|
.LBracket => { ret parse_array_literal(self); },
|
||||||
if @enum2int(p) == @enum2int(Punct.LBrace) { ret parse_block(self); }
|
.LBrace => { ret parse_block(self); },
|
||||||
if @enum2int(p) == @enum2int(Punct.At) || @enum2int(p) == @enum2int(Punct.Sharp) {
|
.At, .Sharp => { ret parse_comptime_call(self); },
|
||||||
ret parse_comptime_call(self);
|
.LParen => {
|
||||||
}
|
|
||||||
if @enum2int(p) == @enum2int(Punct.LParen) {
|
|
||||||
advance(self);
|
advance(self);
|
||||||
var elems = ref_vec();
|
var elems = ref_vec();
|
||||||
if at_punct(self, .RParen) {
|
if at_punct(self, .RParen) {
|
||||||
@@ -1394,12 +1566,12 @@ fn parse_primary(self: *Parser): usize {
|
|||||||
}
|
}
|
||||||
var first = parse_expr(self);
|
var first = parse_expr(self);
|
||||||
if at_punct(self, .Comma) {
|
if at_punct(self, .Comma) {
|
||||||
match Vec.push(&elems, &first as *u8) { .Ok => {}, .Err(e) => { ret 0; } }
|
Vec.push(&elems, &first as *u8);
|
||||||
while at_punct(self, .Comma) {
|
while at_punct(self, .Comma) {
|
||||||
advance(self);
|
advance(self);
|
||||||
if at_punct(self, .RParen) { break; }
|
if at_punct(self, .RParen) { break; }
|
||||||
var nx = parse_expr(self);
|
var nx = parse_expr(self);
|
||||||
match Vec.push(&elems, &nx as *u8) { .Ok => {}, .Err(e) => { ret 0; } }
|
Vec.push(&elems, &nx as *u8);
|
||||||
}
|
}
|
||||||
expect_punct(self, .RParen, "')' in tuple literal");
|
expect_punct(self, .RParen, "')' in tuple literal");
|
||||||
ret new_node(self, .ExprTuple(.{ .items = elems }), t);
|
ret new_node(self, .ExprTuple(.{ .items = elems }), t);
|
||||||
@@ -1407,6 +1579,8 @@ fn parse_primary(self: *Parser): usize {
|
|||||||
expect_punct(self, .RParen, "')' in paren expression");
|
expect_punct(self, .RParen, "')' in paren expression");
|
||||||
Vec.drop(&elems);
|
Vec.drop(&elems);
|
||||||
ret first;
|
ret first;
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
@@ -1513,6 +1687,15 @@ fn parse_comptime_call(self: *Parser): usize {
|
|||||||
args = parse_expr_list(self);
|
args = parse_expr_list(self);
|
||||||
if !expect_punct(self, .RParen, "')' in comptime call") { ret 0; }
|
if !expect_punct(self, .RParen, "')' in comptime call") { ret 0; }
|
||||||
}
|
}
|
||||||
|
// `@name(args){ ... }`:构造该编译期类型的值(聚合初始化)。
|
||||||
|
if at_punct(self, .LBrace) && self.stop_agg == 0 {
|
||||||
|
var fields = parse_aggregate_fields(self);
|
||||||
|
ret new_node(self, .ExprComptimeInit(.{
|
||||||
|
.name = name.text,
|
||||||
|
.args = args,
|
||||||
|
.fields = fields,
|
||||||
|
}), start);
|
||||||
|
}
|
||||||
ret new_node(self, .ExprComptimeCall(.{ .name = name.text, .args = args }), start);
|
ret new_node(self, .ExprComptimeCall(.{ .name = name.text, .args = args }), start);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1547,7 +1730,6 @@ fn parse_expr_list(self: *Parser): Vec {
|
|||||||
ret vec;
|
ret vec;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- if / while / loop / for / match ----
|
|
||||||
|
|
||||||
fn parse_if_expr(self: *Parser): usize {
|
fn parse_if_expr(self: *Parser): usize {
|
||||||
var kw = peek(self);
|
var kw = peek(self);
|
||||||
@@ -1714,7 +1896,7 @@ fn parse_match_expr(self: *Parser): usize {
|
|||||||
if at_punct(self, .Comma) { advance(self); }
|
if at_punct(self, .Comma) { advance(self); }
|
||||||
}
|
}
|
||||||
if !expect_punct(self, .RBrace, "'}' in match") { ret 0; }
|
if !expect_punct(self, .RBrace, "'}' in match") { ret 0; }
|
||||||
// stage1 先分配 MATCH_EXPR,再逐个分配 MATCH_ARM。
|
// 先分配 MATCH_EXPR,再逐个分配 MATCH_ARM。
|
||||||
var ref = new_node(self, .ExprMatch(.{
|
var ref = new_node(self, .ExprMatch(.{
|
||||||
.matched_expr = matched,
|
.matched_expr = matched,
|
||||||
.arms = ref_vec(),
|
.arms = ref_vec(),
|
||||||
@@ -1743,7 +1925,6 @@ fn parse_match_expr(self: *Parser): usize {
|
|||||||
ret ref;
|
ret ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 语句 ----
|
|
||||||
|
|
||||||
fn parse_ret_stmt(self: *Parser): usize {
|
fn parse_ret_stmt(self: *Parser): usize {
|
||||||
var kw = peek(self);
|
var kw = peek(self);
|
||||||
@@ -1751,7 +1932,18 @@ fn parse_ret_stmt(self: *Parser): usize {
|
|||||||
advance(self);
|
advance(self);
|
||||||
var expr: usize = 0;
|
var expr: usize = 0;
|
||||||
var t = peek(self);
|
var t = peek(self);
|
||||||
if t != null && can_start_expr(t.kind) { expr = parse_expr(self); }
|
var can = false;
|
||||||
|
if t != null {
|
||||||
|
can = can_start_expr(t.kind);
|
||||||
|
// comptime 上下文:允许返回类型表达式(如 `ret struct { ... };`)。
|
||||||
|
if !can && self.comptime_depth > 0 {
|
||||||
|
if kind_is_keyword(t.kind, .Struct) || kind_is_keyword(t.kind, .Union) ||
|
||||||
|
kind_is_keyword(t.kind, .Enum) || kind_is_keyword(t.kind, .Shape) {
|
||||||
|
can = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if can { expr = parse_expr(self); }
|
||||||
t = peek(self);
|
t = peek(self);
|
||||||
if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); }
|
if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); }
|
||||||
ret new_node(self, .StatementRet(.{ .expr = expr }), kw);
|
ret new_node(self, .StatementRet(.{ .expr = expr }), kw);
|
||||||
@@ -1796,6 +1988,22 @@ fn parse_defer_stmt(self: *Parser): usize {
|
|||||||
ret new_node(self, .StatementDefer(.{ .body = body }), kw);
|
ret new_node(self, .StatementDefer(.{ .body = body }), kw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_errdefer_stmt(self: *Parser): usize {
|
||||||
|
var kw = peek(self);
|
||||||
|
if kw == null || !kind_is_keyword(kw.kind, .Errdefer) { ret 0; }
|
||||||
|
advance(self);
|
||||||
|
var body: usize = 0;
|
||||||
|
var t = peek(self);
|
||||||
|
if t != null && kind_is_punct(t.kind, .LBrace) { body = parse_block(self); }
|
||||||
|
else if t != null && can_start_statement(t.kind) { body = parse_statement(self); }
|
||||||
|
else {
|
||||||
|
body = parse_expr(self);
|
||||||
|
t = peek(self);
|
||||||
|
if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); }
|
||||||
|
}
|
||||||
|
ret new_node(self, .StatementErrdefer(.{ .body = body }), kw);
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_comptime_stmt(self: *Parser): usize {
|
fn parse_comptime_stmt(self: *Parser): usize {
|
||||||
var kw = peek(self);
|
var kw = peek(self);
|
||||||
if kw == null || !kind_is_keyword(kw.kind, .Comptime) { ret 0; }
|
if kw == null || !kind_is_keyword(kw.kind, .Comptime) { ret 0; }
|
||||||
@@ -1813,26 +2021,26 @@ fn parse_statement(self: *Parser): usize {
|
|||||||
|
|
||||||
match t.kind {
|
match t.kind {
|
||||||
.Keyword(k) => {
|
.Keyword(k) => {
|
||||||
if @enum2int(k) == @enum2int(Keyword.Ret) { ret parse_ret_stmt(self); }
|
match k {
|
||||||
if @enum2int(k) == @enum2int(Keyword.Break) { ret parse_break_stmt(self); }
|
.Ret => { ret parse_ret_stmt(self); },
|
||||||
if @enum2int(k) == @enum2int(Keyword.Continue) { ret parse_continue_stmt(self); }
|
.Break => { ret parse_break_stmt(self); },
|
||||||
if @enum2int(k) == @enum2int(Keyword.Defer) { ret parse_defer_stmt(self); }
|
.Continue => { ret parse_continue_stmt(self); },
|
||||||
if @enum2int(k) == @enum2int(Keyword.Comptime) { ret parse_comptime_stmt(self); }
|
.Defer => { ret parse_defer_stmt(self); },
|
||||||
if @enum2int(k) == @enum2int(Keyword.Var) ||
|
.Errdefer => { ret parse_errdefer_stmt(self); },
|
||||||
@enum2int(k) == @enum2int(Keyword.Const) ||
|
.Comptime => { ret parse_comptime_stmt(self); },
|
||||||
@enum2int(k) == @enum2int(Keyword.Type) {
|
.Var, .Const, .Type => {
|
||||||
var tags = ref_vec();
|
var tags = ref_vec();
|
||||||
parse_tag_list(self, &tags, false, false);
|
parse_tag_list(self, &tags, false, false, false);
|
||||||
var decl: usize = 0;
|
var decl: usize = 0;
|
||||||
if @enum2int(k) == @enum2int(Keyword.Var) {
|
match k {
|
||||||
decl = parse_var_const_decl(self, tags, false);
|
.Var => { decl = parse_var_const_decl(self, tags, false); },
|
||||||
} else if @enum2int(k) == @enum2int(Keyword.Const) {
|
.Const => { decl = parse_var_const_decl(self, tags, true); },
|
||||||
decl = parse_var_const_decl(self, tags, true);
|
_ => { decl = parse_type_decl(self, tags); },
|
||||||
} else {
|
|
||||||
decl = parse_type_decl(self, tags);
|
|
||||||
}
|
}
|
||||||
if decl == 0 { ret 0; }
|
if decl == 0 { ret 0; }
|
||||||
ret new_node(self, .StatementDecl(.{ .decl = decl }), t);
|
ret new_node(self, .StatementDecl(.{ .decl = decl }), t);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
@@ -1937,7 +2145,7 @@ fn block_push_stmt(self: *Parser, ref: usize, stmt: usize) {
|
|||||||
var n = Ast.node(self.ast, ref);
|
var n = Ast.node(self.ast, ref);
|
||||||
match n.body {
|
match n.body {
|
||||||
.ExprBlock(b) => {
|
.ExprBlock(b) => {
|
||||||
match Vec.push(&b.stmts, &stmt as *u8) { .Ok => {}, .Err(e) => {} }
|
Vec.push(&b.stmts, &stmt as *u8);
|
||||||
n.body = .ExprBlock(b);
|
n.body = .ExprBlock(b);
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
@@ -1958,7 +2166,6 @@ fn block_set_tail(self: *Parser, ref: usize, expr: usize) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 入口 ----
|
|
||||||
|
|
||||||
fn skip_to_sync(self: *Parser) {
|
fn skip_to_sync(self: *Parser) {
|
||||||
while !self.failed {
|
while !self.failed {
|
||||||
@@ -1973,7 +2180,7 @@ fn skip_to_sync(self: *Parser) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析整个文件;Root/ContainerMembers 最后分配(与 stage1 一致)。
|
// 解析整个文件;Root/ContainerMembers 最后分配(#ref 顺序须与 splc0 一致)。
|
||||||
fn parse(self: *Parser) {
|
fn parse(self: *Parser) {
|
||||||
var external_tags = ref_vec();
|
var external_tags = ref_vec();
|
||||||
var declarations = ref_vec();
|
var declarations = ref_vec();
|
||||||
@@ -1989,7 +2196,7 @@ fn parse(self: *Parser) {
|
|||||||
var t2 = peek(self);
|
var t2 = peek(self);
|
||||||
self.pos = save;
|
self.pos = save;
|
||||||
if t2 != null && kind_is_punct(t2.kind, .Not) {
|
if t2 != null && kind_is_punct(t2.kind, .Not) {
|
||||||
parse_tag_list(self, &external_tags, true, true);
|
parse_tag_list(self, &external_tags, true, true, true);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2016,7 +2223,6 @@ fn parse(self: *Parser) {
|
|||||||
self.ast.root = root;
|
self.ast.root = root;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn lex_tokens(src: []u8): Vec {
|
fn lex_tokens(src: []u8): Vec {
|
||||||
var out = Vec.blank(@sizeof(Token), @alignof(Token));
|
var out = Vec.blank(@sizeof(Token), @alignof(Token));
|
||||||
@@ -2080,7 +2286,7 @@ fn expect_text(buf: *ByteBuffer, expected: []u8) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 与 stage1 `splc0 --dump ast` 输出逐字节一致(含 #ref 与 [line:col])。
|
// 与 splc0 --dump ast 逐字节一致(含 #ref 与 [line:col])。
|
||||||
fn test_parse_decls() {
|
fn test_parse_decls() {
|
||||||
var src: []u8 = "type Point = struct { x: i32, y: i32 };\nfn f(a: i32): i32;\n";
|
var src: []u8 = "type Point = struct { x: i32, y: i32 };\nfn f(a: i32): i32;\n";
|
||||||
var toks = lex_tokens(src);
|
var toks = lex_tokens(src);
|
||||||
@@ -2112,7 +2318,7 @@ fn test_parse_decls() {
|
|||||||
"AST:\n`-root #13\n `-container_members #12\n |-type_decl #2 [1:1]\n | |-pattern_ident #1 [1:6]\n | `-type_aggregate #3 [1:14]\n | |-member_decl #5 [1:23]\n | | |-type_path #4 [1:26]\n | `-member_decl #7 [1:31]\n | |-type_path #6 [1:34]\n `-fn_decl #11 [2:1]\n `-param_decl #9 [2:6]\n `-type_path #8 [2:9]\n |-type_path #10 [2:15]\n");
|
"AST:\n`-root #13\n `-container_members #12\n |-type_decl #2 [1:1]\n | |-pattern_ident #1 [1:6]\n | `-type_aggregate #3 [1:14]\n | |-member_decl #5 [1:23]\n | | |-type_path #4 [1:26]\n | `-member_decl #7 [1:31]\n | |-type_path #6 [1:34]\n `-fn_decl #11 [2:1]\n `-param_decl #9 [2:6]\n `-type_path #8 [2:9]\n |-type_path #10 [2:15]\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 表达式/语句/块:与 001_main.spl 的 stage1 dump 逐字节一致。
|
// 表达式/语句/块:与 splc0 dump 逐字节一致。
|
||||||
fn test_parse_main() {
|
fn test_parse_main() {
|
||||||
var src: []u8 = "// Expect retcode = 0\n\nfn main(): i32 {\n ret 0;\n}\n";
|
var src: []u8 = "// Expect retcode = 0\n\nfn main(): i32 {\n ret 0;\n}\n";
|
||||||
var toks = lex_tokens(src);
|
var toks = lex_tokens(src);
|
||||||
@@ -2144,7 +2350,7 @@ fn test_parse_main() {
|
|||||||
"AST:\n`-root #7\n `-container_members #6\n `-fn_decl #5 [3:1]\n |-type_path #1 [3:12]\n `-block #2 [3:16]\n `-statement_ret #4 [4:5]\n `-int_lit #3 [4:9]\n");
|
"AST:\n`-root #7\n `-container_members #6\n `-fn_decl #5 [3:1]\n |-type_path #1 [3:12]\n `-block #2 [3:16]\n `-statement_ret #4 [4:5]\n `-int_lit #3 [4:9]\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 任意宽度整数:2^64 也能正确解析(stage1 u64 之外);含分隔符与多进制。
|
// 任意宽度整数:2^64 也能正确解析;含分隔符与多进制。
|
||||||
fn check_int(src: []u8, expected: []u8) {
|
fn check_int(src: []u8, expected: []u8) {
|
||||||
var toks = lex_tokens(src);
|
var toks = lex_tokens(src);
|
||||||
defer { Vec.drop(&toks); }
|
defer { Vec.drop(&toks); }
|
||||||
@@ -2183,10 +2389,60 @@ fn test_big_int_literal() {
|
|||||||
check_int("0o_17", "15");
|
check_int("0o_17", "15");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expect_contains(buf: *ByteBuffer, needle: []u8) {
|
||||||
|
var actual = ByteBuffer.as_slice(buf.*);
|
||||||
|
if needle.len > actual.len { @assert(false, "contains length"); }
|
||||||
|
var i: usize = 0;
|
||||||
|
while i + needle.len <= actual.len {
|
||||||
|
var j: usize = 0;
|
||||||
|
var ok = true;
|
||||||
|
while j < needle.len {
|
||||||
|
if actual.ptr[i + j] != needle.ptr[j] { ok = false; break; }
|
||||||
|
j = j + 1;
|
||||||
|
}
|
||||||
|
if ok { ret; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
@assert(false, "expected substring not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// stage3 语法:`comptime fn ... (t: type) type { ... }` 与省略冒号的返回类型。
|
||||||
|
fn test_parse_comptime_fn() {
|
||||||
|
var src: []u8 = "comptime fn vec(t: type) type { ret t; }\n";
|
||||||
|
var toks = lex_tokens(src);
|
||||||
|
defer { Vec.drop(&toks); }
|
||||||
|
|
||||||
|
var sources = set_new();
|
||||||
|
defer { set_drop(&sources); }
|
||||||
|
match set_add(&sources, ByteView.from_slice("test.spl"), ByteView.from_slice(src)) {
|
||||||
|
.Ok(index) => {},
|
||||||
|
.Err(error) => { @assert(false, "set_add"); },
|
||||||
|
}
|
||||||
|
|
||||||
|
var ast: Ast = undefined;
|
||||||
|
match Ast.new(&sources) {
|
||||||
|
.Ok(v) => { ast = v; },
|
||||||
|
.Err(error) => { @assert(false, "ast new"); },
|
||||||
|
}
|
||||||
|
defer { Ast.drop(&ast); }
|
||||||
|
|
||||||
|
var p = new(&ast, toks, null);
|
||||||
|
parse(&p);
|
||||||
|
@assert(!p.failed, "comptime fn parse ok");
|
||||||
|
@assert(p.error_count == 0, "comptime fn no errors");
|
||||||
|
|
||||||
|
var buf = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&buf); }
|
||||||
|
@assert(Ast.dump(&ast, &buf), "dump");
|
||||||
|
expect_contains(&buf, "fn_decl");
|
||||||
|
expect_contains(&buf, "type_path");
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
test_peek_advance();
|
test_peek_advance();
|
||||||
test_new_node_span();
|
test_new_node_span();
|
||||||
test_parse_decls();
|
test_parse_decls();
|
||||||
test_parse_main();
|
test_parse_main();
|
||||||
test_big_int_literal();
|
test_big_int_literal();
|
||||||
|
test_parse_comptime_fn();
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
// 作用域树:名字 -> Symbol,节点经 parent 上溯查找(支持遮蔽)。名字借用源字节,
|
||||||
|
// 存活由调用方保证;node 为 def.spl 的 DefId。
|
||||||
|
|
||||||
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
|
type .{ .Map, .hash_kind_view } = @import("utils/map.spl");
|
||||||
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||||
|
type .{ .hash_view, .eq_view } = @import("utils/hash.spl");
|
||||||
|
|
||||||
|
type SymbolKind = enum {
|
||||||
|
Error,
|
||||||
|
Var,
|
||||||
|
Member,
|
||||||
|
Fn,
|
||||||
|
Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Symbol = struct {
|
||||||
|
name: ByteView,
|
||||||
|
kind: SymbolKind,
|
||||||
|
node: usize, // DefId
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScopeNode = struct {
|
||||||
|
parent: usize,
|
||||||
|
symbols: Map,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Scope = @self;
|
||||||
|
|
||||||
|
scopes: Vec, // ScopeNode
|
||||||
|
root_scope: usize,
|
||||||
|
current_scope: usize,
|
||||||
|
|
||||||
|
type ScopeResult = enum {
|
||||||
|
Ok: Scope,
|
||||||
|
Err: Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_map(): Map {
|
||||||
|
ret Map.new(@sizeof(ByteView), @alignof(ByteView), @sizeof(Symbol), @alignof(Symbol),
|
||||||
|
hash_kind_view());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(): ScopeResult {
|
||||||
|
var sc = Scope {
|
||||||
|
.scopes = Vec.blank(@sizeof(ScopeNode), @alignof(ScopeNode)),
|
||||||
|
.root_scope = 0,
|
||||||
|
.current_scope = 0,
|
||||||
|
};
|
||||||
|
var sentinel = ScopeNode { .parent = 0, .symbols = new_map() };
|
||||||
|
match Vec.push(&sc.scopes, &sentinel as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => {
|
||||||
|
Vec.drop(&sc.scopes);
|
||||||
|
Map.drop(&sentinel.symbols);
|
||||||
|
ret .Err(error);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ret .Ok(sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drop(self: *Scope) {
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < self.scopes.len {
|
||||||
|
var n = node(self, i);
|
||||||
|
if n != null {
|
||||||
|
Map.drop(&n.symbols);
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
Vec.drop(&self.scopes);
|
||||||
|
self.root_scope = 0;
|
||||||
|
self.current_scope = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// id 0 与越界返回 null。
|
||||||
|
fn node(self: *Scope, id: usize): *ScopeNode {
|
||||||
|
if id == 0 || id >= self.scopes.len { ret null; }
|
||||||
|
ret (self.scopes.data as *ScopeNode) + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新作用域 parent = 当前作用域;不自动切换 current(调用方负责,见 sema)。
|
||||||
|
fn alloc(self: *Scope): usize {
|
||||||
|
var n = ScopeNode { .parent = self.current_scope, .symbols = new_map() };
|
||||||
|
match Vec.push(&self.scopes, &n as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => {
|
||||||
|
Map.drop(&n.symbols);
|
||||||
|
ret 0;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ret self.scopes.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 插入符号;同名已存在返回 false(不改动)。
|
||||||
|
fn insert(self: *Scope, id: usize, symbol: Symbol): bool {
|
||||||
|
if id == 0 || id >= self.scopes.len { ret false; }
|
||||||
|
var n = node(self, id);
|
||||||
|
if n == null { ret false; }
|
||||||
|
var old = Symbol {
|
||||||
|
.name = ByteView.empty(),
|
||||||
|
.kind = .Error,
|
||||||
|
.node = 0,
|
||||||
|
};
|
||||||
|
match Map.get(&n.symbols, &symbol.name as *u8, &old as *u8) {
|
||||||
|
.Found => { ret false; },
|
||||||
|
.NotFound => {},
|
||||||
|
}
|
||||||
|
match Map.put(&n.symbols, &symbol.name as *u8, &symbol as *u8) {
|
||||||
|
.Ok => { ret true; },
|
||||||
|
.Err(error) => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从 current_scope 沿父链查找;找到填 out 返回 true。
|
||||||
|
fn find(self: *Scope, name: ByteView, out: *Symbol): bool {
|
||||||
|
if out == null { ret false; }
|
||||||
|
var id = self.current_scope;
|
||||||
|
while id != 0 {
|
||||||
|
var n = node(self, id);
|
||||||
|
if n == null { ret false; }
|
||||||
|
match Map.get(&n.symbols, &name as *u8, out as *u8) {
|
||||||
|
.Found => { ret true; },
|
||||||
|
.NotFound => {},
|
||||||
|
}
|
||||||
|
id = n.parent;
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn sym(name: []u8, kind: SymbolKind, node: usize): Symbol {
|
||||||
|
ret Symbol {
|
||||||
|
.name = ByteView.from_slice(name),
|
||||||
|
.kind = kind,
|
||||||
|
.node = node,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_alloc_and_find() {
|
||||||
|
var sc: Scope = undefined;
|
||||||
|
match new() {
|
||||||
|
.Ok(v) => { sc = v; },
|
||||||
|
.Err(error) => { @assert(false, "scope new failed"); },
|
||||||
|
}
|
||||||
|
defer { drop(&sc); }
|
||||||
|
|
||||||
|
@assert(sc.root_scope == 0 && sc.current_scope == 0, "initial");
|
||||||
|
|
||||||
|
// root
|
||||||
|
var root = alloc(&sc);
|
||||||
|
sc.root_scope = root;
|
||||||
|
sc.current_scope = root;
|
||||||
|
|
||||||
|
// module scope(child of root)
|
||||||
|
var mod = alloc(&sc);
|
||||||
|
sc.current_scope = mod;
|
||||||
|
|
||||||
|
@assert(insert(&sc, mod, sym("x", .Var, 11)), "insert x");
|
||||||
|
@assert(!insert(&sc, mod, sym("x", .Var, 12)), "duplicate x");
|
||||||
|
|
||||||
|
// 函数作用域
|
||||||
|
var fn_scope = alloc(&sc);
|
||||||
|
sc.current_scope = fn_scope;
|
||||||
|
|
||||||
|
var out = sym("", .Error, 0);
|
||||||
|
@assert(find(&sc, ByteView.from_slice("x"), &out), "find x from child");
|
||||||
|
@assert(out.node == 11, "x def id");
|
||||||
|
|
||||||
|
// 遮蔽:函数内层再定义 x
|
||||||
|
@assert(insert(&sc, fn_scope, sym("x", .Fn, 99)), "shadow x");
|
||||||
|
@assert(find(&sc, ByteView.from_slice("x"), &out), "find shadow");
|
||||||
|
@assert(out.node == 99, "shadow def id");
|
||||||
|
|
||||||
|
// 未定义
|
||||||
|
@assert(!find(&sc, ByteView.from_slice("nope"), &out), "missing");
|
||||||
|
|
||||||
|
// 父链结构
|
||||||
|
var m = node(&sc, mod);
|
||||||
|
var f = node(&sc, fn_scope);
|
||||||
|
@assert(m != null && m.parent == root, "mod parent");
|
||||||
|
@assert(f != null && f.parent == mod, "fn parent");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_kinds_isolated() {
|
||||||
|
var sc: Scope = undefined;
|
||||||
|
match new() {
|
||||||
|
.Ok(v) => { sc = v; },
|
||||||
|
.Err(error) => { @assert(false, "scope new failed"); },
|
||||||
|
}
|
||||||
|
defer { drop(&sc); }
|
||||||
|
|
||||||
|
var a = alloc(&sc);
|
||||||
|
sc.current_scope = a;
|
||||||
|
@assert(insert(&sc, a, sym("T", .Type, 1)), "type T");
|
||||||
|
@assert(insert(&sc, a, sym("f", .Fn, 2)), "fn f");
|
||||||
|
|
||||||
|
var out = sym("", .Error, 0);
|
||||||
|
@assert(find(&sc, ByteView.from_slice("T"), &out), "find T");
|
||||||
|
var kind_matches = false;
|
||||||
|
match out.kind {
|
||||||
|
.Type => { kind_matches = true; },
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
@assert(kind_matches, "T kind");
|
||||||
|
@assert(find(&sc, ByteView.from_slice("f"), &out), "find f");
|
||||||
|
@assert(out.node == 2, "f def id");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_alloc_and_find();
|
||||||
|
test_kinds_isolated();
|
||||||
|
}
|
||||||
+3087
File diff suppressed because it is too large.
Load diff
+605
-23
@@ -1,8 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// splc1 (stage2 自举编译器入口)。
|
// splc1:自举编译器入口。`splc1 <in> <out>` 编译;`--dump <mode> <file>` 打印各阶段
|
||||||
// 当前仅实现 `--dump lex <file> [out]`:读取源文件,词法分析,按与 stage1
|
// 产物,格式必须与 splc0 逐字节一致以通过 diff。
|
||||||
// splc0 完全一致的格式打印 token 列表,供 diff 测试逐字节比对。
|
|
||||||
|
|
||||||
type .{ .ByteView } = @import("./utils/byte_view.spl");
|
type .{ .ByteView } = @import("./utils/byte_view.spl");
|
||||||
type .{ .ByteBuffer } = @import("./utils/byte_buffer.spl");
|
type .{ .ByteBuffer } = @import("./utils/byte_buffer.spl");
|
||||||
@@ -11,11 +10,22 @@ type .{ .Token, .kind_name } = @import("./token.spl");
|
|||||||
type .{ .Lexer } = @import("./lexer.spl");
|
type .{ .Lexer } = @import("./lexer.spl");
|
||||||
type Ast = @import("./ast.spl");
|
type Ast = @import("./ast.spl");
|
||||||
type Parser = @import("./parser.spl");
|
type Parser = @import("./parser.spl");
|
||||||
|
type Sema = @import("./sema.spl");
|
||||||
type .{ .append_usize } = @import("./utils/fmt.spl");
|
type .{ .append_usize } = @import("./utils/fmt.spl");
|
||||||
type .{ .read_all, .print, .eprint } = @import("./utils/io.spl");
|
type .{ .read_all, .print, .eprint, .open_cstr, .write_all, .File } = @import("./utils/io.spl");
|
||||||
type .{ .set_new, .set_add, .set_drop } = @import("./utils/source.spl");
|
type .{ .Sources, .Source, .set_new, .set_add, .set_at, .set_drop } = @import("./utils/source.spl");
|
||||||
type Diag = @import("./diag.spl");
|
type .{ .Diag, .Code, .span_none } = @import("./diag.spl");
|
||||||
type Logger = @import("./utils/log.spl");
|
type Logger = @import("./utils/log.spl");
|
||||||
|
type .{ .Loader, .LoadResult } = @import("./sema.spl");
|
||||||
|
type IrBuilder = @import("./ir_builder.spl");
|
||||||
|
type Ir = @import("./ir.spl");
|
||||||
|
type Ast2Ir = @import("./ast2ir.spl");
|
||||||
|
type LayoutM = @import("./layout.spl");
|
||||||
|
type .{ .Layout } = @import("./layout.spl");
|
||||||
|
type Ir2VmM = @import("./ir2vm.spl");
|
||||||
|
type .{ .Ir2Vm } = @import("./ir2vm.spl");
|
||||||
|
type Ir2CM = @import("./ir2c.spl");
|
||||||
|
type .{ .alloc, .free } = @import("./utils/memory.spl");
|
||||||
|
|
||||||
fn token_at(tokens: *Vec, index: usize): Token {
|
fn token_at(tokens: *Vec, index: usize): Token {
|
||||||
match Vec.get(tokens, index) {
|
match Vec.get(tokens, index) {
|
||||||
@@ -119,14 +129,11 @@ fn dump_lex(log: *Logger, path: ByteView): i32 {
|
|||||||
}
|
}
|
||||||
defer { Vec.drop(&tokens); }
|
defer { Vec.drop(&tokens); }
|
||||||
|
|
||||||
// 非法输入:走诊断(stage1 在此处是宽容无视,stage2 显式报错)。
|
// 非法输入:走诊断(显式报错,不静默)。
|
||||||
if Diag.has_error(diag) {
|
if Diag.has_error(diag) {
|
||||||
var rendered = ByteBuffer.new();
|
var rendered = ByteBuffer.new();
|
||||||
defer { ByteBuffer.drop(&rendered); }
|
defer { ByteBuffer.drop(&rendered); }
|
||||||
match Diag.render_human(&diag, &rendered) {
|
Diag.render_human(&diag, &rendered);
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => {},
|
|
||||||
}
|
|
||||||
eprint(ByteBuffer.as_slice(rendered));
|
eprint(ByteBuffer.as_slice(rendered));
|
||||||
ret 1;
|
ret 1;
|
||||||
}
|
}
|
||||||
@@ -188,10 +195,7 @@ fn dump_ast(log: *Logger, path: ByteView): i32 {
|
|||||||
if Diag.has_error(diag) || p.failed {
|
if Diag.has_error(diag) || p.failed {
|
||||||
var rendered = ByteBuffer.new();
|
var rendered = ByteBuffer.new();
|
||||||
defer { ByteBuffer.drop(&rendered); }
|
defer { ByteBuffer.drop(&rendered); }
|
||||||
match Diag.render_human(&diag, &rendered) {
|
Diag.render_human(&diag, &rendered);
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => {},
|
|
||||||
}
|
|
||||||
eprint(ByteBuffer.as_slice(rendered));
|
eprint(ByteBuffer.as_slice(rendered));
|
||||||
ret 1;
|
ret 1;
|
||||||
}
|
}
|
||||||
@@ -209,31 +213,609 @@ fn dump_ast(log: *Logger, path: ByteView): i32 {
|
|||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type ModEntry = struct {
|
||||||
|
path: ByteView,
|
||||||
|
ast: *Ast,
|
||||||
|
ok: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoaderCtx = struct {
|
||||||
|
sources: *Sources,
|
||||||
|
mods: Vec, // ModEntry
|
||||||
|
buffers: Vec, // ByteBuffer(路径/文本稳定存储)
|
||||||
|
diag: *Diag,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn append_bytes(buf: *ByteBuffer, bytes: []u8): bool {
|
||||||
|
match ByteBuffer.append(buf, bytes) {
|
||||||
|
.Ok => { ret true; },
|
||||||
|
.Err(error) => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append_byte(buf: *ByteBuffer, b: u8): bool {
|
||||||
|
match ByteBuffer.append_byte(buf, b) {
|
||||||
|
.Ok => { ret true; },
|
||||||
|
.Err(error) => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sub(p: ByteView, a: usize, b: usize): []u8 {
|
||||||
|
ret ByteView.as_slice(p)[a..b];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 "..":弹出 out 的最后一个路径分量;无分量可弹(空 / 根 / 末段已是 "..")时,
|
||||||
|
// 退化为追加 ".."(相对路径的语义),绝对根则忽略。这样 "../x" 与规范路径能归一到同一模块。
|
||||||
|
fn normalize_pop(out: *ByteBuffer) {
|
||||||
|
var cur = ByteBuffer.as_slice(out.*);
|
||||||
|
if cur.len == 0 {
|
||||||
|
append_bytes(out, "..");
|
||||||
|
ret;
|
||||||
|
}
|
||||||
|
var end = cur.len;
|
||||||
|
if cur.ptr[end - 1] == '/' {
|
||||||
|
if end == 1 { ret; } // 绝对根 "/":忽略
|
||||||
|
end = end - 1;
|
||||||
|
}
|
||||||
|
var start = end;
|
||||||
|
while start > 0 && cur.ptr[start - 1] != '/' { start = start - 1; }
|
||||||
|
if end - start == 2 && cur.ptr[start] == '.' && cur.ptr[start + 1] == '.' {
|
||||||
|
append_bytes(out, "..");
|
||||||
|
ret;
|
||||||
|
}
|
||||||
|
var new_len: usize = 0;
|
||||||
|
if start > 0 { new_len = start - 1; }
|
||||||
|
if new_len == 0 && cur.ptr[0] == '/' { new_len = 1; }
|
||||||
|
out.*.storage.len = new_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 折叠重复分隔符、解析 "." 与 ".."、'\' 统一成 '/'。
|
||||||
|
fn normalize_into(out: *ByteBuffer, p: ByteView) {
|
||||||
|
if p.len > 0 && (p.ptr[0] == '/' || p.ptr[0] == '\\') {
|
||||||
|
append_byte(out, '/');
|
||||||
|
}
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < p.len {
|
||||||
|
while i < p.len && (p.ptr[i] == '/' || p.ptr[i] == '\\') { i = i + 1; }
|
||||||
|
if i >= p.len { break; }
|
||||||
|
var start = i;
|
||||||
|
while i < p.len && p.ptr[i] != '/' && p.ptr[i] != '\\' { i = i + 1; }
|
||||||
|
var seg = sub(p, start, i);
|
||||||
|
if seg.len == 1 && seg.ptr[0] == '.' { continue; }
|
||||||
|
if seg.len == 2 && seg.ptr[0] == '.' && seg.ptr[1] == '.' {
|
||||||
|
normalize_pop(out);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var cur = ByteBuffer.as_slice(out.*);
|
||||||
|
if cur.len > 0 && cur.ptr[cur.len - 1] != '/' {
|
||||||
|
append_byte(out, '/');
|
||||||
|
}
|
||||||
|
append_bytes(out, seg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 把 path 拼到 from 所在目录,并规范化;绝对路径直通。
|
||||||
|
fn resolve_path(from: ByteView, path: ByteView): ByteBuffer {
|
||||||
|
var out = ByteBuffer.new();
|
||||||
|
var absolute = false;
|
||||||
|
if path.len > 0 && (path.ptr[0] == '/' || path.ptr[0] == '\\') { absolute = true; }
|
||||||
|
if path.len >= 2 && path.ptr[1] == ':' { absolute = true; }
|
||||||
|
if absolute {
|
||||||
|
normalize_into(&out, path);
|
||||||
|
ret out;
|
||||||
|
}
|
||||||
|
var last: isize = -1;
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < from.len {
|
||||||
|
if from.ptr[i] == '/' || from.ptr[i] == '\\' { last = i as isize; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
if last >= 0 {
|
||||||
|
append_bytes(&out, sub(from, 0, (last as usize) + 1));
|
||||||
|
}
|
||||||
|
append_bytes(&out, ByteView.as_slice(path));
|
||||||
|
var norm = ByteBuffer.new();
|
||||||
|
normalize_into(&norm, ByteView.from_slice(ByteBuffer.as_slice(out)));
|
||||||
|
ByteBuffer.drop(&out);
|
||||||
|
ret norm;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mod_at(ctx: *LoaderCtx, i: usize): *ModEntry {
|
||||||
|
match Vec.get(&ctx.mods, i) {
|
||||||
|
.Ok(ptr) => { ret ptr as *ModEntry; },
|
||||||
|
.Err(error) => { ret null; },
|
||||||
|
}
|
||||||
|
ret null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn driver_load(user: *any, from: ByteView, path: ByteView, diag: *Diag): LoadResult {
|
||||||
|
var ctx = user as *LoaderCtx;
|
||||||
|
var absbuf = resolve_path(from, path);
|
||||||
|
var abs = ByteView.from_slice(ByteBuffer.as_slice(absbuf));
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < ctx.mods.len {
|
||||||
|
var m = mod_at(ctx, i);
|
||||||
|
if m != null && ByteView.eq(m.path, abs) {
|
||||||
|
ByteBuffer.drop(&absbuf);
|
||||||
|
if !m.ok {
|
||||||
|
Diag.err(diag, span_none(), .CircularDep, "@import: circular import");
|
||||||
|
ret .Missing;
|
||||||
|
}
|
||||||
|
ret .Ok(m.ast);
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var midx = ctx.mods.len;
|
||||||
|
var placeholder = ModEntry { .path = abs, .ast = null, .ok = false };
|
||||||
|
match Vec.push(&ctx.mods, &placeholder as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ByteBuffer.drop(&absbuf); ret .Missing; },
|
||||||
|
}
|
||||||
|
match Vec.push(&ctx.buffers, &absbuf as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ByteBuffer.drop(&absbuf); ret .Missing; },
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = ByteBuffer.new();
|
||||||
|
match read_all(abs, &content) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => {
|
||||||
|
Diag.err(diag, span_none(), .LexUnexpected, "@import: cannot open module");
|
||||||
|
ByteBuffer.drop(&content);
|
||||||
|
ret .Missing;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var text = ByteView.from_slice(ByteBuffer.as_slice(content));
|
||||||
|
var sidx: usize = 0;
|
||||||
|
match set_add(ctx.sources, abs, text) {
|
||||||
|
.Ok(index) => { sidx = index; },
|
||||||
|
.Err(error) => { ByteBuffer.drop(&content); ret .Missing; },
|
||||||
|
}
|
||||||
|
match Vec.push(&ctx.buffers, &content as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ByteBuffer.drop(&content); ret .Missing; },
|
||||||
|
}
|
||||||
|
|
||||||
|
var tokens = Vec.blank(@sizeof(Token), @alignof(Token));
|
||||||
|
match Lexer.lex(text, diag, 0) {
|
||||||
|
.Ok(v) => { tokens = v; },
|
||||||
|
.Err(error) => { Vec.drop(&tokens); ret .Missing; },
|
||||||
|
}
|
||||||
|
|
||||||
|
var ast_val: Ast = undefined;
|
||||||
|
match Ast.new(ctx.sources) {
|
||||||
|
.Ok(v) => { ast_val = v; },
|
||||||
|
.Err(error) => { Vec.drop(&tokens); ret .Missing; },
|
||||||
|
}
|
||||||
|
|
||||||
|
var p = Parser.new(&ast_val, tokens, diag);
|
||||||
|
p.file = sidx;
|
||||||
|
Parser.parse(&p);
|
||||||
|
Vec.drop(&tokens);
|
||||||
|
if Diag.has_error(diag.*) || p.failed {
|
||||||
|
Ast.drop(&ast_val);
|
||||||
|
ret .Missing;
|
||||||
|
}
|
||||||
|
|
||||||
|
var raw = alloc(@sizeof(Ast));
|
||||||
|
if raw == null {
|
||||||
|
Ast.drop(&ast_val);
|
||||||
|
ret .Missing;
|
||||||
|
}
|
||||||
|
var ap = raw as *Ast;
|
||||||
|
ap.* = ast_val;
|
||||||
|
|
||||||
|
var slot = mod_at(ctx, midx);
|
||||||
|
if slot != null {
|
||||||
|
slot.ast = ap;
|
||||||
|
slot.ok = true;
|
||||||
|
}
|
||||||
|
ret .Ok(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loader_ctx_drop(ctx: *LoaderCtx) {
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < ctx.mods.len {
|
||||||
|
var m = mod_at(ctx, i);
|
||||||
|
if m != null && m.ok && m.ast != null {
|
||||||
|
Ast.drop(m.ast);
|
||||||
|
free(m.ast as *any);
|
||||||
|
m.ast = null;
|
||||||
|
m.ok = false;
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
Vec.drop(&ctx.mods);
|
||||||
|
|
||||||
|
var j: usize = 0;
|
||||||
|
while j < ctx.buffers.len {
|
||||||
|
match Vec.get(&ctx.buffers, j) {
|
||||||
|
.Ok(ptr) => { ByteBuffer.drop(ptr as *ByteBuffer); },
|
||||||
|
.Err(error) => {},
|
||||||
|
}
|
||||||
|
j = j + 1;
|
||||||
|
}
|
||||||
|
Vec.drop(&ctx.buffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_sema(log: *Logger, path: ByteView): i32 {
|
||||||
|
var content = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&content); }
|
||||||
|
match read_all(path, &content) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => {
|
||||||
|
Logger.error(log, "cannot read input file");
|
||||||
|
ret 1;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var text = ByteView.from_slice(ByteBuffer.as_slice(content));
|
||||||
|
|
||||||
|
var sources = set_new();
|
||||||
|
defer { set_drop(&sources); }
|
||||||
|
match set_add(&sources, path, text) {
|
||||||
|
.Ok(index) => {},
|
||||||
|
.Err(error) => {
|
||||||
|
Logger.error(log, "cannot register source");
|
||||||
|
ret 1;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var diag = Diag.new(&sources);
|
||||||
|
defer { Diag.drop(&diag); }
|
||||||
|
|
||||||
|
var tokens = Vec.blank(@sizeof(Token), @alignof(Token));
|
||||||
|
match Lexer.lex(text, &diag, 0) {
|
||||||
|
.Ok(v) => { tokens = v; },
|
||||||
|
.Err(error) => {
|
||||||
|
Logger.error(log, "lex failed");
|
||||||
|
ret 1;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
defer { Vec.drop(&tokens); }
|
||||||
|
|
||||||
|
var ast: Ast = undefined;
|
||||||
|
match Ast.new(&sources) {
|
||||||
|
.Ok(v) => { ast = v; },
|
||||||
|
.Err(error) => {
|
||||||
|
Logger.error(log, "ast init failed");
|
||||||
|
ret 1;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
defer { Ast.drop(&ast); }
|
||||||
|
|
||||||
|
var p = Parser.new(&ast, tokens, &diag);
|
||||||
|
Parser.parse(&p);
|
||||||
|
|
||||||
|
if Diag.has_error(diag) || p.failed {
|
||||||
|
var rendered = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&rendered); }
|
||||||
|
Diag.render_human(&diag, &rendered);
|
||||||
|
eprint(ByteBuffer.as_slice(rendered));
|
||||||
|
ret 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ctx = LoaderCtx {
|
||||||
|
.sources = &sources,
|
||||||
|
.mods = Vec.blank(@sizeof(ModEntry), @alignof(ModEntry)),
|
||||||
|
.buffers = Vec.blank(@sizeof(ByteBuffer), @alignof(ByteBuffer)),
|
||||||
|
.diag = &diag,
|
||||||
|
};
|
||||||
|
defer { loader_ctx_drop(&ctx); }
|
||||||
|
var loader = Loader { .load = driver_load, .user = &ctx as *any };
|
||||||
|
|
||||||
|
var sema: Sema = undefined;
|
||||||
|
match Sema.new(&ast, &diag) {
|
||||||
|
.Ok(v) => { sema = v; },
|
||||||
|
.Err(error) => {
|
||||||
|
Logger.error(log, "sema init failed");
|
||||||
|
ret 1;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
defer { Sema.drop(&sema); }
|
||||||
|
sema.sources = &sources;
|
||||||
|
sema.loader = &loader;
|
||||||
|
|
||||||
|
Sema.run(&sema);
|
||||||
|
|
||||||
|
if Diag.has_error(diag) {
|
||||||
|
var rendered = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&rendered); }
|
||||||
|
Diag.render_human(&diag, &rendered);
|
||||||
|
eprint(ByteBuffer.as_slice(rendered));
|
||||||
|
ret 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var out = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&out); }
|
||||||
|
if !Sema.dump(&sema, &out) {
|
||||||
|
Logger.fatal(log, "out of memory while dumping sema");
|
||||||
|
ret 1;
|
||||||
|
}
|
||||||
|
if !Ast.dump(&ast, &out) {
|
||||||
|
Logger.fatal(log, "out of memory while dumping ast");
|
||||||
|
ret 1;
|
||||||
|
}
|
||||||
|
match print(ByteBuffer.as_slice(out)) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ret 1; },
|
||||||
|
}
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
type PipeMode = enum {
|
||||||
|
Ir, // 只 dump IR
|
||||||
|
Vm, // 继续 layout + ir2vm,dump VM
|
||||||
|
Compile, // 继续 layout + ir2vm,写 outpath
|
||||||
|
State, // 只逐步打日志(诊断“卡在哪一步”)
|
||||||
|
C, // 继续 layout + ir2c,dump C
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step(mode: PipeMode, s: []u8) {
|
||||||
|
if @enum2int(mode) == @enum2int(PipeMode.State) {
|
||||||
|
eprint(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 完整前端流水线:lex -> parse -> sema -> ast2ir ->(可选)layout + ir2vm。
|
||||||
|
fn run_pipeline(log: *Logger, path: ByteView, mode: PipeMode, outpath: ByteView): i32 {
|
||||||
|
var content = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&content); }
|
||||||
|
match read_all(path, &content) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => {
|
||||||
|
Logger.error(log, "cannot read input file");
|
||||||
|
ret 1;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var text = ByteView.from_slice(ByteBuffer.as_slice(content));
|
||||||
|
step(mode, "state: read ok\n");
|
||||||
|
|
||||||
|
var sources = set_new();
|
||||||
|
defer { set_drop(&sources); }
|
||||||
|
var main_idx: usize = 0;
|
||||||
|
match set_add(&sources, path, text) {
|
||||||
|
.Ok(index) => { main_idx = index; },
|
||||||
|
.Err(error) => {
|
||||||
|
Logger.error(log, "cannot register source");
|
||||||
|
ret 1;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var diag = Diag.new(&sources);
|
||||||
|
defer { Diag.drop(&diag); }
|
||||||
|
|
||||||
|
var tokens = Vec.blank(@sizeof(Token), @alignof(Token));
|
||||||
|
match Lexer.lex(text, &diag, 0) {
|
||||||
|
.Ok(v) => { tokens = v; },
|
||||||
|
.Err(error) => {
|
||||||
|
Logger.error(log, "lex failed");
|
||||||
|
ret 1;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
defer { Vec.drop(&tokens); }
|
||||||
|
|
||||||
|
var ast: Ast = undefined;
|
||||||
|
match Ast.new(&sources) {
|
||||||
|
.Ok(v) => { ast = v; },
|
||||||
|
.Err(error) => {
|
||||||
|
Logger.error(log, "ast init failed");
|
||||||
|
ret 1;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
defer { Ast.drop(&ast); }
|
||||||
|
|
||||||
|
var p = Parser.new(&ast, tokens, &diag);
|
||||||
|
p.file = main_idx;
|
||||||
|
Parser.parse(&p);
|
||||||
|
|
||||||
|
if Diag.has_error(diag) || p.failed {
|
||||||
|
var rendered = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&rendered); }
|
||||||
|
Diag.render_human(&diag, &rendered);
|
||||||
|
eprint(ByteBuffer.as_slice(rendered));
|
||||||
|
ret 1;
|
||||||
|
}
|
||||||
|
step(mode, "state: parse ok\n");
|
||||||
|
|
||||||
|
var ctx = LoaderCtx {
|
||||||
|
.sources = &sources,
|
||||||
|
.mods = Vec.blank(@sizeof(ModEntry), @alignof(ModEntry)),
|
||||||
|
.buffers = Vec.blank(@sizeof(ByteBuffer), @alignof(ByteBuffer)),
|
||||||
|
.diag = &diag,
|
||||||
|
};
|
||||||
|
defer { loader_ctx_drop(&ctx); }
|
||||||
|
var loader = Loader { .load = driver_load, .user = &ctx as *any };
|
||||||
|
|
||||||
|
var sema: Sema = undefined;
|
||||||
|
match Sema.new(&ast, &diag) {
|
||||||
|
.Ok(v) => { sema = v; },
|
||||||
|
.Err(error) => {
|
||||||
|
Logger.error(log, "sema init failed");
|
||||||
|
ret 1;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
defer { Sema.drop(&sema); }
|
||||||
|
sema.sources = &sources;
|
||||||
|
sema.loader = &loader;
|
||||||
|
|
||||||
|
Sema.run(&sema);
|
||||||
|
if Diag.has_error(diag) {
|
||||||
|
var rendered = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&rendered); }
|
||||||
|
Diag.render_human(&diag, &rendered);
|
||||||
|
eprint(ByteBuffer.as_slice(rendered));
|
||||||
|
ret 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
step(mode, "state: sema ok\n");
|
||||||
|
|
||||||
|
var builder = IrBuilder.builder_new();
|
||||||
|
defer { IrBuilder.builder_drop(&builder); }
|
||||||
|
var a2ir = Ast2Ir.new(&builder, &sema, &diag);
|
||||||
|
defer { Ast2Ir.drop(&a2ir); }
|
||||||
|
|
||||||
|
var asts = Vec.blank(@sizeof(usize), @alignof(usize));
|
||||||
|
defer { Vec.drop(&asts); }
|
||||||
|
var ap: *Ast = *
|
||||||
|
Vec.push(&asts, &ap as *u8);
|
||||||
|
var mi: usize = 0;
|
||||||
|
while mi < ctx.mods.len {
|
||||||
|
var m = mod_at(&ctx, mi);
|
||||||
|
if m != null && m.ok && m.ast != null {
|
||||||
|
var mp: *Ast = m.ast;
|
||||||
|
Vec.push(&asts, &mp as *u8);
|
||||||
|
}
|
||||||
|
mi = mi + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast2Ir.run(&a2ir, asts);
|
||||||
|
if Diag.has_error(diag) {
|
||||||
|
var rendered = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&rendered); }
|
||||||
|
Diag.render_human(&diag, &rendered);
|
||||||
|
eprint(ByteBuffer.as_slice(rendered));
|
||||||
|
ret 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
step(mode, "state: ast2ir ok\n");
|
||||||
|
|
||||||
|
if @enum2int(mode) == @enum2int(PipeMode.Ir) {
|
||||||
|
var out = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&out); }
|
||||||
|
if !Ir.dump(&builder.ir, &sema.types.ir, &sema.attr, &out) {
|
||||||
|
Logger.fatal(log, "out of memory while dumping ir");
|
||||||
|
ret 1;
|
||||||
|
}
|
||||||
|
match print(ByteBuffer.as_slice(out)) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ret 1; },
|
||||||
|
}
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 类型就绪后解析布局,再做 IR -> VM。
|
||||||
|
var layout: Layout = undefined;
|
||||||
|
match LayoutM.new() {
|
||||||
|
.Ok(v) => { layout = v; },
|
||||||
|
.Err(error) => {
|
||||||
|
Logger.error(log, "layout init failed");
|
||||||
|
ret 1;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
defer { LayoutM.drop(&layout); }
|
||||||
|
LayoutM.resolve(&layout, &sema.types.ir);
|
||||||
|
step(mode, "state: layout ok\n");
|
||||||
|
|
||||||
|
if @enum2int(mode) == @enum2int(PipeMode.C) {
|
||||||
|
var cout = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&cout); }
|
||||||
|
var ok = Ir2CM.compile(&builder.ir, &sema.types.ir, &layout, &sema.attr, &cout);
|
||||||
|
if outpath.len != 0 {
|
||||||
|
var f = open_cstr(outpath, ByteView.from_slice("wb"));
|
||||||
|
if !File.valid(&f) {
|
||||||
|
Logger.error(log, "cannot open C output file");
|
||||||
|
ret 1;
|
||||||
|
}
|
||||||
|
write_all(&f, ByteBuffer.as_slice(cout));
|
||||||
|
File.close(&f);
|
||||||
|
} else {
|
||||||
|
match print(ByteBuffer.as_slice(cout)) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ret 1; },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
Logger.error(log, "ir2c failed (see generated C for unsupported markers)");
|
||||||
|
ret 1;
|
||||||
|
}
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ir2vm: Ir2Vm = undefined;
|
||||||
|
Ir2VmM.init(&ir2vm, &builder.ir, &sema.types.ir, &layout, &sema.attr);
|
||||||
|
defer { Ir2VmM.drop(&ir2vm); }
|
||||||
|
Ir2VmM.run(&ir2vm);
|
||||||
|
step(mode, "state: ir2vm ok\n");
|
||||||
|
|
||||||
|
if @enum2int(mode) == @enum2int(PipeMode.Vm) {
|
||||||
|
var vout = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&vout); }
|
||||||
|
if !Ir2VmM.dump(&ir2vm, &vout) {
|
||||||
|
Logger.fatal(log, "out of memory while dumping vm");
|
||||||
|
ret 1;
|
||||||
|
}
|
||||||
|
match print(ByteBuffer.as_slice(vout)) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ret 1; },
|
||||||
|
}
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if @enum2int(mode) == @enum2int(PipeMode.State) {
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Ir2VmM.store(&ir2vm, outpath) {
|
||||||
|
Logger.error(log, "cannot write output file");
|
||||||
|
ret 1;
|
||||||
|
}
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fn main(argc: i32, argv: **u8): i32 {
|
fn main(argc: i32, argv: **u8): i32 {
|
||||||
// 无参数:自测运行(`nob test stage2`)视为成功。
|
// 无参数:自测运行(`nob test stage2`)视为成功。
|
||||||
if argc <= 1 { ret 0; }
|
if argc <= 1 { ret 0; }
|
||||||
|
|
||||||
var log = Logger.new(ByteView.from_slice("splc1"));
|
var log = Logger.new(ByteView.from_slice("splc1"));
|
||||||
|
|
||||||
if argc < 4 {
|
|
||||||
Logger.fatal(&log, "usage: splc1 --dump <lex|ast> <file> [out]");
|
|
||||||
ret 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var arg1 = ByteView.from_cstr(argv[1]);
|
var arg1 = ByteView.from_cstr(argv[1]);
|
||||||
var mode = ByteView.from_cstr(argv[2]);
|
|
||||||
var path = ByteView.from_cstr(argv[3]);
|
|
||||||
|
|
||||||
if !ByteView.eq(arg1, ByteView.from_slice("--dump")) {
|
if !ByteView.eq(arg1, ByteView.from_slice("--dump")) {
|
||||||
Logger.fatal(&log, "usage: splc1 --dump <lex|ast> <file> [out]");
|
// 编译模式:splc1 <in> <out>
|
||||||
|
if argc < 3 {
|
||||||
|
Logger.fatal(&log, "usage: splc1 <in> <out> | splc1 --dump <lex|ast|sema|ir|vm> <file>");
|
||||||
ret 1;
|
ret 1;
|
||||||
}
|
}
|
||||||
|
var in = ByteView.from_cstr(argv[1]);
|
||||||
|
var out = ByteView.from_cstr(argv[2]);
|
||||||
|
ret run_pipeline(&log, in, .Compile, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
if argc < 4 {
|
||||||
|
Logger.fatal(&log, "usage: splc1 --dump <lex|ast|sema|ir|vm|c> <file> [out]");
|
||||||
|
ret 1;
|
||||||
|
}
|
||||||
|
var mode = ByteView.from_cstr(argv[2]);
|
||||||
|
var path = ByteView.from_cstr(argv[3]);
|
||||||
|
var dout = ByteView.empty();
|
||||||
|
if argc >= 5 { dout = ByteView.from_cstr(argv[4]); }
|
||||||
|
|
||||||
if ByteView.eq(mode, ByteView.from_slice("lex")) {
|
if ByteView.eq(mode, ByteView.from_slice("lex")) {
|
||||||
ret dump_lex(&log, path);
|
ret dump_lex(&log, path);
|
||||||
}
|
}
|
||||||
if ByteView.eq(mode, ByteView.from_slice("ast")) {
|
if ByteView.eq(mode, ByteView.from_slice("ast")) {
|
||||||
ret dump_ast(&log, path);
|
ret dump_ast(&log, path);
|
||||||
}
|
}
|
||||||
|
if ByteView.eq(mode, ByteView.from_slice("sema")) {
|
||||||
|
ret dump_sema(&log, path);
|
||||||
|
}
|
||||||
|
if ByteView.eq(mode, ByteView.from_slice("ir")) {
|
||||||
|
ret run_pipeline(&log, path, .Ir, ByteView.empty());
|
||||||
|
}
|
||||||
|
if ByteView.eq(mode, ByteView.from_slice("vm")) {
|
||||||
|
ret run_pipeline(&log, path, .Vm, ByteView.empty());
|
||||||
|
}
|
||||||
|
if ByteView.eq(mode, ByteView.from_slice("state")) {
|
||||||
|
ret run_pipeline(&log, path, .State, ByteView.empty());
|
||||||
|
}
|
||||||
|
if ByteView.eq(mode, ByteView.from_slice("c")) {
|
||||||
|
ret run_pipeline(&log, path, .C, dout);
|
||||||
|
}
|
||||||
Logger.fatal(&log, "dump mode not implemented");
|
Logger.fatal(&log, "dump mode not implemented");
|
||||||
ret 1;
|
ret 1;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
// 时间/参考量接口(原生 vm_timestamp,case 探测风格,不假设一定可用)。
|
||||||
|
// available() -> 1 = 时间可用;0 = 不可用(其余 time 相关返回值恒为 0)
|
||||||
|
// sec() -> 当前秒
|
||||||
|
// ns() -> 当前纳秒部分
|
||||||
|
// steps() -> VM 已执行指令数(参考量)
|
||||||
|
// now_ns() -> 完整纳秒时间戳(单次调用;纪元纳秒 < 2^64,无需 i128)
|
||||||
|
|
||||||
|
#extern(vm) fn vm_timestamp(kind: usize): usize;
|
||||||
|
|
||||||
|
fn available(): usize {
|
||||||
|
ret vm_timestamp(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sec(): usize {
|
||||||
|
ret vm_timestamp(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ns(): usize {
|
||||||
|
ret vm_timestamp(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn steps(): usize {
|
||||||
|
ret vm_timestamp(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calls(): usize {
|
||||||
|
ret vm_timestamp(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn now_ns(): usize {
|
||||||
|
ret vm_timestamp(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main(): void {
|
||||||
|
var has = available();
|
||||||
|
if has != 0 {
|
||||||
|
var a = now_ns();
|
||||||
|
var b = now_ns();
|
||||||
|
@assert(b >= a, "now monotonic");
|
||||||
|
}
|
||||||
|
|
||||||
|
var s0 = steps();
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < 1000 { i = i + 1; }
|
||||||
|
var s1 = steps();
|
||||||
|
@assert(s1 >= s0, "steps monotonic");
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
type spl_vm_val_t = u64; // usize
|
||||||
|
|
||||||
|
#extern(vm) fn vm_new(): *any;
|
||||||
|
#extern(vm) fn vm_drop(vm: *any): void;
|
||||||
|
|
||||||
|
#extern(vm) fn vm_load(vm: *any, fpath: *u8): void;
|
||||||
|
#extern(vm) fn vm_load_from_buffer(vm: *any, buf: *any, len: usize): i32;
|
||||||
|
|
||||||
|
#extern(vm) fn vm_push(vm: *any, val: spl_vm_val_t): void;
|
||||||
|
#extern(vm) fn vm_call(vm: *any, name: *u8, nargs: u64): i32;
|
||||||
|
#extern(vm) fn vm_run(vm: *any): i32;
|
||||||
|
#extern(vm) fn vm_ret_val(vm: *any): spl_vm_val_t;
|
||||||
|
#extern(vm) fn vm_mem_alloc(vm: *any, size: usize): spl_vm_val_t;
|
||||||
|
#extern(vm) fn vm_mem_read(vm: *any, off: spl_vm_val_t, dst: *any, n: usize): i32;
|
||||||
|
#extern(vm) fn vm_mem_write(vm: *any, off: spl_vm_val_t, src: *any, n: usize): i32;
|
||||||
|
#extern(vm) fn vm_stack_get(vm: *any, index: usize): spl_vm_val_t;
|
||||||
|
#extern(vm) fn vm_set_foreign(vm: *any, entry: []u8, ctx: u64): i32;
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
+60
-34
@@ -1,7 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// 词法单元:用带载荷 ADT 表达 TokenKind;关键词/算符各自独立 enum。
|
// 词法单元:TokenKind 为带载荷 ADT,关键词/算符各自独立 enum;整型/字符在词法期
|
||||||
// 整型与字符在词法期解码并随载荷携带;文本一律借用源切片(不复制)。
|
// 解码,文本一律借用源切片。
|
||||||
|
|
||||||
type .{ .ByteView } = @import("utils/byte_view.spl");
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||||
|
|
||||||
@@ -66,41 +66,68 @@ fn is_trivia(kind: TokenKind): bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 先按长度与首字节分派,再做完整比较,避免对每个标识符做全表串比较。
|
||||||
fn keyword_of(text: ByteView): KeywordResult {
|
fn keyword_of(text: ByteView): KeywordResult {
|
||||||
if is(text, "any") { ret .Yes(.Any); }
|
var n = text.len;
|
||||||
if is(text, "as") { ret .Yes(.As); }
|
if n == 0 { ret .No; }
|
||||||
if is(text, "bool") { ret .Yes(.Bool); }
|
var c0 = text.ptr[0];
|
||||||
if is(text, "break") { ret .Yes(.Break); }
|
if n == 2 {
|
||||||
if is(text, "catch") { ret .Yes(.Catch); }
|
if c0 == 'a' && is(text, "as") { ret .Yes(.As); }
|
||||||
if is(text, "comptime") { ret .Yes(.Comptime); }
|
if c0 == 'f' && is(text, "fn") { ret .Yes(.Fn); }
|
||||||
if is(text, "const") { ret .Yes(.Const); }
|
if c0 == 'i' && is(text, "if") { ret .Yes(.If); }
|
||||||
if is(text, "continue") { ret .Yes(.Continue); }
|
ret .No;
|
||||||
if is(text, "defer") { ret .Yes(.Defer); }
|
}
|
||||||
if is(text, "else") { ret .Yes(.Else); }
|
if n == 3 {
|
||||||
if is(text, "enum") { ret .Yes(.Enum); }
|
if c0 == 'a' && is(text, "any") { ret .Yes(.Any); }
|
||||||
if is(text, "errdefer") { ret .Yes(.Errdefer); }
|
if c0 == 'f' && is(text, "for") { ret .Yes(.For); }
|
||||||
if is(text, "false") { ret .Yes(.False); }
|
if c0 == 'r' && is(text, "ret") { ret .Yes(.Ret); }
|
||||||
if is(text, "fn") { ret .Yes(.Fn); }
|
if c0 == 't' && is(text, "try") { ret .Yes(.Try); }
|
||||||
if is(text, "for") { ret .Yes(.For); }
|
if c0 == 'v' && is(text, "var") { ret .Yes(.Var); }
|
||||||
if is(text, "if") { ret .Yes(.If); }
|
ret .No;
|
||||||
if is(text, "loop") { ret .Yes(.Loop); }
|
}
|
||||||
if is(text, "match") { ret .Yes(.Match); }
|
if n == 4 {
|
||||||
if is(text, "null") { ret .Yes(.Null); }
|
if c0 == 'b' && is(text, "bool") { ret .Yes(.Bool); }
|
||||||
if is(text, "ret") { ret .Yes(.Ret); }
|
if c0 == 'e' && is(text, "else") { ret .Yes(.Else); }
|
||||||
if is(text, "shape") { ret .Yes(.Shape); }
|
if c0 == 'e' && is(text, "enum") { ret .Yes(.Enum); }
|
||||||
if is(text, "struct") { ret .Yes(.Struct); }
|
if c0 == 'l' && is(text, "loop") { ret .Yes(.Loop); }
|
||||||
if is(text, "true") { ret .Yes(.True); }
|
if c0 == 'n' && is(text, "null") { ret .Yes(.Null); }
|
||||||
if is(text, "try") { ret .Yes(.Try); }
|
if c0 == 't' && is(text, "true") { ret .Yes(.True); }
|
||||||
if is(text, "type") { ret .Yes(.Type); }
|
if c0 == 't' && is(text, "type") { ret .Yes(.Type); }
|
||||||
if is(text, "undefined") { ret .Yes(.Undefined); }
|
ret .No;
|
||||||
if is(text, "union") { ret .Yes(.Union); }
|
}
|
||||||
if is(text, "var") { ret .Yes(.Var); }
|
if n == 5 {
|
||||||
if is(text, "while") { ret .Yes(.While); }
|
if c0 == 'b' && is(text, "break") { ret .Yes(.Break); }
|
||||||
if is(text, "_") { ret .Yes(.Underscore); }
|
if c0 == 'c' && is(text, "catch") { ret .Yes(.Catch); }
|
||||||
|
if c0 == 'c' && is(text, "const") { ret .Yes(.Const); }
|
||||||
|
if c0 == 'd' && is(text, "defer") { ret .Yes(.Defer); }
|
||||||
|
if c0 == 'f' && is(text, "false") { ret .Yes(.False); }
|
||||||
|
if c0 == 'm' && is(text, "match") { ret .Yes(.Match); }
|
||||||
|
if c0 == 's' && is(text, "shape") { ret .Yes(.Shape); }
|
||||||
|
if c0 == 'u' && is(text, "union") { ret .Yes(.Union); }
|
||||||
|
if c0 == 'w' && is(text, "while") { ret .Yes(.While); }
|
||||||
|
ret .No;
|
||||||
|
}
|
||||||
|
if n == 6 {
|
||||||
|
if c0 == 's' && is(text, "struct") { ret .Yes(.Struct); }
|
||||||
|
ret .No;
|
||||||
|
}
|
||||||
|
if n == 8 {
|
||||||
|
if c0 == 'c' && is(text, "comptime") { ret .Yes(.Comptime); }
|
||||||
|
if c0 == 'c' && is(text, "continue") { ret .Yes(.Continue); }
|
||||||
|
if c0 == 'e' && is(text, "errdefer") { ret .Yes(.Errdefer); }
|
||||||
|
ret .No;
|
||||||
|
}
|
||||||
|
if n == 9 {
|
||||||
|
if c0 == 'u' && is(text, "undefined") { ret .Yes(.Undefined); }
|
||||||
|
ret .No;
|
||||||
|
}
|
||||||
|
if n == 1 {
|
||||||
|
if c0 == '_' && is(text, "_") { ret .Yes(.Underscore); }
|
||||||
|
ret .No;
|
||||||
|
}
|
||||||
ret .No;
|
ret .No;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- token 类型名(与 stage1 spl_tok_type_name 逐字一致,供 dump/diff 使用)----
|
|
||||||
|
|
||||||
fn keyword_name(kw: Keyword): []u8 {
|
fn keyword_name(kw: Keyword): []u8 {
|
||||||
match kw {
|
match kw {
|
||||||
@@ -214,7 +241,6 @@ fn kind_name(kind: TokenKind): []u8 {
|
|||||||
ret "?";
|
ret "?";
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn view(text: []u8): ByteView {
|
fn view(text: []u8): ByteView {
|
||||||
ret ByteView.from_slice(text);
|
ret ByteView.from_slice(text);
|
||||||
|
|||||||
@@ -0,0 +1,889 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
// IR 类型表:按结构 hash-consing。id 0 为保留哨兵(Error)。成员/参数 Vec 由本表
|
||||||
|
// 拥有;hash 仅用于选桶,相等始终由结构比较决定。
|
||||||
|
|
||||||
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
|
type .{ .Map, .hash_kind_usize } = @import("utils/map.spl");
|
||||||
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||||
|
type .{ .ByteBuffer } = @import("utils/byte_buffer.spl");
|
||||||
|
type .{ .append_usize } = @import("utils/fmt.spl");
|
||||||
|
type .{ .hash_usize, .eq_usize } = @import("utils/hash.spl");
|
||||||
|
|
||||||
|
type LayoutMode = enum {
|
||||||
|
Auto,
|
||||||
|
ExternC,
|
||||||
|
Packed,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Member = struct {
|
||||||
|
name: ByteView,
|
||||||
|
type_id: usize,
|
||||||
|
ast_node_ref: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Agg = struct {
|
||||||
|
mode: LayoutMode,
|
||||||
|
fixed_align_bits: usize,
|
||||||
|
tag_type: usize,
|
||||||
|
variants: Vec, // Member
|
||||||
|
}
|
||||||
|
|
||||||
|
type AggKind = enum {
|
||||||
|
Struct,
|
||||||
|
Union,
|
||||||
|
Enum,
|
||||||
|
Shape,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 变体顺序即 kind 数值;Forall/Exists 暂按 quant 打印。
|
||||||
|
type TypeNode = enum {
|
||||||
|
Error,
|
||||||
|
Never,
|
||||||
|
Any,
|
||||||
|
Unit,
|
||||||
|
Infer,
|
||||||
|
Bool,
|
||||||
|
Int: struct { bits: usize, is_signed: bool },
|
||||||
|
Float: struct { bits: usize },
|
||||||
|
Ptr: struct { pointee: usize },
|
||||||
|
Array: struct { element: usize, len: usize },
|
||||||
|
Struct: Agg,
|
||||||
|
Union: Agg,
|
||||||
|
Enum: Agg,
|
||||||
|
Fn: struct { params: Vec, ret_type: usize }, // params: usize
|
||||||
|
Forall,
|
||||||
|
Exists,
|
||||||
|
Shape: Agg,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表项 = 节点 + intern 桶链指针(hash_next 不参与结构相等)。
|
||||||
|
type TypeEntry = struct {
|
||||||
|
hash_next: usize,
|
||||||
|
node: TypeNode,
|
||||||
|
}
|
||||||
|
|
||||||
|
type TypeTable = @self;
|
||||||
|
|
||||||
|
table: Vec, // TypeEntry
|
||||||
|
fold: Map, // usize hash -> usize chain head
|
||||||
|
|
||||||
|
type TypeTableResult = enum {
|
||||||
|
Ok: TypeTable,
|
||||||
|
Err: Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn seed(): usize {
|
||||||
|
ret 14695981039346656037;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mix(h: usize, x: usize): usize {
|
||||||
|
ret (h ^ x) * 1099511628211;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mode_code(mode: LayoutMode): usize {
|
||||||
|
var m: LayoutMode = mode;
|
||||||
|
ret @enum2int(m) as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_agg(h0: usize, agg: Agg): usize {
|
||||||
|
var h = mix(h0, mode_code(agg.mode));
|
||||||
|
h = mix(h, agg.fixed_align_bits);
|
||||||
|
h = mix(h, agg.tag_type);
|
||||||
|
h = mix(h, agg.variants.len);
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < agg.variants.len {
|
||||||
|
var m = member_at(&agg.variants, i);
|
||||||
|
if m != null {
|
||||||
|
h = mix(h, m.type_id);
|
||||||
|
h = mix_bytes(h, m.name);
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret h;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mix_bytes(h0: usize, view: ByteView): usize {
|
||||||
|
var h = h0;
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < view.len {
|
||||||
|
h = mix(h, view.ptr[i] as usize);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret mix(h, 0); // 终止符参与混合,避免名称拼接歧义
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_node(n: *TypeNode): usize {
|
||||||
|
var h = seed();
|
||||||
|
match n.* {
|
||||||
|
.Error => { h = mix(h, 0); },
|
||||||
|
.Never => { h = mix(h, 1); },
|
||||||
|
.Any => { h = mix(h, 2); },
|
||||||
|
.Unit => { h = mix(h, 3); },
|
||||||
|
.Infer => { h = mix(h, 4); },
|
||||||
|
.Bool => { h = mix(h, 5); },
|
||||||
|
.Int(x) => {
|
||||||
|
h = mix(h, 6);
|
||||||
|
h = mix(h, x.bits);
|
||||||
|
h = mix(h, if x.is_signed { 1 } else { 0 });
|
||||||
|
},
|
||||||
|
.Float(x) => {
|
||||||
|
h = mix(h, 7);
|
||||||
|
h = mix(h, x.bits);
|
||||||
|
},
|
||||||
|
.Ptr(x) => {
|
||||||
|
h = mix(h, 8);
|
||||||
|
h = mix(h, x.pointee);
|
||||||
|
},
|
||||||
|
.Array(x) => {
|
||||||
|
h = mix(h, 9);
|
||||||
|
h = mix(h, x.element);
|
||||||
|
h = mix(h, x.len);
|
||||||
|
},
|
||||||
|
.Struct(x) => { h = hash_agg(mix(h, 10), x); },
|
||||||
|
.Union(x) => { h = hash_agg(mix(h, 11), x); },
|
||||||
|
.Enum(x) => { h = hash_agg(mix(h, 12), x); },
|
||||||
|
.Fn(x) => {
|
||||||
|
h = mix(h, 13);
|
||||||
|
h = mix(h, x.ret_type);
|
||||||
|
h = mix(h, x.params.len);
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < x.params.len {
|
||||||
|
h = mix(h, usize_at(x.params, i));
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Forall => { h = mix(h, 14); },
|
||||||
|
.Exists => { h = mix(h, 15); },
|
||||||
|
.Shape(x) => { h = hash_agg(mix(h, 16), x); },
|
||||||
|
}
|
||||||
|
ret h;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tag(n: *TypeNode): usize {
|
||||||
|
match n.* {
|
||||||
|
.Error => { ret 0; },
|
||||||
|
.Never => { ret 1; },
|
||||||
|
.Any => { ret 2; },
|
||||||
|
.Unit => { ret 3; },
|
||||||
|
.Infer => { ret 4; },
|
||||||
|
.Bool => { ret 5; },
|
||||||
|
.Int(x) => { ret 6; },
|
||||||
|
.Float(x) => { ret 7; },
|
||||||
|
.Ptr(x) => { ret 8; },
|
||||||
|
.Array(x) => { ret 9; },
|
||||||
|
.Struct(x) => { ret 10; },
|
||||||
|
.Union(x) => { ret 11; },
|
||||||
|
.Enum(x) => { ret 12; },
|
||||||
|
.Fn(x) => { ret 13; },
|
||||||
|
.Forall => { ret 14; },
|
||||||
|
.Exists => { ret 15; },
|
||||||
|
.Shape(x) => { ret 16; },
|
||||||
|
}
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn agg_eq(x: Agg, y: Agg): bool {
|
||||||
|
if mode_code(x.mode) != mode_code(y.mode) { ret false; }
|
||||||
|
if x.fixed_align_bits != y.fixed_align_bits { ret false; }
|
||||||
|
if x.tag_type != y.tag_type { ret false; }
|
||||||
|
if x.variants.len != y.variants.len { ret false; }
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < x.variants.len {
|
||||||
|
var ma = member_at(&x.variants, i);
|
||||||
|
var mb = member_at(&y.variants, i);
|
||||||
|
if ma == null || mb == null { ret false; }
|
||||||
|
if ma.type_id != mb.type_id { ret false; }
|
||||||
|
if !ByteView.eq(ma.name, mb.name) { ret false; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node_eq(a: *TypeNode, b: *TypeNode): bool {
|
||||||
|
if tag(a) != tag(b) { ret false; }
|
||||||
|
match a.* {
|
||||||
|
.Int(x) => {
|
||||||
|
match b.* {
|
||||||
|
.Int(y) => { ret x.bits == y.bits && x.is_signed == y.is_signed; },
|
||||||
|
_ => { ret false; },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Float(x) => {
|
||||||
|
match b.* {
|
||||||
|
.Float(y) => { ret x.bits == y.bits; },
|
||||||
|
_ => { ret false; },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Ptr(x) => {
|
||||||
|
match b.* {
|
||||||
|
.Ptr(y) => { ret x.pointee == y.pointee; },
|
||||||
|
_ => { ret false; },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Array(x) => {
|
||||||
|
match b.* {
|
||||||
|
.Array(y) => { ret x.element == y.element && x.len == y.len; },
|
||||||
|
_ => { ret false; },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Struct(x) => {
|
||||||
|
match b.* {
|
||||||
|
.Struct(y) => { ret agg_eq(x, y); },
|
||||||
|
_ => { ret false; },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Union(x) => {
|
||||||
|
match b.* {
|
||||||
|
.Union(y) => { ret agg_eq(x, y); },
|
||||||
|
_ => { ret false; },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Enum(x) => {
|
||||||
|
match b.* {
|
||||||
|
.Enum(y) => { ret agg_eq(x, y); },
|
||||||
|
_ => { ret false; },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Fn(x) => {
|
||||||
|
match b.* {
|
||||||
|
.Fn(y) => {
|
||||||
|
if x.ret_type != y.ret_type { ret false; }
|
||||||
|
if x.params.len != y.params.len { ret false; }
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < x.params.len {
|
||||||
|
if usize_at(x.params, i) != usize_at(y.params, i) { ret false; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret true;
|
||||||
|
},
|
||||||
|
_ => { ret false; },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Shape(x) => {
|
||||||
|
match b.* {
|
||||||
|
.Shape(y) => { ret agg_eq(x, y); },
|
||||||
|
_ => { ret false; },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => { ret true; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 释放节点内部拥有的 Vec(仅聚合变体与函数参数)。
|
||||||
|
fn free_payload(n: *TypeNode) {
|
||||||
|
match n.* {
|
||||||
|
.Struct(x) => { Vec.drop(&x.variants); },
|
||||||
|
.Union(x) => { Vec.drop(&x.variants); },
|
||||||
|
.Enum(x) => { Vec.drop(&x.variants); },
|
||||||
|
.Shape(x) => { Vec.drop(&x.variants); },
|
||||||
|
.Fn(x) => { Vec.drop(&x.params); },
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn usize_at(v: Vec, i: usize): usize {
|
||||||
|
match Vec.get(&v, i) {
|
||||||
|
.Ok(ptr) => { ret (ptr as *usize).*; },
|
||||||
|
.Err(error) => { ret 0; },
|
||||||
|
}
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn member_at(v: *Vec, i: usize): *Member {
|
||||||
|
if i >= v.len { ret null; }
|
||||||
|
ret (v.data as *Member) + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_member(v: *Vec, name: []u8, type_id: usize) {
|
||||||
|
var m = Member {
|
||||||
|
.name = ByteView.from_slice(name),
|
||||||
|
.type_id = type_id,
|
||||||
|
.ast_node_ref = 0,
|
||||||
|
};
|
||||||
|
match Vec.push(v, &m as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { @assert(false, "push member"); },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// id 0 与越界均返回 null。
|
||||||
|
fn node(self: *TypeTable, id: usize): *TypeEntry {
|
||||||
|
if id == 0 || id >= self.table.len { ret null; }
|
||||||
|
ret (self.table.data as *TypeEntry) + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn new(): TypeTableResult {
|
||||||
|
var tt = TypeTable {
|
||||||
|
.table = Vec.blank(@sizeof(TypeEntry), @alignof(TypeEntry)),
|
||||||
|
.fold = Map.new(@sizeof(usize), @alignof(usize), @sizeof(usize), @alignof(usize),
|
||||||
|
hash_kind_usize()),
|
||||||
|
};
|
||||||
|
var sentinel = TypeEntry { .hash_next = 0, .node = .Error };
|
||||||
|
match Vec.push(&tt.table, &sentinel as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => {
|
||||||
|
Vec.drop(&tt.table);
|
||||||
|
Map.drop(&tt.fold);
|
||||||
|
ret .Err(error);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ret .Ok(tt);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drop(self: *TypeTable) {
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < self.table.len {
|
||||||
|
var n = node(self, i);
|
||||||
|
if n != null {
|
||||||
|
free_payload(&n.node);
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
Vec.drop(&self.table);
|
||||||
|
Map.drop(&self.fold);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预留一个空 ERROR 槽位作为身份锚点(不写 fold、不参与 hash-consing)。
|
||||||
|
// 用于自引用聚合:先拿到稳定 id,稍后用 define 填内容。
|
||||||
|
fn reserve(self: *TypeTable): usize {
|
||||||
|
var n: TypeNode = .Error;
|
||||||
|
match Vec.push(&self.table, &n as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ret 0; },
|
||||||
|
}
|
||||||
|
ret self.table.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 把 src 的内容填进已 reserve 的槽位;src 载荷所有权转移,之后 src 失效。
|
||||||
|
fn define(self: *TypeTable, id: usize, src: *TypeNode) {
|
||||||
|
if id == 0 { ret; }
|
||||||
|
var slot = node(self, id);
|
||||||
|
if slot == null { ret; }
|
||||||
|
slot.node = src.*;
|
||||||
|
src.* = .Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn fold_find(self: *TypeTable, hash: usize, probe: *TypeNode): usize {
|
||||||
|
var head: usize = 0;
|
||||||
|
match Map.get(&self.fold, &hash as *u8, &head as *u8) {
|
||||||
|
.Found => {},
|
||||||
|
.NotFound => { ret 0; },
|
||||||
|
}
|
||||||
|
var cur = head;
|
||||||
|
while cur != 0 {
|
||||||
|
var n = node(self, cur);
|
||||||
|
if n == null { ret 0; }
|
||||||
|
if node_eq(&n.node, probe) { ret cur; }
|
||||||
|
cur = n.hash_next;
|
||||||
|
}
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_insert(self: *TypeTable, hash: usize, id: usize) {
|
||||||
|
var old: usize = 0;
|
||||||
|
match Map.get(&self.fold, &hash as *u8, &old as *u8) {
|
||||||
|
.Found => {},
|
||||||
|
.NotFound => { old = 0; },
|
||||||
|
}
|
||||||
|
var n = node(self, id);
|
||||||
|
if n == null { ret; }
|
||||||
|
n.hash_next = old;
|
||||||
|
Map.put(&self.fold, &hash as *u8, &id as *u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// intern 一个 probe:命中则释放 probe 载荷并返回已有 id;否则复制入表。
|
||||||
|
// probe 在返回后失效(载荷所有权转移,probe 被置为 Error)。失败返回 0。
|
||||||
|
fn push(self: *TypeTable, probe: *TypeNode): usize {
|
||||||
|
var hash = hash_node(probe);
|
||||||
|
var existing = fold_find(self, hash, probe);
|
||||||
|
if existing != 0 {
|
||||||
|
free_payload(probe);
|
||||||
|
probe.* = .Error;
|
||||||
|
ret existing;
|
||||||
|
}
|
||||||
|
var entry = TypeEntry { .hash_next = 0, .node = probe.* };
|
||||||
|
probe.* = .Error; // 载荷所有权已转移给 entry
|
||||||
|
var id = self.table.len;
|
||||||
|
match Vec.push(&self.table, &entry as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => {
|
||||||
|
free_payload(&entry.node);
|
||||||
|
ret 0;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fold_insert(self, hash, id);
|
||||||
|
ret id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn make_error(self: *TypeTable): usize {
|
||||||
|
var n: TypeNode = .Error;
|
||||||
|
ret push(self, &n);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_never(self: *TypeTable): usize {
|
||||||
|
var n: TypeNode = .Never;
|
||||||
|
ret push(self, &n);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_any(self: *TypeTable): usize {
|
||||||
|
var n: TypeNode = .Any;
|
||||||
|
ret push(self, &n);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_unit(self: *TypeTable): usize {
|
||||||
|
var n: TypeNode = .Unit;
|
||||||
|
ret push(self, &n);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_infer(self: *TypeTable): usize {
|
||||||
|
var n: TypeNode = .Infer;
|
||||||
|
ret push(self, &n);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_bool(self: *TypeTable): usize {
|
||||||
|
var n: TypeNode = .Bool;
|
||||||
|
ret push(self, &n);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_forall(self: *TypeTable): usize {
|
||||||
|
var n: TypeNode = .Forall;
|
||||||
|
ret push(self, &n);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_exists(self: *TypeTable): usize {
|
||||||
|
var n: TypeNode = .Exists;
|
||||||
|
ret push(self, &n);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_int(self: *TypeTable, bits: usize, is_signed: bool): usize {
|
||||||
|
var n: TypeNode = .Int(.{ .bits = bits, .is_signed = is_signed });
|
||||||
|
ret push(self, &n);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_float(self: *TypeTable, bits: usize): usize {
|
||||||
|
var n: TypeNode = .Float(.{ .bits = bits });
|
||||||
|
ret push(self, &n);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_ptr(self: *TypeTable, pointee: usize): usize {
|
||||||
|
var n: TypeNode = .Ptr(.{ .pointee = pointee });
|
||||||
|
ret push(self, &n);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_array(self: *TypeTable, element: usize, len: usize): usize {
|
||||||
|
var n: TypeNode = .Array(.{ .element = element, .len = len });
|
||||||
|
ret push(self, &n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// params 所有权转移进表;返回后调用方不得再 drop。
|
||||||
|
fn make_fn(self: *TypeTable, ret_tid: usize, params: Vec): usize {
|
||||||
|
var n: TypeNode = .Fn(.{ .params = params, .ret_type = ret_tid });
|
||||||
|
ret push(self, &n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// agg 所有权转移进表;返回后调用方不得再 drop。
|
||||||
|
fn make_agg(self: *TypeTable, kind: AggKind, agg: Agg): usize {
|
||||||
|
var n: TypeNode = .Error;
|
||||||
|
match kind {
|
||||||
|
.Struct => { n = .Struct(agg); },
|
||||||
|
.Union => { n = .Union(agg); },
|
||||||
|
.Enum => { n = .Enum(agg); },
|
||||||
|
.Shape => { n = .Shape(agg); },
|
||||||
|
}
|
||||||
|
ret push(self, &n);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn is_subtype(self: *TypeTable, sub: usize, super: usize): bool {
|
||||||
|
if sub == 0 || super == 0 { ret false; }
|
||||||
|
if sub == super { ret true; }
|
||||||
|
|
||||||
|
var ns = node(self, sub);
|
||||||
|
var np = node(self, super);
|
||||||
|
if ns == null || np == null { ret false; }
|
||||||
|
|
||||||
|
match ns.node {
|
||||||
|
.Never => { ret true; },
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
match np.node {
|
||||||
|
.Any => { ret true; },
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
match ns.node {
|
||||||
|
.Int(x) => {
|
||||||
|
match np.node {
|
||||||
|
.Int(y) => {
|
||||||
|
if x.bits == 0 { ret true; }
|
||||||
|
if x.is_signed == y.is_signed { ret x.bits <= y.bits; }
|
||||||
|
if !x.is_signed && y.is_signed { ret y.bits > x.bits; }
|
||||||
|
ret false;
|
||||||
|
},
|
||||||
|
_ => { ret false; },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Float(x) => {
|
||||||
|
match np.node {
|
||||||
|
.Float(y) => {
|
||||||
|
if x.bits == 0 { ret true; }
|
||||||
|
ret x.bits <= y.bits;
|
||||||
|
},
|
||||||
|
_ => { ret false; },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fn_param_tid(self: *TypeTable, fn_tid: usize, idx: usize): usize {
|
||||||
|
var n = node(self, fn_tid);
|
||||||
|
if n == null { ret 0; }
|
||||||
|
match n.node {
|
||||||
|
.Fn(x) => {
|
||||||
|
if idx >= x.params.len { ret 0; }
|
||||||
|
ret usize_at(x.params, idx);
|
||||||
|
},
|
||||||
|
_ => { ret 0; },
|
||||||
|
}
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn agg_find(agg: Agg, name: ByteView, out_idx: *usize): bool {
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < agg.variants.len {
|
||||||
|
var m = member_at(&agg.variants, i);
|
||||||
|
if m != null && ByteView.eq(m.name, name) {
|
||||||
|
if out_idx != null { out_idx.* = i; }
|
||||||
|
ret true;
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn agg_member_idx(self: *TypeTable, tid: usize, name: ByteView, out_idx: *usize): bool {
|
||||||
|
var n = node(self, tid);
|
||||||
|
if n == null { ret false; }
|
||||||
|
match n.node {
|
||||||
|
.Struct(x) => { ret agg_find(x, name, out_idx); },
|
||||||
|
.Union(x) => { ret agg_find(x, name, out_idx); },
|
||||||
|
.Enum(x) => { ret agg_find(x, name, out_idx); },
|
||||||
|
.Shape(x) => { ret agg_find(x, name, out_idx); },
|
||||||
|
_ => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn push_out(buf: *ByteBuffer, bytes: []u8): bool {
|
||||||
|
match ByteBuffer.append(buf, bytes) {
|
||||||
|
.Ok => { ret true; },
|
||||||
|
.Err(error) => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_usize(buf: *ByteBuffer, value: usize): bool {
|
||||||
|
match append_usize(buf, value, 10, false) {
|
||||||
|
.Ok => { ret true; },
|
||||||
|
.Err(error) => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_rec(self: *TypeTable, id: usize, out: *ByteBuffer, stack: *Vec): bool {
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < stack.len {
|
||||||
|
if usize_at(stack.*, i) == id {
|
||||||
|
if !push_out(out, "<rec#") { ret false; }
|
||||||
|
if !push_usize(out, id) { ret false; }
|
||||||
|
ret push_out(out, ">");
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
if stack.len > 32 {
|
||||||
|
ret push_out(out, "...");
|
||||||
|
}
|
||||||
|
match Vec.push(stack, &id as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ret false; },
|
||||||
|
}
|
||||||
|
|
||||||
|
var n = node(self, id);
|
||||||
|
if n == null {
|
||||||
|
if !push_out(out, "none") { ret false; }
|
||||||
|
var popped: usize = 0;
|
||||||
|
Vec.pop(stack, &popped as *u8);
|
||||||
|
ret true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ok = true;
|
||||||
|
match n.node {
|
||||||
|
.Error => { ok = push_out(out, "error"); },
|
||||||
|
.Never => { ok = push_out(out, "never"); },
|
||||||
|
.Any => { ok = push_out(out, "any"); },
|
||||||
|
.Unit => { ok = push_out(out, "unit"); },
|
||||||
|
.Infer => { ok = push_out(out, "infer"); },
|
||||||
|
.Bool => { ok = push_out(out, "bool"); },
|
||||||
|
.Forall => { ok = push_out(out, "quant"); },
|
||||||
|
.Exists => { ok = push_out(out, "quant"); },
|
||||||
|
.Int(x) => {
|
||||||
|
if x.bits == 0 {
|
||||||
|
ok = push_out(out, "comptime_int");
|
||||||
|
} else {
|
||||||
|
if !push_out(out, if x.is_signed { "i" } else { "u" }) { ok = false; }
|
||||||
|
if ok && !push_usize(out, x.bits) { ok = false; }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Float(x) => {
|
||||||
|
if x.bits == 0 {
|
||||||
|
ok = push_out(out, "comptime_float");
|
||||||
|
} else {
|
||||||
|
if !push_out(out, "f") { ok = false; }
|
||||||
|
if ok && !push_usize(out, x.bits) { ok = false; }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Ptr(x) => {
|
||||||
|
if !push_out(out, "*") { ok = false; }
|
||||||
|
if ok { ok = dump_rec(self, x.pointee, out, stack); }
|
||||||
|
},
|
||||||
|
.Array(x) => {
|
||||||
|
if !push_out(out, "[") { ok = false; }
|
||||||
|
if ok && !push_usize(out, x.len) { ok = false; }
|
||||||
|
if ok && !push_out(out, "]") { ok = false; }
|
||||||
|
if ok { ok = dump_rec(self, x.element, out, stack); }
|
||||||
|
},
|
||||||
|
.Struct(x) => { ok = dump_agg(self, "struct", x, out, stack); },
|
||||||
|
.Union(x) => { ok = dump_agg(self, "union", x, out, stack); },
|
||||||
|
.Enum(x) => { ok = dump_agg(self, "enum", x, out, stack); },
|
||||||
|
.Shape(x) => { ok = dump_agg(self, "shape", x, out, stack); },
|
||||||
|
.Fn(x) => {
|
||||||
|
if !push_out(out, "fn(") { ok = false; }
|
||||||
|
var j: usize = 0;
|
||||||
|
while ok && j < x.params.len {
|
||||||
|
if j != 0 && !push_out(out, ",") { ok = false; }
|
||||||
|
if ok { ok = dump_rec(self, usize_at(x.params, j), out, stack); }
|
||||||
|
j = j + 1;
|
||||||
|
}
|
||||||
|
if ok && !push_out(out, ")") { ok = false; }
|
||||||
|
if ok { ok = dump_rec(self, x.ret_type, out, stack); }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var dropped: usize = 0;
|
||||||
|
Vec.pop(stack, &dropped as *u8);
|
||||||
|
ret ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_agg(self: *TypeTable, kw: []u8, agg: Agg, out: *ByteBuffer, stack: *Vec): bool {
|
||||||
|
if !push_out(out, kw) { ret false; }
|
||||||
|
if !push_out(out, "{") { ret false; }
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < agg.variants.len {
|
||||||
|
if i != 0 && !push_out(out, ", ") { ret false; }
|
||||||
|
var m = member_at(&agg.variants, i);
|
||||||
|
if m == null { ret false; }
|
||||||
|
if m.name.len == 0 {
|
||||||
|
if !push_out(out, "?") { ret false; }
|
||||||
|
} else {
|
||||||
|
if !push_out(out, ByteView.as_slice(m.name)) { ret false; }
|
||||||
|
}
|
||||||
|
if !push_out(out, ":") { ret false; }
|
||||||
|
if !dump_rec(self, m.type_id, out, stack) { ret false; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret push_out(out, "}");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pure_dump(self: *TypeTable, id: usize, out: *ByteBuffer): bool {
|
||||||
|
var stack = Vec.blank(@sizeof(usize), @alignof(usize));
|
||||||
|
defer { Vec.drop(&stack); }
|
||||||
|
ret dump_rec(self, id, out, &stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pure_dump_text(self: *TypeTable, id: usize): ByteBuffer {
|
||||||
|
var out = ByteBuffer.new();
|
||||||
|
if !pure_dump(self, id, &out) {
|
||||||
|
ByteBuffer.clear(&out);
|
||||||
|
}
|
||||||
|
ret out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn test_intern_simple() {
|
||||||
|
var tt: TypeTable = undefined;
|
||||||
|
match new() {
|
||||||
|
.Ok(v) => { tt = v; },
|
||||||
|
.Err(error) => { @assert(false, "type table new failed"); },
|
||||||
|
}
|
||||||
|
defer { drop(&tt); }
|
||||||
|
|
||||||
|
var i32a = make_int(&tt, 32, true);
|
||||||
|
var i32b = make_int(&tt, 32, true);
|
||||||
|
var u32 = make_int(&tt, 32, false);
|
||||||
|
@assert(i32a != 0 && i32a == i32b, "i32 interned");
|
||||||
|
@assert(u32 != i32a, "u32 distinct");
|
||||||
|
|
||||||
|
var p1 = make_ptr(&tt, i32a);
|
||||||
|
var p2 = make_ptr(&tt, i32a);
|
||||||
|
@assert(p1 == p2, "ptr interned");
|
||||||
|
@assert(make_ptr(&tt, u32) != p1, "ptr pointee distinct");
|
||||||
|
|
||||||
|
var a1 = make_array(&tt, i32a, 3);
|
||||||
|
var a2 = make_array(&tt, i32a, 3);
|
||||||
|
var a3 = make_array(&tt, i32a, 4);
|
||||||
|
@assert(a1 == a2 && a1 != a3, "array interned");
|
||||||
|
|
||||||
|
var params = Vec.blank(@sizeof(usize), @alignof(usize));
|
||||||
|
match Vec.push(¶ms, &i32a as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { @assert(false, "push param"); },
|
||||||
|
}
|
||||||
|
var f1 = make_fn(&tt, u32, params);
|
||||||
|
var params2 = Vec.blank(@sizeof(usize), @alignof(usize));
|
||||||
|
match Vec.push(¶ms2, &i32a as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { @assert(false, "push param"); },
|
||||||
|
}
|
||||||
|
var f2 = make_fn(&tt, u32, params2);
|
||||||
|
@assert(f1 == f2, "fn interned");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_agg_intern_and_member() {
|
||||||
|
var tt: TypeTable = undefined;
|
||||||
|
match new() {
|
||||||
|
.Ok(v) => { tt = v; },
|
||||||
|
.Err(error) => { @assert(false, "type table new failed"); },
|
||||||
|
}
|
||||||
|
defer { drop(&tt); }
|
||||||
|
|
||||||
|
var u8t = make_int(&tt, 8, false);
|
||||||
|
var i32t = make_int(&tt, 32, true);
|
||||||
|
|
||||||
|
var m1 = Vec.blank(@sizeof(Member), @alignof(Member));
|
||||||
|
push_member(&m1, "a", u8t);
|
||||||
|
push_member(&m1, "b", i32t);
|
||||||
|
|
||||||
|
var s1 = make_agg(&tt, .Struct, .{
|
||||||
|
.mode = .Auto,
|
||||||
|
.fixed_align_bits = 0,
|
||||||
|
.tag_type = 0,
|
||||||
|
.variants = m1,
|
||||||
|
});
|
||||||
|
|
||||||
|
var m2 = Vec.blank(@sizeof(Member), @alignof(Member));
|
||||||
|
push_member(&m2, "a", u8t);
|
||||||
|
push_member(&m2, "b", i32t);
|
||||||
|
var s2 = make_agg(&tt, .Struct, .{
|
||||||
|
.mode = .Auto,
|
||||||
|
.fixed_align_bits = 0,
|
||||||
|
.tag_type = 0,
|
||||||
|
.variants = m2,
|
||||||
|
});
|
||||||
|
@assert(s1 != 0, "s1 nonzero");
|
||||||
|
@assert(s2 != 0, "s2 nonzero");
|
||||||
|
@assert(pure_dump_str(&tt, s1, "struct{a:u8, b:i32}"), "s1 shape");
|
||||||
|
@assert(pure_dump_str(&tt, s2, "struct{a:u8, b:i32}"), "s2 shape");
|
||||||
|
@assert(s1 == s2, "struct interned");
|
||||||
|
|
||||||
|
var idx: usize = 0;
|
||||||
|
@assert(agg_member_idx(&tt, s1, ByteView.from_slice("b"), &idx), "find b");
|
||||||
|
@assert(idx == 1, "b index");
|
||||||
|
@assert(!agg_member_idx(&tt, s1, ByteView.from_slice("z"), &idx), "missing z");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_subtype() {
|
||||||
|
var tt: TypeTable = undefined;
|
||||||
|
match new() {
|
||||||
|
.Ok(v) => { tt = v; },
|
||||||
|
.Err(error) => { @assert(false, "type table new failed"); },
|
||||||
|
}
|
||||||
|
defer { drop(&tt); }
|
||||||
|
|
||||||
|
var i8t = make_int(&tt, 8, true);
|
||||||
|
var i32t = make_int(&tt, 32, true);
|
||||||
|
var u32t = make_int(&tt, 32, false);
|
||||||
|
var u64t = make_int(&tt, 64, false);
|
||||||
|
var ci = make_int(&tt, 0, true);
|
||||||
|
var never = make_never(&tt);
|
||||||
|
var anyt = make_any(&tt);
|
||||||
|
var f32t = make_float(&tt, 32);
|
||||||
|
var f64t = make_float(&tt, 64);
|
||||||
|
|
||||||
|
@assert(is_subtype(&tt, i8t, i32t), "i8 <= i32");
|
||||||
|
@assert(!is_subtype(&tt, i32t, i8t), "i32 !<= i8");
|
||||||
|
@assert(!is_subtype(&tt, i32t, u32t), "i32 !<= u32");
|
||||||
|
@assert(is_subtype(&tt, u32t, u64t), "u32 <= u64");
|
||||||
|
@assert(!is_subtype(&tt, u32t, i32t), "u32 !<= i32");
|
||||||
|
@assert(is_subtype(&tt, u32t, make_int(&tt, 64, true)), "u32 <= i64");
|
||||||
|
@assert(is_subtype(&tt, ci, i32t), "comptime_int <= any int");
|
||||||
|
@assert(is_subtype(&tt, never, i32t), "never <= all");
|
||||||
|
@assert(is_subtype(&tt, i32t, anyt), "all <= any");
|
||||||
|
@assert(is_subtype(&tt, f32t, f64t), "f32 <= f64");
|
||||||
|
@assert(!is_subtype(&tt, f64t, f32t), "f64 !<= f32");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_dump() {
|
||||||
|
var tt: TypeTable = undefined;
|
||||||
|
match new() {
|
||||||
|
.Ok(v) => { tt = v; },
|
||||||
|
.Err(error) => { @assert(false, "type table new failed"); },
|
||||||
|
}
|
||||||
|
defer { drop(&tt); }
|
||||||
|
|
||||||
|
@assert(pure_dump_str(&tt, make_int(&tt, 32, true), "i32"), "dump i32");
|
||||||
|
@assert(pure_dump_str(&tt, make_int(&tt, 0, true), "comptime_int"), "dump comptime_int");
|
||||||
|
@assert(pure_dump_str(&tt, make_float(&tt, 64), "f64"), "dump f64");
|
||||||
|
@assert(pure_dump_str(&tt, make_ptr(&tt, 0), "*none"), "dump ptr none");
|
||||||
|
|
||||||
|
var u8t = make_int(&tt, 8, false);
|
||||||
|
@assert(pure_dump_str(&tt, make_array(&tt, u8t, 3), "[3]u8"), "dump array");
|
||||||
|
|
||||||
|
var params = Vec.blank(@sizeof(usize), @alignof(usize));
|
||||||
|
match Vec.push(¶ms, &u8t as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { @assert(false, "push param"); },
|
||||||
|
}
|
||||||
|
var fn_t = make_fn(&tt, 0, params);
|
||||||
|
@assert(pure_dump_str(&tt, fn_t, "fn(u8)none"), "dump fn");
|
||||||
|
|
||||||
|
var members = Vec.blank(@sizeof(Member), @alignof(Member));
|
||||||
|
push_member(&members, "x", u8t);
|
||||||
|
var st = make_agg(&tt, .Struct, .{
|
||||||
|
.mode = .Auto,
|
||||||
|
.fixed_align_bits = 0,
|
||||||
|
.tag_type = 0,
|
||||||
|
.variants = members,
|
||||||
|
});
|
||||||
|
@assert(pure_dump_str(&tt, st, "struct{x:u8}"), "dump struct");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pure_dump_str(tt: *TypeTable, id: usize, expected: []u8): bool {
|
||||||
|
var out = pure_dump_text(tt, id);
|
||||||
|
defer { ByteBuffer.drop(&out); }
|
||||||
|
var got = ByteBuffer.as_slice(out);
|
||||||
|
if got.len != expected.len { ret false; }
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < expected.len {
|
||||||
|
if got.ptr[i] != expected.ptr[i] { ret false; }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_intern_simple();
|
||||||
|
test_agg_intern_and_member();
|
||||||
|
test_subtype();
|
||||||
|
test_dump();
|
||||||
|
}
|
||||||
@@ -155,7 +155,6 @@ fn stable_sort(data: *u8, len: usize, elem_size: usize,
|
|||||||
ret .Ok;
|
ret .Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
type Item = struct {
|
type Item = struct {
|
||||||
key: i32,
|
key: i32,
|
||||||
|
|||||||
@@ -127,7 +127,6 @@ fn drop(self: *Arena) {
|
|||||||
self.current = 0;
|
self.current = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn is_aligned(p: *u8, align: usize): bool {
|
fn is_aligned(p: *u8, align: usize): bool {
|
||||||
ret ((p as usize) & (align - 1)) == 0;
|
ret ((p as usize) & (align - 1)) == 0;
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ fn new(): ByteBuffer {
|
|||||||
ret ByteBuffer { .storage = Vec.blank(1, 1) };
|
ret ByteBuffer { .storage = Vec.blank(1, 1) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn with_capacity(cap: usize): ByteBuffer {
|
||||||
|
var b = new();
|
||||||
|
reserve(&b, cap);
|
||||||
|
ret b;
|
||||||
|
}
|
||||||
|
|
||||||
fn size(self: ByteBuffer): usize {
|
fn size(self: ByteBuffer): usize {
|
||||||
ret self.storage.len;
|
ret self.storage.len;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
type .{ .Error, .make } = @import("error.spl");
|
type .{ .Error, .make } = @import("error.spl");
|
||||||
|
|
||||||
|
#extern(vm) fn vm_bytes_eq(a: *any, alen: usize, b: *any, blen: usize): i32;
|
||||||
|
|
||||||
type ByteView = @self;
|
type ByteView = @self;
|
||||||
|
|
||||||
ptr: *u8,
|
ptr: *u8,
|
||||||
@@ -62,13 +64,7 @@ fn byte_at(self: ByteView, index: usize): ByteResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn eq(a: ByteView, b: ByteView): bool {
|
fn eq(a: ByteView, b: ByteView): bool {
|
||||||
if a.len != b.len { ret false; }
|
ret vm_bytes_eq(a.ptr as *any, a.len, b.ptr as *any, b.len) != 0;
|
||||||
var i: usize = 0;
|
|
||||||
while i < a.len {
|
|
||||||
if a.ptr[i] != b.ptr[i] { ret false; }
|
|
||||||
i = i + 1;
|
|
||||||
}
|
|
||||||
ret true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 按无符号字节进行字典序比较,结果只承诺小于、等于或大于零。
|
// 按无符号字节进行字典序比较,结果只承诺小于、等于或大于零。
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ type .{ .ByteView } = @import("byte_view.spl");
|
|||||||
type .{ .Value, .int, .uint, .char, .str, .addr } = @import("value.spl");
|
type .{ .Value, .int, .uint, .char, .str, .addr } = @import("value.spl");
|
||||||
type .{ .BigInt, .Bits } = @import("mp.spl");
|
type .{ .BigInt, .Bits } = @import("mp.spl");
|
||||||
|
|
||||||
// 注意:radix 边界直接用字面量。stage1 刻意不做常量求值/折叠,
|
// radix 边界直接用字面量:顶层 const 不参与运行时常量折叠。
|
||||||
// 顶层 const 不应用于运行时表达式。
|
|
||||||
fn valid_radix(radix: u8): bool {
|
fn valid_radix(radix: u8): bool {
|
||||||
ret radix >= 2 && radix <= 36;
|
ret radix >= 2 && radix <= 36;
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,6 @@ fn append_bool(out: *ByteBuffer, value: bool): Status {
|
|||||||
ret ByteBuffer.append(out, "false");
|
ret ByteBuffer.append(out, "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 显式装箱值格式化 ----
|
|
||||||
|
|
||||||
// Int/Uint 落到整数;signed 仅对 Int 的十进制生效,其余按无符号位模式输出。
|
// Int/Uint 落到整数;signed 仅对 Int 的十进制生效,其余按无符号位模式输出。
|
||||||
fn append_value_int(out: *ByteBuffer, value: Value, radix: u8, uppercase: bool,
|
fn append_value_int(out: *ByteBuffer, value: Value, radix: u8, uppercase: bool,
|
||||||
@@ -223,7 +222,6 @@ fn format(out: *ByteBuffer, fmt: []u8, args: []Value): Status {
|
|||||||
ret ByteBuffer.append(out, ByteBuffer.as_slice(temp));
|
ret ByteBuffer.append(out, ByteBuffer.as_slice(temp));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn expect_ok(result: Status) {
|
fn expect_ok(result: Status) {
|
||||||
match result {
|
match result {
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ fn hash_usize_value(value: usize): usize {
|
|||||||
ret h;
|
ret h;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- map 回调 ----
|
|
||||||
|
|
||||||
// 键按 usize 解释。
|
// 键按 usize 解释。
|
||||||
fn hash_usize(key: *u8): usize {
|
fn hash_usize(key: *u8): usize {
|
||||||
@@ -55,7 +54,6 @@ fn eq_view(a: *u8, b: *u8): bool {
|
|||||||
ret ByteView.eq((a as *ByteView).*, (b as *ByteView).*);
|
ret ByteView.eq((a as *ByteView).*, (b as *ByteView).*);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn expect_hash(view: []u8, expected: usize) {
|
fn expect_hash(view: []u8, expected: usize) {
|
||||||
var got = hash_bytes(view.ptr, view.len);
|
var got = hash_bytes(view.ptr, view.len);
|
||||||
|
|||||||
+2
-7
@@ -1,11 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// I/O 便利层(构建在 sys/file 之上):
|
// sys/file 之上的 I/O 便利层:C 字符串路径 open、精确读取/完整写入、整读、stdout/stderr 打印。
|
||||||
// - C 字符串路径/模式的 open 包装
|
// 短读/短写零进展与关闭失败返回 Err(.Io);写入失败不承诺回滚。
|
||||||
// - 精确读取、完整写入和普通文件整读
|
|
||||||
// - stdout/stderr 打印
|
|
||||||
// 说明:read_all 会按文件大小一次性读入 ByteBuffer;不处理增长中的文件。
|
|
||||||
// 短读、短写后的零进展及关闭失败返回 Err(.Io);文件写入不承诺回滚。
|
|
||||||
|
|
||||||
type .{ .Error, .Status, .make } = @import("error.spl");
|
type .{ .Error, .Status, .make } = @import("error.spl");
|
||||||
type .{ .ByteView } = @import("byte_view.spl");
|
type .{ .ByteView } = @import("byte_view.spl");
|
||||||
@@ -130,7 +126,6 @@ fn read_all(path: ByteView, out: *ByteBuffer): Status {
|
|||||||
ret .Ok;
|
ret .Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn view(text: []u8): ByteView {
|
fn view(text: []u8): ByteView {
|
||||||
ret ByteView.from_slice(text);
|
ret ByteView.from_slice(text);
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
// 轻量 JSON 解析 / 输出器(递归下降 + 节点池),用于验证底层库的拼装,未来服务 LSP。
|
// 轻量 JSON 解析/输出器:输入按源切片借用、不解码(round-trip 保真),数字不做数值化。
|
||||||
// 设计:
|
// 节点/子项存 Vec 保留插入顺序;输出写 ByteBuffer。递归/输出深度上限见 max_depth()。
|
||||||
// - 输入 ByteView,字符串/数字以“源切片”借用,不做解码,round-trip 保真;
|
|
||||||
// - 节点池 Vec[JsonNode];数组/对象子项存 Vec[JsonChild],保留插入顺序;
|
|
||||||
// - 错误带字节位置(error.spl);输出写 ByteBuffer;缩进/数字用 ByteBuffer/fmt。
|
|
||||||
// 限制:不做字符串转义解码、数字不做数值化(原样保存);递归/输出深度上限见 max_depth()。
|
|
||||||
|
|
||||||
type .{ .Error, .Status, .make, .at } = @import("error.spl");
|
type .{ .Error, .Status, .make, .at } = @import("error.spl");
|
||||||
type .{ .ByteView, .ViewResult, .FindResult } = @import("byte_view.spl");
|
type .{ .ByteView, .ViewResult, .FindResult } = @import("byte_view.spl");
|
||||||
@@ -393,7 +389,7 @@ fn root_index(doc: *Json): usize {
|
|||||||
ret doc.root;
|
ret doc.root;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 枚举 tag 取整(stage1 枚举 == 有缺陷,用 @enum2int 比较更稳妥)。
|
// 枚举 tag 取整:枚举 == 不可靠,用 @enum2int 比较。
|
||||||
fn kind_code(kind: JsonKind): usize {
|
fn kind_code(kind: JsonKind): usize {
|
||||||
ret @enum2int(kind) as usize;
|
ret @enum2int(kind) as usize;
|
||||||
}
|
}
|
||||||
@@ -578,7 +574,6 @@ fn emit_pretty(out: *ByteBuffer, doc: *Json, indent: usize): Status {
|
|||||||
ret emit(out, doc, doc.root, indent, 0);
|
ret emit(out, doc, doc.root, indent, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 构造辅助(写 JSON 片段;字符串需由调用方保证已转义)----
|
|
||||||
|
|
||||||
fn write_int(out: *ByteBuffer, value: i64): Status {
|
fn write_int(out: *ByteBuffer, value: i64): Status {
|
||||||
ret append_i64(out, value, 10, false);
|
ret append_i64(out, value, 10, false);
|
||||||
@@ -588,7 +583,6 @@ fn write_u64(out: *ByteBuffer, value: u64): Status {
|
|||||||
ret append_u64(out, value, 10, false);
|
ret append_u64(out, value, 10, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn expect_ok(result: Status) {
|
fn expect_ok(result: Status) {
|
||||||
match result {
|
match result {
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ fn fatal(self: *Logger, message: []u8) {
|
|||||||
Exit.fail();
|
Exit.fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 格式化入口(显式装箱,见 value.spl / fmt.format)----
|
|
||||||
|
|
||||||
// 先格式化到临时缓冲;格式错误在写出前返回 Err(.InvalidFormat)。
|
// 先格式化到临时缓冲;格式错误在写出前返回 Err(.InvalidFormat)。
|
||||||
fn logf(self: *Logger, level: Level, fmt: []u8, args: []Value): Status {
|
fn logf(self: *Logger, level: Level, fmt: []u8, args: []Value): Status {
|
||||||
@@ -125,7 +124,6 @@ fn fatalf(self: *Logger, fmt: []u8, args: []Value) {
|
|||||||
Exit.fail();
|
Exit.fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn expect_name(level: Level, expected: []u8) {
|
fn expect_name(level: Level, expected: []u8) {
|
||||||
@assert(ByteView.eq(ByteView.from_slice(level_name(level)),
|
@assert(ByteView.eq(ByteView.from_slice(level_name(level)),
|
||||||
|
|||||||
@@ -99,7 +99,6 @@ fn parse_frame(input: ByteView, body_out: *ByteBuffer): FrameResult {
|
|||||||
ret .Ok(body_start + content_len);
|
ret .Ok(body_start + content_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn expect_ok(result: Status) {
|
fn expect_ok(result: Status) {
|
||||||
match result {
|
match result {
|
||||||
|
|||||||
+45
-191
@@ -7,9 +7,14 @@
|
|||||||
type .{ .Error, .Status, .make } = @import("error.spl");
|
type .{ .Error, .Status, .make } = @import("error.spl");
|
||||||
type .{ .Vec } = @import("vec.spl");
|
type .{ .Vec } = @import("vec.spl");
|
||||||
type .{ .ByteView } = @import("byte_view.spl");
|
type .{ .ByteView } = @import("byte_view.spl");
|
||||||
type .{ .hash_usize, .eq_usize, .hash_view, .eq_view } = @import("hash.spl");
|
|
||||||
type .{ .checked_add, .checked_mul, .grow_capacity } = @import("memory.spl");
|
#extern(vm) fn vm_map_valid(m: *any): i32;
|
||||||
type .{ .copy } = @import("../sys/mem.spl");
|
#extern(vm) fn vm_map_reserve(m: *any, need: usize): i32;
|
||||||
|
#extern(vm) fn vm_map_put(m: *any, key: *any, val: *any): i32;
|
||||||
|
#extern(vm) fn vm_map_get(m: *any, key: *any, out: *any): i32;
|
||||||
|
#extern(vm) fn vm_map_del(m: *any, key: *any): i32;
|
||||||
|
#extern(vm) fn vm_map_clear(m: *any): void;
|
||||||
|
#extern(vm) fn vm_map_drop(m: *any): void;
|
||||||
|
|
||||||
// 槽位状态:Empty/Occupied/Deleted(墓碑,仅在扩容重哈希时清除)。
|
// 槽位状态:Empty/Occupied/Deleted(墓碑,仅在扩容重哈希时清除)。
|
||||||
// 负载因子 70/128。
|
// 负载因子 70/128。
|
||||||
@@ -20,11 +25,6 @@ type SlotState = enum {
|
|||||||
Deleted,
|
Deleted,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state_code(state: SlotState): u8 {
|
|
||||||
var s: SlotState = state;
|
|
||||||
ret @enum2int(s) as u8;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查找/删除结果:显式区分命中与缺失,替代 bool 哨兵。
|
// 查找/删除结果:显式区分命中与缺失,替代 bool 哨兵。
|
||||||
type LookupResult = enum {
|
type LookupResult = enum {
|
||||||
Found,
|
Found,
|
||||||
@@ -39,11 +39,15 @@ states: Vec,
|
|||||||
size: usize,
|
size: usize,
|
||||||
key_size: usize,
|
key_size: usize,
|
||||||
val_size: usize,
|
val_size: usize,
|
||||||
hash_fn: fn(*u8): usize,
|
hash_kind: usize,
|
||||||
eq_fn: fn(*u8, *u8): bool,
|
|
||||||
|
// 键解释:0 = usize;1 = ByteView(按内容哈希/比较)。
|
||||||
|
fn hash_kind_usize(): usize { ret 0; }
|
||||||
|
fn hash_kind_view(): usize { ret 1; }
|
||||||
|
fn hash_kind_constant(): usize { ret 2; }
|
||||||
|
|
||||||
fn blank(key_size: usize, key_align: usize, val_size: usize, val_align: usize,
|
fn blank(key_size: usize, key_align: usize, val_size: usize, val_align: usize,
|
||||||
hash_fn: fn(*u8): usize, eq_fn: fn(*u8, *u8): bool): Map {
|
hash_kind: usize): Map {
|
||||||
ret Map {
|
ret Map {
|
||||||
.keys = Vec.blank(key_size, key_align),
|
.keys = Vec.blank(key_size, key_align),
|
||||||
.vals = Vec.blank(val_size, val_align),
|
.vals = Vec.blank(val_size, val_align),
|
||||||
@@ -51,23 +55,30 @@ fn blank(key_size: usize, key_align: usize, val_size: usize, val_align: usize,
|
|||||||
.size = 0,
|
.size = 0,
|
||||||
.key_size = key_size,
|
.key_size = key_size,
|
||||||
.val_size = val_size,
|
.val_size = val_size,
|
||||||
.hash_fn = hash_fn,
|
.hash_kind = hash_kind,
|
||||||
.eq_fn = eq_fn,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(key_size: usize, key_align: usize, val_size: usize, val_align: usize,
|
fn new(key_size: usize, key_align: usize, val_size: usize, val_align: usize,
|
||||||
hash_fn: fn(*u8): usize, eq_fn: fn(*u8, *u8): bool): Map {
|
hash_kind: usize): Map {
|
||||||
ret blank(key_size, key_align, val_size, val_align, hash_fn, eq_fn);
|
ret blank(key_size, key_align, val_size, val_align, hash_kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn valid(self: Map): bool {
|
fn status_from_code(code: i32): Status {
|
||||||
if self.key_size == 0 || self.val_size == 0 { ret false; }
|
if code == 0 { ret .Ok; }
|
||||||
if self.states.len != self.keys.len || self.states.len != self.vals.len {
|
if code == 2 { ret .Err(make(.OutOfRange)); }
|
||||||
ret false;
|
if code == 3 { ret .Err(make(.Overflow)); }
|
||||||
}
|
if code == 4 { ret .Err(make(.InvalidAlignment)); }
|
||||||
if self.size > self.states.len { ret false; }
|
ret .Err(make(.InvalidArgument));
|
||||||
ret true;
|
}
|
||||||
|
|
||||||
|
fn lookup_from_code(code: i32): LookupResult {
|
||||||
|
if code == 0 { ret .Found; }
|
||||||
|
ret .NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn valid(self: *Map): bool {
|
||||||
|
ret vm_map_valid(self as *any) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(self: Map): usize {
|
fn size(self: Map): usize {
|
||||||
@@ -85,10 +96,8 @@ fn slot_count(self: Map): usize {
|
|||||||
|
|
||||||
fn slot_state(self: Map, index: usize): SlotState {
|
fn slot_state(self: Map, index: usize): SlotState {
|
||||||
var code = self.states.data[index];
|
var code = self.states.data[index];
|
||||||
var occupied: SlotState = .Occupied;
|
if code == 1 { ret .Occupied; }
|
||||||
var deleted_state: SlotState = .Deleted;
|
if code == 2 { ret .Deleted; }
|
||||||
if code == state_code(occupied) { ret .Occupied; }
|
|
||||||
if code == state_code(deleted_state) { ret .Deleted; }
|
|
||||||
ret .Empty;
|
ret .Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,186 +118,32 @@ fn val_at(self: *Map, index: usize): *u8 {
|
|||||||
ret self.vals.data + index * self.val_size;
|
ret self.vals.data + index * self.val_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回占用键的槽位;未找到返回 ~0。容量为 0 时直接未找到。
|
|
||||||
fn find_slot(self: *Map, key: *u8): usize {
|
|
||||||
if self.states.cap == 0 { ret ~(0 as usize); }
|
|
||||||
var mask = self.states.cap - 1;
|
|
||||||
var i = self.hash_fn(key) & mask;
|
|
||||||
var empty: SlotState = .Empty;
|
|
||||||
var occupied: SlotState = .Occupied;
|
|
||||||
var probes: usize = 0;
|
|
||||||
while probes < self.states.cap && self.states.data[i] != state_code(empty) {
|
|
||||||
if self.states.data[i] == state_code(occupied) &&
|
|
||||||
self.eq_fn(self.keys.data + i * self.key_size, key) {
|
|
||||||
ret i;
|
|
||||||
}
|
|
||||||
i = (i + 1) & mask;
|
|
||||||
probes = probes + 1;
|
|
||||||
}
|
|
||||||
ret ~(0 as usize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 要求容量非零且键不存在。优先复用探测链中的第一个墓碑。
|
|
||||||
fn place(self: *Map, key: *u8, val: *u8) {
|
|
||||||
var mask = self.states.cap - 1;
|
|
||||||
var i = self.hash_fn(key) & mask;
|
|
||||||
var empty: SlotState = .Empty;
|
|
||||||
var deleted: SlotState = .Deleted;
|
|
||||||
var occupied: SlotState = .Occupied;
|
|
||||||
var first_deleted = ~(0 as usize);
|
|
||||||
var probes: usize = 0;
|
|
||||||
while probes < self.states.cap && self.states.data[i] != state_code(empty) {
|
|
||||||
if self.states.data[i] == state_code(deleted) && first_deleted == ~(0 as usize) {
|
|
||||||
first_deleted = i;
|
|
||||||
}
|
|
||||||
i = (i + 1) & mask;
|
|
||||||
probes = probes + 1;
|
|
||||||
}
|
|
||||||
if first_deleted != ~(0 as usize) {
|
|
||||||
i = first_deleted;
|
|
||||||
}
|
|
||||||
copy((self.keys.data + i * self.key_size) as *any, key as *any, self.key_size);
|
|
||||||
copy((self.vals.data + i * self.val_size) as *any, val as *any, self.val_size);
|
|
||||||
self.states.data[i] = state_code(occupied);
|
|
||||||
self.size = self.size + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保证至少 need 个槽位。所有检查在旧表改动前完成,失败保持原表不变。
|
// 保证至少 need 个槽位。所有检查在旧表改动前完成,失败保持原表不变。
|
||||||
fn reserve(self: *Map, need: usize): Status {
|
fn reserve(self: *Map, need: usize): Status {
|
||||||
if !valid(self.*) { ret .Err(make(.InvalidArgument)); }
|
ret status_from_code(vm_map_reserve(self as *any, need));
|
||||||
if need <= self.states.cap { ret .Ok; }
|
|
||||||
|
|
||||||
var new_cap = grow_capacity(self.states.cap, 8, need);
|
|
||||||
|
|
||||||
var fresh = self.*;
|
|
||||||
fresh.keys = Vec.blank(self.key_size, self.keys.elem_align);
|
|
||||||
fresh.vals = Vec.blank(self.val_size, self.vals.elem_align);
|
|
||||||
fresh.states = Vec.blank(1, 1);
|
|
||||||
fresh.size = 0;
|
|
||||||
|
|
||||||
match Vec.reserve(&fresh.keys, new_cap) {
|
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => { drop(&fresh); ret .Err(error); },
|
|
||||||
}
|
|
||||||
match Vec.reserve(&fresh.vals, new_cap) {
|
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => { drop(&fresh); ret .Err(error); },
|
|
||||||
}
|
|
||||||
match Vec.reserve(&fresh.states, new_cap) {
|
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => { drop(&fresh); ret .Err(error); },
|
|
||||||
}
|
|
||||||
fresh.keys.len = new_cap;
|
|
||||||
fresh.vals.len = new_cap;
|
|
||||||
fresh.states.len = new_cap;
|
|
||||||
|
|
||||||
var i: usize = 0;
|
|
||||||
var empty: SlotState = .Empty;
|
|
||||||
var occupied: SlotState = .Occupied;
|
|
||||||
while i < new_cap {
|
|
||||||
fresh.states.data[i] = state_code(empty);
|
|
||||||
i = i + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
while i < self.states.len {
|
|
||||||
if self.states.data[i] == state_code(occupied) {
|
|
||||||
place(&fresh, self.keys.data + i * self.key_size,
|
|
||||||
self.vals.data + i * self.val_size);
|
|
||||||
}
|
|
||||||
i = i + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var old_keys = self.keys;
|
|
||||||
var old_vals = self.vals;
|
|
||||||
var old_states = self.states;
|
|
||||||
self.* = fresh;
|
|
||||||
Vec.drop(&old_keys);
|
|
||||||
Vec.drop(&old_vals);
|
|
||||||
Vec.drop(&old_states);
|
|
||||||
ret .Ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 覆盖已存在的键;否则插入并可能扩容。
|
// 覆盖已存在的键;否则插入并可能扩容。
|
||||||
fn put(self: *Map, key: *u8, val: *u8): Status {
|
fn put(self: *Map, key: *u8, val: *u8): Status {
|
||||||
if !valid(self.*) || key == null || val == null {
|
ret status_from_code(vm_map_put(self as *any, key as *any, val as *any));
|
||||||
ret .Err(make(.InvalidArgument));
|
|
||||||
}
|
|
||||||
|
|
||||||
var existing = find_slot(self, key);
|
|
||||||
if existing != ~(0 as usize) {
|
|
||||||
copy((self.vals.data + existing * self.val_size) as *any, val as *any, self.val_size);
|
|
||||||
ret .Ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.states.cap == 0 {
|
|
||||||
match reserve(self, 8) {
|
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => { ret .Err(error); },
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var next_size: usize = 0;
|
|
||||||
match checked_add(self.size, 1) {
|
|
||||||
.Ok(value) => { next_size = value; },
|
|
||||||
.Err(error) => { ret .Err(error); },
|
|
||||||
}
|
|
||||||
var scaled: usize = 0;
|
|
||||||
match checked_mul(next_size, 128) {
|
|
||||||
.Ok(value) => { scaled = value; },
|
|
||||||
.Err(error) => { ret .Err(error); },
|
|
||||||
}
|
|
||||||
if scaled / self.states.cap >= 70 {
|
|
||||||
var need: usize = 0;
|
|
||||||
match checked_mul(self.states.cap, 2) {
|
|
||||||
.Ok(value) => { need = value; },
|
|
||||||
.Err(error) => { ret .Err(error); },
|
|
||||||
}
|
|
||||||
match reserve(self, need) {
|
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => { ret .Err(error); },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
place(self, key, val);
|
|
||||||
ret .Ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 找到键则把值复制到 out 并返回 Found;否则 out 不变并返回 NotFound。
|
// 找到键则把值复制到 out 并返回 Found;否则 out 不变并返回 NotFound。
|
||||||
fn get(self: *Map, key: *u8, out: *u8): LookupResult {
|
fn get(self: *Map, key: *u8, out: *u8): LookupResult {
|
||||||
if !valid(self.*) || key == null || out == null { ret .NotFound; }
|
ret lookup_from_code(vm_map_get(self as *any, key as *any, out as *any));
|
||||||
var i = find_slot(self, key);
|
|
||||||
if i == ~(0 as usize) { ret .NotFound; }
|
|
||||||
copy(out as *any, (self.vals.data + i * self.val_size) as *any, self.val_size);
|
|
||||||
ret .Found;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn del(self: *Map, key: *u8): LookupResult {
|
fn del(self: *Map, key: *u8): LookupResult {
|
||||||
if !valid(self.*) || key == null { ret .NotFound; }
|
ret lookup_from_code(vm_map_del(self as *any, key as *any));
|
||||||
var i = find_slot(self, key);
|
|
||||||
if i == ~(0 as usize) { ret .NotFound; }
|
|
||||||
var deleted_state: SlotState = .Deleted;
|
|
||||||
self.states.data[i] = state_code(deleted_state);
|
|
||||||
self.size = self.size - 1;
|
|
||||||
ret .Found;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清空内容但保留容量。墓碑与负载计数一并复位。
|
// 清空内容但保留容量。墓碑与负载计数一并复位。
|
||||||
fn clear(self: *Map) {
|
fn clear(self: *Map) {
|
||||||
var i: usize = 0;
|
vm_map_clear(self as *any);
|
||||||
var empty: SlotState = .Empty;
|
|
||||||
while i < self.states.len {
|
|
||||||
self.states.data[i] = state_code(empty);
|
|
||||||
i = i + 1;
|
|
||||||
}
|
|
||||||
self.size = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drop(self: *Map) {
|
fn drop(self: *Map) {
|
||||||
Vec.drop(&self.keys);
|
vm_map_drop(self as *any);
|
||||||
Vec.drop(&self.vals);
|
|
||||||
Vec.drop(&self.states);
|
|
||||||
self.size = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take(source: *Map): Map {
|
fn take(source: *Map): Map {
|
||||||
@@ -300,7 +155,6 @@ fn take(source: *Map): Map {
|
|||||||
ret result;
|
ret result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn expect_ok(result: Status) {
|
fn expect_ok(result: Status) {
|
||||||
match result {
|
match result {
|
||||||
@@ -348,13 +202,13 @@ fn deleted(m: *Map, key: *u8): bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn make_int_map(): Map {
|
fn make_int_map(): Map {
|
||||||
ret new(@sizeof(usize), @alignof(usize), @sizeof(i32), @alignof(i32), hash_usize, eq_usize);
|
ret new(@sizeof(usize), @alignof(usize), @sizeof(i32), @alignof(i32), hash_kind_usize());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_put_get_update() {
|
fn test_put_get_update() {
|
||||||
var m = make_int_map();
|
var m = make_int_map();
|
||||||
@assert(size(m) == 0 && capacity(m) == 0, "empty map");
|
@assert(size(m) == 0 && capacity(m) == 0, "empty map");
|
||||||
@assert(valid(m));
|
@assert(valid(&m));
|
||||||
|
|
||||||
var key: usize = 0;
|
var key: usize = 0;
|
||||||
var value: i32 = 0;
|
var value: i32 = 0;
|
||||||
@@ -367,7 +221,7 @@ fn test_put_get_update() {
|
|||||||
key = 7;
|
key = 7;
|
||||||
value = 70;
|
value = 70;
|
||||||
expect_ok(put(&m, &key as *u8, &value as *u8));
|
expect_ok(put(&m, &key as *u8, &value as *u8));
|
||||||
@assert(size(m) == 1 && valid(m));
|
@assert(size(m) == 1 && valid(&m));
|
||||||
|
|
||||||
out = -1;
|
out = -1;
|
||||||
@assert(found(&m, &key as *u8, &out as *u8) && out == 70, "insert then get");
|
@assert(found(&m, &key as *u8, &out as *u8) && out == 70, "insert then get");
|
||||||
@@ -463,7 +317,7 @@ fn test_take_and_self_put() {
|
|||||||
|
|
||||||
fn test_byte_view_keys() {
|
fn test_byte_view_keys() {
|
||||||
var m = new(@sizeof(ByteView), @alignof(ByteView), @sizeof(i32), @alignof(i32),
|
var m = new(@sizeof(ByteView), @alignof(ByteView), @sizeof(i32), @alignof(i32),
|
||||||
hash_view, eq_view);
|
hash_kind_view());
|
||||||
defer { drop(&m); }
|
defer { drop(&m); }
|
||||||
|
|
||||||
var alpha = ByteView.from_slice("alpha");
|
var alpha = ByteView.from_slice("alpha");
|
||||||
|
|||||||
+18
-14
@@ -1,8 +1,23 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// 尺寸计算和字节移动;不分配、不释放内存,不改变任何资源的所有权。
|
// 尺寸计算、字节移动与内存分配统一门面(分配/释放转发自 sys/mem)。
|
||||||
|
// 本模块不改变任何资源的所有权。
|
||||||
|
|
||||||
type .{ .Error, .Status, .make } = @import("error.spl");
|
type .{ .Error, .Status, .make } = @import("error.spl");
|
||||||
|
type SysMem = @import("../sys/mem.spl");
|
||||||
|
|
||||||
|
// 分配/释放/字节操作转发(core 只经本模块使用内存,不直接依赖 sys)。
|
||||||
|
fn alloc(sz: usize): *any { ret SysMem.alloc(sz); }
|
||||||
|
fn free(p: *any) { SysMem.free(p); }
|
||||||
|
fn realloc(p: *any, sz: usize): *any { ret SysMem.realloc(p, sz); }
|
||||||
|
fn copy(dst: *any, src: *any, n: usize): *any { ret SysMem.copy(dst, src, n); }
|
||||||
|
fn cmp(a: *any, b: *any, n: usize): isize { ret SysMem.cmp(a, b, n); }
|
||||||
|
fn set(dst: *any, byte: u8, n: usize): *any { ret SysMem.set(dst, byte, n); }
|
||||||
|
fn zero(dst: *any, n: usize): *any { ret SysMem.zero(dst, n); }
|
||||||
|
|
||||||
|
#extern(vm) fn vm_memcpy(dst: *any, src: *any, n: usize): *any;
|
||||||
|
#extern(vm) fn vm_memmove(dst: *any, src: *any, n: usize): *any;
|
||||||
|
|
||||||
|
|
||||||
type SizeResult = enum {
|
type SizeResult = enum {
|
||||||
Ok: usize,
|
Ok: usize,
|
||||||
@@ -69,19 +84,8 @@ fn move(dst: []u8, src: []u8): Status {
|
|||||||
}
|
}
|
||||||
if dst_addr == src_addr { ret .Ok; }
|
if dst_addr == src_addr { ret .Ok; }
|
||||||
|
|
||||||
if dst_addr < src_addr {
|
// 重叠安全,统一走原生 memmove。
|
||||||
var i: usize = 0;
|
vm_memmove(dst.ptr as *any, src.ptr as *any, count);
|
||||||
while i < count {
|
|
||||||
dst.ptr[i] = src.ptr[i];
|
|
||||||
i = i + 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var i = count;
|
|
||||||
while i > 0 {
|
|
||||||
i = i - 1;
|
|
||||||
dst.ptr[i] = src.ptr[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret .Ok;
|
ret .Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -297,7 +297,6 @@ fn normalize_into(out: *ByteBuffer, path: ByteView, style: PathStyle): Status {
|
|||||||
ret .Ok;
|
ret .Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
fn expect_ok(result: Status) {
|
fn expect_ok(result: Status) {
|
||||||
match result { .Ok => {}, .Err(error) => { @assert(false, "unexpected path error"); }, }
|
match result { .Ok => {}, .Err(error) => { @assert(false, "unexpected path error"); }, }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,297 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
// 采样热点记录器:每个调用点聚合 count 与 self/total 的 steps/ns(self = total 减子区间)。
|
||||||
|
// 严格 LIFO,用 defer 配对:
|
||||||
|
// var tok = Profile.begin(&prof, @file_name(), @line(), @col());
|
||||||
|
// defer { Profile.end(&prof, tok); }
|
||||||
|
|
||||||
|
type .{ .Error } = @import("error.spl");
|
||||||
|
type .{ .Vec } = @import("vec.spl");
|
||||||
|
type .{ .ByteView } = @import("byte_view.spl");
|
||||||
|
type .{ .ByteBuffer } = @import("byte_buffer.spl");
|
||||||
|
type .{ .append_usize } = @import("fmt.spl");
|
||||||
|
type .{ .open_cstr, .write_all } = @import("io.spl");
|
||||||
|
type File = @import("../sys/file.spl");
|
||||||
|
type Time = @import("../sys/time.spl");
|
||||||
|
|
||||||
|
type Site = struct {
|
||||||
|
fname: ByteView,
|
||||||
|
line: usize,
|
||||||
|
col: usize,
|
||||||
|
count: usize,
|
||||||
|
self_steps: usize,
|
||||||
|
total_steps: usize,
|
||||||
|
self_ns: usize,
|
||||||
|
total_ns: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
type SpanRec = struct {
|
||||||
|
site: usize,
|
||||||
|
t0_ns: usize,
|
||||||
|
t0_steps: usize,
|
||||||
|
child_steps: usize,
|
||||||
|
child_ns: usize,
|
||||||
|
active: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Profile = @self;
|
||||||
|
|
||||||
|
sites: Vec, // Site
|
||||||
|
spans: Vec, // SpanRec
|
||||||
|
stack: Vec, // usize(活动 span 的 token,LIFO)
|
||||||
|
has_time: bool,
|
||||||
|
|
||||||
|
fn new(): Profile {
|
||||||
|
var has = false;
|
||||||
|
if Time.available() != 0 { has = true; }
|
||||||
|
ret Profile {
|
||||||
|
.sites = Vec.blank(@sizeof(Site), @alignof(Site)),
|
||||||
|
.spans = Vec.blank(@sizeof(SpanRec), @alignof(SpanRec)),
|
||||||
|
.stack = Vec.blank(@sizeof(usize), @alignof(usize)),
|
||||||
|
.has_time = has,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drop(self: *Profile) {
|
||||||
|
Vec.drop(&self.stack);
|
||||||
|
Vec.drop(&self.spans);
|
||||||
|
Vec.drop(&self.sites);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn site_at(self: *Profile, i: usize): *Site {
|
||||||
|
match Vec.get(&self.sites, i) {
|
||||||
|
.Ok(ptr) => { ret ptr as *Site; },
|
||||||
|
.Err(error) => { ret null; },
|
||||||
|
}
|
||||||
|
ret null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn span_at(self: *Profile, i: usize): *SpanRec {
|
||||||
|
match Vec.get(&self.spans, i) {
|
||||||
|
.Ok(ptr) => { ret ptr as *SpanRec; },
|
||||||
|
.Err(error) => { ret null; },
|
||||||
|
}
|
||||||
|
ret null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usize_at(v: *Vec, i: usize): usize {
|
||||||
|
match Vec.get(v, i) {
|
||||||
|
.Ok(ptr) => { ret (ptr as *usize).*; },
|
||||||
|
.Err(error) => { ret 0; },
|
||||||
|
}
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_site(self: *Profile, fname: []u8, line: usize, col: usize): usize {
|
||||||
|
var fv = ByteView.from_slice(fname);
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < self.sites.len {
|
||||||
|
var s = site_at(self, i);
|
||||||
|
if s != null && s.line == line && s.col == col && ByteView.eq(s.fname, fv) {
|
||||||
|
ret i;
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
var fresh = Site {
|
||||||
|
.fname = fv,
|
||||||
|
.line = line,
|
||||||
|
.col = col,
|
||||||
|
.count = 0,
|
||||||
|
.self_steps = 0,
|
||||||
|
.total_steps = 0,
|
||||||
|
.self_ns = 0,
|
||||||
|
.total_ns = 0,
|
||||||
|
};
|
||||||
|
match Vec.push(&self.sites, &fresh as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ret 0; },
|
||||||
|
}
|
||||||
|
ret self.sites.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 仅计数(轻量点)。
|
||||||
|
fn hit(self: *Profile, fname: []u8, line: usize, col: usize) {
|
||||||
|
var site = find_site(self, fname, line, col);
|
||||||
|
var s = site_at(self, site);
|
||||||
|
if s != null { s.count = s.count + 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn begin(self: *Profile, fname: []u8, line: usize, col: usize): usize {
|
||||||
|
var site = find_site(self, fname, line, col);
|
||||||
|
var s = site_at(self, site);
|
||||||
|
if s != null { s.count = s.count + 1; }
|
||||||
|
|
||||||
|
var t_ns: usize = 0;
|
||||||
|
if self.has_time { t_ns = Time.now_ns(); }
|
||||||
|
var rec = SpanRec {
|
||||||
|
.site = site,
|
||||||
|
.t0_ns = t_ns,
|
||||||
|
.t0_steps = Time.steps(),
|
||||||
|
.child_steps = 0,
|
||||||
|
.child_ns = 0,
|
||||||
|
.active = true,
|
||||||
|
};
|
||||||
|
match Vec.push(&self.spans, &rec as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ret 0; },
|
||||||
|
}
|
||||||
|
var token = self.spans.len - 1;
|
||||||
|
Vec.push(&self.stack, &token as *u8);
|
||||||
|
ret token;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end(self: *Profile, token: usize) {
|
||||||
|
var rec = span_at(self, token);
|
||||||
|
if rec == null || !rec.active { ret; }
|
||||||
|
|
||||||
|
var e_steps = Time.steps() - rec.t0_steps;
|
||||||
|
var e_ns: usize = 0;
|
||||||
|
if self.has_time { e_ns = Time.now_ns() - rec.t0_ns; }
|
||||||
|
|
||||||
|
var s = site_at(self, rec.site);
|
||||||
|
if s != null {
|
||||||
|
s.total_steps = s.total_steps + e_steps;
|
||||||
|
s.self_steps = s.self_steps + (e_steps - rec.child_steps);
|
||||||
|
if self.has_time {
|
||||||
|
s.total_ns = s.total_ns + e_ns;
|
||||||
|
s.self_ns = s.self_ns + (e_ns - rec.child_ns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rec.active = false;
|
||||||
|
|
||||||
|
// 弹栈并把区间计入父 span(LIFO)。
|
||||||
|
var popped: usize = 0;
|
||||||
|
Vec.pop(&self.stack, &popped as *u8);
|
||||||
|
var n = self.stack.len;
|
||||||
|
if n > 0 {
|
||||||
|
var p = span_at(self, usize_at(&self.stack, n - 1));
|
||||||
|
if p != null {
|
||||||
|
p.child_steps = p.child_steps + e_steps;
|
||||||
|
p.child_ns = p.child_ns + e_ns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn out_s(out: *ByteBuffer, s: []u8): bool {
|
||||||
|
match ByteBuffer.append(out, s) { .Ok => { ret true; }, .Err(error) => { ret false; } }
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn out_u(out: *ByteBuffer, v: usize): bool {
|
||||||
|
match append_usize(out, v, 10, false) { .Ok => { ret true; }, .Err(error) => { ret false; } }
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列:count self_steps total_steps self_ns total_ns avg_self location;按 self_steps 降序。
|
||||||
|
fn dump(self: *Profile, out: *ByteBuffer): bool {
|
||||||
|
if !out_s(out, "profile sites=") { ret false; }
|
||||||
|
if !out_u(out, self.sites.len) { ret false; }
|
||||||
|
if !out_s(out, " has_time=") { ret false; }
|
||||||
|
if !out_u(out, if self.has_time { 1 } else { 0 }) { ret false; }
|
||||||
|
if !out_s(out, "\n") { ret false; }
|
||||||
|
if !out_s(out, "count\tself_steps\ttotal_steps\tself_ns\ttotal_ns\tavg_self\tlocation\n") {
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var idx = Vec.blank(@sizeof(usize), @alignof(usize));
|
||||||
|
defer { Vec.drop(&idx); }
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < self.sites.len {
|
||||||
|
Vec.push(&idx, &i as *u8);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 1;
|
||||||
|
while i < idx.len {
|
||||||
|
var j = i;
|
||||||
|
while j > 0 {
|
||||||
|
var a = site_at(self, usize_at(&idx, j - 1));
|
||||||
|
var b = site_at(self, usize_at(&idx, j));
|
||||||
|
var swap = false;
|
||||||
|
if a != null && b != null {
|
||||||
|
if b.self_steps > a.self_steps { swap = true; }
|
||||||
|
}
|
||||||
|
if !swap { break; }
|
||||||
|
var tmp = usize_at(&idx, j - 1);
|
||||||
|
match Vec.get(&idx, j - 1) { .Ok(p) => { (p as *usize).* = usize_at(&idx, j); }, .Err(e) => {} }
|
||||||
|
match Vec.get(&idx, j) { .Ok(p) => { (p as *usize).* = tmp; }, .Err(e) => {} }
|
||||||
|
j = j - 1;
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while i < idx.len {
|
||||||
|
var s = site_at(self, usize_at(&idx, i));
|
||||||
|
if s != null {
|
||||||
|
if !out_u(out, s.count) { ret false; }
|
||||||
|
if !out_s(out, "\t") { ret false; }
|
||||||
|
if !out_u(out, s.self_steps) { ret false; }
|
||||||
|
if !out_s(out, "\t") { ret false; }
|
||||||
|
if !out_u(out, s.total_steps) { ret false; }
|
||||||
|
if !out_s(out, "\t") { ret false; }
|
||||||
|
if !out_u(out, s.self_ns) { ret false; }
|
||||||
|
if !out_s(out, "\t") { ret false; }
|
||||||
|
if !out_u(out, s.total_ns) { ret false; }
|
||||||
|
if !out_s(out, "\t") { ret false; }
|
||||||
|
if s.count != 0 { if !out_u(out, s.self_steps / s.count) { ret false; } }
|
||||||
|
else { if !out_s(out, "0") { ret false; } }
|
||||||
|
if !out_s(out, "\t") { ret false; }
|
||||||
|
if !out_s(out, ByteView.as_slice(s.fname)) { ret false; }
|
||||||
|
if !out_s(out, ":") { ret false; }
|
||||||
|
if !out_u(out, s.line) { ret false; }
|
||||||
|
if !out_s(out, ":") { ret false; }
|
||||||
|
if !out_u(out, s.col) { ret false; }
|
||||||
|
if !out_s(out, "\n") { ret false; }
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save(self: *Profile, path: ByteView): bool {
|
||||||
|
var out = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&out); }
|
||||||
|
if !dump(self, &out) { ret false; }
|
||||||
|
|
||||||
|
var f = open_cstr(path, ByteView.from_slice("wb"));
|
||||||
|
if !File.valid(&f) { ret false; }
|
||||||
|
var w = write_all(&f, ByteBuffer.as_slice(out));
|
||||||
|
var closed = File.close(&f);
|
||||||
|
match w {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ret false; },
|
||||||
|
}
|
||||||
|
if closed != 0 { ret false; }
|
||||||
|
ret true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn spin(n: usize): usize {
|
||||||
|
var i: usize = 0;
|
||||||
|
var s: usize = 0;
|
||||||
|
while i < n { s = s + i; i = i + 1; }
|
||||||
|
ret s;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_profile() {
|
||||||
|
var p = new();
|
||||||
|
defer { drop(&p); }
|
||||||
|
|
||||||
|
var tok = begin(&p, @file_name(), @line(), @col());
|
||||||
|
var v = spin(1000);
|
||||||
|
end(&p, tok);
|
||||||
|
|
||||||
|
@assert(p.sites.len >= 1, "sites");
|
||||||
|
var s = site_at(&p, 0);
|
||||||
|
@assert(s != null && s.count >= 1, "count");
|
||||||
|
@assert(s.total_steps > 0, "steps");
|
||||||
|
@assert(s.self_steps > 0, "self steps");
|
||||||
|
if v == 0 { v + 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_profile();
|
||||||
|
}
|
||||||
@@ -3,7 +3,8 @@
|
|||||||
// 开放寻址集合。键按字节复制到集合自己的存储中;集合只释放自身存储,
|
// 开放寻址集合。键按字节复制到集合自己的存储中;集合只释放自身存储,
|
||||||
// 不释放键值内部引用的资源。哈希与相等回调的约定和 Map 一致。
|
// 不释放键值内部引用的资源。哈希与相等回调的约定和 Map 一致。
|
||||||
type .{ .Status } = @import("error.spl");
|
type .{ .Status } = @import("error.spl");
|
||||||
type .{ .Map, .LookupResult } = @import("map.spl");
|
type .{ .Map, .LookupResult, .hash_kind_usize, .hash_kind_view, .hash_kind_constant } =
|
||||||
|
@import("map.spl");
|
||||||
type .{ .ByteView } = @import("byte_view.spl");
|
type .{ .ByteView } = @import("byte_view.spl");
|
||||||
type .{ .hash_usize, .eq_usize, .hash_view, .eq_view } = @import("hash.spl");
|
type .{ .hash_usize, .eq_usize, .hash_view, .eq_view } = @import("hash.spl");
|
||||||
|
|
||||||
@@ -11,15 +12,14 @@ type Set = @self;
|
|||||||
|
|
||||||
map: Map,
|
map: Map,
|
||||||
|
|
||||||
fn new(key_size: usize, key_align: usize,
|
fn new(key_size: usize, key_align: usize, hash_kind: usize): Set {
|
||||||
hash_fn: fn(*u8): usize, eq_fn: fn(*u8, *u8): bool): Set {
|
|
||||||
ret Set {
|
ret Set {
|
||||||
.map = Map.new(key_size, key_align, 1, 1, hash_fn, eq_fn),
|
.map = Map.new(key_size, key_align, 1, 1, hash_kind),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn valid(self: Set): bool {
|
fn valid(self: Set): bool {
|
||||||
ret Map.valid(self.map) && self.map.val_size == 1;
|
ret Map.valid(&self.map) && self.map.val_size == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(self: Set): usize {
|
fn size(self: Set): usize {
|
||||||
@@ -74,7 +74,6 @@ fn drop(self: *Set) {
|
|||||||
Map.drop(&self.map);
|
Map.drop(&self.map);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn expect_ok(result: Status) {
|
fn expect_ok(result: Status) {
|
||||||
match result {
|
match result {
|
||||||
@@ -104,7 +103,7 @@ fn constant_hash(key: *u8): usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn test_insert_find_remove() {
|
fn test_insert_find_remove() {
|
||||||
var set = new(@sizeof(usize), @alignof(usize), hash_usize, eq_usize);
|
var set = new(@sizeof(usize), @alignof(usize), hash_kind_usize());
|
||||||
defer { drop(&set); }
|
defer { drop(&set); }
|
||||||
@assert(valid(set));
|
@assert(valid(set));
|
||||||
@assert(size(set) == 0 && capacity(set) == 0);
|
@assert(size(set) == 0 && capacity(set) == 0);
|
||||||
@@ -169,7 +168,7 @@ fn test_insert_find_remove() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn test_collisions_and_take() {
|
fn test_collisions_and_take() {
|
||||||
var set = new(@sizeof(usize), @alignof(usize), constant_hash, eq_usize);
|
var set = new(@sizeof(usize), @alignof(usize), hash_kind_constant());
|
||||||
defer { drop(&set); }
|
defer { drop(&set); }
|
||||||
|
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
@@ -213,7 +212,7 @@ fn test_collisions_and_take() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn test_byte_view_keys() {
|
fn test_byte_view_keys() {
|
||||||
var set = new(@sizeof(ByteView), @alignof(ByteView), hash_view, eq_view);
|
var set = new(@sizeof(ByteView), @alignof(ByteView), hash_kind_view());
|
||||||
defer { drop(&set); }
|
defer { drop(&set); }
|
||||||
|
|
||||||
var alpha = ByteView.from_slice("alpha");
|
var alpha = ByteView.from_slice("alpha");
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
type .{ .Error, .Status, .make } = @import("error.spl");
|
type .{ .Error, .Status, .make } = @import("error.spl");
|
||||||
type .{ .ByteView, .FindResult } = @import("byte_view.spl");
|
type .{ .ByteView, .FindResult } = @import("byte_view.spl");
|
||||||
type .{ .Vec } = @import("vec.spl");
|
type .{ .Vec } = @import("vec.spl");
|
||||||
type .{ .Map } = @import("map.spl");
|
type .{ .Map, .hash_kind_view } = @import("map.spl");
|
||||||
type .{ .hash_view, .eq_view } = @import("hash.spl");
|
type .{ .hash_view, .eq_view } = @import("hash.spl");
|
||||||
|
|
||||||
type Position = struct {
|
type Position = struct {
|
||||||
@@ -123,7 +123,6 @@ fn drop(self: *Source) {
|
|||||||
Vec.drop(&self.line_starts);
|
Vec.drop(&self.line_starts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 多文件集合 ----
|
|
||||||
|
|
||||||
type Sources = struct {
|
type Sources = struct {
|
||||||
items: Vec, // Source
|
items: Vec, // Source
|
||||||
@@ -134,7 +133,7 @@ fn set_new(): Sources {
|
|||||||
ret Sources {
|
ret Sources {
|
||||||
.items = Vec.blank(@sizeof(Source), 8),
|
.items = Vec.blank(@sizeof(Source), 8),
|
||||||
.by_name = Map.blank(@sizeof(ByteView), @alignof(ByteView), @sizeof(usize),
|
.by_name = Map.blank(@sizeof(ByteView), @alignof(ByteView), @sizeof(usize),
|
||||||
@alignof(usize), hash_view, eq_view),
|
@alignof(usize), hash_kind_view()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +190,6 @@ fn set_drop(set: *Sources) {
|
|||||||
Map.drop(&set.by_name);
|
Map.drop(&set.by_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn view(text: []u8): ByteView {
|
fn view(text: []u8): ByteView {
|
||||||
ret ByteView.from_slice(text);
|
ret ByteView.from_slice(text);
|
||||||
|
|||||||
@@ -122,7 +122,6 @@ fn drop(self: *Str) {
|
|||||||
ByteBuffer.drop(&self.storage);
|
ByteBuffer.drop(&self.storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn expect_ok(result: Status) {
|
fn expect_ok(result: Status) {
|
||||||
match result {
|
match result {
|
||||||
|
|||||||
@@ -181,7 +181,6 @@ fn utf16_to_byte(data: *u8, len: usize, utf16_off: usize): usize {
|
|||||||
ret i;
|
ret i;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn expect_ok(result: Status) {
|
fn expect_ok(result: Status) {
|
||||||
match result {
|
match result {
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ fn addr(value: *any): Value {
|
|||||||
ret .Ptr(value);
|
ret .Ptr(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn test_int_and_uint() {
|
fn test_int_and_uint() {
|
||||||
match int(-5) {
|
match int(-5) {
|
||||||
|
|||||||
+66
-167
@@ -7,6 +7,18 @@ type .{ .Error, .Kind, .Status } = @import("error.spl");
|
|||||||
type .{ .SizeResult, .checked_add, .checked_mul, .grow_capacity, .move } = @import("memory.spl");
|
type .{ .SizeResult, .checked_add, .checked_mul, .grow_capacity, .move } = @import("memory.spl");
|
||||||
type .{ .alloc, .copy, .free } = @import("../sys/mem.spl");
|
type .{ .alloc, .copy, .free } = @import("../sys/mem.spl");
|
||||||
|
|
||||||
|
#extern(vm) fn vm_vec_valid(v: *any): i32;
|
||||||
|
#extern(vm) fn vm_vec_push_raw(v: *any, elem: *any): i32;
|
||||||
|
#extern(vm) fn vm_vec_push(v: *any, elem: *any): i32;
|
||||||
|
#extern(vm) fn vm_vec_get(v: *any, index: usize, out: *any): i32;
|
||||||
|
#extern(vm) fn vm_vec_allocate(out: *any, capacity: usize, elem_size: usize, elem_align: usize): i32;
|
||||||
|
#extern(vm) fn vm_vec_reserve_raw(v: *any, need: usize): i32;
|
||||||
|
#extern(vm) fn vm_vec_append(v: *any, elems: *any, count: usize): i32;
|
||||||
|
#extern(vm) fn vm_vec_set(v: *any, index: usize, elem: *any): i32;
|
||||||
|
#extern(vm) fn vm_vec_pop(v: *any, out: *any): i32;
|
||||||
|
#extern(vm) fn vm_vec_clone(out: *any, src: *any): i32;
|
||||||
|
#extern(vm) fn vm_vec_drop(v: *any): void;
|
||||||
|
|
||||||
type Vec = @self;
|
type Vec = @self;
|
||||||
|
|
||||||
raw: *u8,
|
raw: *u8,
|
||||||
@@ -49,177 +61,91 @@ fn new(elem_size: usize, elem_align: usize): VecResult {
|
|||||||
ret .Ok(blank(elem_size, elem_align));
|
ret .Ok(blank(elem_size, elem_align));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn valid(self: Vec): bool {
|
// 按指针接收,避免每次调用复制整个 Vec;内联对齐检查,省一次函数调用。
|
||||||
if self.elem_size == 0 || !valid_alignment(self.elem_align) { ret false; }
|
fn valid(self: *Vec): bool {
|
||||||
if self.len > self.cap { ret false; }
|
ret vm_vec_valid(self as *any) != 0;
|
||||||
if self.cap == 0 {
|
|
||||||
ret self.raw == null && self.data == null && self.len == 0;
|
|
||||||
}
|
|
||||||
if self.raw == null || self.data == null { ret false; }
|
|
||||||
ret (self.data as usize & (self.elem_align - 1)) == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn status_error(kind: Kind): Status {
|
fn status_error(kind: Kind): Status {
|
||||||
ret .Err(Error.make(kind));
|
ret .Err(Error.make(kind));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// native 错误码 -> Error.Kind(0 ok / 1 InvalidArgument / 2 OutOfRange / 3 Overflow / 4 InvalidAlignment)
|
||||||
|
fn kind_from_code(code: i32): Kind {
|
||||||
|
if code == 2 { ret .OutOfRange; }
|
||||||
|
if code == 3 { ret .Overflow; }
|
||||||
|
if code == 4 { ret .InvalidAlignment; }
|
||||||
|
ret .InvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn status_from_code(code: i32): Status {
|
||||||
|
if code == 0 { ret .Ok; }
|
||||||
|
ret status_error(kind_from_code(code));
|
||||||
|
}
|
||||||
|
|
||||||
// 计算元素区字节数。错误时不修改任何 Vec。
|
// 计算元素区字节数。错误时不修改任何 Vec。
|
||||||
fn byte_count(count: usize, elem_size: usize): SizeResult {
|
fn byte_count(count: usize, elem_size: usize): SizeResult {
|
||||||
ret checked_mul(count, elem_size);
|
ret checked_mul(count, elem_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn allocate(capacity: usize, elem_size: usize, elem_align: usize): VecResult {
|
fn allocate(capacity: usize, elem_size: usize, elem_align: usize): VecResult {
|
||||||
if elem_size == 0 { ret .Err(Error.make(.InvalidArgument)); }
|
var v: Vec = undefined;
|
||||||
if !valid_alignment(elem_align) {
|
var rc = vm_vec_allocate(&v as *any, capacity, elem_size, elem_align);
|
||||||
ret .Err(Error.make(.InvalidAlignment));
|
if rc == 0 { ret .Ok(v); }
|
||||||
}
|
ret .Err(Error.make(kind_from_code(rc)));
|
||||||
if capacity == 0 { ret .Ok(blank(elem_size, elem_align)); }
|
|
||||||
|
|
||||||
var bytes: usize = 0;
|
|
||||||
match byte_count(capacity, elem_size) {
|
|
||||||
.Ok(value) => { bytes = value; },
|
|
||||||
.Err(error) => { ret .Err(error); },
|
|
||||||
}
|
|
||||||
|
|
||||||
var total: usize = 0;
|
|
||||||
match checked_add(bytes, elem_align - 1) {
|
|
||||||
.Ok(value) => { total = value; },
|
|
||||||
.Err(error) => { ret .Err(error); },
|
|
||||||
}
|
|
||||||
|
|
||||||
var raw = alloc(total) as *u8;
|
|
||||||
if raw == null {
|
|
||||||
ret .Err(Error.make(.Overflow));
|
|
||||||
}
|
|
||||||
var address = raw as usize;
|
|
||||||
var mask = elem_align - 1;
|
|
||||||
if address > (~(0 as usize)) - mask {
|
|
||||||
free(raw as *any);
|
|
||||||
ret .Err(Error.make(.Overflow));
|
|
||||||
}
|
|
||||||
var aligned = (address + mask) & ~mask;
|
|
||||||
ret .Ok(Vec {
|
|
||||||
.raw = raw,
|
|
||||||
.data = aligned as *u8,
|
|
||||||
.len = 0,
|
|
||||||
.cap = capacity,
|
|
||||||
.elem_size = elem_size,
|
|
||||||
.elem_align = elem_align,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保证至少可容纳 need 个元素。所有检查在原对象改变前完成。
|
// 与 reserve 相同,但假定调用方已校验 self(内部热路径用,省去重复 valid)。
|
||||||
|
fn reserve_raw(self: *Vec, need: usize): Status {
|
||||||
|
ret status_from_code(vm_vec_reserve_raw(self as *any, need));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保证至少可容纳 need 个元素(外部入口,含校验)。
|
||||||
fn reserve(self: *Vec, need: usize): Status {
|
fn reserve(self: *Vec, need: usize): Status {
|
||||||
if !valid(self.*) { ret status_error(.InvalidArgument); }
|
if !valid(self) { ret status_error(.InvalidArgument); }
|
||||||
if need <= self.cap { ret .Ok; }
|
ret reserve_raw(self, need);
|
||||||
|
}
|
||||||
|
|
||||||
var new_cap = grow_capacity(self.cap, 8, need);
|
// 预分配恰好 capacity 个元素(等价“with_capacity”)。
|
||||||
|
fn with_capacity(elem_size: usize, elem_align: usize, capacity: usize): VecResult {
|
||||||
var fresh: Vec = undefined;
|
ret allocate(capacity, elem_size, elem_align);
|
||||||
match allocate(new_cap, self.elem_size, self.elem_align) {
|
|
||||||
.Ok(value) => { fresh = value; },
|
|
||||||
.Err(error) => { ret .Err(error); },
|
|
||||||
}
|
|
||||||
|
|
||||||
var used_bytes: usize = 0;
|
|
||||||
match byte_count(self.len, self.elem_size) {
|
|
||||||
.Ok(value) => { used_bytes = value; },
|
|
||||||
.Err(error) => {
|
|
||||||
drop(&fresh);
|
|
||||||
ret .Err(error);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if used_bytes != 0 {
|
|
||||||
copy(fresh.data as *any, self.data as *any, used_bytes);
|
|
||||||
}
|
|
||||||
fresh.len = self.len;
|
|
||||||
|
|
||||||
var old_raw = self.raw;
|
|
||||||
self.* = fresh;
|
|
||||||
free(old_raw as *any);
|
|
||||||
ret .Ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(self: *Vec, index: usize): PtrResult {
|
fn get(self: *Vec, index: usize): PtrResult {
|
||||||
if !valid(self.*) { ret .Err(Error.make(.InvalidArgument)); }
|
var out: *u8 = null;
|
||||||
if index >= self.len { ret .Err(Error.make(.OutOfRange)); }
|
var rc = vm_vec_get(self as *any, index, &out as *any);
|
||||||
ret .Ok(self.data + index * self.elem_size);
|
if rc == 0 { ret .Ok(out); }
|
||||||
|
if rc == 2 { ret .Err(Error.make(.OutOfRange)); }
|
||||||
|
ret .Err(Error.make(.InvalidArgument));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(self: *Vec, index: usize, elem: *u8): Status {
|
fn set(self: *Vec, index: usize, elem: *u8): Status {
|
||||||
if !valid(self.*) || elem == null { ret status_error(.InvalidArgument); }
|
ret status_from_code(vm_vec_set(self as *any, index, elem as *any));
|
||||||
if index >= self.len { ret status_error(.OutOfRange); }
|
|
||||||
var dst = self.data + index * self.elem_size;
|
|
||||||
ret move(dst[0..self.elem_size], elem[0..self.elem_size]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 追加 count 个连续元素。elems 可以指向 self 当前已初始化的元素区。
|
// 追加 count 个连续元素。elems 可以指向 self 当前已初始化的元素区。
|
||||||
fn append(self: *Vec, elems: *u8, count: usize): Status {
|
fn append(self: *Vec, elems: *u8, count: usize): Status {
|
||||||
if !valid(self.*) { ret status_error(.InvalidArgument); }
|
ret status_from_code(vm_vec_append(self as *any, elems as *any, count));
|
||||||
if count == 0 { ret .Ok; }
|
|
||||||
if elems == null { ret status_error(.InvalidArgument); }
|
|
||||||
|
|
||||||
var copy_bytes: usize = 0;
|
|
||||||
match byte_count(count, self.elem_size) {
|
|
||||||
.Ok(value) => { copy_bytes = value; },
|
|
||||||
.Err(error) => { ret .Err(error); },
|
|
||||||
}
|
|
||||||
var new_len: usize = 0;
|
|
||||||
match checked_add(self.len, count) {
|
|
||||||
.Ok(value) => { new_len = value; },
|
|
||||||
.Err(error) => { ret .Err(error); },
|
|
||||||
}
|
|
||||||
|
|
||||||
var used_bytes: usize = 0;
|
|
||||||
match byte_count(self.len, self.elem_size) {
|
|
||||||
.Ok(value) => { used_bytes = value; },
|
|
||||||
.Err(error) => { ret .Err(error); },
|
|
||||||
}
|
|
||||||
|
|
||||||
var source_inside = false;
|
|
||||||
var source_offset: usize = 0;
|
|
||||||
if used_bytes != 0 {
|
|
||||||
var base = self.data as usize;
|
|
||||||
var source = elems as usize;
|
|
||||||
if source >= base && source - base < used_bytes {
|
|
||||||
source_offset = source - base;
|
|
||||||
if copy_bytes > used_bytes - source_offset {
|
|
||||||
ret status_error(.OutOfRange);
|
|
||||||
}
|
|
||||||
source_inside = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match reserve(self, new_len) {
|
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => { ret .Err(error); },
|
|
||||||
}
|
|
||||||
if source_inside { elems = self.data + source_offset; }
|
|
||||||
|
|
||||||
var dst = self.data + used_bytes;
|
|
||||||
match move(dst[0..copy_bytes], elems[0..copy_bytes]) {
|
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => { ret .Err(error); },
|
|
||||||
}
|
|
||||||
self.len = new_len;
|
|
||||||
ret .Ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push(self: *Vec, elem: *u8): Status {
|
fn push(self: *Vec, elem: *u8): Status {
|
||||||
ret append(self, elem, 1);
|
ret status_from_code(vm_vec_push(self as *any, elem as *any));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 热路径:假定 self 有效且已有容量时直接写入;容量不足才回退到 append。
|
||||||
|
fn push_raw(self: *Vec, elem: *u8): bool {
|
||||||
|
if elem == null { ret false; }
|
||||||
|
if vm_vec_push_raw(self as *any, elem as *any) != 0 { ret true; }
|
||||||
|
match append(self, elem, 1) {
|
||||||
|
.Ok => { ret true; },
|
||||||
|
.Err(error) => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将末元素复制到 out,然后缩短数组。out 可以与元素区重叠。
|
// 将末元素复制到 out,然后缩短数组。out 可以与元素区重叠。
|
||||||
fn pop(self: *Vec, out: *u8): Status {
|
fn pop(self: *Vec, out: *u8): Status {
|
||||||
if !valid(self.*) || out == null { ret status_error(.InvalidArgument); }
|
ret status_from_code(vm_vec_pop(self as *any, out as *any));
|
||||||
if self.len == 0 { ret status_error(.OutOfRange); }
|
|
||||||
var source = self.data + (self.len - 1) * self.elem_size;
|
|
||||||
match move(out[0..self.elem_size], source[0..self.elem_size]) {
|
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => { ret .Err(error); },
|
|
||||||
}
|
|
||||||
self.len = self.len - 1;
|
|
||||||
ret .Ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(self: *Vec) {
|
fn clear(self: *Vec) {
|
||||||
@@ -229,31 +155,7 @@ fn clear(self: *Vec) {
|
|||||||
// 成功时先释放 out 原有存储再提交副本;失败保持 out 不变。
|
// 成功时先释放 out 原有存储再提交副本;失败保持 out 不变。
|
||||||
// out 与 src 指向同一个 Vec 也允许。
|
// out 与 src 指向同一个 Vec 也允许。
|
||||||
fn clone(out: *Vec, src: Vec): Status {
|
fn clone(out: *Vec, src: Vec): Status {
|
||||||
if !valid(out.*) || !valid(src) { ret status_error(.InvalidArgument); }
|
ret status_from_code(vm_vec_clone(out as *any, &src as *any));
|
||||||
if out.elem_size != src.elem_size || out.elem_align != src.elem_align {
|
|
||||||
ret status_error(.InvalidArgument);
|
|
||||||
}
|
|
||||||
|
|
||||||
var fresh = blank(src.elem_size, src.elem_align);
|
|
||||||
match reserve(&fresh, src.len) {
|
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => { ret .Err(error); },
|
|
||||||
}
|
|
||||||
var bytes: usize = 0;
|
|
||||||
match byte_count(src.len, src.elem_size) {
|
|
||||||
.Ok(value) => { bytes = value; },
|
|
||||||
.Err(error) => {
|
|
||||||
drop(&fresh);
|
|
||||||
ret .Err(error);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if bytes != 0 {
|
|
||||||
copy(fresh.data as *any, src.data as *any, bytes);
|
|
||||||
}
|
|
||||||
fresh.len = src.len;
|
|
||||||
drop(out);
|
|
||||||
out.* = fresh;
|
|
||||||
ret .Ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显式转移所有权,返回值必须由接收方最终 drop。
|
// 显式转移所有权,返回值必须由接收方最终 drop。
|
||||||
@@ -264,10 +166,7 @@ fn take(source: *Vec): Vec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn drop(self: *Vec) {
|
fn drop(self: *Vec) {
|
||||||
var elem_size = self.elem_size;
|
vm_vec_drop(self as *any);
|
||||||
var elem_align = self.elem_align;
|
|
||||||
free(self.raw as *any);
|
|
||||||
self.* = blank(elem_size, elem_align);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expect_ok(result: Status) {
|
fn expect_ok(result: Status) {
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
fn main(): i32 {
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// Expect retcode = 1
|
||||||
|
|
||||||
|
fn main(): i32 {
|
||||||
|
var x: i32 = 1;
|
||||||
|
ret x;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
fn main(): i32 {
|
||||||
|
var x: i32 = undefined;
|
||||||
|
x = 0;
|
||||||
|
ret x;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// Expect retcode = 42
|
||||||
|
|
||||||
|
fn main(): i32 {
|
||||||
|
var x: i32 = undefined;
|
||||||
|
x = 1;
|
||||||
|
var y = 6 + x;
|
||||||
|
x -= y;
|
||||||
|
y *= x * -1;
|
||||||
|
ret y;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// Expect retcode = 6
|
||||||
|
|
||||||
|
fn add(a: i32, b: i32): i32 {
|
||||||
|
ret a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main(): i32 {
|
||||||
|
var a = add(1, 2);
|
||||||
|
ret add(a, 3);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// Expect retcode = 10
|
||||||
|
|
||||||
|
fn main(): i32 {
|
||||||
|
var result: i32 = 0;
|
||||||
|
var x: i32 = 0;
|
||||||
|
if x != 0 {
|
||||||
|
result += 1;
|
||||||
|
} else {
|
||||||
|
result += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = 2;
|
||||||
|
if x == 1 {
|
||||||
|
result += 4;
|
||||||
|
} else if x == 2 {
|
||||||
|
result += 8;
|
||||||
|
} else {
|
||||||
|
result += 16;
|
||||||
|
}
|
||||||
|
ret result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// Expect retcode = 10
|
||||||
|
|
||||||
|
fn main(): i32 {
|
||||||
|
var i: i32 = 0;
|
||||||
|
while i < 10 {
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
ret i;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// Expect retcode = 20
|
||||||
|
|
||||||
|
fn main(): i32 {
|
||||||
|
var res: i32 = 0;
|
||||||
|
var array: [5]i32 = .[ 1, 2, 3, 4, 5 ];
|
||||||
|
var slice: []i32 = array[1..3];
|
||||||
|
|
||||||
|
var i: u64 = undefined;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while i < array.len {
|
||||||
|
res += array[i];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while i < slice.len {
|
||||||
|
res += slice[i];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret res;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// Expect retcode = 4
|
||||||
|
|
||||||
|
fn main(): i32 {
|
||||||
|
var str = "hello world";
|
||||||
|
ret str[2] as i32 - str[0] as i32;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
// Expect stdout = `hello vm_printf\n1 0 -1 65536\n`
|
||||||
|
#extern(vm) fn vm_printf(fmt: []u8, ...);
|
||||||
|
|
||||||
|
fn main(): () {
|
||||||
|
vm_printf("hello vm_printf\n");
|
||||||
|
vm_printf("%d %d %d %d\n", 1, 0, -1, 65536);
|
||||||
|
vm_printf("%s %d %s\n", "hello world", 666, "hello spl");
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
fn main(): i32 {
|
||||||
|
@assert(0 == 0);
|
||||||
|
@assert(0 != 1);
|
||||||
|
@assert(1 != 0);
|
||||||
|
@assert(1 == 1);
|
||||||
|
@assert(true);
|
||||||
|
@assert(!false);
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
type Vector2i = struct {
|
||||||
|
x: isize,
|
||||||
|
y: isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
var v = Vector2i {};
|
||||||
|
v.x = -2;
|
||||||
|
v.y = 3;
|
||||||
|
@assert(v.x * v.y == -6);
|
||||||
|
@assert((v.x * v.y != -6) == false);
|
||||||
|
@assert((v.x * v.y * -1) as i32 == 6);
|
||||||
|
|
||||||
|
var a: Vector2i = .{ .y = 2 };
|
||||||
|
@assert(a.x == 0);
|
||||||
|
@assert(a.y == 2);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
// non-optimized
|
||||||
|
|
||||||
|
type Rectangle = struct {
|
||||||
|
x: isize,
|
||||||
|
y: isize
|
||||||
|
}
|
||||||
|
|
||||||
|
type Shape = enum {
|
||||||
|
square: struct { x: isize },
|
||||||
|
rectangle: Rectangle,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
var s: Shape = .{ .square = .{ .x = 6 } };
|
||||||
|
@assert(s.square.x == 6);
|
||||||
|
// non-optimized
|
||||||
|
@assert(s.rectangle.x == 6);
|
||||||
|
// non-optimized
|
||||||
|
@assert(s.rectangle.y == 0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
type Rectangle = struct {
|
||||||
|
fn area(self: *@self): isize {
|
||||||
|
ret self.x * self.y;
|
||||||
|
}
|
||||||
|
x: isize,
|
||||||
|
y: isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
var r = Rectangle { .x = 2, .y = 3 };
|
||||||
|
@assert(r.area(&r) == 6, "method call");
|
||||||
|
@assert(Rectangle.area(&r) == 6, "type call");
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
type Outer = struct {
|
||||||
|
inner: Inner,
|
||||||
|
fn total(self: *@self): i32 {
|
||||||
|
ret self.compute(self) + constant_value();
|
||||||
|
}
|
||||||
|
fn compute(self: *@self): i32 {
|
||||||
|
ret self.inner.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Inner = struct {
|
||||||
|
value: i32,
|
||||||
|
fn doubled(self: *@self): i32 {
|
||||||
|
ret self.value * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
var i: Inner = .{ .value = 21 };
|
||||||
|
@assert(i.doubled(&i) == 42);
|
||||||
|
|
||||||
|
var o: Outer = .{ .inner = i };
|
||||||
|
@assert(o.total(&o) == 21 + 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn constant_value(): i32 {
|
||||||
|
ret 7;
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
var res = add((1, 2), (3, 4));
|
||||||
|
@assert(res[0] == 4);
|
||||||
|
@assert(res[1] == 6);
|
||||||
|
@assert(res[1] + res[0] == 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add(a: (i32, i32), b: (i32, i32)): (i32, i32) {
|
||||||
|
ret (a[0] + b[0], a[1] + b[1]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
var count: i32 = 0;
|
||||||
|
$outter while count < 10 {
|
||||||
|
$inner loop {
|
||||||
|
count = count + 1;
|
||||||
|
if count == 3 {
|
||||||
|
continue $outter;
|
||||||
|
}
|
||||||
|
if count == 7 {
|
||||||
|
break $outter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@assert(count == 7);
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
while count < 10 {
|
||||||
|
count = count + 1;
|
||||||
|
if count == 5 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@assert(count == 5);
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
var sum: i32 = 0;
|
||||||
|
while count < 5 {
|
||||||
|
count = count + 1;
|
||||||
|
if count == 3 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sum = sum + count;
|
||||||
|
}
|
||||||
|
@assert(sum == 12);
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
var outer_hits: i32 = 0;
|
||||||
|
$a loop {
|
||||||
|
$b loop {
|
||||||
|
count = count + 1;
|
||||||
|
if count == 2 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
outer_hits = outer_hits + 1;
|
||||||
|
if outer_hits == 3 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@assert(outer_hits == 3);
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
var g: i32 = 0;
|
||||||
|
|
||||||
|
fn f1() {
|
||||||
|
defer { g = g + 1; }
|
||||||
|
defer { g = g * 10; }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f2(): i32 {
|
||||||
|
var x: i32 = 42;
|
||||||
|
defer { x = 100; }
|
||||||
|
ret x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f3(): i32 {
|
||||||
|
defer { g = 999; }
|
||||||
|
ret 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f4(): i32 {
|
||||||
|
var acc: i32 = 0;
|
||||||
|
acc = acc + 1;
|
||||||
|
defer { acc = acc + 100; }
|
||||||
|
acc = acc + 10;
|
||||||
|
ret acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// 1. LIFO:先注册的 defer 最后执行
|
||||||
|
g = 0;
|
||||||
|
f1();
|
||||||
|
@assert(g == 1); // 0 * 10 = 0, 0 + 1 = 1
|
||||||
|
|
||||||
|
// 2. ret 先求值再 defer,defer 改不了返回值
|
||||||
|
g = 0;
|
||||||
|
var r2: i32 = f2();
|
||||||
|
@assert(r2 == 42);
|
||||||
|
@assert(g == 0);
|
||||||
|
|
||||||
|
// 3. defer 在 ret 前执行
|
||||||
|
g = 0;
|
||||||
|
var r3: i32 = f3();
|
||||||
|
@assert(r3 == 7);
|
||||||
|
@assert(g == 999);
|
||||||
|
|
||||||
|
// 4. defer 里的代码对 ret 表达式的结果无影响
|
||||||
|
var r4: i32 = f4();
|
||||||
|
@assert(r4 == 11); // acc = 1 + 10 = 11
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
var str: []u8 = undefined;
|
||||||
|
str = "hello world";
|
||||||
|
var p: *any = str.ptr;
|
||||||
|
@assert((p as *u8)[1] == 'e');
|
||||||
|
p = null;
|
||||||
|
@assert(p == null);
|
||||||
|
p = str.ptr as *u16 + 1;
|
||||||
|
@assert((p as *u8)[0] == 'l');
|
||||||
|
@assert((p as *u8).* == 'l');
|
||||||
|
@assert((&(p as *u8)[1])[1] == 'o');
|
||||||
|
@assert((&(p as *u8).*)[2] == 'o');
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
type Rectangle = @import("014_method.spl").Rectangle;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
var r = Rectangle { .x = 2, .y = 3 };
|
||||||
|
@assert(r.area(&r) == 6);
|
||||||
|
@assert(Rectangle.area(&r) == 6);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
type Node = struct {
|
||||||
|
val: i32,
|
||||||
|
next: *Node,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn sum(n: *Node): i32 {
|
||||||
|
if n == null {
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
ret n.val + sum(n.next);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
var a = Node { .val = 1, .next = null };
|
||||||
|
var b = Node { .val = 2, .next = &a };
|
||||||
|
var c = Node { .val = 3, .next = &b };
|
||||||
|
@assert(sum(&c) == 6);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
type A = struct {
|
||||||
|
tag: i32,
|
||||||
|
b: *B,
|
||||||
|
};
|
||||||
|
|
||||||
|
type B = struct {
|
||||||
|
tag: i32,
|
||||||
|
a: *A,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
var a = A { .tag = 1, .b = null };
|
||||||
|
var b = B { .tag = 2, .a = &a };
|
||||||
|
a.b = &b;
|
||||||
|
@assert(a.b.tag == 2);
|
||||||
|
@assert(b.a.tag == 1);
|
||||||
|
@assert(a.b.a.tag == 1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
type Rectangle = @import("014_method.spl").Rectangle;
|
||||||
|
|
||||||
|
fn make_rect(): Rectangle {
|
||||||
|
ret Rectangle { .x = 5, .y = 7 };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
var r = make_rect();
|
||||||
|
@assert(r.area(&r) == 35);
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// Expect retcode = 0
|
||||||
|
|
||||||
|
type Option = enum {
|
||||||
|
None,
|
||||||
|
Some: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_unit_variant() {
|
||||||
|
var x: Option = .{ .None };
|
||||||
|
var .None = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_payload_variant() {
|
||||||
|
var x: Option = .{ .Some = 42 };
|
||||||
|
var .Some(n) = x;
|
||||||
|
@assert(n == 42, "some payload");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unwrap_some(o: Option): i32 {
|
||||||
|
var .Some(n) = o;
|
||||||
|
ret n;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_unit_variant();
|
||||||
|
test_payload_variant();
|
||||||
|
|
||||||
|
var x: Option = .{ .Some = 7 };
|
||||||
|
@assert(unwrap_some(x) == 7, "cross-fn unwrap");
|
||||||
|
}
|
||||||
Loaded 100 of 146 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user