summaryrefslogtreecommitdiff
path: root/src/interpreter.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/interpreter.cc')
-rw-r--r--src/interpreter.cc2402
1 files changed, 2402 insertions, 0 deletions
diff --git a/src/interpreter.cc b/src/interpreter.cc
new file mode 100644
index 00000000..1630c34a
--- /dev/null
+++ b/src/interpreter.cc
@@ -0,0 +1,2402 @@
+/*
+ * 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 "interpreter.h"
+
+#include <assert.h>
+#include <inttypes.h>
+#include <math.h>
+
+#include "stream.h"
+
+#define INITIAL_ISTREAM_CAPACITY (64 * 1024)
+
+static const char* s_interpreter_opcode_name[256];
+
+#define CHECK_RESULT(expr) \
+ do { \
+ if (WABT_FAILED(expr)) \
+ return WABT_ERROR; \
+ } while (0)
+
+/* TODO(binji): It's annoying to have to have an initializer function, but it
+ * seems to be necessary as g++ doesn't allow non-trival designated
+ * initializers (e.g. [314] = "blah") */
+static void init_interpreter_opcode_table(void) {
+ static bool s_initialized = false;
+ if (!s_initialized) {
+#define V(rtype, type1, type2, mem_size, code, NAME, text) \
+ s_interpreter_opcode_name[code] = text;
+
+ WABT_FOREACH_OPCODE(V)
+ s_interpreter_opcode_name[WABT_OPCODE_ALLOCA] = "alloca";
+ s_interpreter_opcode_name[WABT_OPCODE_BR_UNLESS] = "br_unless";
+ s_interpreter_opcode_name[WABT_OPCODE_CALL_HOST] = "call_host";
+ s_interpreter_opcode_name[WABT_OPCODE_DATA] = "data";
+ s_interpreter_opcode_name[WABT_OPCODE_DROP_KEEP] = "drop_keep";
+
+#undef V
+ }
+}
+
+static const char* wabt_get_interpreter_opcode_name(uint8_t opcode) {
+ init_interpreter_opcode_table();
+ return s_interpreter_opcode_name[opcode];
+}
+
+void wabt_init_interpreter_environment(WabtInterpreterEnvironment* env) {
+ WABT_ZERO_MEMORY(*env);
+ wabt_init_output_buffer(&env->istream, INITIAL_ISTREAM_CAPACITY);
+}
+
+static void wabt_destroy_interpreter_func_signature(
+ WabtInterpreterFuncSignature* sig) {
+ wabt_destroy_type_vector(&sig->param_types);
+ wabt_destroy_type_vector(&sig->result_types);
+}
+
+static void wabt_destroy_interpreter_func(WabtInterpreterFunc* func) {
+ if (!func->is_host)
+ wabt_destroy_type_vector(&func->defined.param_and_local_types);
+}
+
+static void wabt_destroy_interpreter_memory(WabtInterpreterMemory* memory) {
+ wabt_free(memory->data);
+}
+
+static void wabt_destroy_interpreter_table(WabtInterpreterTable* table) {
+ wabt_destroy_uint32_array(&table->func_indexes);
+}
+
+static void wabt_destroy_interpreter_import(WabtInterpreterImport* import) {
+ wabt_destroy_string_slice(&import->module_name);
+ wabt_destroy_string_slice(&import->field_name);
+}
+
+static void wabt_destroy_interpreter_module(WabtInterpreterModule* module) {
+ wabt_destroy_interpreter_export_vector(&module->exports);
+ wabt_destroy_binding_hash(&module->export_bindings);
+ wabt_destroy_string_slice(&module->name);
+ if (!module->is_host) {
+ WABT_DESTROY_ARRAY_AND_ELEMENTS(module->defined.imports,
+ interpreter_import);
+ }
+}
+
+void wabt_destroy_interpreter_environment(WabtInterpreterEnvironment* env) {
+ WABT_DESTROY_VECTOR_AND_ELEMENTS(env->modules, interpreter_module);
+ WABT_DESTROY_VECTOR_AND_ELEMENTS(env->sigs, interpreter_func_signature);
+ WABT_DESTROY_VECTOR_AND_ELEMENTS(env->funcs, interpreter_func);
+ WABT_DESTROY_VECTOR_AND_ELEMENTS(env->memories, interpreter_memory);
+ WABT_DESTROY_VECTOR_AND_ELEMENTS(env->tables, interpreter_table);
+ wabt_destroy_interpreter_global_vector(&env->globals);
+ wabt_destroy_output_buffer(&env->istream);
+ wabt_destroy_binding_hash(&env->module_bindings);
+ wabt_destroy_binding_hash(&env->registered_module_bindings);
+}
+
+WabtInterpreterEnvironmentMark wabt_mark_interpreter_environment(
+ WabtInterpreterEnvironment* env) {
+ WabtInterpreterEnvironmentMark mark;
+ WABT_ZERO_MEMORY(mark);
+ mark.modules_size = env->modules.size;
+ mark.sigs_size = env->sigs.size;
+ mark.funcs_size = env->funcs.size;
+ mark.memories_size = env->memories.size;
+ mark.tables_size = env->tables.size;
+ mark.globals_size = env->globals.size;
+ mark.istream_size = env->istream.size;
+ return mark;
+}
+
+void wabt_reset_interpreter_environment_to_mark(
+ WabtInterpreterEnvironment* env,
+ WabtInterpreterEnvironmentMark mark) {
+ size_t i;
+
+#define DESTROY_PAST_MARK(destroy_name, names) \
+ do { \
+ assert(mark.names##_size <= env->names.size); \
+ for (i = mark.names##_size; i < env->names.size; ++i) \
+ wabt_destroy_interpreter_##destroy_name(&env->names.data[i]); \
+ env->names.size = mark.names##_size; \
+ } while (0)
+
+ /* Destroy entries in the binding hash. */
+ for (i = mark.modules_size; i < env->modules.size; ++i) {
+ const WabtStringSlice* name = &env->modules.data[i].name;
+ if (!wabt_string_slice_is_empty(name))
+ wabt_remove_binding(&env->module_bindings, name);
+ }
+
+ /* registered_module_bindings maps from an arbitrary name to a module index,
+ * so we have to iterate through the entire table to find entries to remove.
+ */
+ for (i = 0; i < env->registered_module_bindings.entries.capacity; ++i) {
+ WabtBindingHashEntry* entry =
+ &env->registered_module_bindings.entries.data[i];
+ if (!wabt_hash_entry_is_free(entry) &&
+ entry->binding.index >= (int)mark.modules_size) {
+ wabt_remove_binding(&env->registered_module_bindings,
+ &entry->binding.name);
+ }
+ }
+
+ DESTROY_PAST_MARK(module, modules);
+ DESTROY_PAST_MARK(func_signature, sigs);
+ DESTROY_PAST_MARK(func, funcs);
+ DESTROY_PAST_MARK(memory, memories);
+ DESTROY_PAST_MARK(table, tables);
+ env->globals.size = mark.globals_size;
+ env->istream.size = mark.istream_size;
+
+#undef DESTROY_PAST_MARK
+}
+
+WabtInterpreterModule* wabt_append_host_module(WabtInterpreterEnvironment* env,
+ WabtStringSlice name) {
+ WabtInterpreterModule* module = wabt_append_interpreter_module(&env->modules);
+ module->name = wabt_dup_string_slice(name);
+ module->memory_index = WABT_INVALID_INDEX;
+ module->table_index = WABT_INVALID_INDEX;
+ module->is_host = true;
+
+ WabtStringSlice dup_name = wabt_dup_string_slice(name);
+ WabtBinding* binding =
+ wabt_insert_binding(&env->registered_module_bindings, &dup_name);
+ binding->index = env->modules.size - 1;
+ return module;
+}
+
+void wabt_init_interpreter_thread(WabtInterpreterEnvironment* env,
+ WabtInterpreterThread* thread,
+ WabtInterpreterThreadOptions* options) {
+ WABT_ZERO_MEMORY(*thread);
+ wabt_new_interpreter_value_array(&thread->value_stack,
+ options->value_stack_size);
+ wabt_new_uint32_array(&thread->call_stack, options->call_stack_size);
+ thread->env = env;
+ thread->value_stack_top = thread->value_stack.data;
+ thread->value_stack_end = thread->value_stack.data + thread->value_stack.size;
+ thread->call_stack_top = thread->call_stack.data;
+ thread->call_stack_end = thread->call_stack.data + thread->call_stack.size;
+ thread->pc = options->pc;
+}
+
+WabtInterpreterResult wabt_push_thread_value(WabtInterpreterThread* thread,
+ WabtInterpreterValue value) {
+ if (thread->value_stack_top >= thread->value_stack_end)
+ return WABT_INTERPRETER_TRAP_VALUE_STACK_EXHAUSTED;
+ *thread->value_stack_top++ = value;
+ return WABT_INTERPRETER_OK;
+}
+
+WabtInterpreterExport* wabt_get_interpreter_export_by_name(
+ WabtInterpreterModule* module,
+ const WabtStringSlice* name) {
+ int field_index =
+ wabt_find_binding_index_by_name(&module->export_bindings, name);
+ if (field_index < 0)
+ return NULL;
+ assert((size_t)field_index < module->exports.size);
+ return &module->exports.data[field_index];
+}
+
+void wabt_destroy_interpreter_thread(WabtInterpreterThread* thread) {
+ wabt_destroy_interpreter_value_array(&thread->value_stack);
+ wabt_destroy_uint32_array(&thread->call_stack);
+ wabt_destroy_interpreter_typed_value_vector(&thread->host_args);
+}
+
+/* 3 32222222 222...00
+ * 1 09876543 210...10
+ * -------------------
+ * 0 00000000 000...00 => 0x00000000 => 0
+ * 0 10011101 111...11 => 0x4effffff => 2147483520 (~INT32_MAX)
+ * 0 10011110 000...00 => 0x4f000000 => 2147483648
+ * 0 10011110 111...11 => 0x4f7fffff => 4294967040 (~UINT32_MAX)
+ * 0 10111110 111...11 => 0x5effffff => 9223371487098961920 (~INT64_MAX)
+ * 0 10111110 000...00 => 0x5f000000 => 9223372036854775808
+ * 0 10111111 111...11 => 0x5f7fffff => 18446742974197923840 (~UINT64_MAX)
+ * 0 10111111 000...00 => 0x5f800000 => 18446744073709551616
+ * 0 11111111 000...00 => 0x7f800000 => inf
+ * 0 11111111 000...01 => 0x7f800001 => nan(0x1)
+ * 0 11111111 111...11 => 0x7fffffff => nan(0x7fffff)
+ * 1 00000000 000...00 => 0x80000000 => -0
+ * 1 01111110 111...11 => 0xbf7fffff => -1 + ulp (~UINT32_MIN, ~UINT64_MIN)
+ * 1 01111111 000...00 => 0xbf800000 => -1
+ * 1 10011110 000...00 => 0xcf000000 => -2147483648 (INT32_MIN)
+ * 1 10111110 000...00 => 0xdf000000 => -9223372036854775808 (INT64_MIN)
+ * 1 11111111 000...00 => 0xff800000 => -inf
+ * 1 11111111 000...01 => 0xff800001 => -nan(0x1)
+ * 1 11111111 111...11 => 0xffffffff => -nan(0x7fffff)
+ */
+
+#define F32_MAX 0x7f7fffffU
+#define F32_INF 0x7f800000U
+#define F32_NEG_MAX 0xff7fffffU
+#define F32_NEG_INF 0xff800000U
+#define F32_NEG_ONE 0xbf800000U
+#define F32_NEG_ZERO 0x80000000U
+#define F32_QUIET_NAN 0x7fc00000U
+#define F32_QUIET_NAN_BIT 0x00400000U
+#define F32_SIG_BITS 23
+#define F32_SIG_MASK 0x7fffff
+#define F32_SIGN_MASK 0x80000000U
+
+bool wabt_is_nan_f32(uint32_t f32_bits) {
+ return (f32_bits > F32_INF && f32_bits < F32_NEG_ZERO) ||
+ (f32_bits > F32_NEG_INF);
+}
+
+static WABT_INLINE bool is_zero_f32(uint32_t f32_bits) {
+ return f32_bits == 0 || f32_bits == F32_NEG_ZERO;
+}
+
+static WABT_INLINE bool is_in_range_i32_trunc_s_f32(uint32_t f32_bits) {
+ return (f32_bits < 0x4f000000U) ||
+ (f32_bits >= F32_NEG_ZERO && f32_bits <= 0xcf000000U);
+}
+
+static WABT_INLINE bool is_in_range_i64_trunc_s_f32(uint32_t f32_bits) {
+ return (f32_bits < 0x5f000000U) ||
+ (f32_bits >= F32_NEG_ZERO && f32_bits <= 0xdf000000U);
+}
+
+static WABT_INLINE bool is_in_range_i32_trunc_u_f32(uint32_t f32_bits) {
+ return (f32_bits < 0x4f800000U) ||
+ (f32_bits >= F32_NEG_ZERO && f32_bits < F32_NEG_ONE);
+}
+
+static WABT_INLINE bool is_in_range_i64_trunc_u_f32(uint32_t f32_bits) {
+ return (f32_bits < 0x5f800000U) ||
+ (f32_bits >= F32_NEG_ZERO && f32_bits < F32_NEG_ONE);
+}
+
+/*
+ * 6 66655555555 5544..2..222221...000
+ * 3 21098765432 1098..9..432109...210
+ * -----------------------------------
+ * 0 00000000000 0000..0..000000...000 0x0000000000000000 => 0
+ * 0 10000011101 1111..1..111000...000 0x41dfffffffc00000 => 2147483647 (INT32_MAX)
+ * 0 10000011110 1111..1..111100...000 0x41efffffffe00000 => 4294967295 (UINT32_MAX)
+ * 0 10000111101 1111..1..111111...111 0x43dfffffffffffff => 9223372036854774784 (~INT64_MAX)
+ * 0 10000111110 0000..0..000000...000 0x43e0000000000000 => 9223372036854775808
+ * 0 10000111110 1111..1..111111...111 0x43efffffffffffff => 18446744073709549568 (~UINT64_MAX)
+ * 0 10000111111 0000..0..000000...000 0x43f0000000000000 => 18446744073709551616
+ * 0 10001111110 1111..1..000000...000 0x47efffffe0000000 => 3.402823e+38 (FLT_MAX)
+ * 0 11111111111 0000..0..000000...000 0x7ff0000000000000 => inf
+ * 0 11111111111 0000..0..000000...001 0x7ff0000000000001 => nan(0x1)
+ * 0 11111111111 1111..1..111111...111 0x7fffffffffffffff => nan(0xfff...)
+ * 1 00000000000 0000..0..000000...000 0x8000000000000000 => -0
+ * 1 01111111110 1111..1..111111...111 0xbfefffffffffffff => -1 + ulp (~UINT32_MIN, ~UINT64_MIN)
+ * 1 01111111111 0000..0..000000...000 0xbff0000000000000 => -1
+ * 1 10000011110 0000..0..000000...000 0xc1e0000000000000 => -2147483648 (INT32_MIN)
+ * 1 10000111110 0000..0..000000...000 0xc3e0000000000000 => -9223372036854775808 (INT64_MIN)
+ * 1 10001111110 1111..1..000000...000 0xc7efffffe0000000 => -3.402823e+38 (-FLT_MAX)
+ * 1 11111111111 0000..0..000000...000 0xfff0000000000000 => -inf
+ * 1 11111111111 0000..0..000000...001 0xfff0000000000001 => -nan(0x1)
+ * 1 11111111111 1111..1..111111...111 0xffffffffffffffff => -nan(0xfff...)
+ */
+
+#define F64_INF 0x7ff0000000000000ULL
+#define F64_NEG_INF 0xfff0000000000000ULL
+#define F64_NEG_ONE 0xbff0000000000000ULL
+#define F64_NEG_ZERO 0x8000000000000000ULL
+#define F64_QUIET_NAN 0x7ff8000000000000ULL
+#define F64_QUIET_NAN_BIT 0x0008000000000000ULL
+#define F64_SIG_BITS 52
+#define F64_SIG_MASK 0xfffffffffffffULL
+#define F64_SIGN_MASK 0x8000000000000000ULL
+
+bool wabt_is_nan_f64(uint64_t f64_bits) {
+ return (f64_bits > F64_INF && f64_bits < F64_NEG_ZERO) ||
+ (f64_bits > F64_NEG_INF);
+}
+
+static WABT_INLINE bool is_zero_f64(uint64_t f64_bits) {
+ return f64_bits == 0 || f64_bits == F64_NEG_ZERO;
+}
+
+static WABT_INLINE bool is_in_range_i32_trunc_s_f64(uint64_t f64_bits) {
+ return (f64_bits <= 0x41dfffffffc00000ULL) ||
+ (f64_bits >= F64_NEG_ZERO && f64_bits <= 0xc1e0000000000000ULL);
+}
+
+static WABT_INLINE bool is_in_range_i32_trunc_u_f64(uint64_t f64_bits) {
+ return (f64_bits <= 0x41efffffffe00000ULL) ||
+ (f64_bits >= F64_NEG_ZERO && f64_bits < F64_NEG_ONE);
+}
+
+static WABT_INLINE bool is_in_range_i64_trunc_s_f64(uint64_t f64_bits) {
+ return (f64_bits < 0x43e0000000000000ULL) ||
+ (f64_bits >= F64_NEG_ZERO && f64_bits <= 0xc3e0000000000000ULL);
+}
+
+static WABT_INLINE bool is_in_range_i64_trunc_u_f64(uint64_t f64_bits) {
+ return (f64_bits < 0x43f0000000000000ULL) ||
+ (f64_bits >= F64_NEG_ZERO && f64_bits < F64_NEG_ONE);
+}
+
+static WABT_INLINE bool is_in_range_f64_demote_f32(uint64_t f64_bits) {
+ return (f64_bits <= 0x47efffffe0000000ULL) ||
+ (f64_bits >= F64_NEG_ZERO && f64_bits <= 0xc7efffffe0000000ULL);
+}
+
+/* The WebAssembly rounding mode means that these values (which are > F32_MAX)
+ * should be rounded to F32_MAX and not set to infinity. Unfortunately, UBSAN
+ * complains that the value is not representable as a float, so we'll special
+ * case them. */
+static WABT_INLINE bool is_in_range_f64_demote_f32_round_to_f32_max(
+ uint64_t f64_bits) {
+ return f64_bits > 0x47efffffe0000000ULL && f64_bits < 0x47effffff0000000ULL;
+}
+
+static WABT_INLINE bool is_in_range_f64_demote_f32_round_to_neg_f32_max(
+ uint64_t f64_bits) {
+ return f64_bits > 0xc7efffffe0000000ULL && f64_bits < 0xc7effffff0000000ULL;
+}
+
+#define IS_NAN_F32 wabt_is_nan_f32
+#define IS_NAN_F64 wabt_is_nan_f64
+#define IS_ZERO_F32 is_zero_f32
+#define IS_ZERO_F64 is_zero_f64
+
+#define DEFINE_BITCAST(name, src, dst) \
+ static WABT_INLINE dst name(src x) { \
+ dst result; \
+ memcpy(&result, &x, sizeof(dst)); \
+ return result; \
+ }
+
+DEFINE_BITCAST(bitcast_u32_to_i32, uint32_t, int32_t)
+DEFINE_BITCAST(bitcast_u64_to_i64, uint64_t, int64_t)
+DEFINE_BITCAST(bitcast_f32_to_u32, float, uint32_t)
+DEFINE_BITCAST(bitcast_u32_to_f32, uint32_t, float)
+DEFINE_BITCAST(bitcast_f64_to_u64, double, uint64_t)
+DEFINE_BITCAST(bitcast_u64_to_f64, uint64_t, double)
+
+#define bitcast_i32_to_u32(x) ((uint32_t)x)
+#define bitcast_i64_to_u64(x) ((uint64_t)x)
+
+#define VALUE_TYPE_I32 uint32_t
+#define VALUE_TYPE_I64 uint64_t
+#define VALUE_TYPE_F32 uint32_t
+#define VALUE_TYPE_F64 uint64_t
+
+#define VALUE_TYPE_SIGNED_MAX_I32 (uint32_t)(0x80000000U)
+#define VALUE_TYPE_UNSIGNED_MAX_I32 (uint32_t)(0xFFFFFFFFU)
+#define VALUE_TYPE_SIGNED_MAX_I64 (uint64_t)(0x8000000000000000ULL)
+#define VALUE_TYPE_UNSIGNED_MAX_I64 (uint64_t)(0xFFFFFFFFFFFFFFFFULL)
+
+#define FLOAT_TYPE_F32 float
+#define FLOAT_TYPE_F64 double
+
+#define MEM_TYPE_I8 int8_t
+#define MEM_TYPE_U8 uint8_t
+#define MEM_TYPE_I16 int16_t
+#define MEM_TYPE_U16 uint16_t
+#define MEM_TYPE_I32 int32_t
+#define MEM_TYPE_U32 uint32_t
+#define MEM_TYPE_I64 int64_t
+#define MEM_TYPE_U64 uint64_t
+#define MEM_TYPE_F32 uint32_t
+#define MEM_TYPE_F64 uint64_t
+
+#define MEM_TYPE_EXTEND_I32_I8 int32_t
+#define MEM_TYPE_EXTEND_I32_U8 uint32_t
+#define MEM_TYPE_EXTEND_I32_I16 int32_t
+#define MEM_TYPE_EXTEND_I32_U16 uint32_t
+#define MEM_TYPE_EXTEND_I32_I32 int32_t
+#define MEM_TYPE_EXTEND_I32_U32 uint32_t
+
+#define MEM_TYPE_EXTEND_I64_I8 int64_t
+#define MEM_TYPE_EXTEND_I64_U8 uint64_t
+#define MEM_TYPE_EXTEND_I64_I16 int64_t
+#define MEM_TYPE_EXTEND_I64_U16 uint64_t
+#define MEM_TYPE_EXTEND_I64_I32 int64_t
+#define MEM_TYPE_EXTEND_I64_U32 uint64_t
+#define MEM_TYPE_EXTEND_I64_I64 int64_t
+#define MEM_TYPE_EXTEND_I64_U64 uint64_t
+
+#define MEM_TYPE_EXTEND_F32_F32 uint32_t
+#define MEM_TYPE_EXTEND_F64_F64 uint64_t
+
+#define BITCAST_I32_TO_SIGNED bitcast_u32_to_i32
+#define BITCAST_I64_TO_SIGNED bitcast_u64_to_i64
+#define BITCAST_I32_TO_UNSIGNED bitcast_i32_to_u32
+#define BITCAST_I64_TO_UNSIGNED bitcast_i64_to_u64
+
+#define BITCAST_TO_F32 bitcast_u32_to_f32
+#define BITCAST_TO_F64 bitcast_u64_to_f64
+#define BITCAST_FROM_F32 bitcast_f32_to_u32
+#define BITCAST_FROM_F64 bitcast_f64_to_u64
+
+#define TYPE_FIELD_NAME_I32 i32
+#define TYPE_FIELD_NAME_I64 i64
+#define TYPE_FIELD_NAME_F32 f32_bits
+#define TYPE_FIELD_NAME_F64 f64_bits
+
+#define TRAP(type) return WABT_INTERPRETER_TRAP_##type
+#define TRAP_UNLESS(cond, type) TRAP_IF(!(cond), type)
+#define TRAP_IF(cond, type) \
+ do { \
+ if (WABT_UNLIKELY(cond)) \
+ return WABT_INTERPRETER_TRAP_##type; \
+ } while (0)
+
+#define CHECK_STACK() \
+ TRAP_IF(thread->value_stack_top >= thread->value_stack_end, \
+ VALUE_STACK_EXHAUSTED)
+
+#define PUSH_NEG_1_AND_BREAK_IF(cond) \
+ if (WABT_UNLIKELY(cond)) { \
+ PUSH_I32(-1); \
+ break; \
+ }
+
+#define PUSH(v) \
+ do { \
+ CHECK_STACK(); \
+ (*thread->value_stack_top++) = (v); \
+ } while (0)
+
+#define PUSH_TYPE(type, v) \
+ do { \
+ CHECK_STACK(); \
+ (*thread->value_stack_top++).TYPE_FIELD_NAME_##type = \
+ (VALUE_TYPE_##type)(v); \
+ } while (0)
+
+#define PUSH_I32(v) PUSH_TYPE(I32, (v))
+#define PUSH_I64(v) PUSH_TYPE(I64, (v))
+#define PUSH_F32(v) PUSH_TYPE(F32, (v))
+#define PUSH_F64(v) PUSH_TYPE(F64, (v))
+
+#define PICK(depth) (*(thread->value_stack_top - (depth)))
+#define TOP() (PICK(1))
+#define POP() (*--thread->value_stack_top)
+#define POP_I32() (POP().i32)
+#define POP_I64() (POP().i64)
+#define POP_F32() (POP().f32_bits)
+#define POP_F64() (POP().f64_bits)
+#define DROP_KEEP(drop, keep) \
+ do { \
+ assert((keep) <= 1); \
+ if ((keep) == 1) \
+ PICK((drop) + 1) = TOP(); \
+ thread->value_stack_top -= (drop); \
+ } while (0)
+
+#define GOTO(offset) pc = &istream[offset]
+
+#define PUSH_CALL() \
+ do { \
+ TRAP_IF(thread->call_stack_top >= thread->call_stack_end, \
+ CALL_STACK_EXHAUSTED); \
+ (*thread->call_stack_top++) = (pc - istream); \
+ } while (0)
+
+#define POP_CALL() (*--thread->call_stack_top)
+
+#define GET_MEMORY(var) \
+ uint32_t memory_index = read_u32(&pc); \
+ assert(memory_index < env->memories.size); \
+ WabtInterpreterMemory* var = &env->memories.data[memory_index]
+
+#define LOAD(type, mem_type) \
+ do { \
+ GET_MEMORY(memory); \
+ uint64_t offset = (uint64_t)POP_I32() + read_u32(&pc); \
+ MEM_TYPE_##mem_type value; \
+ TRAP_IF(offset + sizeof(value) > memory->byte_size, \
+ MEMORY_ACCESS_OUT_OF_BOUNDS); \
+ void* src = (void*)((intptr_t)memory->data + (uint32_t)offset); \
+ memcpy(&value, src, sizeof(MEM_TYPE_##mem_type)); \
+ PUSH_##type((MEM_TYPE_EXTEND_##type##_##mem_type)value); \
+ } while (0)
+
+#define STORE(type, mem_type) \
+ do { \
+ GET_MEMORY(memory); \
+ VALUE_TYPE_##type value = POP_##type(); \
+ uint64_t offset = (uint64_t)POP_I32() + read_u32(&pc); \
+ MEM_TYPE_##mem_type src = (MEM_TYPE_##mem_type)value; \
+ TRAP_IF(offset + sizeof(src) > memory->byte_size, \
+ MEMORY_ACCESS_OUT_OF_BOUNDS); \
+ void* dst = (void*)((intptr_t)memory->data + (uint32_t)offset); \
+ memcpy(dst, &src, sizeof(MEM_TYPE_##mem_type)); \
+ } while (0)
+
+#define BINOP(rtype, type, op) \
+ do { \
+ VALUE_TYPE_##type rhs = POP_##type(); \
+ VALUE_TYPE_##type lhs = POP_##type(); \
+ PUSH_##rtype(lhs op rhs); \
+ } while (0)
+
+#define BINOP_SIGNED(rtype, type, op) \
+ do { \
+ VALUE_TYPE_##type rhs = POP_##type(); \
+ VALUE_TYPE_##type lhs = POP_##type(); \
+ PUSH_##rtype(BITCAST_##type##_TO_SIGNED(lhs) \
+ op BITCAST_##type##_TO_SIGNED(rhs)); \
+ } while (0)
+
+#define SHIFT_MASK_I32 31
+#define SHIFT_MASK_I64 63
+
+#define BINOP_SHIFT(type, op, sign) \
+ do { \
+ VALUE_TYPE_##type rhs = POP_##type(); \
+ VALUE_TYPE_##type lhs = POP_##type(); \
+ PUSH_##type(BITCAST_##type##_TO_##sign(lhs) op(rhs& SHIFT_MASK_##type)); \
+ } while (0)
+
+#define ROT_LEFT_0_SHIFT_OP <<
+#define ROT_LEFT_1_SHIFT_OP >>
+#define ROT_RIGHT_0_SHIFT_OP >>
+#define ROT_RIGHT_1_SHIFT_OP <<
+
+#define BINOP_ROT(type, dir) \
+ do { \
+ VALUE_TYPE_##type rhs = POP_##type(); \
+ VALUE_TYPE_##type lhs = POP_##type(); \
+ uint32_t amount = rhs & SHIFT_MASK_##type; \
+ if (WABT_LIKELY(amount != 0)) { \
+ PUSH_##type( \
+ (lhs ROT_##dir##_0_SHIFT_OP amount) | \
+ (lhs ROT_##dir##_1_SHIFT_OP((SHIFT_MASK_##type + 1) - amount))); \
+ } else { \
+ PUSH_##type(lhs); \
+ } \
+ } while (0)
+
+#define BINOP_DIV_REM_U(type, op) \
+ do { \
+ VALUE_TYPE_##type rhs = POP_##type(); \
+ VALUE_TYPE_##type lhs = POP_##type(); \
+ TRAP_IF(rhs == 0, INTEGER_DIVIDE_BY_ZERO); \
+ PUSH_##type(BITCAST_##type##_TO_UNSIGNED(lhs) \
+ op BITCAST_##type##_TO_UNSIGNED(rhs)); \
+ } while (0)
+
+/* {i32,i64}.{div,rem}_s are special-cased because they trap when dividing the
+ * max signed value by -1. The modulo operation on x86 uses the same
+ * instruction to generate the quotient and the remainder. */
+#define BINOP_DIV_S(type) \
+ do { \
+ VALUE_TYPE_##type rhs = POP_##type(); \
+ VALUE_TYPE_##type lhs = POP_##type(); \
+ TRAP_IF(rhs == 0, INTEGER_DIVIDE_BY_ZERO); \
+ TRAP_IF(lhs == VALUE_TYPE_SIGNED_MAX_##type && \
+ rhs == VALUE_TYPE_UNSIGNED_MAX_##type, \
+ INTEGER_OVERFLOW); \
+ PUSH_##type(BITCAST_##type##_TO_SIGNED(lhs) / \
+ BITCAST_##type##_TO_SIGNED(rhs)); \
+ } while (0)
+
+#define BINOP_REM_S(type) \
+ do { \
+ VALUE_TYPE_##type rhs = POP_##type(); \
+ VALUE_TYPE_##type lhs = POP_##type(); \
+ TRAP_IF(rhs == 0, INTEGER_DIVIDE_BY_ZERO); \
+ if (WABT_UNLIKELY(lhs == VALUE_TYPE_SIGNED_MAX_##type && \
+ rhs == VALUE_TYPE_UNSIGNED_MAX_##type)) { \
+ PUSH_##type(0); \
+ } else { \
+ PUSH_##type(BITCAST_##type##_TO_SIGNED(lhs) % \
+ BITCAST_##type##_TO_SIGNED(rhs)); \
+ } \
+ } while (0)
+
+#define UNOP_FLOAT(type, func) \
+ do { \
+ FLOAT_TYPE_##type value = BITCAST_TO_##type(POP_##type()); \
+ PUSH_##type(BITCAST_FROM_##type(func(value))); \
+ break; \
+ } while (0)
+
+#define BINOP_FLOAT(type, op) \
+ do { \
+ FLOAT_TYPE_##type rhs = BITCAST_TO_##type(POP_##type()); \
+ FLOAT_TYPE_##type lhs = BITCAST_TO_##type(POP_##type()); \
+ PUSH_##type(BITCAST_FROM_##type(lhs op rhs)); \
+ } while (0)
+
+#define BINOP_FLOAT_DIV(type) \
+ do { \
+ VALUE_TYPE_##type rhs = POP_##type(); \
+ VALUE_TYPE_##type lhs = POP_##type(); \
+ if (WABT_UNLIKELY(IS_ZERO_##type(rhs))) { \
+ if (IS_NAN_##type(lhs)) { \
+ PUSH_##type(lhs | type##_QUIET_NAN); \
+ } else if (IS_ZERO_##type(lhs)) { \
+ PUSH_##type(type##_QUIET_NAN); \
+ } else { \
+ VALUE_TYPE_##type sign = \
+ (lhs & type##_SIGN_MASK) ^ (rhs & type##_SIGN_MASK); \
+ PUSH_##type(sign | type##_INF); \
+ } \
+ } else { \
+ PUSH_##type(BITCAST_FROM_##type(BITCAST_TO_##type(lhs) / \
+ BITCAST_TO_##type(rhs))); \
+ } \
+ } while (0)
+
+#define BINOP_FLOAT_COMPARE(type, op) \
+ do { \
+ FLOAT_TYPE_##type rhs = BITCAST_TO_##type(POP_##type()); \
+ FLOAT_TYPE_##type lhs = BITCAST_TO_##type(POP_##type()); \
+ PUSH_I32(lhs op rhs); \
+ } while (0)
+
+#define MIN_OP <
+#define MAX_OP >
+
+#define MINMAX_FLOAT(type, op) \
+ do { \
+ VALUE_TYPE_##type rhs = POP_##type(); \
+ VALUE_TYPE_##type lhs = POP_##type(); \
+ VALUE_TYPE_##type result; \
+ if (WABT_UNLIKELY(IS_NAN_##type(lhs))) { \
+ result = lhs | type##_QUIET_NAN_BIT; \
+ } else if (WABT_UNLIKELY(IS_NAN_##type(rhs))) { \
+ result = rhs | type##_QUIET_NAN_BIT; \
+ } else if ((lhs ^ rhs) & type##_SIGN_MASK) { \
+ /* min(-0.0, 0.0) => -0.0; since we know the sign bits are different, we \
+ * can just use the inverse integer comparison (because the sign bit is \
+ * set when the value is negative) */ \
+ result = !(lhs op##_OP rhs) ? lhs : rhs; \
+ } else { \
+ FLOAT_TYPE_##type float_rhs = BITCAST_TO_##type(rhs); \
+ FLOAT_TYPE_##type float_lhs = BITCAST_TO_##type(lhs); \
+ result = BITCAST_FROM_##type(float_lhs op##_OP float_rhs ? float_lhs \
+ : float_rhs); \
+ } \
+ PUSH_##type(result); \
+ } while (0)
+
+static WABT_INLINE uint32_t read_u32_at(const uint8_t* pc) {
+ uint32_t result;
+ memcpy(&result, pc, sizeof(uint32_t));
+ return result;
+}
+
+static WABT_INLINE uint32_t read_u32(const uint8_t** pc) {
+ uint32_t result = read_u32_at(*pc);
+ *pc += sizeof(uint32_t);
+ return result;
+}
+
+static WABT_INLINE uint64_t read_u64_at(const uint8_t* pc) {
+ uint64_t result;
+ memcpy(&result, pc, sizeof(uint64_t));
+ return result;
+}
+
+static WABT_INLINE uint64_t read_u64(const uint8_t** pc) {
+ uint64_t result = read_u64_at(*pc);
+ *pc += sizeof(uint64_t);
+ return result;
+}
+
+static WABT_INLINE void read_table_entry_at(const uint8_t* pc,
+ uint32_t* out_offset,
+ uint32_t* out_drop,
+ uint8_t* out_keep) {
+ *out_offset = read_u32_at(pc + WABT_TABLE_ENTRY_OFFSET_OFFSET);
+ *out_drop = read_u32_at(pc + WABT_TABLE_ENTRY_DROP_OFFSET);
+ *out_keep = *(pc + WABT_TABLE_ENTRY_KEEP_OFFSET);
+}
+
+bool wabt_func_signatures_are_equal(WabtInterpreterEnvironment* env,
+ uint32_t sig_index_0,
+ uint32_t sig_index_1) {
+ if (sig_index_0 == sig_index_1)
+ return true;
+ WabtInterpreterFuncSignature* sig_0 = &env->sigs.data[sig_index_0];
+ WabtInterpreterFuncSignature* sig_1 = &env->sigs.data[sig_index_1];
+ return wabt_type_vectors_are_equal(&sig_0->param_types,
+ &sig_1->param_types) &&
+ wabt_type_vectors_are_equal(&sig_0->result_types,
+ &sig_1->result_types);
+}
+
+WabtInterpreterResult wabt_call_host(WabtInterpreterThread* thread,
+ WabtInterpreterFunc* func) {
+ assert(func->is_host);
+ assert(func->sig_index < thread->env->sigs.size);
+ WabtInterpreterFuncSignature* sig = &thread->env->sigs.data[func->sig_index];
+
+ uint32_t num_args = sig->param_types.size;
+ if (thread->host_args.size < num_args) {
+ wabt_resize_interpreter_typed_value_vector(&thread->host_args, num_args);
+ }
+
+ uint32_t i;
+ for (i = num_args; i > 0; --i) {
+ WabtInterpreterValue value = POP();
+ WabtInterpreterTypedValue* arg = &thread->host_args.data[i - 1];
+ arg->type = sig->param_types.data[i - 1];
+ arg->value = value;
+ }
+
+ uint32_t num_results = sig->result_types.size;
+ WabtInterpreterTypedValue* call_result_values =
+ (WabtInterpreterTypedValue*)alloca(sizeof(WabtInterpreterTypedValue) *
+ num_results);
+
+ WabtResult call_result = func->host.callback(
+ func, sig, num_args, thread->host_args.data, num_results,
+ call_result_values, func->host.user_data);
+ TRAP_IF(call_result != WABT_OK, HOST_TRAPPED);
+
+ for (i = 0; i < num_results; ++i) {
+ TRAP_IF(call_result_values[i].type != sig->result_types.data[i],
+ HOST_RESULT_TYPE_MISMATCH);
+ PUSH(call_result_values[i].value);
+ }
+
+ return WABT_INTERPRETER_OK;
+}
+
+WabtInterpreterResult wabt_run_interpreter(WabtInterpreterThread* thread,
+ uint32_t num_instructions,
+ uint32_t* call_stack_return_top) {
+ WabtInterpreterResult result = WABT_INTERPRETER_OK;
+ assert(call_stack_return_top < thread->call_stack_end);
+
+ WabtInterpreterEnvironment* env = thread->env;
+
+ const uint8_t* istream = (const uint8_t*)env->istream.start;
+ const uint8_t* pc = &istream[thread->pc];
+ uint32_t i;
+ for (i = 0; i < num_instructions; ++i) {
+ uint8_t opcode = *pc++;
+ switch (opcode) {
+ case WABT_OPCODE_SELECT: {
+ VALUE_TYPE_I32 cond = POP_I32();
+ WabtInterpreterValue false_ = POP();
+ WabtInterpreterValue true_ = POP();
+ PUSH(cond ? true_ : false_);
+ break;
+ }
+
+ case WABT_OPCODE_BR:
+ GOTO(read_u32(&pc));
+ break;
+
+ case WABT_OPCODE_BR_IF: {
+ uint32_t new_pc = read_u32(&pc);
+ if (POP_I32())
+ GOTO(new_pc);
+ break;
+ }
+
+ case WABT_OPCODE_BR_TABLE: {
+ uint32_t num_targets = read_u32(&pc);
+ uint32_t table_offset = read_u32(&pc);
+ VALUE_TYPE_I32 key = POP_I32();
+ uint32_t key_offset =
+ (key >= num_targets ? num_targets : key) * WABT_TABLE_ENTRY_SIZE;
+ const uint8_t* entry = istream + table_offset + key_offset;
+ uint32_t new_pc;
+ uint32_t drop_count;
+ uint8_t keep_count;
+ read_table_entry_at(entry, &new_pc, &drop_count, &keep_count);
+ DROP_KEEP(drop_count, keep_count);
+ GOTO(new_pc);
+ break;
+ }
+
+ case WABT_OPCODE_RETURN:
+ if (thread->call_stack_top == call_stack_return_top) {
+ result = WABT_INTERPRETER_RETURNED;
+ goto exit_loop;
+ }
+ GOTO(POP_CALL());
+ break;
+
+ case WABT_OPCODE_UNREACHABLE:
+ TRAP(UNREACHABLE);
+ break;
+
+ case WABT_OPCODE_I32_CONST:
+ PUSH_I32(read_u32(&pc));
+ break;
+
+ case WABT_OPCODE_I64_CONST:
+ PUSH_I64(read_u64(&pc));
+ break;
+
+ case WABT_OPCODE_F32_CONST:
+ PUSH_F32(read_u32(&pc));
+ break;
+
+ case WABT_OPCODE_F64_CONST:
+ PUSH_F64(read_u64(&pc));
+ break;
+
+ case WABT_OPCODE_GET_GLOBAL: {
+ uint32_t index = read_u32(&pc);
+ assert(index < env->globals.size);
+ PUSH(env->globals.data[index].typed_value.value);
+ break;
+ }
+
+ case WABT_OPCODE_SET_GLOBAL: {
+ uint32_t index = read_u32(&pc);
+ assert(index < env->globals.size);
+ env->globals.data[index].typed_value.value = POP();
+ break;
+ }
+
+ case WABT_OPCODE_GET_LOCAL: {
+ WabtInterpreterValue value = PICK(read_u32(&pc));
+ PUSH(value);
+ break;
+ }
+
+ case WABT_OPCODE_SET_LOCAL: {
+ WabtInterpreterValue value = POP();
+ PICK(read_u32(&pc)) = value;
+ break;
+ }
+
+ case WABT_OPCODE_TEE_LOCAL:
+ PICK(read_u32(&pc)) = TOP();
+ break;
+
+ case WABT_OPCODE_CALL: {
+ uint32_t offset = read_u32(&pc);
+ PUSH_CALL();
+ GOTO(offset);
+ break;
+ }
+
+ case WABT_OPCODE_CALL_INDIRECT: {
+ uint32_t table_index = read_u32(&pc);
+ assert(table_index < env->tables.size);
+ WabtInterpreterTable* table = &env->tables.data[table_index];
+ uint32_t sig_index = read_u32(&pc);
+ assert(sig_index < env->sigs.size);
+ VALUE_TYPE_I32 entry_index = POP_I32();
+ TRAP_IF(entry_index >= table->func_indexes.size, UNDEFINED_TABLE_INDEX);
+ uint32_t func_index = table->func_indexes.data[entry_index];
+ TRAP_IF(func_index == WABT_INVALID_INDEX, UNINITIALIZED_TABLE_ELEMENT);
+ WabtInterpreterFunc* func = &env->funcs.data[func_index];
+ TRAP_UNLESS(
+ wabt_func_signatures_are_equal(env, func->sig_index, sig_index),
+ INDIRECT_CALL_SIGNATURE_MISMATCH);
+ if (func->is_host) {
+ wabt_call_host(thread, func);
+ } else {
+ PUSH_CALL();
+ GOTO(func->defined.offset);
+ }
+ break;
+ }
+
+ case WABT_OPCODE_CALL_HOST: {
+ uint32_t func_index = read_u32(&pc);
+ assert(func_index < env->funcs.size);
+ WabtInterpreterFunc* func = &env->funcs.data[func_index];
+ wabt_call_host(thread, func);
+ break;
+ }
+
+ case WABT_OPCODE_I32_LOAD8_S:
+ LOAD(I32, I8);
+ break;
+
+ case WABT_OPCODE_I32_LOAD8_U:
+ LOAD(I32, U8);
+ break;
+
+ case WABT_OPCODE_I32_LOAD16_S:
+ LOAD(I32, I16);
+ break;
+
+ case WABT_OPCODE_I32_LOAD16_U:
+ LOAD(I32, U16);
+ break;
+
+ case WABT_OPCODE_I64_LOAD8_S:
+ LOAD(I64, I8);
+ break;
+
+ case WABT_OPCODE_I64_LOAD8_U:
+ LOAD(I64, U8);
+ break;
+
+ case WABT_OPCODE_I64_LOAD16_S:
+ LOAD(I64, I16);
+ break;
+
+ case WABT_OPCODE_I64_LOAD16_U:
+ LOAD(I64, U16);
+ break;
+
+ case WABT_OPCODE_I64_LOAD32_S:
+ LOAD(I64, I32);
+ break;
+
+ case WABT_OPCODE_I64_LOAD32_U:
+ LOAD(I64, U32);
+ break;
+
+ case WABT_OPCODE_I32_LOAD:
+ LOAD(I32, U32);
+ break;
+
+ case WABT_OPCODE_I64_LOAD:
+ LOAD(I64, U64);
+ break;
+
+ case WABT_OPCODE_F32_LOAD:
+ LOAD(F32, F32);
+ break;
+
+ case WABT_OPCODE_F64_LOAD:
+ LOAD(F64, F64);
+ break;
+
+ case WABT_OPCODE_I32_STORE8:
+ STORE(I32, U8);
+ break;
+
+ case WABT_OPCODE_I32_STORE16:
+ STORE(I32, U16);
+ break;
+
+ case WABT_OPCODE_I64_STORE8:
+ STORE(I64, U8);
+ break;
+
+ case WABT_OPCODE_I64_STORE16:
+ STORE(I64, U16);
+ break;
+
+ case WABT_OPCODE_I64_STORE32:
+ STORE(I64, U32);
+ break;
+
+ case WABT_OPCODE_I32_STORE:
+ STORE(I32, U32);
+ break;
+
+ case WABT_OPCODE_I64_STORE:
+ STORE(I64, U64);
+ break;
+
+ case WABT_OPCODE_F32_STORE:
+ STORE(F32, F32);
+ break;
+
+ case WABT_OPCODE_F64_STORE:
+ STORE(F64, F64);
+ break;
+
+ case WABT_OPCODE_CURRENT_MEMORY: {
+ GET_MEMORY(memory);
+ PUSH_I32(memory->page_limits.initial);
+ break;
+ }
+
+ case WABT_OPCODE_GROW_MEMORY: {
+ GET_MEMORY(memory);
+ uint32_t old_page_size = memory->page_limits.initial;
+ uint32_t old_byte_size = memory->byte_size;
+ VALUE_TYPE_I32 grow_pages = POP_I32();
+ uint32_t new_page_size = old_page_size + grow_pages;
+ uint32_t max_page_size = memory->page_limits.has_max
+ ? memory->page_limits.max
+ : WABT_MAX_PAGES;
+ PUSH_NEG_1_AND_BREAK_IF(new_page_size > max_page_size);
+ PUSH_NEG_1_AND_BREAK_IF((uint64_t)new_page_size * WABT_PAGE_SIZE >
+ UINT32_MAX);
+ uint32_t new_byte_size = new_page_size * WABT_PAGE_SIZE;
+ void* new_data = wabt_realloc(memory->data, new_byte_size);
+ PUSH_NEG_1_AND_BREAK_IF(new_data == NULL);
+ memset((void*)((intptr_t)new_data + old_byte_size), 0,
+ new_byte_size - old_byte_size);
+ memory->data = new_data;
+ memory->page_limits.initial = new_page_size;
+ memory->byte_size = new_byte_size;
+ PUSH_I32(old_page_size);
+ break;
+ }
+
+ case WABT_OPCODE_I32_ADD:
+ BINOP(I32, I32, +);
+ break;
+
+ case WABT_OPCODE_I32_SUB:
+ BINOP(I32, I32, -);
+ break;
+
+ case WABT_OPCODE_I32_MUL:
+ BINOP(I32, I32, *);
+ break;
+
+ case WABT_OPCODE_I32_DIV_S:
+ BINOP_DIV_S(I32);
+ break;
+
+ case WABT_OPCODE_I32_DIV_U:
+ BINOP_DIV_REM_U(I32, / );
+ break;
+
+ case WABT_OPCODE_I32_REM_S:
+ BINOP_REM_S(I32);
+ break;
+
+ case WABT_OPCODE_I32_REM_U:
+ BINOP_DIV_REM_U(I32, % );
+ break;
+
+ case WABT_OPCODE_I32_AND:
+ BINOP(I32, I32, &);
+ break;
+
+ case WABT_OPCODE_I32_OR:
+ BINOP(I32, I32, | );
+ break;
+
+ case WABT_OPCODE_I32_XOR:
+ BINOP(I32, I32, ^);
+ break;
+
+ case WABT_OPCODE_I32_SHL:
+ BINOP_SHIFT(I32, <<, UNSIGNED);
+ break;
+
+ case WABT_OPCODE_I32_SHR_U:
+ BINOP_SHIFT(I32, >>, UNSIGNED);
+ break;
+
+ case WABT_OPCODE_I32_SHR_S:
+ BINOP_SHIFT(I32, >>, SIGNED);
+ break;
+
+ case WABT_OPCODE_I32_EQ:
+ BINOP(I32, I32, == );
+ break;
+
+ case WABT_OPCODE_I32_NE:
+ BINOP(I32, I32, != );
+ break;
+
+ case WABT_OPCODE_I32_LT_S:
+ BINOP_SIGNED(I32, I32, < );
+ break;
+
+ case WABT_OPCODE_I32_LE_S:
+ BINOP_SIGNED(I32, I32, <= );
+ break;
+
+ case WABT_OPCODE_I32_LT_U:
+ BINOP(I32, I32, < );
+ break;
+
+ case WABT_OPCODE_I32_LE_U:
+ BINOP(I32, I32, <= );
+ break;
+
+ case WABT_OPCODE_I32_GT_S:
+ BINOP_SIGNED(I32, I32, > );
+ break;
+
+ case WABT_OPCODE_I32_GE_S:
+ BINOP_SIGNED(I32, I32, >= );
+ break;
+
+ case WABT_OPCODE_I32_GT_U:
+ BINOP(I32, I32, > );
+ break;
+
+ case WABT_OPCODE_I32_GE_U:
+ BINOP(I32, I32, >= );
+ break;
+
+ case WABT_OPCODE_I32_CLZ: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_I32(value != 0 ? wabt_clz_u32(value) : 32);
+ break;
+ }
+
+ case WABT_OPCODE_I32_CTZ: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_I32(value != 0 ? wabt_ctz_u32(value) : 32);
+ break;
+ }
+
+ case WABT_OPCODE_I32_POPCNT: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_I32(wabt_popcount_u32(value));
+ break;
+ }
+
+ case WABT_OPCODE_I32_EQZ: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_I32(value == 0);
+ break;
+ }
+
+ case WABT_OPCODE_I64_ADD:
+ BINOP(I64, I64, +);
+ break;
+
+ case WABT_OPCODE_I64_SUB:
+ BINOP(I64, I64, -);
+ break;
+
+ case WABT_OPCODE_I64_MUL:
+ BINOP(I64, I64, *);
+ break;
+
+ case WABT_OPCODE_I64_DIV_S:
+ BINOP_DIV_S(I64);
+ break;
+
+ case WABT_OPCODE_I64_DIV_U:
+ BINOP_DIV_REM_U(I64, / );
+ break;
+
+ case WABT_OPCODE_I64_REM_S:
+ BINOP_REM_S(I64);
+ break;
+
+ case WABT_OPCODE_I64_REM_U:
+ BINOP_DIV_REM_U(I64, % );
+ break;
+
+ case WABT_OPCODE_I64_AND:
+ BINOP(I64, I64, &);
+ break;
+
+ case WABT_OPCODE_I64_OR:
+ BINOP(I64, I64, | );
+ break;
+
+ case WABT_OPCODE_I64_XOR:
+ BINOP(I64, I64, ^);
+ break;
+
+ case WABT_OPCODE_I64_SHL:
+ BINOP_SHIFT(I64, <<, UNSIGNED);
+ break;
+
+ case WABT_OPCODE_I64_SHR_U:
+ BINOP_SHIFT(I64, >>, UNSIGNED);
+ break;
+
+ case WABT_OPCODE_I64_SHR_S:
+ BINOP_SHIFT(I64, >>, SIGNED);
+ break;
+
+ case WABT_OPCODE_I64_EQ:
+ BINOP(I32, I64, == );
+ break;
+
+ case WABT_OPCODE_I64_NE:
+ BINOP(I32, I64, != );
+ break;
+
+ case WABT_OPCODE_I64_LT_S:
+ BINOP_SIGNED(I32, I64, < );
+ break;
+
+ case WABT_OPCODE_I64_LE_S:
+ BINOP_SIGNED(I32, I64, <= );
+ break;
+
+ case WABT_OPCODE_I64_LT_U:
+ BINOP(I32, I64, < );
+ break;
+
+ case WABT_OPCODE_I64_LE_U:
+ BINOP(I32, I64, <= );
+ break;
+
+ case WABT_OPCODE_I64_GT_S:
+ BINOP_SIGNED(I32, I64, > );
+ break;
+
+ case WABT_OPCODE_I64_GE_S:
+ BINOP_SIGNED(I32, I64, >= );
+ break;
+
+ case WABT_OPCODE_I64_GT_U:
+ BINOP(I32, I64, > );
+ break;
+
+ case WABT_OPCODE_I64_GE_U:
+ BINOP(I32, I64, >= );
+ break;
+
+ case WABT_OPCODE_I64_CLZ: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_I64(value != 0 ? wabt_clz_u64(value) : 64);
+ break;
+ }
+
+ case WABT_OPCODE_I64_CTZ: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_I64(value != 0 ? wabt_ctz_u64(value) : 64);
+ break;
+ }
+
+ case WABT_OPCODE_I64_POPCNT: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_I64(wabt_popcount_u64(value));
+ break;
+ }
+
+ case WABT_OPCODE_F32_ADD:
+ BINOP_FLOAT(F32, +);
+ break;
+
+ case WABT_OPCODE_F32_SUB:
+ BINOP_FLOAT(F32, -);
+ break;
+
+ case WABT_OPCODE_F32_MUL:
+ BINOP_FLOAT(F32, *);
+ break;
+
+ case WABT_OPCODE_F32_DIV:
+ BINOP_FLOAT_DIV(F32);
+ break;
+
+ case WABT_OPCODE_F32_MIN:
+ MINMAX_FLOAT(F32, MIN);
+ break;
+
+ case WABT_OPCODE_F32_MAX:
+ MINMAX_FLOAT(F32, MAX);
+ break;
+
+ case WABT_OPCODE_F32_ABS:
+ TOP().f32_bits &= ~F32_SIGN_MASK;
+ break;
+
+ case WABT_OPCODE_F32_NEG:
+ TOP().f32_bits ^= F32_SIGN_MASK;
+ break;
+
+ case WABT_OPCODE_F32_COPYSIGN: {
+ VALUE_TYPE_F32 rhs = POP_F32();
+ VALUE_TYPE_F32 lhs = POP_F32();
+ PUSH_F32((lhs & ~F32_SIGN_MASK) | (rhs & F32_SIGN_MASK));
+ break;
+ }
+
+ case WABT_OPCODE_F32_CEIL:
+ UNOP_FLOAT(F32, ceilf);
+ break;
+
+ case WABT_OPCODE_F32_FLOOR:
+ UNOP_FLOAT(F32, floorf);
+ break;
+
+ case WABT_OPCODE_F32_TRUNC:
+ UNOP_FLOAT(F32, truncf);
+ break;
+
+ case WABT_OPCODE_F32_NEAREST:
+ UNOP_FLOAT(F32, nearbyintf);
+ break;
+
+ case WABT_OPCODE_F32_SQRT:
+ UNOP_FLOAT(F32, sqrtf);
+ break;
+
+ case WABT_OPCODE_F32_EQ:
+ BINOP_FLOAT_COMPARE(F32, == );
+ break;
+
+ case WABT_OPCODE_F32_NE:
+ BINOP_FLOAT_COMPARE(F32, != );
+ break;
+
+ case WABT_OPCODE_F32_LT:
+ BINOP_FLOAT_COMPARE(F32, < );
+ break;
+
+ case WABT_OPCODE_F32_LE:
+ BINOP_FLOAT_COMPARE(F32, <= );
+ break;
+
+ case WABT_OPCODE_F32_GT:
+ BINOP_FLOAT_COMPARE(F32, > );
+ break;
+
+ case WABT_OPCODE_F32_GE:
+ BINOP_FLOAT_COMPARE(F32, >= );
+ break;
+
+ case WABT_OPCODE_F64_ADD:
+ BINOP_FLOAT(F64, +);
+ break;
+
+ case WABT_OPCODE_F64_SUB:
+ BINOP_FLOAT(F64, -);
+ break;
+
+ case WABT_OPCODE_F64_MUL:
+ BINOP_FLOAT(F64, *);
+ break;
+
+ case WABT_OPCODE_F64_DIV:
+ BINOP_FLOAT_DIV(F64);
+ break;
+
+ case WABT_OPCODE_F64_MIN:
+ MINMAX_FLOAT(F64, MIN);
+ break;
+
+ case WABT_OPCODE_F64_MAX:
+ MINMAX_FLOAT(F64, MAX);
+ break;
+
+ case WABT_OPCODE_F64_ABS:
+ TOP().f64_bits &= ~F64_SIGN_MASK;
+ break;
+
+ case WABT_OPCODE_F64_NEG:
+ TOP().f64_bits ^= F64_SIGN_MASK;
+ break;
+
+ case WABT_OPCODE_F64_COPYSIGN: {
+ VALUE_TYPE_F64 rhs = POP_F64();
+ VALUE_TYPE_F64 lhs = POP_F64();
+ PUSH_F64((lhs & ~F64_SIGN_MASK) | (rhs & F64_SIGN_MASK));
+ break;
+ }
+
+ case WABT_OPCODE_F64_CEIL:
+ UNOP_FLOAT(F64, ceil);
+ break;
+
+ case WABT_OPCODE_F64_FLOOR:
+ UNOP_FLOAT(F64, floor);
+ break;
+
+ case WABT_OPCODE_F64_TRUNC:
+ UNOP_FLOAT(F64, trunc);
+ break;
+
+ case WABT_OPCODE_F64_NEAREST:
+ UNOP_FLOAT(F64, nearbyint);
+ break;
+
+ case WABT_OPCODE_F64_SQRT:
+ UNOP_FLOAT(F64, sqrt);
+ break;
+
+ case WABT_OPCODE_F64_EQ:
+ BINOP_FLOAT_COMPARE(F64, == );
+ break;
+
+ case WABT_OPCODE_F64_NE:
+ BINOP_FLOAT_COMPARE(F64, != );
+ break;
+
+ case WABT_OPCODE_F64_LT:
+ BINOP_FLOAT_COMPARE(F64, < );
+ break;
+
+ case WABT_OPCODE_F64_LE:
+ BINOP_FLOAT_COMPARE(F64, <= );
+ break;
+
+ case WABT_OPCODE_F64_GT:
+ BINOP_FLOAT_COMPARE(F64, > );
+ break;
+
+ case WABT_OPCODE_F64_GE:
+ BINOP_FLOAT_COMPARE(F64, >= );
+ break;
+
+ case WABT_OPCODE_I32_TRUNC_S_F32: {
+ VALUE_TYPE_F32 value = POP_F32();
+ TRAP_IF(wabt_is_nan_f32(value), INVALID_CONVERSION_TO_INTEGER);
+ TRAP_UNLESS(is_in_range_i32_trunc_s_f32(value), INTEGER_OVERFLOW);
+ PUSH_I32((int32_t)BITCAST_TO_F32(value));
+ break;
+ }
+
+ case WABT_OPCODE_I32_TRUNC_S_F64: {
+ VALUE_TYPE_F64 value = POP_F64();
+ TRAP_IF(wabt_is_nan_f64(value), INVALID_CONVERSION_TO_INTEGER);
+ TRAP_UNLESS(is_in_range_i32_trunc_s_f64(value), INTEGER_OVERFLOW);
+ PUSH_I32((int32_t)BITCAST_TO_F64(value));
+ break;
+ }
+
+ case WABT_OPCODE_I32_TRUNC_U_F32: {
+ VALUE_TYPE_F32 value = POP_F32();
+ TRAP_IF(wabt_is_nan_f32(value), INVALID_CONVERSION_TO_INTEGER);
+ TRAP_UNLESS(is_in_range_i32_trunc_u_f32(value), INTEGER_OVERFLOW);
+ PUSH_I32((uint32_t)BITCAST_TO_F32(value));
+ break;
+ }
+
+ case WABT_OPCODE_I32_TRUNC_U_F64: {
+ VALUE_TYPE_F64 value = POP_F64();
+ TRAP_IF(wabt_is_nan_f64(value), INVALID_CONVERSION_TO_INTEGER);
+ TRAP_UNLESS(is_in_range_i32_trunc_u_f64(value), INTEGER_OVERFLOW);
+ PUSH_I32((uint32_t)BITCAST_TO_F64(value));
+ break;
+ }
+
+ case WABT_OPCODE_I32_WRAP_I64: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_I32((uint32_t)value);
+ break;
+ }
+
+ case WABT_OPCODE_I64_TRUNC_S_F32: {
+ VALUE_TYPE_F32 value = POP_F32();
+ TRAP_IF(wabt_is_nan_f32(value), INVALID_CONVERSION_TO_INTEGER);
+ TRAP_UNLESS(is_in_range_i64_trunc_s_f32(value), INTEGER_OVERFLOW);
+ PUSH_I64((int64_t)BITCAST_TO_F32(value));
+ break;
+ }
+
+ case WABT_OPCODE_I64_TRUNC_S_F64: {
+ VALUE_TYPE_F64 value = POP_F64();
+ TRAP_IF(wabt_is_nan_f64(value), INVALID_CONVERSION_TO_INTEGER);
+ TRAP_UNLESS(is_in_range_i64_trunc_s_f64(value), INTEGER_OVERFLOW);
+ PUSH_I64((int64_t)BITCAST_TO_F64(value));
+ break;
+ }
+
+ case WABT_OPCODE_I64_TRUNC_U_F32: {
+ VALUE_TYPE_F32 value = POP_F32();
+ TRAP_IF(wabt_is_nan_f32(value), INVALID_CONVERSION_TO_INTEGER);
+ TRAP_UNLESS(is_in_range_i64_trunc_u_f32(value), INTEGER_OVERFLOW);
+ PUSH_I64((uint64_t)BITCAST_TO_F32(value));
+ break;
+ }
+
+ case WABT_OPCODE_I64_TRUNC_U_F64: {
+ VALUE_TYPE_F64 value = POP_F64();
+ TRAP_IF(wabt_is_nan_f64(value), INVALID_CONVERSION_TO_INTEGER);
+ TRAP_UNLESS(is_in_range_i64_trunc_u_f64(value), INTEGER_OVERFLOW);
+ PUSH_I64((uint64_t)BITCAST_TO_F64(value));
+ break;
+ }
+
+ case WABT_OPCODE_I64_EXTEND_S_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_I64((int64_t)BITCAST_I32_TO_SIGNED(value));
+ break;
+ }
+
+ case WABT_OPCODE_I64_EXTEND_U_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_I64((uint64_t)value);
+ break;
+ }
+
+ case WABT_OPCODE_F32_CONVERT_S_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_F32(BITCAST_FROM_F32((float)BITCAST_I32_TO_SIGNED(value)));
+ break;
+ }
+
+ case WABT_OPCODE_F32_CONVERT_U_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_F32(BITCAST_FROM_F32((float)value));
+ break;
+ }
+
+ case WABT_OPCODE_F32_CONVERT_S_I64: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_F32(BITCAST_FROM_F32((float)BITCAST_I64_TO_SIGNED(value)));
+ break;
+ }
+
+ case WABT_OPCODE_F32_CONVERT_U_I64: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_F32(BITCAST_FROM_F32((float)value));
+ break;
+ }
+
+ case WABT_OPCODE_F32_DEMOTE_F64: {
+ VALUE_TYPE_F64 value = POP_F64();
+ if (WABT_LIKELY(is_in_range_f64_demote_f32(value))) {
+ PUSH_F32(BITCAST_FROM_F32((float)BITCAST_TO_F64(value)));
+ } else if (is_in_range_f64_demote_f32_round_to_f32_max(value)) {
+ PUSH_F32(F32_MAX);
+ } else if (is_in_range_f64_demote_f32_round_to_neg_f32_max(value)) {
+ PUSH_F32(F32_NEG_MAX);
+ } else {
+ uint32_t sign = (value >> 32) & F32_SIGN_MASK;
+ uint32_t tag = 0;
+ if (IS_NAN_F64(value)) {
+ tag = F32_QUIET_NAN_BIT |
+ ((value >> (F64_SIG_BITS - F32_SIG_BITS)) & F32_SIG_MASK);
+ }
+ PUSH_F32(sign | F32_INF | tag);
+ }
+ break;
+ }
+
+ case WABT_OPCODE_F32_REINTERPRET_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_F32(value);
+ break;
+ }
+
+ case WABT_OPCODE_F64_CONVERT_S_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_F64(BITCAST_FROM_F64((double)BITCAST_I32_TO_SIGNED(value)));
+ break;
+ }
+
+ case WABT_OPCODE_F64_CONVERT_U_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_F64(BITCAST_FROM_F64((double)value));
+ break;
+ }
+
+ case WABT_OPCODE_F64_CONVERT_S_I64: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_F64(BITCAST_FROM_F64((double)BITCAST_I64_TO_SIGNED(value)));
+ break;
+ }
+
+ case WABT_OPCODE_F64_CONVERT_U_I64: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_F64(BITCAST_FROM_F64((double)value));
+ break;
+ }
+
+ case WABT_OPCODE_F64_PROMOTE_F32: {
+ VALUE_TYPE_F32 value = POP_F32();
+ PUSH_F64(BITCAST_FROM_F64((double)BITCAST_TO_F32(value)));
+ break;
+ }
+
+ case WABT_OPCODE_F64_REINTERPRET_I64: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_F64(value);
+ break;
+ }
+
+ case WABT_OPCODE_I32_REINTERPRET_F32: {
+ VALUE_TYPE_F32 value = POP_F32();
+ PUSH_I32(value);
+ break;
+ }
+
+ case WABT_OPCODE_I64_REINTERPRET_F64: {
+ VALUE_TYPE_F64 value = POP_F64();
+ PUSH_I64(value);
+ break;
+ }
+
+ case WABT_OPCODE_I32_ROTR:
+ BINOP_ROT(I32, RIGHT);
+ break;
+
+ case WABT_OPCODE_I32_ROTL:
+ BINOP_ROT(I32, LEFT);
+ break;
+
+ case WABT_OPCODE_I64_ROTR:
+ BINOP_ROT(I64, RIGHT);
+ break;
+
+ case WABT_OPCODE_I64_ROTL:
+ BINOP_ROT(I64, LEFT);
+ break;
+
+ case WABT_OPCODE_I64_EQZ: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_I64(value == 0);
+ break;
+ }
+
+ case WABT_OPCODE_ALLOCA: {
+ WabtInterpreterValue* old_value_stack_top = thread->value_stack_top;
+ thread->value_stack_top += read_u32(&pc);
+ CHECK_STACK();
+ memset(old_value_stack_top, 0,
+ (thread->value_stack_top - old_value_stack_top) *
+ sizeof(WabtInterpreterValue));
+ break;
+ }
+
+ case WABT_OPCODE_BR_UNLESS: {
+ uint32_t new_pc = read_u32(&pc);
+ if (!POP_I32())
+ GOTO(new_pc);
+ break;
+ }
+
+ case WABT_OPCODE_DROP:
+ (void)POP();
+ break;
+
+ case WABT_OPCODE_DROP_KEEP: {
+ uint32_t drop_count = read_u32(&pc);
+ uint8_t keep_count = *pc++;
+ DROP_KEEP(drop_count, keep_count);
+ break;
+ }
+
+ case WABT_OPCODE_DATA:
+ /* shouldn't ever execute this */
+ assert(0);
+ break;
+
+ case WABT_OPCODE_NOP:
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+ }
+
+exit_loop:
+ thread->pc = pc - istream;
+ return result;
+}
+
+void wabt_trace_pc(WabtInterpreterThread* thread, WabtStream* stream) {
+ const uint8_t* istream = (const uint8_t*)thread->env->istream.start;
+ const uint8_t* pc = &istream[thread->pc];
+ size_t value_stack_depth = thread->value_stack_top - thread->value_stack.data;
+ size_t call_stack_depth = thread->call_stack_top - thread->call_stack.data;
+
+ wabt_writef(stream, "#%" PRIzd ". %4" PRIzd ": V:%-3" PRIzd "| ",
+ call_stack_depth, pc - (uint8_t*)thread->env->istream.start,
+ value_stack_depth);
+
+ uint8_t opcode = *pc++;
+ switch (opcode) {
+ case WABT_OPCODE_SELECT:
+ wabt_writef(stream, "%s %u, %" PRIu64 ", %" PRIu64 "\n",
+ wabt_get_interpreter_opcode_name(opcode), PICK(3).i32,
+ PICK(2).i64, PICK(1).i64);
+ break;
+
+ case WABT_OPCODE_BR:
+ wabt_writef(stream, "%s @%u\n", wabt_get_interpreter_opcode_name(opcode),
+ read_u32_at(pc));
+ break;
+
+ case WABT_OPCODE_BR_IF:
+ wabt_writef(stream, "%s @%u, %u\n",
+ wabt_get_interpreter_opcode_name(opcode), read_u32_at(pc),
+ TOP().i32);
+ break;
+
+ case WABT_OPCODE_BR_TABLE: {
+ uint32_t num_targets = read_u32_at(pc);
+ uint32_t table_offset = read_u32_at(pc + 4);
+ VALUE_TYPE_I32 key = TOP().i32;
+ wabt_writef(stream, "%s %u, $#%u, table:$%u\n",
+ wabt_get_interpreter_opcode_name(opcode), key, num_targets,
+ table_offset);
+ break;
+ }
+
+ case WABT_OPCODE_NOP:
+ case WABT_OPCODE_RETURN:
+ case WABT_OPCODE_UNREACHABLE:
+ case WABT_OPCODE_DROP:
+ wabt_writef(stream, "%s\n", wabt_get_interpreter_opcode_name(opcode));
+ break;
+
+ case WABT_OPCODE_CURRENT_MEMORY: {
+ uint32_t memory_index = read_u32(&pc);
+ wabt_writef(stream, "%s $%u\n", wabt_get_interpreter_opcode_name(opcode),
+ memory_index);
+ break;
+ }
+
+ case WABT_OPCODE_I32_CONST:
+ wabt_writef(stream, "%s $%u\n", wabt_get_interpreter_opcode_name(opcode),
+ read_u32_at(pc));
+ break;
+
+ case WABT_OPCODE_I64_CONST:
+ wabt_writef(stream, "%s $%" PRIu64 "\n",
+ wabt_get_interpreter_opcode_name(opcode), read_u64_at(pc));
+ break;
+
+ case WABT_OPCODE_F32_CONST:
+ wabt_writef(stream, "%s $%g\n", wabt_get_interpreter_opcode_name(opcode),
+ bitcast_u32_to_f32(read_u32_at(pc)));
+ break;
+
+ case WABT_OPCODE_F64_CONST:
+ wabt_writef(stream, "%s $%g\n", wabt_get_interpreter_opcode_name(opcode),
+ bitcast_u64_to_f64(read_u64_at(pc)));
+ break;
+
+ case WABT_OPCODE_GET_LOCAL:
+ case WABT_OPCODE_GET_GLOBAL:
+ wabt_writef(stream, "%s $%u\n", wabt_get_interpreter_opcode_name(opcode),
+ read_u32_at(pc));
+ break;
+
+ case WABT_OPCODE_SET_LOCAL:
+ case WABT_OPCODE_SET_GLOBAL:
+ case WABT_OPCODE_TEE_LOCAL:
+ wabt_writef(stream, "%s $%u, %u\n",
+ wabt_get_interpreter_opcode_name(opcode), read_u32_at(pc),
+ TOP().i32);
+ break;
+
+ case WABT_OPCODE_CALL:
+ wabt_writef(stream, "%s @%u\n", wabt_get_interpreter_opcode_name(opcode),
+ read_u32_at(pc));
+ break;
+
+ case WABT_OPCODE_CALL_INDIRECT:
+ wabt_writef(stream, "%s $%u, %u\n",
+ wabt_get_interpreter_opcode_name(opcode), read_u32_at(pc),
+ TOP().i32);
+ break;
+
+ case WABT_OPCODE_CALL_HOST:
+ wabt_writef(stream, "%s $%u\n", wabt_get_interpreter_opcode_name(opcode),
+ read_u32_at(pc));
+ break;
+
+ case WABT_OPCODE_I32_LOAD8_S:
+ case WABT_OPCODE_I32_LOAD8_U:
+ case WABT_OPCODE_I32_LOAD16_S:
+ case WABT_OPCODE_I32_LOAD16_U:
+ case WABT_OPCODE_I64_LOAD8_S:
+ case WABT_OPCODE_I64_LOAD8_U:
+ case WABT_OPCODE_I64_LOAD16_S:
+ case WABT_OPCODE_I64_LOAD16_U:
+ case WABT_OPCODE_I64_LOAD32_S:
+ case WABT_OPCODE_I64_LOAD32_U:
+ case WABT_OPCODE_I32_LOAD:
+ case WABT_OPCODE_I64_LOAD:
+ case WABT_OPCODE_F32_LOAD:
+ case WABT_OPCODE_F64_LOAD: {
+ uint32_t memory_index = read_u32(&pc);
+ wabt_writef(stream, "%s $%u:%u+$%u\n",
+ wabt_get_interpreter_opcode_name(opcode), memory_index,
+ TOP().i32, read_u32_at(pc));
+ break;
+ }
+
+ case WABT_OPCODE_I32_STORE8:
+ case WABT_OPCODE_I32_STORE16:
+ case WABT_OPCODE_I32_STORE: {
+ uint32_t memory_index = read_u32(&pc);
+ wabt_writef(stream, "%s $%u:%u+$%u, %u\n",
+ wabt_get_interpreter_opcode_name(opcode), memory_index,
+ PICK(2).i32, read_u32_at(pc), PICK(1).i32);
+ break;
+ }
+
+ case WABT_OPCODE_I64_STORE8:
+ case WABT_OPCODE_I64_STORE16:
+ case WABT_OPCODE_I64_STORE32:
+ case WABT_OPCODE_I64_STORE: {
+ uint32_t memory_index = read_u32(&pc);
+ wabt_writef(stream, "%s $%u:%u+$%u, %" PRIu64 "\n",
+ wabt_get_interpreter_opcode_name(opcode), memory_index,
+ PICK(2).i32, read_u32_at(pc), PICK(1).i64);
+ break;
+ }
+
+ case WABT_OPCODE_F32_STORE: {
+ uint32_t memory_index = read_u32(&pc);
+ wabt_writef(stream, "%s $%u:%u+$%u, %g\n",
+ wabt_get_interpreter_opcode_name(opcode), memory_index,
+ PICK(2).i32, read_u32_at(pc),
+ bitcast_u32_to_f32(PICK(1).f32_bits));
+ break;
+ }
+
+ case WABT_OPCODE_F64_STORE: {
+ uint32_t memory_index = read_u32(&pc);
+ wabt_writef(stream, "%s $%u:%u+$%u, %g\n",
+ wabt_get_interpreter_opcode_name(opcode), memory_index,
+ PICK(2).i32, read_u32_at(pc),
+ bitcast_u64_to_f64(PICK(1).f64_bits));
+ break;
+ }
+
+ case WABT_OPCODE_GROW_MEMORY: {
+ uint32_t memory_index = read_u32(&pc);
+ wabt_writef(stream, "%s $%u:%u\n",
+ wabt_get_interpreter_opcode_name(opcode), memory_index,
+ TOP().i32);
+ break;
+ }
+
+ case WABT_OPCODE_I32_ADD:
+ case WABT_OPCODE_I32_SUB:
+ case WABT_OPCODE_I32_MUL:
+ case WABT_OPCODE_I32_DIV_S:
+ case WABT_OPCODE_I32_DIV_U:
+ case WABT_OPCODE_I32_REM_S:
+ case WABT_OPCODE_I32_REM_U:
+ case WABT_OPCODE_I32_AND:
+ case WABT_OPCODE_I32_OR:
+ case WABT_OPCODE_I32_XOR:
+ case WABT_OPCODE_I32_SHL:
+ case WABT_OPCODE_I32_SHR_U:
+ case WABT_OPCODE_I32_SHR_S:
+ case WABT_OPCODE_I32_EQ:
+ case WABT_OPCODE_I32_NE:
+ case WABT_OPCODE_I32_LT_S:
+ case WABT_OPCODE_I32_LE_S:
+ case WABT_OPCODE_I32_LT_U:
+ case WABT_OPCODE_I32_LE_U:
+ case WABT_OPCODE_I32_GT_S:
+ case WABT_OPCODE_I32_GE_S:
+ case WABT_OPCODE_I32_GT_U:
+ case WABT_OPCODE_I32_GE_U:
+ case WABT_OPCODE_I32_ROTR:
+ case WABT_OPCODE_I32_ROTL:
+ wabt_writef(stream, "%s %u, %u\n",
+ wabt_get_interpreter_opcode_name(opcode), PICK(2).i32,
+ PICK(1).i32);
+ break;
+
+ case WABT_OPCODE_I32_CLZ:
+ case WABT_OPCODE_I32_CTZ:
+ case WABT_OPCODE_I32_POPCNT:
+ case WABT_OPCODE_I32_EQZ:
+ wabt_writef(stream, "%s %u\n", wabt_get_interpreter_opcode_name(opcode),
+ TOP().i32);
+ break;
+
+ case WABT_OPCODE_I64_ADD:
+ case WABT_OPCODE_I64_SUB:
+ case WABT_OPCODE_I64_MUL:
+ case WABT_OPCODE_I64_DIV_S:
+ case WABT_OPCODE_I64_DIV_U:
+ case WABT_OPCODE_I64_REM_S:
+ case WABT_OPCODE_I64_REM_U:
+ case WABT_OPCODE_I64_AND:
+ case WABT_OPCODE_I64_OR:
+ case WABT_OPCODE_I64_XOR:
+ case WABT_OPCODE_I64_SHL:
+ case WABT_OPCODE_I64_SHR_U:
+ case WABT_OPCODE_I64_SHR_S:
+ case WABT_OPCODE_I64_EQ:
+ case WABT_OPCODE_I64_NE:
+ case WABT_OPCODE_I64_LT_S:
+ case WABT_OPCODE_I64_LE_S:
+ case WABT_OPCODE_I64_LT_U:
+ case WABT_OPCODE_I64_LE_U:
+ case WABT_OPCODE_I64_GT_S:
+ case WABT_OPCODE_I64_GE_S:
+ case WABT_OPCODE_I64_GT_U:
+ case WABT_OPCODE_I64_GE_U:
+ case WABT_OPCODE_I64_ROTR:
+ case WABT_OPCODE_I64_ROTL:
+ wabt_writef(stream, "%s %" PRIu64 ", %" PRIu64 "\n",
+ wabt_get_interpreter_opcode_name(opcode), PICK(2).i64,
+ PICK(1).i64);
+ break;
+
+ case WABT_OPCODE_I64_CLZ:
+ case WABT_OPCODE_I64_CTZ:
+ case WABT_OPCODE_I64_POPCNT:
+ case WABT_OPCODE_I64_EQZ:
+ wabt_writef(stream, "%s %" PRIu64 "\n",
+ wabt_get_interpreter_opcode_name(opcode), TOP().i64);
+ break;
+
+ case WABT_OPCODE_F32_ADD:
+ case WABT_OPCODE_F32_SUB:
+ case WABT_OPCODE_F32_MUL:
+ case WABT_OPCODE_F32_DIV:
+ case WABT_OPCODE_F32_MIN:
+ case WABT_OPCODE_F32_MAX:
+ case WABT_OPCODE_F32_COPYSIGN:
+ case WABT_OPCODE_F32_EQ:
+ case WABT_OPCODE_F32_NE:
+ case WABT_OPCODE_F32_LT:
+ case WABT_OPCODE_F32_LE:
+ case WABT_OPCODE_F32_GT:
+ case WABT_OPCODE_F32_GE:
+ wabt_writef(
+ stream, "%s %g, %g\n", wabt_get_interpreter_opcode_name(opcode),
+ bitcast_u32_to_f32(PICK(2).i32), bitcast_u32_to_f32(PICK(1).i32));
+ break;
+
+ case WABT_OPCODE_F32_ABS:
+ case WABT_OPCODE_F32_NEG:
+ case WABT_OPCODE_F32_CEIL:
+ case WABT_OPCODE_F32_FLOOR:
+ case WABT_OPCODE_F32_TRUNC:
+ case WABT_OPCODE_F32_NEAREST:
+ case WABT_OPCODE_F32_SQRT:
+ wabt_writef(stream, "%s %g\n", wabt_get_interpreter_opcode_name(opcode),
+ bitcast_u32_to_f32(TOP().i32));
+ break;
+
+ case WABT_OPCODE_F64_ADD:
+ case WABT_OPCODE_F64_SUB:
+ case WABT_OPCODE_F64_MUL:
+ case WABT_OPCODE_F64_DIV:
+ case WABT_OPCODE_F64_MIN:
+ case WABT_OPCODE_F64_MAX:
+ case WABT_OPCODE_F64_COPYSIGN:
+ case WABT_OPCODE_F64_EQ:
+ case WABT_OPCODE_F64_NE:
+ case WABT_OPCODE_F64_LT:
+ case WABT_OPCODE_F64_LE:
+ case WABT_OPCODE_F64_GT:
+ case WABT_OPCODE_F64_GE:
+ wabt_writef(
+ stream, "%s %g, %g\n", wabt_get_interpreter_opcode_name(opcode),
+ bitcast_u64_to_f64(PICK(2).i64), bitcast_u64_to_f64(PICK(1).i64));
+ break;
+
+ case WABT_OPCODE_F64_ABS:
+ case WABT_OPCODE_F64_NEG:
+ case WABT_OPCODE_F64_CEIL:
+ case WABT_OPCODE_F64_FLOOR:
+ case WABT_OPCODE_F64_TRUNC:
+ case WABT_OPCODE_F64_NEAREST:
+ case WABT_OPCODE_F64_SQRT:
+ wabt_writef(stream, "%s %g\n", wabt_get_interpreter_opcode_name(opcode),
+ bitcast_u64_to_f64(TOP().i64));
+ break;
+
+ case WABT_OPCODE_I32_TRUNC_S_F32:
+ case WABT_OPCODE_I32_TRUNC_U_F32:
+ case WABT_OPCODE_I64_TRUNC_S_F32:
+ case WABT_OPCODE_I64_TRUNC_U_F32:
+ case WABT_OPCODE_F64_PROMOTE_F32:
+ case WABT_OPCODE_I32_REINTERPRET_F32:
+ wabt_writef(stream, "%s %g\n", wabt_get_interpreter_opcode_name(opcode),
+ bitcast_u32_to_f32(TOP().i32));
+ break;
+
+ case WABT_OPCODE_I32_TRUNC_S_F64:
+ case WABT_OPCODE_I32_TRUNC_U_F64:
+ case WABT_OPCODE_I64_TRUNC_S_F64:
+ case WABT_OPCODE_I64_TRUNC_U_F64:
+ case WABT_OPCODE_F32_DEMOTE_F64:
+ case WABT_OPCODE_I64_REINTERPRET_F64:
+ wabt_writef(stream, "%s %g\n", wabt_get_interpreter_opcode_name(opcode),
+ bitcast_u64_to_f64(TOP().i64));
+ break;
+
+ case WABT_OPCODE_I32_WRAP_I64:
+ case WABT_OPCODE_F32_CONVERT_S_I64:
+ case WABT_OPCODE_F32_CONVERT_U_I64:
+ case WABT_OPCODE_F64_CONVERT_S_I64:
+ case WABT_OPCODE_F64_CONVERT_U_I64:
+ case WABT_OPCODE_F64_REINTERPRET_I64:
+ wabt_writef(stream, "%s %" PRIu64 "\n",
+ wabt_get_interpreter_opcode_name(opcode), TOP().i64);
+ break;
+
+ case WABT_OPCODE_I64_EXTEND_S_I32:
+ case WABT_OPCODE_I64_EXTEND_U_I32:
+ case WABT_OPCODE_F32_CONVERT_S_I32:
+ case WABT_OPCODE_F32_CONVERT_U_I32:
+ case WABT_OPCODE_F32_REINTERPRET_I32:
+ case WABT_OPCODE_F64_CONVERT_S_I32:
+ case WABT_OPCODE_F64_CONVERT_U_I32:
+ wabt_writef(stream, "%s %u\n", wabt_get_interpreter_opcode_name(opcode),
+ TOP().i32);
+ break;
+
+ case WABT_OPCODE_ALLOCA:
+ wabt_writef(stream, "%s $%u\n", wabt_get_interpreter_opcode_name(opcode),
+ read_u32_at(pc));
+ break;
+
+ case WABT_OPCODE_BR_UNLESS:
+ wabt_writef(stream, "%s @%u, %u\n",
+ wabt_get_interpreter_opcode_name(opcode), read_u32_at(pc),
+ TOP().i32);
+ break;
+
+ case WABT_OPCODE_DROP_KEEP:
+ wabt_writef(stream, "%s $%u $%u\n",
+ wabt_get_interpreter_opcode_name(opcode), read_u32_at(pc),
+ *(pc + 4));
+ break;
+
+ case WABT_OPCODE_DATA:
+ /* shouldn't ever execute this */
+ assert(0);
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+}
+
+void wabt_disassemble(WabtInterpreterEnvironment* env,
+ WabtStream* stream,
+ uint32_t from,
+ uint32_t to) {
+ /* TODO(binji): mark function entries */
+ /* TODO(binji): track value stack size */
+ if (from >= env->istream.size)
+ return;
+ if (to > env->istream.size)
+ to = env->istream.size;
+ const uint8_t* istream = (const uint8_t*)env->istream.start;
+ const uint8_t* pc = &istream[from];
+
+ while ((uint32_t)(pc - istream) < to) {
+ wabt_writef(stream, "%4" PRIzd "| ", pc - istream);
+
+ uint8_t opcode = *pc++;
+ switch (opcode) {
+ case WABT_OPCODE_SELECT:
+ wabt_writef(stream, "%s %%[-3], %%[-2], %%[-1]\n",
+ wabt_get_interpreter_opcode_name(opcode));
+ break;
+
+ case WABT_OPCODE_BR:
+ wabt_writef(stream, "%s @%u\n",
+ wabt_get_interpreter_opcode_name(opcode), read_u32(&pc));
+ break;
+
+ case WABT_OPCODE_BR_IF:
+ wabt_writef(stream, "%s @%u, %%[-1]\n",
+ wabt_get_interpreter_opcode_name(opcode), read_u32(&pc));
+ break;
+
+ case WABT_OPCODE_BR_TABLE: {
+ uint32_t num_targets = read_u32(&pc);
+ uint32_t table_offset = read_u32(&pc);
+ wabt_writef(stream, "%s %%[-1], $#%u, table:$%u\n",
+ wabt_get_interpreter_opcode_name(opcode), num_targets,
+ table_offset);
+ break;
+ }
+
+ case WABT_OPCODE_NOP:
+ case WABT_OPCODE_RETURN:
+ case WABT_OPCODE_UNREACHABLE:
+ case WABT_OPCODE_DROP:
+ wabt_writef(stream, "%s\n", wabt_get_interpreter_opcode_name(opcode));
+ break;
+
+ case WABT_OPCODE_CURRENT_MEMORY: {
+ uint32_t memory_index = read_u32(&pc);
+ wabt_writef(stream, "%s $%u\n",
+ wabt_get_interpreter_opcode_name(opcode), memory_index);
+ break;
+ }
+
+ case WABT_OPCODE_I32_CONST:
+ wabt_writef(stream, "%s $%u\n",
+ wabt_get_interpreter_opcode_name(opcode), read_u32(&pc));
+ break;
+
+ case WABT_OPCODE_I64_CONST:
+ wabt_writef(stream, "%s $%" PRIu64 "\n",
+ wabt_get_interpreter_opcode_name(opcode), read_u64(&pc));
+ break;
+
+ case WABT_OPCODE_F32_CONST:
+ wabt_writef(stream, "%s $%g\n",
+ wabt_get_interpreter_opcode_name(opcode),
+ bitcast_u32_to_f32(read_u32(&pc)));
+ break;
+
+ case WABT_OPCODE_F64_CONST:
+ wabt_writef(stream, "%s $%g\n",
+ wabt_get_interpreter_opcode_name(opcode),
+ bitcast_u64_to_f64(read_u64(&pc)));
+ break;
+
+ case WABT_OPCODE_GET_LOCAL:
+ case WABT_OPCODE_GET_GLOBAL:
+ wabt_writef(stream, "%s $%u\n",
+ wabt_get_interpreter_opcode_name(opcode), read_u32(&pc));
+ break;
+
+ case WABT_OPCODE_SET_LOCAL:
+ case WABT_OPCODE_SET_GLOBAL:
+ case WABT_OPCODE_TEE_LOCAL:
+ wabt_writef(stream, "%s $%u, %%[-1]\n",
+ wabt_get_interpreter_opcode_name(opcode), read_u32(&pc));
+ break;
+
+ case WABT_OPCODE_CALL:
+ wabt_writef(stream, "%s @%u\n",
+ wabt_get_interpreter_opcode_name(opcode), read_u32(&pc));
+ break;
+
+ case WABT_OPCODE_CALL_INDIRECT: {
+ uint32_t table_index = read_u32(&pc);
+ wabt_writef(stream, "%s $%u:%u, %%[-1]\n",
+ wabt_get_interpreter_opcode_name(opcode), table_index,
+ read_u32(&pc));
+ break;
+ }
+
+ case WABT_OPCODE_CALL_HOST:
+ wabt_writef(stream, "%s $%u\n",
+ wabt_get_interpreter_opcode_name(opcode), read_u32(&pc));
+ break;
+
+ case WABT_OPCODE_I32_LOAD8_S:
+ case WABT_OPCODE_I32_LOAD8_U:
+ case WABT_OPCODE_I32_LOAD16_S:
+ case WABT_OPCODE_I32_LOAD16_U:
+ case WABT_OPCODE_I64_LOAD8_S:
+ case WABT_OPCODE_I64_LOAD8_U:
+ case WABT_OPCODE_I64_LOAD16_S:
+ case WABT_OPCODE_I64_LOAD16_U:
+ case WABT_OPCODE_I64_LOAD32_S:
+ case WABT_OPCODE_I64_LOAD32_U:
+ case WABT_OPCODE_I32_LOAD:
+ case WABT_OPCODE_I64_LOAD:
+ case WABT_OPCODE_F32_LOAD:
+ case WABT_OPCODE_F64_LOAD: {
+ uint32_t memory_index = read_u32(&pc);
+ wabt_writef(stream, "%s $%u:%%[-1]+$%u\n",
+ wabt_get_interpreter_opcode_name(opcode), memory_index,
+ read_u32(&pc));
+ break;
+ }
+
+ case WABT_OPCODE_I32_STORE8:
+ case WABT_OPCODE_I32_STORE16:
+ case WABT_OPCODE_I32_STORE:
+ case WABT_OPCODE_I64_STORE8:
+ case WABT_OPCODE_I64_STORE16:
+ case WABT_OPCODE_I64_STORE32:
+ case WABT_OPCODE_I64_STORE:
+ case WABT_OPCODE_F32_STORE:
+ case WABT_OPCODE_F64_STORE: {
+ uint32_t memory_index = read_u32(&pc);
+ wabt_writef(stream, "%s %%[-2]+$%u, $%u:%%[-1]\n",
+ wabt_get_interpreter_opcode_name(opcode), memory_index,
+ read_u32(&pc));
+ break;
+ }
+
+ case WABT_OPCODE_I32_ADD:
+ case WABT_OPCODE_I32_SUB:
+ case WABT_OPCODE_I32_MUL:
+ case WABT_OPCODE_I32_DIV_S:
+ case WABT_OPCODE_I32_DIV_U:
+ case WABT_OPCODE_I32_REM_S:
+ case WABT_OPCODE_I32_REM_U:
+ case WABT_OPCODE_I32_AND:
+ case WABT_OPCODE_I32_OR:
+ case WABT_OPCODE_I32_XOR:
+ case WABT_OPCODE_I32_SHL:
+ case WABT_OPCODE_I32_SHR_U:
+ case WABT_OPCODE_I32_SHR_S:
+ case WABT_OPCODE_I32_EQ:
+ case WABT_OPCODE_I32_NE:
+ case WABT_OPCODE_I32_LT_S:
+ case WABT_OPCODE_I32_LE_S:
+ case WABT_OPCODE_I32_LT_U:
+ case WABT_OPCODE_I32_LE_U:
+ case WABT_OPCODE_I32_GT_S:
+ case WABT_OPCODE_I32_GE_S:
+ case WABT_OPCODE_I32_GT_U:
+ case WABT_OPCODE_I32_GE_U:
+ case WABT_OPCODE_I32_ROTR:
+ case WABT_OPCODE_I32_ROTL:
+ case WABT_OPCODE_F32_ADD:
+ case WABT_OPCODE_F32_SUB:
+ case WABT_OPCODE_F32_MUL:
+ case WABT_OPCODE_F32_DIV:
+ case WABT_OPCODE_F32_MIN:
+ case WABT_OPCODE_F32_MAX:
+ case WABT_OPCODE_F32_COPYSIGN:
+ case WABT_OPCODE_F32_EQ:
+ case WABT_OPCODE_F32_NE:
+ case WABT_OPCODE_F32_LT:
+ case WABT_OPCODE_F32_LE:
+ case WABT_OPCODE_F32_GT:
+ case WABT_OPCODE_F32_GE:
+ case WABT_OPCODE_I64_ADD:
+ case WABT_OPCODE_I64_SUB:
+ case WABT_OPCODE_I64_MUL:
+ case WABT_OPCODE_I64_DIV_S:
+ case WABT_OPCODE_I64_DIV_U:
+ case WABT_OPCODE_I64_REM_S:
+ case WABT_OPCODE_I64_REM_U:
+ case WABT_OPCODE_I64_AND:
+ case WABT_OPCODE_I64_OR:
+ case WABT_OPCODE_I64_XOR:
+ case WABT_OPCODE_I64_SHL:
+ case WABT_OPCODE_I64_SHR_U:
+ case WABT_OPCODE_I64_SHR_S:
+ case WABT_OPCODE_I64_EQ:
+ case WABT_OPCODE_I64_NE:
+ case WABT_OPCODE_I64_LT_S:
+ case WABT_OPCODE_I64_LE_S:
+ case WABT_OPCODE_I64_LT_U:
+ case WABT_OPCODE_I64_LE_U:
+ case WABT_OPCODE_I64_GT_S:
+ case WABT_OPCODE_I64_GE_S:
+ case WABT_OPCODE_I64_GT_U:
+ case WABT_OPCODE_I64_GE_U:
+ case WABT_OPCODE_I64_ROTR:
+ case WABT_OPCODE_I64_ROTL:
+ case WABT_OPCODE_F64_ADD:
+ case WABT_OPCODE_F64_SUB:
+ case WABT_OPCODE_F64_MUL:
+ case WABT_OPCODE_F64_DIV:
+ case WABT_OPCODE_F64_MIN:
+ case WABT_OPCODE_F64_MAX:
+ case WABT_OPCODE_F64_COPYSIGN:
+ case WABT_OPCODE_F64_EQ:
+ case WABT_OPCODE_F64_NE:
+ case WABT_OPCODE_F64_LT:
+ case WABT_OPCODE_F64_LE:
+ case WABT_OPCODE_F64_GT:
+ case WABT_OPCODE_F64_GE:
+ wabt_writef(stream, "%s %%[-2], %%[-1]\n",
+ wabt_get_interpreter_opcode_name(opcode));
+ break;
+
+ case WABT_OPCODE_I32_CLZ:
+ case WABT_OPCODE_I32_CTZ:
+ case WABT_OPCODE_I32_POPCNT:
+ case WABT_OPCODE_I32_EQZ:
+ case WABT_OPCODE_I64_CLZ:
+ case WABT_OPCODE_I64_CTZ:
+ case WABT_OPCODE_I64_POPCNT:
+ case WABT_OPCODE_I64_EQZ:
+ case WABT_OPCODE_F32_ABS:
+ case WABT_OPCODE_F32_NEG:
+ case WABT_OPCODE_F32_CEIL:
+ case WABT_OPCODE_F32_FLOOR:
+ case WABT_OPCODE_F32_TRUNC:
+ case WABT_OPCODE_F32_NEAREST:
+ case WABT_OPCODE_F32_SQRT:
+ case WABT_OPCODE_F64_ABS:
+ case WABT_OPCODE_F64_NEG:
+ case WABT_OPCODE_F64_CEIL:
+ case WABT_OPCODE_F64_FLOOR:
+ case WABT_OPCODE_F64_TRUNC:
+ case WABT_OPCODE_F64_NEAREST:
+ case WABT_OPCODE_F64_SQRT:
+ case WABT_OPCODE_I32_TRUNC_S_F32:
+ case WABT_OPCODE_I32_TRUNC_U_F32:
+ case WABT_OPCODE_I64_TRUNC_S_F32:
+ case WABT_OPCODE_I64_TRUNC_U_F32:
+ case WABT_OPCODE_F64_PROMOTE_F32:
+ case WABT_OPCODE_I32_REINTERPRET_F32:
+ case WABT_OPCODE_I32_TRUNC_S_F64:
+ case WABT_OPCODE_I32_TRUNC_U_F64:
+ case WABT_OPCODE_I64_TRUNC_S_F64:
+ case WABT_OPCODE_I64_TRUNC_U_F64:
+ case WABT_OPCODE_F32_DEMOTE_F64:
+ case WABT_OPCODE_I64_REINTERPRET_F64:
+ case WABT_OPCODE_I32_WRAP_I64:
+ case WABT_OPCODE_F32_CONVERT_S_I64:
+ case WABT_OPCODE_F32_CONVERT_U_I64:
+ case WABT_OPCODE_F64_CONVERT_S_I64:
+ case WABT_OPCODE_F64_CONVERT_U_I64:
+ case WABT_OPCODE_F64_REINTERPRET_I64:
+ case WABT_OPCODE_I64_EXTEND_S_I32:
+ case WABT_OPCODE_I64_EXTEND_U_I32:
+ case WABT_OPCODE_F32_CONVERT_S_I32:
+ case WABT_OPCODE_F32_CONVERT_U_I32:
+ case WABT_OPCODE_F32_REINTERPRET_I32:
+ case WABT_OPCODE_F64_CONVERT_S_I32:
+ case WABT_OPCODE_F64_CONVERT_U_I32:
+ wabt_writef(stream, "%s %%[-1]\n",
+ wabt_get_interpreter_opcode_name(opcode));
+ break;
+
+ case WABT_OPCODE_GROW_MEMORY: {
+ uint32_t memory_index = read_u32(&pc);
+ wabt_writef(stream, "%s $%u:%%[-1]\n",
+ wabt_get_interpreter_opcode_name(opcode), memory_index);
+ break;
+ }
+
+ case WABT_OPCODE_ALLOCA:
+ wabt_writef(stream, "%s $%u\n",
+ wabt_get_interpreter_opcode_name(opcode), read_u32(&pc));
+ break;
+
+ case WABT_OPCODE_BR_UNLESS:
+ wabt_writef(stream, "%s @%u, %%[-1]\n",
+ wabt_get_interpreter_opcode_name(opcode), read_u32(&pc));
+ break;
+
+ case WABT_OPCODE_DROP_KEEP: {
+ uint32_t drop = read_u32(&pc);
+ uint32_t keep = *pc++;
+ wabt_writef(stream, "%s $%u $%u\n",
+ wabt_get_interpreter_opcode_name(opcode), drop, keep);
+ break;
+ }
+
+ case WABT_OPCODE_DATA: {
+ uint32_t num_bytes = read_u32(&pc);
+ wabt_writef(stream, "%s $%u\n",
+ wabt_get_interpreter_opcode_name(opcode), num_bytes);
+ /* for now, the only reason this is emitted is for br_table, so display
+ * it as a list of table entries */
+ if (num_bytes % WABT_TABLE_ENTRY_SIZE == 0) {
+ uint32_t num_entries = num_bytes / WABT_TABLE_ENTRY_SIZE;
+ uint32_t i;
+ for (i = 0; i < num_entries; ++i) {
+ wabt_writef(stream, "%4" PRIzd "| ", pc - istream);
+ uint32_t offset;
+ uint32_t drop;
+ uint8_t keep;
+ read_table_entry_at(pc, &offset, &drop, &keep);
+ wabt_writef(stream, " entry %d: offset: %u drop: %u keep: %u\n", i,
+ offset, drop, keep);
+ pc += WABT_TABLE_ENTRY_SIZE;
+ }
+ } else {
+ /* just skip those data bytes */
+ pc += num_bytes;
+ }
+
+ break;
+ }
+
+ default:
+ assert(0);
+ break;
+ }
+ }
+}
+
+void wabt_disassemble_module(WabtInterpreterEnvironment* env,
+ WabtStream* stream,
+ WabtInterpreterModule* module) {
+ assert(!module->is_host);
+ wabt_disassemble(env, stream, module->defined.istream_start,
+ module->defined.istream_end);
+}
+