protoverse

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

commit e33e8cf6c54e4f3d2f6fbd028b6d5ba30d9a1c5a
parent d403d6c74134e0b1fac6bfe5c4dc8fd44ef529ac
Author: William Casarin <jb55@jb55.com>
Date:   Mon, 19 Jul 2021 18:11:35 -0700

element and table init working

Diffstat:
Msrc/cursor.h | 1-
Msrc/wasm.c | 1377+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/wasm.h | 83+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
3 files changed, 935 insertions(+), 526 deletions(-)

diff --git a/src/cursor.h b/src/cursor.h @@ -170,7 +170,6 @@ static inline int pull_data_into_cursor(struct cursor *cursor, static inline int cursor_drop(struct cursor *cur, int len) { if (unlikely(cur->p - len < cur->start)) { - printf("cursor drop oob\n"); return 0; } diff --git a/src/wasm.c b/src/wasm.c @@ -66,6 +66,10 @@ static const char *valtype_name(enum valtype valtype) return "?"; } +static const char *reftype_name(enum reftype reftype) { + return valtype_name((enum valtype)reftype); +} + static const char *valtype_literal(enum valtype valtype) { switch (valtype) { @@ -123,6 +127,11 @@ static struct val *get_local(struct wasm_interp *interp, int ind) return get_fn_local(interp, frame->fn, ind); } +static INLINE int stack_dropval(struct wasm_interp *interp) +{ + return cursor_drop(&interp->stack, sizeof(struct val)); +} + static INLINE int stack_popval(struct wasm_interp *interp, struct val *val) { return cursor_popval(&interp->stack, val); @@ -149,6 +158,45 @@ static INLINE int cursor_pop_i32(struct cursor *stack, int *i) return 1; } +static int is_reftype(enum valtype type) +{ + switch (type) { + case val_i32: + case val_i64: + case val_f32: + case val_f64: + return 0; + case val_ref_null: + case val_ref_func: + case val_ref_extern: + return 1; + } + return 0; +} + +static INLINE int cursor_pop_ref(struct cursor *stack, struct val *val) +{ + if (!cursor_popval(stack, val)) { + return 0; + } + if (!is_reftype(val->type)) { + return 0; + } + return 1; +} + +static INLINE int stack_pop_ref(struct wasm_interp *interp, struct val *val) +{ + if (!cursor_popval(&interp->stack, val)) { + return interp_error(interp, "no value on stack"); + } + if (!is_reftype(val->type)) { + return interp_error(interp, "not a reftype, got %s", + valtype_name(val->type)); + } + return 1; +} + static INLINE int stack_pop_i32(struct wasm_interp *interp, int *i) { return cursor_pop_i32(&interp->stack, i); @@ -178,7 +226,7 @@ static void print_val(struct val *val) { switch (val->type) { case val_i32: printf("%d", val->num.i32); break; - case val_i64: printf("%llu", val->num.i64); break; + case val_i64: printf("%lu", val->num.i64); break; case val_f32: printf("%f", val->num.f32); break; case val_f64: printf("%f", val->num.f64); break; @@ -242,6 +290,14 @@ static INLINE int cursor_push_i32(struct cursor *stack, int i) return cursor_pushval(stack, &val); } +static INLINE int cursor_push_funcref(struct cursor *stack, int addr) +{ + struct val val; + val.type = val_ref_func; + val.ref.addr = addr; + return cursor_pushval(stack, &val); +} + static INLINE int stack_push_i32(struct wasm_interp *interp, int i) { return cursor_push_i32(&interp->stack, i); @@ -500,6 +556,9 @@ static char *instr_name(enum instr_tag tag) case i_i64_extend8_s: return "i64_extend8_s"; case i_i64_extend16_s: return "i64_extend16_s"; case i_i64_extend32_s: return "i64_extend32_s"; + case i_ref_null: return "ref_null"; + case i_ref_func: return "ref_func"; + case i_ref_is_null: return "ref_is_null"; } snprintf(unk, sizeof(unk), "0x%02x", tag); @@ -526,6 +585,11 @@ static INLINE int cursor_drop_callframe(struct cursor *cur) return cursor_drop(cur, sizeof(struct callframe)); } +static INLINE int cursor_dropval(struct cursor *stack) +{ + return cursor_drop(stack, sizeof(struct val)); +} + static INLINE int cursor_popint(struct cursor *cur, int *i) { return cursor_pop(cur, (u8 *)i, sizeof(int)); @@ -659,15 +723,6 @@ static void print_limits(struct limits *limits) } } -static const char *reftype_name(enum reftype reftype) -{ - switch (reftype) { - case funcref: return "funcref"; - case externref: return "externref"; - } - return "unknown_reftype"; -} - static void print_memory_section(struct memsec *memory) { int i; @@ -866,25 +921,22 @@ static void print_module(struct module *module) } -/* I DONT NEED THIS (yet?) */ -/* -static int leb128_write(struct cursor *write, unsigned int val) +static int leb128_write(struct cursor *write, unsigned int value) { unsigned char byte; while (1) { byte = value & 0x7F; value >>= 7; if (value == 0) { - if (!push_byte(write, byte)) + if (!cursor_push_byte(write, byte)) return 0; return 1; } else { - if (!push_byte(write, byte | 0x80)) + if (!cursor_push_byte(write, byte | 0x80)) return 0; } } } -*/ #define BYTE_AT(type, i, shift) (((type)(p[i]) & 0x7f) << (shift)) #define LEB128_1(type) (BYTE_AT(type, 0, 0)) @@ -1383,148 +1435,409 @@ static int cursor_push_nullval(struct cursor *stack) return cursor_pushval(stack, &val); } -static int eval_const_instr(struct instr *instr, struct errors *errs, - struct cursor *stack) -{ - switch ((enum const_instr)instr->tag) { - case ci_global_get: - return note_error(errs, stack, "todo: global_get inside global"); - case ci_ref_null: - if (unlikely(!cursor_push_nullval(stack))) { - return note_error(errs, stack, "couldn't push null"); - } - return 1; - case ci_ref_func: - return note_error(errs, stack, "todo: global func ref"); - case ci_const_i32: - if (unlikely(!cursor_push_i32(stack, instr->integer))) { - return note_error(errs, stack, - "global push i32 const"); - } - return 1; - case ci_const_i64: - return note_error(errs, stack, "todo: global push const i64"); - case ci_const_f32: - return note_error(errs, stack, "todo: global push const f32"); - case ci_end: - return note_error(errs, stack, "unexpected end tag"); - case ci_const_f64: - return note_error(errs, stack, "todo: global push const f64"); - } - - return note_error(errs, stack, "non-const expr instr %s", - instr_name(instr->tag)); -} - -static int parse_const_expr(struct expr_parser *p, struct expr *expr) -{ - u8 tag; - struct instr instr; - - expr->code = p->code->p; - - while (1) { - if (unlikely(!pull_byte(p->code, &tag))) { - return note_error(p->errs, p->code, "oob"); - } - - if (unlikely(!is_const_instr(tag))) { - return note_error(p->errs, p->code, - "invalid const expr instruction: '%s'", - instr_name(tag)); - } - - if (tag == i_end) { - expr->code_len = p->code->p - expr->code; - return 1; - } - - if (unlikely(!parse_instr(p, tag, &instr))) { - return note_error(p->errs, p->code, - "couldn't parse const expr instr '%s'", - instr_name(tag)); - } - - if (p->stack && - unlikely(!eval_const_instr(&instr, p->errs, p->stack))) { - return note_error(p->errs, p->code, "eval const instr"); - } - } - - return 0; -} - -static INLINE void make_const_expr_evaluator(struct errors *errs, - struct cursor *code, struct cursor *stack, - struct expr_parser *parser) +static const char *show_instr(struct instr *instr) { - parser->interp = NULL; - parser->stack = stack; - parser->code = code; - parser->errs = errs; -} + struct cursor buf; + static char buffer[64]; + static char tmp[32]; + int len, i; -static INLINE void make_const_expr_parser(struct wasm_parser *p, - struct expr_parser *parser) -{ - parser->interp = NULL; - parser->stack = NULL; - parser->code = &p->cur; - parser->errs = &p->errs; -} + buffer[sizeof(buffer)-1] = 0; + make_cursor((u8*)buffer, (u8*)buffer + sizeof(buffer) - 1, &buf); -/* -static INLINE int eval_const_expr(struct expr *expr, struct errors *errs, - struct cursor *stack) -{ - struct cursor code; - struct expr expr_out; - struct expr_parser parser; + cursor_push_str(&buf, instr_name(instr->tag)); + len = buf.p - buf.start; - make_cursor(expr->code, expr->code + expr->code_len, &code); - make_const_expr_evaluator(errs, &code, stack, &parser); + for (i = 0; i < 14-len; i++) + cursor_push_byte(&buf, ' '); - return parse_const_expr(&parser, &expr_out); -} -*/ + switch (instr->tag) { + // two-byte instrs + case i_memory_size: + case i_memory_grow: + sprintf(tmp, "0x%02x", instr->memidx); + cursor_push_str(&buf, tmp); + break; + case i_block: + case i_loop: + case i_if: + break; -static int parse_global(struct wasm_parser *p, - struct global *global) -{ - struct expr_parser parser; - struct cursor stack; + case i_else: + case i_end: + break; - stack.start = p->mem.p; - stack.p = p->mem.p; - stack.end = p->mem.end; + case i_call: + case i_local_get: + case i_local_set: + case i_local_tee: + case i_global_get: + case i_global_set: + case i_br: + case i_br_if: + case i_i32_const: + case i_i64_const: + case i_ref_func: + sprintf(tmp, "%d", instr->integer); + cursor_push_str(&buf, tmp); + break; - make_const_expr_evaluator(&p->errs, &p->cur, &stack, &parser); + case i_ref_null: + sprintf(tmp, "%s", reftype_name(instr->reftype)); + cursor_push_str(&buf, tmp); + break; - if (!parse_globaltype(p, &global->type)) { - return parse_err(p, "type"); - } - if (!parse_const_expr(&parser, &global->init)) { - return parse_err(p, "init code"); - } + case i_i32_load: + case i_i64_load: + case i_f32_load: + case i_f64_load: + case i_i32_load8_s: + case i_i32_load8_u: + case i_i32_load16_s: + case i_i32_load16_u: + case i_i64_load8_s: + case i_i64_load8_u: + case i_i64_load16_s: + case i_i64_load16_u: + case i_i64_load32_s: + case i_i64_load32_u: + case i_i32_store: + case i_i64_store: + case i_f32_store: + case i_f64_store: + case i_i32_store8: + case i_i32_store16: + case i_i64_store8: + case i_i64_store16: + case i_i64_store32: + sprintf(tmp, "%d %d", instr->memarg.offset, instr->memarg.align); + cursor_push_str(&buf, tmp); + break; - if (!cursor_popval(&stack, &global->val)) { - return parse_err(p, "couldn't eval global expr"); - } + case i_br_table: + break; - return 1; -} + case i_call_indirect: + sprintf(tmp, "%d %d", instr->call_indirect.typeidx, + instr->call_indirect.tableidx); + cursor_push_str(&buf, tmp); + break; -static int parse_global_section(struct wasm_parser *p, - struct globalsec *global_section) -{ - struct global *globals; - unsigned int elems, i; + case i_f32_const: + sprintf(tmp, "%f", instr->fp_single); + cursor_push_str(&buf, tmp); + break; - if (!parse_vector(p, sizeof(*globals), &elems, (void**)&globals)) { - return parse_err(p, "globals vector"); - } + case i_f64_const: + sprintf(tmp, "%f", instr->fp_double); + cursor_push_str(&buf, tmp); + break; + + // single-tag ops + case i_unreachable: + case i_nop: + case i_return: + case i_drop: + case i_select: + case i_i32_eqz: + case i_i32_eq: + case i_i32_ne: + case i_i32_lt_s: + case i_i32_lt_u: + case i_i32_gt_s: + case i_i32_gt_u: + case i_i32_le_s: + case i_i32_le_u: + case i_i32_ge_s: + case i_i32_ge_u: + case i_i64_eqz: + case i_i64_eq: + case i_i64_ne: + case i_i64_lt_s: + case i_i64_lt_u: + case i_i64_gt_s: + case i_i64_gt_u: + case i_i64_le_s: + case i_i64_le_u: + case i_i64_ge_s: + case i_i64_ge_u: + case i_f32_eq: + case i_f32_ne: + case i_f32_lt: + case i_f32_gt: + case i_f32_le: + case i_f32_ge: + case i_f64_eq: + case i_f64_ne: + case i_f64_lt: + case i_f64_gt: + case i_f64_le: + case i_f64_ge: + case i_i32_clz: + case i_i32_add: + case i_i32_sub: + case i_i32_mul: + case i_i32_div_s: + case i_i32_div_u: + case i_i32_rem_s: + case i_i32_rem_u: + case i_i32_and: + case i_i32_or: + case i_i32_xor: + case i_i32_shl: + case i_i32_shr_s: + case i_i32_shr_u: + case i_i32_rotl: + case i_i32_rotr: + case i_i64_clz: + case i_i64_ctz: + case i_i64_popcnt: + case i_i64_add: + case i_i64_sub: + case i_i64_mul: + case i_i64_div_s: + case i_i64_div_u: + case i_i64_rem_s: + case i_i64_rem_u: + case i_i64_and: + case i_i64_or: + case i_i64_xor: + case i_i64_shl: + case i_i64_shr_s: + case i_i64_shr_u: + case i_i64_rotl: + case i_i64_rotr: + case i_f32_abs: + case i_f32_neg: + case i_f32_ceil: + case i_f32_floor: + case i_f32_trunc: + case i_f32_nearest: + case i_f32_sqrt: + case i_f32_add: + case i_f32_sub: + case i_f32_mul: + case i_f32_div: + case i_f32_min: + case i_f32_max: + case i_f32_copysign: + case i_f64_abs: + case i_f64_neg: + case i_f64_ceil: + case i_f64_floor: + case i_f64_trunc: + case i_f64_nearest: + case i_f64_sqrt: + case i_f64_add: + case i_f64_sub: + case i_f64_mul: + case i_f64_div: + case i_f64_min: + case i_f64_max: + case i_f64_copysign: + case i_i32_wrap_i64: + case i_i32_trunc_f32_s: + case i_i32_trunc_f32_u: + case i_i32_trunc_f64_s: + case i_i32_trunc_f64_u: + case i_i64_extend_i32_s: + case i_i64_extend_i32_u: + case i_i64_trunc_f32_s: + case i_i64_trunc_f32_u: + case i_i64_trunc_f64_s: + case i_i64_trunc_f64_u: + case i_f32_convert_i32_s: + case i_f32_convert_i32_u: + case i_f32_convert_i64_s: + case i_f32_convert_i64_u: + case i_f32_demote_f64: + case i_f64_convert_i32_s: + case i_f64_convert_i32_u: + case i_f64_convert_i64_s: + case i_f64_convert_i64_u: + case i_f64_promote_f32: + case i_i32_reinterpret_f32: + case i_i64_reinterpret_f64: + case i_f32_reinterpret_i32: + case i_f64_reinterpret_i64: + case i_i32_extend8_s: + case i_i32_extend16_s: + case i_i64_extend8_s: + case i_i64_extend16_s: + case i_i64_extend32_s: + case i_ref_is_null: + break; + } + + cursor_push_byte(&buf, 0); + return buffer; +} + +static int eval_const_instr(struct instr *instr, struct errors *errs, + struct cursor *stack) +{ + debug("eval_const_instr %s\n", show_instr(instr)); + + switch ((enum const_instr)instr->tag) { + case ci_global_get: + return note_error(errs, stack, "todo: global_get inside global"); + case ci_ref_null: + if (unlikely(!cursor_push_nullval(stack))) { + return note_error(errs, stack, "couldn't push null"); + } + return 1; + case ci_ref_func: + if (unlikely(!cursor_push_funcref(stack, instr->integer))) { + return note_error(errs, stack, "couldn't push funcref"); + } + return 1; + case ci_const_i32: + if (unlikely(!cursor_push_i32(stack, instr->integer))) { + return note_error(errs, stack, + "global push i32 const"); + } + return 1; + case ci_const_i64: + return note_error(errs, stack, "todo: global push const i64"); + case ci_const_f32: + return note_error(errs, stack, "todo: global push const f32"); + case ci_end: + return note_error(errs, stack, "unexpected end tag"); + case ci_const_f64: + return note_error(errs, stack, "todo: global push const f64"); + } + + return note_error(errs, stack, "non-const expr instr %s", + instr_name(instr->tag)); +} + +static int parse_const_expr(struct expr_parser *p, struct expr *expr) +{ + u8 tag; + struct instr instr; + + expr->code = p->code->p; + + while (1) { + if (unlikely(!pull_byte(p->code, &tag))) { + return note_error(p->errs, p->code, "oob"); + } + + if (unlikely(!is_const_instr(tag))) { + return note_error(p->errs, p->code, + "invalid const expr instruction: '%s'", + instr_name(tag)); + } + + if (tag == i_end) { + expr->code_len = p->code->p - expr->code; + return 1; + } + + if (unlikely(!parse_instr(p, tag, &instr))) { + return note_error(p->errs, p->code, + "couldn't parse const expr instr '%s'", + instr_name(tag)); + } + + if (p->stack && + unlikely(!eval_const_instr(&instr, p->errs, p->stack))) { + return note_error(p->errs, p->code, "eval const instr"); + } + } + + return 0; +} + +static INLINE void make_const_expr_evaluator(struct errors *errs, + struct cursor *code, struct cursor *stack, + struct expr_parser *parser) +{ + parser->interp = NULL; + parser->stack = stack; + parser->code = code; + parser->errs = errs; +} + +static INLINE void make_const_expr_parser(struct wasm_parser *p, + struct expr_parser *parser) +{ + parser->interp = NULL; + parser->stack = NULL; + parser->code = &p->cur; + parser->errs = &p->errs; +} + +static INLINE int eval_const_expr(struct expr *expr, struct errors *errs, + struct cursor *stack) +{ + struct cursor code; + struct expr expr_out; + struct expr_parser parser; + + make_cursor(expr->code, expr->code + expr->code_len, &code); + make_const_expr_evaluator(errs, &code, stack, &parser); + + return parse_const_expr(&parser, &expr_out); +} + +static INLINE int eval_const_val(struct expr *expr, struct errors *errs, + struct cursor *stack, struct val *val) +{ + if (!eval_const_expr(expr, errs, stack)) { + return note_error(errs, stack, "eval const expr"); + } + + if (!cursor_popval(stack, val)) { + return note_error(errs, stack, "no val to pop?"); + } + + if (cursor_dropval(stack)) { + return note_error(errs, stack, "stack not empty"); + } + + return 1; +} + + +static int parse_global(struct wasm_parser *p, + struct global *global) +{ + struct expr_parser parser; + struct cursor stack; + + stack.start = p->mem.p; + stack.p = p->mem.p; + stack.end = p->mem.end; + + make_const_expr_evaluator(&p->errs, &p->cur, &stack, &parser); + + if (!parse_globaltype(p, &global->type)) { + return parse_err(p, "type"); + } + + if (!parse_const_expr(&parser, &global->init)) { + return parse_err(p, "init code"); + } + + if (!cursor_popval(&stack, &global->val)) { + return parse_err(p, "couldn't eval global expr"); + } + + return 1; +} + +static int parse_global_section(struct wasm_parser *p, + struct globalsec *global_section) +{ + struct global *globals; + unsigned int elems, i; + + if (!parse_vector(p, sizeof(*globals), &elems, (void**)&globals)) { + return parse_err(p, "globals vector"); + } for (i = 0; i < elems; i++) { if (!parse_global(p, &globals[i])) { @@ -1593,10 +1906,42 @@ static int parse_expr(struct wasm_parser *p, struct expr *expr) return 1; } +static int parse_elem_func_inits(struct wasm_parser *p, struct elem *elem) +{ + int index, i; + struct expr *expr; + + if (!read_int(&p->cur, &elem->num_inits)) + return parse_err(p, "func indices vec read fail"); + + if (!(elem->inits = cursor_alloc(&p->mem, elem->num_inits * + sizeof(struct expr)))) { + return parse_err(p, "couldn't alloc vec(funcidx) for elem"); + } + + for (i = 0; i < elem->num_inits; i++) { + expr = &elem->inits[i]; + expr->code = p->mem.p; + + if (!read_int(&p->cur, &index)) + return parse_err(p, "func index %d read fail", i); + if (!cursor_push_byte(&p->mem, i_ref_func)) + return parse_err(p, "push ref_func instr oob for %d", i); + if (!leb128_write(&p->mem, index)) + return parse_err(p, "push ref_func int index oob for %d", i); + if (!cursor_push_byte(&p->mem, i_end)) + return parse_err(p, "push i_end for init %d", i); + + expr->code_len = p->mem.p - expr->code; + } + + return 1; +} + + static int parse_element(struct wasm_parser *p, struct elem *elem) { u8 tag = 0; - unsigned int i; (void)elem; if (!pull_byte(&p->cur, &tag)) @@ -1610,17 +1955,9 @@ static int parse_element(struct wasm_parser *p, struct elem *elem) if (!parse_expr(p, &elem->offset)) return parse_err(p, "elem 0x00 offset expr"); - if (!parse_vector(p, - sizeof(*elem->func_indices), - &elem->num_func_indices, - (void**)&elem->func_indices)) { - return parse_err(p, "elem 0x00 func indices"); - } - - for (i = 0; i < elem->num_func_indices; i++) { - if (!leb128_read(&p->cur, &elem->func_indices[i])) - return parse_err(p, "func index %d read fail", i); - } + // func inits + if (!parse_elem_func_inits(p, elem)) + return parse_err(p, "generate func index exprs"); elem->mode = elem_mode_active; elem->tableidx = 0; @@ -3135,6 +3472,7 @@ static int parse_instr(struct expr_parser *p, u8 tag, struct instr *op) case i_br_if: case i_i32_const: case i_i64_const: + case i_ref_func: return leb128_read(p->code, (unsigned int*)&op->integer); case i_i32_load: @@ -3516,22 +3854,11 @@ static INLINE int interp_br_if(struct wasm_interp *interp, int ind) return 1; } -static INLINE u8 *global_init_state(struct wasm_interp *interp, int ind) -{ - if (ind >= interp->module_inst.num_globals) { - interp_error(interp, "global ind %d oob", ind); - return NULL; - } - - return &interp->module_inst.globals_init[ind]; -} - static struct val *get_global(struct wasm_interp *interp, int ind) { struct globalsec *globsec; struct global *global; struct global_inst *global_inst; - u8 *init; if (unlikely(!was_section_parsed(interp->module, section_global))) { interp_error(interp, @@ -3541,33 +3868,18 @@ static struct val *get_global(struct wasm_interp *interp, int ind) globsec = &interp->module->global_section; - if (unlikely(ind >= interp->module_inst.num_globals)) { - interp_error(interp, "invalid global index %d (max %d)", ind, - globsec->num_globals-1); - return NULL; - } - - global_inst = &interp->module_inst.globals[ind]; - - if (unlikely(!(init = global_init_state(interp, ind)))) { - interp_error(interp, - "couldn't get global init state for global %d", ind); - return NULL; - } - - /* global is already initialized, return it */ - if (*init == 1) { - return &global_inst->val; + if (unlikely(ind >= interp->module_inst.num_globals)) { + interp_error(interp, "invalid global index %d (max %d)", ind, + globsec->num_globals-1); + return NULL; } - /* initialize global then return it */ global = &interp->module->global_section.globals[ind]; + global_inst = &interp->module_inst.globals[ind]; /* copy initialized global from module to global instance */ memcpy(&global_inst->val, &global->val, sizeof(global_inst->val)); - *init = 1; - return &global_inst->val; } @@ -3861,261 +4173,30 @@ static int interp_i32_shl(struct wasm_interp *interp) return interp_error(interp, "binop prep"); } - c.num.i32 = lhs.num.i32 << rhs.num.i32; - return stack_pushval(interp, &c); -} - -#ifdef DEBUG -static void print_linestack(struct cursor *stack) -{ - struct val *val; - int first = 1; - - val = (struct val*)stack->p; - - while (--val >= (struct val*)stack->start) { - if (first) { - first = 0; - } else { - printf(", "); - } - print_val(val); - } - - printf("\n"); -} - -static const char *show_instr(struct instr *instr) -{ - struct cursor buf; - static char buffer[64]; - static char tmp[32]; - int len, i; - - buffer[sizeof(buffer)-1] = 0; - make_cursor((u8*)buffer, (u8*)buffer + sizeof(buffer) - 1, &buf); - - cursor_push_str(&buf, instr_name(instr->tag)); - len = buf.p - buf.start; - - for (i = 0; i < 14-len; i++) - cursor_push_byte(&buf, ' '); - - switch (instr->tag) { - // two-byte instrs - case i_memory_size: - case i_memory_grow: - sprintf(tmp, "0x%02x", instr->memidx); - cursor_push_str(&buf, tmp); - break; - - case i_block: - case i_loop: - case i_if: - break; - - case i_else: - case i_end: - break; - - case i_call: - case i_local_get: - case i_local_set: - case i_local_tee: - case i_global_get: - case i_global_set: - case i_br: - case i_br_if: - case i_i32_const: - case i_i64_const: - sprintf(tmp, "%d", instr->integer); - cursor_push_str(&buf, tmp); - break; - - case i_i32_load: - case i_i64_load: - case i_f32_load: - case i_f64_load: - case i_i32_load8_s: - case i_i32_load8_u: - case i_i32_load16_s: - case i_i32_load16_u: - case i_i64_load8_s: - case i_i64_load8_u: - case i_i64_load16_s: - case i_i64_load16_u: - case i_i64_load32_s: - case i_i64_load32_u: - case i_i32_store: - case i_i64_store: - case i_f32_store: - case i_f64_store: - case i_i32_store8: - case i_i32_store16: - case i_i64_store8: - case i_i64_store16: - case i_i64_store32: - sprintf(tmp, "%d %d", instr->memarg.offset, instr->memarg.align); - cursor_push_str(&buf, tmp); - break; - - case i_br_table: - break; - - case i_call_indirect: - sprintf(tmp, "%d %d", instr->call_indirect.typeidx, - instr->call_indirect.tableidx); - cursor_push_str(&buf, tmp); - break; - - case i_f32_const: - sprintf(tmp, "%f", instr->fp_single); - cursor_push_str(&buf, tmp); - break; - - case i_f64_const: - sprintf(tmp, "%f", instr->fp_double); - cursor_push_str(&buf, tmp); - break; - - // single-tag ops - case i_unreachable: - case i_nop: - case i_return: - case i_drop: - case i_select: - case i_i32_eqz: - case i_i32_eq: - case i_i32_ne: - case i_i32_lt_s: - case i_i32_lt_u: - case i_i32_gt_s: - case i_i32_gt_u: - case i_i32_le_s: - case i_i32_le_u: - case i_i32_ge_s: - case i_i32_ge_u: - case i_i64_eqz: - case i_i64_eq: - case i_i64_ne: - case i_i64_lt_s: - case i_i64_lt_u: - case i_i64_gt_s: - case i_i64_gt_u: - case i_i64_le_s: - case i_i64_le_u: - case i_i64_ge_s: - case i_i64_ge_u: - case i_f32_eq: - case i_f32_ne: - case i_f32_lt: - case i_f32_gt: - case i_f32_le: - case i_f32_ge: - case i_f64_eq: - case i_f64_ne: - case i_f64_lt: - case i_f64_gt: - case i_f64_le: - case i_f64_ge: - case i_i32_clz: - case i_i32_add: - case i_i32_sub: - case i_i32_mul: - case i_i32_div_s: - case i_i32_div_u: - case i_i32_rem_s: - case i_i32_rem_u: - case i_i32_and: - case i_i32_or: - case i_i32_xor: - case i_i32_shl: - case i_i32_shr_s: - case i_i32_shr_u: - case i_i32_rotl: - case i_i32_rotr: - case i_i64_clz: - case i_i64_ctz: - case i_i64_popcnt: - case i_i64_add: - case i_i64_sub: - case i_i64_mul: - case i_i64_div_s: - case i_i64_div_u: - case i_i64_rem_s: - case i_i64_rem_u: - case i_i64_and: - case i_i64_or: - case i_i64_xor: - case i_i64_shl: - case i_i64_shr_s: - case i_i64_shr_u: - case i_i64_rotl: - case i_i64_rotr: - case i_f32_abs: - case i_f32_neg: - case i_f32_ceil: - case i_f32_floor: - case i_f32_trunc: - case i_f32_nearest: - case i_f32_sqrt: - case i_f32_add: - case i_f32_sub: - case i_f32_mul: - case i_f32_div: - case i_f32_min: - case i_f32_max: - case i_f32_copysign: - case i_f64_abs: - case i_f64_neg: - case i_f64_ceil: - case i_f64_floor: - case i_f64_trunc: - case i_f64_nearest: - case i_f64_sqrt: - case i_f64_add: - case i_f64_sub: - case i_f64_mul: - case i_f64_div: - case i_f64_min: - case i_f64_max: - case i_f64_copysign: - case i_i32_wrap_i64: - case i_i32_trunc_f32_s: - case i_i32_trunc_f32_u: - case i_i32_trunc_f64_s: - case i_i32_trunc_f64_u: - case i_i64_extend_i32_s: - case i_i64_extend_i32_u: - case i_i64_trunc_f32_s: - case i_i64_trunc_f32_u: - case i_i64_trunc_f64_s: - case i_i64_trunc_f64_u: - case i_f32_convert_i32_s: - case i_f32_convert_i32_u: - case i_f32_convert_i64_s: - case i_f32_convert_i64_u: - case i_f32_demote_f64: - case i_f64_convert_i32_s: - case i_f64_convert_i32_u: - case i_f64_convert_i64_s: - case i_f64_convert_i64_u: - case i_f64_promote_f32: - case i_i32_reinterpret_f32: - case i_i64_reinterpret_f64: - case i_f32_reinterpret_i32: - case i_f64_reinterpret_i64: - case i_i32_extend8_s: - case i_i32_extend16_s: - case i_i64_extend8_s: - case i_i64_extend16_s: - case i_i64_extend32_s: - break; + c.num.i32 = lhs.num.i32 << rhs.num.i32; + return stack_pushval(interp, &c); +} + +#ifdef DEBUG +static void print_linestack(struct cursor *stack) +{ + struct val *val; + int first = 1; + + val = (struct val*)stack->p; + + while (--val >= (struct val*)stack->start) { + if (first) { + first = 0; + } else { + printf(", "); + } + print_val(val); } - cursor_push_byte(&buf, 0); - return buffer; + printf("\n"); } + #endif static int interp_extend(struct wasm_interp *interp, enum valtype to, @@ -4356,6 +4437,131 @@ static enum interp_end interp_code_end(struct wasm_interp *interp, return interp_end_next; } +static int interp_table_set(struct wasm_interp *interp, int tableidx) +{ + struct table_inst *table; + struct val val; + int ind; + + if (unlikely(tableidx >= interp->module_inst.num_tables)) { + return interp_error(interp, "tableidx oob %d (max %d)", + tableidx, + interp->module_inst.num_tables + 1); + } + + table = &interp->module_inst.tables[tableidx]; + + if (unlikely(!stack_pop_ref(interp, &val))) { + return interp_error(interp, "pop ref"); + } + + if (unlikely(!stack_pop_i32(interp, &ind))) { + return interp_error(interp, "pop elem index"); + } + + if (ind >= table->num_refs) { + return interp_error(interp, "invalid index %d for table %d (max %d)", + ind, tableidx, + interp->module_inst.num_tables); + } + + if (table->reftype != (enum reftype)val.type) { + return interp_error(interp, "can't store %s ref in %s table", + valtype_name(val.type), + valtype_name((enum valtype)table->reftype)); + } + + memcpy(&table->refs[ind], &val.ref, sizeof(struct refval)); + + return 1; +} + +static int interp_table_init(struct wasm_interp *interp, struct table_init *t) +{ + struct elem_inst *elem_inst; + struct table_inst *table; + int n, s, d; + + if (unlikely(t->tableidx >= interp->module_inst.num_tables)) { + return interp_error(interp, "tableidx oob %d (max %d)", + t->tableidx, + interp->module_inst.num_tables + 1); + } + + table = &interp->module_inst.tables[t->tableidx]; + + // TODO: elem addr ? + if (unlikely(t->elemidx >= interp->module_inst.num_elements)) { + return interp_error(interp, "elemidx oob %d (max %d)", + t->elemidx, + interp->module_inst.num_elements + 1); + } + + if (unlikely(!stack_pop_i32(interp, &n))) { + return interp_error(interp, "pop n"); + } + + if (unlikely(!stack_pop_i32(interp, &s))) { + return interp_error(interp, "pop s"); + } + + if (unlikely(!stack_pop_i32(interp, &d))) { + return interp_error(interp, "pop d"); + } + + if (unlikely(s + n > interp->module_inst.num_elements)) { + return interp_error(interp, "index oob elem.elem s+n %d (max %d)", + s + n, + interp->module_inst.num_elements + 1); + } + + if (unlikely(d + n > table->num_refs)) { + return interp_error(interp, "index oob tab.elem d+n %d (max %d)", + d + n, + table->num_refs); + } + + if (n == 0) { + return 1; + } + + elem_inst = &interp->module_inst.elements[s]; + + if (unlikely(!stack_push_i32(interp, d))) { + return interp_error(interp, "push d"); + } + + if (unlikely(!stack_pushval(interp, &elem_inst->ref))) { + return interp_error(interp, "push ref"); + } + + if (unlikely(!interp_table_set(interp, t->tableidx))) { + return interp_error(interp, "table set"); + } + + if (unlikely(!stack_push_i32(interp, d+1))) { + return interp_error(interp, "push d+1"); + } + + if (unlikely(!stack_push_i32(interp, s+1))) { + return interp_error(interp, "push s+1"); + } + + if (unlikely(!stack_push_i32(interp, n-1))) { + return interp_error(interp, "push n-1"); + } + + return interp_table_init(interp, t); +} + +static int interp_elem_drop(struct wasm_interp *interp, int elemidx) +{ + (void)interp; + (void)elemidx; + // we don't really need to do anything here... + return 1; +} + static int interp_code(struct wasm_interp *interp) { struct instr instr; @@ -4495,6 +4701,7 @@ static int alloc_tables(struct wasm_interp *interp) inst->reftype = t->reftype; inst->num_refs = t->limits.min; size = sizeof(struct refval) * t->limits.min; + if (!(inst->refs = cursor_alloc(&interp->mem, size))) { return interp_error(interp, "couldn't alloc table inst %d/%d", @@ -4524,7 +4731,7 @@ static int calculate_locals_size(struct module *module) static int alloc_locals(struct module *module, struct cursor *mem, struct errors *errs) { - int i, num_locals, size; + int i, num_locals, size, sizes = 0; struct func *func; for (i = 0; i < module->num_funcs; i++) { @@ -4535,14 +4742,227 @@ static int alloc_locals(struct module *module, struct cursor *mem, 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); } } + debug("alloc_locals sizes %d\n", sizes); + + return 1; +} + +static int init_element(struct wasm_interp *interp, struct expr *init, + struct elem_inst *elem_inst) +{ + if (!eval_const_val(init, &interp->errors, &interp->stack, &elem_inst->ref)) { + return interp_error(interp, "failed to eval element init expr"); + } + return 1; +} + +static int init_table(struct wasm_interp *interp, struct elem *elem, + int elemidx, int num_elems) +{ + struct table_init t; + + if (elem->tableidx != 0) { + return interp_error(interp, + "tableidx should be 0 for elem %d", elemidx); + } + + if (!eval_const_expr(&elem->offset, &interp->errors, &interp->stack)) { + return interp_error(interp, "failed to eval elem offset expr"); + } + + if (!stack_push_i32(interp, 0)) { + return interp_error(interp, "push 0 when init element"); + } + + if (!stack_push_i32(interp, num_elems)) { + return interp_error(interp, "push num_elems in init element"); + } + + t.tableidx = elem->tableidx; + t.elemidx = elemidx; + + if (!interp_table_init(interp, &t)) { + return interp_error(interp, "table init"); + } + + if (!interp_elem_drop(interp, elemidx)) { + return interp_error(interp, "drop elem"); + } + + return 1; +} + +static int init_global(struct wasm_interp *interp, struct global *global, + struct global_inst *global_inst) +{ + if (!eval_const_val(&global->init, &interp->errors, &interp->stack, + &global_inst->val)) { + return interp_error(interp, "eval const expr"); + } + + if (cursor_top(&interp->stack, sizeof(struct val))) { + return interp_error(interp, "stack not empty"); + } + + return 1; +} + +static int init_globals(struct wasm_interp *interp) +{ + struct global *globals, *global; + struct global_inst *global_insts, *global_inst; + int i; + + if (!was_section_parsed(interp->module, section_global)) { + // nothing to init + return 1; + } + + globals = interp->module->global_section.globals; + global_insts = interp->module_inst.globals; + + for (i = 0; i < interp->module->global_section.num_globals; i++) { + global = &globals[i]; + global_inst = &global_insts[i]; + + if (!init_global(interp, global, global_inst)) { + return interp_error(interp, "global init"); + } + } + + return 1; +} + +static int count_element_insts(struct module *module) +{ + struct elem *elems, *elem; + int i, size = 0; + + if (!was_section_parsed(module, section_element)) + return 0; + + elems = module->element_section.elements; + + for (i = 0; i < module->element_section.num_elements; i++) { + elem = &elems[i]; + + if (elem->mode != elem_mode_active) + continue; + + size += elem->num_inits; + } + + return size; +} + + +static int init_tables(struct wasm_interp *interp) +{ + struct elem *elem; + struct elem_inst *inst; + int i; + + for (i = 0; i < interp->module_inst.num_elements; i++) { + inst = &interp->module_inst.elements[i]; + elem = &interp->module->element_section.elements[inst->elem]; + + if (!init_table(interp, elem, i, + interp->module_inst.num_elements)) { + return interp_error(interp, "init table failed"); + } + } + + return 1; +} + +static int init_elements(struct wasm_interp *interp) +{ + struct elem *elems, *elem; + struct elem_inst *inst; + struct expr *init; + int count = 0; + int i, j; + + if (!was_section_parsed(interp->module, section_element)) + return 0; + + elems = interp->module->element_section.elements; + + for (i = 0; i < interp->module->element_section.num_elements; i++) { + elem = &elems[i]; + + if (elem->mode != elem_mode_active) + continue; + + for (j = 0; j < elem->num_inits; j++, count++) { + init = &elem->inits[j]; + + assert(count < interp->module_inst.num_elements); + inst = &interp->module_inst.elements[count]; + inst->elem = i; + inst->init = j; + + if (!init_element(interp, init, inst)) { + return interp_error(interp, "init element %d", j); + } + } + + } + + return 1; +} + +// https://webassembly.github.io/spec/core/exec/modules.html#instantiation +static int instantiate_module(struct wasm_interp *interp, int func) +{ + //TODO:Assert module is valid with external types classifying its imports + + // TODO: If the number # of imports is not equal to the number of provided external values then fail + + /* + if (!push_aux_callframe(interp)) { + return interp_error(interp, + "failed to pushed aux callframe?? " + "ok if this happens seriously wtf why am I even" + " writing this error message..)"; + } + */ + + memset(interp->module_inst.globals, 0, + interp->module_inst.num_globals * + sizeof(*interp->module_inst.globals)); + + memset(interp->module_inst.globals_init, 0, + interp->module_inst.num_globals); + + if (func == -1) { + return interp_error(interp, "no start function found"); + } else { + interp->module_inst.start_fn = func; + } + + if (!init_elements(interp)) { + return interp_error(interp, "elements init"); + } + + if (!init_tables(interp)) { + return interp_error(interp, "table init"); + } + + if (!init_globals(interp)) { + return interp_error(interp, "globals init"); + } + return 1; } @@ -4552,9 +4972,9 @@ int wasm_interp_init(struct wasm_interp *interp, struct module *module) unsigned int ok, fns, errors_size, stack_size, locals_size, callframes_size, resolver_size, labels_size, num_labels_size, - labels_capacity, num_labels_elemsize, memsize, memory_pages_size, + labels_capacity, memsize, memory_pages_size, resolver_offsets_size, num_mems, globals_size, num_globals, - global_init_size, tables_size; + tables_size, elems_size, num_elements; memset(interp, 0, sizeof(*interp)); @@ -4568,7 +4988,6 @@ int wasm_interp_init(struct wasm_interp *interp, struct module *module) //stack = calloc(1, STACK_SPACE); fns = module->num_funcs; labels_capacity = fns * MAX_LABELS; - num_labels_elemsize = sizeof(u16); num_mems = was_section_parsed(module, section_memory)? module->memory_section.num_mems : 0; @@ -4585,7 +5004,9 @@ int wasm_interp_init(struct wasm_interp *interp, struct module *module) callframes_size = sizeof(struct callframe) * 0xFF; resolver_size = sizeof(struct resolver) * MAX_LABELS; globals_size = sizeof(struct global_inst) * num_globals; - global_init_size = num_globals; + + num_elements = count_element_insts(module); + elems_size = num_elements * sizeof(struct elem_inst); locals_size = calculate_locals_size(module); tables_size = calculate_tables_size(module); @@ -4594,8 +5015,6 @@ int wasm_interp_init(struct wasm_interp *interp, struct module *module) return 0; } - interp->module_inst.num_globals = num_globals; - memory_pages_size = 256 * WASM_PAGE_SIZE; memsize = @@ -4605,11 +5024,12 @@ int wasm_interp_init(struct wasm_interp *interp, struct module *module) resolver_size + callframes_size + globals_size + - global_init_size + + num_globals + labels_size + num_labels_size + locals_size + - tables_size + tables_size + + elems_size ; mem = calloc(1, memsize); @@ -4642,6 +5062,8 @@ int wasm_interp_init(struct wasm_interp *interp, struct module *module) ok = ok && cursor_slice(&interp->mem, &interp->callframes, callframes_size); assert(interp->mem.p - start == callframes_size); + interp->module_inst.num_globals = num_globals; + start = interp->mem.p; ok = ok && (interp->module_inst.globals = cursor_alloc(&interp->mem, globals_size)); assert(interp->mem.p - start == globals_size); @@ -4655,17 +5077,24 @@ int wasm_interp_init(struct wasm_interp *interp, struct module *module) assert(interp->mem.p - start == labels_size); start = interp->mem.p; - ok = ok && cursor_slice(&interp->mem, &interp->num_labels, num_labels_size); - assert(interp->mem.p - start == num_labels_size); - - start = interp->mem.p; ok = ok && alloc_tables(interp); assert(interp->mem.p - start == tables_size); start = interp->mem.p; + ok = ok && cursor_slice(&interp->mem, &interp->num_labels, num_labels_size); + assert(interp->mem.p - start == num_labels_size); + + start = interp->mem.p; ok = ok && alloc_locals(interp->module, &interp->mem, &interp->errors); assert(interp->mem.p - start == locals_size); + interp->module_inst.num_elements = num_elements; + + start = interp->mem.p; + ok = ok && (interp->module_inst.elements = + cursor_alloc(&interp->mem, elems_size)); + assert(interp->mem.p - start == elems_size); + /* init memory pages */ assert((interp->mem.end - interp->mem.start) == memsize); @@ -4709,30 +5138,6 @@ static int reset_memory(struct wasm_interp *interp) return 1; } -/* -static int init_tables(struct wasm_interp *interp) -{ - struct element *elem, *elems; - - if (!(was_section_parsed(interp->module, section_element) && - was_section_parsed(interp->module, section_table))) { - // nothing to init - return 1; - } - - elems = interp->module->element_section.elements; - for (i = 0; i < interp->module->element_section.num_elements; i++) { - elem = &elems[i]; - - if (elem->mode == elem_mode_passive || - elem->mode == elem_mode_declarative) { - continue; - } - - } -} -*/ - int interp_wasm_module(struct wasm_interp *interp) { int func; @@ -4751,36 +5156,24 @@ int interp_wasm_module(struct wasm_interp *interp) reset_cursor(&interp->errors.cur); reset_cursor(&interp->callframes); - //init_tables(interp); - if (!reset_memory(interp)) return interp_error(interp, "reset memory"); - memset(interp->module_inst.globals, 0, - interp->module_inst.num_globals * - sizeof(*interp->module_inst.globals)); - - memset(interp->module_inst.globals_init, 0, - interp->module_inst.num_globals); - // don't reset labels for perf! //interp->mem.p = interp->mem.start; - func = interp->module_inst.start_fn != -1 - ? interp->module_inst.start_fn + func = interp->module_inst.start_fn != -1 + ? interp->module_inst.start_fn : find_start_function(interp->module); - if (func == -1) { - return interp_error(interp, "no start function found"); - } else { - interp->module_inst.start_fn = func; - } + if (!instantiate_module(interp, func)) + return interp_error(interp, "instantiate module"); debug("found start function %s (%d)\n", get_function_name(interp->module, func), func); - if (!prepare_call(interp, func)) { + if (!prepare_call(interp, interp->module_inst.start_fn)) { return interp_error(interp, "preparing start function"); } diff --git a/src/wasm.h b/src/wasm.h @@ -82,12 +82,6 @@ struct table { struct limits limits; }; -struct table_inst { - struct refval *refs; - enum reftype reftype; - int num_refs; -}; - struct tablesec { struct table *tables; int num_tables; @@ -104,13 +98,47 @@ struct expr { int code_len; }; +struct refval { + int addr; +}; + +struct table_inst { + struct refval *refs; + enum reftype reftype; + int num_refs; +}; + +struct numval { + union { + int i32; + u64 i64; + float f32; + double f64; + }; +}; + +struct val { + enum valtype type; + union { + struct numval num; + struct refval ref; + }; +}; + +struct elem_inst { + struct val ref; + u16 elem; + u16 init; +}; + struct elem { struct expr offset; int tableidx; - unsigned int *func_indices; - unsigned int num_func_indices; + struct expr *inits; + int num_inits; enum elem_mode mode; enum reftype reftype; + struct val val; }; struct customsec { @@ -183,27 +211,6 @@ struct importsec { int num_imports; }; -struct refval { - int addr; -}; - -struct numval { - union { - int i32; - u64 i64; - float f32; - double f64; - }; -}; - -struct val { - enum valtype type; - union { - struct numval num; - struct refval ref; - }; -}; - struct global { struct globaltype type; struct expr init; @@ -463,6 +470,10 @@ enum instr_tag { i_i64_extend16_s = 0xc3, i_i64_extend32_s = 0xc4, + i_ref_null = 0xD0, + i_ref_is_null = 0xD1, + i_ref_func = 0xD2, + /* TODO: more instrs */ }; @@ -503,6 +514,11 @@ struct call_indirect { int typeidx; }; +struct table_init { + int tableidx; + int elemidx; +}; + struct instr { enum instr_tag tag; int pos; @@ -590,14 +606,15 @@ struct global_inst { struct module_inst { struct table_inst *tables; - int num_tables; - struct global_inst *globals; - unsigned char *globals_init; - int num_globals; + struct elem_inst *elements; + int num_tables; + int num_globals; + int num_elements; int start_fn; + unsigned char *globals_init; }; struct wasm_interp {