protoverse

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

commit c1014fcf09349c3ee1736bda913682c403232283
parent 9c469d86bba8ac6cd7a02ecba6a9d95b6ac676e1
Author: William Casarin <jb55@jb55.com>
Date:   Wed,  4 Aug 2021 09:58:05 -0700

fix locals

Diffstat:
Msrc/wasm.c | 268++++++++++++++++++++++++++++++++++---------------------------------------------
Msrc/wasm.h | 12+++++++++---
Mtodo/todo.txt | 3++-
3 files changed, 126 insertions(+), 157 deletions(-)

diff --git a/src/wasm.c b/src/wasm.c @@ -176,34 +176,25 @@ static INLINE int read_mem_i32(struct wasm_interp *interp, u32 ptr, int *i) return read_mem(interp, ptr, sizeof(*i), i); } -static struct val *get_fn_local(struct wasm_interp *interp, int fn, u32 ind) +static INLINE struct val *get_local(struct wasm_interp *interp, u32 ind) { + struct callframe *frame; struct func *func; - if (unlikely(!(func = get_fn(interp->module, fn)))) { - interp_error(interp, "unknown fn %d", fn); + if (unlikely(!(frame = top_callframe(&interp->callframes)))) { + interp_error(interp, "no callframe?"); return NULL; } + func = &interp->module->funcs[frame->fn]; + if (unlikely(ind >= func->num_locals)) { interp_error(interp, "local index %d too high for %s:%d (max %d)", - ind, func->name, fn, func->num_locals-1); - return NULL; - } - - return &func->locals[ind]; -} - -static INLINE struct val *get_local(struct wasm_interp *interp, u32 ind) -{ - struct callframe *frame; - - if (unlikely(!(frame = top_callframe(&interp->callframes)))) { - interp_error(interp, "no callframe?"); + ind, func->name, frame->fn, func->num_locals-1); return NULL; } - return get_fn_local(interp, frame->fn, ind); + return &frame->locals[ind]; } static INLINE int get_params(struct wasm_interp *interp, @@ -224,7 +215,7 @@ static INLINE int get_params(struct wasm_interp *interp, num_vals, fn->functype->params.num_valtypes); } - *vals = fn->locals; + *vals = frame->locals; return 1; } @@ -1480,14 +1471,14 @@ static int parse_export(struct wasm_parser *p, struct wexport *export) return 1; } -static int parse_local(struct wasm_parser *p, struct val *local) +static int parse_local_def(struct wasm_parser *p, struct local_def *def) { - if (unlikely(!parse_u32(&p->cur, &local->num.u32))) { - debug("fail parse local\n"); + if (unlikely(!parse_u32(&p->cur, &def->num_types))) { + debug("fail parse local def\n"); return parse_err(p, "n"); } - if (unlikely(!parse_valtype(p, &local->type))) { + if (unlikely(!parse_valtype(p, &def->type))) { debug("fail parse valtype\n"); return parse_err(p, "valtype"); } @@ -1620,7 +1611,7 @@ static int parse_name_section(struct wasm_parser *p, struct namesec *sec, static int parse_func(struct wasm_parser *p, struct wasm_func *func) { - struct val *locals; + struct local_def *defs; u32 i, size; u8 *start; @@ -1629,27 +1620,24 @@ static int parse_func(struct wasm_parser *p, struct wasm_func *func) } start = p->cur.p; - locals = (struct val*)p->mem.p; + defs = (struct local_def*)p->mem.p; - if (!parse_u32(&p->cur, &func->num_locals)) { + if (!parse_u32(&p->cur, &func->num_local_defs)) return parse_err(p, "read locals vec"); - } - if (!cursor_alloc(&p->mem, sizeof(*locals) * func->num_locals)) { + if (!cursor_alloc(&p->mem, sizeof(*defs) * func->num_local_defs)) return parse_err(p, "oom alloc param locals"); - } - if (p->cur.p > p->cur.end) { + if (p->cur.p > p->cur.end) return parse_err(p, "corrupt functype?"); - } - for (i = 0; i < func->num_locals; i++) { - if (!parse_local(p, &locals[i])) { + for (i = 0; i < func->num_local_defs; i++) { + if (!parse_local_def(p, &defs[i])) { return parse_err(p, "local #%d", i); } } - func->locals = locals; + func->local_defs = defs; func->code.code_len = size - (p->cur.p - start); if (!pull_data_into_cursor(&p->cur, &p->mem, &func->code.code, @@ -3005,6 +2993,22 @@ static const char *find_function_name(struct module *module, u32 fn) return "unknown"; } +static int count_fn_locals(struct func *func) +{ + u32 i, num_locals = 0; + + num_locals += func->functype->params.num_valtypes; + + if (func->type == func_type_wasm) { + // counts locals of the same type + for (i = 0; i < func->wasm_func->num_local_defs; i++) { + num_locals += func->wasm_func->local_defs[i].num_types; + } + } + + return num_locals; +} + static int make_func_lookup_table(struct wasm_parser *parser) { u32 i, num_imports, num_func_imports, num_internal_funcs, typeidx, fn; @@ -3040,6 +3044,7 @@ static int make_func_lookup_table(struct wasm_parser *parser) typeidx = import->desc.typeidx; func->functype = &parser->module.type_section.functypes[typeidx]; func->type = func_type_builtin; + func->num_locals = count_fn_locals(func); if (import->resolved_builtin == -1) { debug("warning: %s not resolved\n", func->name); @@ -3058,6 +3063,7 @@ static int make_func_lookup_table(struct wasm_parser *parser) func->wasm_func = &parser->module.code_section.funcs[i]; func->functype = &parser->module.type_section.functypes[typeidx]; func->name = find_function_name(&parser->module, fn); + func->num_locals = count_fn_locals(func); } assert(fn == parser->module.num_funcs); @@ -3128,30 +3134,20 @@ static INLINE int interp_prep_binop(struct wasm_interp *interp, struct val *lhs, return 1; } -static INLINE int set_fn_local(struct wasm_interp *interp, int fn, u32 ind, - struct val *val) -{ - struct val *local; - - if (unlikely(!(local = get_fn_local(interp, fn, ind)))) { - return interp_error(interp, "no local?"); - } - - memcpy(local, val, sizeof(*val)); - - return 1; -} - static INLINE int set_local(struct wasm_interp *interp, u32 ind, struct val *val) { struct callframe *frame; + struct val *local; - if (unlikely(!(frame = top_callframe(&interp->callframes)))) { + if (unlikely(!(frame = top_callframe(&interp->callframes)))) return interp_error(interp, "no callframe?"); - } - return set_fn_local(interp, frame->fn, ind, val); + if (unlikely(!(local = get_local(interp, ind)))) + return interp_error(interp, "no local?"); + + memcpy(local, val, sizeof(*val)); + return 1; } static INLINE int interp_local_tee(struct wasm_interp *interp, u32 index) @@ -3661,10 +3657,11 @@ static INLINE int count_local_resolvers(struct wasm_interp *interp, int *count) static INLINE int drop_callframe(struct wasm_interp *interp) { int offset; + struct callframe *frame; + #ifdef DEBUG int count, from_fn, to_fn; const char *from, *to; - struct callframe *frame; if (unlikely(!count_local_resolvers(interp, &count))) { return interp_error(interp, "count local resolvers"); @@ -3675,7 +3672,8 @@ static INLINE int drop_callframe(struct wasm_interp *interp) " %d unpopped labels", count); } - if (!(frame = top_callframes(&interp->callframes, 0))) { + frame = top_callframe(&interp->callframes); + if (!frame) { from = "(aux)"; from_fn = -1; } else { @@ -3691,23 +3689,30 @@ static INLINE int drop_callframe(struct wasm_interp *interp) to_fn = frame->fn; } #endif + frame = top_callframe(&interp->callframes); if (unlikely(!cursor_popint(&interp->resolver_offsets, &offset))) { return interp_error(interp, "pop resolver_offsets"); } + // free frame locals + assert((u8*)frame->locals <= interp->locals.p); + interp->locals.p = (u8*)frame->locals; + debug("returning from %s:%d to %s:%d\n", from, from_fn, to, to_fn); return cursor_drop_callframe(&interp->callframes); } -static INLINE int call_wasm_func(struct wasm_interp *interp, struct wasm_func *func, int fn) +static INLINE int call_wasm_func(struct wasm_interp *interp, struct wasm_func *func, + int fn, struct val *locals) { 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; + callframe.locals = locals; assert(func->code.code_len > 0); @@ -3729,12 +3734,14 @@ static INLINE int call_wasm_func(struct wasm_interp *interp, struct wasm_func *f } -static INLINE int call_builtin_func(struct wasm_interp *interp, struct builtin *builtin, int fn) +static INLINE int call_builtin_func(struct wasm_interp *interp, struct builtin *builtin, + int fn, struct val *params) { struct callframe callframe = {}; /* update current function and push it to the callframe as well */ callframe.fn = fn; + callframe.locals = params; if (unlikely(!push_callframe(interp, &callframe))) return interp_error(interp, "oob cursor_pushcode"); @@ -3748,18 +3755,19 @@ static INLINE int call_builtin_func(struct wasm_interp *interp, struct builtin * return 1; } -static INLINE int call_func(struct wasm_interp *interp, struct func *func, int fn) +static INLINE int call_func(struct wasm_interp *interp, struct func *func, + int fn, struct val *locals) { switch (func->type) { case func_type_wasm: - return call_wasm_func(interp, func->wasm_func, fn); + return call_wasm_func(interp, func->wasm_func, fn, locals); case func_type_builtin: if (func->builtin == NULL) { return interp_error(interp, "attempted to call unresolved fn: %s", func->name); } - return call_builtin_func(interp, func->builtin, fn); + return call_builtin_func(interp, func->builtin, fn, locals); } return interp_error(interp, "corrupt func type: %02x", func->type); } @@ -3787,21 +3795,41 @@ static void make_default_val(struct val *val) } } +static struct val *alloc_frame_locals(struct wasm_interp *interp, + struct func *func) +{ + struct val *locals; + u32 size; + + size = func->num_locals * sizeof(struct val); + + if (!(locals = cursor_malloc(&interp->locals, size))) { + debug("alloc_locals err size %d\n", size); + interp_error(interp, "could not alloc locals for %s", + func->name); + return NULL; + } + + return locals; +} + static int prepare_call(struct wasm_interp *interp, struct func *func, - int func_index) + int func_index, struct val **locals) { static char buf[128]; struct val *local; - enum valtype paramtype; struct val val; - u32 i, ind; + u32 i, j, ind; + + if (!(*locals = alloc_frame_locals(interp, func))) + return interp_error(interp, "locals stack oom"); /* push params as locals */ for (i = 0; i < func->functype->params.num_valtypes; i++) { ind = func->functype->params.num_valtypes-1-i; - paramtype = (enum valtype)func->functype->params.valtypes[ind]; + local = &(*locals)[ind]; + local->type = (enum valtype)func->functype->params.valtypes[ind]; //ind = i; - local = &func->locals[ind]; if (unlikely(!cursor_popval(&interp->stack, &val))) { return interp_error(interp, @@ -3812,29 +3840,39 @@ static int prepare_call(struct wasm_interp *interp, struct func *func, ind); } - if (unlikely(val.type != paramtype)) { + if (unlikely(val.type != local->type)) { return interp_error(interp, "call parameter %d type mismatch. got %s, expected %s", ind+1, valtype_name(val.type), - valtype_name(paramtype)); + valtype_name(local->type)); } +#ifdef DEBUG debug("setting param %d (%s) to ", ind, valtype_name(local->type)); -#ifdef DEBUG print_val(&val); printf("\n"); #endif - memcpy(&func->locals[ind], &val, sizeof(struct val)); + memcpy(local, &val, sizeof(struct val)); } - for (i=func->functype->params.num_valtypes; - i < func->num_locals; i++) { - local = &func->locals[i]; + if (func->type == func_type_builtin) + return 1; + + ind = i; + + for (i = 0; i < func->wasm_func->num_local_defs; i++) { + for (j = 0; j < func->wasm_func->local_defs[i].num_types; j++, ind++) { + assert(ind < func->num_locals); + local = (*locals) + ind; + + debug("initializing local %d to type %s\n", + ind-func->functype->params.num_valtypes, + valtype_name(func->wasm_func->local_defs[i].type)); + + local->type = func->wasm_func->local_defs[i].type; make_default_val(local); - debug("setting local %d (%s) to default\n", - i-func->functype->params.num_valtypes, - valtype_name(local->type)); + } } return 1; @@ -3843,6 +3881,7 @@ static int prepare_call(struct wasm_interp *interp, struct func *func, static int call_function(struct wasm_interp *interp, int func_index) { struct func *func; + struct val *locals; debug("calling %s:%d\n", get_function_name(interp->module, func_index), func_index); @@ -3854,11 +3893,11 @@ static int call_function(struct wasm_interp *interp, int func_index) interp->module->code_section.num_funcs); } - if (!prepare_call(interp, func, func_index)) { + if (!prepare_call(interp, func, func_index, &locals)) { return interp_error(interp, "prepare args"); } - return call_func(interp, func, func_index); + return call_func(interp, func, func_index, locals); } static int interp_call(struct wasm_interp *interp, int func_index) @@ -6300,22 +6339,6 @@ void wasm_parser_init(struct wasm_parser *p, u8 *wasm, size_t wasm_len, size_t a cursor_slice(&p->mem, &p->errs.cur, 0xFFFF); } -static int count_fn_locals(struct func *func) -{ - u32 i, num_locals = 0; - - num_locals += func->functype->params.num_valtypes; - - if (func->type == func_type_wasm) { - // counts locals of the same type - for (i = 0; i < func->wasm_func->num_locals; i++) { - num_locals += func->wasm_func->locals->num.i32; - } - } - - return num_locals; -} - static int calculate_tables_size(struct module *module) { u32 i, num_tables, size; @@ -6370,67 +6393,6 @@ static int alloc_tables(struct wasm_interp *interp) return 1; } - -static int calculate_locals_size(struct module *module) -{ - u32 i, locals_size = 0; - struct func *func; - - for (i = 0; i < module->num_funcs; i++) { - func = &module->funcs[i]; - locals_size += count_fn_locals(func) * sizeof(struct val); - } - - return locals_size; -} - -static int alloc_locals(struct module *module, struct cursor *mem, - struct errors *errs) -{ - u32 i, j, k, num_locals, size, sizes = 0, ind; - struct func *func; - - for (i = 0; i < module->num_funcs; i++) { - func = &module->funcs[i]; - num_locals = count_fn_locals(func); - - func->locals = (struct val*)mem->p; - func->num_locals = num_locals; - - size = num_locals * sizeof(struct val); - sizes += size; - - if (!cursor_alloc(mem, size)) { - debug("alloc_locals err sizes %d\n", sizes); - return note_error(errs, mem, - "could not alloc locals for %s", - func->name); - } - - for (j = 0, ind = 0; j < func->functype->params.num_valtypes; j++, ind++) { - func->locals[j].type = func->functype->params.valtypes[j]; - } - - if (func->type != func_type_wasm) - continue; - - for (j = 0; j < func->wasm_func->num_locals; j++) { - for (k = 0; k < func->wasm_func->locals[j].num.u32; k++, ind++) { - /* - debug("initializing function %d local %d to type %s\n", - i, ind, valtype_name( - func->wasm_func->locals[j].type)); - */ - - func->locals[ind].type = - func->wasm_func->locals[j].type; - } - } - } - - return 1; -} - static int init_element(struct wasm_interp *interp, struct expr *init, struct elem_inst *elem_inst) { @@ -6796,7 +6758,7 @@ int wasm_interp_init(struct wasm_interp *interp, struct module *module) num_elements = count_element_insts(module); elems_size = num_elements * sizeof(struct elem_inst); - locals_size = calculate_locals_size(module); + locals_size = sizeof(struct val) * 2048; tables_size = calculate_tables_size(module); if (num_mems > 1) { @@ -6874,7 +6836,7 @@ int wasm_interp_init(struct wasm_interp *interp, struct module *module) assert(interp->mem.p - start == num_labels_size); start = interp->mem.p; - ok = ok && alloc_locals(interp->module, &interp->mem, &interp->errors); + ok = ok && cursor_slice(&interp->mem, &interp->locals, locals_size); assert(interp->mem.p - start == locals_size); interp->module_inst.num_elements = num_elements; diff --git a/src/wasm.h b/src/wasm.h @@ -227,11 +227,16 @@ struct global { struct val val; }; +struct local_def { + u32 num_types; + enum valtype type; +}; + /* "code" */ struct wasm_func { struct expr code; - struct val *locals; - u32 num_locals; + struct local_def *local_defs; + u32 num_local_defs; }; enum func_type { @@ -244,7 +249,6 @@ struct func { struct wasm_func *wasm_func; struct builtin *builtin; }; - struct val *locals; u32 num_locals; struct functype *functype; enum func_type type; @@ -634,6 +638,7 @@ struct label { struct callframe { struct cursor code; + struct val *locals; u32 fn; }; @@ -684,6 +689,7 @@ struct wasm_interp { struct cursor memory; /* memory pages (65536 blocks) */ + struct cursor locals; /* struct val */ struct cursor labels; /* struct labels */ struct cursor num_labels; diff --git a/todo/todo.txt b/todo/todo.txt @@ -1,7 +1,8 @@ chunked envelope (1280-byte sized chunks) @net -do we need to reset function locals on return? @wasm check blocktypes when popping labels @wasm make chess demo @examples getting started @docs move module instantiate from alloc to wasm_interp @wasm memcpy table init like we do for memory init @wasm +use hashtable for labels @wasm +callframe locals