diff options
Diffstat (limited to 'src/wasm-binary-reader-interpreter.c')
-rw-r--r-- | src/wasm-binary-reader-interpreter.c | 1537 |
1 files changed, 1537 insertions, 0 deletions
diff --git a/src/wasm-binary-reader-interpreter.c b/src/wasm-binary-reader-interpreter.c new file mode 100644 index 00000000..d50e5751 --- /dev/null +++ b/src/wasm-binary-reader-interpreter.c @@ -0,0 +1,1537 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wasm-binary-reader-interpreter.h" + +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> + +#include "wasm-allocator.h" +#include "wasm-binary-reader.h" +#include "wasm-interpreter.h" +#include "wasm-writer.h" + +#define LOG 0 + +#define INVALID_FUNC_INDEX ((uint32_t)~0) + +#define CHECK_ALLOC_(ctx, cond) \ + do { \ + if (!(cond)) { \ + print_error((ctx), "%s:%d: allocation failed", __FILE__, __LINE__); \ + return WASM_ERROR; \ + } \ + } while (0) + +#define CHECK_ALLOC(ctx, e) CHECK_ALLOC_((ctx), (e) == WASM_OK) +#define CHECK_ALLOC_NULL(ctx, v) CHECK_ALLOC_((ctx), (v)) +#define CHECK_ALLOC_NULL_STR(ctx, v) CHECK_ALLOC_((ctx), (v).start) + +#define CHECK_RESULT(expr) \ + do { \ + if ((expr) != WASM_OK) \ + return WASM_ERROR; \ + } while (0) + +#define CHECK_DEPTH(ctx, depth) \ + do { \ + if ((depth) >= (ctx)->depth_stack.size) { \ + print_error((ctx), "invalid depth: %d (max %d)", (depth), \ + (int)((ctx)->depth_stack.size)); \ + return WASM_ERROR; \ + } \ + } while (0) + +#define CHECK_LOCAL(ctx, local_index) \ + do { \ + uint32_t max_local_index = \ + (ctx)->current_func->param_and_local_types.size; \ + if ((local_index) >= max_local_index) { \ + print_error((ctx), "invalid local_index: %d (max %d)", (local_index), \ + max_local_index); \ + return WASM_ERROR; \ + } \ + } while (0) + +#define WASM_TYPE_ANY WASM_NUM_TYPES + +static const char* s_type_names[] = { + "void", "i32", "i64", "f32", "f64", "any", +}; +WASM_STATIC_ASSERT(WASM_ARRAY_SIZE(s_type_names) == WASM_NUM_TYPES + 1); + +/* TODO(binji): combine with the ones defined in wasm-check? */ +#define V(rtype, type1, type2, mem_size, code, NAME, text) \ + [code] = WASM_TYPE_##rtype, +static WasmType s_opcode_rtype[] = {WASM_FOREACH_OPCODE(V)}; +#undef V + +#define V(rtype, type1, type2, mem_size, code, NAME, text) \ + [code] = WASM_TYPE_##type1, +static WasmType s_opcode_type1[] = {WASM_FOREACH_OPCODE(V)}; +#undef V + +#define V(rtype, type1, type2, mem_size, code, NAME, text) \ + [code] = WASM_TYPE_##type2, +static WasmType s_opcode_type2[] = {WASM_FOREACH_OPCODE(V)}; +#undef V + +#if LOG +#define V(rtype, type1, type2, mem_size, code, NAME, text) [code] = text, +static const char* s_opcode_name[] = { + WASM_FOREACH_OPCODE(V) + [WASM_OPCODE_ALLOCA] = "alloca", + [WASM_OPCODE_DISCARD] = "discard", + [WASM_OPCODE_DISCARD_KEEP] = "discard_keep", +}; +#undef V +#endif + +WASM_DEFINE_VECTOR(uint32, WasmUint32); +WASM_DEFINE_VECTOR(uint32_vector, WasmUint32Vector); + +typedef struct WasmDepthNode { + WasmType type; + /* we store the value stack size at this depth so we know how many + * values to discard if we break to this depth */ + uint32_t value_stack_size; + uint32_t offset; +} WasmDepthNode; +WASM_DEFINE_VECTOR(depth_node, WasmDepthNode); + +typedef struct WasmInterpreterExpr { + WasmOpcode opcode; + WasmType type; + union { + /* clang-format off */ + struct { uint32_t depth; } br, br_if; + struct { uint32_t value_stack_size; } block, loop; + struct { uint32_t num_targets, table_offset; } br_table; + struct { uint32_t func_index; } call; + struct { uint32_t import_index; } call_import; + struct { uint32_t sig_index; } call_indirect; + struct { uint32_t fixup_offset; } if_; + struct { + uint32_t fixup_cond_offset, fixup_true_offset, value_stack_size; + } if_else; + struct { uint32_t mem_offset, alignment_log2; } load, store; + struct { uint32_t local_index; } get_local, set_local; + /* clang-format on */ + }; +} WasmInterpreterExpr; + +typedef struct WasmExprNode { + WasmInterpreterExpr expr; + uint32_t index; + uint32_t total; +} WasmExprNode; +WASM_DEFINE_VECTOR(expr_node, WasmExprNode); + +typedef struct WasmInterpreterFunc { + uint32_t sig_index; + uint32_t offset; + uint32_t local_decl_count; + uint32_t local_count; + WasmTypeVector param_and_local_types; +} WasmInterpreterFunc; +WASM_DEFINE_ARRAY(interpreter_func, WasmInterpreterFunc); + +typedef struct WasmReadInterpreterContext { + WasmAllocator* allocator; + WasmAllocator* memory_allocator; + WasmInterpreterModule* module; + WasmInterpreterFuncArray funcs; + WasmInterpreterFunc* current_func; + WasmExprNodeVector expr_stack; + WasmDepthNodeVector depth_stack; + WasmUint32VectorVector func_fixups; + WasmUint32VectorVector depth_fixups; + uint32_t value_stack_size; + uint32_t depth; + uint32_t start_func_index; + WasmMemoryWriter istream_writer; + uint32_t istream_offset; + /* the last expression evaluated at the top-level of a func */ + WasmInterpreterExpr last_expr; + int last_expr_was_discarded; +} WasmReadInterpreterContext; + +static WasmDepthNode* get_depth_node(WasmReadInterpreterContext* ctx, + uint32_t depth) { + assert(depth < ctx->depth_stack.size); + return &ctx->depth_stack.data[depth]; +} + +static uint32_t get_istream_offset(WasmReadInterpreterContext* ctx) { + return ctx->istream_offset; +} + +static uint32_t get_result_count(WasmType result_type) { + return (result_type == WASM_TYPE_VOID || result_type == WASM_TYPE_ANY) ? 0 + : 1; +} + +static void on_error(uint32_t offset, const char* message, void* user_data); + +static void print_error(WasmReadInterpreterContext* ctx, + const char* format, + ...) { + va_list args; + va_list args_copy; + va_start(args, format); + va_copy(args_copy, args); + + char buffer[128]; + int len = wasm_vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + if (len + 1 > sizeof(buffer)) { + char* buffer2 = alloca(len + 1); + len = wasm_vsnprintf(buffer2, len + 1, format, args_copy); + va_end(args_copy); + } + + on_error(WASM_INVALID_OFFSET, buffer, ctx); +} + +static void adjust_value_stack(WasmReadInterpreterContext* 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->depth_stack.size > 0) { + assert(ctx->value_stack_size >= + ctx->depth_stack.data[ctx->depth_stack.size - 1].value_stack_size); + } else { + assert(ctx->value_stack_size >= + ctx->current_func->param_and_local_types.size); + } +#endif +} + +static void reset_value_stack(WasmReadInterpreterContext* ctx, + WasmInterpreterExpr* expr) { + ctx->value_stack_size = + expr->block.value_stack_size + get_result_count(expr->type); +} + +static WasmResult type_mismatch(WasmReadInterpreterContext* ctx, + WasmType expected_type, + WasmType type, + const char* desc) { + print_error(ctx, "type mismatch%s, expected %s but got %s.", desc, + s_type_names[expected_type], s_type_names[type]); + return WASM_ERROR; +} + +static WasmResult check_type_exact(WasmReadInterpreterContext* ctx, + WasmType expected_type, + WasmType type, + const char* desc) { + if (expected_type == type) + return WASM_OK; + return type_mismatch(ctx, expected_type, type, desc); +} + +static WasmResult check_type(WasmReadInterpreterContext* ctx, + WasmType expected_type, + WasmType type, + const char* desc) { + if (expected_type == WASM_TYPE_ANY || type == WASM_TYPE_ANY || + expected_type == WASM_TYPE_VOID) { + return WASM_OK; + } + return check_type_exact(ctx, expected_type, type, desc); +} + +static void unify_type(WasmType* dest_type, WasmType type) { + if (*dest_type == WASM_TYPE_ANY) + *dest_type = type; + else if (type != WASM_TYPE_ANY && *dest_type != type) + *dest_type = WASM_TYPE_VOID; +} + +static WasmResult unify_and_check_type(WasmReadInterpreterContext* ctx, + WasmType* dest_type, + WasmType type, + const char* desc) { + unify_type(dest_type, type); + return check_type(ctx, *dest_type, type, desc); +} + +static WasmResult unify_and_check_type_exact(WasmReadInterpreterContext* ctx, + WasmType* dest_type, + WasmType type, + const char* desc) { + unify_type(dest_type, type); + return check_type_exact(ctx, *dest_type, type, desc); +} + +static WasmResult emit_data_at(WasmReadInterpreterContext* 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(WasmReadInterpreterContext* 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(WasmReadInterpreterContext* ctx, + WasmOpcode opcode, + int32_t stack_adjustment) { + CHECK_RESULT(emit_data(ctx, &opcode, sizeof(uint8_t))); + adjust_value_stack(ctx, stack_adjustment); + return WASM_OK; +} + +static WasmResult emit_i8(WasmReadInterpreterContext* ctx, uint8_t value) { + return emit_data(ctx, &value, sizeof(value)); +} + +static WasmResult emit_i32(WasmReadInterpreterContext* ctx, uint32_t value) { + return emit_data(ctx, &value, sizeof(value)); +} + +static WasmResult emit_i64(WasmReadInterpreterContext* ctx, uint64_t value) { + return emit_data(ctx, &value, sizeof(value)); +} + +static WasmResult emit_i32_at(WasmReadInterpreterContext* ctx, + uint32_t offset, + uint32_t value) { + return emit_data_at(ctx, offset, &value, sizeof(value)); +} + +static void unemit_discard(WasmReadInterpreterContext* ctx) { + assert(ctx->istream_offset > 0); + assert(ctx->istream_offset <= ctx->istream_writer.buf.size); + assert(((uint8_t*)ctx->istream_writer.buf.start)[ctx->istream_offset - 1] == + WASM_OPCODE_DISCARD); + ctx->istream_offset--; +} + +static WasmResult emit_discard(WasmReadInterpreterContext* ctx) { + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_DISCARD, -1)); + return WASM_OK; +} + +static WasmResult maybe_emit_discard(WasmReadInterpreterContext* ctx, + WasmType type, + int* out_discarded) { + int should_discard = type != WASM_TYPE_VOID && type != WASM_TYPE_ANY; + if (out_discarded) + *out_discarded = should_discard; + if (should_discard) + return emit_discard(ctx); + return WASM_OK; +} + +static WasmResult emit_discard_keep(WasmReadInterpreterContext* ctx, + uint32_t discard, + uint8_t keep) { + assert(discard != UINT32_MAX); + assert(keep <= 1); + if (discard > 0) { + if (discard == 1 && keep == 0) { + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_DISCARD, 0)); + } else { + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_DISCARD_KEEP, 0)); + CHECK_RESULT(emit_i32(ctx, discard)); + CHECK_RESULT(emit_i8(ctx, keep)); + } + } + return WASM_OK; +} + +static WasmResult emit_return(WasmReadInterpreterContext* ctx, + WasmType result_type) { + uint32_t discard_count = ctx->value_stack_size; + uint32_t keep_count = get_result_count(result_type); + CHECK_RESULT(emit_discard_keep(ctx, discard_count, keep_count)); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_RETURN, 0)); + return WASM_OK; +} + +static WasmResult append_fixup(WasmReadInterpreterContext* ctx, + WasmUint32VectorVector* fixups_vector, + uint32_t index) { + if (index >= fixups_vector->size) { + CHECK_ALLOC(ctx, wasm_resize_uint32_vector_vector( + ctx->allocator, fixups_vector, index + 1)); + } + WasmUint32Vector* fixups = &fixups_vector->data[index]; + uint32_t offset = get_istream_offset(ctx); + CHECK_ALLOC(ctx, wasm_append_uint32_value(ctx->allocator, fixups, &offset)); + return WASM_OK; +} + +static WasmResult emit_br_offset(WasmReadInterpreterContext* 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(WasmReadInterpreterContext* ctx, + uint32_t depth, + WasmDepthNode* node) { + WasmType expected_type = node->type; + assert(ctx->value_stack_size >= node->value_stack_size); + uint32_t discard_count = ctx->value_stack_size - node->value_stack_size; + uint8_t keep_count = get_result_count(expected_type); + CHECK_RESULT(emit_discard_keep(ctx, discard_count, keep_count)); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_BR, 0)); + CHECK_RESULT(emit_br_offset(ctx, depth, node->offset)); + return WASM_OK; +} + +static WasmResult emit_br_table_offset(WasmReadInterpreterContext* ctx, + uint32_t depth, + WasmDepthNode* node, + uint32_t discard_count) { + discard_count = ctx->value_stack_size - node->value_stack_size; + CHECK_RESULT(emit_br_offset(ctx, depth, node->offset)); + CHECK_RESULT(emit_i32(ctx, discard_count)); + return WASM_OK; +} + +static WasmResult emit_func_offset(WasmReadInterpreterContext* ctx, + WasmInterpreterFunc* 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 WasmInterpreterFunc* get_func(WasmReadInterpreterContext* ctx, + uint32_t func_index) { + assert(func_index < ctx->funcs.size); + return &ctx->funcs.data[func_index]; +} + +static WasmInterpreterImport* get_import(WasmReadInterpreterContext* ctx, + uint32_t import_index) { + assert(import_index < ctx->module->imports.size); + return &ctx->module->imports.data[import_index]; +} + +static WasmInterpreterFuncSignature* get_signature( + WasmReadInterpreterContext* ctx, + uint32_t sig_index) { + assert(sig_index < ctx->module->sigs.size); + return &ctx->module->sigs.data[sig_index]; +} + +static WasmInterpreterFuncSignature* get_func_signature( + WasmReadInterpreterContext* ctx, + WasmInterpreterFunc* func) { + return get_signature(ctx, func->sig_index); +} + +static WasmType get_local_index_type(WasmInterpreterFunc* func, + uint32_t local_index) { + assert(local_index < func->param_and_local_types.size); + return func->param_and_local_types.data[local_index]; +} + +static WasmResult push_depth_with_offset(WasmReadInterpreterContext* ctx, + WasmType type, + uint32_t offset) { + WasmDepthNode* node = + wasm_append_depth_node(ctx->allocator, &ctx->depth_stack); + CHECK_ALLOC_NULL(ctx, node); + node->type = type; + node->value_stack_size = ctx->value_stack_size; + node->offset = offset; +#if LOG + fprintf(stderr, " (%d): push depth %" PRIzd ":%s\n", ctx->value_stack_size, + ctx->depth_stack.size - 1, s_type_names[type]); +#endif + return WASM_OK; +} + +static WasmResult push_depth(WasmReadInterpreterContext* ctx, WasmType type) { + return push_depth_with_offset(ctx, type, WASM_INVALID_OFFSET); +} + +static void pop_depth(WasmReadInterpreterContext* ctx) { +#if LOG + fprintf(stderr, " (%d): pop depth %" PRIzd "\n", ctx->value_stack_size, + ctx->depth_stack.size - 1); +#endif + assert(ctx->depth_stack.size > 0); + ctx->depth_stack.size--; + ctx->depth_fixups.size = ctx->depth_stack.size; +} + +static uint32_t translate_depth(WasmReadInterpreterContext* ctx, + uint32_t depth) { + assert(depth < ctx->depth_stack.size); + return ctx->depth_stack.size - 1 - depth; +} + +static WasmResult fixup_top_depth(WasmReadInterpreterContext* ctx, + uint32_t offset) { + uint32_t top = ctx->depth_stack.size - 1; + if (top >= ctx->depth_fixups.size) { + /* nothing to fixup */ + return WASM_OK; + } + + WasmUint32Vector* 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 uint32_t translate_local_index(WasmReadInterpreterContext* ctx, + uint32_t local_index) { + assert(local_index < ctx->value_stack_size); + return ctx->value_stack_size - local_index; +} + +void on_error(uint32_t offset, const char* message, void* user_data) { + if (offset == WASM_INVALID_OFFSET) + fprintf(stderr, "error: %s\n", message); + else + fprintf(stderr, "error: @0x%08x: %s\n", offset, message); +} + +static WasmResult on_memory_initial_size_pages(uint32_t pages, + void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterMemory* memory = &ctx->module->memory; + memory->allocator = ctx->memory_allocator; + memory->page_size = pages; + memory->byte_size = pages * WASM_PAGE_SIZE; + CHECK_ALLOC_NULL( + ctx, memory->data = wasm_alloc_zero( + ctx->memory_allocator, memory->byte_size, WASM_DEFAULT_ALIGN)); + return WASM_OK; +} + +static WasmResult on_data_segment(uint32_t index, + uint32_t address, + const void* src_data, + uint32_t size, + void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterMemory* memory = &ctx->module->memory; + uint8_t* dst_data = memory->data; + memcpy(&dst_data[address], src_data, size); + return WASM_OK; +} + +static WasmResult on_signature_count(uint32_t count, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + CHECK_ALLOC(ctx, wasm_new_interpreter_func_signature_array( + ctx->allocator, &ctx->module->sigs, count)); + return WASM_OK; +} + +static WasmResult on_signature(uint32_t index, + WasmType result_type, + uint32_t param_count, + WasmType* param_types, + void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterFuncSignature* sig = get_signature(ctx, index); + sig->result_type = result_type; + + CHECK_ALLOC( + ctx, wasm_reserve_types(ctx->allocator, &sig->param_types, param_count)); + sig->param_types.size = param_count; + memcpy(sig->param_types.data, param_types, param_count * sizeof(WasmType)); + return WASM_OK; +} + +static WasmResult on_import_count(uint32_t count, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + CHECK_ALLOC(ctx, wasm_new_interpreter_import_array( + ctx->allocator, &ctx->module->imports, count)); + return WASM_OK; +} + +static WasmResult on_import(uint32_t index, + uint32_t sig_index, + WasmStringSlice module_name, + WasmStringSlice function_name, + void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterImport* import = &ctx->module->imports.data[index]; + CHECK_ALLOC_NULL_STR(ctx, import->module_name = wasm_dup_string_slice( + ctx->allocator, module_name)); + CHECK_ALLOC_NULL_STR(ctx, import->func_name = wasm_dup_string_slice( + ctx->allocator, function_name)); + assert(sig_index < ctx->module->sigs.size); + import->sig_index = sig_index; + return WASM_OK; +} + +static WasmResult on_function_signatures_count(uint32_t count, + void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + CHECK_ALLOC( + ctx, wasm_new_interpreter_func_array(ctx->allocator, &ctx->funcs, count)); + CHECK_ALLOC(ctx, wasm_resize_uint32_vector_vector(ctx->allocator, + &ctx->func_fixups, count)); + return WASM_OK; +} + +static WasmResult on_function_signature(uint32_t index, + uint32_t sig_index, + void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + assert(sig_index < ctx->module->sigs.size); + WasmInterpreterFunc* func = get_func(ctx, index); + func->offset = WASM_INVALID_OFFSET; + func->sig_index = sig_index; + return WASM_OK; +} + +static WasmResult on_function_bodies_count(uint32_t count, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + assert(count == ctx->funcs.size); + WASM_USE(ctx); + return WASM_OK; +} + +static WasmResult begin_function_body(uint32_t index, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterFunc* func = get_func(ctx, index); + WasmInterpreterFuncSignature* sig = get_signature(ctx, func->sig_index); + ctx->current_func = func; + func->offset = get_istream_offset(ctx); + + /* fixup function references */ + uint32_t i; + WasmUint32Vector* 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) { + CHECK_RESULT(wasm_append_type_value(ctx->allocator, + &func->param_and_local_types, + &sig->param_types.data[i])); + } + + ctx->value_stack_size = sig->param_types.size; + WASM_ZERO_MEMORY(ctx->last_expr); + return WASM_OK; +} + +static WasmResult end_function_body(uint32_t index, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + if (ctx->expr_stack.size != 0) { + print_error(ctx, "expression stack not empty on function exit! %d items", + (int)ctx->expr_stack.size); + return WASM_ERROR; + } + WasmInterpreterFunc* func = ctx->current_func; + WasmInterpreterFuncSignature* sig = get_signature(ctx, func->sig_index); + if (ctx->last_expr.opcode != WASM_OPCODE_RETURN) { + if (sig->result_type != WASM_TYPE_VOID) { + CHECK_RESULT(check_type(ctx, sig->result_type, ctx->last_expr.type, + " in function result")); + if (ctx->last_expr_was_discarded) + unemit_discard(ctx); + } + CHECK_RESULT(emit_return(ctx, sig->result_type)); + } + ctx->current_func = NULL; + ctx->value_stack_size = 0; + return WASM_OK; +} + +static WasmResult end_function_bodies_section(void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + if (ctx->start_func_index != INVALID_FUNC_INDEX) { + WasmInterpreterFunc* func = get_func(ctx, ctx->start_func_index); + assert(func->offset != WASM_INVALID_OFFSET); + ctx->module->start_func_offset = func->offset; + } + return WASM_OK; +} + +static WasmResult on_local_decl_count(uint32_t count, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + ctx->current_func->local_decl_count = count; + return WASM_OK; +} + +static WasmResult on_local_decl(uint32_t decl_index, + uint32_t count, + WasmType type, + void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterFunc* func = ctx->current_func; + func->local_count += count; + + uint32_t i; + for (i = 0; i < count; ++i) { + CHECK_RESULT(wasm_append_type_value(ctx->allocator, + &func->param_and_local_types, &type)); + } + + if (decl_index == func->local_decl_count - 1) { + /* last local declaration, allocate space for all locals. */ + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_ALLOCA, func->local_count)); + CHECK_RESULT(emit_i32(ctx, func->local_count)); + } + return WASM_OK; +} + +static WasmResult reduce(WasmReadInterpreterContext* ctx, + WasmInterpreterExpr* expr) { + int done = 0; + while (!done) { + done = 1; + + if (ctx->expr_stack.size == 0) { +#if LOG + fprintf(stderr, "%3" PRIzd "(%d): reduce: <- %s:%s\n", + ctx->expr_stack.size, ctx->value_stack_size, + s_opcode_name[expr->opcode], s_type_names[expr->type]); +#endif + + /* discard all top-level values. The last one is the return value, which + * we don't want to discard, but we won't know if this is the last + * expression until we get the end_function_body message. So we'll always + * write in a discard here, then remove it later if necessary. */ + CHECK_RESULT( + maybe_emit_discard(ctx, expr->type, &ctx->last_expr_was_discarded)); + ctx->last_expr = *expr; + } else { + WasmExprNode* top = &ctx->expr_stack.data[ctx->expr_stack.size - 1]; + assert(top->index < top->total); + +#if LOG + fprintf(stderr, "%3" PRIzd "(%d): reduce: %s(%d/%d) <- %s:%s\n", + ctx->expr_stack.size, ctx->value_stack_size, + s_opcode_name[top->expr.opcode], top->index, top->total, + s_opcode_name[expr->opcode], s_type_names[expr->type]); +#endif +#if LOG + if (top->expr.opcode == WASM_OPCODE_BR) { + fprintf(stderr, " : br depth %u\n", top->expr.br.depth); + } +#endif + + uint32_t cur_index = top->index++; + int is_expr_done = top->index == top->total; + + switch (top->expr.opcode) { + /* handles all unary and binary operators */ + default: + if (is_expr_done) { + CHECK_RESULT(emit_opcode(ctx, top->expr.opcode, 1 - top->total)); + } else { + WasmType expected_type; + if (cur_index == 0) { + expected_type = s_opcode_type1[top->expr.opcode]; + } else if (cur_index == 1) { + expected_type = s_opcode_type2[top->expr.opcode]; + } else { + assert(0); + break; + } + /* TODO use opcode name here */ + CHECK_RESULT(check_type_exact(ctx, expected_type, expr->type, "")); + } + break; + + case WASM_OPCODE_BLOCK: + if (is_expr_done) + unify_type(&top->expr.type, expr->type); + if (top->expr.type == WASM_TYPE_VOID || !is_expr_done) + CHECK_RESULT(maybe_emit_discard(ctx, expr->type, NULL)); + if (is_expr_done) { + CHECK_RESULT(fixup_top_depth(ctx, get_istream_offset(ctx))); + pop_depth(ctx); + reset_value_stack(ctx, &top->expr); + } + break; + + case WASM_OPCODE_BR: { + assert(cur_index == 0 && is_expr_done); + uint32_t depth = top->expr.br.depth; + WasmDepthNode* node = get_depth_node(ctx, depth); + CHECK_RESULT(unify_and_check_type_exact(ctx, &node->type, expr->type, + " in br")); + CHECK_RESULT(emit_br(ctx, depth, node)); + break; + } + + case WASM_OPCODE_BR_IF: { + uint32_t depth = top->expr.br.depth; + WasmDepthNode* node = get_depth_node(ctx, depth); + if (cur_index == 0) { + CHECK_RESULT(unify_and_check_type_exact(ctx, &node->type, + expr->type, " in br_if")); + } else { + assert(cur_index == 1 && is_expr_done); + CHECK_RESULT( + check_type_exact(ctx, WASM_TYPE_I32, expr->type, " in br_if")); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_I32_EQZ, 0)); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_BR_IF, -1)); + uint32_t fixup_br_offset = get_istream_offset(ctx); + CHECK_RESULT(emit_i32(ctx, WASM_INVALID_OFFSET)); + CHECK_RESULT(emit_br(ctx, depth, node)); + CHECK_RESULT(emit_i32_at(ctx, fixup_br_offset, + get_istream_offset(ctx))); + } + break; + } + + case WASM_OPCODE_BR_TABLE: { + assert(cur_index == 0 && is_expr_done); + CHECK_RESULT( + check_type_exact(ctx, WASM_TYPE_I32, expr->type, " in br_table")); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_BR_TABLE, -1)); + CHECK_RESULT(emit_i32(ctx, top->expr.br_table.num_targets)); + CHECK_RESULT(emit_i32(ctx, top->expr.br_table.table_offset)); + break; + } + + case WASM_OPCODE_CALL_FUNCTION: { + WasmInterpreterFunc* func = get_func(ctx, top->expr.call.func_index); + WasmInterpreterFuncSignature* sig = get_func_signature(ctx, func); + CHECK_RESULT(check_type_exact(ctx, sig->param_types.data[cur_index], + expr->type, " in call")); + if (is_expr_done) { + int32_t num_results = get_result_count(sig->result_type); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_CALL_FUNCTION, + num_results - top->total)); + CHECK_RESULT( + emit_func_offset(ctx, func, top->expr.call.func_index)); + } + break; + } + + case WASM_OPCODE_CALL_IMPORT: { + WasmInterpreterImport* import = + get_import(ctx, top->expr.call_import.import_index); + WasmInterpreterFuncSignature* sig = + get_signature(ctx, import->sig_index); + CHECK_RESULT(check_type_exact(ctx, sig->param_types.data[cur_index], + expr->type, " in call_import")); + if (is_expr_done) { + int32_t num_results = get_result_count(sig->result_type); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_CALL_IMPORT, + num_results - top->total)); + CHECK_RESULT(emit_i32(ctx, top->expr.call_import.import_index)); + } + break; + } + + case WASM_OPCODE_CALL_INDIRECT: { + WasmInterpreterFuncSignature* sig = + get_signature(ctx, top->expr.call_indirect.sig_index); + if (cur_index == 0) { + CHECK_RESULT(check_type_exact(ctx, WASM_TYPE_I32, expr->type, + " in call_indirect")); + } else { + CHECK_RESULT(check_type_exact(ctx, + sig->param_types.data[cur_index - 1], + expr->type, " in call_indirect")); + } + if (is_expr_done) { + int32_t num_results = get_result_count(sig->result_type); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_CALL_INDIRECT, + num_results - top->total)); + CHECK_RESULT(emit_i32(ctx, top->expr.call_indirect.sig_index)); + /* the callee cleans up the params for us, but we have to clean up + * the function table index */ + CHECK_RESULT(emit_discard_keep(ctx, 1, num_results)); + } + break; + } + + case WASM_OPCODE_GROW_MEMORY: + assert(cur_index == 0 && is_expr_done); + CHECK_RESULT(check_type_exact(ctx, WASM_TYPE_I32, expr->type, + " in grow_memory")); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_GROW_MEMORY, 0)); + break; + + case WASM_OPCODE_IF: + if (cur_index == 0) { + /* after cond */ + CHECK_RESULT( + check_type_exact(ctx, WASM_TYPE_I32, expr->type, " in if")); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_I32_EQZ, 0)); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_BR_IF, -1)); + top->expr.if_.fixup_offset = get_istream_offset(ctx); + CHECK_RESULT(emit_i32(ctx, WASM_INVALID_OFFSET)); + } else { + /* after true */ + assert(cur_index == 1 && is_expr_done); + /* discard the last value, if there is one; if is always void */ + CHECK_RESULT(maybe_emit_discard(ctx, expr->type, NULL)); + CHECK_RESULT(emit_i32_at(ctx, top->expr.if_.fixup_offset, + get_istream_offset(ctx))); + } + break; + + case WASM_OPCODE_IF_ELSE: { + if (cur_index == 0) { + /* after cond */ + CHECK_RESULT(check_type_exact(ctx, WASM_TYPE_I32, expr->type, + " in if_else")); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_I32_EQZ, 0)); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_BR_IF, -1)); + top->expr.if_else.fixup_cond_offset = get_istream_offset(ctx); + top->expr.if_else.value_stack_size = ctx->value_stack_size; + CHECK_RESULT(emit_i32(ctx, WASM_INVALID_OFFSET)); + } else { + CHECK_RESULT(unify_and_check_type(ctx, &top->expr.type, expr->type, + " in if_else")); + if (top->expr.type == WASM_TYPE_VOID) + CHECK_RESULT(maybe_emit_discard(ctx, expr->type, NULL)); + + if (cur_index == 1) { + /* after true */ + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_BR, 0)); + top->expr.if_else.fixup_true_offset = get_istream_offset(ctx); + CHECK_RESULT(emit_i32(ctx, WASM_INVALID_OFFSET)); + CHECK_RESULT(emit_i32_at(ctx, top->expr.if_else.fixup_cond_offset, + get_istream_offset(ctx))); + /* reset the value stack for the other branch arm */ + ctx->value_stack_size = top->expr.if_else.value_stack_size; + } else { + /* after false */ + assert(cur_index == 2 && is_expr_done); + CHECK_RESULT(emit_i32_at(ctx, top->expr.if_else.fixup_true_offset, + get_istream_offset(ctx))); + + /* weird case: if the true branch's type is not VOID or ANY, and + * the false branch's type is any, we need to adjust the stack up + * to match the other branch. + * + * A reduced expression's type is only ANY if it requires + * non-local control flow. In that case, we will not have + * adjusted the stack for that value, but the only normal control + * flow that will produce a value is from the true branch. */ + if (top->expr.type != WASM_TYPE_VOID && + top->expr.type != WASM_TYPE_ANY && + expr->type == WASM_TYPE_ANY) { + adjust_value_stack(ctx, 1); + } + } + } + break; + } + + case WASM_OPCODE_I32_LOAD8_S: + case WASM_OPCODE_I32_LOAD8_U: + case WASM_OPCODE_I32_LOAD16_S: + case WASM_OPCODE_I32_LOAD16_U: + case WASM_OPCODE_I64_LOAD8_S: + case WASM_OPCODE_I64_LOAD8_U: + case WASM_OPCODE_I64_LOAD16_S: + case WASM_OPCODE_I64_LOAD16_U: + case WASM_OPCODE_I64_LOAD32_S: + case WASM_OPCODE_I64_LOAD32_U: + case WASM_OPCODE_I32_LOAD: + case WASM_OPCODE_I64_LOAD: + case WASM_OPCODE_F32_LOAD: + case WASM_OPCODE_F64_LOAD: + assert(cur_index == 0 && is_expr_done); + /* TODO use opcode name here */ + CHECK_RESULT( + check_type_exact(ctx, WASM_TYPE_I32, expr->type, " in load")); + CHECK_RESULT(emit_opcode(ctx, top->expr.opcode, 0)); + CHECK_RESULT(emit_i32(ctx, top->expr.load.mem_offset)); + break; + + case WASM_OPCODE_LOOP: { + if (is_expr_done) + unify_type(&top->expr.type, expr->type); + if (top->expr.type == WASM_TYPE_VOID || !is_expr_done) + CHECK_RESULT(maybe_emit_discard(ctx, expr->type, NULL)); + if (is_expr_done) { + pop_depth(ctx); /* continue */ + CHECK_RESULT(fixup_top_depth(ctx, get_istream_offset(ctx))); + pop_depth(ctx); /* exit */ + reset_value_stack(ctx, &top->expr); + } + break; + } + + case WASM_OPCODE_RETURN: { + WasmInterpreterFuncSignature* sig = + get_func_signature(ctx, ctx->current_func); + CHECK_RESULT(check_type_exact(ctx, sig->result_type, expr->type, + " in return")); + CHECK_RESULT(emit_return(ctx, sig->result_type)); + adjust_value_stack(ctx, -get_result_count(sig->result_type)); + break; + } + + case WASM_OPCODE_SELECT: { + if (is_expr_done) { + assert(cur_index == 2); + CHECK_RESULT( + check_type_exact(ctx, WASM_TYPE_I32, expr->type, " in select")); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_SELECT, -2)); + } else { + assert(cur_index < 2); + CHECK_RESULT(unify_and_check_type_exact(ctx, &top->expr.type, + expr->type, " in select")); + } + break; + } + + case WASM_OPCODE_SET_LOCAL: { + assert(cur_index == 0 && is_expr_done); + CHECK_RESULT(check_type_exact(ctx, top->expr.type, expr->type, + " in set_local")); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_SET_LOCAL, 0)); + uint32_t local_index = + translate_local_index(ctx, top->expr.set_local.local_index); + CHECK_RESULT(emit_i32(ctx, local_index)); + break; + } + + case WASM_OPCODE_I32_STORE8: + case WASM_OPCODE_I32_STORE16: + case WASM_OPCODE_I64_STORE8: + case WASM_OPCODE_I64_STORE16: + case WASM_OPCODE_I64_STORE32: + case WASM_OPCODE_I32_STORE: + case WASM_OPCODE_I64_STORE: + case WASM_OPCODE_F32_STORE: + case WASM_OPCODE_F64_STORE: + if (cur_index == 0) { + /* TODO use opcode name here */ + CHECK_RESULT( + check_type_exact(ctx, WASM_TYPE_I32, expr->type, " in store")); + } else { + assert(cur_index == 1 && is_expr_done); + CHECK_RESULT( + check_type_exact(ctx, top->expr.type, expr->type, " in store")); + CHECK_RESULT(emit_opcode(ctx, top->expr.opcode, -1)); + CHECK_RESULT(emit_i32(ctx, top->expr.store.mem_offset)); + } + break; + + case WASM_OPCODE_F32_CONST: + case WASM_OPCODE_F64_CONST: + case WASM_OPCODE_GET_LOCAL: + case WASM_OPCODE_I32_CONST: + case WASM_OPCODE_I64_CONST: + case WASM_OPCODE_MEMORY_SIZE: + case WASM_OPCODE_NOP: + case WASM_OPCODE_UNREACHABLE: + assert(0); + break; + } + + if (is_expr_done) { + /* "recurse" and reduce the current expr */ + expr = &top->expr; + ctx->expr_stack.size--; + done = 0; + } + } + } + + return WASM_OK; +} + +static WasmResult shift(WasmReadInterpreterContext* ctx, + WasmInterpreterExpr* expr, + uint32_t count) { + if (count > 0) { +#if LOG + fprintf(stderr, "%3" PRIzd "(%d): shift: %s:%s %u\n", ctx->expr_stack.size, + ctx->value_stack_size, s_opcode_name[expr->opcode], + s_type_names[expr->type], count); +#endif + WasmExprNode* node = + wasm_append_expr_node(ctx->allocator, &ctx->expr_stack); + CHECK_ALLOC_NULL(ctx, node); + node->expr = *expr; + node->index = 0; + node->total = count; + return WASM_OK; + } else { + adjust_value_stack(ctx, get_result_count(expr->type)); + return reduce(ctx, expr); + } +} + +static WasmResult on_unary_expr(WasmOpcode opcode, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = s_opcode_rtype[opcode]; + expr.opcode = opcode; + return shift(ctx, &expr, 1); +} + +static WasmResult on_binary_expr(WasmOpcode opcode, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = s_opcode_rtype[opcode]; + expr.opcode = opcode; + return shift(ctx, &expr, 2); +} + +static WasmResult on_block_expr(uint32_t count, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = count ? WASM_TYPE_ANY : WASM_TYPE_VOID; + expr.opcode = WASM_OPCODE_BLOCK; + expr.block.value_stack_size = ctx->value_stack_size; + CHECK_RESULT(push_depth(ctx, expr.type)); + return shift(ctx, &expr, count); +} + +static WasmResult on_br_expr(uint32_t depth, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + CHECK_DEPTH(ctx, depth); + WasmInterpreterExpr expr; + expr.type = WASM_TYPE_ANY; + expr.opcode = WASM_OPCODE_BR; + expr.br.depth = translate_depth(ctx, depth); + return shift(ctx, &expr, 1); +} + +static WasmResult on_br_if_expr(uint32_t depth, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + CHECK_DEPTH(ctx, depth); + WasmInterpreterExpr expr; + expr.type = WASM_TYPE_VOID; + expr.opcode = WASM_OPCODE_BR_IF; + expr.br.depth = translate_depth(ctx, depth); + return shift(ctx, &expr, 2); +} + +static WasmResult on_br_table_expr(uint32_t num_targets, + uint32_t* target_depths, + uint32_t default_target_depth, + void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = WASM_TYPE_ANY; + expr.opcode = WASM_OPCODE_BR_TABLE; + expr.br_table.num_targets = num_targets; + + /* we need to parse the "key" expression before we can execute the br_table. + * Rather than store the target_depths in an Expr, we just write them out + * into the instruction stream and just jump over it. */ + uint32_t fixup_br_offset = get_istream_offset(ctx); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_BR, 0)); + CHECK_RESULT(emit_i32(ctx, WASM_INVALID_OFFSET)); + + /* write the branch table as (offset, discard count) pairs */ + expr.br_table.table_offset = get_istream_offset(ctx); + + WasmDepthNode* node; + uint32_t discard_count; + uint32_t i; + for (i = 0; i < num_targets; ++i) { + uint32_t depth = translate_depth(ctx, target_depths[i]); + node = get_depth_node(ctx, depth); + discard_count = ctx->value_stack_size - node->value_stack_size; + CHECK_RESULT(unify_and_check_type_exact(ctx, &node->type, WASM_TYPE_VOID, + " in br_table")); + CHECK_RESULT(emit_br_table_offset(ctx, depth, node, discard_count)); + } + /* write default target */ + node = get_depth_node(ctx, translate_depth(ctx, default_target_depth)); + discard_count = ctx->value_stack_size - node->value_stack_size; + CHECK_RESULT(unify_and_check_type_exact(ctx, &node->type, WASM_TYPE_VOID, + " in br_table")); + CHECK_RESULT( + emit_br_table_offset(ctx, default_target_depth, node, discard_count)); + + CHECK_RESULT(emit_i32_at(ctx, fixup_br_offset, get_istream_offset(ctx))); + return shift(ctx, &expr, 1); +} + +static WasmResult on_call_expr(uint32_t func_index, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + assert(func_index < ctx->funcs.size); + WasmInterpreterFunc* func = get_func(ctx, func_index); + WasmInterpreterFuncSignature* sig = get_func_signature(ctx, func); + WasmInterpreterExpr expr; + expr.type = sig->result_type; + expr.opcode = WASM_OPCODE_CALL_FUNCTION; + expr.call.func_index = func_index; + return shift(ctx, &expr, sig->param_types.size); +} + +static WasmResult on_call_import_expr(uint32_t import_index, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + assert(import_index < ctx->module->imports.size); + WasmInterpreterImport* import = get_import(ctx, import_index); + WasmInterpreterFuncSignature* sig = get_signature(ctx, import->sig_index); + WasmInterpreterExpr expr; + expr.type = sig->result_type; + expr.opcode = WASM_OPCODE_CALL_IMPORT; + expr.call_import.import_index = import_index; + return shift(ctx, &expr, sig->param_types.size); +} + +static WasmResult on_call_indirect_expr(uint32_t sig_index, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterFuncSignature* sig = get_signature(ctx, sig_index); + WasmInterpreterExpr expr; + expr.type = sig->result_type; + expr.opcode = WASM_OPCODE_CALL_INDIRECT; + expr.call_indirect.sig_index = sig_index; + return shift(ctx, &expr, sig->param_types.size + 1); +} + +static WasmResult on_i32_const_expr(uint32_t value, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = WASM_TYPE_I32; + expr.opcode = WASM_OPCODE_I32_CONST; + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_I32_CONST, 1)); + CHECK_RESULT(emit_i32(ctx, value)); + return reduce(ctx, &expr); +} + +static WasmResult on_i64_const_expr(uint64_t value, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = WASM_TYPE_I64; + expr.opcode = WASM_OPCODE_I64_CONST; + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_I64_CONST, 1)); + CHECK_RESULT(emit_i64(ctx, value)); + return reduce(ctx, &expr); +} + +static WasmResult on_f32_const_expr(uint32_t value_bits, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = WASM_TYPE_F32; + expr.opcode = WASM_OPCODE_F32_CONST; + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_F32_CONST, 1)); + CHECK_RESULT(emit_i32(ctx, value_bits)); + return reduce(ctx, &expr); +} + +static WasmResult on_f64_const_expr(uint64_t value_bits, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = WASM_TYPE_F64; + expr.opcode = WASM_OPCODE_F64_CONST; + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_F64_CONST, 1)); + CHECK_RESULT(emit_i64(ctx, value_bits)); + return reduce(ctx, &expr); +} + +static WasmResult on_get_local_expr(uint32_t local_index, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = get_local_index_type(ctx->current_func, local_index); + expr.opcode = WASM_OPCODE_GET_LOCAL; + expr.get_local.local_index = translate_local_index(ctx, local_index); + CHECK_LOCAL(ctx, local_index); + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_GET_LOCAL, 1)); + CHECK_RESULT(emit_i32(ctx, expr.get_local.local_index)); + return reduce(ctx, &expr); +} + +static WasmResult on_grow_memory_expr(void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = WASM_TYPE_I32; + expr.opcode = WASM_OPCODE_GROW_MEMORY; + return shift(ctx, &expr, 1); +} + +static WasmResult on_if_expr(void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = WASM_TYPE_VOID; + expr.opcode = WASM_OPCODE_IF; + return shift(ctx, &expr, 2); +} + +static WasmResult on_if_else_expr(void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = WASM_TYPE_ANY; + expr.opcode = WASM_OPCODE_IF_ELSE; + return shift(ctx, &expr, 3); +} + +static WasmResult on_load_expr(WasmOpcode opcode, + uint32_t alignment_log2, + uint32_t offset, + void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = s_opcode_rtype[opcode]; + expr.opcode = opcode; + expr.load.mem_offset = offset; + expr.load.alignment_log2 = alignment_log2; + return shift(ctx, &expr, 1); +} + +static WasmResult on_loop_expr(uint32_t count, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = count ? WASM_TYPE_ANY : WASM_TYPE_VOID; + expr.opcode = WASM_OPCODE_LOOP; + expr.loop.value_stack_size = ctx->value_stack_size; + CHECK_RESULT(push_depth(ctx, expr.type)); /* exit */ + CHECK_RESULT(push_depth_with_offset(ctx, WASM_TYPE_VOID, + get_istream_offset(ctx))); /* continue */ + return shift(ctx, &expr, count); +} + +static WasmResult on_memory_size_expr(void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = WASM_TYPE_I32; + expr.opcode = WASM_OPCODE_MEMORY_SIZE; + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_MEMORY_SIZE, 0)); + return reduce(ctx, &expr); +} + +static WasmResult on_nop_expr(void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = WASM_TYPE_VOID; + expr.opcode = WASM_OPCODE_NOP; + return reduce(ctx, &expr); +} + +static WasmResult on_return_expr(void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterFuncSignature* sig = + get_func_signature(ctx, ctx->current_func); + WasmInterpreterExpr expr; + expr.type = WASM_TYPE_ANY; + expr.opcode = WASM_OPCODE_RETURN; + return shift(ctx, &expr, get_result_count(sig->result_type)); +} + +static WasmResult on_select_expr(void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = WASM_TYPE_ANY; + expr.opcode = WASM_OPCODE_SELECT; + return shift(ctx, &expr, 3); +} + +static WasmResult on_set_local_expr(uint32_t local_index, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = get_local_index_type(ctx->current_func, local_index); + expr.opcode = WASM_OPCODE_SET_LOCAL; + expr.set_local.local_index = local_index; + CHECK_LOCAL(ctx, local_index); + return shift(ctx, &expr, 1); +} + +static WasmResult on_store_expr(WasmOpcode opcode, + uint32_t alignment_log2, + uint32_t offset, + void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = s_opcode_rtype[opcode]; + expr.opcode = opcode; + expr.store.mem_offset = offset; + expr.store.alignment_log2 = alignment_log2; + return shift(ctx, &expr, 2); +} + +static WasmResult on_unreachable_expr(void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExpr expr; + expr.type = WASM_TYPE_ANY; + expr.opcode = WASM_OPCODE_UNREACHABLE; + CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_UNREACHABLE, 0)); + return reduce(ctx, &expr); +} + +static WasmResult on_function_table_count(uint32_t count, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + CHECK_ALLOC(ctx, wasm_new_interpreter_func_table_entry_array( + ctx->allocator, &ctx->module->func_table, count)); + return WASM_OK; +} + +static WasmResult on_function_table_entry(uint32_t index, + uint32_t func_index, + void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + assert(index < ctx->module->func_table.size); + WasmInterpreterFuncTableEntry* entry = &ctx->module->func_table.data[index]; + WasmInterpreterFunc* func = get_func(ctx, func_index); + entry->sig_index = func->sig_index; + assert(func->offset != WASM_INVALID_OFFSET); + entry->func_offset = func->offset; + return WASM_OK; +} + +static WasmResult on_start_function(uint32_t func_index, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + /* can't get the function offset yet, because we haven't parsed the + * functions. Just store the function index and resolve it later in + * end_function_bodies_section. */ + assert(func_index < ctx->funcs.size); + ctx->start_func_index = func_index; + return WASM_OK; +} + +static WasmResult on_export_count(uint32_t count, void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + CHECK_ALLOC(ctx, wasm_new_interpreter_export_array( + ctx->allocator, &ctx->module->exports, count)); + return WASM_OK; +} + +static WasmResult on_export(uint32_t index, + uint32_t func_index, + WasmStringSlice name, + void* user_data) { + WasmReadInterpreterContext* ctx = user_data; + WasmInterpreterExport* export = &ctx->module->exports.data[index]; + WasmInterpreterFunc* func = get_func(ctx, func_index); + CHECK_ALLOC_NULL_STR( + ctx, export->name = wasm_dup_string_slice(ctx->allocator, name)); + export->sig_index = func->sig_index; + export->func_offset = func->offset; + return WASM_OK; +} + +static WasmBinaryReader s_binary_reader = { + .user_data = NULL, + .on_error = &on_error, + + .on_memory_initial_size_pages = &on_memory_initial_size_pages, + + .on_data_segment = &on_data_segment, + + .on_signature_count = &on_signature_count, + .on_signature = &on_signature, + + .on_import_count = &on_import_count, + .on_import = &on_import, + + .on_function_signatures_count = &on_function_signatures_count, + .on_function_signature = &on_function_signature, + + .on_function_bodies_count = &on_function_bodies_count, + .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, + .on_br_expr = &on_br_expr, + .on_br_if_expr = &on_br_if_expr, + .on_br_table_expr = &on_br_table_expr, + .on_call_expr = &on_call_expr, + .on_call_import_expr = &on_call_import_expr, + .on_call_indirect_expr = &on_call_indirect_expr, + .on_compare_expr = &on_binary_expr, + .on_i32_const_expr = &on_i32_const_expr, + .on_i64_const_expr = &on_i64_const_expr, + .on_f32_const_expr = &on_f32_const_expr, + .on_f64_const_expr = &on_f64_const_expr, + .on_convert_expr = &on_unary_expr, + .on_get_local_expr = &on_get_local_expr, + .on_grow_memory_expr = &on_grow_memory_expr, + .on_if_expr = &on_if_expr, + .on_if_else_expr = &on_if_else_expr, + .on_load_expr = &on_load_expr, + .on_loop_expr = &on_loop_expr, + .on_memory_size_expr = &on_memory_size_expr, + .on_nop_expr = &on_nop_expr, + .on_return_expr = &on_return_expr, + .on_select_expr = &on_select_expr, + .on_set_local_expr = &on_set_local_expr, + .on_store_expr = &on_store_expr, + .on_unary_expr = &on_unary_expr, + .on_unreachable_expr = &on_unreachable_expr, + .end_function_body = &end_function_body, + .end_function_bodies_section = &end_function_bodies_section, + + .on_function_table_count = &on_function_table_count, + .on_function_table_entry = &on_function_table_entry, + + .on_start_function = &on_start_function, + + .on_export_count = &on_export_count, + .on_export = &on_export, +}; + +static void destroy_context(WasmReadInterpreterContext* ctx) { + wasm_destroy_expr_node_vector(ctx->allocator, &ctx->expr_stack); + wasm_destroy_depth_node_vector(ctx->allocator, &ctx->depth_stack); + WASM_DESTROY_VECTOR_AND_ELEMENTS(ctx->allocator, ctx->depth_fixups, + uint32_vector); + WASM_DESTROY_VECTOR_AND_ELEMENTS(ctx->allocator, ctx->func_fixups, + uint32_vector); +} + +WasmResult wasm_read_binary_interpreter( + struct WasmAllocator* allocator, + struct WasmAllocator* memory_allocator, + const void* data, + size_t size, + struct WasmReadBinaryOptions* options, + struct WasmInterpreterModule* out_module) { + WasmReadInterpreterContext ctx; + WASM_ZERO_MEMORY(ctx); + ctx.allocator = allocator; + ctx.memory_allocator = memory_allocator; + ctx.module = out_module; + ctx.start_func_index = INVALID_FUNC_INDEX; + CHECK_RESULT(wasm_init_mem_writer(allocator, &ctx.istream_writer)); + + WasmBinaryReader reader; + WASM_ZERO_MEMORY(reader); + reader = s_binary_reader; + reader.user_data = &ctx; + + WasmResult result = wasm_read_binary(allocator, data, size, &reader, options); + if (result == WASM_OK) { + wasm_steal_mem_writer_output_buffer(&ctx.istream_writer, + &out_module->istream); + out_module->istream.size = ctx.istream_offset; + } + destroy_context(&ctx); + return result; +} |