diff options
-rw-r--r-- | src/wasm-binary-reader-interpreter.c | 1428 | ||||
-rw-r--r-- | src/wasm-interpreter.c | 57 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 5 |
3 files changed, 419 insertions, 1071 deletions
diff --git a/src/wasm-binary-reader-interpreter.c b/src/wasm-binary-reader-interpreter.c index bcc8193d..661caa58 100644 --- a/src/wasm-binary-reader-interpreter.c +++ b/src/wasm-binary-reader-interpreter.c @@ -43,9 +43,9 @@ #define CHECK_DEPTH(ctx, depth) \ do { \ - if ((depth) >= (ctx)->typecheck_label_stack.size) { \ + if ((depth) >= (ctx)->label_stack.size) { \ print_error((ctx), "invalid depth: %d (max %" PRIzd ")", (depth), \ - ((ctx)->typecheck_label_stack.size)); \ + ((ctx)->label_stack.size)); \ return WASM_ERROR; \ } \ } while (0) @@ -99,8 +99,7 @@ static const char* s_opcode_name[] = { /* clang-format off */ WASM_FOREACH_OPCODE(V) [WASM_OPCODE_ALLOCA] = "alloca", - [WASM_OPCODE_DISCARD] = "discard", - [WASM_OPCODE_DISCARD_KEEP] = "discard_keep", + [WASM_OPCODE_DROP_KEEP] = "drop_keep", /* clang-format on */ }; #undef V @@ -121,31 +120,14 @@ static const char* s_label_type_name[] = { "func", "block", "loop", "if", "else", }; -/* used for the typecheck pass */ -typedef struct TypecheckLabel { +typedef struct Label { LabelType label_type; WasmType type; uint32_t expr_stack_size; - uint32_t expr_index; /* the index of the starting op (block/loop/if) */ - uint32_t last_expr_index; /* last index of the true branch in an if */ -} TypecheckLabel; -WASM_DEFINE_VECTOR(typecheck_label, TypecheckLabel); - -/* used for the emit pass */ -typedef struct EmitLabel { - LabelType label_type; uint32_t offset; /* branch location in the istream */ uint32_t fixup_offset; - uint32_t value_stack_size; - WasmBool has_value; -} EmitLabel; -WASM_DEFINE_VECTOR(emit_label, EmitLabel); - -typedef struct ExprNode { - uint32_t index; - WasmType type; -} ExprNode; -WASM_DEFINE_VECTOR(expr_node, ExprNode); +} Label; +WASM_DEFINE_VECTOR(label, Label); typedef struct InterpreterFunc { uint32_t sig_index; @@ -164,15 +146,11 @@ typedef struct Context { WasmInterpreterModule* module; InterpreterFuncArray funcs; InterpreterFunc* current_func; - ExprNodeVector expr_stack; - TypecheckLabelVector typecheck_label_stack; - EmitLabelVector emit_label_stack; + WasmTypeVector expr_stack; + LabelVector label_stack; Uint32VectorVector func_fixups; Uint32VectorVector depth_fixups; - Uint32Vector discarded_exprs; /* bitset */ - uint32_t value_stack_size; uint32_t depth; - uint32_t expr_count; uint32_t start_func_index; WasmMemoryWriter istream_writer; uint32_t istream_offset; @@ -180,13 +158,13 @@ typedef struct Context { uint32_t table_offset; } Context; -static TypecheckLabel* get_typecheck_label(Context* ctx, uint32_t depth) { - assert(depth < ctx->typecheck_label_stack.size); - return &ctx->typecheck_label_stack.data[depth]; +static Label* get_label(Context* ctx, uint32_t depth) { + assert(depth < ctx->label_stack.size); + return &ctx->label_stack.data[depth]; } -static TypecheckLabel* top_typecheck_label(Context* ctx) { - return get_typecheck_label(ctx, ctx->typecheck_label_stack.size - 1); +static Label* top_label(Context* ctx) { + return get_label(ctx, ctx->label_stack.size - 1); } static uint32_t get_value_count(WasmType result_type) { @@ -194,18 +172,6 @@ static uint32_t get_value_count(WasmType result_type) { : 1; } -static WasmBool is_expr_discarded(Context* ctx, uint32_t expr_index) { - uint32_t word_index = expr_index >> 5; - if (word_index >= ctx->discarded_exprs.size) - return WASM_FALSE; - - uint32_t bit_index = expr_index & 31; - assert(word_index < ctx->discarded_exprs.size); - return (ctx->discarded_exprs.data[word_index] & (1U << bit_index)) - ? WASM_TRUE - : WASM_FALSE; -} - static void on_error(uint32_t offset, const char* message, void* user_data); static void print_error(Context* ctx, const char* format, ...) { @@ -250,14 +216,142 @@ static WasmType get_global_index_type(Context* ctx, uint32_t global_index) { return ctx->module->globals.data[global_index].type; } -static uint32_t translate_depth(Context* ctx, size_t size, uint32_t depth) { - assert(depth < size); - return size - 1 - depth; +static uint32_t translate_local_index(Context* ctx, uint32_t local_index) { + assert(local_index < ctx->expr_stack.size); + return ctx->expr_stack.size - local_index; } -static uint32_t translate_local_index(Context* ctx, uint32_t local_index) { - assert(local_index < ctx->value_stack_size); - return ctx->value_stack_size - local_index; +static uint32_t get_istream_offset(Context* ctx) { + return ctx->istream_offset; +} + +static WasmResult emit_data_at(Context* ctx, + size_t offset, + const void* data, + size_t size) { + return ctx->istream_writer.base.write_data( + offset, data, size, ctx->istream_writer.base.user_data); +} + +static WasmResult emit_data(Context* ctx, const void* data, size_t size) { + CHECK_RESULT(emit_data_at(ctx, ctx->istream_offset, data, size)); + ctx->istream_offset += size; + return WASM_OK; +} + +static WasmResult emit_opcode(Context* ctx, WasmOpcode opcode) { + return emit_data(ctx, &opcode, sizeof(uint8_t)); +} + +static WasmResult emit_i8(Context* ctx, uint8_t value) { + return emit_data(ctx, &value, sizeof(value)); +} + +static WasmResult emit_i32(Context* ctx, uint32_t value) { + return emit_data(ctx, &value, sizeof(value)); +} + +static WasmResult emit_i64(Context* ctx, uint64_t value) { + return emit_data(ctx, &value, sizeof(value)); +} + +static WasmResult emit_i32_at(Context* ctx, uint32_t offset, uint32_t value) { + return emit_data_at(ctx, offset, &value, sizeof(value)); +} + +static WasmResult emit_drop_keep(Context* ctx, uint32_t drop, uint8_t keep) { + assert(drop != UINT32_MAX); + assert(keep <= 1); + if (drop > 0) { + if (drop == 1 && keep == 0) { + LOGF("%3" PRIzd ": drop\n", ctx->expr_stack.size); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_DROP)); + } else { + LOGF("%3" PRIzd ": drop_keep %u %u\n", ctx->expr_stack.size, drop, keep); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_DROP_KEEP)); + CHECK_RESULT(emit_i32(ctx, drop)); + CHECK_RESULT(emit_i8(ctx, keep)); + } + } + return WASM_OK; +} + +static WasmResult emit_return(Context* ctx, WasmType result_type) { + /* drop the locals and params, but keep the return value, if any */ + uint32_t keep_count = get_value_count(result_type); + assert(ctx->expr_stack.size >= keep_count); + uint32_t drop_count = ctx->expr_stack.size - keep_count; + CHECK_RESULT(emit_drop_keep(ctx, drop_count, keep_count)); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_RETURN)); + return WASM_OK; +} + +static WasmResult append_fixup(Context* ctx, + Uint32VectorVector* fixups_vector, + uint32_t index) { + if (index >= fixups_vector->size) + wasm_resize_uint32_vector_vector(ctx->allocator, fixups_vector, index + 1); + Uint32Vector* fixups = &fixups_vector->data[index]; + uint32_t offset = get_istream_offset(ctx); + wasm_append_uint32_value(ctx->allocator, fixups, &offset); + return WASM_OK; +} + +static WasmResult emit_br_offset(Context* ctx, + uint32_t depth, + uint32_t offset) { + if (offset == WASM_INVALID_OFFSET) + CHECK_RESULT(append_fixup(ctx, &ctx->depth_fixups, depth)); + CHECK_RESULT(emit_i32(ctx, offset)); + return WASM_OK; +} + +static WasmResult emit_br(Context* ctx, uint8_t arity, uint32_t depth) { + Label* label = get_label(ctx, depth); + assert(ctx->expr_stack.size >= label->expr_stack_size + arity); + uint32_t drop_count = (ctx->expr_stack.size - label->expr_stack_size) - arity; + CHECK_RESULT(emit_drop_keep(ctx, drop_count, arity)); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_BR)); + CHECK_RESULT(emit_br_offset(ctx, depth, label->offset)); + return WASM_OK; +} + +static WasmResult emit_br_table_offset(Context* ctx, + uint8_t arity, + uint32_t depth) { + Label* label = get_label(ctx, depth); + assert(ctx->expr_stack.size >= label->expr_stack_size + arity); + uint32_t drop_count = (ctx->expr_stack.size - label->expr_stack_size) - arity; + CHECK_RESULT(emit_br_offset(ctx, depth, label->offset)); + CHECK_RESULT(emit_i32(ctx, drop_count)); + CHECK_RESULT(emit_i8(ctx, arity)); + return WASM_OK; +} + +static WasmResult fixup_top_label(Context* ctx, uint32_t offset) { + uint32_t top = ctx->label_stack.size - 1; + if (top >= ctx->depth_fixups.size) { + /* nothing to fixup */ + return WASM_OK; + } + + Uint32Vector* fixups = &ctx->depth_fixups.data[top]; + uint32_t i; + for (i = 0; i < fixups->size; ++i) + CHECK_RESULT(emit_i32_at(ctx, fixups->data[i], offset)); + /* reduce the size to 0 in case this gets reused. Keep the allocations for + * later use */ + fixups->size = 0; + return WASM_OK; +} + +static WasmResult emit_func_offset(Context* ctx, + InterpreterFunc* func, + uint32_t func_index) { + if (func->offset == WASM_INVALID_OFFSET) + CHECK_RESULT(append_fixup(ctx, &ctx->func_fixups, func_index)); + CHECK_RESULT(emit_i32(ctx, func->offset)); + return WASM_OK; } static void on_error(uint32_t offset, const char* message, void* user_data) { @@ -510,42 +604,6 @@ static WasmResult on_function_bodies_count(uint32_t count, void* user_data) { return WASM_OK; } -/* defined below so they can reference s_binary_reader{,_emit} */ -static WasmResult begin_function_body_pass(uint32_t index, - uint32_t pass, - void* user_data); -static WasmResult end_function_body_pass(uint32_t index, - uint32_t pass, - void* user_data); - -/* Code generation happens per function in two passes. First, the function is - * type-checked, bottom up. While this is done, a bitset ("discarded_exprs") is - * maintained. - * - * There are three different but related meanings for a bit being set in - * |discarded_exprs|: - * - * 1) if the operator is a block, loop or if: the bit is set if the block does - * not use its value. This is used when emitting a br to determine whether its - * value should be discarded. - * - * 2) if the operator is br_if: the bit is set if the br_if has a value. This - * is used to determine whether the br_if value needs to be discarded if the - * branch is not taken. - * - * 3) otherwise: the bit is set if the expression is subsequently discarded - * (e.g. a set_local whose value is not used.) - * - * The second pass is the emit pass, where instructions are written to an - * instruction stream, to be executed by the stack machine interpreter. - * - * Two passes are needed because you can't determine whether a value is used in - * one pass. It would be possible to generate code in one pass, but it would - * use a very large stack for blocks with many discarded expressions. - */ - -/*** typecheck pass ***********************************************************/ - static WasmResult type_mismatch(Context* ctx, WasmType expected_type, WasmType type, @@ -583,81 +641,93 @@ static WasmResult unify_and_check_type(Context* ctx, return check_type(ctx, *dest_type, type, desc); } -static uint32_t translate_typecheck_depth(Context* ctx, uint32_t depth) { - return translate_depth(ctx, ctx->typecheck_label_stack.size, depth); +static uint32_t translate_depth(Context* ctx, uint32_t depth) { + assert(depth < ctx->label_stack.size); + return ctx->label_stack.size - 1 - depth; } -static void push_typecheck_label(Context* ctx, - LabelType label_type, - WasmType type) { - TypecheckLabel* label = - wasm_append_typecheck_label(ctx->allocator, &ctx->typecheck_label_stack); +static void push_label(Context* ctx, + LabelType label_type, + WasmType type, + uint32_t offset, + uint32_t fixup_offset) { + Label* label = wasm_append_label(ctx->allocator, &ctx->label_stack); label->label_type = label_type; label->type = type; label->expr_stack_size = ctx->expr_stack.size; - label->expr_index = ctx->expr_count; - LOGF(" : +depth %" PRIzd ":%s\n", ctx->typecheck_label_stack.size - 1, + label->offset = offset; + label->fixup_offset = fixup_offset; + LOGF(" : +depth %" PRIzd ":%s\n", ctx->label_stack.size - 1, s_type_names[type]); } -static void pop_typecheck_label(Context* ctx) { - LOGF(" : -depth %" PRIzd "\n", ctx->typecheck_label_stack.size - 1); - assert(ctx->typecheck_label_stack.size > 0); - ctx->typecheck_label_stack.size--; +static void pop_label(Context* ctx) { + LOGF(" : -depth %" PRIzd "\n", ctx->label_stack.size - 1); + assert(ctx->label_stack.size > 0); + ctx->label_stack.size--; + /* reduce the depth_fixups stack as well, but it may be smaller than + * label_stack so only do it conditionally. */ + if (ctx->depth_fixups.size > ctx->label_stack.size) { + uint32_t from = ctx->label_stack.size; + uint32_t to = ctx->depth_fixups.size; + uint32_t i; + for (i = from; i < to; ++i) + wasm_destroy_uint32_vector(ctx->allocator, &ctx->depth_fixups.data[i]); + ctx->depth_fixups.size = ctx->label_stack.size; + } } static void push_expr(Context* ctx, WasmType type, WasmOpcode opcode) { + /* TODO: refactor to remove opcode param */ if (type != WASM_TYPE_VOID) { - LOGF("%3" PRIzd ": push %s:%s (#%u)\n", ctx->expr_stack.size, - s_opcode_name[opcode], s_type_names[type], ctx->expr_count); - ExprNode* expr; - expr = wasm_append_expr_node(ctx->allocator, &ctx->expr_stack); - expr->index = ctx->expr_count; - expr->type = type; + Label* label = top_label(ctx); + if (ctx->expr_stack.size > label->expr_stack_size) { + WasmType top_type = ctx->expr_stack.data[ctx->expr_stack.size - 1]; + if (top_type == WASM_TYPE_ANY) + return; + } + + LOGF("%3" PRIzd ": push %s:%s\n", ctx->expr_stack.size, + s_opcode_name[opcode], s_type_names[type]); + wasm_append_type_value(ctx->allocator, &ctx->expr_stack, &type); } - ctx->expr_count++; } -static WasmType pop_expr(Context* ctx) { - assert(ctx->expr_stack.size > 0); - ExprNode* expr = &ctx->expr_stack.data[ctx->expr_stack.size - 1]; - - LOGF("%3" PRIzd ": pop %s (#%u)\n", ctx->expr_stack.size, - s_type_names[expr->type], expr->index); +static WasmType top_expr(Context* ctx) { + Label* label = top_label(ctx); + assert(ctx->expr_stack.size > label->expr_stack_size); + return ctx->expr_stack.data[ctx->expr_stack.size - 1]; +} - WasmType type = expr->type; +static WasmType pop_expr(Context* ctx) { + WasmType type = top_expr(ctx); + LOGF("%3" PRIzd ": pop %s\n", ctx->expr_stack.size, s_type_names[type]); ctx->expr_stack.size--; return type; } -static WasmType pop_expr_if(Context* ctx, WasmBool cond) { +static WasmType top_expr_if(Context* ctx, WasmBool cond) { if (cond) - return pop_expr(ctx); + return top_expr(ctx); return WASM_TYPE_VOID; } -static void set_expr_discarded(Context* ctx, uint32_t expr_index) { - LOGF(" : set_expr_discarded #%u\n", expr_index); - uint32_t word_index = expr_index >> 5; - size_t new_size = word_index + 1; - if (new_size > ctx->discarded_exprs.capacity) - wasm_reserve_uint32s(ctx->allocator, &ctx->discarded_exprs, new_size); - size_t old_size = ctx->discarded_exprs.size; - if (new_size > old_size) { - memset(&ctx->discarded_exprs.data[old_size], 0, - (new_size - old_size) * sizeof(uint32_t)); - ctx->discarded_exprs.size = new_size; +static WasmResult check_end(Context* ctx, WasmType* top_type) { + Label* label = top_label(ctx); + assert(ctx->expr_stack.size >= label->expr_stack_size); + size_t stack_size = ctx->expr_stack.size - label->expr_stack_size; + if (stack_size == 0) { + *top_type = WASM_TYPE_VOID; + return WASM_OK; } - uint32_t bit_index = expr_index & 31; - ctx->discarded_exprs.data[word_index] |= 1U << bit_index; -} + WasmType type = top_expr(ctx); + *top_type = type; + if (type == WASM_TYPE_ANY || stack_size == 1) + return WASM_OK; -static void set_expr_discarded_unless_void(Context* ctx, - uint32_t expr_index, - WasmType type) { - if (type != WASM_TYPE_VOID) - set_expr_discarded(ctx, expr_index); + print_error(ctx, "maximum arity is 1, got %" PRIzd ".", stack_size); + return WASM_ERROR; } static WasmResult begin_function_body(uint32_t index, void* user_data) { @@ -665,51 +735,53 @@ static WasmResult begin_function_body(uint32_t index, void* user_data) { InterpreterFunc* func = get_func(ctx, index); WasmInterpreterFuncSignature* sig = get_signature(ctx, func->sig_index); + func->offset = get_istream_offset(ctx); + func->local_decl_count = 0; + func->local_count = 0; + ctx->current_func = func; - ctx->expr_stack.size = 0; - ctx->typecheck_label_stack.size = 0; - ctx->discarded_exprs.size = 0; + ctx->depth_fixups.size = 0; + ctx->expr_stack.size = sig->param_types.size; + ctx->label_stack.size = 0; ctx->depth = 0; - ctx->expr_count = 0; - /* append param types */ + /* fixup function references */ uint32_t i; + Uint32Vector* fixups = &ctx->func_fixups.data[index]; + for (i = 0; i < fixups->size; ++i) + CHECK_RESULT(emit_i32_at(ctx, fixups->data[i], func->offset)); + + /* append param types */ for (i = 0; i < sig->param_types.size; ++i) { wasm_append_type_value(ctx->allocator, &func->param_and_local_types, &sig->param_types.data[i]); } /* push implicit func label (equivalent to return) */ - push_typecheck_label(ctx, LABEL_TYPE_FUNC, sig->result_type); - + push_label(ctx, LABEL_TYPE_FUNC, sig->result_type, WASM_INVALID_OFFSET, + WASM_INVALID_OFFSET); return WASM_OK; } static WasmResult end_function_body(uint32_t index, void* user_data) { Context* ctx = user_data; - uint32_t discard_max = 0; - - pop_typecheck_label(ctx); - WasmInterpreterFuncSignature* sig = get_func_signature(ctx, ctx->current_func); - if (sig->result_type == WASM_TYPE_VOID) { - /* discard all expr */ - discard_max = ctx->expr_stack.size; - } else if (ctx->expr_stack.size > 0) { - /* discard everything except the last expr */ - discard_max = ctx->expr_stack.size - 1; - } else { - return WASM_ERROR; - } - - uint32_t i; - for (i = 0; i < discard_max; ++i) { - ExprNode* expr = &ctx->expr_stack.data[i]; - set_expr_discarded_unless_void(ctx, expr->index, expr->type); - } + WasmType top_type; + CHECK_RESULT(check_end(ctx, &top_type)); + CHECK_RESULT(check_type(ctx, sig->result_type, top_type, "implicit return")); + CHECK_RESULT(emit_return(ctx, sig->result_type)); + pop_label(ctx); ctx->current_func = NULL; + ctx->expr_stack.size = 0; + return WASM_OK; +} + +static WasmResult on_local_decl_count(uint32_t count, void* user_data) { + Context* ctx = user_data; + InterpreterFunc* func = ctx->current_func; + func->local_decl_count = count; return WASM_OK; } @@ -718,96 +790,111 @@ static WasmResult on_local_decl(uint32_t decl_index, WasmType type, void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": alloca\n", ctx->expr_stack.size); InterpreterFunc* func = ctx->current_func; + func->local_count += count; uint32_t i; - for (i = 0; i < count; ++i) + for (i = 0; i < count; ++i) { wasm_append_type_value(ctx->allocator, &func->param_and_local_types, &type); + push_expr(ctx, type, WASM_OPCODE_ALLOCA); + } + + if (decl_index == func->local_decl_count - 1) { + /* last local declaration, allocate space for all locals. */ + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_ALLOCA)); + CHECK_RESULT(emit_i32(ctx, func->local_count)); + /* fixup the function label's expr_stack_size to include these values. */ + Label* label = top_label(ctx); + assert(label->label_type == LABEL_TYPE_FUNC); + label->expr_stack_size += func->local_count; + } return WASM_OK; } static WasmResult on_unary_expr(WasmOpcode opcode, void* user_data) { Context* ctx = user_data; + const char* opcode_name = s_opcode_name[opcode]; + LOGF("%3" PRIzd ": %s\n", ctx->expr_stack.size, opcode_name); WasmType value = pop_expr(ctx); - CHECK_RESULT( - check_type(ctx, s_opcode_type1[opcode], value, s_opcode_name[opcode])); + CHECK_RESULT(check_type(ctx, s_opcode_type1[opcode], value, opcode_name)); + CHECK_RESULT(emit_opcode(ctx, opcode)); push_expr(ctx, s_opcode_rtype[opcode], opcode); return WASM_OK; } static WasmResult on_binary_expr(WasmOpcode opcode, void* user_data) { Context* ctx = user_data; + const char* opcode_name = s_opcode_name[opcode]; + LOGF("%3" PRIzd ": %s\n", ctx->expr_stack.size, opcode_name); WasmType right = pop_expr(ctx); WasmType left = pop_expr(ctx); - /* TODO use opcode name here */ - CHECK_RESULT( - check_type(ctx, s_opcode_type1[opcode], left, s_opcode_name[opcode])); - CHECK_RESULT( - check_type(ctx, s_opcode_type2[opcode], right, s_opcode_name[opcode])); + CHECK_RESULT(check_type(ctx, s_opcode_type1[opcode], left, opcode_name)); + CHECK_RESULT(check_type(ctx, s_opcode_type2[opcode], right, opcode_name)); + CHECK_RESULT(emit_opcode(ctx, opcode)); push_expr(ctx, s_opcode_rtype[opcode], opcode); return WASM_OK; } static WasmResult on_block_expr(void* user_data) { Context* ctx = user_data; - LOGF("%3" PRIzd ": block (#%u)\n", ctx->expr_stack.size, ctx->expr_count); - push_typecheck_label(ctx, LABEL_TYPE_BLOCK, WASM_TYPE_ANY); - ctx->expr_count++; + LOGF("%3" PRIzd ": block\n", ctx->expr_stack.size); + push_label(ctx, LABEL_TYPE_BLOCK, WASM_TYPE_ANY, WASM_INVALID_OFFSET, + WASM_INVALID_OFFSET); return WASM_OK; } static WasmResult on_loop_expr(void* user_data) { Context* ctx = user_data; - LOGF("%3" PRIzd ": loop (#%u)\n", ctx->expr_stack.size, ctx->expr_count); - push_typecheck_label(ctx, LABEL_TYPE_LOOP, WASM_TYPE_VOID); - ctx->expr_count++; + LOGF("%3" PRIzd ": loop\n", ctx->expr_stack.size); + push_label(ctx, LABEL_TYPE_LOOP, WASM_TYPE_VOID, get_istream_offset(ctx), + WASM_INVALID_OFFSET); return WASM_OK; } static WasmResult on_if_expr(void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": if\n", ctx->expr_stack.size); WasmType cond = pop_expr(ctx); CHECK_RESULT(check_type(ctx, WASM_TYPE_I32, cond, "if")); - LOGF("%3" PRIzd ": if (#%u)\n", ctx->expr_stack.size, ctx->expr_count); - push_typecheck_label(ctx, LABEL_TYPE_IF, WASM_TYPE_ANY); - ctx->expr_count++; + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_BR_UNLESS)); + uint32_t fixup_offset = get_istream_offset(ctx); + CHECK_RESULT(emit_i32(ctx, WASM_INVALID_OFFSET)); + push_label(ctx, LABEL_TYPE_IF, WASM_TYPE_ANY, WASM_INVALID_OFFSET, + fixup_offset); return WASM_OK; } static WasmResult on_else_expr(void* user_data) { Context* ctx = user_data; - TypecheckLabel* label = top_typecheck_label(ctx); + LOGF("%3" PRIzd ": else\n", ctx->expr_stack.size); + Label* label = top_label(ctx); if (!label || label->label_type != LABEL_TYPE_IF) { print_error(ctx, "unexpected else operator"); return WASM_ERROR; } - LOGF("%3" PRIzd ": else (#%u)\n", ctx->expr_stack.size, ctx->expr_count); - - if (label->expr_stack_size < ctx->expr_stack.size) { - ExprNode* last_expr = &ctx->expr_stack.data[ctx->expr_stack.size - 1]; - label->last_expr_index = last_expr->index; - CHECK_RESULT(unify_and_check_type(ctx, &label->type, last_expr->type, - "if true branch")); - - /* discard everything except the last expr; we don't know if we'll need to - * discard that one until after we process the false branch */ - uint32_t i; - for (i = label->expr_stack_size; i < ctx->expr_stack.size - 1; ++i) { - ExprNode* expr = &ctx->expr_stack.data[i]; - set_expr_discarded_unless_void(ctx, expr->index, expr->type); - } - } + WasmType top_type; + CHECK_RESULT(check_end(ctx, &top_type)); + CHECK_RESULT( + unify_and_check_type(ctx, &label->type, top_type, "if true branch")); label->label_type = LABEL_TYPE_ELSE; + uint32_t fixup_cond_offset = label->fixup_offset; + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_BR)); + label->fixup_offset = get_istream_offset(ctx); + CHECK_RESULT(emit_i32(ctx, WASM_INVALID_OFFSET)); + CHECK_RESULT(emit_i32_at(ctx, fixup_cond_offset, get_istream_offset(ctx))); + /* reset the expr stack for the other branch arm */ ctx->expr_stack.size = label->expr_stack_size; return WASM_OK; } static WasmResult on_end_expr(void* user_data) { Context* ctx = user_data; - TypecheckLabel* label = top_typecheck_label(ctx); - if (!label) { + LOGF("%3" PRIzd ": end\n", ctx->expr_stack.size); + Label* label = top_label(ctx); + if (!label || label->label_type == LABEL_TYPE_FUNC) { print_error(ctx, "unexpected end operator"); return WASM_ERROR; } @@ -815,56 +902,33 @@ static WasmResult on_end_expr(void* user_data) { if (label->label_type == LABEL_TYPE_IF) label->type = WASM_TYPE_VOID; - if (label->expr_stack_size < ctx->expr_stack.size) { - ExprNode* last_expr = &ctx->expr_stack.data[ctx->expr_stack.size - 1]; - WasmType old_type = label->type; - CHECK_RESULT(unify_and_check_type(ctx, &label->type, last_expr->type, - s_label_type_name[label->label_type])); - if (label->label_type == LABEL_TYPE_ELSE && - get_value_count(old_type) > get_value_count(label->type)) { - /* we unified the types between the true and false branches; the true - * branch had a value and the false branch didn't, so we need to discard - * the true branch's value */ - set_expr_discarded(ctx, label->last_expr_index); - } - - /* discard everything except the last expr */ - uint32_t i; - for (i = label->expr_stack_size; i < ctx->expr_stack.size - 1; ++i) { - ExprNode* expr = &ctx->expr_stack.data[i]; - set_expr_discarded_unless_void(ctx, expr->index, expr->type); - } - - if (label->type == WASM_TYPE_VOID) { - /* discard the last child expr if this block/if/loop is void */ - set_expr_discarded_unless_void(ctx, last_expr->index, last_expr->type); - } - } else { - /* the block/loop/if was empty */ - label->type = WASM_TYPE_VOID; - } + WasmType top_type; + CHECK_RESULT(check_end(ctx, &top_type)); + CHECK_RESULT(unify_and_check_type(ctx, &label->type, top_type, + s_label_type_name[label->label_type])); - if (label->type == WASM_TYPE_VOID) { - /* the "discarded" bit is overloaded for the initial block/loop/if - * operators; in that case it means that there is no value expected to be - * returned, so if a br operator yields a value, it should be immediately - * discarded */ - set_expr_discarded(ctx, label->expr_index); + if (label->label_type == LABEL_TYPE_IF || + label->label_type == LABEL_TYPE_ELSE) { + uint32_t fixup_true_offset = label->fixup_offset; + CHECK_RESULT(emit_i32_at(ctx, fixup_true_offset, get_istream_offset(ctx))); } + fixup_top_label(ctx, get_istream_offset(ctx)); ctx->expr_stack.size = label->expr_stack_size; - pop_typecheck_label(ctx); + pop_label(ctx); push_expr(ctx, label->type, WASM_OPCODE_END); return WASM_OK; } static WasmResult on_br_expr(uint8_t arity, uint32_t depth, void* user_data) { Context* ctx = user_data; - WasmType value = pop_expr_if(ctx, arity == 1); + LOGF("%3" PRIzd ": br\n", ctx->expr_stack.size); + WasmType value = top_expr_if(ctx, arity > 0); CHECK_DEPTH(ctx, depth); - depth = translate_typecheck_depth(ctx, depth); - TypecheckLabel* label = get_typecheck_label(ctx, depth); + depth = translate_depth(ctx, depth); + Label* label = get_label(ctx, depth); CHECK_RESULT(unify_and_check_type(ctx, &label->type, value, "br")); + CHECK_RESULT(emit_br(ctx, arity, depth)); push_expr(ctx, WASM_TYPE_ANY, WASM_OPCODE_BR); return WASM_OK; } @@ -873,16 +937,23 @@ static WasmResult on_br_if_expr(uint8_t arity, uint32_t depth, void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": br_if\n", ctx->expr_stack.size); WasmType cond = pop_expr(ctx); - WasmType value = pop_expr_if(ctx, arity == 1); + WasmType value = top_expr_if(ctx, arity > 0); CHECK_DEPTH(ctx, depth); - depth = translate_typecheck_depth(ctx, depth); - TypecheckLabel* label = get_typecheck_label(ctx, depth); + depth = translate_depth(ctx, depth); + Label* label = get_label(ctx, depth); CHECK_RESULT(unify_and_check_type(ctx, &label->type, value, "br_if")); CHECK_RESULT(check_type(ctx, WASM_TYPE_I32, cond, "br_if")); - /* the "discarded" bit is overloaded for br_if; if set, the br_if value is - * non-void; i.e. should be discarded if the branch is not taken */ - set_expr_discarded_unless_void(ctx, ctx->expr_count, value); + /* flip the br_if so if <cond> is true it can drop values from the stack */ + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_BR_UNLESS)); + uint32_t fixup_br_offset = get_istream_offset(ctx); + CHECK_RESULT(emit_i32(ctx, WASM_INVALID_OFFSET)); + CHECK_RESULT(emit_br(ctx, arity, depth)); + CHECK_RESULT(emit_i32_at(ctx, fixup_br_offset, get_istream_offset(ctx))); + /* clean up the value (if any), when the branch isn't taken */ + CHECK_RESULT(emit_drop_keep(ctx, arity, 0)); + ctx->expr_stack.size -= arity; push_expr(ctx, WASM_TYPE_VOID, WASM_OPCODE_BR_IF); return WASM_OK; } @@ -893,16 +964,28 @@ static WasmResult on_br_table_expr(uint8_t arity, uint32_t default_target_depth, void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": br_table\n", ctx->expr_stack.size); WasmType key = pop_expr(ctx); - WasmType value = pop_expr_if(ctx, arity == 1); + WasmType value = top_expr_if(ctx, arity > 0); CHECK_RESULT(check_type(ctx, WASM_TYPE_I32, key, "br_table")); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_BR_TABLE)); + CHECK_RESULT(emit_i32(ctx, num_targets)); + uint32_t fixup_table_offset = get_istream_offset(ctx); + CHECK_RESULT(emit_i32(ctx, WASM_INVALID_OFFSET)); + /* not necessary for the interpreter, but it makes it easier to disassemble. + * This opcode specifies how many bytes of data follow. */ + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_DATA)); + CHECK_RESULT(emit_i32(ctx, (num_targets + 1) * WASM_TABLE_ENTRY_SIZE)); + CHECK_RESULT(emit_i32_at(ctx, fixup_table_offset, get_istream_offset(ctx))); + uint32_t i; for (i = 0; i <= num_targets; ++i) { uint32_t depth = i != num_targets ? target_depths[i] : default_target_depth; - uint32_t translated_depth = translate_typecheck_depth(ctx, depth); - TypecheckLabel* label = get_typecheck_label(ctx, translated_depth); + depth = translate_depth(ctx, depth); + Label* label = get_label(ctx, depth); CHECK_RESULT(unify_and_check_type(ctx, &label->type, value, "br_table")); + CHECK_RESULT(emit_br_table_offset(ctx, arity, depth)); } push_expr(ctx, WASM_TYPE_ANY, WASM_OPCODE_BR_TABLE); @@ -911,6 +994,7 @@ static WasmResult on_br_table_expr(uint8_t arity, static WasmResult on_call_expr(uint32_t func_index, void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": call\n", ctx->expr_stack.size); assert(func_index < ctx->funcs.size); InterpreterFunc* func = get_func(ctx, func_index); WasmInterpreterFuncSignature* sig = get_func_signature(ctx, func); @@ -921,12 +1005,15 @@ static WasmResult on_call_expr(uint32_t func_index, void* user_data) { CHECK_RESULT(check_type(ctx, sig->param_types.data[i - 1], arg, "call")); } + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_CALL_FUNCTION)); + CHECK_RESULT(emit_func_offset(ctx, func, func_index)); push_expr(ctx, sig->result_type, WASM_OPCODE_CALL_FUNCTION); return WASM_OK; } static WasmResult on_call_import_expr(uint32_t import_index, void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": call_import\n", ctx->expr_stack.size); assert(import_index < ctx->module->imports.size); WasmInterpreterImport* import = get_import(ctx, import_index); WasmInterpreterFuncSignature* sig = get_signature(ctx, import->sig_index); @@ -938,14 +1025,16 @@ static WasmResult on_call_import_expr(uint32_t import_index, void* user_data) { check_type(ctx, sig->param_types.data[i - 1], arg, "call_import")); } + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_CALL_IMPORT)); + CHECK_RESULT(emit_i32(ctx, import_index)); push_expr(ctx, sig->result_type, WASM_OPCODE_CALL_IMPORT); return WASM_OK; } static WasmResult on_call_indirect_expr(uint32_t sig_index, void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": call_indirect\n", ctx->expr_stack.size); WasmInterpreterFuncSignature* sig = get_signature(ctx, sig_index); - WasmType entry_index = pop_expr(ctx); CHECK_RESULT(check_type(ctx, WASM_TYPE_I32, entry_index, "call_indirect")); @@ -956,90 +1045,120 @@ static WasmResult on_call_indirect_expr(uint32_t sig_index, void* user_data) { check_type(ctx, sig->param_types.data[i - 1], arg, "call_indirect")); } + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_CALL_INDIRECT)); + CHECK_RESULT(emit_i32(ctx, sig_index)); push_expr(ctx, sig->result_type, WASM_OPCODE_CALL_INDIRECT); return WASM_OK; } static WasmResult on_drop_expr(void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": drop\n", ctx->expr_stack.size); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_DROP)); pop_expr(ctx); return WASM_OK; } static WasmResult on_i32_const_expr(uint32_t value, void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": i32.const\n", ctx->expr_stack.size); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_I32_CONST)); + CHECK_RESULT(emit_i32(ctx, value)); push_expr(ctx, WASM_TYPE_I32, WASM_OPCODE_I32_CONST); return WASM_OK; } static WasmResult on_i64_const_expr(uint64_t value, void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": i64.const\n", ctx->expr_stack.size); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_I64_CONST)); + CHECK_RESULT(emit_i64(ctx, value)); push_expr(ctx, WASM_TYPE_I64, WASM_OPCODE_I64_CONST); return WASM_OK; } static WasmResult on_f32_const_expr(uint32_t value_bits, void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": f32.const\n", ctx->expr_stack.size); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_F32_CONST)); + CHECK_RESULT(emit_i32(ctx, value_bits)); push_expr(ctx, WASM_TYPE_F32, WASM_OPCODE_F32_CONST); return WASM_OK; } static WasmResult on_f64_const_expr(uint64_t value_bits, void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": f64.const\n", ctx->expr_stack.size); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_F64_CONST)); + CHECK_RESULT(emit_i64(ctx, value_bits)); push_expr(ctx, WASM_TYPE_F64, WASM_OPCODE_F64_CONST); return WASM_OK; } static WasmResult on_get_global_expr(uint32_t global_index, void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": get_global\n", ctx->expr_stack.size); CHECK_GLOBAL(ctx, global_index); WasmType type = get_global_index_type(ctx, global_index); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_GET_GLOBAL)); + CHECK_RESULT(emit_i32(ctx, global_index)); push_expr(ctx, type, WASM_OPCODE_GET_GLOBAL); return WASM_OK; } static WasmResult on_set_global_expr(uint32_t global_index, void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": set_global\n", ctx->expr_stack.size); CHECK_GLOBAL(ctx, global_index); WasmType type = get_global_index_type(ctx, global_index); WasmType value = pop_expr(ctx); CHECK_RESULT(check_type(ctx, type, value, "set_global")); - ctx->expr_count++; + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_SET_GLOBAL)); + CHECK_RESULT(emit_i32(ctx, global_index)); return WASM_OK; } static WasmResult on_get_local_expr(uint32_t local_index, void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": get_local\n", ctx->expr_stack.size); CHECK_LOCAL(ctx, local_index); WasmType type = get_local_index_type(ctx->current_func, local_index); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_GET_LOCAL)); + CHECK_RESULT(emit_i32(ctx, translate_local_index(ctx, local_index))); push_expr(ctx, type, WASM_OPCODE_GET_LOCAL); return WASM_OK; } static WasmResult on_set_local_expr(uint32_t local_index, void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": set_local\n", ctx->expr_stack.size); CHECK_LOCAL(ctx, local_index); WasmType type = get_local_index_type(ctx->current_func, local_index); WasmType value = pop_expr(ctx); CHECK_RESULT(check_type(ctx, type, value, "set_local")); - ctx->expr_count++; + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_SET_LOCAL)); + CHECK_RESULT(emit_i32(ctx, translate_local_index(ctx, local_index))); return WASM_OK; } static WasmResult on_tee_local_expr(uint32_t local_index, void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": tee_local\n", ctx->expr_stack.size); CHECK_LOCAL(ctx, local_index); WasmType type = get_local_index_type(ctx->current_func, local_index); WasmType value = pop_expr(ctx); CHECK_RESULT(check_type(ctx, type, value, "tee_local")); - push_expr(ctx, type, WASM_OPCODE_TEE_LOCAL); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_TEE_LOCAL)); + CHECK_RESULT(emit_i32(ctx, translate_local_index(ctx, local_index))); return WASM_OK; } static WasmResult on_grow_memory_expr(void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": grow_memory\n", ctx->expr_stack.size); WasmType value = pop_expr(ctx); CHECK_RESULT(check_type(ctx, WASM_TYPE_I32, value, "grow_memory")); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_GROW_MEMORY)); push_expr(ctx, WASM_TYPE_I32, WASM_OPCODE_GROW_MEMORY); return WASM_OK; } @@ -1049,8 +1168,11 @@ static WasmResult on_load_expr(WasmOpcode opcode, uint32_t offset, void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": %s\n", ctx->expr_stack.size, s_opcode_name[opcode]); WasmType addr = pop_expr(ctx); CHECK_RESULT(check_type(ctx, WASM_TYPE_I32, addr, s_opcode_name[opcode])); + CHECK_RESULT(emit_opcode(ctx, opcode)); + CHECK_RESULT(emit_i32(ctx, offset)); push_expr(ctx, s_opcode_rtype[opcode], opcode); return WASM_OK; } @@ -1060,41 +1182,50 @@ static WasmResult on_store_expr(WasmOpcode opcode, uint32_t offset, void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": %s\n", ctx->expr_stack.size, s_opcode_name[opcode]); WasmType value = pop_expr(ctx); WasmType addr = pop_expr(ctx); WasmType type = s_opcode_rtype[opcode]; CHECK_RESULT(check_type(ctx, WASM_TYPE_I32, addr, s_opcode_name[opcode])); CHECK_RESULT(check_type(ctx, type, value, s_opcode_name[opcode])); + CHECK_RESULT(emit_opcode(ctx, opcode)); + CHECK_RESULT(emit_i32(ctx, offset)); push_expr(ctx, type, opcode); return WASM_OK; } static WasmResult on_current_memory_expr(void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": current_memory\n", ctx->expr_stack.size); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_CURRENT_MEMORY)); push_expr(ctx, WASM_TYPE_I32, WASM_OPCODE_CURRENT_MEMORY); return WASM_OK; } static WasmResult on_nop_expr(void* user_data) { Context* ctx = user_data; - push_expr(ctx, WASM_TYPE_VOID, WASM_OPCODE_NOP); + WASM_USE(ctx); + LOGF("%3" PRIzd ": nop\n", ctx->expr_stack.size); return WASM_OK; } static WasmResult on_return_expr(void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": return\n", ctx->expr_stack.size); WasmInterpreterFuncSignature* sig = get_func_signature(ctx, ctx->current_func); if (get_value_count(sig->result_type)) { - WasmType value = pop_expr(ctx); + WasmType value = top_expr(ctx); CHECK_RESULT(check_type(ctx, sig->result_type, value, "return")); } + CHECK_RESULT(emit_return(ctx, sig->result_type)); push_expr(ctx, WASM_TYPE_ANY, WASM_OPCODE_RETURN); return WASM_OK; } static WasmResult on_select_expr(void* user_data) { Context* ctx = user_data; + LOGF("%3" PRIzd ": select\n", ctx->expr_stack.size); WasmType cond = pop_expr(ctx); WasmType right = pop_expr(ctx); WasmType left = pop_expr(ctx); @@ -1102,726 +1233,16 @@ static WasmResult on_select_expr(void* user_data) { CHECK_RESULT(unify_and_check_type(ctx, &type, left, "select")); CHECK_RESULT(unify_and_check_type(ctx, &type, right, "select")); CHECK_RESULT(check_type(ctx, WASM_TYPE_I32, cond, "select")); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_SELECT)); push_expr(ctx, type, WASM_OPCODE_SELECT); return WASM_OK; } static WasmResult on_unreachable_expr(void* user_data) { Context* ctx = user_data; - push_expr(ctx, WASM_TYPE_ANY, WASM_OPCODE_UNREACHABLE); - return WASM_OK; -} - -/*** emit pass ****************************************************************/ - -static uint32_t get_istream_offset(Context* ctx) { - return ctx->istream_offset; -} - -static WasmResult emit_data_at(Context* ctx, - size_t offset, - const void* data, - size_t size) { - return ctx->istream_writer.base.write_data( - offset, data, size, ctx->istream_writer.base.user_data); -} - -static WasmResult emit_data(Context* ctx, const void* data, size_t size) { - CHECK_RESULT(emit_data_at(ctx, ctx->istream_offset, data, size)); - ctx->istream_offset += size; - return WASM_OK; -} - -static WasmResult emit_opcode(Context* ctx, WasmOpcode opcode) { - return emit_data(ctx, &opcode, sizeof(uint8_t)); -} - -static WasmResult emit_i8(Context* ctx, uint8_t value) { - return emit_data(ctx, &value, sizeof(value)); -} - -static WasmResult emit_i32(Context* ctx, uint32_t value) { - return emit_data(ctx, &value, sizeof(value)); -} - -static WasmResult emit_i64(Context* ctx, uint64_t value) { - return emit_data(ctx, &value, sizeof(value)); -} - -static WasmResult emit_i32_at(Context* ctx, uint32_t offset, uint32_t value) { - return emit_data_at(ctx, offset, &value, sizeof(value)); -} - -static void adjust_value_stack(Context* ctx, int32_t amount) { - uint32_t old_size = ctx->value_stack_size; - uint32_t new_size = old_size + (uint32_t)amount; - assert((amount <= 0 && new_size <= old_size) || - (amount > 0 && new_size > old_size)); - WASM_USE(old_size); - WASM_USE(new_size); - ctx->value_stack_size += (uint32_t)amount; -#ifndef NDEBUG - if (ctx->emit_label_stack.size > 0) { - assert(ctx->value_stack_size >= - ctx->emit_label_stack.data[ctx->emit_label_stack.size - 1] - .value_stack_size); - } else { - assert(ctx->value_stack_size >= - ctx->current_func->param_and_local_types.size); - } -#endif -} - -static void adjust_value_stack_for_nonlocal_continuation(Context* ctx) { - /* adjust stack up; these operations type-check as ANY, so they can be used in - * any operation. No value will actually be pushed, and the expressions that - * use the result won't ever be executed. But it will make the stack the - * "normal" size, so we won't have to special case it anywhere else. */ - adjust_value_stack(ctx, is_expr_discarded(ctx, ctx->expr_count) ? 0 : 1); -} - -static EmitLabel* get_emit_label(Context* ctx, uint32_t depth) { - assert(depth < ctx->emit_label_stack.size); - return &ctx->emit_label_stack.data[depth]; -} - -static EmitLabel* top_emit_label(Context* ctx) { - return get_emit_label(ctx, ctx->emit_label_stack.size - 1); -} - -static uint32_t translate_emit_depth(Context* ctx, uint32_t depth) { - return translate_depth(ctx, ctx->emit_label_stack.size, depth); -} - -static void push_emit_label(Context* ctx, - LabelType label_type, - uint32_t offset, - uint32_t fixup_offset, - WasmBool has_value) { - EmitLabel* label = - wasm_append_emit_label(ctx->allocator, &ctx->emit_label_stack); - label->label_type = label_type; - label->offset = offset; - label->value_stack_size = ctx->value_stack_size; - label->has_value = has_value; - label->fixup_offset = fixup_offset; - LOGF(" : +depth %" PRIzd "\n", ctx->emit_label_stack.size - 1); -} - -static void pop_emit_label(Context* ctx) { - LOGF(" : -depth %" PRIzd "\n", ctx->emit_label_stack.size - 1); - assert(ctx->emit_label_stack.size > 0); - ctx->emit_label_stack.size--; - /* reduce the depth_fixups stack as well, but it may be smaller than - * emit_label_stack so only do it conditionally. */ - if (ctx->depth_fixups.size > ctx->emit_label_stack.size) { - uint32_t from = ctx->emit_label_stack.size; - uint32_t to = ctx->depth_fixups.size; - uint32_t i; - for (i = from; i < to; ++i) - wasm_destroy_uint32_vector(ctx->allocator, &ctx->depth_fixups.data[i]); - ctx->depth_fixups.size = ctx->emit_label_stack.size; - } -} - -static WasmResult fixup_top_emit_label(Context* ctx, uint32_t offset) { - uint32_t top = ctx->emit_label_stack.size - 1; - if (top >= ctx->depth_fixups.size) { - /* nothing to fixup */ - return WASM_OK; - } - - Uint32Vector* fixups = &ctx->depth_fixups.data[top]; - uint32_t i; - for (i = 0; i < fixups->size; ++i) - CHECK_RESULT(emit_i32_at(ctx, fixups->data[i], offset)); - /* reduce the size to 0 in case this gets reused. Keep the allocations for - * later use */ - fixups->size = 0; - return WASM_OK; -} - -static WasmResult emit_discard(Context* ctx) { - LOGF("%3" PRIzd ": discard\n", ctx->value_stack_size); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_DISCARD)); - adjust_value_stack(ctx, -1); - return WASM_OK; -} - -static WasmResult maybe_emit_discard(Context* ctx, uint32_t expr_index) { - WasmBool should_discard = is_expr_discarded(ctx, expr_index); - if (should_discard) - return emit_discard(ctx); - return WASM_OK; -} - -static WasmResult emit_discard_keep(Context* ctx, - uint32_t discard, - uint8_t keep) { - assert(discard != UINT32_MAX); - assert(keep <= 1); - if (discard > 0) { - if (discard == 1 && keep == 0) { - LOGF("%3" PRIzd ": discard\n", ctx->value_stack_size); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_DISCARD)); - } else { - LOGF("%3" PRIzd ": discard_keep %u %u\n", ctx->value_stack_size, discard, - keep); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_DISCARD_KEEP)); - CHECK_RESULT(emit_i32(ctx, discard)); - CHECK_RESULT(emit_i8(ctx, keep)); - } - } - return WASM_OK; -} - -static WasmResult emit_return(Context* ctx, WasmType result_type) { - uint32_t keep_count = get_value_count(result_type); - uint32_t discard_count = ctx->value_stack_size - keep_count; - CHECK_RESULT(emit_discard_keep(ctx, discard_count, keep_count)); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_RETURN)); - return WASM_OK; -} - -static WasmResult append_fixup(Context* ctx, - Uint32VectorVector* fixups_vector, - uint32_t index) { - if (index >= fixups_vector->size) - wasm_resize_uint32_vector_vector(ctx->allocator, fixups_vector, index + 1); - Uint32Vector* fixups = &fixups_vector->data[index]; - uint32_t offset = get_istream_offset(ctx); - wasm_append_uint32_value(ctx->allocator, fixups, &offset); - return WASM_OK; -} - -static WasmResult emit_br_offset(Context* ctx, - uint32_t depth, - uint32_t offset) { - if (offset == WASM_INVALID_OFFSET) - CHECK_RESULT(append_fixup(ctx, &ctx->depth_fixups, depth)); - CHECK_RESULT(emit_i32(ctx, offset)); - return WASM_OK; -} - -static WasmResult emit_br(Context* ctx, uint32_t depth) { - EmitLabel* label = get_emit_label(ctx, depth); - assert(ctx->value_stack_size >= label->value_stack_size); - uint8_t keep_count = label->has_value ? 1 : 0; - uint32_t discard_count = - (ctx->value_stack_size - label->value_stack_size) - keep_count; - CHECK_RESULT(emit_discard_keep(ctx, discard_count, keep_count)); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_BR)); - CHECK_RESULT(emit_br_offset(ctx, depth, label->offset)); - return WASM_OK; -} - -static WasmResult emit_br_table_offset(Context* ctx, uint32_t depth) { - EmitLabel* label = get_emit_label(ctx, depth); - uint8_t keep_count = label->has_value ? 1 : 0; - uint32_t discard_count = - (ctx->value_stack_size - label->value_stack_size) - keep_count; - CHECK_RESULT(emit_br_offset(ctx, depth, label->offset)); - CHECK_RESULT(emit_i32(ctx, discard_count)); - CHECK_RESULT(emit_i8(ctx, keep_count)); - return WASM_OK; -} - -static WasmResult emit_func_offset(Context* ctx, - InterpreterFunc* func, - uint32_t func_index) { - if (func->offset == WASM_INVALID_OFFSET) - CHECK_RESULT(append_fixup(ctx, &ctx->func_fixups, func_index)); - CHECK_RESULT(emit_i32(ctx, func->offset)); - return WASM_OK; -} - -static WasmResult begin_emit_function_body(uint32_t index, void* user_data) { - Context* ctx = user_data; - InterpreterFunc* func = get_func(ctx, index); - WasmInterpreterFuncSignature* sig = get_signature(ctx, func->sig_index); - - func->offset = get_istream_offset(ctx); - func->local_decl_count = 0; - func->local_count = 0; - - ctx->current_func = func; - ctx->emit_label_stack.size = 0; - ctx->depth_fixups.size = 0; - ctx->value_stack_size = sig->param_types.size; - ctx->depth = 0; - ctx->expr_count = 0; - - /* fixup function references */ - uint32_t i; - Uint32Vector* fixups = &ctx->func_fixups.data[index]; - for (i = 0; i < fixups->size; ++i) - CHECK_RESULT(emit_i32_at(ctx, fixups->data[i], func->offset)); - - push_emit_label(ctx, LABEL_TYPE_FUNC, WASM_INVALID_OFFSET, 0, - sig->result_type != WASM_TYPE_VOID); - - return WASM_OK; -} - -static WasmResult end_emit_function_body(uint32_t index, void* user_data) { - Context* ctx = user_data; - WasmInterpreterFuncSignature* sig = - get_func_signature(ctx, ctx->current_func); - fixup_top_emit_label(ctx, get_istream_offset(ctx)); - EmitLabel* label = top_emit_label(ctx); - ctx->value_stack_size = label->value_stack_size; - adjust_value_stack(ctx, label->has_value ? 1 : 0); - CHECK_RESULT(emit_return(ctx, sig->result_type)); - pop_emit_label(ctx); - ctx->current_func = NULL; - ctx->value_stack_size = 0; - return WASM_OK; -} - -static WasmResult on_emit_local_decl_count(uint32_t count, void* user_data) { - Context* ctx = user_data; - ctx->current_func->local_decl_count = count; - return WASM_OK; -} - -static WasmResult on_emit_local_decl(uint32_t decl_index, - uint32_t count, - WasmType type, - void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_ALLOCA]); - InterpreterFunc* func = ctx->current_func; - func->local_count += count; - - if (decl_index == func->local_decl_count - 1) { - /* last local declaration, allocate space for all locals. */ - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_ALLOCA)); - CHECK_RESULT(emit_i32(ctx, func->local_count)); - adjust_value_stack(ctx, func->local_count); - - /* adjust the "return" label to include the locals in the value stack; this - * way they'll be discarded when the function returns. */ - EmitLabel* label = top_emit_label(ctx); - assert(label->label_type == LABEL_TYPE_FUNC); - label->value_stack_size += func->local_count; - } - return WASM_OK; -} - -static WasmResult on_emit_unary_expr(WasmOpcode opcode, void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, s_opcode_name[opcode]); - CHECK_RESULT(emit_opcode(ctx, opcode)); - CHECK_RESULT(maybe_emit_discard(ctx, ctx->expr_count)); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_binary_expr(WasmOpcode opcode, void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, s_opcode_name[opcode]); - adjust_value_stack(ctx, -1); - CHECK_RESULT(emit_opcode(ctx, opcode)); - CHECK_RESULT(maybe_emit_discard(ctx, ctx->expr_count)); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_block_expr(void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_BLOCK]); - push_emit_label(ctx, LABEL_TYPE_BLOCK, WASM_INVALID_OFFSET, 0, - !is_expr_discarded(ctx, ctx->expr_count)); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_loop_expr(void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_LOOP]); - push_emit_label(ctx, LABEL_TYPE_LOOP, get_istream_offset(ctx), 0, WASM_FALSE); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_if_expr(void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_IF]); - - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_BR_UNLESS)); - adjust_value_stack(ctx, -1); - uint32_t fixup_offset = get_istream_offset(ctx); - CHECK_RESULT(emit_i32(ctx, WASM_INVALID_OFFSET)); - - push_emit_label(ctx, LABEL_TYPE_IF, WASM_INVALID_OFFSET, fixup_offset, - !is_expr_discarded(ctx, ctx->expr_count)); - - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_else_expr(void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_ELSE]); - EmitLabel* label = top_emit_label(ctx); - assert(label->label_type == LABEL_TYPE_IF); - label->label_type = LABEL_TYPE_ELSE; - uint32_t fixup_cond_offset = label->fixup_offset; - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_BR)); - label->fixup_offset = get_istream_offset(ctx); - CHECK_RESULT(emit_i32(ctx, WASM_INVALID_OFFSET)); - CHECK_RESULT(emit_i32_at(ctx, fixup_cond_offset, get_istream_offset(ctx))); - /* reset the value stack for the other branch arm */ - ctx->value_stack_size = label->value_stack_size; - return WASM_OK; -} - -static WasmResult on_emit_end_expr(void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_END]); - EmitLabel* label = top_emit_label(ctx); - switch (label->label_type) { - case LABEL_TYPE_LOOP: - break; - - case LABEL_TYPE_IF: - case LABEL_TYPE_ELSE: { - uint32_t fixup_true_offset = label->fixup_offset; - CHECK_RESULT( - emit_i32_at(ctx, fixup_true_offset, get_istream_offset(ctx))); - break; - } - - default: - break; - } - fixup_top_emit_label(ctx, get_istream_offset(ctx)); - ctx->value_stack_size = label->value_stack_size; - adjust_value_stack(ctx, label->has_value ? 1 : 0); - CHECK_RESULT(maybe_emit_discard(ctx, ctx->expr_count)); - pop_emit_label(ctx); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_br_expr(uint8_t arity, - uint32_t depth, - void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_BR]); - CHECK_RESULT(emit_br(ctx, translate_emit_depth(ctx, depth))); - adjust_value_stack_for_nonlocal_continuation(ctx); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_br_if_expr(uint8_t arity, - uint32_t depth, - void* user_data) { - /* flip the br_if so if <cond> is true it can discard values from the stack */ - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_BR_IF]); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_BR_UNLESS)); - adjust_value_stack(ctx, -1); /* account for br_unless consuming <cond> */ - uint32_t fixup_br_offset = get_istream_offset(ctx); - CHECK_RESULT(emit_i32(ctx, WASM_INVALID_OFFSET)); - CHECK_RESULT(emit_br(ctx, translate_emit_depth(ctx, depth))); - CHECK_RESULT(emit_i32_at(ctx, fixup_br_offset, get_istream_offset(ctx))); - CHECK_RESULT(maybe_emit_discard(ctx, ctx->expr_count)); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_br_table_expr(uint8_t arity, - uint32_t num_targets, - uint32_t* target_depths, - uint32_t default_target_depth, - void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_BR_TABLE]); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_BR_TABLE)); - CHECK_RESULT(emit_i32(ctx, num_targets)); - uint32_t fixup_table_offset = get_istream_offset(ctx); - CHECK_RESULT(emit_i32(ctx, WASM_INVALID_OFFSET)); - adjust_value_stack(ctx, -1); - - /* not necessary for the interpreter, but it makes it easier to disassemble. - * This opcode specifies how many bytes of data follow. */ - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_DATA)); - CHECK_RESULT(emit_i32(ctx, (num_targets + 1) * WASM_TABLE_ENTRY_SIZE)); - CHECK_RESULT(emit_i32_at(ctx, fixup_table_offset, get_istream_offset(ctx))); - - /* write the branch table as (offset, discard count) pairs */ - uint32_t i; - for (i = 0; i <= num_targets; ++i) { - uint32_t depth = i != num_targets ? target_depths[i] : default_target_depth; - CHECK_RESULT(emit_br_table_offset(ctx, translate_emit_depth(ctx, depth))); - } - - adjust_value_stack_for_nonlocal_continuation(ctx); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_call_expr(uint32_t func_index, void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_CALL_FUNCTION]); - InterpreterFunc* func = get_func(ctx, func_index); - WasmInterpreterFuncSignature* sig = get_func_signature(ctx, func); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_CALL_FUNCTION)); - CHECK_RESULT(emit_func_offset(ctx, func, func_index)); - adjust_value_stack(ctx, - get_value_count(sig->result_type) - sig->param_types.size); - CHECK_RESULT(maybe_emit_discard(ctx, ctx->expr_count)); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_call_import_expr(uint32_t import_index, - void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_CALL_IMPORT]); - WasmInterpreterImport* import = get_import(ctx, import_index); - WasmInterpreterFuncSignature* sig = get_signature(ctx, import->sig_index); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_CALL_IMPORT)); - CHECK_RESULT(emit_i32(ctx, import_index)); - adjust_value_stack(ctx, - get_value_count(sig->result_type) - sig->param_types.size); - CHECK_RESULT(maybe_emit_discard(ctx, ctx->expr_count)); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_call_indirect_expr(uint32_t sig_index, - void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_CALL_INDIRECT]); - WasmInterpreterFuncSignature* sig = get_signature(ctx, sig_index); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_CALL_INDIRECT)); - CHECK_RESULT(emit_i32(ctx, sig_index)); - adjust_value_stack(ctx, - get_value_count(sig->result_type) - sig->param_types.size); - adjust_value_stack(ctx, -1); - CHECK_RESULT(maybe_emit_discard(ctx, ctx->expr_count)); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_drop_expr(void *user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_DROP]); - CHECK_RESULT(emit_discard(ctx)); - return WASM_OK; -} - -static WasmResult on_emit_i32_const_expr(uint32_t value, void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_I32_CONST]); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_I32_CONST)); - CHECK_RESULT(emit_i32(ctx, value)); - adjust_value_stack(ctx, 1); - CHECK_RESULT(maybe_emit_discard(ctx, ctx->expr_count)); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_i64_const_expr(uint64_t value, void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_I64_CONST]); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_I64_CONST)); - CHECK_RESULT(emit_i64(ctx, value)); - adjust_value_stack(ctx, 1); - CHECK_RESULT(maybe_emit_discard(ctx, ctx->expr_count)); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_f32_const_expr(uint32_t value_bits, void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_F32_CONST]); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_F32_CONST)); - CHECK_RESULT(emit_i32(ctx, value_bits)); - adjust_value_stack(ctx, 1); - CHECK_RESULT(maybe_emit_discard(ctx, ctx->expr_count)); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_f64_const_expr(uint64_t value_bits, void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_F64_CONST]); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_F64_CONST)); - CHECK_RESULT(emit_i64(ctx, value_bits)); - adjust_value_stack(ctx, 1); - CHECK_RESULT(maybe_emit_discard(ctx, ctx->expr_count)); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_get_global_expr(uint32_t global_index, - void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_GET_GLOBAL]); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_GET_GLOBAL)); - CHECK_RESULT(emit_i32(ctx, global_index)); - adjust_value_stack(ctx, 1); - CHECK_RESULT(maybe_emit_discard(ctx, ctx->expr_count)); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_get_local_expr(uint32_t local_index, - void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_GET_LOCAL]); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_GET_LOCAL)); - CHECK_RESULT(emit_i32(ctx, translate_local_index(ctx, local_index))); - adjust_value_stack(ctx, 1); - CHECK_RESULT(maybe_emit_discard(ctx, ctx->expr_count)); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_set_global_expr(uint32_t global_index, - void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_SET_GLOBAL]); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_SET_GLOBAL)); - CHECK_RESULT(emit_i32(ctx, global_index)); - adjust_value_stack(ctx, -1); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_set_local_expr(uint32_t local_index, - void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_SET_LOCAL]); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_SET_LOCAL)); - CHECK_RESULT(emit_i32(ctx, translate_local_index(ctx, local_index))); - adjust_value_stack(ctx, -1); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_tee_local_expr(uint32_t local_index, - void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_TEE_LOCAL]); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_TEE_LOCAL)); - CHECK_RESULT(emit_i32(ctx, translate_local_index(ctx, local_index))); - CHECK_RESULT(maybe_emit_discard(ctx, ctx->expr_count)); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_grow_memory_expr(void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_GROW_MEMORY]); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_GROW_MEMORY)); - CHECK_RESULT(maybe_emit_discard(ctx, ctx->expr_count)); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_load_expr(WasmOpcode opcode, - uint32_t alignment_log2, - uint32_t offset, - void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, s_opcode_name[opcode]); - CHECK_RESULT(emit_opcode(ctx, opcode)); - CHECK_RESULT(emit_i32(ctx, offset)); - CHECK_RESULT(maybe_emit_discard(ctx, ctx->expr_count)); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_store_expr(WasmOpcode opcode, - uint32_t alignment_log2, - uint32_t offset, - void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, s_opcode_name[opcode]); - CHECK_RESULT(emit_opcode(ctx, opcode)); - CHECK_RESULT(emit_i32(ctx, offset)); - adjust_value_stack(ctx, -1); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_current_memory_expr(void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_CURRENT_MEMORY]); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_CURRENT_MEMORY)); - adjust_value_stack(ctx, 1); - CHECK_RESULT(maybe_emit_discard(ctx, ctx->expr_count)); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_nop_expr(void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_NOP]); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_return_expr(void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_RETURN]); - WasmInterpreterFuncSignature* sig = - get_func_signature(ctx, ctx->current_func); - CHECK_RESULT(emit_return(ctx, sig->result_type)); - adjust_value_stack_for_nonlocal_continuation(ctx); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_select_expr(void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_SELECT]); - CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_SELECT)); - CHECK_RESULT(maybe_emit_discard(ctx, ctx->expr_count)); - adjust_value_stack(ctx, -2); - ctx->expr_count++; - return WASM_OK; -} - -static WasmResult on_emit_unreachable_expr(void* user_data) { - Context* ctx = user_data; - LOGF("%3" PRIzd ": %s\n", ctx->value_stack_size, - s_opcode_name[WASM_OPCODE_UNREACHABLE]); + LOGF("%3" PRIzd ": unreachable\n", ctx->expr_stack.size); CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_UNREACHABLE)); - adjust_value_stack_for_nonlocal_continuation(ctx); - ctx->expr_count++; + push_expr(ctx, WASM_TYPE_ANY, WASM_OPCODE_UNREACHABLE); return WASM_OK; } @@ -1872,8 +1293,8 @@ static WasmBinaryReader s_binary_reader = { .on_start_function = on_start_function, .on_function_bodies_count = on_function_bodies_count, - .begin_function_body_pass = begin_function_body_pass, .begin_function_body = begin_function_body, + .on_local_decl_count = on_local_decl_count, .on_local_decl = on_local_decl, .on_binary_expr = on_binary_expr, .on_block_expr = on_block_expr, @@ -1910,7 +1331,6 @@ static WasmBinaryReader s_binary_reader = { .on_unreachable_expr = on_unreachable_expr, .end_function_body = end_function_body, .end_function_bodies_section = end_function_bodies_section, - .end_function_body_pass = end_function_body_pass, .end_elem_segment_init_expr = end_elem_segment_init_expr, .on_elem_segment_function_index = on_elem_segment_function_index, @@ -1924,82 +1344,14 @@ static WasmBinaryReader s_binary_reader = { .on_init_expr_i64_const_expr = on_init_expr_i64_const_expr, }; -static WasmBinaryReader s_binary_reader_emit = { - .on_error = on_error, - .begin_function_body_pass = begin_function_body_pass, - .begin_function_body = begin_emit_function_body, - .on_local_decl_count = on_emit_local_decl_count, - .on_local_decl = on_emit_local_decl, - .on_binary_expr = on_emit_binary_expr, - .on_block_expr = on_emit_block_expr, - .on_br_expr = on_emit_br_expr, - .on_br_if_expr = on_emit_br_if_expr, - .on_br_table_expr = on_emit_br_table_expr, - .on_call_expr = on_emit_call_expr, - .on_call_import_expr = on_emit_call_import_expr, - .on_call_indirect_expr = on_emit_call_indirect_expr, - .on_compare_expr = on_emit_binary_expr, - .on_convert_expr = on_emit_unary_expr, - .on_current_memory_expr = on_emit_current_memory_expr, - .on_drop_expr = on_emit_drop_expr, - .on_else_expr = on_emit_else_expr, - .on_end_expr = on_emit_end_expr, - .on_f32_const_expr = on_emit_f32_const_expr, - .on_f64_const_expr = on_emit_f64_const_expr, - .on_get_global_expr = on_emit_get_global_expr, - .on_get_local_expr = on_emit_get_local_expr, - .on_grow_memory_expr = on_emit_grow_memory_expr, - .on_i32_const_expr = on_emit_i32_const_expr, - .on_i64_const_expr = on_emit_i64_const_expr, - .on_if_expr = on_emit_if_expr, - .on_load_expr = on_emit_load_expr, - .on_loop_expr = on_emit_loop_expr, - .on_nop_expr = on_emit_nop_expr, - .on_return_expr = on_emit_return_expr, - .on_select_expr = on_emit_select_expr, - .on_set_global_expr = on_emit_set_global_expr, - .on_set_local_expr = on_emit_set_local_expr, - .on_store_expr = on_emit_store_expr, - .on_tee_local_expr = on_emit_tee_local_expr, - .on_unary_expr = on_emit_unary_expr, - .on_unreachable_expr = on_emit_unreachable_expr, - .end_function_body = end_emit_function_body, - .end_function_body_pass = end_function_body_pass, -}; - -static WasmResult begin_function_body_pass(uint32_t index, - uint32_t pass, - void* user_data) { - LOGF("*** func %d pass %d ***\n", index, pass); - Context* ctx = user_data; - assert(pass < 2); - *ctx->reader = pass == 0 ? s_binary_reader : s_binary_reader_emit; - ctx->reader->user_data = user_data; - return WASM_OK; -} - -static WasmResult end_function_body_pass(uint32_t index, - uint32_t pass, - void* user_data) { - Context* ctx = user_data; - /* reset the reader to its original callbacks */ - if (pass == 1) - *ctx->reader = s_binary_reader; - ctx->reader->user_data = user_data; - return WASM_OK; -} - static void wasm_destroy_interpreter_func(WasmAllocator* allocator, InterpreterFunc* func) { wasm_destroy_type_vector(allocator, &func->param_and_local_types); } static void destroy_context(Context* ctx) { - wasm_destroy_uint32_vector(ctx->allocator, &ctx->discarded_exprs); - wasm_destroy_expr_node_vector(ctx->allocator, &ctx->expr_stack); - wasm_destroy_emit_label_vector(ctx->allocator, &ctx->emit_label_stack); - wasm_destroy_typecheck_label_vector(ctx->allocator, - &ctx->typecheck_label_stack); + wasm_destroy_type_vector(ctx->allocator, &ctx->expr_stack); + wasm_destroy_label_vector(ctx->allocator, &ctx->label_stack); WASM_DESTROY_ARRAY_AND_ELEMENTS(ctx->allocator, ctx->funcs, interpreter_func); WASM_DESTROY_VECTOR_AND_ELEMENTS(ctx->allocator, ctx->depth_fixups, uint32_vector); @@ -2032,7 +1384,7 @@ WasmResult wasm_read_binary_interpreter(WasmAllocator* allocator, reader = s_binary_reader; reader.user_data = &ctx; - const uint32_t num_function_passes = 2; + const uint32_t num_function_passes = 1; WasmResult result = wasm_read_binary(allocator, data, size, &reader, num_function_passes, options); if (WASM_SUCCEEDED(result)) { diff --git a/src/wasm-interpreter.c b/src/wasm-interpreter.c index 4a8813f5..393a37a8 100644 --- a/src/wasm-interpreter.c +++ b/src/wasm-interpreter.c @@ -28,8 +28,7 @@ static const char* s_opcode_name[] = { [WASM_OPCODE_ALLOCA] = "alloca", [WASM_OPCODE_BR_UNLESS] = "br_unless", [WASM_OPCODE_DATA] = "data", - [WASM_OPCODE_DISCARD] = "discard", - [WASM_OPCODE_DISCARD_KEEP] = "discard_keep", + [WASM_OPCODE_DROP_KEEP] = "drop_keep", }; #undef V @@ -372,12 +371,12 @@ DEFINE_BITCAST(bitcast_u64_to_f64, uint64_t, double) #define POP_I64() (POP().i64) #define POP_F32() (POP().f32_bits) #define POP_F64() (POP().f64_bits) -#define DISCARD_KEEP(discard, keep) \ - do { \ - assert((keep) <= 1); \ - if ((keep) == 1) \ - PICK((discard) + 1) = TOP(); \ - vs_top -= (discard); \ +#define DROP_KEEP(drop, keep) \ + do { \ + assert((keep) <= 1); \ + if ((keep) == 1) \ + PICK((drop) + 1) = TOP(); \ + vs_top -= (drop); \ } while (0) #define GOTO(offset) pc = &istream[offset] @@ -587,10 +586,10 @@ static WASM_INLINE uint64_t read_u64(const uint8_t** pc) { static WASM_INLINE void read_table_entry_at(const uint8_t* pc, uint32_t* out_offset, - uint32_t* out_discard, + uint32_t* out_drop, uint8_t* out_keep) { *out_offset = read_u32_at(pc + WASM_TABLE_ENTRY_OFFSET_OFFSET); - *out_discard = read_u32_at(pc + WASM_TABLE_ENTRY_DISCARD_OFFSET); + *out_drop = read_u32_at(pc + WASM_TABLE_ENTRY_DROP_OFFSET); *out_keep = *(pc + WASM_TABLE_ENTRY_KEEP_OFFSET); } @@ -657,10 +656,10 @@ WasmInterpreterResult wasm_run_interpreter(WasmInterpreterModule* module, (key >= num_targets ? num_targets : key) * WASM_TABLE_ENTRY_SIZE; const uint8_t* entry = istream + table_offset + key_offset; uint32_t new_pc; - uint32_t discard_count; + uint32_t drop_count; uint8_t keep_count; - read_table_entry_at(entry, &new_pc, &discard_count, &keep_count); - DISCARD_KEEP(discard_count, keep_count); + read_table_entry_at(entry, &new_pc, &drop_count, &keep_count); + DROP_KEEP(drop_count, keep_count); GOTO(new_pc); break; } @@ -715,8 +714,7 @@ WasmInterpreterResult wasm_run_interpreter(WasmInterpreterModule* module, case WASM_OPCODE_SET_LOCAL: { WasmInterpreterValue value = POP(); - /* -1 because we just popped a value off the stack */ - PICK(read_u32(&pc) - 1) = value; + PICK(read_u32(&pc)) = value; break; } @@ -1502,14 +1500,14 @@ WasmInterpreterResult wasm_run_interpreter(WasmInterpreterModule* module, break; } - case WASM_OPCODE_DISCARD: + case WASM_OPCODE_DROP: (void)POP(); break; - case WASM_OPCODE_DISCARD_KEEP: { - uint32_t discard_count = read_u32(&pc); + case WASM_OPCODE_DROP_KEEP: { + uint32_t drop_count = read_u32(&pc); uint8_t keep_count = *pc++; - DISCARD_KEEP(discard_count, keep_count); + DROP_KEEP(drop_count, keep_count); break; } @@ -1575,7 +1573,7 @@ void wasm_trace_pc(WasmInterpreterModule* module, case WASM_OPCODE_RETURN: case WASM_OPCODE_UNREACHABLE: case WASM_OPCODE_CURRENT_MEMORY: - case WASM_OPCODE_DISCARD: + case WASM_OPCODE_DROP: wasm_writef(stream, "%s\n", s_opcode_name[opcode]); break; @@ -1845,7 +1843,7 @@ void wasm_trace_pc(WasmInterpreterModule* module, read_u32_at(pc), TOP().i32); break; - case WASM_OPCODE_DISCARD_KEEP: + case WASM_OPCODE_DROP_KEEP: wasm_writef(stream, "%s $%u $%u\n", s_opcode_name[opcode], read_u32_at(pc), *(pc + 4)); break; @@ -1905,7 +1903,7 @@ void wasm_disassemble_module(WasmInterpreterModule* module, case WASM_OPCODE_RETURN: case WASM_OPCODE_UNREACHABLE: case WASM_OPCODE_CURRENT_MEMORY: - case WASM_OPCODE_DISCARD: + case WASM_OPCODE_DROP: wasm_writef(stream, "%s\n", s_opcode_name[opcode]); break; @@ -2120,11 +2118,10 @@ void wasm_disassemble_module(WasmInterpreterModule* module, read_u32(&pc)); break; - case WASM_OPCODE_DISCARD_KEEP: { - uint32_t discard = read_u32(&pc); + case WASM_OPCODE_DROP_KEEP: { + uint32_t drop = read_u32(&pc); uint32_t keep = *pc++; - wasm_writef(stream, "%s $%u $%u\n", s_opcode_name[opcode], discard, - keep); + wasm_writef(stream, "%s $%u $%u\n", s_opcode_name[opcode], drop, keep); break; } @@ -2139,11 +2136,11 @@ void wasm_disassemble_module(WasmInterpreterModule* module, for (i = 0; i < num_entries; ++i) { wasm_writef(stream, "%4" PRIzd "| ", pc - istream); uint32_t offset; - uint32_t discard; + uint32_t drop; uint8_t keep; - read_table_entry_at(pc, &offset, &discard, &keep); - wasm_writef(stream, " entry %d: offset: %u discard: %u keep: %u\n", - i, offset, discard, keep); + read_table_entry_at(pc, &offset, &drop, &keep); + wasm_writef(stream, " entry %d: offset: %u drop: %u keep: %u\n", i, + offset, drop, keep); pc += WASM_TABLE_ENTRY_SIZE; } } else { diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index df643718..b0b733d5 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -67,7 +67,7 @@ typedef enum WasmInterpreterResult { #define WASM_INVALID_OFFSET ((uint32_t)~0) #define WASM_TABLE_ENTRY_SIZE (sizeof(uint32_t) * 2 + sizeof(uint8_t)) #define WASM_TABLE_ENTRY_OFFSET_OFFSET 0 -#define WASM_TABLE_ENTRY_DISCARD_OFFSET sizeof(uint32_t) +#define WASM_TABLE_ENTRY_DROP_OFFSET sizeof(uint32_t) #define WASM_TABLE_ENTRY_KEEP_OFFSET (sizeof(uint32_t) * 2) enum { @@ -75,8 +75,7 @@ enum { WASM_OPCODE_ALLOCA = WASM_LAST_OPCODE, WASM_OPCODE_BR_UNLESS, WASM_OPCODE_DATA, - WASM_OPCODE_DISCARD, - WASM_OPCODE_DISCARD_KEEP, + WASM_OPCODE_DROP_KEEP, WASM_LAST_INTERPRETER_OPCODE, }; WASM_STATIC_ASSERT(WASM_LAST_INTERPRETER_OPCODE <= 256); |