summaryrefslogtreecommitdiff
path: root/src/wasm-interpreter.c
diff options
context:
space:
mode:
authorBen Smith <binji@chromium.org>2016-03-25 23:47:26 -0700
committerBen Smith <binji@chromium.org>2016-04-02 22:32:14 -0700
commit2e4639e97f03c306374a02c4e8097add36f31aa7 (patch)
tree21e5c7bc7288da93c8dd6a6b4b3ee748ea3932c0 /src/wasm-interpreter.c
parentda5e6ec567a41f6d21b9477d67b5d1960bf1dcfa (diff)
downloadwabt-2e4639e97f03c306374a02c4e8097add36f31aa7.tar.gz
wabt-2e4639e97f03c306374a02c4e8097add36f31aa7.tar.bz2
wabt-2e4639e97f03c306374a02c4e8097add36f31aa7.zip
wasm interpreter
Works by generating an instruction stream for a simple stack machine.
Diffstat (limited to 'src/wasm-interpreter.c')
-rw-r--r--src/wasm-interpreter.c2118
1 files changed, 2118 insertions, 0 deletions
diff --git a/src/wasm-interpreter.c b/src/wasm-interpreter.c
new file mode 100644
index 00000000..fbcf4511
--- /dev/null
+++ b/src/wasm-interpreter.c
@@ -0,0 +1,2118 @@
+/*
+ * 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-interpreter.h"
+
+#include <assert.h>
+#include <math.h>
+
+#define LOG 0
+
+#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
+
+#define CHECK_RESULT(expr) \
+ do { \
+ if ((expr) != WASM_OK) \
+ return WASM_ERROR; \
+ } while (0)
+
+WasmResult wasm_init_interpreter_thread(WasmAllocator* allocator,
+ WasmInterpreterModule* module,
+ WasmInterpreterThread* thread,
+ WasmInterpreterThreadOptions* options) {
+ CHECK_RESULT(wasm_new_interpreter_value_array(allocator, &thread->value_stack,
+ options->value_stack_size));
+ CHECK_RESULT(wasm_new_uint32_array(allocator, &thread->call_stack,
+ options->call_stack_size));
+ thread->value_stack_top = 0;
+ thread->call_stack_top = 0;
+ thread->pc = options->pc;
+
+ /* allocate import_args based on the signature with the most params */
+ /* TODO(binji): move this elsewhere? */
+ uint32_t i;
+ uint32_t max_import_params = 0;
+ for (i = 0; i < module->imports.size; ++i) {
+ WasmInterpreterImport* import = &module->imports.data[i];
+ assert(import->sig_index < module->sigs.size);
+ WasmInterpreterFuncSignature* sig = &module->sigs.data[import->sig_index];
+ if (sig->param_types.size > max_import_params)
+ max_import_params = sig->param_types.size;
+ }
+ CHECK_RESULT(wasm_new_interpreter_typed_value_array(
+ allocator, &thread->import_args, max_import_params));
+ return WASM_OK;
+}
+
+void wasm_destroy_interpreter_thread(WasmAllocator* allocator,
+ WasmInterpreterThread* thread) {
+ wasm_destroy_interpreter_value_array(allocator, &thread->value_stack);
+ wasm_destroy_uint32_array(allocator, &thread->call_stack);
+}
+
+#define F32_SIGN_MASK 0x80000000U
+#define F32_EXP_MASK 0x7f800000U
+#define F32_SIG_MASK 0x007fffffU
+#define F32_EXP_SHIFT 23
+#define F32_EXP_BIAS 127
+#define F64_SIGN_MASK 0x8000000000000000ULL
+#define F64_EXP_MASK 0x7ff0000000000000ULL
+#define F64_SIG_MASK 0x000fffffffffffffULL
+#define F64_EXP_SHIFT 52
+#define F64_EXP_BIAS 1023
+
+static WASM_INLINE int is_nan_f32(uint32_t f32_bits) {
+ return ((f32_bits & F32_EXP_MASK) == F32_EXP_MASK) &&
+ ((f32_bits & F32_SIG_MASK) != 0);
+}
+
+static WASM_INLINE int is_nan_f64(uint64_t f64_bits) {
+ return ((f64_bits & F64_EXP_MASK) == F64_EXP_MASK) &&
+ ((f64_bits & F64_SIG_MASK) != 0);
+}
+
+static WASM_INLINE int get_exp_f32(uint32_t f32_bits) {
+ return (int)((f32_bits & F32_EXP_MASK) >> F32_EXP_SHIFT) - F32_EXP_BIAS;
+}
+
+static WASM_INLINE int get_exp_f64(uint64_t f64_bits) {
+ return (int)((f64_bits & F64_EXP_MASK) >> F64_EXP_SHIFT) - F64_EXP_BIAS;
+}
+
+static WASM_INLINE int is_signed_f32(uint32_t f32_bits) {
+ return (f32_bits & F32_SIGN_MASK) != 0;
+}
+
+static WASM_INLINE int is_signed_f64(uint64_t f64_bits) {
+ return (f64_bits & F64_SIGN_MASK) != 0;
+}
+
+#define IS_NAN_F32 is_nan_f32
+#define IS_NAN_F64 is_nan_f64
+
+#define DEFINE_BITCAST(name, src, dst) \
+ static WASM_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 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 WASM_INTERPRETER_TRAP_##type
+
+#define CHECK_STACK() \
+ do { \
+ if (vs_top >= vs_end) \
+ TRAP(VALUE_STACK_EXHAUSTED); \
+ } while (0)
+
+#define PUSH(v) \
+ do { \
+ CHECK_STACK(); \
+ (*vs_top++) = (v); \
+ } while (0)
+
+#define PUSH_TYPE(type, v) \
+ do { \
+ CHECK_STACK(); \
+ (*vs_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 STACK_VALUE(depth) (*(vs_top - (depth)))
+#define TOP() (STACK_VALUE(1))
+#define POP() (*--vs_top)
+#define POP_I32() (POP().i32)
+#define POP_I64() (POP().i64)
+#define POP_F32() (POP().f32_bits)
+#define POP_F64() (POP().f64_bits)
+
+#define GOTO(offset) pc = &istream[offset]
+
+#define PUSH_CALL(o) \
+ do { \
+ if (cs_top >= cs_end) \
+ TRAP(CALL_STACK_EXHAUSTED); \
+ (*cs_top++) = (pc - istream); \
+ } while (0)
+
+#define POP_CALL() (*--cs_top)
+
+#define LOAD(type, mem_type) \
+ do { \
+ uint64_t offset = (uint64_t)POP_I32() + read_u32(&pc); \
+ if (offset >= module->memory.byte_size) \
+ TRAP(MEMORY_ACCESS_OUT_OF_BOUNDS); \
+ void* src = (void*)((size_t)module->memory.data + offset); \
+ MEM_TYPE_##mem_type value; \
+ 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 { \
+ VALUE_TYPE_##type value = POP_##type(); \
+ uint64_t offset = (uint64_t)POP_I32() + read_u32(&pc); \
+ if (offset >= module->memory.byte_size) \
+ TRAP(MEMORY_ACCESS_OUT_OF_BOUNDS); \
+ void* dst = (void*)((size_t)module->memory.data + offset); \
+ MEM_TYPE_##mem_type src = (MEM_TYPE_##mem_type)value; \
+ memcpy(dst, &src, sizeof(MEM_TYPE_##mem_type)); \
+ PUSH_##type(value); \
+ } while (0)
+
+#define BINOP(type, op) \
+ do { \
+ VALUE_TYPE_##type rhs = POP_##type(); \
+ VALUE_TYPE_##type lhs = POP_##type(); \
+ PUSH_##type(lhs op rhs); \
+ } while (0)
+
+#define BINOP_SIGNED(type, op) \
+ do { \
+ VALUE_TYPE_##type rhs = POP_##type(); \
+ VALUE_TYPE_##type lhs = POP_##type(); \
+ PUSH_##type(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 (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_TRAP_ZERO(type, op, sign) \
+ do { \
+ VALUE_TYPE_##type rhs = POP_##type(); \
+ VALUE_TYPE_##type lhs = POP_##type(); \
+ if (rhs == 0) \
+ TRAP(INTEGER_DIVIDE_BY_ZERO); \
+ PUSH_##type(BITCAST_##type##_TO_##sign(lhs) \
+ op BITCAST_##type##_TO_##sign(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 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 (IS_NAN_##type(lhs)) { \
+ result = lhs; \
+ } else if (IS_NAN_##type(rhs)) { \
+ result = 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 WASM_INLINE uint32_t read_u32(const uint8_t** pc) {
+ uint32_t result;
+ memcpy(&result, *pc, sizeof(uint32_t));
+ *pc += sizeof(uint32_t);
+ return result;
+}
+
+static WASM_INLINE uint32_t read_u32_at(const uint8_t* pc) {
+ uint32_t result;
+ memcpy(&result, pc, sizeof(uint32_t));
+ return result;
+}
+
+static WASM_INLINE uint64_t read_u64(const uint8_t** pc) {
+ uint64_t result;
+ memcpy(&result, *pc, sizeof(uint64_t));
+ *pc += sizeof(uint64_t);
+ return result;
+}
+
+WasmInterpreterResult wasm_run_interpreter(WasmInterpreterModule* module,
+ WasmInterpreterThread* thread,
+ uint32_t num_instructions) {
+ WasmInterpreterResult result = WASM_INTERPRETER_OK;
+ WasmInterpreterValue* vs_bottom = &thread->value_stack.data[0];
+ WasmInterpreterValue* vs_top = vs_bottom + thread->value_stack_top;
+ WasmInterpreterValue* vs_end = vs_bottom + thread->value_stack.size;
+ uint32_t* cs_bottom = &thread->call_stack.data[0];
+ uint32_t* cs_top = cs_bottom + thread->call_stack_top;
+ uint32_t* cs_end = cs_bottom + thread->call_stack.size;
+
+ const uint8_t* istream = module->istream.start;
+ const uint8_t* pc = &istream[thread->pc];
+ uint32_t i;
+ for (i = 0; i < num_instructions; ++i) {
+ uint8_t opcode = *pc++;
+#if LOG
+ printf("%" PRIzd ": %s\n", (pc - 1) - istream, s_opcode_name[opcode]);
+#endif
+ switch (opcode) {
+ case WASM_OPCODE_SELECT: {
+ VALUE_TYPE_I32 cond = POP_I32();
+ WasmInterpreterValue false_ = POP();
+ WasmInterpreterValue true_ = POP();
+ PUSH(cond ? true_ : false_);
+ break;
+ }
+
+ case WASM_OPCODE_BR:
+ GOTO(read_u32(&pc));
+ break;
+
+ case WASM_OPCODE_BR_IF: {
+ uint32_t new_pc = read_u32(&pc);
+ if (POP_I32())
+ GOTO(new_pc);
+ break;
+ }
+
+ case WASM_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_offset =
+ (key >= num_targets ? num_targets : key) * sizeof(uint32_t);
+ uint32_t new_pc = read_u32_at(istream + table_offset + key_offset);
+ GOTO(new_pc);
+ break;
+ }
+
+ case WASM_OPCODE_RETURN:
+ if (cs_top == cs_bottom) {
+ result = WASM_INTERPRETER_RETURNED;
+ goto exit_loop;
+ }
+ GOTO(POP_CALL());
+ break;
+
+ case WASM_OPCODE_UNREACHABLE:
+ TRAP(UNREACHABLE);
+ break;
+
+ case WASM_OPCODE_I8_CONST:
+ break;
+
+ case WASM_OPCODE_I32_CONST:
+ PUSH_TYPE(I32, read_u32(&pc));
+ break;
+
+ case WASM_OPCODE_I64_CONST:
+ PUSH_TYPE(I64, read_u64(&pc));
+ break;
+
+ case WASM_OPCODE_F32_CONST:
+ PUSH_TYPE(F32, read_u32(&pc));
+ break;
+
+ case WASM_OPCODE_F64_CONST:
+ PUSH_TYPE(F64, read_u64(&pc));
+ break;
+
+ case WASM_OPCODE_GET_LOCAL: {
+ WasmInterpreterValue value = STACK_VALUE(read_u32(&pc));
+ PUSH(value);
+ break;
+ }
+
+ case WASM_OPCODE_SET_LOCAL:
+ STACK_VALUE(read_u32(&pc)) = TOP();
+ break;
+
+ case WASM_OPCODE_CALL_FUNCTION: {
+ uint32_t offset = read_u32(&pc);
+ PUSH_CALL();
+ GOTO(offset);
+ break;
+ }
+
+ case WASM_OPCODE_CALL_INDIRECT: {
+ uint32_t sig_index = read_u32(&pc);
+ VALUE_TYPE_I32 entry_index = POP_I32();
+ if (entry_index >= module->func_table.size)
+ TRAP(UNDEFINED_TABLE_INDEX);
+ WasmInterpreterFuncTableEntry* entry =
+ &module->func_table.data[entry_index];
+ if (entry->sig_index != sig_index)
+ TRAP(INDIRECT_CALL_SIGNATURE_MISMATCH);
+ PUSH_CALL();
+ GOTO(entry->func_offset);
+ break;
+ }
+
+ case WASM_OPCODE_CALL_IMPORT: {
+ uint32_t import_index = read_u32(&pc);
+ assert(import_index < module->imports.size);
+ WasmInterpreterImport* import = &module->imports.data[import_index];
+ uint32_t sig_index = import->sig_index;
+ assert(sig_index < module->sigs.size);
+ WasmInterpreterFuncSignature* sig = &module->sigs.data[sig_index];
+ uint32_t num_args = sig->param_types.size;
+ uint32_t i;
+ assert(num_args <= thread->import_args.size);
+ for (i = num_args; i > 0; --i) {
+ WasmInterpreterValue value = POP();
+ WasmInterpreterTypedValue* arg = &thread->import_args.data[i - 1];
+ arg->type = sig->param_types.data[i - 1];
+ arg->value = value;
+ }
+ assert(import->callback);
+ WasmInterpreterTypedValue call_result =
+ import->callback(module, import, num_args, thread->import_args.data,
+ import->user_data);
+ if (sig->result_type != WASM_TYPE_VOID) {
+ if (call_result.type != sig->result_type)
+ TRAP(IMPORT_RESULT_TYPE_MISMATCH);
+ PUSH(call_result.value);
+ }
+ break;
+ }
+
+ case WASM_OPCODE_I32_LOAD8_S:
+ LOAD(I32, I8);
+ break;
+
+ case WASM_OPCODE_I32_LOAD8_U:
+ LOAD(I32, U8);
+ break;
+
+ case WASM_OPCODE_I32_LOAD16_S:
+ LOAD(I32, I16);
+ break;
+
+ case WASM_OPCODE_I32_LOAD16_U:
+ LOAD(I32, U16);
+ break;
+
+ case WASM_OPCODE_I64_LOAD8_S:
+ LOAD(I64, I8);
+ break;
+
+ case WASM_OPCODE_I64_LOAD8_U:
+ LOAD(I64, U8);
+ break;
+
+ case WASM_OPCODE_I64_LOAD16_S:
+ LOAD(I64, I16);
+ break;
+
+ case WASM_OPCODE_I64_LOAD16_U:
+ LOAD(I64, U16);
+ break;
+
+ case WASM_OPCODE_I64_LOAD32_S:
+ LOAD(I64, I32);
+ break;
+
+ case WASM_OPCODE_I64_LOAD32_U:
+ LOAD(I64, U32);
+ break;
+
+ case WASM_OPCODE_I32_LOAD:
+ LOAD(I32, U32);
+ break;
+
+ case WASM_OPCODE_I64_LOAD:
+ LOAD(I64, U64);
+ break;
+
+ case WASM_OPCODE_F32_LOAD:
+ LOAD(F32, F32);
+ break;
+
+ case WASM_OPCODE_F64_LOAD:
+ LOAD(F64, F64);
+ break;
+
+ case WASM_OPCODE_I32_STORE8:
+ STORE(I32, U8);
+ break;
+
+ case WASM_OPCODE_I32_STORE16:
+ STORE(I32, U16);
+ break;
+
+ case WASM_OPCODE_I64_STORE8:
+ STORE(I64, U8);
+ break;
+
+ case WASM_OPCODE_I64_STORE16:
+ STORE(I64, U16);
+ break;
+
+ case WASM_OPCODE_I64_STORE32:
+ STORE(I64, U32);
+ break;
+
+ case WASM_OPCODE_I32_STORE:
+ STORE(I32, U32);
+ break;
+
+ case WASM_OPCODE_I64_STORE:
+ STORE(I64, U64);
+ break;
+
+ case WASM_OPCODE_F32_STORE:
+ STORE(F32, F32);
+ break;
+
+ case WASM_OPCODE_F64_STORE:
+ STORE(F64, F64);
+ break;
+
+ case WASM_OPCODE_MEMORY_SIZE:
+ PUSH_TYPE(I32, module->memory.page_size);
+ break;
+
+ case WASM_OPCODE_GROW_MEMORY: {
+ VALUE_TYPE_I32 new_page_size = POP_I32();
+ uint64_t new_byte_size = (uint64_t)new_page_size * WASM_PAGE_SIZE;
+ if (new_byte_size > UINT32_MAX)
+ TRAP(MEMORY_SIZE_OVERFLOW);
+ WasmAllocator* allocator = module->memory.allocator;
+ void* new_data = wasm_realloc(allocator, module->memory.data,
+ new_byte_size, WASM_DEFAULT_ALIGN);
+ if (new_data == NULL)
+ TRAP(OUT_OF_MEMORY);
+ module->memory.data = new_data;
+ module->memory.page_size = new_page_size;
+ module->memory.byte_size = new_byte_size;
+ break;
+ }
+
+ case WASM_OPCODE_I32_ADD:
+ BINOP(I32, +);
+ break;
+
+ case WASM_OPCODE_I32_SUB:
+ BINOP(I32, -);
+ break;
+
+ case WASM_OPCODE_I32_MUL:
+ BINOP(I32, *);
+ break;
+
+ case WASM_OPCODE_I32_DIV_S:
+ BINOP_TRAP_ZERO(I32, /, SIGNED);
+ break;
+
+ case WASM_OPCODE_I32_DIV_U:
+ BINOP_TRAP_ZERO(I32, /, UNSIGNED);
+ break;
+
+ case WASM_OPCODE_I32_REM_S:
+ BINOP_TRAP_ZERO(I32, %, SIGNED);
+ break;
+
+ case WASM_OPCODE_I32_REM_U:
+ BINOP_TRAP_ZERO(I32, %, UNSIGNED);
+ break;
+
+ case WASM_OPCODE_I32_AND:
+ BINOP(I32, &);
+ break;
+
+ case WASM_OPCODE_I32_OR:
+ BINOP(I32, |);
+ break;
+
+ case WASM_OPCODE_I32_XOR:
+ BINOP(I32, ^);
+ break;
+
+ case WASM_OPCODE_I32_SHL:
+ BINOP_SHIFT(I32, <<, UNSIGNED);
+ break;
+
+ case WASM_OPCODE_I32_SHR_U:
+ BINOP_SHIFT(I32, >>, UNSIGNED);
+ break;
+
+ case WASM_OPCODE_I32_SHR_S:
+ BINOP_SHIFT(I32, >>, SIGNED);
+ break;
+
+ case WASM_OPCODE_I32_EQ:
+ BINOP(I32, ==);
+ break;
+
+ case WASM_OPCODE_I32_NE:
+ BINOP(I32, !=);
+ break;
+
+ case WASM_OPCODE_I32_LT_S:
+ BINOP_SIGNED(I32, <);
+ break;
+
+ case WASM_OPCODE_I32_LE_S:
+ BINOP_SIGNED(I32, <=);
+ break;
+
+ case WASM_OPCODE_I32_LT_U:
+ BINOP(I32, <);
+ break;
+
+ case WASM_OPCODE_I32_LE_U:
+ BINOP(I32, <=);
+ break;
+
+ case WASM_OPCODE_I32_GT_S:
+ BINOP_SIGNED(I32, >);
+ break;
+
+ case WASM_OPCODE_I32_GE_S:
+ BINOP_SIGNED(I32, >=);
+ break;
+
+ case WASM_OPCODE_I32_GT_U:
+ BINOP(I32, >);
+ break;
+
+ case WASM_OPCODE_I32_GE_U:
+ BINOP(I32, >=);
+ break;
+
+ case WASM_OPCODE_I32_CLZ: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_I32(value != 0 ? wasm_clz_u32(value) : 32);
+ break;
+ }
+
+ case WASM_OPCODE_I32_CTZ: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_I32(value != 0 ? wasm_ctz_u32(value) : 32);
+ break;
+ }
+
+ case WASM_OPCODE_I32_POPCNT: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_I32(wasm_popcount_u32(value));
+ break;
+ }
+
+ case WASM_OPCODE_I32_EQZ: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_I32(value == 0);
+ break;
+ }
+
+ case WASM_OPCODE_I64_ADD:
+ BINOP(I64, +);
+ break;
+
+ case WASM_OPCODE_I64_SUB:
+ BINOP(I64, -);
+ break;
+
+ case WASM_OPCODE_I64_MUL:
+ BINOP(I64, *);
+ break;
+
+ case WASM_OPCODE_I64_DIV_S:
+ BINOP_TRAP_ZERO(I64, /, SIGNED);
+ break;
+
+ case WASM_OPCODE_I64_DIV_U:
+ BINOP_TRAP_ZERO(I64, /, UNSIGNED);
+ break;
+
+ case WASM_OPCODE_I64_REM_S:
+ BINOP_TRAP_ZERO(I64, %, SIGNED);
+ break;
+
+ case WASM_OPCODE_I64_REM_U:
+ BINOP_TRAP_ZERO(I64, %, UNSIGNED);
+ break;
+
+ case WASM_OPCODE_I64_AND:
+ BINOP(I64, &);
+ break;
+
+ case WASM_OPCODE_I64_OR:
+ BINOP(I64, |);
+ break;
+
+ case WASM_OPCODE_I64_XOR:
+ BINOP(I64, ^);
+ break;
+
+ case WASM_OPCODE_I64_SHL:
+ BINOP_SHIFT(I64, <<, UNSIGNED);
+ break;
+
+ case WASM_OPCODE_I64_SHR_U:
+ BINOP_SHIFT(I64, >>, UNSIGNED);
+ break;
+
+ case WASM_OPCODE_I64_SHR_S:
+ BINOP_SHIFT(I64, >>, SIGNED);
+ break;
+
+ case WASM_OPCODE_I64_EQ:
+ BINOP(I64, ==);
+ break;
+
+ case WASM_OPCODE_I64_NE:
+ BINOP(I64, !=);
+ break;
+
+ case WASM_OPCODE_I64_LT_S:
+ BINOP_SIGNED(I64, <);
+ break;
+
+ case WASM_OPCODE_I64_LE_S:
+ BINOP_SIGNED(I64, <=);
+ break;
+
+ case WASM_OPCODE_I64_LT_U:
+ BINOP(I64, <);
+ break;
+
+ case WASM_OPCODE_I64_LE_U:
+ BINOP(I64, <=);
+ break;
+
+ case WASM_OPCODE_I64_GT_S:
+ BINOP_SIGNED(I64, >);
+ break;
+
+ case WASM_OPCODE_I64_GE_S:
+ BINOP_SIGNED(I64, >=);
+ break;
+
+ case WASM_OPCODE_I64_GT_U:
+ BINOP(I64, >);
+ break;
+
+ case WASM_OPCODE_I64_GE_U:
+ BINOP(I64, >=);
+ break;
+
+ case WASM_OPCODE_I64_CLZ: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_I64(value != 0 ? wasm_clz_u64(value) : 64);
+ break;
+ }
+
+ case WASM_OPCODE_I64_CTZ: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_I64(value != 0 ? wasm_ctz_u64(value) : 64);
+ break;
+ }
+
+ case WASM_OPCODE_I64_POPCNT: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_I64(wasm_popcount_u64(value));
+ break;
+ }
+
+ case WASM_OPCODE_F32_ADD:
+ BINOP_FLOAT(F32, +);
+ break;
+
+ case WASM_OPCODE_F32_SUB:
+ BINOP_FLOAT(F32, -);
+ break;
+
+ case WASM_OPCODE_F32_MUL:
+ BINOP_FLOAT(F32, *);
+ break;
+
+ case WASM_OPCODE_F32_DIV:
+ BINOP_FLOAT(F32, /);
+ break;
+
+ case WASM_OPCODE_F32_MIN:
+ MINMAX_FLOAT(F32, MIN);
+ break;
+
+ case WASM_OPCODE_F32_MAX:
+ MINMAX_FLOAT(F32, MAX);
+ break;
+
+ case WASM_OPCODE_F32_ABS:
+ TOP().f32_bits &= ~F32_SIGN_MASK;
+ break;
+
+ case WASM_OPCODE_F32_NEG:
+ TOP().f32_bits ^= F32_SIGN_MASK;
+ break;
+
+ case WASM_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 WASM_OPCODE_F32_CEIL:
+ UNOP_FLOAT(F32, ceilf);
+ break;
+
+ case WASM_OPCODE_F32_FLOOR:
+ UNOP_FLOAT(F32, floorf);
+ break;
+
+ case WASM_OPCODE_F32_TRUNC:
+ UNOP_FLOAT(F32, truncf);
+ break;
+
+ case WASM_OPCODE_F32_NEAREST:
+ UNOP_FLOAT(F32, nearbyintf);
+ break;
+
+ case WASM_OPCODE_F32_SQRT:
+ UNOP_FLOAT(F32, sqrtf);
+ break;
+
+ case WASM_OPCODE_F32_EQ:
+ BINOP_FLOAT(F32, ==);
+ break;
+
+ case WASM_OPCODE_F32_NE:
+ BINOP_FLOAT(F32, !=);
+ break;
+
+ case WASM_OPCODE_F32_LT:
+ BINOP_FLOAT(F32, <);
+ break;
+
+ case WASM_OPCODE_F32_LE:
+ BINOP_FLOAT(F32, <=);
+ break;
+
+ case WASM_OPCODE_F32_GT:
+ BINOP_FLOAT(F32, >);
+ break;
+
+ case WASM_OPCODE_F32_GE:
+ BINOP_FLOAT(F32, >=);
+ break;
+
+ case WASM_OPCODE_F64_ADD:
+ BINOP_FLOAT(F64, +);
+ break;
+
+ case WASM_OPCODE_F64_SUB:
+ BINOP_FLOAT(F64, -);
+ break;
+
+ case WASM_OPCODE_F64_MUL:
+ BINOP_FLOAT(F64, *);
+ break;
+
+ case WASM_OPCODE_F64_DIV:
+ BINOP_FLOAT(F64, /);
+ break;
+
+ case WASM_OPCODE_F64_MIN:
+ MINMAX_FLOAT(F64, MIN);
+ break;
+
+ case WASM_OPCODE_F64_MAX:
+ MINMAX_FLOAT(F64, MAX);
+ break;
+
+ case WASM_OPCODE_F64_ABS:
+ TOP().f64_bits &= ~F64_SIGN_MASK;
+ break;
+
+ case WASM_OPCODE_F64_NEG:
+ TOP().f64_bits ^= F64_SIGN_MASK;
+ break;
+
+ case WASM_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 WASM_OPCODE_F64_CEIL:
+ UNOP_FLOAT(F64, ceilf);
+ break;
+
+ case WASM_OPCODE_F64_FLOOR:
+ UNOP_FLOAT(F64, floorf);
+ break;
+
+ case WASM_OPCODE_F64_TRUNC:
+ UNOP_FLOAT(F64, truncf);
+ break;
+
+ case WASM_OPCODE_F64_NEAREST:
+ UNOP_FLOAT(F64, nearbyintf);
+ break;
+
+ case WASM_OPCODE_F64_SQRT:
+ UNOP_FLOAT(F64, sqrtf);
+ break;
+
+ case WASM_OPCODE_F64_EQ:
+ BINOP_FLOAT(F64, ==);
+ break;
+
+ case WASM_OPCODE_F64_NE:
+ BINOP_FLOAT(F64, !=);
+ break;
+
+ case WASM_OPCODE_F64_LT:
+ BINOP_FLOAT(F64, <);
+ break;
+
+ case WASM_OPCODE_F64_LE:
+ BINOP_FLOAT(F64, <=);
+ break;
+
+ case WASM_OPCODE_F64_GT:
+ BINOP_FLOAT(F64, >);
+ break;
+
+ case WASM_OPCODE_F64_GE:
+ BINOP_FLOAT(F64, >=);
+ break;
+
+ case WASM_OPCODE_I32_TRUNC_S_F32: {
+ VALUE_TYPE_F32 value = POP_F32();
+ if (is_nan_f32(value))
+ TRAP(INVALID_CONVERSION_TO_INTEGER);
+ int exp = get_exp_f32(value);
+ if (exp > 31)
+ TRAP(INTEGER_OVERFLOW);
+ PUSH_I32((int32_t)BITCAST_TO_F32(value));
+ break;
+ }
+
+ case WASM_OPCODE_I32_TRUNC_S_F64: {
+ VALUE_TYPE_F64 value = POP_F64();
+ if (is_nan_f64(value))
+ TRAP(INVALID_CONVERSION_TO_INTEGER);
+ int exp = get_exp_f64(value);
+ if (exp >= 31)
+ TRAP(INTEGER_OVERFLOW);
+ PUSH_I32((int32_t)BITCAST_TO_F64(value));
+ break;
+ }
+
+ case WASM_OPCODE_I32_TRUNC_U_F32: {
+ VALUE_TYPE_F32 value = POP_F32();
+ if (is_nan_f32(value))
+ TRAP(INVALID_CONVERSION_TO_INTEGER);
+ int exp = get_exp_f32(value);
+ if (is_signed_f32(value) || exp >= 32)
+ TRAP(INTEGER_OVERFLOW);
+ PUSH_I32((uint32_t)BITCAST_TO_F32(value));
+ break;
+ }
+
+ case WASM_OPCODE_I32_TRUNC_U_F64: {
+ VALUE_TYPE_F64 value = POP_F64();
+ if (is_nan_f64(value))
+ TRAP(INVALID_CONVERSION_TO_INTEGER);
+ int exp = get_exp_f64(value);
+ if (is_signed_f64(value) || exp >= 32)
+ TRAP(INTEGER_OVERFLOW);
+ PUSH_I32((uint32_t)BITCAST_TO_F64(value));
+ break;
+ }
+
+ case WASM_OPCODE_I32_WRAP_I64: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_I32((uint32_t)value);
+ break;
+ }
+
+ case WASM_OPCODE_I64_TRUNC_S_F32: {
+ VALUE_TYPE_F32 value = POP_F32();
+ if (is_nan_f32(value))
+ TRAP(INVALID_CONVERSION_TO_INTEGER);
+ int exp = get_exp_f32(value);
+ if (exp >= 63)
+ TRAP(INTEGER_OVERFLOW);
+ PUSH_I64((int64_t)BITCAST_TO_F32(value));
+ break;
+ }
+
+ case WASM_OPCODE_I64_TRUNC_S_F64: {
+ VALUE_TYPE_F64 value = POP_F64();
+ if (is_nan_f64(value))
+ TRAP(INVALID_CONVERSION_TO_INTEGER);
+ int exp = get_exp_f64(value);
+ if (exp >= 63)
+ TRAP(INTEGER_OVERFLOW);
+ PUSH_I64((int64_t)BITCAST_TO_F64(value));
+ break;
+ }
+
+ case WASM_OPCODE_I64_TRUNC_U_F32: {
+ VALUE_TYPE_F32 value = POP_F32();
+ if (is_nan_f32(value))
+ TRAP(INVALID_CONVERSION_TO_INTEGER);
+ int exp = get_exp_f32(value);
+ if (is_signed_f64(value) || exp >= 64)
+ TRAP(INTEGER_OVERFLOW);
+ PUSH_I64((uint64_t)BITCAST_TO_F32(value));
+ break;
+ }
+
+ case WASM_OPCODE_I64_TRUNC_U_F64: {
+ VALUE_TYPE_F64 value = POP_F64();
+ if (is_nan_f64(value))
+ TRAP(INVALID_CONVERSION_TO_INTEGER);
+ int exp = get_exp_f64(value);
+ if (is_signed_f64(value) || exp >= 64)
+ TRAP(INTEGER_OVERFLOW);
+ PUSH_I64((uint64_t)BITCAST_TO_F64(value));
+ break;
+ }
+
+ case WASM_OPCODE_I64_EXTEND_S_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_I64((int64_t)BITCAST_I32_TO_SIGNED(value));
+ break;
+ }
+
+ case WASM_OPCODE_I64_EXTEND_U_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_I64((uint64_t)value);
+ break;
+ }
+
+ case WASM_OPCODE_F32_CONVERT_S_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_F32(BITCAST_FROM_F32((float)BITCAST_I32_TO_SIGNED(value)));
+ break;
+ }
+
+ case WASM_OPCODE_F32_CONVERT_U_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_F32(BITCAST_FROM_F32((float)value));
+ break;
+ }
+
+ case WASM_OPCODE_F32_CONVERT_S_I64: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_F32(BITCAST_FROM_F32((float)BITCAST_I64_TO_SIGNED(value)));
+ break;
+ }
+
+ case WASM_OPCODE_F32_CONVERT_U_I64: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_F32(BITCAST_FROM_F32((float)value));
+ break;
+ }
+
+ case WASM_OPCODE_F32_DEMOTE_F64: {
+ VALUE_TYPE_F64 value = POP_F64();
+ PUSH_F32(BITCAST_FROM_F32((float)BITCAST_TO_F64(value)));
+ break;
+ }
+
+ case WASM_OPCODE_F32_REINTERPRET_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_F32(value);
+ break;
+ }
+
+ case WASM_OPCODE_F64_CONVERT_S_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_F64(BITCAST_FROM_F64((float)BITCAST_I32_TO_SIGNED(value)));
+ break;
+ }
+
+ case WASM_OPCODE_F64_CONVERT_U_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_F64(BITCAST_FROM_F64((float)value));
+ break;
+ }
+
+ case WASM_OPCODE_F64_CONVERT_S_I64: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_F64(BITCAST_FROM_F64((float)BITCAST_I64_TO_SIGNED(value)));
+ break;
+ }
+
+ case WASM_OPCODE_F64_CONVERT_U_I64: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_F64(BITCAST_FROM_F64((float)value));
+ break;
+ }
+
+ case WASM_OPCODE_F64_PROMOTE_F32: {
+ VALUE_TYPE_F32 value = POP_F32();
+ PUSH_F64(BITCAST_FROM_F64((double)BITCAST_TO_F32(value)));
+ break;
+ }
+
+ case WASM_OPCODE_F64_REINTERPRET_I64: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_F64(value);
+ break;
+ }
+
+ case WASM_OPCODE_I32_REINTERPRET_F32: {
+ VALUE_TYPE_F32 value = POP_F32();
+ PUSH_I32(value);
+ break;
+ }
+
+ case WASM_OPCODE_I64_REINTERPRET_F64: {
+ VALUE_TYPE_F64 value = POP_F64();
+ PUSH_I64(value);
+ break;
+ }
+
+ case WASM_OPCODE_I32_ROTR:
+ BINOP_ROT(I32, RIGHT);
+ break;
+
+ case WASM_OPCODE_I32_ROTL:
+ BINOP_ROT(I32, LEFT);
+ break;
+
+ case WASM_OPCODE_I64_ROTR:
+ BINOP_ROT(I64, RIGHT);
+ break;
+
+ case WASM_OPCODE_I64_ROTL:
+ BINOP_ROT(I64, LEFT);
+ break;
+
+ case WASM_OPCODE_I64_EQZ: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_I64(value == 0);
+ break;
+ }
+
+ case WASM_OPCODE_ALLOCA: {
+ WasmInterpreterValue* old_vs_top = vs_top;
+ vs_top += read_u32(&pc);
+ CHECK_STACK();
+ memset(old_vs_top, 0, vs_top - old_vs_top);
+ break;
+ }
+
+ case WASM_OPCODE_DISCARD:
+ POP();
+ break;
+
+ case WASM_OPCODE_DISCARD_KEEP: {
+ uint32_t discard_count = read_u32(&pc);
+ uint8_t keep_count = *pc++;
+ assert(keep_count <= 1);
+ if (keep_count == 1)
+ STACK_VALUE(discard_count + 1) = TOP();
+ vs_top -= discard_count;
+ break;
+ }
+
+ default:
+ assert(0);
+ break;
+ }
+ }
+
+exit_loop:
+ thread->value_stack_top = vs_top - vs_bottom;
+ thread->call_stack_top = cs_top - cs_bottom;
+ thread->pc = pc - istream;
+ return result;
+}
+
+#if 0
+void wasm_trace_pc(WasmInterpreterModule* module,
+ WasmInterpreterThread* thread) {
+ const uint8_t* istream = module->istream.start;
+ const uint8_t* pc = &istream[thread->pc];
+ uint8_t opcode = *pc++;
+ switch (opcode) {
+ case WASM_OPCODE_SELECT:
+ printf("select %u, %" PRIu64 ", %" PRIu64 "\n", STACK_VALUE(3).u32,
+ STACK_VALUE(2).u64, STACK_VALUE(1).u64);
+ break;
+
+ case WASM_OPCODE_BR:
+ printf("br @%u\n", read_u32_at(pc));
+ break;
+
+ case WASM_OPCODE_BR_IF:
+ printf("br_if @%u, %u\n", read_u32_at(pc), STACK_VALUE(1).u32);
+ break;
+
+ case WASM_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_offset =
+ (key >= num_targets ? num_targets : key) * sizeof(uint32_t);
+ uint32_t new_pc = read_u32_at(istream + table_offset + key_offset);
+ GOTO(new_pc);
+ break;
+ }
+
+ case WASM_OPCODE_RETURN:
+ if (cs_top > 0) {
+ result = WASM_INTERPRETER_RETURNED;
+ goto exit_loop;
+ }
+ GOTO(POP_CALL());
+ break;
+
+ case WASM_OPCODE_UNREACHABLE:
+ TRAP(UNREACHABLE);
+ break;
+
+ case WASM_OPCODE_I8_CONST:
+ break;
+
+ case WASM_OPCODE_I32_CONST:
+ PUSH_TYPE(I32, read_u32(&pc));
+ break;
+
+ case WASM_OPCODE_I64_CONST:
+ PUSH_TYPE(I64, read_u64(&pc));
+ break;
+
+ case WASM_OPCODE_F32_CONST:
+ PUSH_TYPE(F32, read_u32(&pc));
+ break;
+
+ case WASM_OPCODE_F64_CONST:
+ PUSH_TYPE(F64, read_u64(&pc));
+ break;
+
+ case WASM_OPCODE_GET_LOCAL: {
+ WasmInterpreterValue value = STACK_VALUE(read_u32(&pc));
+ PUSH(value);
+ break;
+ }
+
+ case WASM_OPCODE_SET_LOCAL:
+ STACK_VALUE(read_u32(&pc)) = TOP();
+ break;
+
+ case WASM_OPCODE_CALL_FUNCTION: {
+ uint32_t offset = read_u32(&pc);
+ PUSH_CALL(offset);
+ GOTO(offset);
+ break;
+ }
+
+ case WASM_OPCODE_CALL_INDIRECT: {
+ uint32_t sig_index = read_u32(&pc);
+ VALUE_TYPE_I32 entry_index = POP_I32();
+ if (entry_index >= module->func_table.size)
+ TRAP(UNDEFINED_TABLE_INDEX);
+ WasmInterpreterFuncTableEntry* entry =
+ &module->func_table.data[entry_index];
+ if (entry->sig_index != sig_index)
+ TRAP(INDIRECT_CALL_SIGNATURE_MISMATCH);
+ PUSH_CALL(entry->func_offset);
+ GOTO(entry->func_offset);
+ break;
+ }
+
+ case WASM_OPCODE_CALL_IMPORT: {
+ uint32_t import_index = read_u32(&pc);
+ assert(import_index < module->imports.size);
+ WasmInterpreterImport* import = &module->imports.data[import_index];
+ uint32_t sig_index = import->sig_index;
+ assert(sig_index < module->sigs.size);
+ WasmInterpreterFuncSignature* sig = &module->sigs.data[sig_index];
+ uint32_t num_args = sig->param_types.size;
+ uint32_t i;
+ assert(num_args <= thread->import_args.size);
+ for (i = num_args; i > 0; --i) {
+ WasmInterpreterValue value = POP();
+ WasmInterpreterTypedValue* arg = &thread->import_args.data[i - 1];
+ arg->type = sig->param_types.data[i - 1];
+ arg->value = value;
+ }
+ assert(import->callback);
+ WasmInterpreterTypedValue call_result =
+ import->callback(module, import, num_args, thread->import_args.data,
+ import->user_data);
+ if (sig->result_type != WASM_TYPE_VOID) {
+ if (call_result.type != sig->result_type)
+ TRAP(IMPORT_RESULT_TYPE_MISMATCH);
+ PUSH(call_result.value);
+ }
+ break;
+ }
+
+ case WASM_OPCODE_I32_LOAD8_S:
+ LOAD(I32, I8);
+ break;
+
+ case WASM_OPCODE_I32_LOAD8_U:
+ LOAD(I32, U8);
+ break;
+
+ case WASM_OPCODE_I32_LOAD16_S:
+ LOAD(I32, I16);
+ break;
+
+ case WASM_OPCODE_I32_LOAD16_U:
+ LOAD(I32, U16);
+ break;
+
+ case WASM_OPCODE_I64_LOAD8_S:
+ LOAD(I64, I8);
+ break;
+
+ case WASM_OPCODE_I64_LOAD8_U:
+ LOAD(I64, U8);
+ break;
+
+ case WASM_OPCODE_I64_LOAD16_S:
+ LOAD(I64, I16);
+ break;
+
+ case WASM_OPCODE_I64_LOAD16_U:
+ LOAD(I64, U16);
+ break;
+
+ case WASM_OPCODE_I64_LOAD32_S:
+ LOAD(I64, I32);
+ break;
+
+ case WASM_OPCODE_I64_LOAD32_U:
+ LOAD(I64, U32);
+ break;
+
+ case WASM_OPCODE_I32_LOAD:
+ LOAD(I32, U32);
+ break;
+
+ case WASM_OPCODE_I64_LOAD:
+ LOAD(I64, U64);
+ break;
+
+ case WASM_OPCODE_F32_LOAD:
+ LOAD(F32, F32);
+ break;
+
+ case WASM_OPCODE_F64_LOAD:
+ LOAD(F64, F64);
+ break;
+
+ case WASM_OPCODE_I32_STORE8:
+ STORE(I32, U8);
+ break;
+
+ case WASM_OPCODE_I32_STORE16:
+ STORE(I32, U16);
+ break;
+
+ case WASM_OPCODE_I64_STORE8:
+ STORE(I64, U8);
+ break;
+
+ case WASM_OPCODE_I64_STORE16:
+ STORE(I64, U16);
+ break;
+
+ case WASM_OPCODE_I64_STORE32:
+ STORE(I64, U32);
+ break;
+
+ case WASM_OPCODE_I32_STORE:
+ STORE(I32, U32);
+ break;
+
+ case WASM_OPCODE_I64_STORE:
+ STORE(I64, U64);
+ break;
+
+ case WASM_OPCODE_F32_STORE:
+ STORE(F32, F32);
+ break;
+
+ case WASM_OPCODE_F64_STORE:
+ STORE(F64, F64);
+ break;
+
+ case WASM_OPCODE_MEMORY_SIZE:
+ PUSH_TYPE(I32, module->memory.page_size);
+ break;
+
+ case WASM_OPCODE_GROW_MEMORY: {
+ VALUE_TYPE_I32 new_page_size = POP_I32();
+ uint64_t new_byte_size = (uint64_t)new_page_size * WASM_PAGE_SIZE;
+ if (new_byte_size > UINT32_MAX)
+ TRAP(MEMORY_SIZE_OVERFLOW);
+ WasmAllocator* allocator = module->memory.allocator;
+ void* new_data = wasm_realloc(allocator, module->memory.data,
+ new_byte_size, WASM_DEFAULT_ALIGN);
+ if (new_data == NULL)
+ TRAP(OUT_OF_MEMORY);
+ module->memory.data = new_data;
+ module->memory.page_size = new_page_size;
+ module->memory.byte_size = new_byte_size;
+ break;
+ }
+
+ case WASM_OPCODE_I32_ADD:
+ BINOP(I32, +);
+ break;
+
+ case WASM_OPCODE_I32_SUB:
+ BINOP(I32, -);
+ break;
+
+ case WASM_OPCODE_I32_MUL:
+ BINOP(I32, *);
+ break;
+
+ case WASM_OPCODE_I32_DIV_S:
+ BINOP_TRAP_ZERO(I32, /, SIGNED);
+ break;
+
+ case WASM_OPCODE_I32_DIV_U:
+ BINOP_TRAP_ZERO(I32, /, UNSIGNED);
+ break;
+
+ case WASM_OPCODE_I32_REM_S:
+ BINOP_TRAP_ZERO(I32, %, SIGNED);
+ break;
+
+ case WASM_OPCODE_I32_REM_U:
+ BINOP_TRAP_ZERO(I32, %, UNSIGNED);
+ break;
+
+ case WASM_OPCODE_I32_AND:
+ BINOP(I32, &);
+ break;
+
+ case WASM_OPCODE_I32_OR:
+ BINOP(I32, |);
+ break;
+
+ case WASM_OPCODE_I32_XOR:
+ BINOP(I32, ^);
+ break;
+
+ case WASM_OPCODE_I32_SHL:
+ BINOP_SHIFT(I32, <<, UNSIGNED);
+ break;
+
+ case WASM_OPCODE_I32_SHR_U:
+ BINOP_SHIFT(I32, >>, UNSIGNED);
+ break;
+
+ case WASM_OPCODE_I32_SHR_S:
+ BINOP_SHIFT(I32, >>, SIGNED);
+ break;
+
+ case WASM_OPCODE_I32_EQ:
+ BINOP(I32, ==);
+ break;
+
+ case WASM_OPCODE_I32_NE:
+ BINOP(I32, !=);
+ break;
+
+ case WASM_OPCODE_I32_LT_S:
+ BINOP_SIGNED(I32, <);
+ break;
+
+ case WASM_OPCODE_I32_LE_S:
+ BINOP_SIGNED(I32, <=);
+ break;
+
+ case WASM_OPCODE_I32_LT_U:
+ BINOP(I32, <);
+ break;
+
+ case WASM_OPCODE_I32_LE_U:
+ BINOP(I32, <=);
+ break;
+
+ case WASM_OPCODE_I32_GT_S:
+ BINOP_SIGNED(I32, >);
+ break;
+
+ case WASM_OPCODE_I32_GE_S:
+ BINOP_SIGNED(I32, >=);
+ break;
+
+ case WASM_OPCODE_I32_GT_U:
+ BINOP(I32, >);
+ break;
+
+ case WASM_OPCODE_I32_GE_U:
+ BINOP(I32, >=);
+ break;
+
+ case WASM_OPCODE_I32_CLZ: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_I32(value != 0 ? wasm_clz_u32(value) : 32);
+ break;
+ }
+
+ case WASM_OPCODE_I32_CTZ: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_I32(value != 0 ? wasm_ctz_u32(value) : 32);
+ break;
+ }
+
+ case WASM_OPCODE_I32_POPCNT: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_I32(wasm_popcount_u32(value));
+ break;
+ }
+
+ case WASM_OPCODE_I32_EQZ: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_I32(value == 0);
+ break;
+ }
+
+ case WASM_OPCODE_I64_ADD:
+ BINOP(I64, +);
+ break;
+
+ case WASM_OPCODE_I64_SUB:
+ BINOP(I64, -);
+ break;
+
+ case WASM_OPCODE_I64_MUL:
+ BINOP(I64, *);
+ break;
+
+ case WASM_OPCODE_I64_DIV_S:
+ BINOP_TRAP_ZERO(I64, /, SIGNED);
+ break;
+
+ case WASM_OPCODE_I64_DIV_U:
+ BINOP_TRAP_ZERO(I64, /, UNSIGNED);
+ break;
+
+ case WASM_OPCODE_I64_REM_S:
+ BINOP_TRAP_ZERO(I64, %, SIGNED);
+ break;
+
+ case WASM_OPCODE_I64_REM_U:
+ BINOP_TRAP_ZERO(I64, %, UNSIGNED);
+ break;
+
+ case WASM_OPCODE_I64_AND:
+ BINOP(I64, &);
+ break;
+
+ case WASM_OPCODE_I64_OR:
+ BINOP(I64, |);
+ break;
+
+ case WASM_OPCODE_I64_XOR:
+ BINOP(I64, ^);
+ break;
+
+ case WASM_OPCODE_I64_SHL:
+ BINOP_SHIFT(I64, <<, UNSIGNED);
+ break;
+
+ case WASM_OPCODE_I64_SHR_U:
+ BINOP_SHIFT(I64, >>, UNSIGNED);
+ break;
+
+ case WASM_OPCODE_I64_SHR_S:
+ BINOP_SHIFT(I64, >>, SIGNED);
+ break;
+
+ case WASM_OPCODE_I64_EQ:
+ BINOP(I64, ==);
+ break;
+
+ case WASM_OPCODE_I64_NE:
+ BINOP(I64, !=);
+ break;
+
+ case WASM_OPCODE_I64_LT_S:
+ BINOP_SIGNED(I64, <);
+ break;
+
+ case WASM_OPCODE_I64_LE_S:
+ BINOP_SIGNED(I64, <=);
+ break;
+
+ case WASM_OPCODE_I64_LT_U:
+ BINOP(I64, <);
+ break;
+
+ case WASM_OPCODE_I64_LE_U:
+ BINOP(I64, <=);
+ break;
+
+ case WASM_OPCODE_I64_GT_S:
+ BINOP_SIGNED(I64, >);
+ break;
+
+ case WASM_OPCODE_I64_GE_S:
+ BINOP_SIGNED(I64, >=);
+ break;
+
+ case WASM_OPCODE_I64_GT_U:
+ BINOP(I64, >);
+ break;
+
+ case WASM_OPCODE_I64_GE_U:
+ BINOP(I64, >=);
+ break;
+
+ case WASM_OPCODE_I64_CLZ: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_I64(value != 0 ? wasm_clz_u64(value) : 64);
+ break;
+ }
+
+ case WASM_OPCODE_I64_CTZ: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_I64(value != 0 ? wasm_ctz_u64(value) : 64);
+ break;
+ }
+
+ case WASM_OPCODE_I64_POPCNT: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_I64(wasm_popcount_u64(value));
+ break;
+ }
+
+ case WASM_OPCODE_F32_ADD:
+ BINOP_FLOAT(F32, +);
+ break;
+
+ case WASM_OPCODE_F32_SUB:
+ BINOP_FLOAT(F32, -);
+ break;
+
+ case WASM_OPCODE_F32_MUL:
+ BINOP_FLOAT(F32, *);
+ break;
+
+ case WASM_OPCODE_F32_DIV:
+ BINOP_FLOAT(F32, /);
+ break;
+
+ case WASM_OPCODE_F32_MIN:
+ MINMAX_FLOAT(F32, MIN);
+ break;
+
+ case WASM_OPCODE_F32_MAX:
+ MINMAX_FLOAT(F32, MAX);
+ break;
+
+ case WASM_OPCODE_F32_ABS:
+ TOP().f32_bits &= ~F32_SIGN_MASK;
+ break;
+
+ case WASM_OPCODE_F32_NEG:
+ TOP().f32_bits ^= F32_SIGN_MASK;
+ break;
+
+ case WASM_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 WASM_OPCODE_F32_CEIL:
+ UNOP_FLOAT(F32, ceilf);
+ break;
+
+ case WASM_OPCODE_F32_FLOOR:
+ UNOP_FLOAT(F32, floorf);
+ break;
+
+ case WASM_OPCODE_F32_TRUNC:
+ UNOP_FLOAT(F32, truncf);
+ break;
+
+ case WASM_OPCODE_F32_NEAREST:
+ UNOP_FLOAT(F32, nearbyintf);
+ break;
+
+ case WASM_OPCODE_F32_SQRT:
+ UNOP_FLOAT(F32, sqrtf);
+ break;
+
+ case WASM_OPCODE_F32_EQ:
+ BINOP_FLOAT(F32, ==);
+ break;
+
+ case WASM_OPCODE_F32_NE:
+ BINOP_FLOAT(F32, !=);
+ break;
+
+ case WASM_OPCODE_F32_LT:
+ BINOP_FLOAT(F32, <);
+ break;
+
+ case WASM_OPCODE_F32_LE:
+ BINOP_FLOAT(F32, <=);
+ break;
+
+ case WASM_OPCODE_F32_GT:
+ BINOP_FLOAT(F32, >);
+ break;
+
+ case WASM_OPCODE_F32_GE:
+ BINOP_FLOAT(F32, >=);
+ break;
+
+ case WASM_OPCODE_F64_ADD:
+ BINOP_FLOAT(F64, +);
+ break;
+
+ case WASM_OPCODE_F64_SUB:
+ BINOP_FLOAT(F64, -);
+ break;
+
+ case WASM_OPCODE_F64_MUL:
+ BINOP_FLOAT(F64, *);
+ break;
+
+ case WASM_OPCODE_F64_DIV:
+ BINOP_FLOAT(F64, /);
+ break;
+
+ case WASM_OPCODE_F64_MIN:
+ MINMAX_FLOAT(F64, MIN);
+ break;
+
+ case WASM_OPCODE_F64_MAX:
+ MINMAX_FLOAT(F64, MAX);
+ break;
+
+ case WASM_OPCODE_F64_ABS:
+ TOP().f64_bits &= ~F64_SIGN_MASK;
+ break;
+
+ case WASM_OPCODE_F64_NEG:
+ TOP().f64_bits ^= F64_SIGN_MASK;
+ break;
+
+ case WASM_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 WASM_OPCODE_F64_CEIL:
+ UNOP_FLOAT(F64, ceilf);
+ break;
+
+ case WASM_OPCODE_F64_FLOOR:
+ UNOP_FLOAT(F64, floorf);
+ break;
+
+ case WASM_OPCODE_F64_TRUNC:
+ UNOP_FLOAT(F64, truncf);
+ break;
+
+ case WASM_OPCODE_F64_NEAREST:
+ UNOP_FLOAT(F64, nearbyintf);
+ break;
+
+ case WASM_OPCODE_F64_SQRT:
+ UNOP_FLOAT(F64, sqrtf);
+ break;
+
+ case WASM_OPCODE_F64_EQ:
+ BINOP_FLOAT(F64, ==);
+ break;
+
+ case WASM_OPCODE_F64_NE:
+ BINOP_FLOAT(F64, !=);
+ break;
+
+ case WASM_OPCODE_F64_LT:
+ BINOP_FLOAT(F64, <);
+ break;
+
+ case WASM_OPCODE_F64_LE:
+ BINOP_FLOAT(F64, <=);
+ break;
+
+ case WASM_OPCODE_F64_GT:
+ BINOP_FLOAT(F64, >);
+ break;
+
+ case WASM_OPCODE_F64_GE:
+ BINOP_FLOAT(F64, >=);
+ break;
+
+ case WASM_OPCODE_I32_TRUNC_S_F32: {
+ VALUE_TYPE_F32 value = POP_F32();
+ if (is_nan_f32(value))
+ TRAP(INVALID_CONVERSION_TO_INTEGER);
+ int exp = get_exp_f32(value);
+ if (exp > 31)
+ TRAP(INTEGER_OVERFLOW);
+ PUSH_I32((int32_t)BITCAST_TO_F32(value));
+ break;
+ }
+
+ case WASM_OPCODE_I32_TRUNC_S_F64: {
+ VALUE_TYPE_F64 value = POP_F64();
+ if (is_nan_f64(value))
+ TRAP(INVALID_CONVERSION_TO_INTEGER);
+ int exp = get_exp_f64(value);
+ if (exp >= 31)
+ TRAP(INTEGER_OVERFLOW);
+ PUSH_I32((int32_t)BITCAST_TO_F64(value));
+ break;
+ }
+
+ case WASM_OPCODE_I32_TRUNC_U_F32: {
+ VALUE_TYPE_F32 value = POP_F32();
+ if (is_nan_f32(value))
+ TRAP(INVALID_CONVERSION_TO_INTEGER);
+ int exp = get_exp_f32(value);
+ if (is_signed_f32(value) || exp >= 32)
+ TRAP(INTEGER_OVERFLOW);
+ PUSH_I32((uint32_t)BITCAST_TO_F32(value));
+ break;
+ }
+
+ case WASM_OPCODE_I32_TRUNC_U_F64: {
+ VALUE_TYPE_F64 value = POP_F64();
+ if (is_nan_f64(value))
+ TRAP(INVALID_CONVERSION_TO_INTEGER);
+ int exp = get_exp_f64(value);
+ if (is_signed_f64(value) || exp >= 32)
+ TRAP(INTEGER_OVERFLOW);
+ PUSH_I32((uint32_t)BITCAST_TO_F64(value));
+ break;
+ }
+
+ case WASM_OPCODE_I32_WRAP_I64: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_I32((uint32_t)value);
+ break;
+ }
+
+ case WASM_OPCODE_I64_TRUNC_S_F32: {
+ VALUE_TYPE_F32 value = POP_F32();
+ if (is_nan_f32(value))
+ TRAP(INVALID_CONVERSION_TO_INTEGER);
+ int exp = get_exp_f32(value);
+ if (exp >= 63)
+ TRAP(INTEGER_OVERFLOW);
+ PUSH_I64((int64_t)BITCAST_TO_F32(value));
+ break;
+ }
+
+ case WASM_OPCODE_I64_TRUNC_S_F64: {
+ VALUE_TYPE_F64 value = POP_F64();
+ if (is_nan_f64(value))
+ TRAP(INVALID_CONVERSION_TO_INTEGER);
+ int exp = get_exp_f64(value);
+ if (exp >= 63)
+ TRAP(INTEGER_OVERFLOW);
+ PUSH_I64((int64_t)BITCAST_TO_F64(value));
+ break;
+ }
+
+ case WASM_OPCODE_I64_TRUNC_U_F32: {
+ VALUE_TYPE_F32 value = POP_F32();
+ if (is_nan_f32(value))
+ TRAP(INVALID_CONVERSION_TO_INTEGER);
+ int exp = get_exp_f32(value);
+ if (is_signed_f64(value) || exp >= 64)
+ TRAP(INTEGER_OVERFLOW);
+ PUSH_I64((uint64_t)BITCAST_TO_F32(value));
+ break;
+ }
+
+ case WASM_OPCODE_I64_TRUNC_U_F64: {
+ VALUE_TYPE_F64 value = POP_F64();
+ if (is_nan_f64(value))
+ TRAP(INVALID_CONVERSION_TO_INTEGER);
+ int exp = get_exp_f64(value);
+ if (is_signed_f64(value) || exp >= 64)
+ TRAP(INTEGER_OVERFLOW);
+ PUSH_I64((uint64_t)BITCAST_TO_F64(value));
+ break;
+ }
+
+ case WASM_OPCODE_I64_EXTEND_S_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_I64((int64_t)BITCAST_I32_TO_SIGNED(value));
+ break;
+ }
+
+ case WASM_OPCODE_I64_EXTEND_U_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_I64((uint64_t)value);
+ break;
+ }
+
+ case WASM_OPCODE_F32_CONVERT_S_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_F32(BITCAST_FROM_F32((float)BITCAST_I32_TO_SIGNED(value)));
+ break;
+ }
+
+ case WASM_OPCODE_F32_CONVERT_U_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_F32(BITCAST_FROM_F32((float)value));
+ break;
+ }
+
+ case WASM_OPCODE_F32_CONVERT_S_I64: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_F32(BITCAST_FROM_F32((float)BITCAST_I64_TO_SIGNED(value)));
+ break;
+ }
+
+ case WASM_OPCODE_F32_CONVERT_U_I64: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_F32(BITCAST_FROM_F32((float)value));
+ break;
+ }
+
+ case WASM_OPCODE_F32_DEMOTE_F64: {
+ VALUE_TYPE_F64 value = POP_F64();
+ PUSH_F32(BITCAST_FROM_F32((float)BITCAST_TO_F64(value)));
+ break;
+ }
+
+ case WASM_OPCODE_F32_REINTERPRET_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_F32(value);
+ break;
+ }
+
+ case WASM_OPCODE_F64_CONVERT_S_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_F64(BITCAST_FROM_F64((float)BITCAST_I32_TO_SIGNED(value)));
+ break;
+ }
+
+ case WASM_OPCODE_F64_CONVERT_U_I32: {
+ VALUE_TYPE_I32 value = POP_I32();
+ PUSH_F64(BITCAST_FROM_F64((float)value));
+ break;
+ }
+
+ case WASM_OPCODE_F64_CONVERT_S_I64: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_F64(BITCAST_FROM_F64((float)BITCAST_I64_TO_SIGNED(value)));
+ break;
+ }
+
+ case WASM_OPCODE_F64_CONVERT_U_I64: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_F64(BITCAST_FROM_F64((float)value));
+ break;
+ }
+
+ case WASM_OPCODE_F64_PROMOTE_F32: {
+ VALUE_TYPE_F32 value = POP_F32();
+ PUSH_F64(BITCAST_FROM_F64((double)BITCAST_TO_F32(value)));
+ break;
+ }
+
+ case WASM_OPCODE_F64_REINTERPRET_I64: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_F64(value);
+ break;
+ }
+
+ case WASM_OPCODE_I32_REINTERPRET_F32: {
+ VALUE_TYPE_F32 value = POP_F32();
+ PUSH_I32(value);
+ break;
+ }
+
+ case WASM_OPCODE_I64_REINTERPRET_F64: {
+ VALUE_TYPE_F64 value = POP_F64();
+ PUSH_I64(value);
+ break;
+ }
+
+ case WASM_OPCODE_I32_ROTR:
+ BINOP_ROT(I32, RIGHT);
+ break;
+
+ case WASM_OPCODE_I32_ROTL:
+ BINOP_ROT(I32, LEFT);
+ break;
+
+ case WASM_OPCODE_I64_ROTR:
+ BINOP_ROT(I64, RIGHT);
+ break;
+
+ case WASM_OPCODE_I64_ROTL:
+ BINOP_ROT(I64, LEFT);
+ break;
+
+ case WASM_OPCODE_I64_EQZ: {
+ VALUE_TYPE_I64 value = POP_I64();
+ PUSH_I64(value == 0);
+ break;
+ }
+
+ case WASM_OPCODE_ALLOCA: {
+ WasmInterpreterValue* old_vs_top = vs_top;
+ vs_top += read_u32(&pc);
+ CHECK_STACK();
+ memset(old_vs_top, 0, vs_top - old_vs_top);
+ break;
+ }
+
+ case WASM_OPCODE_DISCARD:
+ POP();
+ break;
+
+ case WASM_OPCODE_DISCARD_KEEP: {
+ uint32_t discard_count = read_u32(&pc);
+ uint8_t keep_count = *pc++;
+ assert(keep_count <= 1);
+ if (keep_count == 1)
+ STACK_VALUE(discard_count + 1) = TOP();
+ vs_top -= discard_count;
+ break;
+ }
+
+ default:
+ assert(0);
+ break;
+ }
+}
+#endif