protoverse

A metaverse protocol
git clone git://jb55.com/protoverse
Log | Files | Refs | README | LICENSE

commit b8400e30eb3c09040cd9c8b41d73389f3998c41c
parent 9fab8459e141b2e2b037f214530bd19a788450ca
Author: William Casarin <jb55@jb55.com>
Date:   Thu, 15 Jul 2021 12:39:58 -0700

builtin calls wip

Signed-off-by: William Casarin <jb55@jb55.com>

Diffstat:
Msrc/wasm.c | 258+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/wasm.h | 26++++++++++++++++++++++++--
2 files changed, 225 insertions(+), 59 deletions(-)

diff --git a/src/wasm.c b/src/wasm.c @@ -35,8 +35,6 @@ struct expr_parser { struct cursor *errs; }; -static int parse_instr(struct expr_parser *parser, u8 tag, struct instr *op); - static inline struct callframe *top_callframe(struct cursor *cur) { if (cur->p <= cur->start) { @@ -54,6 +52,19 @@ static inline struct cursor *interp_codeptr(struct wasm_interp *interp) return &frame->code; } +static int builtin_get_args(struct wasm_interp *interp) +{ + return interp_error(interp, "run get_args"); +} + +static struct builtin BUILTINS[] = { + { .name = "args_get", .fn = builtin_get_args }, +}; + +static const int NUM_BUILTINS = sizeof(BUILTINS) / sizeof(*BUILTINS); + +static int parse_instr(struct expr_parser *parser, u8 tag, struct instr *op); + static inline int is_valtype(unsigned char byte) { switch ((enum valtype)byte) { @@ -323,29 +334,60 @@ void print_error_backtrace(struct cursor *errors) } } -static void print_functype(struct functype *ft) +static int _functype_str(struct functype *ft, struct cursor *buf) { int i; - printf("("); + if (!cursor_push_str(buf, "(")) + return 0; for (i = 0; i < ft->params.num_valtypes; i++) { - printf("%s", valtype_name(ft->params.valtypes[i])); + if (!cursor_push_str(buf, valtype_name(ft->params.valtypes[i]))) + return 0; + if (i != ft->params.num_valtypes-1) { - printf(", "); + if (!cursor_push_str(buf, ", ")) + return 0; } } - printf(") -> ("); + if (!cursor_push_str(buf, ") -> (")) + return 0; for (i = 0; i < ft->result.num_valtypes; i++) { - printf("%s", valtype_name(ft->result.valtypes[i])); + if (!cursor_push_str(buf, valtype_name(ft->result.valtypes[i]))) + return 0; + if (i != ft->result.num_valtypes-1) { - printf(", "); + if (!cursor_push_str(buf, ", ")) + return 0; } } - printf(")\n"); + return cursor_push_c_str(buf, ")"); +} + +static const char *functype_str(struct functype *ft, struct cursor *buf) +{ + if (buf->start == buf->end) + return ""; + + if (!_functype_str(ft, buf)) { + if (buf->p == buf->start) + return ""; + buf->p[-1] = 0; + } + + return (const char*)buf->start; +} + +static void print_functype(struct functype *ft) +{ + static unsigned char buf[0xFF]; + struct cursor cur; + buf[0] = 0; + make_cursor(buf, buf + sizeof(buf), &cur); + printf("%s\n", functype_str(ft, &cur)); } static void print_type_section(struct typesec *typesec) @@ -528,7 +570,7 @@ static void print_local(struct local *local) debug("%d %s\n", local->n, valtype_name(local->valtype)); } -static void print_func(struct func *func) +static void print_func(struct wasm_func *func) { int i; @@ -882,7 +924,7 @@ static int parse_vector(struct wasm_parser *p, unsigned int item_size, return 1; } -static int parse_func(struct wasm_parser *p, struct func *func) +static int parse_func(struct wasm_parser *p, struct wasm_func *func) { unsigned int elems, size, i; unsigned char *start; @@ -928,7 +970,7 @@ static int parse_func(struct wasm_parser *p, struct func *func) static int parse_code_section(struct wasm_parser *p, struct codesec *code_section) { - struct func *funcs; + struct wasm_func *funcs; unsigned int elems, i; if (!parse_vector(p, sizeof(*funcs), &elems, (void**)&funcs)) { @@ -1578,6 +1620,7 @@ static int parse_importdesc(struct wasm_parser *p, struct importdesc *desc) parse_err(p, "typeidx"); return 0; } + return 1; case import_table: @@ -1604,21 +1647,35 @@ static int parse_importdesc(struct wasm_parser *p, struct importdesc *desc) return 0; } -static int parse_import(struct wasm_parser *p, struct import *import) +static int find_builtin(const char *name) { - if (!parse_name(p, &import->module_name)) { - parse_err(p, "module name"); - return 0; - } + struct builtin *b; + u32 i; - if (!parse_name(p, &import->name)) { - parse_err(p, "name"); - return 0; + for (i = 0; i < NUM_BUILTINS; i++) { + b = &BUILTINS[i]; + if (!strcmp(b->name, name)) + return i; } + return -1; +} - if (!parse_importdesc(p, &import->desc)) { - parse_err(p, "desc"); - return 0; +static int parse_import(struct wasm_parser *p, struct import *import) +{ + import->resolved_builtin = -1; + + if (!parse_name(p, &import->module_name)) + return parse_err(p, "module name"); + + if (!parse_name(p, &import->name)) + return parse_err(p, "name"); + + if (!parse_importdesc(p, &import->desc)) + return parse_err(p, "desc"); + + if (import->desc.type == import_func) { + import->resolved_builtin = + find_builtin(import->name); } return 1; @@ -2059,18 +2116,68 @@ static inline int functions_count(struct module *module) return imports_count(module) + code_count(module); } -static inline struct func *get_function(struct module *module, int ind) +static struct builtin *builtin_func(int ind) { - if ((ind - imports_count(module)) < 0) { - // TODO imports + if (ind < 0 || ind >= NUM_BUILTINS) { + printf("UNUSUAL: invalid builtin index %d (max %d)\n", ind, + NUM_BUILTINS-1); return NULL; } + return &BUILTINS[ind]; +} + +static int get_import_function(struct module *module, int ind, struct func *func) +{ + struct import *import; + int i, fn = 0; + + if (ind >= module->import_section.num_imports) + return 0; + + for (i = 0; i < module->import_section.num_imports; i++) { + import = &module->import_section.imports[i]; + + if (import->desc.type != import_func) + continue; + + if (ind != fn++) + continue; + + if (import->resolved_builtin != -1) { + /* builtin! */ + func->type = func_type_builtin; + func->builtin = builtin_func(import->resolved_builtin); + if (func->builtin == NULL) + return 0; + return 1; + } else { + /* TODO: linked imports */ + return 0; + } + } + + return 0; +} + +static inline void init_func(struct func *func) +{ + memset(func, 0, sizeof(*func)); +} + +static inline int get_function(struct module *module, int ind, struct func *func) +{ + if ((ind - imports_count(module)) < 0) { + return get_import_function(module, ind, func); + } if (ind >= module->code_section.num_funcs) { - return NULL; + return 0; } - return &module->code_section.funcs[ind - imports_count(module)]; + func->type = func_type_wasm; + func->wasm_func = &module->code_section.funcs[ind - imports_count(module)]; + + return 1; } static struct functype *get_function_type(struct wasm_interp *interp, int ind) @@ -2111,75 +2218,112 @@ static struct functype *get_function_type(struct wasm_interp *interp, int ind) return &interp->module->type_section.functypes[typeidx]; } +static inline int call_wasm_func(struct wasm_interp *interp, struct wasm_func *func, int fn) +{ + struct callframe callframe; + + /* update current function and push it to the callframe as well */ + make_cursor(func->code.code, func->code.code + func->code.code_len, &callframe.code); + callframe.fn = fn; + + if (!cursor_push_callframe(&interp->callframes, &callframe)) + return interp_error(interp, "oob cursor_pushcode"); + + return 1; +} + +static inline int call_builtin_func(struct wasm_interp *interp, struct builtin *func) +{ + return interp_error(interp, "call builtin %s", func->name); +} + +static inline int call_func(struct wasm_interp *interp, struct func *func, int fn) +{ + switch (func->type) { + case func_type_wasm: + return call_wasm_func(interp, func->wasm_func, fn); + case func_type_builtin: + return call_builtin_func(interp, func->builtin); + } + interp_error(interp, "corrupt func type: %02x", func->type); + return 0; +} + +static inline int prepare_builtin(struct wasm_interp *interp, struct builtin *func) +{ + return func->prepare_args(interp); +} + static int prepare_call(struct wasm_interp *interp, int func_index) { + static u8 tmp[0xFF]; int i; + struct cursor buf; struct functype *functype; - struct func *func; + struct func func; struct val val; - struct callframe callframe; enum valtype paramtype; unsigned int offset; debug("calling %s (%d)\n", get_function_name(interp->module, func_index), func_index); - if (!(func = get_function(interp->module, func_index))) { - interp_error(interp, "function %s (%d) not found (%d funcs)", + if (!get_function(interp->module, func_index, &func)) { + return interp_error(interp, + "function %s (%d) not found (%d funcs)", get_function_name(interp->module, func_index), func_index, interp->module->code_section.num_funcs); - return 0; } /* record locals offset for indexing locals in the next function */ offset = cursor_count(&interp->locals, sizeof(struct val)); - if (!cursor_push_int(&interp->locals_offsets, offset)) { - interp_error(interp, "push locals offset"); - return 0; - } + if (!cursor_push_int(&interp->locals_offsets, offset)) + return interp_error(interp, "push locals offset"); /* get type signature to know how many locals to push as params */ if (!(functype = get_function_type(interp, func_index))) { - interp_error(interp, "couldn't get function type for function '%s' (%d)", - get_function_name(interp->module, func_index), func_index); - return 0; + return interp_error(interp, + "couldn't get function type for function '%s' (%d)", + get_function_name(interp->module, func_index), + func_index); } + /* + if (func->type == func_type_builtin && !func.builtin.prepare_args(interp)) { + return interp_error(interp, "prepare '%s' builtin", + func->builtin.name); + } + */ /* push params as locals */ for (i = 0; i < functype->params.num_valtypes; i++) { paramtype = (enum valtype)functype->params.valtypes[i]; if (!cursor_popval(&interp->stack, &val)) { - interp_error(interp, "not enough arguments for call"); - return 0; + make_cursor(tmp, tmp + sizeof(tmp), &buf); + + return interp_error(interp, + "not enough arguments for call to %s: [%s], needed %d args, got %d", + get_function_name(interp->module, func_index), + functype_str(functype, &buf), + functype->params.num_valtypes, + i); } if (val.type != paramtype) { - interp_error(interp, + return interp_error(interp, "call parameter %d type mismatch. got %s, expected %s", i+1, valtype_name(val.type), valtype_name(paramtype)); - return 0; } if (!cursor_pushval(&interp->locals, &val)) { - interp_error(interp, "push param local"); - return 0; + return interp_error(interp, "push param local"); } } - /* update current function and push it to the callframe as well */ - make_cursor(func->code.code, func->code.code + func->code.code_len, &callframe.code); - callframe.fn = func_index; - - if (!cursor_push_callframe(&interp->callframes, &callframe)) { - interp_error(interp, "oob cursor_pushcode"); - return 0; - } - - return 1; + return call_func(interp, &func, func_index); } int interp_code(struct wasm_interp *interp); diff --git a/src/wasm.h b/src/wasm.h @@ -167,6 +167,7 @@ struct import { const char *module_name; const char *name; struct importdesc desc; + int resolved_builtin; }; struct importsec { @@ -185,14 +186,27 @@ struct local { }; /* "code" */ -struct func { +struct wasm_func { struct expr code; struct local *locals; int num_locals; }; +enum func_type { + func_type_wasm, + func_type_builtin, +}; + +struct func { + union { + struct wasm_func *wasm_func; + struct builtin *builtin; + }; + enum func_type type; +}; + struct codesec { - struct func *funcs; + struct wasm_func *funcs; int num_funcs; }; @@ -421,6 +435,7 @@ struct startsec { struct module { unsigned int parsed; unsigned int custom_sections; + struct customsec custom_section[MAX_CUSTOM_SECTIONS]; struct typesec type_section; struct funcsec func_section; @@ -448,6 +463,7 @@ struct callframe { struct wasm_interp { struct module *module; + struct cursor errors; /* struct error */ size_t ops; @@ -466,6 +482,12 @@ struct wasm_interp { struct cursor resolver_stack; /* u32 */ }; +struct builtin { + const char *name; + int (*fn)(struct wasm_interp *); + int (*prepare_args)(struct wasm_interp *); +}; + struct wasm_parser { struct module module; struct cursor cur;