commit 4141ece5135aed61fe3674533de390d8202a8340
parent 167d1a17f58b9fcca5a88d77d253903e0babc931
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 19 May 2026 15:59:54 -0700
lang/wasm/ refactor into multiple files
Diffstat:
| A | lang/wasm/cg.c | | | 2048 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | lang/wasm/decode.c | | | 1195 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | lang/wasm/encode.c | | | 778 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | lang/wasm/insn.c | | | 409 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | lang/wasm/module.c | | | 328 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | lang/wasm/validate.c | | | 665 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | lang/wasm/wasm.c | | | 8307 | +------------------------------------------------------------------------------ |
| A | lang/wasm/wasm_internal.h | | | 416 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | lang/wasm/wat.c | | | 2548 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
9 files changed, 8390 insertions(+), 8304 deletions(-)
diff --git a/lang/wasm/cg.c b/lang/wasm/cg.c
@@ -0,0 +1,2048 @@
+#include "wasm_internal.h"
+
+static CfreeCgTypeId wasm_cg_type(CfreeCompiler* c, CfreeCgBuiltinTypes b,
+ WasmValType vt) {
+ switch (vt) {
+ case WASM_VAL_I32:
+ return b.id[CFREE_CG_BUILTIN_I32];
+ case WASM_VAL_I64:
+ return b.id[CFREE_CG_BUILTIN_I64];
+ case WASM_VAL_F32:
+ return b.id[CFREE_CG_BUILTIN_F32];
+ case WASM_VAL_F64:
+ return b.id[CFREE_CG_BUILTIN_F64];
+ case WASM_VAL_FUNCREF:
+ case WASM_VAL_EXTERNREF:
+ return cfree_cg_type_ptr(c, b.id[CFREE_CG_BUILTIN_VOID], 0);
+ }
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported value type");
+ return b.id[CFREE_CG_BUILTIN_I32];
+}
+
+
+static CfreeCgMemAccess wasm_cg_mem(CfreeCompiler* c, CfreeCgBuiltinTypes b,
+ WasmValType vt) {
+ CfreeCgMemAccess mem;
+ memset(&mem, 0, sizeof mem);
+ mem.type = wasm_cg_type(c, b, vt);
+ return mem;
+}
+
+static CfreeCgMemAccess wasm_cg_mem_type(CfreeCgTypeId ty) {
+ CfreeCgMemAccess mem;
+ memset(&mem, 0, sizeof mem);
+ mem.type = ty;
+ return mem;
+}
+
+static void wasm_cg_push_zero(CfreeCompiler* c, CfreeCg* cg,
+ CfreeCgBuiltinTypes b, WasmValType vt) {
+ CfreeCgTypeId ty = wasm_cg_type(c, b, vt);
+ if (wasm_is_ref_type(vt))
+ cfree_cg_push_null(cg, ty);
+ else if (vt == WASM_VAL_F32 || vt == WASM_VAL_F64)
+ cfree_cg_push_float(cg, 0.0, ty);
+ else
+ cfree_cg_push_int(cg, 0, ty);
+}
+
+static CfreeCgTypeId wasm_load_storage_type(CfreeCgBuiltinTypes b,
+ uint8_t kind) {
+ switch (kind) {
+ case WASM_INSN_I32_LOAD8_S:
+ case WASM_INSN_I32_LOAD8_U:
+ case WASM_INSN_I64_LOAD8_S:
+ case WASM_INSN_I64_LOAD8_U:
+ return b.id[CFREE_CG_BUILTIN_I8];
+ case WASM_INSN_I32_LOAD16_S:
+ case WASM_INSN_I32_LOAD16_U:
+ case WASM_INSN_I64_LOAD16_S:
+ case WASM_INSN_I64_LOAD16_U:
+ return b.id[CFREE_CG_BUILTIN_I16];
+ case WASM_INSN_I64_LOAD32_S:
+ case WASM_INSN_I64_LOAD32_U:
+ case WASM_INSN_I32_LOAD:
+ return b.id[CFREE_CG_BUILTIN_I32];
+ default:
+ return b.id[CFREE_CG_BUILTIN_I64];
+ }
+}
+
+static CfreeCgTypeId wasm_store_storage_type(CfreeCgBuiltinTypes b,
+ uint8_t kind) {
+ switch (kind) {
+ case WASM_INSN_I32_STORE8:
+ case WASM_INSN_I64_STORE8:
+ return b.id[CFREE_CG_BUILTIN_I8];
+ case WASM_INSN_I32_STORE16:
+ case WASM_INSN_I64_STORE16:
+ return b.id[CFREE_CG_BUILTIN_I16];
+ case WASM_INSN_I64_STORE32:
+ case WASM_INSN_I32_STORE:
+ return b.id[CFREE_CG_BUILTIN_I32];
+ default:
+ return b.id[CFREE_CG_BUILTIN_I64];
+ }
+}
+
+typedef enum WasmTrapKind {
+ WASM_TRAP_UNREACHABLE,
+ WASM_TRAP_DIVISION,
+ WASM_TRAP_INVALID_CONVERSION,
+ WASM_TRAP_BOUNDS,
+ WASM_TRAP_TABLE,
+ WASM_TRAP_SIGNATURE,
+ WASM_TRAP_COUNT,
+} WasmTrapKind;
+
+typedef struct WasmCgRuntime {
+ CfreeCgTypeId i8_ptr_ty;
+ CfreeCgTypeId void_ptr_ty;
+ CfreeCgTypeId memory_ty;
+ CfreeCgTypeId func_import_ty;
+ CfreeCgTypeId global_import_ty;
+ CfreeCgTypeId table_entry_ty;
+ CfreeCgTypeId table_entry_ptr_ty;
+ CfreeCgTypeId table_ty;
+ CfreeCgTypeId instance_ty;
+ CfreeCgTypeId instance_ptr_ty;
+ uint32_t memory_field[64];
+ uint32_t memory_data_field;
+ uint32_t memory_pages_field;
+ uint32_t memory_max_pages_field;
+ uint32_t memory_flags_field;
+ uint32_t func_import_field[64];
+ uint32_t func_ref_entry_field[64];
+ uint32_t global_field[64];
+ uint32_t global_import_addr_field;
+ uint32_t table_field[64];
+ uint32_t table_entries_field[64];
+ uint32_t table_entry_fn_field;
+ uint32_t table_entry_typeidx_field;
+ uint32_t table_entries_ptr_field;
+ uint32_t table_len_field;
+ uint32_t table_max_field;
+ CfreeCgTypeId trap_func_ty;
+ CfreeCgSym trap_syms[WASM_TRAP_COUNT];
+} WasmCgRuntime;
+
+static const char* wasm_trap_name(WasmTrapKind kind) {
+ switch (kind) {
+ case WASM_TRAP_UNREACHABLE:
+ return "__cfree_wasm_trap_unreachable";
+ case WASM_TRAP_DIVISION:
+ return "__cfree_wasm_trap_division";
+ case WASM_TRAP_INVALID_CONVERSION:
+ return "__cfree_wasm_trap_invalid_conversion";
+ case WASM_TRAP_BOUNDS:
+ return "__cfree_wasm_trap_bounds";
+ case WASM_TRAP_TABLE:
+ return "__cfree_wasm_trap_table";
+ case WASM_TRAP_SIGNATURE:
+ return "__cfree_wasm_trap_signature";
+ default:
+ return "__cfree_wasm_trap";
+ }
+}
+
+static void wasm_cg_emit_raw_trap(CfreeCg* cg) {
+ cfree_cg_intrinsic(cg, CFREE_CG_INTRIN_TRAP, 0, CFREE_CG_TYPE_NONE);
+ cfree_cg_unreachable(cg);
+}
+
+static void wasm_cg_trap(CfreeCg* cg, const WasmCgRuntime* rt,
+ WasmTrapKind kind) {
+ if (rt && kind < WASM_TRAP_COUNT && rt->trap_syms[kind])
+ cfree_cg_call_symbol(cg, rt->trap_syms[kind], 0,
+ (CfreeCgCallAttrs){.flags = CFREE_CG_CALL_COLD});
+ else
+ cfree_cg_intrinsic(cg, CFREE_CG_INTRIN_TRAP, 0, CFREE_CG_TYPE_NONE);
+ cfree_cg_unreachable(cg);
+}
+
+static void wasm_cg_trap_unreachable(CfreeCg* cg, const WasmCgRuntime* rt) {
+ wasm_cg_trap(cg, rt, WASM_TRAP_UNREACHABLE);
+}
+static void wasm_cg_trap_division(CfreeCg* cg, const WasmCgRuntime* rt) {
+ wasm_cg_trap(cg, rt, WASM_TRAP_DIVISION);
+}
+static void wasm_cg_trap_invalid_conversion(CfreeCg* cg,
+ const WasmCgRuntime* rt) {
+ wasm_cg_trap(cg, rt, WASM_TRAP_INVALID_CONVERSION);
+}
+static void wasm_cg_trap_bounds(CfreeCg* cg, const WasmCgRuntime* rt) {
+ wasm_cg_trap(cg, rt, WASM_TRAP_BOUNDS);
+}
+static void wasm_cg_trap_table(CfreeCg* cg, const WasmCgRuntime* rt) {
+ wasm_cg_trap(cg, rt, WASM_TRAP_TABLE);
+}
+static void wasm_cg_trap_signature(CfreeCg* cg, const WasmCgRuntime* rt) {
+ wasm_cg_trap(cg, rt, WASM_TRAP_SIGNATURE);
+}
+
+static void wasm_indexed_name(char* name, size_t cap, const char* prefix,
+ uint32_t index) {
+ size_t pos = 0;
+ uint32_t n = index, div = 1000000000u;
+ while (*prefix && pos + 1u < cap) name[pos++] = *prefix++;
+ while (div > 1u && n / div == 0) div /= 10u;
+ while (div && pos + 1u < cap) {
+ name[pos++] = (char)('0' + (n / div) % 10u);
+ div /= 10u;
+ }
+ name[pos] = '\0';
+}
+
+static void wasm_cg_build_runtime(CfreeCompiler* c, CfreeCgBuiltinTypes b,
+ const WasmModule* m, WasmCgRuntime* rt) {
+ CfreeCgField memory_fields[4];
+ CfreeCgField func_import_fields[1];
+ CfreeCgField global_import_fields[1];
+ CfreeCgField table_entry_fields[2];
+ CfreeCgField table_fields[3];
+ CfreeCgField instance_fields[256];
+ uint32_t nfields = 0;
+ memset(rt, 0, sizeof *rt);
+ rt->i8_ptr_ty = cfree_cg_type_ptr(c, b.id[CFREE_CG_BUILTIN_I8], 0);
+ rt->void_ptr_ty = rt->i8_ptr_ty;
+ memset(memory_fields, 0, sizeof memory_fields);
+ memory_fields[0].name = cfree_sym_intern(c, "data");
+ memory_fields[0].type = rt->i8_ptr_ty;
+ memory_fields[1].name = cfree_sym_intern(c, "pages");
+ memory_fields[1].type = b.id[CFREE_CG_BUILTIN_I64];
+ memory_fields[2].name = cfree_sym_intern(c, "max_pages");
+ memory_fields[2].type = b.id[CFREE_CG_BUILTIN_I64];
+ memory_fields[3].name = cfree_sym_intern(c, "flags");
+ memory_fields[3].type = b.id[CFREE_CG_BUILTIN_I32];
+ rt->memory_ty = cfree_cg_type_record(
+ c, cfree_sym_intern(c, "CfreeWasmMemory"), memory_fields, 4);
+ rt->memory_data_field = 0;
+ rt->memory_pages_field = 1;
+ rt->memory_max_pages_field = 2;
+ rt->memory_flags_field = 3;
+ memset(func_import_fields, 0, sizeof func_import_fields);
+ func_import_fields[0].name = cfree_sym_intern(c, "fn");
+ func_import_fields[0].type = rt->void_ptr_ty;
+ rt->func_import_ty = cfree_cg_type_record(
+ c, cfree_sym_intern(c, "CfreeWasmFuncImport"), func_import_fields, 1);
+ memset(global_import_fields, 0, sizeof global_import_fields);
+ global_import_fields[0].name = cfree_sym_intern(c, "addr");
+ global_import_fields[0].type = rt->void_ptr_ty;
+ rt->global_import_ty = cfree_cg_type_record(
+ c, cfree_sym_intern(c, "CfreeWasmGlobalImport"), global_import_fields, 1);
+ memset(table_entry_fields, 0, sizeof table_entry_fields);
+ table_entry_fields[0].name = cfree_sym_intern(c, "fn");
+ table_entry_fields[0].type = rt->void_ptr_ty;
+ table_entry_fields[1].name = cfree_sym_intern(c, "typeidx");
+ table_entry_fields[1].type = b.id[CFREE_CG_BUILTIN_I32];
+ rt->table_entry_ty = cfree_cg_type_record(
+ c, cfree_sym_intern(c, "CfreeWasmTableEntry"), table_entry_fields, 2);
+ rt->table_entry_ptr_ty = cfree_cg_type_ptr(c, rt->table_entry_ty, 0);
+ rt->table_entry_fn_field = 0;
+ rt->table_entry_typeidx_field = 1;
+ memset(table_fields, 0, sizeof table_fields);
+ table_fields[0].name = cfree_sym_intern(c, "entries");
+ table_fields[0].type = rt->table_entry_ptr_ty;
+ table_fields[1].name = cfree_sym_intern(c, "len");
+ table_fields[1].type = b.id[CFREE_CG_BUILTIN_I32];
+ table_fields[2].name = cfree_sym_intern(c, "max");
+ table_fields[2].type = b.id[CFREE_CG_BUILTIN_I32];
+ rt->table_ty = cfree_cg_type_record(c, cfree_sym_intern(c, "CfreeWasmTable"),
+ table_fields, 3);
+ rt->table_entries_ptr_field = 0;
+ rt->table_len_field = 1;
+ rt->table_max_field = 2;
+ memset(instance_fields, 0, sizeof instance_fields);
+ for (uint32_t i = 0; i < m->nmemories; ++i) {
+ char name[40];
+ if (nfields >= 256u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: instance layout too large");
+ if (i == 0)
+ memcpy(name, "memory", sizeof "memory");
+ else
+ wasm_indexed_name(name, sizeof name, "memory_", i);
+ rt->memory_field[i] = nfields;
+ instance_fields[nfields].name = cfree_sym_intern(c, name);
+ instance_fields[nfields].type = rt->memory_ty;
+ nfields++;
+ }
+ for (uint32_t i = 0; i < m->nfuncs; ++i) {
+ char name[40];
+ if (!m->funcs[i].is_import) continue;
+ if (nfields >= 256u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: instance layout too large");
+ wasm_indexed_name(name, sizeof name, "import_func_", i);
+ rt->func_import_field[i] = nfields;
+ instance_fields[nfields].name = cfree_sym_intern(c, name);
+ instance_fields[nfields].type = rt->func_import_ty;
+ nfields++;
+ }
+ for (uint32_t i = 0; i < m->nfuncs; ++i) {
+ char name[40];
+ if (nfields >= 256u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: instance layout too large");
+ wasm_indexed_name(name, sizeof name, "func_ref_", i);
+ rt->func_ref_entry_field[i] = nfields;
+ instance_fields[nfields].name = cfree_sym_intern(c, name);
+ instance_fields[nfields].type = rt->table_entry_ty;
+ nfields++;
+ }
+ for (uint32_t i = 0; i < m->nglobals; ++i) {
+ char name[32];
+ if (nfields >= 256u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: instance layout too large");
+ wasm_indexed_name(name, sizeof name,
+ m->globals[i].is_import ? "import_global_" : "global_",
+ i);
+ rt->global_field[i] = nfields;
+ instance_fields[nfields].name = cfree_sym_intern(c, name);
+ instance_fields[nfields].type =
+ m->globals[i].is_import ? rt->global_import_ty
+ : wasm_cg_type(c, b, m->globals[i].type);
+ nfields++;
+ }
+ rt->global_import_addr_field = 0;
+ for (uint32_t i = 0; i < m->ntables; ++i) {
+ char name[40];
+ uint32_t max = m->tables[i].has_max ? m->tables[i].max : m->tables[i].min;
+ if (nfields + 2u > 256u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: instance layout too large");
+ wasm_indexed_name(name, sizeof name, "table_", i);
+ rt->table_field[i] = nfields;
+ instance_fields[nfields].name = cfree_sym_intern(c, name);
+ instance_fields[nfields].type = rt->table_ty;
+ nfields++;
+ wasm_indexed_name(name, sizeof name, "table_entries_", i);
+ rt->table_entries_field[i] = nfields;
+ instance_fields[nfields].name = cfree_sym_intern(c, name);
+ instance_fields[nfields].type =
+ cfree_cg_type_array(c, rt->table_entry_ty, max ? max : 1u);
+ nfields++;
+ }
+ rt->instance_ty = cfree_cg_type_record(
+ c, cfree_sym_intern(c, "CfreeWasmInstance"), instance_fields, nfields);
+ rt->instance_ptr_ty = cfree_cg_type_ptr(c, rt->instance_ty, 0);
+}
+
+static void wasm_cg_push_instance_lvalue(CfreeCg* cg, const WasmCgRuntime* rt,
+ CfreeCgLocal instance_local) {
+ cfree_cg_push_local(cg, instance_local);
+ cfree_cg_load(cg, wasm_cg_mem_type(rt->instance_ptr_ty));
+ cfree_cg_indirect(cg);
+}
+
+static void wasm_cg_push_memory_lvalue(CfreeCg* cg, const WasmCgRuntime* rt,
+ CfreeCgLocal instance_local,
+ uint32_t memidx) {
+ wasm_cg_push_instance_lvalue(cg, rt, instance_local);
+ cfree_cg_field(cg, rt->memory_field[memidx]);
+}
+
+static void wasm_cg_push_memory_data_ptr(CfreeCg* cg, const WasmCgRuntime* rt,
+ CfreeCgLocal instance_local,
+ uint32_t memidx) {
+ wasm_cg_push_memory_lvalue(cg, rt, instance_local, memidx);
+ cfree_cg_field(cg, rt->memory_data_field);
+ cfree_cg_load(cg, wasm_cg_mem_type(rt->i8_ptr_ty));
+}
+
+static void wasm_cg_push_memory_pages_lvalue(CfreeCg* cg,
+ const WasmCgRuntime* rt,
+ CfreeCgLocal instance_local,
+ uint32_t memidx) {
+ wasm_cg_push_memory_lvalue(cg, rt, instance_local, memidx);
+ cfree_cg_field(cg, rt->memory_pages_field);
+}
+
+static void wasm_cg_push_memory_max_lvalue(CfreeCg* cg, const WasmCgRuntime* rt,
+ CfreeCgLocal instance_local,
+ uint32_t memidx) {
+ wasm_cg_push_memory_lvalue(cg, rt, instance_local, memidx);
+ cfree_cg_field(cg, rt->memory_max_pages_field);
+}
+
+static void wasm_cg_push_global_lvalue(CfreeCg* cg, const WasmCgRuntime* rt,
+ CfreeCgLocal instance_local,
+ uint32_t global_index) {
+ wasm_cg_push_instance_lvalue(cg, rt, instance_local);
+ cfree_cg_field(cg, rt->global_field[global_index]);
+}
+
+static void wasm_cg_push_import_func_lvalue(CfreeCg* cg,
+ const WasmCgRuntime* rt,
+ CfreeCgLocal instance_local,
+ uint32_t func_index) {
+ wasm_cg_push_instance_lvalue(cg, rt, instance_local);
+ cfree_cg_field(cg, rt->func_import_field[func_index]);
+}
+
+static void wasm_cg_push_import_func_ptr(CfreeCg* cg, const WasmCgRuntime* rt,
+ CfreeCgLocal instance_local,
+ uint32_t func_index) {
+ wasm_cg_push_import_func_lvalue(cg, rt, instance_local, func_index);
+ cfree_cg_field(cg, 0);
+ cfree_cg_load(cg, wasm_cg_mem_type(rt->void_ptr_ty));
+}
+
+static void wasm_cg_push_func_ref_lvalue(CfreeCg* cg, const WasmCgRuntime* rt,
+ CfreeCgLocal instance_local,
+ uint32_t func_index) {
+ wasm_cg_push_instance_lvalue(cg, rt, instance_local);
+ cfree_cg_field(cg, rt->func_ref_entry_field[func_index]);
+}
+
+static void wasm_cg_push_global_value_lvalue(CfreeCompiler* c, CfreeCg* cg,
+ CfreeCgBuiltinTypes b,
+ const WasmCgRuntime* rt,
+ CfreeCgLocal instance_local,
+ const WasmModule* m,
+ uint32_t global_index) {
+ wasm_cg_push_global_lvalue(cg, rt, instance_local, global_index);
+ if (m->globals[global_index].is_import) {
+ CfreeCgTypeId ptr_ty = cfree_cg_type_ptr(
+ c, wasm_cg_type(c, b, m->globals[global_index].type), 0);
+ cfree_cg_field(cg, rt->global_import_addr_field);
+ cfree_cg_load(cg, wasm_cg_mem_type(rt->void_ptr_ty));
+ cfree_cg_dup(cg);
+ cfree_cg_push_null(cg, rt->void_ptr_ty);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_NE);
+ {
+ CfreeCgLabel ok = cfree_cg_label_new(cg);
+ cfree_cg_branch_true(cg, ok);
+ wasm_cg_trap_table(cg, rt);
+ cfree_cg_label_place(cg, ok);
+ }
+ cfree_cg_bitcast(cg, ptr_ty);
+ cfree_cg_indirect(cg);
+ }
+}
+
+static void wasm_cg_push_table_lvalue(CfreeCg* cg, const WasmCgRuntime* rt,
+ CfreeCgLocal instance_local,
+ uint32_t table_index) {
+ wasm_cg_push_instance_lvalue(cg, rt, instance_local);
+ cfree_cg_field(cg, rt->table_field[table_index]);
+}
+
+static void wasm_cg_push_table_entries_array_lvalue(CfreeCg* cg,
+ const WasmCgRuntime* rt,
+ CfreeCgLocal instance_local,
+ uint32_t table_index) {
+ wasm_cg_push_instance_lvalue(cg, rt, instance_local);
+ cfree_cg_field(cg, rt->table_entries_field[table_index]);
+}
+
+static void wasm_cg_push_table_entry_lvalue(CfreeCg* cg,
+ const WasmCgRuntime* rt,
+ CfreeCgLocal instance_local,
+ uint32_t table_index,
+ CfreeCgLocal index_local,
+ CfreeCgMemAccess index_mem) {
+ wasm_cg_push_table_lvalue(cg, rt, instance_local, table_index);
+ cfree_cg_field(cg, rt->table_entries_ptr_field);
+ cfree_cg_load(cg, wasm_cg_mem_type(rt->table_entry_ptr_ty));
+ cfree_cg_push_local(cg, index_local);
+ cfree_cg_load(cg, index_mem);
+ cfree_cg_index(cg, 0);
+}
+
+static void wasm_cg_memory_check(CfreeCompiler* c, CfreeCg* cg,
+ CfreeCgBuiltinTypes b, const WasmModule* m,
+ const WasmCgRuntime* rt,
+ CfreeCgLocal instance_local,
+ const WasmInsn* in) {
+ uint32_t width = wasm_mem_width(in->kind);
+ uint64_t end = in->offset64 + width;
+ CfreeCgLabel ok = cfree_cg_label_new(cg);
+ uint32_t max_pages =
+ (uint32_t)(m->memories[in->memidx].has_max
+ ? m->memories[in->memidx].max_pages
+ : m->memories[in->memidx].min_pages);
+ if (end > (uint64_t)max_pages * 65536u) {
+ wasm_cg_trap_bounds(cg, rt);
+ return;
+ }
+ (void)c;
+ cfree_cg_dup(cg);
+ wasm_cg_push_memory_pages_lvalue(cg, rt, instance_local, in->memidx);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
+ cfree_cg_push_int(cg, 65536u, b.id[CFREE_CG_BUILTIN_I64]);
+ cfree_cg_int_binop(cg, CFREE_CG_INT_MUL, 0);
+ cfree_cg_push_int(cg, end, b.id[CFREE_CG_BUILTIN_I64]);
+ cfree_cg_int_binop(cg, CFREE_CG_INT_SUB, 0);
+ if (!m->memories[in->memidx].is64) {
+ cfree_cg_swap(cg);
+ cfree_cg_zext(cg, b.id[CFREE_CG_BUILTIN_I64]);
+ cfree_cg_swap(cg);
+ }
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_LE_U);
+ cfree_cg_branch_true(cg, ok);
+ wasm_cg_trap_bounds(cg, rt);
+ cfree_cg_label_place(cg, ok);
+}
+
+static void wasm_cg_memory_lvalue(CfreeCg* cg, const WasmCgRuntime* rt,
+ CfreeCgLocal instance_local,
+ uint32_t memidx, uint64_t offset) {
+ wasm_cg_push_memory_data_ptr(cg, rt, instance_local, memidx);
+ cfree_cg_swap(cg);
+ cfree_cg_index(cg, offset);
+}
+
+static void wasm_cg_rotate(CfreeCompiler* c, CfreeCg* cg, CfreeCgBuiltinTypes b,
+ WasmValType vt, int right) {
+ CfreeCgTypeId ty = wasm_cg_type(c, b, vt);
+ CfreeCgMemAccess mem = wasm_cg_mem(c, b, vt);
+ CfreeCgLocalAttrs attrs;
+ CfreeCgLocal lhs, rhs;
+ uint32_t mask = vt == WASM_VAL_I32 ? 31u : 63u;
+ memset(&attrs, 0, sizeof attrs);
+ attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
+ rhs = cfree_cg_local(cg, ty, attrs);
+ lhs = cfree_cg_local(cg, ty, attrs);
+ cfree_cg_push_local(cg, rhs);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, mem);
+ cfree_cg_push_local(cg, lhs);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, mem);
+
+ cfree_cg_push_local(cg, lhs);
+ cfree_cg_load(cg, mem);
+ cfree_cg_push_local(cg, rhs);
+ cfree_cg_load(cg, mem);
+ cfree_cg_push_int(cg, mask, ty);
+ cfree_cg_int_binop(cg, CFREE_CG_INT_AND, 0);
+ cfree_cg_int_binop(cg, right ? CFREE_CG_INT_LSHR : CFREE_CG_INT_SHL, 0);
+
+ cfree_cg_push_local(cg, lhs);
+ cfree_cg_load(cg, mem);
+ cfree_cg_push_int(cg, 0, ty);
+ cfree_cg_push_local(cg, rhs);
+ cfree_cg_load(cg, mem);
+ cfree_cg_int_binop(cg, CFREE_CG_INT_SUB, 0);
+ cfree_cg_push_int(cg, mask, ty);
+ cfree_cg_int_binop(cg, CFREE_CG_INT_AND, 0);
+ cfree_cg_int_binop(cg, right ? CFREE_CG_INT_SHL : CFREE_CG_INT_LSHR, 0);
+ cfree_cg_int_binop(cg, CFREE_CG_INT_OR, 0);
+}
+
+static void wasm_cg_checked_divrem(CfreeCompiler* c, CfreeCg* cg,
+ CfreeCgBuiltinTypes b,
+ const WasmCgRuntime* rt, WasmValType vt,
+ CfreeCgIntBinOp op) {
+ CfreeCgTypeId ty = wasm_cg_type(c, b, vt);
+ CfreeCgMemAccess mem = wasm_cg_mem(c, b, vt);
+ CfreeCgLocalAttrs attrs;
+ CfreeCgLocal lhs, rhs;
+ CfreeCgLabel ok = cfree_cg_label_new(cg);
+ memset(&attrs, 0, sizeof attrs);
+ attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
+ rhs = cfree_cg_local(cg, ty, attrs);
+ lhs = cfree_cg_local(cg, ty, attrs);
+ cfree_cg_push_local(cg, rhs);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, mem);
+ cfree_cg_push_local(cg, lhs);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, mem);
+ cfree_cg_push_local(cg, rhs);
+ cfree_cg_load(cg, mem);
+ cfree_cg_push_int(cg, 0, ty);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_NE);
+ cfree_cg_branch_true(cg, ok);
+ wasm_cg_trap_division(cg, rt);
+ cfree_cg_label_place(cg, ok);
+ if (op == CFREE_CG_INT_SDIV) {
+ CfreeCgLabel no_overflow = cfree_cg_label_new(cg);
+ uint64_t min_val = vt == WASM_VAL_I32 ? UINT64_C(0x80000000)
+ : UINT64_C(0x8000000000000000);
+ cfree_cg_push_local(cg, lhs);
+ cfree_cg_load(cg, mem);
+ cfree_cg_push_int(cg, min_val, ty);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_NE);
+ cfree_cg_branch_true(cg, no_overflow);
+ cfree_cg_push_local(cg, rhs);
+ cfree_cg_load(cg, mem);
+ cfree_cg_push_int(cg, UINT64_MAX, ty);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_NE);
+ cfree_cg_branch_true(cg, no_overflow);
+ wasm_cg_trap_division(cg, rt);
+ cfree_cg_label_place(cg, no_overflow);
+ }
+ cfree_cg_push_local(cg, lhs);
+ cfree_cg_load(cg, mem);
+ cfree_cg_push_local(cg, rhs);
+ cfree_cg_load(cg, mem);
+ cfree_cg_int_binop(cg, op, 0);
+}
+
+static void wasm_cg_checked_trunc(CfreeCompiler* c, CfreeCg* cg,
+ CfreeCgBuiltinTypes b,
+ const WasmCgRuntime* rt, WasmValType src,
+ WasmValType dst, int is_unsigned) {
+ CfreeCgTypeId src_ty = wasm_cg_type(c, b, src);
+ CfreeCgTypeId dst_ty = wasm_cg_type(c, b, dst);
+ CfreeCgTypeId bit_ty = b.id[src == WASM_VAL_F32 ? CFREE_CG_BUILTIN_I32
+ : CFREE_CG_BUILTIN_I64];
+ CfreeCgMemAccess src_mem = wasm_cg_mem(c, b, src);
+ CfreeCgMemAccess bit_mem = wasm_cg_mem_type(bit_ty);
+ CfreeCgLocalAttrs attrs;
+ CfreeCgLocal value, bits, abs_bits;
+ uint64_t abs_mask = src == WASM_VAL_F32 ? UINT64_C(0x7fffffff)
+ : UINT64_C(0x7fffffffffffffff);
+ uint64_t sign_mask = src == WASM_VAL_F32 ? UINT64_C(0x80000000)
+ : UINT64_C(0x8000000000000000);
+ uint64_t inf_bits = src == WASM_VAL_F32 ? UINT64_C(0x7f800000)
+ : UINT64_C(0x7ff0000000000000);
+ uint64_t limit_bits;
+ memset(&attrs, 0, sizeof attrs);
+ attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
+ value = cfree_cg_local(cg, src_ty, attrs);
+ bits = cfree_cg_local(cg, bit_ty, attrs);
+ abs_bits = cfree_cg_local(cg, bit_ty, attrs);
+ cfree_cg_push_local(cg, value);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, src_mem);
+
+ cfree_cg_push_local(cg, bits);
+ cfree_cg_push_local(cg, value);
+ cfree_cg_load(cg, src_mem);
+ cfree_cg_bitcast(cg, bit_ty);
+ cfree_cg_store(cg, bit_mem);
+
+ cfree_cg_push_local(cg, abs_bits);
+ cfree_cg_push_local(cg, bits);
+ cfree_cg_load(cg, bit_mem);
+ cfree_cg_push_int(cg, abs_mask, bit_ty);
+ cfree_cg_int_binop(cg, CFREE_CG_INT_AND, 0);
+ cfree_cg_store(cg, bit_mem);
+
+ {
+ CfreeCgLabel finite = cfree_cg_label_new(cg);
+ cfree_cg_push_local(cg, abs_bits);
+ cfree_cg_load(cg, bit_mem);
+ cfree_cg_push_int(cg, inf_bits, bit_ty);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_LE_U);
+ cfree_cg_branch_true(cg, finite);
+ wasm_cg_trap_invalid_conversion(cg, rt);
+ cfree_cg_label_place(cg, finite);
+ }
+
+ if (src == WASM_VAL_F32) {
+ if (dst == WASM_VAL_I32)
+ limit_bits = is_unsigned ? UINT64_C(0x4f800000) : UINT64_C(0x4f000000);
+ else
+ limit_bits = is_unsigned ? UINT64_C(0x5f800000) : UINT64_C(0x5f000000);
+ } else {
+ if (dst == WASM_VAL_I32)
+ limit_bits = is_unsigned ? UINT64_C(0x41f0000000000000)
+ : UINT64_C(0x41e0000000000000);
+ else
+ limit_bits = is_unsigned ? UINT64_C(0x43f0000000000000)
+ : UINT64_C(0x43e0000000000000);
+ }
+
+ if (is_unsigned) {
+ CfreeCgLabel nonnegative = cfree_cg_label_new(cg);
+ cfree_cg_push_local(cg, abs_bits);
+ cfree_cg_load(cg, bit_mem);
+ cfree_cg_push_int(cg, 0, bit_ty);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_EQ);
+ cfree_cg_branch_true(cg, nonnegative);
+ cfree_cg_push_local(cg, bits);
+ cfree_cg_load(cg, bit_mem);
+ cfree_cg_push_int(cg, sign_mask, bit_ty);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_LT_U);
+ cfree_cg_branch_true(cg, nonnegative);
+ wasm_cg_trap_invalid_conversion(cg, rt);
+ cfree_cg_label_place(cg, nonnegative);
+
+ {
+ CfreeCgLabel in_range = cfree_cg_label_new(cg);
+ cfree_cg_push_local(cg, abs_bits);
+ cfree_cg_load(cg, bit_mem);
+ cfree_cg_push_int(cg, limit_bits, bit_ty);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_LT_U);
+ cfree_cg_branch_true(cg, in_range);
+ wasm_cg_trap_invalid_conversion(cg, rt);
+ cfree_cg_label_place(cg, in_range);
+ }
+ } else {
+ CfreeCgLabel negative = cfree_cg_label_new(cg);
+ CfreeCgLabel in_range = cfree_cg_label_new(cg);
+ cfree_cg_push_local(cg, bits);
+ cfree_cg_load(cg, bit_mem);
+ cfree_cg_push_int(cg, sign_mask, bit_ty);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_GE_U);
+ cfree_cg_branch_true(cg, negative);
+ cfree_cg_push_local(cg, abs_bits);
+ cfree_cg_load(cg, bit_mem);
+ cfree_cg_push_int(cg, limit_bits, bit_ty);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_LT_U);
+ cfree_cg_branch_true(cg, in_range);
+ wasm_cg_trap_invalid_conversion(cg, rt);
+ cfree_cg_label_place(cg, negative);
+ cfree_cg_push_local(cg, abs_bits);
+ cfree_cg_load(cg, bit_mem);
+ cfree_cg_push_int(cg, limit_bits, bit_ty);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_LE_U);
+ cfree_cg_branch_true(cg, in_range);
+ wasm_cg_trap_invalid_conversion(cg, rt);
+ cfree_cg_label_place(cg, in_range);
+ }
+
+ cfree_cg_push_local(cg, value);
+ cfree_cg_load(cg, src_mem);
+ if (is_unsigned)
+ cfree_cg_float_to_uint(cg, dst_ty, CFREE_CG_ROUND_TOWARD_ZERO);
+ else
+ cfree_cg_float_to_sint(cg, dst_ty, CFREE_CG_ROUND_TOWARD_ZERO);
+}
+
+
+static int wasm_fp_binop(uint8_t kind, CfreeCgFpBinOp* out) {
+ switch (kind) {
+ case WASM_INSN_F32_ADD:
+ case WASM_INSN_F64_ADD:
+ *out = CFREE_CG_FP_ADD;
+ return 1;
+ case WASM_INSN_F32_SUB:
+ case WASM_INSN_F64_SUB:
+ *out = CFREE_CG_FP_SUB;
+ return 1;
+ case WASM_INSN_F32_MUL:
+ case WASM_INSN_F64_MUL:
+ *out = CFREE_CG_FP_MUL;
+ return 1;
+ case WASM_INSN_F32_DIV:
+ case WASM_INSN_F64_DIV:
+ *out = CFREE_CG_FP_DIV;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int wasm_fp_cmp_op(uint8_t kind, CfreeCgFpCmpOp* out) {
+ switch (kind) {
+ case WASM_INSN_F32_EQ:
+ case WASM_INSN_F64_EQ:
+ *out = CFREE_CG_FP_OEQ;
+ return 1;
+ case WASM_INSN_F32_NE:
+ case WASM_INSN_F64_NE:
+ *out = CFREE_CG_FP_ONE;
+ return 1;
+ case WASM_INSN_F32_LT:
+ case WASM_INSN_F64_LT:
+ *out = CFREE_CG_FP_OLT;
+ return 1;
+ case WASM_INSN_F32_GT:
+ case WASM_INSN_F64_GT:
+ *out = CFREE_CG_FP_OGT;
+ return 1;
+ case WASM_INSN_F32_LE:
+ case WASM_INSN_F64_LE:
+ *out = CFREE_CG_FP_OLE;
+ return 1;
+ case WASM_INSN_F32_GE:
+ case WASM_INSN_F64_GE:
+ *out = CFREE_CG_FP_OGE;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static void wasm_cg_call_func(CfreeCompiler* c, CfreeCg* cg,
+ CfreeCgBuiltinTypes b, const WasmFunc* f,
+ const WasmCgRuntime* rt, CfreeCgSym sym,
+ CfreeCgTypeId func_type,
+ CfreeCgLocal instance_local,
+ uint32_t func_index, int must_tail) {
+ CfreeCgLocalAttrs attrs;
+ CfreeCgLocal args[16];
+ CfreeCgLocal callee = CFREE_CG_LOCAL_NONE;
+ memset(&attrs, 0, sizeof attrs);
+ attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
+ if (f->nparams > 16u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: too many call arguments");
+ for (uint32_t p = 0; p < f->nparams; ++p) {
+ uint32_t param = f->nparams - 1u - p;
+ args[param] =
+ cfree_cg_local(cg, wasm_cg_type(c, b, f->params[param]), attrs);
+ cfree_cg_push_local(cg, args[param]);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, f->params[param]));
+ }
+ if (f->is_import) {
+ CfreeCgLabel ok = cfree_cg_label_new(cg);
+ callee = cfree_cg_local(cg, rt->void_ptr_ty, attrs);
+ cfree_cg_push_local(cg, callee);
+ wasm_cg_push_import_func_ptr(cg, rt, instance_local, func_index);
+ cfree_cg_store(cg, wasm_cg_mem_type(rt->void_ptr_ty));
+ cfree_cg_push_local(cg, callee);
+ cfree_cg_load(cg, wasm_cg_mem_type(rt->void_ptr_ty));
+ cfree_cg_push_null(cg, rt->void_ptr_ty);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_NE);
+ cfree_cg_branch_true(cg, ok);
+ wasm_cg_trap_table(cg, rt);
+ cfree_cg_label_place(cg, ok);
+ cfree_cg_push_local(cg, callee);
+ cfree_cg_load(cg, wasm_cg_mem_type(rt->void_ptr_ty));
+ cfree_cg_bitcast(cg, cfree_cg_type_ptr(c, func_type, 0));
+ }
+ cfree_cg_push_local(cg, instance_local);
+ cfree_cg_load(cg, wasm_cg_mem_type(rt->instance_ptr_ty));
+ for (uint32_t p = 0; p < f->nparams; ++p) {
+ cfree_cg_push_local(cg, args[p]);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, f->params[p]));
+ }
+ if (f->is_import)
+ cfree_cg_call(cg, f->nparams + 1u, func_type,
+ (CfreeCgCallAttrs){
+ .tail = must_tail ? CFREE_CG_TAIL_MUST
+ : CFREE_CG_TAIL_DEFAULT});
+ else
+ cfree_cg_call_symbol(cg, sym, f->nparams + 1u,
+ (CfreeCgCallAttrs){
+ .tail = must_tail ? CFREE_CG_TAIL_MUST
+ : CFREE_CG_TAIL_DEFAULT});
+ if (must_tail) {
+ if (f->nresults) wasm_cg_push_zero(c, cg, b, f->results[0]);
+ cfree_cg_unreachable(cg);
+ }
+}
+
+void wasm_emit_cg(CfreeCompiler* c, const CfreeCodeOptions* code_opts,
+ CfreeObjBuilder* out, const WasmModule* m) {
+ CfreeCg* cg = NULL;
+ CfreeStatus cg_st = cfree_cg_new(c, out, code_opts, &cg);
+ if (cg_st != CFREE_OK)
+ wasm_error(c, wasm_loc(0, 0), "wasm: failed to initialize codegen");
+ CfreeCgBuiltinTypes b = cfree_cg_builtin_types(c);
+ WasmCgRuntime rt;
+ CfreeCgSym syms[64];
+ CfreeCgSym init_sym = CFREE_CG_SYM_NONE;
+ CfreeCgTypeId func_types[64];
+ CfreeCgLocal locals[48];
+ uint32_t i, j;
+ if (!cg) wasm_error(c, wasm_loc(0, 0), "wasm: failed to initialize codegen");
+ if (m->nfuncs > 64u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: too many functions");
+ if (m->nglobals > 64u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: too many globals");
+ wasm_cg_build_runtime(c, b, m, &rt);
+ memset(syms, 0, sizeof syms);
+ memset(func_types, 0, sizeof func_types);
+ {
+ CfreeCgFuncSig sig;
+ CfreeCgDecl decl;
+ memset(&sig, 0, sizeof sig);
+ sig.ret = b.id[CFREE_CG_BUILTIN_VOID];
+ sig.call_conv = CFREE_CG_CC_TARGET_C;
+ rt.trap_func_ty = cfree_cg_type_func(c, sig);
+ if (!rt.trap_func_ty)
+ wasm_error(c, wasm_loc(0, 0), "wasm: failed to create trap type");
+ for (uint32_t k = 0; k < WASM_TRAP_COUNT; ++k) {
+ CfreeSym source_name = cfree_sym_intern(c, wasm_trap_name(k));
+ memset(&decl, 0, sizeof decl);
+ decl.kind = CFREE_CG_DECL_FUNC;
+ decl.linkage_name = cfree_cg_c_linkage_name(c, source_name);
+ decl.display_name = source_name;
+ decl.type = rt.trap_func_ty;
+ decl.sym.bind = CFREE_SB_LOCAL;
+ decl.as.func.flags = CFREE_CG_FUNC_NORETURN | CFREE_CG_FUNC_COLD;
+ rt.trap_syms[k] = cfree_cg_decl(cg, decl);
+ if (!rt.trap_syms[k])
+ wasm_error(c, wasm_loc(0, 0), "wasm: failed to declare trap helper");
+ }
+ }
+ {
+ CfreeCgDecl decl;
+ CfreeCgFuncParam init_param;
+ CfreeCgFuncSig sig;
+ memset(&init_param, 0, sizeof init_param);
+ init_param.type = rt.instance_ptr_ty;
+ memset(&sig, 0, sizeof sig);
+ sig.ret = b.id[CFREE_CG_BUILTIN_VOID];
+ sig.params = &init_param;
+ sig.nparams = 1;
+ sig.call_conv = CFREE_CG_CC_TARGET_C;
+ memset(&decl, 0, sizeof decl);
+ decl.kind = CFREE_CG_DECL_FUNC;
+ decl.linkage_name =
+ cfree_cg_c_linkage_name(c, cfree_sym_intern(c, "__cfree_wasm_init"));
+ decl.display_name = decl.linkage_name;
+ decl.type = cfree_cg_type_func(c, sig);
+ decl.sym.bind = CFREE_SB_GLOBAL;
+ init_sym = cfree_cg_decl(cg, decl);
+ }
+ for (i = 0; i < m->nfuncs; ++i) {
+ const WasmFunc* f = &m->funcs[i];
+ CfreeCgFuncParam cg_params[17];
+ CfreeCgFuncSig sig;
+ CfreeCgDecl decl;
+ char local_name[40];
+ CfreeSym source_name;
+ memset(&cg_params[0], 0, sizeof cg_params[0]);
+ cg_params[0].type = rt.instance_ptr_ty;
+ for (j = 0; j < f->nparams; ++j) {
+ memset(&cg_params[j + 1u], 0, sizeof cg_params[j + 1u]);
+ cg_params[j + 1u].type = wasm_cg_type(c, b, f->params[j]);
+ }
+ memset(&sig, 0, sizeof sig);
+ sig.ret = f->nresults ? wasm_cg_type(c, b, f->results[0])
+ : b.id[CFREE_CG_BUILTIN_VOID];
+ sig.params = cg_params;
+ sig.nparams = f->nparams + 1u;
+ sig.call_conv = CFREE_CG_CC_TARGET_C;
+ func_types[i] = cfree_cg_type_func(c, sig);
+ if (!func_types[i])
+ wasm_error(c, wasm_loc(0, 0), "wasm: failed to create function type");
+ if (f->is_import) {
+ syms[i] = CFREE_CG_SYM_NONE;
+ continue;
+ }
+ if (f->export_name) {
+ source_name = cfree_sym_intern(c, f->export_name);
+ } else {
+ size_t pos = 0;
+ const char prefix[] = "__cfree_wasm_func_";
+ uint32_t n = i, div = 1000000000u;
+ memcpy(local_name, prefix, sizeof(prefix) - 1u);
+ pos = sizeof(prefix) - 1u;
+ while (div > 1u && n / div == 0) div /= 10u;
+ while (div) {
+ local_name[pos++] = (char)('0' + (n / div) % 10u);
+ div /= 10u;
+ }
+ local_name[pos] = '\0';
+ source_name = cfree_sym_intern(c, local_name);
+ }
+ memset(&decl, 0, sizeof decl);
+ decl.kind = CFREE_CG_DECL_FUNC;
+ decl.linkage_name = cfree_cg_c_linkage_name(c, source_name);
+ decl.display_name = source_name;
+ decl.type = func_types[i];
+ decl.sym.bind = f->export_name ? CFREE_SB_GLOBAL : CFREE_SB_LOCAL;
+ syms[i] = cfree_cg_decl(cg, decl);
+ if (!syms[i])
+ wasm_error(c, wasm_loc(0, 0), "wasm: failed to declare function");
+ }
+ for (uint32_t k = 0; k < WASM_TRAP_COUNT; ++k) {
+ cfree_cg_func_begin(cg, rt.trap_syms[k]);
+ wasm_cg_emit_raw_trap(cg);
+ cfree_cg_func_end(cg);
+ }
+ if (init_sym) {
+ CfreeCgLocalAttrs attrs;
+ CfreeCgLocal instance_local;
+ memset(&attrs, 0, sizeof attrs);
+ cfree_cg_func_begin(cg, init_sym);
+ instance_local = cfree_cg_param(cg, 0, rt.instance_ptr_ty, attrs);
+ for (i = 0; i < m->nmemories; ++i) {
+ const WasmMemory* mem = &m->memories[i];
+ uint64_t max_pages = mem->has_max ? mem->max_pages : mem->min_pages;
+ uint32_t flags = (mem->shared ? 1u : 0u) | (mem->is64 ? 2u : 0u);
+ wasm_cg_push_memory_pages_lvalue(cg, &rt, instance_local, i);
+ cfree_cg_push_int(cg, mem->min_pages, b.id[CFREE_CG_BUILTIN_I64]);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
+ wasm_cg_push_memory_max_lvalue(cg, &rt, instance_local, i);
+ cfree_cg_push_int(cg, max_pages, b.id[CFREE_CG_BUILTIN_I64]);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
+ wasm_cg_push_memory_lvalue(cg, &rt, instance_local, i);
+ cfree_cg_field(cg, rt.memory_flags_field);
+ cfree_cg_push_int(cg, flags, b.id[CFREE_CG_BUILTIN_I32]);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
+ if (mem->data_init_len) {
+ CfreeCgSym data_sym =
+ cfree_cg_const_data(cg, mem->data, mem->data_init_len, 16,
+ b.id[CFREE_CG_BUILTIN_I8]);
+ CfreeCgMemAccess byte_mem =
+ wasm_cg_mem_type(b.id[CFREE_CG_BUILTIN_I8]);
+ wasm_cg_push_memory_data_ptr(cg, &rt, instance_local, i);
+ cfree_cg_push_symbol_addr(cg, data_sym, 0);
+ cfree_cg_memcpy(cg, m->memories[i].data_init_len, byte_mem, byte_mem);
+ }
+ }
+ for (i = 0; i < m->nfuncs; ++i) {
+ wasm_cg_push_func_ref_lvalue(cg, &rt, instance_local, i);
+ cfree_cg_field(cg, rt.table_entry_fn_field);
+ if (m->funcs[i].is_import) {
+ wasm_cg_push_import_func_ptr(cg, &rt, instance_local, i);
+ } else {
+ cfree_cg_push_symbol_addr(cg, syms[i], 0);
+ cfree_cg_bitcast(cg, rt.void_ptr_ty);
+ }
+ cfree_cg_store(cg, wasm_cg_mem_type(rt.void_ptr_ty));
+ wasm_cg_push_func_ref_lvalue(cg, &rt, instance_local, i);
+ cfree_cg_field(cg, rt.table_entry_typeidx_field);
+ cfree_cg_push_int(cg, m->funcs[i].typeidx, b.id[CFREE_CG_BUILTIN_I32]);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
+ }
+ for (i = 0; i < m->ntables; ++i) {
+ const WasmTable* t = &m->tables[i];
+ uint32_t max = t->has_max ? t->max : t->min;
+ wasm_cg_push_table_lvalue(cg, &rt, instance_local, i);
+ cfree_cg_field(cg, rt.table_entries_ptr_field);
+ wasm_cg_push_table_entries_array_lvalue(cg, &rt, instance_local, i);
+ cfree_cg_push_int(cg, 0, b.id[CFREE_CG_BUILTIN_I32]);
+ cfree_cg_index(cg, 0);
+ cfree_cg_addr(cg);
+ cfree_cg_store(cg, wasm_cg_mem_type(rt.table_entry_ptr_ty));
+ wasm_cg_push_table_lvalue(cg, &rt, instance_local, i);
+ cfree_cg_field(cg, rt.table_len_field);
+ cfree_cg_push_int(cg, t->min, b.id[CFREE_CG_BUILTIN_I32]);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
+ wasm_cg_push_table_lvalue(cg, &rt, instance_local, i);
+ cfree_cg_field(cg, rt.table_max_field);
+ cfree_cg_push_int(cg, max, b.id[CFREE_CG_BUILTIN_I32]);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
+ }
+ for (i = 0; i < m->nelems; ++i) {
+ const WasmElemSegment* seg = &m->elems[i];
+ for (j = 0; j < seg->nfuncs; ++j) {
+ uint32_t slot = (uint32_t)(seg->offset + j);
+ uint32_t funcidx = seg->funcs[j];
+ CfreeCgLocalAttrs tmp_attrs;
+ CfreeCgLocal slot_local;
+ memset(&tmp_attrs, 0, sizeof tmp_attrs);
+ tmp_attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
+ slot_local = cfree_cg_local(cg, b.id[CFREE_CG_BUILTIN_I32], tmp_attrs);
+ cfree_cg_push_local(cg, slot_local);
+ cfree_cg_push_int(cg, slot, b.id[CFREE_CG_BUILTIN_I32]);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
+ wasm_cg_push_table_entry_lvalue(cg, &rt, instance_local, seg->tableidx,
+ slot_local,
+ wasm_cg_mem(c, b, WASM_VAL_I32));
+ cfree_cg_field(cg, rt.table_entry_fn_field);
+ if (m->funcs[funcidx].is_import) {
+ wasm_cg_push_import_func_ptr(cg, &rt, instance_local, funcidx);
+ } else {
+ cfree_cg_push_symbol_addr(cg, syms[funcidx], 0);
+ cfree_cg_bitcast(cg, rt.void_ptr_ty);
+ }
+ cfree_cg_store(cg, wasm_cg_mem_type(rt.void_ptr_ty));
+ wasm_cg_push_table_entry_lvalue(cg, &rt, instance_local, seg->tableidx,
+ slot_local,
+ wasm_cg_mem(c, b, WASM_VAL_I32));
+ cfree_cg_field(cg, rt.table_entry_typeidx_field);
+ cfree_cg_push_int(cg, m->funcs[funcidx].typeidx,
+ b.id[CFREE_CG_BUILTIN_I32]);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
+ }
+ }
+ for (i = 0; i < m->nglobals; ++i) {
+ const WasmGlobal* g = &m->globals[i];
+ if (g->is_import) continue;
+ wasm_cg_push_global_lvalue(cg, &rt, instance_local, i);
+ if (g->type == WASM_VAL_F32 || g->type == WASM_VAL_F64)
+ cfree_cg_push_float(cg, g->init.fp, wasm_cg_type(c, b, g->type));
+ else
+ cfree_cg_push_int(cg, (uint64_t)g->init.imm,
+ wasm_cg_type(c, b, g->type));
+ cfree_cg_store(cg, wasm_cg_mem(c, b, g->type));
+ }
+ if (m->has_start)
+ wasm_cg_call_func(c, cg, b, &m->funcs[m->start_func], &rt,
+ syms[m->start_func], func_types[m->start_func],
+ instance_local, m->start_func, 0);
+ cfree_cg_ret_void(cg);
+ cfree_cg_func_end(cg);
+ }
+ for (i = 0; i < m->nfuncs; ++i) {
+ const WasmFunc* f = &m->funcs[i];
+ struct {
+ uint8_t kind;
+ int seen_else;
+ CfreeCgLabel start;
+ CfreeCgLabel end;
+ CfreeCgLabel else_label;
+ } control[64];
+ uint32_t ncontrol = 0;
+ CfreeCgLocal instance_local;
+ if (f->is_import) continue;
+ cfree_cg_func_begin(cg, syms[i]);
+ {
+ CfreeCgLocalAttrs attrs;
+ memset(&attrs, 0, sizeof attrs);
+ instance_local = cfree_cg_param(cg, 0, rt.instance_ptr_ty, attrs);
+ }
+ for (j = 0; j < f->nparams; ++j) {
+ CfreeCgLocalAttrs attrs;
+ memset(&attrs, 0, sizeof attrs);
+ locals[j] =
+ cfree_cg_param(cg, j + 1u, wasm_cg_type(c, b, f->params[j]), attrs);
+ }
+ for (j = 0; j < f->nlocals; ++j) {
+ CfreeCgLocalAttrs attrs;
+ memset(&attrs, 0, sizeof attrs);
+ attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
+ locals[f->nparams + j] =
+ cfree_cg_local(cg, wasm_cg_type(c, b, f->locals[j]), attrs);
+ cfree_cg_push_local(cg, locals[f->nparams + j]);
+ wasm_cg_push_zero(c, cg, b, f->locals[j]);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, f->locals[j]));
+ }
+ for (j = 0; j < f->ninsns; ++j) {
+ WasmInsn in = f->insns[j];
+ switch (in.kind) {
+ case WASM_INSN_UNREACHABLE:
+ wasm_cg_trap_unreachable(cg, &rt);
+ break;
+ case WASM_INSN_NOP:
+ break;
+ case WASM_INSN_BLOCK:
+ if (ncontrol >= 64u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: control stack too deep");
+ memset(&control[ncontrol], 0, sizeof control[ncontrol]);
+ control[ncontrol].kind = WASM_INSN_BLOCK;
+ control[ncontrol].end = cfree_cg_label_new(cg);
+ ncontrol++;
+ break;
+ case WASM_INSN_LOOP:
+ if (ncontrol >= 64u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: control stack too deep");
+ memset(&control[ncontrol], 0, sizeof control[ncontrol]);
+ control[ncontrol].kind = WASM_INSN_LOOP;
+ control[ncontrol].start = cfree_cg_label_new(cg);
+ control[ncontrol].end = cfree_cg_label_new(cg);
+ cfree_cg_label_place(cg, control[ncontrol].start);
+ ncontrol++;
+ break;
+ case WASM_INSN_IF:
+ if (ncontrol >= 64u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: control stack too deep");
+ memset(&control[ncontrol], 0, sizeof control[ncontrol]);
+ control[ncontrol].kind = WASM_INSN_IF;
+ control[ncontrol].else_label = cfree_cg_label_new(cg);
+ control[ncontrol].end = cfree_cg_label_new(cg);
+ cfree_cg_branch_false(cg, control[ncontrol].else_label);
+ ncontrol++;
+ break;
+ case WASM_INSN_ELSE:
+ if (!ncontrol || control[ncontrol - 1u].kind != WASM_INSN_IF)
+ wasm_error(c, wasm_loc(0, 0), "wasm: else without if");
+ cfree_cg_jump(cg, control[ncontrol - 1u].end);
+ cfree_cg_label_place(cg, control[ncontrol - 1u].else_label);
+ control[ncontrol - 1u].seen_else = 1;
+ break;
+ case WASM_INSN_END:
+ if (!ncontrol)
+ wasm_error(c, wasm_loc(0, 0), "wasm: end without block");
+ ncontrol--;
+ if (control[ncontrol].kind == WASM_INSN_IF &&
+ !control[ncontrol].seen_else)
+ cfree_cg_label_place(cg, control[ncontrol].else_label);
+ cfree_cg_label_place(cg, control[ncontrol].end);
+ break;
+ case WASM_INSN_BR: {
+ uint32_t depth = (uint32_t)in.imm;
+ uint32_t idx;
+ if (depth >= ncontrol)
+ wasm_error(c, wasm_loc(0, 0), "wasm: branch depth out of range");
+ idx = ncontrol - 1u - depth;
+ cfree_cg_jump(cg, control[idx].kind == WASM_INSN_LOOP
+ ? control[idx].start
+ : control[idx].end);
+ break;
+ }
+ case WASM_INSN_BR_IF: {
+ uint32_t depth = (uint32_t)in.imm;
+ uint32_t idx;
+ if (depth >= ncontrol)
+ wasm_error(c, wasm_loc(0, 0), "wasm: branch depth out of range");
+ idx = ncontrol - 1u - depth;
+ cfree_cg_branch_true(cg, control[idx].kind == WASM_INSN_LOOP
+ ? control[idx].start
+ : control[idx].end);
+ break;
+ }
+ case WASM_INSN_BR_TABLE: {
+ CfreeCgSwitchCase cases[15];
+ CfreeCgSwitch sw;
+ memset(cases, 0, sizeof cases);
+ if (in.ntargets == 0 || in.ntargets > 16u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: bad br_table target count");
+ for (uint32_t k = 0; k + 1u < in.ntargets; ++k) {
+ uint32_t idx;
+ if (in.targets[k] >= ncontrol)
+ wasm_error(c, wasm_loc(0, 0), "wasm: branch depth out of range");
+ idx = ncontrol - 1u - in.targets[k];
+ cases[k].value = k;
+ cases[k].label = control[idx].kind == WASM_INSN_LOOP
+ ? control[idx].start
+ : control[idx].end;
+ }
+ if (in.targets[in.ntargets - 1u] >= ncontrol)
+ wasm_error(c, wasm_loc(0, 0), "wasm: branch depth out of range");
+ memset(&sw, 0, sizeof sw);
+ sw.selector_type = b.id[CFREE_CG_BUILTIN_I32];
+ sw.cases = cases;
+ sw.ncases = in.ntargets - 1u;
+ sw.default_label =
+ control[ncontrol - 1u - in.targets[in.ntargets - 1u]].kind ==
+ WASM_INSN_LOOP
+ ? control[ncontrol - 1u - in.targets[in.ntargets - 1u]].start
+ : control[ncontrol - 1u - in.targets[in.ntargets - 1u]].end;
+ sw.hint = CFREE_CG_SWITCH_BRANCH_CHAIN;
+ cfree_cg_switch(cg, sw);
+ break;
+ }
+ case WASM_INSN_SELECT: {
+ CfreeCgTypeId ty = wasm_cg_type(c, b, (WasmValType)in.type);
+ CfreeCgMemAccess mem = wasm_cg_mem(c, b, (WasmValType)in.type);
+ CfreeCgLocalAttrs attrs;
+ CfreeCgLocal lhs, rhs, cond, result;
+ CfreeCgLabel else_label = cfree_cg_label_new(cg);
+ CfreeCgLabel end_label = cfree_cg_label_new(cg);
+ memset(&attrs, 0, sizeof attrs);
+ attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
+ lhs = cfree_cg_local(cg, ty, attrs);
+ rhs = cfree_cg_local(cg, ty, attrs);
+ result = cfree_cg_local(cg, ty, attrs);
+ cond = cfree_cg_local(cg, b.id[CFREE_CG_BUILTIN_I32], attrs);
+
+ cfree_cg_push_local(cg, cond);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
+ cfree_cg_push_local(cg, rhs);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, mem);
+ cfree_cg_push_local(cg, lhs);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, mem);
+
+ cfree_cg_push_local(cg, cond);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
+ cfree_cg_branch_false(cg, else_label);
+ cfree_cg_push_local(cg, result);
+ cfree_cg_push_local(cg, lhs);
+ cfree_cg_load(cg, mem);
+ cfree_cg_store(cg, mem);
+ cfree_cg_jump(cg, end_label);
+ cfree_cg_label_place(cg, else_label);
+ cfree_cg_push_local(cg, result);
+ cfree_cg_push_local(cg, rhs);
+ cfree_cg_load(cg, mem);
+ cfree_cg_store(cg, mem);
+ cfree_cg_label_place(cg, end_label);
+ cfree_cg_push_local(cg, result);
+ cfree_cg_load(cg, mem);
+ } break;
+ case WASM_INSN_I32_CONST:
+ cfree_cg_push_int(cg, (uint64_t)(uint32_t)in.imm,
+ b.id[CFREE_CG_BUILTIN_I32]);
+ break;
+ case WASM_INSN_I64_CONST:
+ cfree_cg_push_int(cg, (uint64_t)in.imm, b.id[CFREE_CG_BUILTIN_I64]);
+ break;
+ case WASM_INSN_F32_CONST:
+ cfree_cg_push_float(cg, in.fp, b.id[CFREE_CG_BUILTIN_F32]);
+ break;
+ case WASM_INSN_F64_CONST:
+ cfree_cg_push_float(cg, in.fp, b.id[CFREE_CG_BUILTIN_F64]);
+ break;
+ case WASM_INSN_LOCAL_GET: {
+ uint32_t index = (uint32_t)in.imm;
+ cfree_cg_push_local(cg, locals[index]);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, wasm_func_local_type(f, index)));
+ break;
+ }
+ case WASM_INSN_LOCAL_SET: {
+ uint32_t index = (uint32_t)in.imm;
+ cfree_cg_push_local(cg, locals[index]);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, wasm_func_local_type(f, index)));
+ break;
+ }
+ case WASM_INSN_LOCAL_TEE: {
+ uint32_t index = (uint32_t)in.imm;
+ cfree_cg_dup(cg);
+ cfree_cg_push_local(cg, locals[index]);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, wasm_func_local_type(f, index)));
+ break;
+ }
+ case WASM_INSN_CALL:
+ case WASM_INSN_RETURN_CALL:
+ wasm_cg_call_func(c, cg, b, &m->funcs[in.imm], &rt, syms[in.imm],
+ func_types[in.imm], instance_local,
+ (uint32_t)in.imm,
+ in.kind == WASM_INSN_RETURN_CALL);
+ break;
+ case WASM_INSN_CALL_INDIRECT:
+ case WASM_INSN_RETURN_CALL_INDIRECT: {
+ const WasmFuncType* t = &m->types[in.imm];
+ CfreeCgLocalAttrs attrs;
+ CfreeCgLocal selector, callee, args[16], result = CFREE_CG_LOCAL_NONE;
+ CfreeCgLabel ok;
+ CfreeCgMemAccess i32_mem = wasm_cg_mem(c, b, WASM_VAL_I32);
+ CfreeCgFuncParam indirect_params[17];
+ CfreeCgFuncSig indirect_sig;
+ CfreeCgTypeId indirect_func_type;
+ memset(&attrs, 0, sizeof attrs);
+ attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
+ if (t->nparams > 16u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: too many call_indirect args");
+ memset(indirect_params, 0, sizeof indirect_params);
+ indirect_params[0].type = rt.instance_ptr_ty;
+ for (uint32_t p = 0; p < t->nparams; ++p)
+ indirect_params[p + 1u].type = wasm_cg_type(c, b, t->params[p]);
+ memset(&indirect_sig, 0, sizeof indirect_sig);
+ indirect_sig.ret = t->nresults ? wasm_cg_type(c, b, t->results[0])
+ : b.id[CFREE_CG_BUILTIN_VOID];
+ indirect_sig.params = indirect_params;
+ indirect_sig.nparams = t->nparams + 1u;
+ indirect_sig.call_conv = CFREE_CG_CC_TARGET_C;
+ indirect_func_type = cfree_cg_type_func(c, indirect_sig);
+ selector = cfree_cg_local(cg, b.id[CFREE_CG_BUILTIN_I32], attrs);
+ callee = cfree_cg_local(cg, rt.void_ptr_ty, attrs);
+ if (t->nresults)
+ result =
+ cfree_cg_local(cg, wasm_cg_type(c, b, t->results[0]), attrs);
+ cfree_cg_push_local(cg, selector);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, i32_mem);
+ for (uint32_t p = 0; p < t->nparams; ++p) {
+ uint32_t param = t->nparams - 1u - p;
+ args[param] =
+ cfree_cg_local(cg, wasm_cg_type(c, b, t->params[param]), attrs);
+ cfree_cg_push_local(cg, args[param]);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, t->params[param]));
+ }
+
+ ok = cfree_cg_label_new(cg);
+ cfree_cg_push_local(cg, selector);
+ cfree_cg_load(cg, i32_mem);
+ wasm_cg_push_table_lvalue(cg, &rt, instance_local, in.align);
+ cfree_cg_field(cg, rt.table_len_field);
+ cfree_cg_load(cg, i32_mem);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_LT_U);
+ cfree_cg_branch_true(cg, ok);
+ wasm_cg_trap_table(cg, &rt);
+ cfree_cg_label_place(cg, ok);
+
+ ok = cfree_cg_label_new(cg);
+ cfree_cg_push_local(cg, callee);
+ wasm_cg_push_table_entry_lvalue(cg, &rt, instance_local, in.align,
+ selector, i32_mem);
+ cfree_cg_field(cg, rt.table_entry_fn_field);
+ cfree_cg_load(cg, wasm_cg_mem_type(rt.void_ptr_ty));
+ cfree_cg_store(cg, wasm_cg_mem_type(rt.void_ptr_ty));
+ cfree_cg_push_local(cg, callee);
+ cfree_cg_load(cg, wasm_cg_mem_type(rt.void_ptr_ty));
+ cfree_cg_push_null(cg, rt.void_ptr_ty);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_NE);
+ cfree_cg_branch_true(cg, ok);
+ wasm_cg_trap_table(cg, &rt);
+ cfree_cg_label_place(cg, ok);
+
+ ok = cfree_cg_label_new(cg);
+ wasm_cg_push_table_entry_lvalue(cg, &rt, instance_local, in.align,
+ selector, i32_mem);
+ cfree_cg_field(cg, rt.table_entry_typeidx_field);
+ cfree_cg_load(cg, i32_mem);
+ cfree_cg_push_int(cg, (uint32_t)in.imm, b.id[CFREE_CG_BUILTIN_I32]);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_EQ);
+ cfree_cg_branch_true(cg, ok);
+ wasm_cg_trap_signature(cg, &rt);
+ cfree_cg_label_place(cg, ok);
+
+ cfree_cg_push_local(cg, callee);
+ cfree_cg_load(cg, wasm_cg_mem_type(rt.void_ptr_ty));
+ cfree_cg_bitcast(cg, cfree_cg_type_ptr(c, indirect_func_type, 0));
+ cfree_cg_push_local(cg, instance_local);
+ cfree_cg_load(cg, wasm_cg_mem_type(rt.instance_ptr_ty));
+ for (uint32_t p = 0; p < t->nparams; ++p) {
+ cfree_cg_push_local(cg, args[p]);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, t->params[p]));
+ }
+ cfree_cg_call(cg, t->nparams + 1u, indirect_func_type,
+ (CfreeCgCallAttrs){
+ .tail = in.kind == WASM_INSN_RETURN_CALL_INDIRECT
+ ? CFREE_CG_TAIL_MUST
+ : CFREE_CG_TAIL_DEFAULT});
+ if (in.kind == WASM_INSN_RETURN_CALL_INDIRECT) {
+ if (t->nresults) wasm_cg_push_zero(c, cg, b, t->results[0]);
+ cfree_cg_unreachable(cg);
+ } else if (t->nresults) {
+ cfree_cg_push_local(cg, result);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, t->results[0]));
+ cfree_cg_push_local(cg, result);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, t->results[0]));
+ }
+ break;
+ }
+ case WASM_INSN_REF_NULL:
+ cfree_cg_push_null(cg, rt.void_ptr_ty);
+ break;
+ case WASM_INSN_REF_FUNC:
+ wasm_cg_push_func_ref_lvalue(cg, &rt, instance_local,
+ (uint32_t)in.imm);
+ cfree_cg_addr(cg);
+ cfree_cg_bitcast(cg, rt.void_ptr_ty);
+ break;
+ case WASM_INSN_REF_IS_NULL:
+ cfree_cg_push_null(cg, rt.void_ptr_ty);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_EQ);
+ break;
+ case WASM_INSN_CALL_REF:
+ case WASM_INSN_RETURN_CALL_REF: {
+ const WasmFuncType* t = &m->types[in.imm];
+ CfreeCgLocalAttrs attrs;
+ CfreeCgLocal callee_ref, callee, args[16],
+ result = CFREE_CG_LOCAL_NONE;
+ CfreeCgLabel ok;
+ CfreeCgMemAccess ref_mem = wasm_cg_mem_type(rt.void_ptr_ty);
+ CfreeCgMemAccess i32_mem = wasm_cg_mem(c, b, WASM_VAL_I32);
+ CfreeCgFuncParam ref_params[17];
+ CfreeCgFuncSig ref_sig;
+ CfreeCgTypeId ref_func_type;
+ memset(&attrs, 0, sizeof attrs);
+ attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
+ if (t->nparams > 16u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: too many call_ref args");
+ callee_ref = cfree_cg_local(cg, rt.void_ptr_ty, attrs);
+ callee = cfree_cg_local(cg, rt.void_ptr_ty, attrs);
+ cfree_cg_push_local(cg, callee_ref);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, ref_mem);
+ for (uint32_t p = 0; p < t->nparams; ++p) {
+ uint32_t param = t->nparams - 1u - p;
+ args[param] =
+ cfree_cg_local(cg, wasm_cg_type(c, b, t->params[param]), attrs);
+ cfree_cg_push_local(cg, args[param]);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, t->params[param]));
+ }
+ ok = cfree_cg_label_new(cg);
+ cfree_cg_push_local(cg, callee_ref);
+ cfree_cg_load(cg, ref_mem);
+ cfree_cg_push_null(cg, rt.void_ptr_ty);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_NE);
+ cfree_cg_branch_true(cg, ok);
+ wasm_cg_trap_table(cg, &rt);
+ cfree_cg_label_place(cg, ok);
+
+ ok = cfree_cg_label_new(cg);
+ cfree_cg_push_local(cg, callee_ref);
+ cfree_cg_load(cg, ref_mem);
+ cfree_cg_bitcast(cg, rt.table_entry_ptr_ty);
+ cfree_cg_indirect(cg);
+ cfree_cg_field(cg, rt.table_entry_typeidx_field);
+ cfree_cg_load(cg, i32_mem);
+ cfree_cg_push_int(cg, (uint32_t)in.imm, b.id[CFREE_CG_BUILTIN_I32]);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_EQ);
+ cfree_cg_branch_true(cg, ok);
+ wasm_cg_trap_signature(cg, &rt);
+ cfree_cg_label_place(cg, ok);
+
+ cfree_cg_push_local(cg, callee);
+ cfree_cg_push_local(cg, callee_ref);
+ cfree_cg_load(cg, ref_mem);
+ cfree_cg_bitcast(cg, rt.table_entry_ptr_ty);
+ cfree_cg_indirect(cg);
+ cfree_cg_field(cg, rt.table_entry_fn_field);
+ cfree_cg_load(cg, ref_mem);
+ cfree_cg_store(cg, ref_mem);
+
+ memset(ref_params, 0, sizeof ref_params);
+ ref_params[0].type = rt.instance_ptr_ty;
+ for (uint32_t p = 0; p < t->nparams; ++p)
+ ref_params[p + 1u].type = wasm_cg_type(c, b, t->params[p]);
+ memset(&ref_sig, 0, sizeof ref_sig);
+ ref_sig.ret = t->nresults ? wasm_cg_type(c, b, t->results[0])
+ : b.id[CFREE_CG_BUILTIN_VOID];
+ ref_sig.params = ref_params;
+ ref_sig.nparams = t->nparams + 1u;
+ ref_sig.call_conv = CFREE_CG_CC_TARGET_C;
+ ref_func_type = cfree_cg_type_func(c, ref_sig);
+ if (t->nresults)
+ result =
+ cfree_cg_local(cg, wasm_cg_type(c, b, t->results[0]), attrs);
+ cfree_cg_push_local(cg, callee);
+ cfree_cg_load(cg, ref_mem);
+ cfree_cg_bitcast(cg, cfree_cg_type_ptr(c, ref_func_type, 0));
+ cfree_cg_push_local(cg, instance_local);
+ cfree_cg_load(cg, wasm_cg_mem_type(rt.instance_ptr_ty));
+ for (uint32_t p = 0; p < t->nparams; ++p) {
+ cfree_cg_push_local(cg, args[p]);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, t->params[p]));
+ }
+ cfree_cg_call(cg, t->nparams + 1u, ref_func_type,
+ (CfreeCgCallAttrs){
+ .tail = in.kind == WASM_INSN_RETURN_CALL_REF
+ ? CFREE_CG_TAIL_MUST
+ : CFREE_CG_TAIL_DEFAULT});
+ if (in.kind == WASM_INSN_RETURN_CALL_REF) {
+ if (t->nresults) wasm_cg_push_zero(c, cg, b, t->results[0]);
+ cfree_cg_unreachable(cg);
+ } else if (t->nresults) {
+ cfree_cg_push_local(cg, result);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, t->results[0]));
+ cfree_cg_push_local(cg, result);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, t->results[0]));
+ }
+ break;
+ }
+ case WASM_INSN_GLOBAL_GET: {
+ uint32_t index = (uint32_t)in.imm;
+ wasm_cg_push_global_value_lvalue(c, cg, b, &rt, instance_local, m,
+ index);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, m->globals[index].type));
+ break;
+ }
+ case WASM_INSN_GLOBAL_SET: {
+ uint32_t index = (uint32_t)in.imm;
+ wasm_cg_push_global_value_lvalue(c, cg, b, &rt, instance_local, m,
+ index);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, m->globals[index].type));
+ break;
+ }
+ case WASM_INSN_RETURN:
+ if (f->nresults)
+ cfree_cg_ret(cg);
+ else
+ cfree_cg_ret_void(cg);
+ break;
+ case WASM_INSN_DROP:
+ cfree_cg_drop(cg);
+ break;
+ case WASM_INSN_MEMORY_SIZE:
+ wasm_cg_push_memory_pages_lvalue(cg, &rt, instance_local,
+ in.memidx);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
+ if (!m->memories[in.memidx].is64)
+ cfree_cg_trunc(cg, b.id[CFREE_CG_BUILTIN_I32]);
+ break;
+ case WASM_INSN_MEMORY_GROW: {
+ CfreeCgLocalAttrs attrs;
+ CfreeCgLocal delta, old_pages, grow_result;
+ CfreeCgLabel fail = cfree_cg_label_new(cg);
+ CfreeCgLabel done = cfree_cg_label_new(cg);
+ WasmValType page_vt =
+ m->memories[in.memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32;
+ CfreeCgTypeId page_ty = wasm_cg_type(c, b, page_vt);
+ memset(&attrs, 0, sizeof attrs);
+ attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
+ delta = cfree_cg_local(cg, page_ty, attrs);
+ old_pages = cfree_cg_local(cg, b.id[CFREE_CG_BUILTIN_I64], attrs);
+ grow_result = cfree_cg_local(cg, page_ty, attrs);
+ cfree_cg_push_local(cg, delta);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, page_vt));
+ cfree_cg_push_local(cg, old_pages);
+ wasm_cg_push_memory_pages_lvalue(cg, &rt, instance_local,
+ in.memidx);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
+ cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
+
+ cfree_cg_push_local(cg, delta);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, page_vt));
+ if (!m->memories[in.memidx].is64)
+ cfree_cg_zext(cg, b.id[CFREE_CG_BUILTIN_I64]);
+ wasm_cg_push_memory_max_lvalue(cg, &rt, instance_local, in.memidx);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
+ cfree_cg_push_local(cg, old_pages);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
+ cfree_cg_int_binop(cg, CFREE_CG_INT_SUB, 0);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_LE_U);
+ cfree_cg_branch_false(cg, fail);
+
+ wasm_cg_push_memory_pages_lvalue(cg, &rt, instance_local,
+ in.memidx);
+ cfree_cg_push_local(cg, old_pages);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
+ cfree_cg_push_local(cg, delta);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, page_vt));
+ if (!m->memories[in.memidx].is64)
+ cfree_cg_zext(cg, b.id[CFREE_CG_BUILTIN_I64]);
+ cfree_cg_int_binop(cg, CFREE_CG_INT_ADD, 0);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
+ cfree_cg_push_local(cg, grow_result);
+ cfree_cg_push_local(cg, old_pages);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
+ if (!m->memories[in.memidx].is64)
+ cfree_cg_trunc(cg, b.id[CFREE_CG_BUILTIN_I32]);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, page_vt));
+ cfree_cg_jump(cg, done);
+ cfree_cg_label_place(cg, fail);
+ cfree_cg_push_local(cg, grow_result);
+ cfree_cg_push_int(cg, UINT64_MAX, page_ty);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, page_vt));
+ cfree_cg_label_place(cg, done);
+ cfree_cg_push_local(cg, grow_result);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, page_vt));
+ break;
+ }
+ case WASM_INSN_ATOMIC_FENCE:
+ cfree_cg_atomic_fence(cg, CFREE_CG_MO_SEQ_CST);
+ break;
+ case WASM_INSN_I32_ATOMIC_LOAD:
+ case WASM_INSN_I64_ATOMIC_LOAD:
+ case WASM_INSN_I32_ATOMIC_LOAD8_U:
+ case WASM_INSN_I32_ATOMIC_LOAD16_U:
+ case WASM_INSN_I64_ATOMIC_LOAD8_U:
+ case WASM_INSN_I64_ATOMIC_LOAD16_U:
+ case WASM_INSN_I64_ATOMIC_LOAD32_U: {
+ CfreeCgTypeId ty = wasm_cg_type(c, b, wasm_atomic_value_type(in.kind));
+ CfreeCgMemAccess mem = wasm_cg_mem_type(ty);
+ mem.align = in.align;
+ wasm_cg_memory_check(c, cg, b, m, &rt, instance_local, &in);
+ wasm_cg_memory_lvalue(cg, &rt, instance_local, in.memidx,
+ in.offset64);
+ cfree_cg_addr(cg);
+ cfree_cg_atomic_load(cg, mem, CFREE_CG_MO_SEQ_CST);
+ break;
+ }
+ case WASM_INSN_I32_ATOMIC_STORE:
+ case WASM_INSN_I64_ATOMIC_STORE:
+ case WASM_INSN_I32_ATOMIC_STORE8:
+ case WASM_INSN_I32_ATOMIC_STORE16:
+ case WASM_INSN_I64_ATOMIC_STORE8:
+ case WASM_INSN_I64_ATOMIC_STORE16:
+ case WASM_INSN_I64_ATOMIC_STORE32: {
+ WasmValType vt = wasm_atomic_value_type(in.kind);
+ CfreeCgTypeId ty = wasm_cg_type(c, b, vt);
+ CfreeCgMemAccess mem = wasm_cg_mem_type(ty);
+ CfreeCgLocalAttrs attrs;
+ CfreeCgLocal value_tmp;
+ memset(&attrs, 0, sizeof attrs);
+ attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
+ mem.align = in.align;
+ value_tmp = cfree_cg_local(cg, ty, attrs);
+ cfree_cg_push_local(cg, value_tmp);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, mem);
+ wasm_cg_memory_check(c, cg, b, m, &rt, instance_local, &in);
+ wasm_cg_memory_lvalue(cg, &rt, instance_local, in.memidx,
+ in.offset64);
+ cfree_cg_addr(cg);
+ cfree_cg_push_local(cg, value_tmp);
+ cfree_cg_load(cg, mem);
+ cfree_cg_atomic_store(cg, mem, CFREE_CG_MO_SEQ_CST);
+ break;
+ }
+ case WASM_INSN_I32_ATOMIC_RMW_ADD:
+ case WASM_INSN_I64_ATOMIC_RMW_ADD:
+ case WASM_INSN_I32_ATOMIC_RMW_SUB:
+ case WASM_INSN_I64_ATOMIC_RMW_SUB:
+ case WASM_INSN_I32_ATOMIC_RMW_AND:
+ case WASM_INSN_I64_ATOMIC_RMW_AND:
+ case WASM_INSN_I32_ATOMIC_RMW_OR:
+ case WASM_INSN_I64_ATOMIC_RMW_OR:
+ case WASM_INSN_I32_ATOMIC_RMW_XOR:
+ case WASM_INSN_I64_ATOMIC_RMW_XOR:
+ case WASM_INSN_I32_ATOMIC_RMW_XCHG:
+ case WASM_INSN_I64_ATOMIC_RMW_XCHG: {
+ WasmValType vt = wasm_atomic_value_type(in.kind);
+ CfreeCgTypeId ty = wasm_cg_type(c, b, vt);
+ CfreeCgMemAccess mem = wasm_cg_mem_type(ty);
+ CfreeCgLocalAttrs attrs;
+ CfreeCgLocal value_tmp;
+ memset(&attrs, 0, sizeof attrs);
+ attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
+ mem.align = in.align;
+ value_tmp = cfree_cg_local(cg, ty, attrs);
+ cfree_cg_push_local(cg, value_tmp);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, mem);
+ wasm_cg_memory_check(c, cg, b, m, &rt, instance_local, &in);
+ wasm_cg_memory_lvalue(cg, &rt, instance_local, in.memidx,
+ in.offset64);
+ cfree_cg_addr(cg);
+ cfree_cg_push_local(cg, value_tmp);
+ cfree_cg_load(cg, mem);
+ cfree_cg_atomic_rmw(cg, mem, wasm_atomic_rmw_op(in.kind),
+ CFREE_CG_MO_SEQ_CST);
+ break;
+ }
+ case WASM_INSN_I32_ATOMIC_RMW_CMPXCHG:
+ case WASM_INSN_I64_ATOMIC_RMW_CMPXCHG: {
+ WasmValType vt = wasm_atomic_value_type(in.kind);
+ CfreeCgTypeId ty = wasm_cg_type(c, b, vt);
+ CfreeCgMemAccess mem = wasm_cg_mem_type(ty);
+ CfreeCgLocalAttrs attrs;
+ CfreeCgLocal expected_tmp, desired_tmp;
+ memset(&attrs, 0, sizeof attrs);
+ attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
+ mem.align = in.align;
+ desired_tmp = cfree_cg_local(cg, ty, attrs);
+ expected_tmp = cfree_cg_local(cg, ty, attrs);
+ cfree_cg_push_local(cg, desired_tmp);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, mem);
+ cfree_cg_push_local(cg, expected_tmp);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, mem);
+ wasm_cg_memory_check(c, cg, b, m, &rt, instance_local, &in);
+ wasm_cg_memory_lvalue(cg, &rt, instance_local, in.memidx,
+ in.offset64);
+ cfree_cg_addr(cg);
+ cfree_cg_push_local(cg, expected_tmp);
+ cfree_cg_load(cg, mem);
+ cfree_cg_push_local(cg, desired_tmp);
+ cfree_cg_load(cg, mem);
+ cfree_cg_atomic_cmpxchg(cg, mem, CFREE_CG_MO_SEQ_CST,
+ CFREE_CG_MO_SEQ_CST, 0);
+ cfree_cg_drop(cg);
+ break;
+ }
+ case WASM_INSN_I32_ATOMIC_WAIT:
+ case WASM_INSN_I64_ATOMIC_WAIT: {
+ WasmValType vt = wasm_atomic_value_type(in.kind);
+ CfreeCgTypeId ty = wasm_cg_type(c, b, vt);
+ CfreeCgMemAccess mem = wasm_cg_mem_type(ty);
+ CfreeCgLocalAttrs attrs;
+ CfreeCgLocal timeout_tmp, expected_tmp, result_tmp;
+ CfreeCgLabel equal = cfree_cg_label_new(cg);
+ CfreeCgLabel done = cfree_cg_label_new(cg);
+ memset(&attrs, 0, sizeof attrs);
+ attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
+ mem.align = in.align;
+ timeout_tmp = cfree_cg_local(cg, b.id[CFREE_CG_BUILTIN_I64], attrs);
+ expected_tmp = cfree_cg_local(cg, ty, attrs);
+ result_tmp = cfree_cg_local(cg, b.id[CFREE_CG_BUILTIN_I32], attrs);
+ cfree_cg_push_local(cg, timeout_tmp);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
+ cfree_cg_push_local(cg, expected_tmp);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, mem);
+ wasm_cg_memory_check(c, cg, b, m, &rt, instance_local, &in);
+ wasm_cg_memory_lvalue(cg, &rt, instance_local, in.memidx,
+ in.offset64);
+ cfree_cg_addr(cg);
+ cfree_cg_atomic_load(cg, mem, CFREE_CG_MO_SEQ_CST);
+ cfree_cg_push_local(cg, expected_tmp);
+ cfree_cg_load(cg, mem);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_EQ);
+ cfree_cg_branch_true(cg, equal);
+ cfree_cg_push_local(cg, result_tmp);
+ cfree_cg_push_int(cg, 1, b.id[CFREE_CG_BUILTIN_I32]);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
+ cfree_cg_jump(cg, done);
+ cfree_cg_label_place(cg, equal);
+ (void)timeout_tmp;
+ cfree_cg_push_local(cg, result_tmp);
+ cfree_cg_push_int(cg, 2, b.id[CFREE_CG_BUILTIN_I32]);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
+ cfree_cg_label_place(cg, done);
+ cfree_cg_push_local(cg, result_tmp);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
+ break;
+ }
+ case WASM_INSN_MEMORY_ATOMIC_NOTIFY: {
+ CfreeCgMemAccess i32_mem = wasm_cg_mem(c, b, WASM_VAL_I32);
+ CfreeCgLocalAttrs attrs;
+ CfreeCgLocal count_tmp;
+ memset(&attrs, 0, sizeof attrs);
+ attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
+ count_tmp = cfree_cg_local(cg, b.id[CFREE_CG_BUILTIN_I32], attrs);
+ cfree_cg_push_local(cg, count_tmp);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, i32_mem);
+ wasm_cg_memory_check(c, cg, b, m, &rt, instance_local, &in);
+ cfree_cg_drop(cg);
+ cfree_cg_push_int(cg, 0, b.id[CFREE_CG_BUILTIN_I32]);
+ break;
+ }
+ case WASM_INSN_I32_LOAD:
+ case WASM_INSN_I64_LOAD:
+ case WASM_INSN_I32_LOAD8_S:
+ case WASM_INSN_I32_LOAD8_U:
+ case WASM_INSN_I32_LOAD16_S:
+ case WASM_INSN_I32_LOAD16_U:
+ case WASM_INSN_I64_LOAD8_S:
+ case WASM_INSN_I64_LOAD8_U:
+ case WASM_INSN_I64_LOAD16_S:
+ case WASM_INSN_I64_LOAD16_U:
+ case WASM_INSN_I64_LOAD32_S:
+ case WASM_INSN_I64_LOAD32_U: {
+ CfreeCgTypeId storage = wasm_load_storage_type(b, in.kind);
+ CfreeCgTypeId result =
+ wasm_cg_type(c, b, wasm_load_result_type(in.kind));
+ CfreeCgMemAccess mem;
+ memset(&mem, 0, sizeof mem);
+ mem.type = storage;
+ mem.align = in.align;
+ wasm_cg_memory_check(c, cg, b, m, &rt, instance_local, &in);
+ wasm_cg_memory_lvalue(cg, &rt, instance_local, in.memidx,
+ in.offset64);
+ cfree_cg_load(cg, mem);
+ if (storage != result) {
+ if (in.kind == WASM_INSN_I32_LOAD8_S ||
+ in.kind == WASM_INSN_I32_LOAD16_S ||
+ in.kind == WASM_INSN_I64_LOAD8_S ||
+ in.kind == WASM_INSN_I64_LOAD16_S ||
+ in.kind == WASM_INSN_I64_LOAD32_S)
+ cfree_cg_sext(cg, result);
+ else
+ cfree_cg_zext(cg, result);
+ }
+ break;
+ }
+ case WASM_INSN_I32_STORE:
+ case WASM_INSN_I64_STORE:
+ case WASM_INSN_I32_STORE8:
+ case WASM_INSN_I32_STORE16:
+ case WASM_INSN_I64_STORE8:
+ case WASM_INSN_I64_STORE16:
+ case WASM_INSN_I64_STORE32: {
+ CfreeCgTypeId storage = wasm_store_storage_type(b, in.kind);
+ CfreeCgTypeId value_type =
+ wasm_cg_type(c, b, wasm_store_value_type(in.kind));
+ CfreeCgMemAccess mem;
+ CfreeCgLocalAttrs attrs;
+ CfreeCgLocal addr_tmp, value_tmp;
+ WasmValType addr_vt =
+ m->memories[in.memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32;
+ memset(&mem, 0, sizeof mem);
+ mem.type = storage;
+ mem.align = in.align;
+ if (storage != value_type) cfree_cg_trunc(cg, storage);
+ memset(&attrs, 0, sizeof attrs);
+ attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
+ value_tmp = cfree_cg_local(cg, storage, attrs);
+ addr_tmp = cfree_cg_local(cg, wasm_cg_type(c, b, addr_vt), attrs);
+ cfree_cg_push_local(cg, value_tmp);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, mem);
+ wasm_cg_memory_check(c, cg, b, m, &rt, instance_local, &in);
+ cfree_cg_push_local(cg, addr_tmp);
+ cfree_cg_swap(cg);
+ cfree_cg_store(cg, wasm_cg_mem(c, b, addr_vt));
+ cfree_cg_push_local(cg, addr_tmp);
+ cfree_cg_load(cg, wasm_cg_mem(c, b, addr_vt));
+ wasm_cg_memory_lvalue(cg, &rt, instance_local, in.memidx,
+ in.offset64);
+ cfree_cg_push_local(cg, value_tmp);
+ cfree_cg_load(cg, mem);
+ cfree_cg_store(cg, mem);
+ break;
+ }
+ case WASM_INSN_I32_ADD:
+ case WASM_INSN_I64_ADD:
+ cfree_cg_int_binop(cg, CFREE_CG_INT_ADD, 0);
+ break;
+ case WASM_INSN_I32_SUB:
+ case WASM_INSN_I64_SUB:
+ cfree_cg_int_binop(cg, CFREE_CG_INT_SUB, 0);
+ break;
+ case WASM_INSN_I32_MUL:
+ case WASM_INSN_I64_MUL:
+ cfree_cg_int_binop(cg, CFREE_CG_INT_MUL, 0);
+ break;
+ case WASM_INSN_I32_DIV_S:
+ wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I32,
+ CFREE_CG_INT_SDIV);
+ break;
+ case WASM_INSN_I32_DIV_U:
+ wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I32,
+ CFREE_CG_INT_UDIV);
+ break;
+ case WASM_INSN_I32_REM_S:
+ wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I32,
+ CFREE_CG_INT_SREM);
+ break;
+ case WASM_INSN_I32_REM_U:
+ wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I32,
+ CFREE_CG_INT_UREM);
+ break;
+ case WASM_INSN_I64_DIV_S:
+ wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I64,
+ CFREE_CG_INT_SDIV);
+ break;
+ case WASM_INSN_I64_DIV_U:
+ wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I64,
+ CFREE_CG_INT_UDIV);
+ break;
+ case WASM_INSN_I64_REM_S:
+ wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I64,
+ CFREE_CG_INT_SREM);
+ break;
+ case WASM_INSN_I64_REM_U:
+ wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I64,
+ CFREE_CG_INT_UREM);
+ break;
+ case WASM_INSN_I32_AND:
+ case WASM_INSN_I64_AND:
+ cfree_cg_int_binop(cg, CFREE_CG_INT_AND, 0);
+ break;
+ case WASM_INSN_I32_OR:
+ case WASM_INSN_I64_OR:
+ cfree_cg_int_binop(cg, CFREE_CG_INT_OR, 0);
+ break;
+ case WASM_INSN_I32_XOR:
+ case WASM_INSN_I64_XOR:
+ cfree_cg_int_binop(cg, CFREE_CG_INT_XOR, 0);
+ break;
+ case WASM_INSN_I32_SHL:
+ case WASM_INSN_I64_SHL:
+ cfree_cg_int_binop(cg, CFREE_CG_INT_SHL, 0);
+ break;
+ case WASM_INSN_I32_SHR_S:
+ case WASM_INSN_I64_SHR_S:
+ cfree_cg_int_binop(cg, CFREE_CG_INT_ASHR, 0);
+ break;
+ case WASM_INSN_I32_SHR_U:
+ case WASM_INSN_I64_SHR_U:
+ cfree_cg_int_binop(cg, CFREE_CG_INT_LSHR, 0);
+ break;
+ case WASM_INSN_I32_ROTL:
+ wasm_cg_rotate(c, cg, b, WASM_VAL_I32, 0);
+ break;
+ case WASM_INSN_I32_ROTR:
+ wasm_cg_rotate(c, cg, b, WASM_VAL_I32, 1);
+ break;
+ case WASM_INSN_I64_ROTL:
+ wasm_cg_rotate(c, cg, b, WASM_VAL_I64, 0);
+ break;
+ case WASM_INSN_I64_ROTR:
+ wasm_cg_rotate(c, cg, b, WASM_VAL_I64, 1);
+ break;
+ case WASM_INSN_I32_CLZ:
+ case WASM_INSN_I64_CLZ:
+ cfree_cg_intrinsic(cg, CFREE_CG_INTRIN_CLZ, 1,
+ in.kind == WASM_INSN_I32_CLZ
+ ? b.id[CFREE_CG_BUILTIN_I32]
+ : b.id[CFREE_CG_BUILTIN_I64]);
+ break;
+ case WASM_INSN_I32_CTZ:
+ case WASM_INSN_I64_CTZ:
+ cfree_cg_intrinsic(cg, CFREE_CG_INTRIN_CTZ, 1,
+ in.kind == WASM_INSN_I32_CTZ
+ ? b.id[CFREE_CG_BUILTIN_I32]
+ : b.id[CFREE_CG_BUILTIN_I64]);
+ break;
+ case WASM_INSN_I32_POPCNT:
+ case WASM_INSN_I64_POPCNT:
+ cfree_cg_intrinsic(cg, CFREE_CG_INTRIN_POPCOUNT, 1,
+ in.kind == WASM_INSN_I32_POPCNT
+ ? b.id[CFREE_CG_BUILTIN_I32]
+ : b.id[CFREE_CG_BUILTIN_I64]);
+ break;
+ case WASM_INSN_I32_EQZ:
+ cfree_cg_push_int(cg, 0, b.id[CFREE_CG_BUILTIN_I32]);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_EQ);
+ cfree_cg_zext(cg, b.id[CFREE_CG_BUILTIN_I32]);
+ break;
+ case WASM_INSN_I64_EQZ:
+ cfree_cg_push_int(cg, 0, b.id[CFREE_CG_BUILTIN_I64]);
+ cfree_cg_int_cmp(cg, CFREE_CG_INT_EQ);
+ cfree_cg_zext(cg, b.id[CFREE_CG_BUILTIN_I32]);
+ break;
+ case WASM_INSN_I32_WRAP_I64:
+ cfree_cg_trunc(cg, b.id[CFREE_CG_BUILTIN_I32]);
+ break;
+ case WASM_INSN_I64_EXTEND_I32_S:
+ cfree_cg_sext(cg, b.id[CFREE_CG_BUILTIN_I64]);
+ break;
+ case WASM_INSN_I64_EXTEND_I32_U:
+ cfree_cg_zext(cg, b.id[CFREE_CG_BUILTIN_I64]);
+ break;
+ case WASM_INSN_I32_TRUNC_F32_S:
+ case WASM_INSN_I32_TRUNC_F64_S:
+ wasm_cg_checked_trunc(c, cg, b, &rt,
+ in.kind == WASM_INSN_I32_TRUNC_F32_S
+ ? WASM_VAL_F32
+ : WASM_VAL_F64,
+ WASM_VAL_I32, 0);
+ break;
+ case WASM_INSN_I32_TRUNC_F32_U:
+ case WASM_INSN_I32_TRUNC_F64_U:
+ wasm_cg_checked_trunc(c, cg, b, &rt,
+ in.kind == WASM_INSN_I32_TRUNC_F32_U
+ ? WASM_VAL_F32
+ : WASM_VAL_F64,
+ WASM_VAL_I32, 1);
+ break;
+ case WASM_INSN_I64_TRUNC_F32_S:
+ case WASM_INSN_I64_TRUNC_F64_S:
+ wasm_cg_checked_trunc(c, cg, b, &rt,
+ in.kind == WASM_INSN_I64_TRUNC_F32_S
+ ? WASM_VAL_F32
+ : WASM_VAL_F64,
+ WASM_VAL_I64, 0);
+ break;
+ case WASM_INSN_I64_TRUNC_F32_U:
+ case WASM_INSN_I64_TRUNC_F64_U:
+ wasm_cg_checked_trunc(c, cg, b, &rt,
+ in.kind == WASM_INSN_I64_TRUNC_F32_U
+ ? WASM_VAL_F32
+ : WASM_VAL_F64,
+ WASM_VAL_I64, 1);
+ break;
+ case WASM_INSN_F32_CONVERT_I32_S:
+ case WASM_INSN_F32_CONVERT_I64_S:
+ cfree_cg_sint_to_float(cg, b.id[CFREE_CG_BUILTIN_F32],
+ CFREE_CG_ROUND_DEFAULT);
+ break;
+ case WASM_INSN_F32_CONVERT_I32_U:
+ case WASM_INSN_F32_CONVERT_I64_U:
+ cfree_cg_uint_to_float(cg, b.id[CFREE_CG_BUILTIN_F32],
+ CFREE_CG_ROUND_DEFAULT);
+ break;
+ case WASM_INSN_F64_CONVERT_I32_S:
+ case WASM_INSN_F64_CONVERT_I64_S:
+ cfree_cg_sint_to_float(cg, b.id[CFREE_CG_BUILTIN_F64],
+ CFREE_CG_ROUND_DEFAULT);
+ break;
+ case WASM_INSN_F64_CONVERT_I32_U:
+ case WASM_INSN_F64_CONVERT_I64_U:
+ cfree_cg_uint_to_float(cg, b.id[CFREE_CG_BUILTIN_F64],
+ CFREE_CG_ROUND_DEFAULT);
+ break;
+ case WASM_INSN_F32_DEMOTE_F64:
+ cfree_cg_fptrunc(cg, b.id[CFREE_CG_BUILTIN_F32]);
+ break;
+ case WASM_INSN_F64_PROMOTE_F32:
+ cfree_cg_fpext(cg, b.id[CFREE_CG_BUILTIN_F64]);
+ break;
+ case WASM_INSN_I32_REINTERPRET_F32:
+ cfree_cg_bitcast(cg, b.id[CFREE_CG_BUILTIN_I32]);
+ break;
+ case WASM_INSN_I64_REINTERPRET_F64:
+ cfree_cg_bitcast(cg, b.id[CFREE_CG_BUILTIN_I64]);
+ break;
+ case WASM_INSN_F32_REINTERPRET_I32:
+ cfree_cg_bitcast(cg, b.id[CFREE_CG_BUILTIN_F32]);
+ break;
+ case WASM_INSN_F64_REINTERPRET_I64:
+ cfree_cg_bitcast(cg, b.id[CFREE_CG_BUILTIN_F64]);
+ break;
+ default: {
+ CfreeCgIntCmpOp cmp;
+ CfreeCgFpBinOp fp_bin;
+ CfreeCgFpCmpOp fp_cmp;
+ if (wasm_int_cmp_op(in.kind, &cmp)) {
+ cfree_cg_int_cmp(cg, cmp);
+ cfree_cg_zext(cg, b.id[CFREE_CG_BUILTIN_I32]);
+ } else if (wasm_fp_binop(in.kind, &fp_bin)) {
+ cfree_cg_fp_binop(cg, fp_bin, CFREE_CG_FP_NONE);
+ } else if (wasm_fp_cmp_op(in.kind, &fp_cmp)) {
+ cfree_cg_fp_cmp(cg, fp_cmp);
+ cfree_cg_zext(cg, b.id[CFREE_CG_BUILTIN_I32]);
+ } else {
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported instruction");
+ }
+ break;
+ }
+ }
+ }
+ if (f->nresults)
+ cfree_cg_ret(cg);
+ else
+ cfree_cg_ret_void(cg);
+ cfree_cg_func_end(cg);
+ }
+ cfree_cg_free(cg);
+}
+
diff --git a/lang/wasm/decode.c b/lang/wasm/decode.c
@@ -0,0 +1,1195 @@
+#include "wasm_internal.h"
+
+typedef struct BinReader {
+ CfreeCompiler* c;
+ const uint8_t* data;
+ size_t len;
+ size_t pos;
+ WasmModule* module;
+} BinReader;
+
+static uint8_t bin_u8(BinReader* r) {
+ if (r->pos >= r->len)
+ wasm_error(r->c, wasm_loc(0, 0), "wasm: unexpected end of file");
+ return r->data[r->pos++];
+}
+
+static uint32_t bin_uleb(BinReader* r) {
+ uint32_t result = 0, shift = 0;
+ uint32_t nbytes = 0;
+ for (;;) {
+ uint8_t b = bin_u8(r);
+ if (nbytes++ >= 5u || (shift == 28u && (b & 0xf0u)))
+ wasm_error(r->c, wasm_loc(0, 0), "wasm: invalid uleb128");
+ result |= (uint32_t)(b & 0x7fu) << shift;
+ if (!(b & 0x80u)) return result;
+ shift += 7u;
+ }
+}
+
+static uint64_t bin_uleb64(BinReader* r) {
+ uint64_t result = 0;
+ uint32_t shift = 0;
+ uint32_t nbytes = 0;
+ for (;;) {
+ uint8_t b = bin_u8(r);
+ if (nbytes++ >= 10u || (shift == 63u && (b & 0xfeu)))
+ wasm_error(r->c, wasm_loc(0, 0), "wasm: invalid uleb128");
+ result |= (uint64_t)(b & 0x7fu) << shift;
+ if (!(b & 0x80u)) return result;
+ shift += 7u;
+ }
+}
+
+static int64_t bin_sleb(BinReader* r, uint32_t bits) {
+ int64_t result = 0;
+ uint32_t shift = 0;
+ uint32_t max_bytes = (bits + 6u) / 7u;
+ uint32_t nbytes = 0;
+ uint8_t b;
+ do {
+ if (nbytes++ >= max_bytes)
+ wasm_error(r->c, wasm_loc(0, 0), "wasm: invalid sleb128");
+ b = bin_u8(r);
+ result |= (int64_t)(b & 0x7fu) << shift;
+ shift += 7u;
+ } while (b & 0x80u);
+ if (shift < bits && (b & 0x40u)) result |= -((int64_t)1 << shift);
+ return result;
+}
+
+static double bin_f32(BinReader* r) {
+ uint32_t bits = 0;
+ float f;
+ for (uint32_t i = 0; i < 4u; ++i) bits |= (uint32_t)bin_u8(r) << (i * 8u);
+ memcpy(&f, &bits, sizeof f);
+ return (double)f;
+}
+
+static double bin_f64(BinReader* r) {
+ uint64_t bits = 0;
+ double d;
+ for (uint32_t i = 0; i < 8u; ++i) bits |= (uint64_t)bin_u8(r) << (i * 8u);
+ memcpy(&d, &bits, sizeof d);
+ return d;
+}
+
+static void bin_memarg(BinReader* r, uint32_t* align, uint64_t* offset,
+ uint32_t* memidx) {
+ uint32_t a = bin_uleb(r);
+ *memidx = 0;
+ if (a >= 64u && a < 128u) {
+ *align = a - 64u;
+ *memidx = bin_uleb(r);
+ } else if (a < 64u) {
+ *align = a;
+ } else {
+ wasm_error(r->c, wasm_loc(0, 0), "wasm: bad memory alignment");
+ }
+ *offset = bin_uleb64(r);
+}
+
+static void bin_need(BinReader* r, size_t n) {
+ if (n > r->len || r->pos > r->len - n)
+ wasm_error(r->c, wasm_loc(0, 0), "wasm: section length out of bounds");
+}
+
+static char* bin_name(BinReader* r, CfreeHeap* h, uint32_t* len_out) {
+ uint32_t n = bin_uleb(r);
+ char* s;
+ bin_need(r, n);
+ s = wasm_strdup(h, (const char*)(r->data + r->pos), n);
+ if (!s) wasm_error(r->c, wasm_loc(0, 0), "wasm: out of memory");
+ r->pos += n;
+ if (len_out) *len_out = n;
+ return s;
+}
+
+static WasmValType bin_val_type(BinReader* r, int refs_ok) {
+ uint8_t b = bin_u8(r);
+ switch (b) {
+ case WASM_VAL_I32:
+ case WASM_VAL_I64:
+ case WASM_VAL_F32:
+ case WASM_VAL_F64:
+ return (WasmValType)b;
+ case WASM_VAL_FUNCREF:
+ case WASM_VAL_EXTERNREF:
+ if (refs_ok) return (WasmValType)b;
+ break;
+ default:
+ break;
+ }
+ wasm_error(r->c, wasm_loc(0, 0), "wasm: unsupported value type 0x%02x", b);
+ return WASM_VAL_I32;
+}
+
+void wasm_decode_binary(CfreeCompiler* c, const CfreeBytes* input,
+ WasmModule* out) {
+ BinReader r;
+ uint32_t nfunc_types = 0;
+ uint8_t last_id = 0;
+ memset(&r, 0, sizeof r);
+ r.c = c;
+ r.data = input->data;
+ r.len = input->len;
+ r.module = out;
+ if (r.len < 8 || memcmp(r.data, "\0asm\1\0\0\0", 8) != 0)
+ wasm_error(c, wasm_loc(0, 0), "wasm: bad magic or version");
+ r.pos = 8;
+ while (r.pos < r.len) {
+ uint8_t id = bin_u8(&r);
+ uint32_t size = bin_uleb(&r);
+ size_t end;
+ bin_need(&r, size);
+ end = r.pos + size;
+ if (id != 0 && id <= last_id)
+ wasm_error(c, wasm_loc(0, 0), "wasm: sections out of order");
+ if (id != 0) last_id = id;
+ if (id == 0) {
+ uint32_t name_len = bin_uleb(&r);
+ bin_need(&r, name_len);
+ if (name_len == 7u && memcmp(r.data + r.pos, "linking", 7) == 0)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: relocatable object metadata is not frontend input");
+ {
+ WasmCustom* cs = wasm_add_custom(c, out);
+ cs->name =
+ wasm_strdup(out->heap, (const char*)(r.data + r.pos), name_len);
+ if (!cs->name) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ r.pos += name_len;
+ if (strcmp(cs->name, "target_features") == 0 ||
+ strcmp(cs->name, "target-feature") == 0)
+ out->has_target_features = 1;
+ cs->len = (uint32_t)(end - r.pos);
+ if (cs->len) {
+ cs->data = (uint8_t*)out->heap->alloc(out->heap, cs->len, 1);
+ if (!cs->data) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ memcpy(cs->data, r.data + r.pos, cs->len);
+ }
+ }
+ r.pos = end;
+ continue;
+ }
+ if (id == 1) {
+ uint32_t i, count = bin_uleb(&r);
+ if (count > 64u) wasm_error(c, wasm_loc(0, 0), "wasm: too many types");
+ for (i = 0; i < count; ++i) {
+ WasmFuncType* t = wasm_add_type(c, out);
+ uint32_t j, nparam, nresult;
+ if (bin_u8(&r) != 0x60u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: expected function type");
+ nparam = bin_uleb(&r);
+ if (nparam > 16u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: too many parameters");
+ t->nparams = nparam;
+ for (j = 0; j < nparam; ++j) t->params[j] = bin_val_type(&r, 1);
+ nresult = bin_uleb(&r);
+ if (nresult > 1u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: multi-result unsupported");
+ t->nresults = nresult;
+ for (j = 0; j < nresult; ++j) t->results[j] = bin_val_type(&r, 1);
+ }
+ } else if (id == 2) {
+ uint32_t i, count = bin_uleb(&r);
+ for (i = 0; i < count; ++i) {
+ char* mod = bin_name(&r, out->heap, NULL);
+ char* name = bin_name(&r, out->heap, NULL);
+ uint8_t kind = bin_u8(&r);
+ if (kind == 0) {
+ uint32_t typeidx = bin_uleb(&r);
+ WasmFunc* f;
+ if (typeidx >= out->ntypes)
+ wasm_error(c, wasm_loc(0, 0), "wasm: bad import type index");
+ f = wasm_add_func(c, out);
+ f->is_import = 1;
+ f->import_module = mod;
+ f->import_name = name;
+ f->typeidx = typeidx;
+ f->has_typeidx = 1;
+ f->nparams = out->types[typeidx].nparams;
+ memcpy(f->params, out->types[typeidx].params,
+ sizeof(f->params[0]) * f->nparams);
+ f->nresults = out->types[typeidx].nresults;
+ memcpy(f->results, out->types[typeidx].results,
+ sizeof(f->results[0]) * f->nresults);
+ } else if (kind == 1) {
+ WasmTable* t = wasm_add_table(c, out);
+ t->is_import = 1;
+ t->import_module = mod;
+ t->import_name = name;
+ t->elem_type = bin_val_type(&r, 1);
+ {
+ uint32_t flags = bin_uleb(&r);
+ if (flags & ~1u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported table limits");
+ t->has_max = (flags & 1u) != 0;
+ t->min = bin_uleb(&r);
+ if (t->has_max) t->max = bin_uleb(&r);
+ }
+ } else if (kind == 2) {
+ uint32_t flags;
+ WasmMemory* mem = wasm_add_memory(c, out);
+ mem->is_import = 1;
+ mem->import_module = mod;
+ mem->import_name = name;
+ flags = bin_uleb(&r);
+ if (flags & ~7u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported memory limits");
+ mem->has_max = (flags & 1u) != 0;
+ mem->shared = (flags & 2u) != 0;
+ mem->is64 = (flags & 4u) != 0;
+ mem->min_pages = mem->is64 ? bin_uleb64(&r) : bin_uleb(&r);
+ if (mem->has_max)
+ mem->max_pages = mem->is64 ? bin_uleb64(&r) : bin_uleb(&r);
+ } else if (kind == 3) {
+ WasmGlobal* g = wasm_add_global(c, out);
+ g->is_import = 1;
+ g->import_module = mod;
+ g->import_name = name;
+ g->type = bin_val_type(&r, 0);
+ g->mutable_ = bin_u8(&r);
+ if (g->mutable_ > 1u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: bad global mutability");
+ } else {
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported import kind");
+ }
+ }
+ } else if (id == 3) {
+ uint32_t i, count = bin_uleb(&r);
+ if (count > 64u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: too many functions");
+ for (i = 0; i < count; ++i) {
+ uint32_t typeidx = bin_uleb(&r);
+ WasmFunc* f;
+ if (typeidx >= out->ntypes)
+ wasm_error(c, wasm_loc(0, 0), "wasm: bad function type index");
+ f = wasm_add_func(c, out);
+ f->typeidx = typeidx;
+ f->has_typeidx = 1;
+ f->nparams = out->types[typeidx].nparams;
+ memcpy(f->params, out->types[typeidx].params,
+ sizeof(f->params[0]) * f->nparams);
+ f->nresults = out->types[typeidx].nresults;
+ memcpy(f->results, out->types[typeidx].results,
+ sizeof(f->results[0]) * f->nresults);
+ nfunc_types++;
+ }
+ } else if (id == 5) {
+ uint32_t count = bin_uleb(&r);
+ for (uint32_t mi = 0; mi < count; ++mi) {
+ uint32_t flags = bin_uleb(&r);
+ WasmMemory* mem = wasm_add_memory(c, out);
+ if (flags & ~7u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported memory limits");
+ mem->has_max = (flags & 1u) != 0;
+ mem->shared = (flags & 2u) != 0;
+ mem->is64 = (flags & 4u) != 0;
+ mem->min_pages = mem->is64 ? bin_uleb64(&r) : bin_uleb(&r);
+ if (mem->has_max)
+ mem->max_pages = mem->is64 ? bin_uleb64(&r) : bin_uleb(&r);
+ wasm_memory_ensure(c, out, out->nmemories - 1u,
+ mem->min_pages * 65536u);
+ }
+ } else if (id == 4) {
+ uint32_t i, count = bin_uleb(&r);
+ for (i = 0; i < count; ++i) {
+ WasmTable* t = wasm_add_table(c, out);
+ uint32_t flags;
+ t->elem_type = bin_val_type(&r, 1);
+ flags = bin_uleb(&r);
+ if (flags & ~1u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported table limits");
+ t->has_max = (flags & 1u) != 0;
+ t->min = bin_uleb(&r);
+ if (t->has_max) t->max = bin_uleb(&r);
+ }
+ } else if (id == 6) {
+ uint32_t i, count = bin_uleb(&r);
+ for (i = 0; i < count; ++i) {
+ WasmGlobal* g = wasm_add_global(c, out);
+ uint8_t op;
+ g->type = bin_val_type(&r, 0);
+ g->mutable_ = bin_u8(&r);
+ if (g->mutable_ > 1u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: bad global mutability");
+ op = bin_u8(&r);
+ switch (op) {
+ case 0x41:
+ g->init.kind = WASM_INSN_I32_CONST;
+ g->init.imm = bin_sleb(&r, 32);
+ break;
+ case 0x42:
+ g->init.kind = WASM_INSN_I64_CONST;
+ g->init.imm = bin_sleb(&r, 64);
+ break;
+ case 0x43:
+ g->init.kind = WASM_INSN_F32_CONST;
+ g->init.fp = bin_f32(&r);
+ break;
+ case 0x44:
+ g->init.kind = WASM_INSN_F64_CONST;
+ g->init.fp = bin_f64(&r);
+ break;
+ default:
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: unsupported global initializer");
+ }
+ if (bin_u8(&r) != 0x0bu)
+ wasm_error(c, wasm_loc(0, 0), "wasm: malformed global initializer");
+ }
+ } else if (id == 7) {
+ uint32_t i, count = bin_uleb(&r);
+ for (i = 0; i < count; ++i) {
+ char* name;
+ uint8_t kind;
+ uint32_t idx;
+ WasmExport* ex;
+ name = bin_name(&r, out->heap, NULL);
+ kind = bin_u8(&r);
+ idx = bin_uleb(&r);
+ if (kind > 3u) wasm_error(c, wasm_loc(0, 0), "wasm: bad export kind");
+ ex = wasm_add_export(c, out);
+ ex->name = name;
+ ex->kind = kind;
+ ex->index = idx;
+ if (kind == 0 && idx < out->nfuncs) {
+ WasmFunc* f = &out->funcs[idx];
+ wasm_free_str(out->heap, &f->export_name);
+ f->export_name = wasm_strdup(out->heap, name, strlen(name));
+ if (!f->export_name)
+ wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ } else if (kind == 1 && idx < out->ntables) {
+ wasm_free_str(out->heap, &out->tables[idx].export_name);
+ out->tables[idx].export_name =
+ wasm_strdup(out->heap, name, strlen(name));
+ } else if (kind == 2 && idx < out->nmemories) {
+ wasm_free_str(out->heap, &out->memories[idx].export_name);
+ out->memories[idx].export_name =
+ wasm_strdup(out->heap, name, strlen(name));
+ } else if (kind == 3 && idx < out->nglobals) {
+ wasm_free_str(out->heap, &out->globals[idx].export_name);
+ out->globals[idx].export_name =
+ wasm_strdup(out->heap, name, strlen(name));
+ }
+ }
+ } else if (id == 8) {
+ out->has_start = 1;
+ out->start_func = bin_uleb(&r);
+ } else if (id == 9) {
+ uint32_t i, count = bin_uleb(&r);
+ for (i = 0; i < count; ++i) {
+ WasmElemSegment* e = wasm_add_elem(c, out);
+ uint32_t flags = bin_uleb(&r);
+ uint32_t n;
+ if (flags != 0)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: unsupported element segment kind");
+ e->tableidx = 0;
+ if (bin_u8(&r) != 0x41)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: expected i32.const element offset");
+ e->offset = bin_sleb(&r, 32);
+ if (bin_u8(&r) != 0x0b || e->offset < 0)
+ wasm_error(c, wasm_loc(0, 0), "wasm: bad element offset");
+ n = bin_uleb(&r);
+ if (n > 64u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: too many element functions");
+ for (uint32_t k = 0; k < n; ++k) e->funcs[e->nfuncs++] = bin_uleb(&r);
+ }
+ } else if (id == 10) {
+ uint32_t i, count = bin_uleb(&r);
+ uint32_t func_index = 0;
+ if (count != nfunc_types)
+ wasm_error(c, wasm_loc(0, 0), "wasm: function/code count mismatch");
+ for (i = 0; i < count; ++i) {
+ uint32_t body_size = bin_uleb(&r);
+ size_t body_end;
+ uint32_t local_groups, j;
+ uint32_t control_depth = 0;
+ int saw_body_end = 0;
+ WasmFunc* f;
+ while (func_index < out->nfuncs && out->funcs[func_index].is_import)
+ func_index++;
+ if (func_index >= out->nfuncs)
+ wasm_error(c, wasm_loc(0, 0), "wasm: code body without function");
+ f = &out->funcs[func_index++];
+ bin_need(&r, body_size);
+ body_end = r.pos + body_size;
+ local_groups = bin_uleb(&r);
+ for (j = 0; j < local_groups; ++j) {
+ uint32_t k, nlocals = bin_uleb(&r);
+ WasmValType vt = bin_val_type(&r, 1);
+ if (nlocals > 32u || f->nlocals > 32u - nlocals)
+ wasm_error(c, wasm_loc(0, 0), "wasm: too many locals");
+ for (k = 0; k < nlocals; ++k) f->locals[f->nlocals++] = vt;
+ }
+ while (r.pos < body_end) {
+ uint8_t op = bin_u8(&r);
+ if (op == 0x0bu) {
+ if (!control_depth) {
+ saw_body_end = 1;
+ break;
+ }
+ control_depth--;
+ wasm_func_add_insn(c, out, f, WASM_INSN_END, 0);
+ continue;
+ }
+ switch (op) {
+ case 0x00:
+ wasm_func_add_insn(c, out, f, WASM_INSN_UNREACHABLE, 0);
+ break;
+ case 0x01:
+ wasm_func_add_insn(c, out, f, WASM_INSN_NOP, 0);
+ break;
+ case 0x02:
+ if (bin_u8(&r) != 0x40u)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: block results are unsupported");
+ control_depth++;
+ wasm_func_add_insn(c, out, f, WASM_INSN_BLOCK, 0);
+ break;
+ case 0x03:
+ if (bin_u8(&r) != 0x40u)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: loop results are unsupported");
+ control_depth++;
+ wasm_func_add_insn(c, out, f, WASM_INSN_LOOP, 0);
+ break;
+ case 0x04:
+ if (bin_u8(&r) != 0x40u)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: if results are unsupported");
+ control_depth++;
+ wasm_func_add_insn(c, out, f, WASM_INSN_IF, 0);
+ break;
+ case 0x05:
+ wasm_func_add_insn(c, out, f, WASM_INSN_ELSE, 0);
+ break;
+ case 0x0c:
+ wasm_func_add_insn(c, out, f, WASM_INSN_BR, bin_uleb(&r));
+ break;
+ case 0x0d:
+ wasm_func_add_insn(c, out, f, WASM_INSN_BR_IF, bin_uleb(&r));
+ break;
+ case 0x0e: {
+ WasmInsn* in;
+ uint32_t n = bin_uleb(&r);
+ if (n >= 16u)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: too many br_table targets");
+ wasm_func_add_insn(c, out, f, WASM_INSN_BR_TABLE, 0);
+ in = &f->insns[f->ninsns - 1u];
+ for (uint32_t k = 0; k < n; ++k) in->targets[k] = bin_uleb(&r);
+ in->targets[n] = bin_uleb(&r);
+ in->ntargets = n + 1u;
+ break;
+ }
+ case 0x0f:
+ wasm_func_add_insn(c, out, f, WASM_INSN_RETURN, 0);
+ break;
+ case 0x1a:
+ wasm_func_add_insn(c, out, f, WASM_INSN_DROP, 0);
+ break;
+ case 0x1b:
+ wasm_func_add_insn(c, out, f, WASM_INSN_SELECT, 0);
+ break;
+ case 0x28:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_LOAD, ma, mo, mi); }
+ break;
+ case 0x29:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD, ma, mo, mi); }
+ break;
+ case 0x2c:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_LOAD8_S, ma, mo, mi); }
+ break;
+ case 0x2d:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_LOAD8_U, ma, mo, mi); }
+ break;
+ case 0x2e:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_LOAD16_S, ma, mo, mi); }
+ break;
+ case 0x2f:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_LOAD16_U, ma, mo, mi); }
+ break;
+ case 0x30:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD8_S, ma, mo, mi); }
+ break;
+ case 0x31:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD8_U, ma, mo, mi); }
+ break;
+ case 0x32:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD16_S, ma, mo, mi); }
+ break;
+ case 0x33:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD16_U, ma, mo, mi); }
+ break;
+ case 0x34:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD32_S, ma, mo, mi); }
+ break;
+ case 0x35:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD32_U, ma, mo, mi); }
+ break;
+ case 0x36:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_STORE, ma, mo, mi); }
+ break;
+ case 0x37:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_STORE, ma, mo, mi); }
+ break;
+ case 0x3a:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_STORE8, ma, mo, mi); }
+ break;
+ case 0x3b:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_STORE16, ma, mo, mi); }
+ break;
+ case 0x3c:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_STORE8, ma, mo, mi); }
+ break;
+ case 0x3d:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_STORE16, ma, mo, mi); }
+ break;
+ case 0x3e:
+ { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_STORE32, ma, mo, mi); }
+ break;
+ case 0x3f:
+ wasm_func_add_insn(c, out, f, WASM_INSN_MEMORY_SIZE, 0);
+ f->insns[f->ninsns - 1u].memidx = bin_uleb(&r);
+ break;
+ case 0x40:
+ wasm_func_add_insn(c, out, f, WASM_INSN_MEMORY_GROW, 0);
+ f->insns[f->ninsns - 1u].memidx = bin_uleb(&r);
+ break;
+ case 0x10:
+ wasm_func_add_insn(c, out, f, WASM_INSN_CALL, bin_uleb(&r));
+ break;
+ case 0x11: {
+ WasmInsn* in;
+ wasm_func_add_insn(c, out, f, WASM_INSN_CALL_INDIRECT,
+ bin_uleb(&r));
+ in = &f->insns[f->ninsns - 1u];
+ in->align = bin_uleb(&r);
+ break;
+ }
+ case 0x12:
+ wasm_func_add_insn(c, out, f, WASM_INSN_RETURN_CALL,
+ bin_uleb(&r));
+ break;
+ case 0x13: {
+ WasmInsn* in;
+ wasm_func_add_insn(c, out, f, WASM_INSN_RETURN_CALL_INDIRECT,
+ bin_uleb(&r));
+ in = &f->insns[f->ninsns - 1u];
+ in->align = bin_uleb(&r);
+ break;
+ }
+ case 0x14:
+ wasm_func_add_insn(c, out, f, WASM_INSN_CALL_REF,
+ bin_uleb(&r));
+ break;
+ case 0x15:
+ wasm_func_add_insn(c, out, f, WASM_INSN_RETURN_CALL_REF,
+ bin_uleb(&r));
+ break;
+ case 0x20:
+ wasm_func_add_insn(c, out, f, WASM_INSN_LOCAL_GET, bin_uleb(&r));
+ break;
+ case 0x21:
+ wasm_func_add_insn(c, out, f, WASM_INSN_LOCAL_SET, bin_uleb(&r));
+ break;
+ case 0x22:
+ wasm_func_add_insn(c, out, f, WASM_INSN_LOCAL_TEE, bin_uleb(&r));
+ break;
+ case 0x23:
+ wasm_func_add_insn(c, out, f, WASM_INSN_GLOBAL_GET, bin_uleb(&r));
+ break;
+ case 0x24:
+ wasm_func_add_insn(c, out, f, WASM_INSN_GLOBAL_SET, bin_uleb(&r));
+ break;
+ case 0x41:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_CONST,
+ bin_sleb(&r, 32));
+ break;
+ case 0x42:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_CONST,
+ bin_sleb(&r, 64));
+ break;
+ case 0x43:
+ wasm_func_add_fp_insn(c, out, f, WASM_INSN_F32_CONST,
+ bin_f32(&r));
+ break;
+ case 0x44:
+ wasm_func_add_fp_insn(c, out, f, WASM_INSN_F64_CONST,
+ bin_f64(&r));
+ break;
+ case 0xd0:
+ wasm_func_add_insn(c, out, f, WASM_INSN_REF_NULL,
+ bin_val_type(&r, 1));
+ break;
+ case 0xd1:
+ wasm_func_add_insn(c, out, f, WASM_INSN_REF_IS_NULL, 0);
+ break;
+ case 0xd2:
+ wasm_func_add_insn(c, out, f, WASM_INSN_REF_FUNC,
+ bin_uleb(&r));
+ break;
+ case 0x45:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_EQZ, 0);
+ break;
+ case 0x46:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_EQ, 0);
+ break;
+ case 0x47:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_NE, 0);
+ break;
+ case 0x48:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_LT_S, 0);
+ break;
+ case 0x49:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_LT_U, 0);
+ break;
+ case 0x4a:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_GT_S, 0);
+ break;
+ case 0x4b:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_GT_U, 0);
+ break;
+ case 0x4c:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_LE_S, 0);
+ break;
+ case 0x4d:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_LE_U, 0);
+ break;
+ case 0x4e:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_GE_S, 0);
+ break;
+ case 0x4f:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_GE_U, 0);
+ break;
+ case 0x50:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_EQZ, 0);
+ break;
+ case 0x51:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_EQ, 0);
+ break;
+ case 0x52:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_NE, 0);
+ break;
+ case 0x53:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_LT_S, 0);
+ break;
+ case 0x54:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_LT_U, 0);
+ break;
+ case 0x55:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_GT_S, 0);
+ break;
+ case 0x56:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_GT_U, 0);
+ break;
+ case 0x57:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_LE_S, 0);
+ break;
+ case 0x58:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_LE_U, 0);
+ break;
+ case 0x59:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_GE_S, 0);
+ break;
+ case 0x5a:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_GE_U, 0);
+ break;
+ case 0x6a:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_ADD, 0);
+ break;
+ case 0x6b:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_SUB, 0);
+ break;
+ case 0x6c:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_MUL, 0);
+ break;
+ case 0x6d:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_DIV_S, 0);
+ break;
+ case 0x6e:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_DIV_U, 0);
+ break;
+ case 0x6f:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_REM_S, 0);
+ break;
+ case 0x70:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_REM_U, 0);
+ break;
+ case 0x71:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_AND, 0);
+ break;
+ case 0x72:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_OR, 0);
+ break;
+ case 0x73:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_XOR, 0);
+ break;
+ case 0x74:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_SHL, 0);
+ break;
+ case 0x75:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_SHR_S, 0);
+ break;
+ case 0x76:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_SHR_U, 0);
+ break;
+ case 0x67:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_CLZ, 0);
+ break;
+ case 0x68:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_CTZ, 0);
+ break;
+ case 0x69:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_POPCNT, 0);
+ break;
+ case 0x77:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_ROTL, 0);
+ break;
+ case 0x78:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_ROTR, 0);
+ break;
+ case 0x79:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_CLZ, 0);
+ break;
+ case 0x7a:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_CTZ, 0);
+ break;
+ case 0x7b:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_POPCNT, 0);
+ break;
+ case 0x7c:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_ADD, 0);
+ break;
+ case 0x7d:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_SUB, 0);
+ break;
+ case 0x7e:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_MUL, 0);
+ break;
+ case 0x7f:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_DIV_S, 0);
+ break;
+ case 0x80:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_DIV_U, 0);
+ break;
+ case 0x81:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_REM_S, 0);
+ break;
+ case 0x82:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_REM_U, 0);
+ break;
+ case 0x83:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_AND, 0);
+ break;
+ case 0x84:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_OR, 0);
+ break;
+ case 0x85:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_XOR, 0);
+ break;
+ case 0x86:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_SHL, 0);
+ break;
+ case 0x87:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_SHR_S, 0);
+ break;
+ case 0x88:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_SHR_U, 0);
+ break;
+ case 0x89:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_ROTL, 0);
+ break;
+ case 0x8a:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_ROTR, 0);
+ break;
+ case 0x92:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F32_ADD, 0);
+ break;
+ case 0x93:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F32_SUB, 0);
+ break;
+ case 0x94:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F32_MUL, 0);
+ break;
+ case 0x95:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F32_DIV, 0);
+ break;
+ case 0xa0:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F64_ADD, 0);
+ break;
+ case 0xa1:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F64_SUB, 0);
+ break;
+ case 0xa2:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F64_MUL, 0);
+ break;
+ case 0xa3:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F64_DIV, 0);
+ break;
+ case 0x5b:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F32_EQ, 0);
+ break;
+ case 0x5c:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F32_NE, 0);
+ break;
+ case 0x5d:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F32_LT, 0);
+ break;
+ case 0x5e:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F32_GT, 0);
+ break;
+ case 0x5f:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F32_LE, 0);
+ break;
+ case 0x60:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F32_GE, 0);
+ break;
+ case 0x61:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F64_EQ, 0);
+ break;
+ case 0x62:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F64_NE, 0);
+ break;
+ case 0x63:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F64_LT, 0);
+ break;
+ case 0x64:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F64_GT, 0);
+ break;
+ case 0x65:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F64_LE, 0);
+ break;
+ case 0x66:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F64_GE, 0);
+ break;
+ case 0xa7:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_WRAP_I64, 0);
+ break;
+ case 0xa8:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_TRUNC_F32_S, 0);
+ break;
+ case 0xa9:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_TRUNC_F32_U, 0);
+ break;
+ case 0xaa:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_TRUNC_F64_S, 0);
+ break;
+ case 0xab:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_TRUNC_F64_U, 0);
+ break;
+ case 0xac:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_EXTEND_I32_S, 0);
+ break;
+ case 0xad:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_EXTEND_I32_U, 0);
+ break;
+ case 0xae:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_TRUNC_F32_S, 0);
+ break;
+ case 0xaf:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_TRUNC_F32_U, 0);
+ break;
+ case 0xb0:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_TRUNC_F64_S, 0);
+ break;
+ case 0xb1:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_TRUNC_F64_U, 0);
+ break;
+ case 0xb2:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F32_CONVERT_I32_S, 0);
+ break;
+ case 0xb3:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F32_CONVERT_I32_U, 0);
+ break;
+ case 0xb4:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F32_CONVERT_I64_S, 0);
+ break;
+ case 0xb5:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F32_CONVERT_I64_U, 0);
+ break;
+ case 0xb6:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F32_DEMOTE_F64, 0);
+ break;
+ case 0xb7:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F64_CONVERT_I32_S, 0);
+ break;
+ case 0xb8:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F64_CONVERT_I32_U, 0);
+ break;
+ case 0xb9:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F64_CONVERT_I64_S, 0);
+ break;
+ case 0xba:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F64_CONVERT_I64_U, 0);
+ break;
+ case 0xbb:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F64_PROMOTE_F32, 0);
+ break;
+ case 0xbc:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I32_REINTERPRET_F32, 0);
+ break;
+ case 0xbd:
+ wasm_func_add_insn(c, out, f, WASM_INSN_I64_REINTERPRET_F64, 0);
+ break;
+ case 0xbe:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F32_REINTERPRET_I32, 0);
+ break;
+ case 0xbf:
+ wasm_func_add_insn(c, out, f, WASM_INSN_F64_REINTERPRET_I64, 0);
+ break;
+ case 0xfe: {
+ uint32_t sub = bin_uleb(&r);
+ uint32_t ma, mi;
+ uint64_t mo;
+ switch (sub) {
+ case 0x00:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_MEMORY_ATOMIC_NOTIFY, ma,
+ mo, mi);
+ break;
+ case 0x01:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_ATOMIC_WAIT,
+ ma, mo, mi);
+ break;
+ case 0x02:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_ATOMIC_WAIT,
+ ma, mo, mi);
+ break;
+ case 0x03:
+ if (bin_u8(&r) != 0)
+ wasm_error(c, wasm_loc(0, 0), "wasm: bad atomic.fence");
+ wasm_func_add_insn(c, out, f, WASM_INSN_ATOMIC_FENCE, 0);
+ break;
+ case 0x10:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_ATOMIC_LOAD,
+ ma, mo, mi);
+ break;
+ case 0x11:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_ATOMIC_LOAD,
+ ma, mo, mi);
+ break;
+ case 0x12:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I32_ATOMIC_LOAD8_U, ma, mo,
+ mi);
+ break;
+ case 0x13:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I32_ATOMIC_LOAD16_U, ma, mo,
+ mi);
+ break;
+ case 0x14:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I64_ATOMIC_LOAD8_U, ma, mo,
+ mi);
+ break;
+ case 0x15:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I64_ATOMIC_LOAD16_U, ma, mo,
+ mi);
+ break;
+ case 0x16:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I64_ATOMIC_LOAD32_U, ma, mo,
+ mi);
+ break;
+ case 0x17:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_ATOMIC_STORE,
+ ma, mo, mi);
+ break;
+ case 0x18:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_ATOMIC_STORE,
+ ma, mo, mi);
+ break;
+ case 0x19:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I32_ATOMIC_STORE8, ma, mo,
+ mi);
+ break;
+ case 0x1a:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I32_ATOMIC_STORE16, ma, mo,
+ mi);
+ break;
+ case 0x1b:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I64_ATOMIC_STORE8, ma, mo,
+ mi);
+ break;
+ case 0x1c:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I64_ATOMIC_STORE16, ma, mo,
+ mi);
+ break;
+ case 0x1d:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I64_ATOMIC_STORE32, ma, mo,
+ mi);
+ break;
+ case 0x1e:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I32_ATOMIC_RMW_ADD, ma, mo,
+ mi);
+ break;
+ case 0x1f:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I64_ATOMIC_RMW_ADD, ma, mo,
+ mi);
+ break;
+ case 0x25:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I32_ATOMIC_RMW_SUB, ma, mo,
+ mi);
+ break;
+ case 0x26:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I64_ATOMIC_RMW_SUB, ma, mo,
+ mi);
+ break;
+ case 0x2c:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I32_ATOMIC_RMW_AND, ma, mo,
+ mi);
+ break;
+ case 0x2d:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I64_ATOMIC_RMW_AND, ma, mo,
+ mi);
+ break;
+ case 0x33:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I32_ATOMIC_RMW_OR, ma, mo,
+ mi);
+ break;
+ case 0x34:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I64_ATOMIC_RMW_OR, ma, mo,
+ mi);
+ break;
+ case 0x3a:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I32_ATOMIC_RMW_XOR, ma, mo,
+ mi);
+ break;
+ case 0x3b:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I64_ATOMIC_RMW_XOR, ma, mo,
+ mi);
+ break;
+ case 0x41:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I32_ATOMIC_RMW_XCHG, ma, mo,
+ mi);
+ break;
+ case 0x42:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I64_ATOMIC_RMW_XCHG, ma, mo,
+ mi);
+ break;
+ case 0x48:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I32_ATOMIC_RMW_CMPXCHG, ma,
+ mo, mi);
+ break;
+ case 0x49:
+ bin_memarg(&r, &ma, &mo, &mi);
+ wasm_func_add_mem_insn(c, out, f,
+ WASM_INSN_I64_ATOMIC_RMW_CMPXCHG, ma,
+ mo, mi);
+ break;
+ default:
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: unsupported threads opcode 0x%x", sub);
+ }
+ break;
+ }
+ default:
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported opcode 0x%02x",
+ op);
+ }
+ }
+ if (!saw_body_end)
+ wasm_error(c, wasm_loc(0, 0), "wasm: function body missing end");
+ r.pos = body_end;
+ }
+ } else if (id == 11) {
+ uint32_t i, count = bin_uleb(&r);
+ for (i = 0; i < count; ++i) {
+ uint32_t flags = bin_uleb(&r);
+ int64_t offset = 0;
+ uint32_t memidx = 0;
+ uint32_t n;
+ if (flags == 2u)
+ memidx = bin_uleb(&r);
+ else if (flags != 0)
+ wasm_error(c, wasm_loc(0, 0), "wasm: passive data unsupported");
+ {
+ uint8_t op = bin_u8(&r);
+ if (op == 0x41)
+ offset = bin_sleb(&r, 32);
+ else if (op == 0x42)
+ offset = bin_sleb(&r, 64);
+ else
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: expected const data offset");
+ }
+ if (bin_u8(&r) != 0x0b || offset < 0)
+ wasm_error(c, wasm_loc(0, 0), "wasm: bad data offset");
+ n = bin_uleb(&r);
+ bin_need(&r, n);
+ wasm_memory_ensure(c, out, memidx, (uint64_t)offset + n);
+ memcpy(out->memories[memidx].data + (uint32_t)offset, r.data + r.pos,
+ n);
+ if ((uint64_t)offset + n > out->memories[memidx].data_init_len)
+ out->memories[memidx].data_init_len = (uint64_t)offset + n;
+ r.pos += n;
+ }
+ } else if (id == 12) {
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: data count sections are unsupported");
+ } else {
+ r.pos = end;
+ }
+ if (r.pos != end)
+ wasm_error(c, wasm_loc(0, 0), "wasm: malformed section length");
+ }
+}
+
+int wasm_is_binary(const CfreeBytes* input) {
+ return input->len >= 4u && input->data[0] == 0x00 && input->data[1] == 0x61 &&
+ input->data[2] == 0x73 && input->data[3] == 0x6d;
+}
diff --git a/lang/wasm/encode.c b/lang/wasm/encode.c
@@ -0,0 +1,778 @@
+#include "wasm_internal.h"
+
+static void write_byte(CfreeWriter* w, uint8_t b) { w->write(w, &b, 1); }
+
+static void write_uleb(CfreeWriter* w, uint64_t v) {
+ do {
+ uint8_t b = (uint8_t)(v & 0x7fu);
+ v >>= 7u;
+ if (v) b |= 0x80u;
+ write_byte(w, b);
+ } while (v);
+}
+
+static void write_sleb(CfreeWriter* w, int64_t v) {
+ int more = 1;
+ while (more) {
+ uint8_t b = (uint8_t)(v & 0x7f);
+ int sign = b & 0x40;
+ v >>= 7;
+ if ((v == 0 && !sign) || (v == -1 && sign))
+ more = 0;
+ else
+ b |= 0x80u;
+ write_byte(w, b);
+ }
+}
+
+static void write_f32(CfreeWriter* w, double value) {
+ float f = (float)value;
+ uint32_t bits;
+ memcpy(&bits, &f, sizeof bits);
+ for (uint32_t i = 0; i < 4u; ++i) write_byte(w, (uint8_t)(bits >> (i * 8u)));
+}
+
+static void write_f64(CfreeWriter* w, double value) {
+ uint64_t bits;
+ memcpy(&bits, &value, sizeof bits);
+ for (uint32_t i = 0; i < 8u; ++i) write_byte(w, (uint8_t)(bits >> (i * 8u)));
+}
+
+static void write_name(CfreeWriter* w, const char* s) {
+ size_t n = strlen(s);
+ write_uleb(w, n);
+ w->write(w, s, n);
+}
+
+static void encode_section(CfreeHeap* h, CfreeWriter* out, uint8_t id,
+ void (*fn)(CfreeWriter*, const WasmModule*),
+ const WasmModule* m) {
+ CfreeWriter* tmp = NULL;
+ size_t len;
+ const uint8_t* bytes;
+ if (cfree_writer_mem(h, &tmp) != CFREE_OK) return;
+ fn(tmp, m);
+ bytes = cfree_writer_mem_bytes(tmp, &len);
+ write_byte(out, id);
+ write_uleb(out, len);
+ out->write(out, bytes, len);
+ cfree_writer_close(tmp);
+}
+
+static void enc_type(CfreeWriter* w, const WasmModule* m) {
+ uint32_t i, j;
+ write_uleb(w, m->ntypes);
+ for (i = 0; i < m->ntypes; ++i) {
+ const WasmFuncType* f = &m->types[i];
+ write_byte(w, 0x60);
+ write_uleb(w, f->nparams);
+ for (j = 0; j < f->nparams; ++j) write_byte(w, (uint8_t)f->params[j]);
+ write_uleb(w, f->nresults);
+ for (j = 0; j < f->nresults; ++j) write_byte(w, (uint8_t)f->results[j]);
+ }
+}
+
+static void write_memory_limits(CfreeWriter* w, const WasmMemory* mem) {
+ uint32_t flags = (mem->has_max ? 1u : 0u) | (mem->shared ? 2u : 0u) |
+ (mem->is64 ? 4u : 0u);
+ write_uleb(w, flags);
+ write_uleb(w, mem->min_pages);
+ if (mem->has_max) write_uleb(w, mem->max_pages);
+}
+
+static void write_limits(CfreeWriter* w, uint32_t min, uint32_t max,
+ int has_max) {
+ write_uleb(w, has_max ? 1 : 0);
+ write_uleb(w, min);
+ if (has_max) write_uleb(w, max);
+}
+
+static void enc_import(CfreeWriter* w, const WasmModule* m) {
+ uint32_t i, n = 0;
+ for (i = 0; i < m->nfuncs; ++i)
+ if (m->funcs[i].is_import) n++;
+ for (i = 0; i < m->ntables; ++i)
+ if (m->tables[i].is_import) n++;
+ for (i = 0; i < m->nmemories; ++i)
+ if (m->memories[i].is_import) n++;
+ for (i = 0; i < m->nglobals; ++i)
+ if (m->globals[i].is_import) n++;
+ write_uleb(w, n);
+ for (i = 0; i < m->nfuncs; ++i) {
+ const WasmFunc* f = &m->funcs[i];
+ if (!f->is_import) continue;
+ write_name(w, f->import_module ? f->import_module : "");
+ write_name(w, f->import_name ? f->import_name : "");
+ write_byte(w, 0);
+ write_uleb(w, f->typeidx);
+ }
+ for (i = 0; i < m->ntables; ++i) {
+ const WasmTable* t = &m->tables[i];
+ if (!t->is_import) continue;
+ write_name(w, t->import_module ? t->import_module : "");
+ write_name(w, t->import_name ? t->import_name : "");
+ write_byte(w, 1);
+ write_byte(w, (uint8_t)t->elem_type);
+ write_limits(w, t->min, t->max, t->has_max);
+ }
+ for (i = 0; i < m->nmemories; ++i) {
+ const WasmMemory* mem = &m->memories[i];
+ if (!mem->is_import) continue;
+ write_name(w, mem->import_module ? mem->import_module : "");
+ write_name(w, mem->import_name ? mem->import_name : "");
+ write_byte(w, 2);
+ write_memory_limits(w, mem);
+ }
+ for (i = 0; i < m->nglobals; ++i) {
+ const WasmGlobal* g = &m->globals[i];
+ if (!g->is_import) continue;
+ write_name(w, g->import_module ? g->import_module : "");
+ write_name(w, g->import_name ? g->import_name : "");
+ write_byte(w, 3);
+ write_byte(w, (uint8_t)g->type);
+ write_byte(w, g->mutable_);
+ }
+}
+
+static int wasm_module_has_imports(const WasmModule* m) {
+ uint32_t i;
+ for (i = 0; i < m->nfuncs; ++i)
+ if (m->funcs[i].is_import) return 1;
+ for (i = 0; i < m->ntables; ++i)
+ if (m->tables[i].is_import) return 1;
+ for (i = 0; i < m->nmemories; ++i)
+ if (m->memories[i].is_import) return 1;
+ for (i = 0; i < m->nglobals; ++i)
+ if (m->globals[i].is_import) return 1;
+ return 0;
+}
+
+static void enc_func(CfreeWriter* w, const WasmModule* m) {
+ uint32_t i;
+ uint32_t n = 0;
+ for (i = 0; i < m->nfuncs; ++i)
+ if (!m->funcs[i].is_import) n++;
+ write_uleb(w, n);
+ for (i = 0; i < m->nfuncs; ++i)
+ if (!m->funcs[i].is_import) write_uleb(w, m->funcs[i].typeidx);
+}
+
+static void enc_export(CfreeWriter* w, const WasmModule* m) {
+ uint32_t i;
+ write_uleb(w, m->nexports);
+ for (i = 0; i < m->nexports; ++i) {
+ write_name(w, m->exports[i].name ? m->exports[i].name : "");
+ write_byte(w, m->exports[i].kind);
+ write_uleb(w, m->exports[i].index);
+ }
+}
+
+static void enc_memory(CfreeWriter* w, const WasmModule* m) {
+ uint32_t i, n = 0;
+ for (i = 0; i < m->nmemories; ++i)
+ if (!m->memories[i].is_import) n++;
+ write_uleb(w, n);
+ for (i = 0; i < m->nmemories; ++i)
+ if (!m->memories[i].is_import) write_memory_limits(w, &m->memories[i]);
+}
+
+static uint8_t wasm_opcode(uint8_t kind);
+
+static void enc_table(CfreeWriter* w, const WasmModule* m) {
+ uint32_t i, n = 0;
+ for (i = 0; i < m->ntables; ++i)
+ if (!m->tables[i].is_import) n++;
+ write_uleb(w, n);
+ for (i = 0; i < m->ntables; ++i) {
+ const WasmTable* t = &m->tables[i];
+ if (t->is_import) continue;
+ write_byte(w, (uint8_t)t->elem_type);
+ write_limits(w, t->min, t->max, t->has_max);
+ }
+}
+
+static void enc_global(CfreeWriter* w, const WasmModule* m) {
+ uint32_t i, n = 0;
+ for (i = 0; i < m->nglobals; ++i)
+ if (!m->globals[i].is_import) n++;
+ write_uleb(w, n);
+ for (i = 0; i < m->nglobals; ++i) {
+ const WasmGlobal* g = &m->globals[i];
+ if (g->is_import) continue;
+ write_byte(w, (uint8_t)g->type);
+ write_byte(w, g->mutable_);
+ write_byte(w, wasm_opcode(g->init.kind));
+ if (g->init.kind == WASM_INSN_I32_CONST ||
+ g->init.kind == WASM_INSN_I64_CONST)
+ write_sleb(w, g->init.imm);
+ else if (g->init.kind == WASM_INSN_F32_CONST)
+ write_f32(w, g->init.fp);
+ else if (g->init.kind == WASM_INSN_F64_CONST)
+ write_f64(w, g->init.fp);
+ write_byte(w, 0x0b);
+ }
+}
+
+static uint8_t wasm_opcode(uint8_t kind) {
+ switch (kind) {
+ case WASM_INSN_UNREACHABLE:
+ return 0x00;
+ case WASM_INSN_NOP:
+ return 0x01;
+ case WASM_INSN_BLOCK:
+ return 0x02;
+ case WASM_INSN_LOOP:
+ return 0x03;
+ case WASM_INSN_IF:
+ return 0x04;
+ case WASM_INSN_ELSE:
+ return 0x05;
+ case WASM_INSN_END:
+ return 0x0b;
+ case WASM_INSN_BR:
+ return 0x0c;
+ case WASM_INSN_BR_IF:
+ return 0x0d;
+ case WASM_INSN_BR_TABLE:
+ return 0x0e;
+ case WASM_INSN_I32_CONST:
+ return 0x41;
+ case WASM_INSN_I64_CONST:
+ return 0x42;
+ case WASM_INSN_F32_CONST:
+ return 0x43;
+ case WASM_INSN_F64_CONST:
+ return 0x44;
+ case WASM_INSN_LOCAL_GET:
+ return 0x20;
+ case WASM_INSN_LOCAL_SET:
+ return 0x21;
+ case WASM_INSN_LOCAL_TEE:
+ return 0x22;
+ case WASM_INSN_CALL:
+ return 0x10;
+ case WASM_INSN_CALL_INDIRECT:
+ return 0x11;
+ case WASM_INSN_RETURN_CALL:
+ return 0x12;
+ case WASM_INSN_RETURN_CALL_INDIRECT:
+ return 0x13;
+ case WASM_INSN_CALL_REF:
+ return 0x14;
+ case WASM_INSN_RETURN_CALL_REF:
+ return 0x15;
+ case WASM_INSN_REF_NULL:
+ return 0xd0;
+ case WASM_INSN_REF_IS_NULL:
+ return 0xd1;
+ case WASM_INSN_REF_FUNC:
+ return 0xd2;
+ case WASM_INSN_GLOBAL_GET:
+ return 0x23;
+ case WASM_INSN_GLOBAL_SET:
+ return 0x24;
+ case WASM_INSN_RETURN:
+ return 0x0f;
+ case WASM_INSN_DROP:
+ return 0x1a;
+ case WASM_INSN_SELECT:
+ return 0x1b;
+ case WASM_INSN_I32_LOAD:
+ return 0x28;
+ case WASM_INSN_I64_LOAD:
+ return 0x29;
+ case WASM_INSN_I32_LOAD8_S:
+ return 0x2c;
+ case WASM_INSN_I32_LOAD8_U:
+ return 0x2d;
+ case WASM_INSN_I32_LOAD16_S:
+ return 0x2e;
+ case WASM_INSN_I32_LOAD16_U:
+ return 0x2f;
+ case WASM_INSN_I64_LOAD8_S:
+ return 0x30;
+ case WASM_INSN_I64_LOAD8_U:
+ return 0x31;
+ case WASM_INSN_I64_LOAD16_S:
+ return 0x32;
+ case WASM_INSN_I64_LOAD16_U:
+ return 0x33;
+ case WASM_INSN_I64_LOAD32_S:
+ return 0x34;
+ case WASM_INSN_I64_LOAD32_U:
+ return 0x35;
+ case WASM_INSN_I32_STORE:
+ return 0x36;
+ case WASM_INSN_I64_STORE:
+ return 0x37;
+ case WASM_INSN_I32_STORE8:
+ return 0x3a;
+ case WASM_INSN_I32_STORE16:
+ return 0x3b;
+ case WASM_INSN_I64_STORE8:
+ return 0x3c;
+ case WASM_INSN_I64_STORE16:
+ return 0x3d;
+ case WASM_INSN_I64_STORE32:
+ return 0x3e;
+ case WASM_INSN_MEMORY_SIZE:
+ return 0x3f;
+ case WASM_INSN_MEMORY_GROW:
+ return 0x40;
+ case WASM_INSN_I32_EQZ:
+ return 0x45;
+ case WASM_INSN_I32_EQ:
+ return 0x46;
+ case WASM_INSN_I32_NE:
+ return 0x47;
+ case WASM_INSN_I32_LT_S:
+ return 0x48;
+ case WASM_INSN_I32_LT_U:
+ return 0x49;
+ case WASM_INSN_I32_GT_S:
+ return 0x4a;
+ case WASM_INSN_I32_GT_U:
+ return 0x4b;
+ case WASM_INSN_I32_LE_S:
+ return 0x4c;
+ case WASM_INSN_I32_LE_U:
+ return 0x4d;
+ case WASM_INSN_I32_GE_S:
+ return 0x4e;
+ case WASM_INSN_I32_GE_U:
+ return 0x4f;
+ case WASM_INSN_I64_EQZ:
+ return 0x50;
+ case WASM_INSN_I64_EQ:
+ return 0x51;
+ case WASM_INSN_I64_NE:
+ return 0x52;
+ case WASM_INSN_I64_LT_S:
+ return 0x53;
+ case WASM_INSN_I64_LT_U:
+ return 0x54;
+ case WASM_INSN_I64_GT_S:
+ return 0x55;
+ case WASM_INSN_I64_GT_U:
+ return 0x56;
+ case WASM_INSN_I64_LE_S:
+ return 0x57;
+ case WASM_INSN_I64_LE_U:
+ return 0x58;
+ case WASM_INSN_I64_GE_S:
+ return 0x59;
+ case WASM_INSN_I64_GE_U:
+ return 0x5a;
+ case WASM_INSN_I32_ADD:
+ return 0x6a;
+ case WASM_INSN_I32_SUB:
+ return 0x6b;
+ case WASM_INSN_I32_MUL:
+ return 0x6c;
+ case WASM_INSN_I32_DIV_S:
+ return 0x6d;
+ case WASM_INSN_I32_DIV_U:
+ return 0x6e;
+ case WASM_INSN_I32_REM_S:
+ return 0x6f;
+ case WASM_INSN_I32_REM_U:
+ return 0x70;
+ case WASM_INSN_I32_AND:
+ return 0x71;
+ case WASM_INSN_I32_OR:
+ return 0x72;
+ case WASM_INSN_I32_XOR:
+ return 0x73;
+ case WASM_INSN_I32_SHL:
+ return 0x74;
+ case WASM_INSN_I32_SHR_S:
+ return 0x75;
+ case WASM_INSN_I32_SHR_U:
+ return 0x76;
+ case WASM_INSN_I32_CLZ:
+ return 0x67;
+ case WASM_INSN_I32_CTZ:
+ return 0x68;
+ case WASM_INSN_I32_POPCNT:
+ return 0x69;
+ case WASM_INSN_I32_ROTL:
+ return 0x77;
+ case WASM_INSN_I32_ROTR:
+ return 0x78;
+ case WASM_INSN_I64_CLZ:
+ return 0x79;
+ case WASM_INSN_I64_CTZ:
+ return 0x7a;
+ case WASM_INSN_I64_POPCNT:
+ return 0x7b;
+ case WASM_INSN_I64_ADD:
+ return 0x7c;
+ case WASM_INSN_I64_SUB:
+ return 0x7d;
+ case WASM_INSN_I64_MUL:
+ return 0x7e;
+ case WASM_INSN_I64_DIV_S:
+ return 0x7f;
+ case WASM_INSN_I64_DIV_U:
+ return 0x80;
+ case WASM_INSN_I64_REM_S:
+ return 0x81;
+ case WASM_INSN_I64_REM_U:
+ return 0x82;
+ case WASM_INSN_I64_AND:
+ return 0x83;
+ case WASM_INSN_I64_OR:
+ return 0x84;
+ case WASM_INSN_I64_XOR:
+ return 0x85;
+ case WASM_INSN_I64_SHL:
+ return 0x86;
+ case WASM_INSN_I64_SHR_S:
+ return 0x87;
+ case WASM_INSN_I64_SHR_U:
+ return 0x88;
+ case WASM_INSN_I64_ROTL:
+ return 0x89;
+ case WASM_INSN_I64_ROTR:
+ return 0x8a;
+ case WASM_INSN_F32_ADD:
+ return 0x92;
+ case WASM_INSN_F32_SUB:
+ return 0x93;
+ case WASM_INSN_F32_MUL:
+ return 0x94;
+ case WASM_INSN_F32_DIV:
+ return 0x95;
+ case WASM_INSN_F64_ADD:
+ return 0xa0;
+ case WASM_INSN_F64_SUB:
+ return 0xa1;
+ case WASM_INSN_F64_MUL:
+ return 0xa2;
+ case WASM_INSN_F64_DIV:
+ return 0xa3;
+ case WASM_INSN_F32_EQ:
+ return 0x5b;
+ case WASM_INSN_F32_NE:
+ return 0x5c;
+ case WASM_INSN_F32_LT:
+ return 0x5d;
+ case WASM_INSN_F32_GT:
+ return 0x5e;
+ case WASM_INSN_F32_LE:
+ return 0x5f;
+ case WASM_INSN_F32_GE:
+ return 0x60;
+ case WASM_INSN_F64_EQ:
+ return 0x61;
+ case WASM_INSN_F64_NE:
+ return 0x62;
+ case WASM_INSN_F64_LT:
+ return 0x63;
+ case WASM_INSN_F64_GT:
+ return 0x64;
+ case WASM_INSN_F64_LE:
+ return 0x65;
+ case WASM_INSN_F64_GE:
+ return 0x66;
+ case WASM_INSN_I32_WRAP_I64:
+ return 0xa7;
+ case WASM_INSN_I32_TRUNC_F32_S:
+ return 0xa8;
+ case WASM_INSN_I32_TRUNC_F32_U:
+ return 0xa9;
+ case WASM_INSN_I32_TRUNC_F64_S:
+ return 0xaa;
+ case WASM_INSN_I32_TRUNC_F64_U:
+ return 0xab;
+ case WASM_INSN_I64_EXTEND_I32_S:
+ return 0xac;
+ case WASM_INSN_I64_EXTEND_I32_U:
+ return 0xad;
+ case WASM_INSN_I64_TRUNC_F32_S:
+ return 0xae;
+ case WASM_INSN_I64_TRUNC_F32_U:
+ return 0xaf;
+ case WASM_INSN_I64_TRUNC_F64_S:
+ return 0xb0;
+ case WASM_INSN_I64_TRUNC_F64_U:
+ return 0xb1;
+ case WASM_INSN_F32_CONVERT_I32_S:
+ return 0xb2;
+ case WASM_INSN_F32_CONVERT_I32_U:
+ return 0xb3;
+ case WASM_INSN_F32_CONVERT_I64_S:
+ return 0xb4;
+ case WASM_INSN_F32_CONVERT_I64_U:
+ return 0xb5;
+ case WASM_INSN_F32_DEMOTE_F64:
+ return 0xb6;
+ case WASM_INSN_F64_CONVERT_I32_S:
+ return 0xb7;
+ case WASM_INSN_F64_CONVERT_I32_U:
+ return 0xb8;
+ case WASM_INSN_F64_CONVERT_I64_S:
+ return 0xb9;
+ case WASM_INSN_F64_CONVERT_I64_U:
+ return 0xba;
+ case WASM_INSN_F64_PROMOTE_F32:
+ return 0xbb;
+ case WASM_INSN_I32_REINTERPRET_F32:
+ return 0xbc;
+ case WASM_INSN_I64_REINTERPRET_F64:
+ return 0xbd;
+ case WASM_INSN_F32_REINTERPRET_I32:
+ return 0xbe;
+ case WASM_INSN_F64_REINTERPRET_I64:
+ return 0xbf;
+ }
+ return 0;
+}
+
+static uint32_t wasm_threads_subopcode(uint8_t kind) {
+ switch (kind) {
+ case WASM_INSN_MEMORY_ATOMIC_NOTIFY:
+ return 0x00;
+ case WASM_INSN_I32_ATOMIC_WAIT:
+ return 0x01;
+ case WASM_INSN_I64_ATOMIC_WAIT:
+ return 0x02;
+ case WASM_INSN_ATOMIC_FENCE:
+ return 0x03;
+ case WASM_INSN_I32_ATOMIC_LOAD:
+ return 0x10;
+ case WASM_INSN_I64_ATOMIC_LOAD:
+ return 0x11;
+ case WASM_INSN_I32_ATOMIC_LOAD8_U:
+ return 0x12;
+ case WASM_INSN_I32_ATOMIC_LOAD16_U:
+ return 0x13;
+ case WASM_INSN_I64_ATOMIC_LOAD8_U:
+ return 0x14;
+ case WASM_INSN_I64_ATOMIC_LOAD16_U:
+ return 0x15;
+ case WASM_INSN_I64_ATOMIC_LOAD32_U:
+ return 0x16;
+ case WASM_INSN_I32_ATOMIC_STORE:
+ return 0x17;
+ case WASM_INSN_I64_ATOMIC_STORE:
+ return 0x18;
+ case WASM_INSN_I32_ATOMIC_STORE8:
+ return 0x19;
+ case WASM_INSN_I32_ATOMIC_STORE16:
+ return 0x1a;
+ case WASM_INSN_I64_ATOMIC_STORE8:
+ return 0x1b;
+ case WASM_INSN_I64_ATOMIC_STORE16:
+ return 0x1c;
+ case WASM_INSN_I64_ATOMIC_STORE32:
+ return 0x1d;
+ case WASM_INSN_I32_ATOMIC_RMW_ADD:
+ return 0x1e;
+ case WASM_INSN_I64_ATOMIC_RMW_ADD:
+ return 0x1f;
+ case WASM_INSN_I32_ATOMIC_RMW_SUB:
+ return 0x25;
+ case WASM_INSN_I64_ATOMIC_RMW_SUB:
+ return 0x26;
+ case WASM_INSN_I32_ATOMIC_RMW_AND:
+ return 0x2c;
+ case WASM_INSN_I64_ATOMIC_RMW_AND:
+ return 0x2d;
+ case WASM_INSN_I32_ATOMIC_RMW_OR:
+ return 0x33;
+ case WASM_INSN_I64_ATOMIC_RMW_OR:
+ return 0x34;
+ case WASM_INSN_I32_ATOMIC_RMW_XOR:
+ return 0x3a;
+ case WASM_INSN_I64_ATOMIC_RMW_XOR:
+ return 0x3b;
+ case WASM_INSN_I32_ATOMIC_RMW_XCHG:
+ return 0x41;
+ case WASM_INSN_I64_ATOMIC_RMW_XCHG:
+ return 0x42;
+ case WASM_INSN_I32_ATOMIC_RMW_CMPXCHG:
+ return 0x48;
+ case WASM_INSN_I64_ATOMIC_RMW_CMPXCHG:
+ return 0x49;
+ default:
+ return UINT32_MAX;
+ }
+}
+
+static void enc_code(CfreeWriter* w, const WasmModule* m) {
+ uint32_t i, j;
+ uint32_t n = 0;
+ for (i = 0; i < m->nfuncs; ++i)
+ if (!m->funcs[i].is_import) n++;
+ write_uleb(w, n);
+ for (i = 0; i < m->nfuncs; ++i) {
+ CfreeWriter* body = NULL;
+ size_t len;
+ const uint8_t* bytes;
+ if (cfree_writer_mem(m->heap, &body) != CFREE_OK) continue;
+ if (m->funcs[i].is_import) {
+ cfree_writer_close(body);
+ continue;
+ }
+ if (m->funcs[i].nlocals) {
+ uint32_t group_count = 0;
+ WasmValType prev = 0;
+ for (j = 0; j < m->funcs[i].nlocals; ++j) {
+ if (j == 0 || m->funcs[i].locals[j] != prev) {
+ group_count++;
+ prev = m->funcs[i].locals[j];
+ }
+ }
+ write_uleb(body, group_count);
+ for (j = 0; j < m->funcs[i].nlocals;) {
+ uint32_t k = j + 1u;
+ while (k < m->funcs[i].nlocals &&
+ m->funcs[i].locals[k] == m->funcs[i].locals[j])
+ k++;
+ write_uleb(body, k - j);
+ write_byte(body, (uint8_t)m->funcs[i].locals[j]);
+ j = k;
+ }
+ } else {
+ write_uleb(body, 0);
+ }
+ for (j = 0; j < m->funcs[i].ninsns; ++j) {
+ WasmInsn in = m->funcs[i].insns[j];
+ uint8_t op = wasm_opcode(in.kind);
+ uint32_t threads_op = wasm_threads_subopcode(in.kind);
+ if (threads_op != UINT32_MAX) {
+ write_byte(body, 0xfe);
+ write_uleb(body, threads_op);
+ } else {
+ write_byte(body, op);
+ }
+ if (in.kind == WASM_INSN_ATOMIC_FENCE) {
+ write_byte(body, 0);
+ } else if (in.kind == WASM_INSN_BLOCK || in.kind == WASM_INSN_LOOP ||
+ in.kind == WASM_INSN_IF)
+ write_byte(body, 0x40);
+ else if (in.kind == WASM_INSN_I32_CONST || in.kind == WASM_INSN_I64_CONST)
+ write_sleb(body, in.imm);
+ else if (in.kind == WASM_INSN_F32_CONST)
+ write_f32(body, in.fp);
+ else if (in.kind == WASM_INSN_F64_CONST)
+ write_f64(body, in.fp);
+ else if (in.kind == WASM_INSN_LOCAL_GET ||
+ in.kind == WASM_INSN_LOCAL_SET ||
+ in.kind == WASM_INSN_LOCAL_TEE ||
+ in.kind == WASM_INSN_GLOBAL_GET ||
+ in.kind == WASM_INSN_GLOBAL_SET || in.kind == WASM_INSN_CALL ||
+ in.kind == WASM_INSN_RETURN_CALL ||
+ in.kind == WASM_INSN_CALL_REF ||
+ in.kind == WASM_INSN_RETURN_CALL_REF ||
+ in.kind == WASM_INSN_REF_NULL ||
+ in.kind == WASM_INSN_REF_FUNC ||
+ in.kind == WASM_INSN_BR || in.kind == WASM_INSN_BR_IF)
+ write_uleb(body, (uint64_t)in.imm);
+ else if (in.kind == WASM_INSN_CALL_INDIRECT ||
+ in.kind == WASM_INSN_RETURN_CALL_INDIRECT) {
+ write_uleb(body, (uint64_t)in.imm);
+ write_uleb(body, in.align);
+ } else if (in.kind == WASM_INSN_BR_TABLE) {
+ write_uleb(body, in.ntargets ? in.ntargets - 1u : 0u);
+ for (uint32_t k = 0; k < in.ntargets; ++k)
+ write_uleb(body, in.targets[k]);
+ } else if (wasm_insn_is_mem(in.kind)) {
+ write_uleb(body, in.memidx ? in.align + 64u : in.align);
+ if (in.memidx) write_uleb(body, in.memidx);
+ write_uleb(body, in.offset64);
+ } else if (in.kind == WASM_INSN_MEMORY_SIZE ||
+ in.kind == WASM_INSN_MEMORY_GROW) {
+ write_uleb(body, in.memidx);
+ }
+ }
+ write_byte(body, 0x0b);
+ bytes = cfree_writer_mem_bytes(body, &len);
+ write_uleb(w, len);
+ w->write(w, bytes, len);
+ cfree_writer_close(body);
+ }
+}
+
+static void enc_data(CfreeWriter* w, const WasmModule* m) {
+ uint32_t i, n = 0;
+ for (i = 0; i < m->nmemories; ++i)
+ if (m->memories[i].data_init_len) n++;
+ write_uleb(w, n);
+ for (i = 0; i < m->nmemories; ++i) {
+ const WasmMemory* mem = &m->memories[i];
+ if (!mem->data_init_len) continue;
+ write_uleb(w, i ? 2u : 0u);
+ if (i) write_uleb(w, i);
+ write_byte(w, mem->is64 ? 0x42 : 0x41);
+ write_sleb(w, 0);
+ write_byte(w, 0x0b);
+ write_uleb(w, mem->data_init_len);
+ w->write(w, mem->data, (size_t)mem->data_init_len);
+ }
+}
+
+static void enc_elem(CfreeWriter* w, const WasmModule* m) {
+ uint32_t i, j;
+ write_uleb(w, m->nelems);
+ for (i = 0; i < m->nelems; ++i) {
+ const WasmElemSegment* e = &m->elems[i];
+ write_uleb(w, 0);
+ write_byte(w, 0x41);
+ write_sleb(w, e->offset);
+ write_byte(w, 0x0b);
+ write_uleb(w, e->nfuncs);
+ for (j = 0; j < e->nfuncs; ++j) write_uleb(w, e->funcs[j]);
+ }
+}
+
+static void enc_start(CfreeWriter* w, const WasmModule* m) {
+ write_uleb(w, m->start_func);
+}
+
+static void encode_custom(CfreeHeap* h, CfreeWriter* out,
+ const WasmCustom* cs) {
+ CfreeWriter* tmp = NULL;
+ size_t len;
+ const uint8_t* bytes;
+ if (cfree_writer_mem(h, &tmp) != CFREE_OK) return;
+ write_name(tmp, cs->name ? cs->name : "");
+ if (cs->len) tmp->write(tmp, cs->data, cs->len);
+ bytes = cfree_writer_mem_bytes(tmp, &len);
+ write_byte(out, 0);
+ write_uleb(out, len);
+ out->write(out, bytes, len);
+ cfree_writer_close(tmp);
+}
+
+void wasm_encode(CfreeCompiler* c, const WasmModule* m,
+ CfreeWriter* out) {
+ static const uint8_t magic[] = {0x00, 0x61, 0x73, 0x6d,
+ 0x01, 0x00, 0x00, 0x00};
+ CfreeHeap* h = cfree_compiler_context(c)->heap;
+ out->write(out, magic, sizeof magic);
+ for (uint32_t i = 0; i < m->ncustoms; ++i)
+ encode_custom(h, out, &m->customs[i]);
+ encode_section(h, out, 1, enc_type, m);
+ if (wasm_module_has_imports(m)) encode_section(h, out, 2, enc_import, m);
+ encode_section(h, out, 3, enc_func, m);
+ if (m->ntables) encode_section(h, out, 4, enc_table, m);
+ {
+ int has_defined_memory = 0;
+ for (uint32_t i = 0; i < m->nmemories; ++i)
+ if (!m->memories[i].is_import) has_defined_memory = 1;
+ if (has_defined_memory) encode_section(h, out, 5, enc_memory, m);
+ }
+ if (m->nglobals) encode_section(h, out, 6, enc_global, m);
+ encode_section(h, out, 7, enc_export, m);
+ if (m->has_start) encode_section(h, out, 8, enc_start, m);
+ if (m->nelems) encode_section(h, out, 9, enc_elem, m);
+ encode_section(h, out, 10, enc_code, m);
+ {
+ int has_data = 0;
+ for (uint32_t i = 0; i < m->nmemories; ++i)
+ if (m->memories[i].data_init_len) has_data = 1;
+ if (has_data) encode_section(h, out, 11, enc_data, m);
+ }
+}
diff --git a/lang/wasm/insn.c b/lang/wasm/insn.c
@@ -0,0 +1,409 @@
+#include "wasm_internal.h"
+
+int wasm_is_num_type(WasmValType vt) {
+ return vt == WASM_VAL_I32 || vt == WASM_VAL_I64 || vt == WASM_VAL_F32 ||
+ vt == WASM_VAL_F64;
+}
+
+int wasm_is_ref_type(WasmValType vt) {
+ return vt == WASM_VAL_FUNCREF || vt == WASM_VAL_EXTERNREF;
+}
+
+int wasm_is_frontend_value_type(WasmValType vt) {
+ return wasm_is_num_type(vt) || vt == WASM_VAL_FUNCREF;
+}
+
+int wasm_feature_enabled(const WasmModule* m, WasmFeatureSet feature) {
+ return (m->features & (uint32_t)feature) != 0;
+}
+
+void wasm_require_feature(CfreeCompiler* c, const WasmModule* m,
+ WasmFeatureSet feature,
+ const char* feature_name,
+ const char* what) {
+ if (!wasm_feature_enabled(m, feature))
+ wasm_error(c, wasm_loc(0, 0), "wasm: %s requires %s", what,
+ feature_name);
+}
+
+
+int wasm_insn_is_load(WasmInsnKind kind) {
+ return kind == WASM_INSN_I32_LOAD || kind == WASM_INSN_I64_LOAD ||
+ kind == WASM_INSN_I32_LOAD8_S || kind == WASM_INSN_I32_LOAD8_U ||
+ kind == WASM_INSN_I32_LOAD16_S || kind == WASM_INSN_I32_LOAD16_U ||
+ kind == WASM_INSN_I64_LOAD8_S || kind == WASM_INSN_I64_LOAD8_U ||
+ kind == WASM_INSN_I64_LOAD16_S || kind == WASM_INSN_I64_LOAD16_U ||
+ kind == WASM_INSN_I64_LOAD32_S || kind == WASM_INSN_I64_LOAD32_U;
+}
+
+int wasm_insn_is_store(WasmInsnKind kind) {
+ return kind == WASM_INSN_I32_STORE || kind == WASM_INSN_I64_STORE ||
+ kind == WASM_INSN_I32_STORE8 || kind == WASM_INSN_I32_STORE16 ||
+ kind == WASM_INSN_I64_STORE8 || kind == WASM_INSN_I64_STORE16 ||
+ kind == WASM_INSN_I64_STORE32;
+}
+
+int wasm_insn_is_atomic_load(WasmInsnKind kind) {
+ return kind == WASM_INSN_I32_ATOMIC_LOAD ||
+ kind == WASM_INSN_I64_ATOMIC_LOAD ||
+ kind == WASM_INSN_I32_ATOMIC_LOAD8_U ||
+ kind == WASM_INSN_I32_ATOMIC_LOAD16_U ||
+ kind == WASM_INSN_I64_ATOMIC_LOAD8_U ||
+ kind == WASM_INSN_I64_ATOMIC_LOAD16_U ||
+ kind == WASM_INSN_I64_ATOMIC_LOAD32_U;
+}
+
+int wasm_insn_is_atomic_store(WasmInsnKind kind) {
+ return kind == WASM_INSN_I32_ATOMIC_STORE ||
+ kind == WASM_INSN_I64_ATOMIC_STORE ||
+ kind == WASM_INSN_I32_ATOMIC_STORE8 ||
+ kind == WASM_INSN_I32_ATOMIC_STORE16 ||
+ kind == WASM_INSN_I64_ATOMIC_STORE8 ||
+ kind == WASM_INSN_I64_ATOMIC_STORE16 ||
+ kind == WASM_INSN_I64_ATOMIC_STORE32;
+}
+
+int wasm_insn_is_atomic_rmw(WasmInsnKind kind) {
+ return kind == WASM_INSN_I32_ATOMIC_RMW_ADD ||
+ kind == WASM_INSN_I64_ATOMIC_RMW_ADD ||
+ kind == WASM_INSN_I32_ATOMIC_RMW_SUB ||
+ kind == WASM_INSN_I64_ATOMIC_RMW_SUB ||
+ kind == WASM_INSN_I32_ATOMIC_RMW_AND ||
+ kind == WASM_INSN_I64_ATOMIC_RMW_AND ||
+ kind == WASM_INSN_I32_ATOMIC_RMW_OR ||
+ kind == WASM_INSN_I64_ATOMIC_RMW_OR ||
+ kind == WASM_INSN_I32_ATOMIC_RMW_XOR ||
+ kind == WASM_INSN_I64_ATOMIC_RMW_XOR ||
+ kind == WASM_INSN_I32_ATOMIC_RMW_XCHG ||
+ kind == WASM_INSN_I64_ATOMIC_RMW_XCHG;
+}
+
+int wasm_insn_is_atomic_cmpxchg(WasmInsnKind kind) {
+ return kind == WASM_INSN_I32_ATOMIC_RMW_CMPXCHG ||
+ kind == WASM_INSN_I64_ATOMIC_RMW_CMPXCHG;
+}
+
+int wasm_insn_is_atomic_wait_notify(WasmInsnKind kind) {
+ return kind == WASM_INSN_I32_ATOMIC_WAIT ||
+ kind == WASM_INSN_I64_ATOMIC_WAIT ||
+ kind == WASM_INSN_MEMORY_ATOMIC_NOTIFY;
+}
+
+int wasm_insn_is_atomic_mem(WasmInsnKind kind) {
+ return wasm_insn_is_atomic_load(kind) || wasm_insn_is_atomic_store(kind) ||
+ wasm_insn_is_atomic_rmw(kind) || wasm_insn_is_atomic_cmpxchg(kind) ||
+ wasm_insn_is_atomic_wait_notify(kind);
+}
+
+int wasm_insn_is_mem(WasmInsnKind kind) {
+ return wasm_insn_is_load(kind) || wasm_insn_is_store(kind) ||
+ wasm_insn_is_atomic_mem(kind);
+}
+
+WasmValType wasm_func_local_type(const WasmFunc* f, uint32_t index) {
+ if (index < f->nparams) return f->params[index];
+ return f->locals[index - f->nparams];
+}
+
+uint32_t wasm_mem_width(uint8_t kind) {
+ switch (kind) {
+ case WASM_INSN_I32_LOAD8_S:
+ case WASM_INSN_I32_LOAD8_U:
+ case WASM_INSN_I64_LOAD8_S:
+ case WASM_INSN_I64_LOAD8_U:
+ case WASM_INSN_I32_STORE8:
+ case WASM_INSN_I64_STORE8:
+ case WASM_INSN_I32_ATOMIC_LOAD8_U:
+ case WASM_INSN_I64_ATOMIC_LOAD8_U:
+ case WASM_INSN_I32_ATOMIC_STORE8:
+ case WASM_INSN_I64_ATOMIC_STORE8:
+ return 1;
+ case WASM_INSN_I32_LOAD16_S:
+ case WASM_INSN_I32_LOAD16_U:
+ case WASM_INSN_I64_LOAD16_S:
+ case WASM_INSN_I64_LOAD16_U:
+ case WASM_INSN_I32_STORE16:
+ case WASM_INSN_I64_STORE16:
+ case WASM_INSN_I32_ATOMIC_LOAD16_U:
+ case WASM_INSN_I64_ATOMIC_LOAD16_U:
+ case WASM_INSN_I32_ATOMIC_STORE16:
+ case WASM_INSN_I64_ATOMIC_STORE16:
+ return 2;
+ case WASM_INSN_I32_LOAD:
+ case WASM_INSN_I64_LOAD32_S:
+ case WASM_INSN_I64_LOAD32_U:
+ case WASM_INSN_I32_STORE:
+ case WASM_INSN_I64_STORE32:
+ case WASM_INSN_I32_ATOMIC_LOAD:
+ case WASM_INSN_I64_ATOMIC_LOAD32_U:
+ case WASM_INSN_I32_ATOMIC_STORE:
+ case WASM_INSN_I64_ATOMIC_STORE32:
+ case WASM_INSN_I32_ATOMIC_RMW_ADD:
+ case WASM_INSN_I32_ATOMIC_RMW_SUB:
+ case WASM_INSN_I32_ATOMIC_RMW_AND:
+ case WASM_INSN_I32_ATOMIC_RMW_OR:
+ case WASM_INSN_I32_ATOMIC_RMW_XOR:
+ case WASM_INSN_I32_ATOMIC_RMW_XCHG:
+ case WASM_INSN_I32_ATOMIC_RMW_CMPXCHG:
+ case WASM_INSN_I32_ATOMIC_WAIT:
+ case WASM_INSN_MEMORY_ATOMIC_NOTIFY:
+ return 4;
+ default:
+ return 8;
+ }
+}
+
+
+int wasm_int_cmp_op(uint8_t kind, CfreeCgIntCmpOp* out) {
+ switch (kind) {
+ case WASM_INSN_I32_EQ:
+ case WASM_INSN_I64_EQ:
+ *out = CFREE_CG_INT_EQ;
+ return 1;
+ case WASM_INSN_I32_NE:
+ case WASM_INSN_I64_NE:
+ *out = CFREE_CG_INT_NE;
+ return 1;
+ case WASM_INSN_I32_LT_S:
+ case WASM_INSN_I64_LT_S:
+ *out = CFREE_CG_INT_LT_S;
+ return 1;
+ case WASM_INSN_I32_LT_U:
+ case WASM_INSN_I64_LT_U:
+ *out = CFREE_CG_INT_LT_U;
+ return 1;
+ case WASM_INSN_I32_GT_S:
+ case WASM_INSN_I64_GT_S:
+ *out = CFREE_CG_INT_GT_S;
+ return 1;
+ case WASM_INSN_I32_GT_U:
+ case WASM_INSN_I64_GT_U:
+ *out = CFREE_CG_INT_GT_U;
+ return 1;
+ case WASM_INSN_I32_LE_S:
+ case WASM_INSN_I64_LE_S:
+ *out = CFREE_CG_INT_LE_S;
+ return 1;
+ case WASM_INSN_I32_LE_U:
+ case WASM_INSN_I64_LE_U:
+ *out = CFREE_CG_INT_LE_U;
+ return 1;
+ case WASM_INSN_I32_GE_S:
+ case WASM_INSN_I64_GE_S:
+ *out = CFREE_CG_INT_GE_S;
+ return 1;
+ case WASM_INSN_I32_GE_U:
+ case WASM_INSN_I64_GE_U:
+ *out = CFREE_CG_INT_GE_U;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+WasmValType wasm_load_result_type(uint8_t kind) {
+ switch (kind) {
+ case WASM_INSN_I64_LOAD:
+ case WASM_INSN_I64_LOAD8_S:
+ case WASM_INSN_I64_LOAD8_U:
+ case WASM_INSN_I64_LOAD16_S:
+ case WASM_INSN_I64_LOAD16_U:
+ case WASM_INSN_I64_LOAD32_S:
+ case WASM_INSN_I64_LOAD32_U:
+ case WASM_INSN_I64_ATOMIC_LOAD:
+ case WASM_INSN_I64_ATOMIC_LOAD8_U:
+ case WASM_INSN_I64_ATOMIC_LOAD16_U:
+ case WASM_INSN_I64_ATOMIC_LOAD32_U:
+ return WASM_VAL_I64;
+ default:
+ return WASM_VAL_I32;
+ }
+}
+
+WasmValType wasm_store_value_type(uint8_t kind) {
+ switch (kind) {
+ case WASM_INSN_I64_STORE:
+ case WASM_INSN_I64_STORE8:
+ case WASM_INSN_I64_STORE16:
+ case WASM_INSN_I64_STORE32:
+ case WASM_INSN_I64_ATOMIC_STORE:
+ case WASM_INSN_I64_ATOMIC_STORE8:
+ case WASM_INSN_I64_ATOMIC_STORE16:
+ case WASM_INSN_I64_ATOMIC_STORE32:
+ return WASM_VAL_I64;
+ default:
+ return WASM_VAL_I32;
+ }
+}
+
+WasmValType wasm_atomic_value_type(uint8_t kind) {
+ switch (kind) {
+ case WASM_INSN_I64_ATOMIC_LOAD:
+ case WASM_INSN_I64_ATOMIC_LOAD8_U:
+ case WASM_INSN_I64_ATOMIC_LOAD16_U:
+ case WASM_INSN_I64_ATOMIC_LOAD32_U:
+ case WASM_INSN_I64_ATOMIC_STORE:
+ case WASM_INSN_I64_ATOMIC_STORE8:
+ case WASM_INSN_I64_ATOMIC_STORE16:
+ case WASM_INSN_I64_ATOMIC_STORE32:
+ case WASM_INSN_I64_ATOMIC_RMW_ADD:
+ case WASM_INSN_I64_ATOMIC_RMW_SUB:
+ case WASM_INSN_I64_ATOMIC_RMW_AND:
+ case WASM_INSN_I64_ATOMIC_RMW_OR:
+ case WASM_INSN_I64_ATOMIC_RMW_XOR:
+ case WASM_INSN_I64_ATOMIC_RMW_XCHG:
+ case WASM_INSN_I64_ATOMIC_RMW_CMPXCHG:
+ case WASM_INSN_I64_ATOMIC_WAIT:
+ return WASM_VAL_I64;
+ default:
+ return WASM_VAL_I32;
+ }
+}
+
+CfreeCgAtomicOp wasm_atomic_rmw_op(uint8_t kind) {
+ switch (kind) {
+ case WASM_INSN_I32_ATOMIC_RMW_ADD:
+ case WASM_INSN_I64_ATOMIC_RMW_ADD:
+ return CFREE_CG_ATOMIC_ADD;
+ case WASM_INSN_I32_ATOMIC_RMW_SUB:
+ case WASM_INSN_I64_ATOMIC_RMW_SUB:
+ return CFREE_CG_ATOMIC_SUB;
+ case WASM_INSN_I32_ATOMIC_RMW_AND:
+ case WASM_INSN_I64_ATOMIC_RMW_AND:
+ return CFREE_CG_ATOMIC_AND;
+ case WASM_INSN_I32_ATOMIC_RMW_OR:
+ case WASM_INSN_I64_ATOMIC_RMW_OR:
+ return CFREE_CG_ATOMIC_OR;
+ case WASM_INSN_I32_ATOMIC_RMW_XOR:
+ case WASM_INSN_I64_ATOMIC_RMW_XOR:
+ return CFREE_CG_ATOMIC_XOR;
+ default:
+ return CFREE_CG_ATOMIC_XCHG;
+ }
+}
+
+int wasm_int_unop_kind(uint8_t kind, WasmValType* vt) {
+ if (kind == WASM_INSN_I32_CLZ || kind == WASM_INSN_I32_CTZ ||
+ kind == WASM_INSN_I32_POPCNT) {
+ *vt = WASM_VAL_I32;
+ return 1;
+ }
+ if (kind == WASM_INSN_I64_CLZ || kind == WASM_INSN_I64_CTZ ||
+ kind == WASM_INSN_I64_POPCNT) {
+ *vt = WASM_VAL_I64;
+ return 1;
+ }
+ return 0;
+}
+
+int wasm_fp_binop_kind(uint8_t kind, WasmValType* vt) {
+ if (kind == WASM_INSN_F32_ADD || kind == WASM_INSN_F32_SUB ||
+ kind == WASM_INSN_F32_MUL || kind == WASM_INSN_F32_DIV) {
+ *vt = WASM_VAL_F32;
+ return 1;
+ }
+ if (kind == WASM_INSN_F64_ADD || kind == WASM_INSN_F64_SUB ||
+ kind == WASM_INSN_F64_MUL || kind == WASM_INSN_F64_DIV) {
+ *vt = WASM_VAL_F64;
+ return 1;
+ }
+ return 0;
+}
+
+int wasm_fp_cmp_kind(uint8_t kind, WasmValType* vt) {
+ if (kind == WASM_INSN_F32_EQ || kind == WASM_INSN_F32_NE ||
+ kind == WASM_INSN_F32_LT || kind == WASM_INSN_F32_GT ||
+ kind == WASM_INSN_F32_LE || kind == WASM_INSN_F32_GE) {
+ *vt = WASM_VAL_F32;
+ return 1;
+ }
+ if (kind == WASM_INSN_F64_EQ || kind == WASM_INSN_F64_NE ||
+ kind == WASM_INSN_F64_LT || kind == WASM_INSN_F64_GT ||
+ kind == WASM_INSN_F64_LE || kind == WASM_INSN_F64_GE) {
+ *vt = WASM_VAL_F64;
+ return 1;
+ }
+ return 0;
+}
+
+int wasm_conversion_kind(uint8_t kind, WasmValType* src,
+ WasmValType* dst) {
+ switch (kind) {
+ case WASM_INSN_I32_WRAP_I64:
+ *src = WASM_VAL_I64;
+ *dst = WASM_VAL_I32;
+ return 1;
+ case WASM_INSN_I64_EXTEND_I32_S:
+ case WASM_INSN_I64_EXTEND_I32_U:
+ *src = WASM_VAL_I32;
+ *dst = WASM_VAL_I64;
+ return 1;
+ case WASM_INSN_I32_TRUNC_F32_S:
+ case WASM_INSN_I32_TRUNC_F32_U:
+ *src = WASM_VAL_F32;
+ *dst = WASM_VAL_I32;
+ return 1;
+ case WASM_INSN_I32_TRUNC_F64_S:
+ case WASM_INSN_I32_TRUNC_F64_U:
+ *src = WASM_VAL_F64;
+ *dst = WASM_VAL_I32;
+ return 1;
+ case WASM_INSN_I64_TRUNC_F32_S:
+ case WASM_INSN_I64_TRUNC_F32_U:
+ *src = WASM_VAL_F32;
+ *dst = WASM_VAL_I64;
+ return 1;
+ case WASM_INSN_I64_TRUNC_F64_S:
+ case WASM_INSN_I64_TRUNC_F64_U:
+ *src = WASM_VAL_F64;
+ *dst = WASM_VAL_I64;
+ return 1;
+ case WASM_INSN_F32_CONVERT_I32_S:
+ case WASM_INSN_F32_CONVERT_I32_U:
+ *src = WASM_VAL_I32;
+ *dst = WASM_VAL_F32;
+ return 1;
+ case WASM_INSN_F32_CONVERT_I64_S:
+ case WASM_INSN_F32_CONVERT_I64_U:
+ *src = WASM_VAL_I64;
+ *dst = WASM_VAL_F32;
+ return 1;
+ case WASM_INSN_F64_CONVERT_I32_S:
+ case WASM_INSN_F64_CONVERT_I32_U:
+ *src = WASM_VAL_I32;
+ *dst = WASM_VAL_F64;
+ return 1;
+ case WASM_INSN_F64_CONVERT_I64_S:
+ case WASM_INSN_F64_CONVERT_I64_U:
+ *src = WASM_VAL_I64;
+ *dst = WASM_VAL_F64;
+ return 1;
+ case WASM_INSN_F32_DEMOTE_F64:
+ *src = WASM_VAL_F64;
+ *dst = WASM_VAL_F32;
+ return 1;
+ case WASM_INSN_F64_PROMOTE_F32:
+ *src = WASM_VAL_F32;
+ *dst = WASM_VAL_F64;
+ return 1;
+ case WASM_INSN_I32_REINTERPRET_F32:
+ *src = WASM_VAL_F32;
+ *dst = WASM_VAL_I32;
+ return 1;
+ case WASM_INSN_I64_REINTERPRET_F64:
+ *src = WASM_VAL_F64;
+ *dst = WASM_VAL_I64;
+ return 1;
+ case WASM_INSN_F32_REINTERPRET_I32:
+ *src = WASM_VAL_I32;
+ *dst = WASM_VAL_F32;
+ return 1;
+ case WASM_INSN_F64_REINTERPRET_I64:
+ *src = WASM_VAL_I64;
+ *dst = WASM_VAL_F64;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
diff --git a/lang/wasm/module.c b/lang/wasm/module.c
@@ -0,0 +1,328 @@
+#include "wasm_internal.h"
+
+CfreeSrcLoc wasm_loc(uint32_t line, uint32_t col) {
+ CfreeSrcLoc loc;
+ loc.file_id = 0;
+ loc.line = line;
+ loc.col = col;
+ return loc;
+}
+
+void wasm_error(CfreeCompiler* c, CfreeSrcLoc loc, const char* fmt,
+ ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ cfree_frontend_vfatal(c, loc, fmt, ap);
+}
+
+void* wasm_realloc(CfreeHeap* h, void* p, size_t old_n, size_t new_n) {
+ return h->realloc(h, p, old_n ? old_n : 1u, new_n ? new_n : 1u,
+ _Alignof(max_align_t));
+}
+
+char* wasm_strdup(CfreeHeap* h, const char* s, size_t len) {
+ char* out = (char*)h->alloc(h, len + 1u, 1);
+ if (!out) return NULL;
+ if (len) memcpy(out, s, len);
+ out[len] = '\0';
+ return out;
+}
+
+void wasm_free_str(CfreeHeap* h, char** s) {
+ if (*s) {
+ h->free(h, *s, strlen(*s) + 1u);
+ *s = NULL;
+ }
+}
+
+void wasm_module_init(WasmModule* m, CfreeHeap* heap) {
+ memset(m, 0, sizeof *m);
+ m->heap = heap;
+ m->features = WASM_FEATURE_THREADS | WASM_FEATURE_TYPED_FUNC_REFS |
+ WASM_FEATURE_TAIL_CALLS | WASM_FEATURE_MULTI_MEMORY |
+ WASM_FEATURE_MEMORY64;
+}
+
+void wasm_module_free(WasmModule* m) {
+ uint32_t i;
+ if (!m || !m->heap) return;
+ for (i = 0; i < m->ntypes; ++i) wasm_free_str(m->heap, &m->types[i].name);
+ if (m->types)
+ m->heap->free(m->heap, m->types, sizeof(*m->types) * m->cap_types);
+ for (i = 0; i < m->nfuncs; ++i) {
+ WasmFunc* f = &m->funcs[i];
+ wasm_free_str(m->heap, &f->name);
+ wasm_free_str(m->heap, &f->import_module);
+ wasm_free_str(m->heap, &f->import_name);
+ wasm_free_str(m->heap, &f->export_name);
+ for (uint32_t j = 0; j < f->nparams + f->nlocals; ++j)
+ wasm_free_str(m->heap, &f->local_names[j]);
+ if (f->insns)
+ m->heap->free(m->heap, f->insns, sizeof(*f->insns) * f->cap_insns);
+ }
+ if (m->funcs)
+ m->heap->free(m->heap, m->funcs, sizeof(*m->funcs) * m->cap_funcs);
+ for (i = 0; i < m->nmemories; ++i) {
+ wasm_free_str(m->heap, &m->memories[i].name);
+ wasm_free_str(m->heap, &m->memories[i].import_module);
+ wasm_free_str(m->heap, &m->memories[i].import_name);
+ wasm_free_str(m->heap, &m->memories[i].export_name);
+ if (m->memories[i].data)
+ m->heap->free(m->heap, m->memories[i].data,
+ (size_t)m->memories[i].data_len);
+ }
+ if (m->memories)
+ m->heap->free(m->heap, m->memories, sizeof(*m->memories) * m->cap_memories);
+ for (i = 0; i < m->ntables; ++i) {
+ wasm_free_str(m->heap, &m->tables[i].name);
+ wasm_free_str(m->heap, &m->tables[i].import_module);
+ wasm_free_str(m->heap, &m->tables[i].import_name);
+ wasm_free_str(m->heap, &m->tables[i].export_name);
+ }
+ if (m->tables)
+ m->heap->free(m->heap, m->tables, sizeof(*m->tables) * m->cap_tables);
+ for (i = 0; i < m->nglobals; ++i) {
+ wasm_free_str(m->heap, &m->globals[i].name);
+ wasm_free_str(m->heap, &m->globals[i].import_module);
+ wasm_free_str(m->heap, &m->globals[i].import_name);
+ wasm_free_str(m->heap, &m->globals[i].export_name);
+ }
+ if (m->globals)
+ m->heap->free(m->heap, m->globals, sizeof(*m->globals) * m->cap_globals);
+ if (m->elems)
+ m->heap->free(m->heap, m->elems, sizeof(*m->elems) * m->cap_elems);
+ for (i = 0; i < m->nexports; ++i) wasm_free_str(m->heap, &m->exports[i].name);
+ if (m->exports)
+ m->heap->free(m->heap, m->exports, sizeof(*m->exports) * m->cap_exports);
+ for (i = 0; i < m->ncustoms; ++i) {
+ wasm_free_str(m->heap, &m->customs[i].name);
+ if (m->customs[i].data)
+ m->heap->free(m->heap, m->customs[i].data, m->customs[i].len);
+ }
+ if (m->customs)
+ m->heap->free(m->heap, m->customs, sizeof(*m->customs) * m->cap_customs);
+ memset(m, 0, sizeof *m);
+}
+
+void wasm_memory_ensure(CfreeCompiler* c, WasmModule* m,
+ uint32_t memidx, uint64_t min_len) {
+ WasmMemory* mem;
+ uint64_t old_len, new_len;
+ void* p;
+ if (memidx >= m->nmemories)
+ wasm_error(c, wasm_loc(0, 0), "wasm: data segment without memory");
+ mem = &m->memories[memidx];
+ old_len = mem->data_len;
+ new_len = old_len;
+ if (new_len < min_len) new_len = min_len;
+ if (mem->min_pages && new_len < mem->min_pages * 65536u)
+ new_len = mem->min_pages * 65536u;
+ if (new_len > SIZE_MAX)
+ wasm_error(c, wasm_loc(0, 0), "wasm: data segment too large");
+ if (new_len == old_len) return;
+ p = wasm_realloc(m->heap, mem->data, (size_t)old_len, (size_t)new_len);
+ if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ mem->data = (uint8_t*)p;
+ memset(mem->data + old_len, 0, (size_t)(new_len - old_len));
+ mem->data_len = new_len;
+}
+
+WasmMemory* wasm_add_memory(CfreeCompiler* c, WasmModule* m) {
+ WasmMemory* mem;
+ if (m->nmemories == m->cap_memories) {
+ uint32_t new_cap = m->cap_memories ? m->cap_memories * 2u : 2u;
+ void* p =
+ wasm_realloc(m->heap, m->memories,
+ sizeof(*m->memories) * m->cap_memories,
+ sizeof(*m->memories) * new_cap);
+ if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ m->memories = (WasmMemory*)p;
+ memset(m->memories + m->cap_memories, 0,
+ sizeof(*m->memories) * (new_cap - m->cap_memories));
+ m->cap_memories = new_cap;
+ }
+ mem = &m->memories[m->nmemories++];
+ memset(mem, 0, sizeof *mem);
+ return mem;
+}
+
+WasmFunc* wasm_add_func(CfreeCompiler* c, WasmModule* m) {
+ WasmFunc* f;
+ if (m->nfuncs == m->cap_funcs) {
+ uint32_t new_cap = m->cap_funcs ? m->cap_funcs * 2u : 4u;
+ void* p = wasm_realloc(m->heap, m->funcs, sizeof(*m->funcs) * m->cap_funcs,
+ sizeof(*m->funcs) * new_cap);
+ if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ m->funcs = (WasmFunc*)p;
+ memset(m->funcs + m->cap_funcs, 0,
+ sizeof(*m->funcs) * (new_cap - m->cap_funcs));
+ m->cap_funcs = new_cap;
+ }
+ f = &m->funcs[m->nfuncs++];
+ memset(f, 0, sizeof *f);
+ return f;
+}
+
+WasmFuncType* wasm_add_type(CfreeCompiler* c, WasmModule* m) {
+ WasmFuncType* t;
+ if (m->ntypes == m->cap_types) {
+ uint32_t new_cap = m->cap_types ? m->cap_types * 2u : 8u;
+ void* p = wasm_realloc(m->heap, m->types, sizeof(*m->types) * m->cap_types,
+ sizeof(*m->types) * new_cap);
+ if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ m->types = (WasmFuncType*)p;
+ memset(m->types + m->cap_types, 0,
+ sizeof(*m->types) * (new_cap - m->cap_types));
+ m->cap_types = new_cap;
+ }
+ t = &m->types[m->ntypes++];
+ memset(t, 0, sizeof *t);
+ return t;
+}
+
+uint32_t wasm_intern_func_type(CfreeCompiler* c, WasmModule* m,
+ const WasmFunc* f) {
+ uint32_t i;
+ for (i = 0; i < m->ntypes; ++i) {
+ WasmFuncType* t = &m->types[i];
+ if (t->nparams == f->nparams && t->nresults == f->nresults &&
+ memcmp(t->params, f->params, sizeof(t->params[0]) * t->nparams) == 0 &&
+ memcmp(t->results, f->results, sizeof(t->results[0]) * t->nresults) ==
+ 0)
+ return i;
+ }
+ {
+ WasmFuncType* t = wasm_add_type(c, m);
+ t->nparams = f->nparams;
+ memcpy(t->params, f->params, sizeof(t->params[0]) * f->nparams);
+ t->nresults = f->nresults;
+ memcpy(t->results, f->results, sizeof(t->results[0]) * f->nresults);
+ return m->ntypes - 1u;
+ }
+}
+
+WasmTable* wasm_add_table(CfreeCompiler* c, WasmModule* m) {
+ WasmTable* t;
+ if (m->ntables == m->cap_tables) {
+ uint32_t new_cap = m->cap_tables ? m->cap_tables * 2u : 2u;
+ void* p =
+ wasm_realloc(m->heap, m->tables, sizeof(*m->tables) * m->cap_tables,
+ sizeof(*m->tables) * new_cap);
+ if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ m->tables = (WasmTable*)p;
+ memset(m->tables + m->cap_tables, 0,
+ sizeof(*m->tables) * (new_cap - m->cap_tables));
+ m->cap_tables = new_cap;
+ }
+ t = &m->tables[m->ntables++];
+ memset(t, 0, sizeof *t);
+ return t;
+}
+
+WasmGlobal* wasm_add_global(CfreeCompiler* c, WasmModule* m) {
+ WasmGlobal* g;
+ if (m->nglobals == m->cap_globals) {
+ uint32_t new_cap = m->cap_globals ? m->cap_globals * 2u : 4u;
+ void* p =
+ wasm_realloc(m->heap, m->globals, sizeof(*m->globals) * m->cap_globals,
+ sizeof(*m->globals) * new_cap);
+ if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ m->globals = (WasmGlobal*)p;
+ memset(m->globals + m->cap_globals, 0,
+ sizeof(*m->globals) * (new_cap - m->cap_globals));
+ m->cap_globals = new_cap;
+ }
+ g = &m->globals[m->nglobals++];
+ memset(g, 0, sizeof *g);
+ return g;
+}
+
+WasmElemSegment* wasm_add_elem(CfreeCompiler* c, WasmModule* m) {
+ WasmElemSegment* e;
+ if (m->nelems == m->cap_elems) {
+ uint32_t new_cap = m->cap_elems ? m->cap_elems * 2u : 4u;
+ void* p = wasm_realloc(m->heap, m->elems, sizeof(*m->elems) * m->cap_elems,
+ sizeof(*m->elems) * new_cap);
+ if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ m->elems = (WasmElemSegment*)p;
+ memset(m->elems + m->cap_elems, 0,
+ sizeof(*m->elems) * (new_cap - m->cap_elems));
+ m->cap_elems = new_cap;
+ }
+ e = &m->elems[m->nelems++];
+ memset(e, 0, sizeof *e);
+ return e;
+}
+
+WasmExport* wasm_add_export(CfreeCompiler* c, WasmModule* m) {
+ WasmExport* e;
+ if (m->nexports == m->cap_exports) {
+ uint32_t new_cap = m->cap_exports ? m->cap_exports * 2u : 8u;
+ void* p =
+ wasm_realloc(m->heap, m->exports, sizeof(*m->exports) * m->cap_exports,
+ sizeof(*m->exports) * new_cap);
+ if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ m->exports = (WasmExport*)p;
+ memset(m->exports + m->cap_exports, 0,
+ sizeof(*m->exports) * (new_cap - m->cap_exports));
+ m->cap_exports = new_cap;
+ }
+ e = &m->exports[m->nexports++];
+ memset(e, 0, sizeof *e);
+ return e;
+}
+
+WasmCustom* wasm_add_custom(CfreeCompiler* c, WasmModule* m) {
+ WasmCustom* cs;
+ if (m->ncustoms == m->cap_customs) {
+ uint32_t new_cap = m->cap_customs ? m->cap_customs * 2u : 4u;
+ void* p =
+ wasm_realloc(m->heap, m->customs, sizeof(*m->customs) * m->cap_customs,
+ sizeof(*m->customs) * new_cap);
+ if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ m->customs = (WasmCustom*)p;
+ memset(m->customs + m->cap_customs, 0,
+ sizeof(*m->customs) * (new_cap - m->cap_customs));
+ m->cap_customs = new_cap;
+ }
+ cs = &m->customs[m->ncustoms++];
+ memset(cs, 0, sizeof *cs);
+ return cs;
+}
+
+void wasm_func_add_insn(CfreeCompiler* c, WasmModule* m, WasmFunc* f,
+ WasmInsnKind kind, int64_t imm) {
+ if (f->ninsns == f->cap_insns) {
+ uint32_t new_cap = f->cap_insns ? f->cap_insns * 2u : 16u;
+ void* p = wasm_realloc(m->heap, f->insns, sizeof(*f->insns) * f->cap_insns,
+ sizeof(*f->insns) * new_cap);
+ if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ f->insns = (WasmInsn*)p;
+ f->cap_insns = new_cap;
+ }
+ f->insns[f->ninsns].kind = (uint8_t)kind;
+ f->insns[f->ninsns].type = 0;
+ f->insns[f->ninsns].imm = imm;
+ f->insns[f->ninsns].fp = 0.0;
+ f->insns[f->ninsns].align = 0;
+ f->insns[f->ninsns].memidx = 0;
+ f->insns[f->ninsns].offset64 = imm < 0 ? 0 : (uint64_t)imm;
+ f->insns[f->ninsns].ntargets = 0;
+ f->ninsns++;
+}
+
+void wasm_func_add_mem_insn(CfreeCompiler* c, WasmModule* m, WasmFunc* f,
+ WasmInsnKind kind, uint32_t align,
+ uint64_t offset, uint32_t memidx) {
+ wasm_func_add_insn(c, m, f, kind, offset > INT64_MAX ? INT64_MAX
+ : (int64_t)offset);
+ f->insns[f->ninsns - 1u].align = align;
+ f->insns[f->ninsns - 1u].offset64 = offset;
+ f->insns[f->ninsns - 1u].memidx = memidx;
+}
+
+void wasm_func_add_fp_insn(CfreeCompiler* c, WasmModule* m, WasmFunc* f,
+ WasmInsnKind kind, double value) {
+ wasm_func_add_insn(c, m, f, kind, 0);
+ f->insns[f->ninsns - 1u].fp = value;
+}
diff --git a/lang/wasm/validate.c b/lang/wasm/validate.c
@@ -0,0 +1,665 @@
+#include "wasm_internal.h"
+
+typedef struct WasmValStack {
+ WasmValType vals[256];
+ uint32_t depth;
+} WasmValStack;
+
+typedef struct WasmControlFrame {
+ uint8_t kind;
+ uint32_t height;
+ int seen_else;
+ int unreachable;
+} WasmControlFrame;
+
+static WasmValType wasm_global_init_type(const WasmInsn* in) {
+ switch (in->kind) {
+ case WASM_INSN_I32_CONST:
+ return WASM_VAL_I32;
+ case WASM_INSN_I64_CONST:
+ return WASM_VAL_I64;
+ case WASM_INSN_F32_CONST:
+ return WASM_VAL_F32;
+ case WASM_INSN_F64_CONST:
+ return WASM_VAL_F64;
+ default:
+ return 0;
+ }
+}
+
+static void wasm_stack_push(CfreeCompiler* c, WasmValStack* s, WasmValType vt) {
+ if (s->depth >= 256u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: operand stack too deep");
+ s->vals[s->depth++] = vt;
+}
+
+static int wasm_stack_pop(CfreeCompiler* c, WasmValStack* s,
+ WasmControlFrame* frames, uint32_t nframes,
+ WasmValType expected, const char* what) {
+ WasmControlFrame* top = &frames[nframes - 1u];
+ if (s->depth <= top->height) {
+ if (top->unreachable) return 1;
+ wasm_error(c, wasm_loc(0, 0), "wasm: operand stack underflow");
+ }
+ if (expected && s->vals[s->depth - 1u] != expected)
+ wasm_error(c, wasm_loc(0, 0), "wasm: %s type mismatch", what);
+ s->depth--;
+ return 1;
+}
+
+static WasmValType wasm_stack_pop_any(CfreeCompiler* c, WasmValStack* s,
+ WasmControlFrame* frames,
+ uint32_t nframes, const char* what) {
+ WasmControlFrame* top = &frames[nframes - 1u];
+ WasmValType vt;
+ if (s->depth <= top->height) {
+ if (top->unreachable) return WASM_VAL_I32;
+ wasm_error(c, wasm_loc(0, 0), "wasm: operand stack underflow");
+ }
+ vt = s->vals[s->depth - 1u];
+ if (!vt) wasm_error(c, wasm_loc(0, 0), "wasm: %s type mismatch", what);
+ s->depth--;
+ return vt;
+}
+
+static void wasm_stack_pop_ref(CfreeCompiler* c, WasmValStack* s,
+ WasmControlFrame* frames, uint32_t nframes,
+ const char* what) {
+ WasmValType vt = wasm_stack_pop_any(c, s, frames, nframes, what);
+ if (!wasm_is_ref_type(vt))
+ wasm_error(c, wasm_loc(0, 0), "wasm: %s type mismatch", what);
+}
+
+static void wasm_mark_unreachable(WasmValStack* s, WasmControlFrame* frames,
+ uint32_t nframes) {
+ WasmControlFrame* top = &frames[nframes - 1u];
+ s->depth = top->height;
+ top->unreachable = 1;
+}
+
+void wasm_validate(WasmModule* m, CfreeCompiler* c) {
+ uint32_t i, j;
+ for (i = 0; i < m->ntypes; ++i) {
+ for (j = 0; j < m->types[i].nparams; ++j)
+ if (!wasm_is_frontend_value_type(m->types[i].params[j]))
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported parameter type");
+ for (j = 0; j < m->types[i].nresults; ++j)
+ if (!wasm_is_frontend_value_type(m->types[i].results[j]))
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported result type");
+ }
+ for (i = 0; i < m->nmemories; ++i) {
+ if (m->memories[i].has_max &&
+ m->memories[i].max_pages < m->memories[i].min_pages)
+ wasm_error(c, wasm_loc(0, 0), "wasm: memory maximum below minimum");
+ if (m->memories[i].shared && !m->memories[i].has_max)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: shared memory requires maximum");
+ }
+ for (i = 0; i < m->ntables; ++i) {
+ if (m->tables[i].elem_type != WASM_VAL_FUNCREF)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: reference type is unsupported for tables");
+ if (m->tables[i].has_max && m->tables[i].max < m->tables[i].min)
+ wasm_error(c, wasm_loc(0, 0), "wasm: table maximum below minimum");
+ }
+ for (i = 0; i < m->nglobals; ++i) {
+ WasmGlobal* g = &m->globals[i];
+ if (!wasm_is_num_type(g->type))
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported global type");
+ if (!g->is_import && wasm_global_init_type(&g->init) != g->type)
+ wasm_error(c, wasm_loc(0, 0), "wasm: global initializer type mismatch");
+ }
+ for (i = 0; i < m->nexports; ++i) {
+ WasmExport* ex = &m->exports[i];
+ if ((ex->kind == 0 && ex->index >= m->nfuncs) ||
+ (ex->kind == 1 && ex->index >= m->ntables) ||
+ (ex->kind == 2 && ex->index >= m->nmemories) ||
+ (ex->kind == 3 && ex->index >= m->nglobals))
+ wasm_error(c, wasm_loc(0, 0), "wasm: export index out of range");
+ }
+ if (m->has_start) {
+ if (m->start_func >= m->nfuncs)
+ wasm_error(c, wasm_loc(0, 0), "wasm: start function index out of range");
+ if (m->funcs[m->start_func].nparams || m->funcs[m->start_func].nresults)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: start function must have no params or results");
+ }
+ for (i = 0; i < m->nelems; ++i) {
+ uint32_t table_min;
+ if (m->elems[i].tableidx >= m->ntables)
+ wasm_error(c, wasm_loc(0, 0), "wasm: element table index out of range");
+ table_min = m->tables[m->elems[i].tableidx].min;
+ if (m->elems[i].offset < 0 ||
+ (uint64_t)m->elems[i].offset + m->elems[i].nfuncs > table_min)
+ wasm_error(c, wasm_loc(0, 0), "wasm: element segment out of range");
+ for (j = 0; j < m->elems[i].nfuncs; ++j)
+ if (m->elems[i].funcs[j] >= m->nfuncs)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: element function index out of range");
+ }
+ for (i = 0; i < m->nfuncs; ++i) {
+ WasmFunc* f = &m->funcs[i];
+ WasmValStack stack;
+ WasmControlFrame control[65];
+ uint32_t ncontrol = 1;
+ memset(&stack, 0, sizeof stack);
+ memset(control, 0, sizeof control);
+ control[0].kind = 0xffu;
+ control[0].height = 0;
+ if (f->is_import) {
+ if (f->ninsns)
+ wasm_error(c, wasm_loc(0, 0), "wasm: imported function has body");
+ continue;
+ }
+ if (f->nresults > 1u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: multi-result unsupported");
+ for (j = 0; j < f->ninsns; ++j) {
+ WasmInsn* in = &f->insns[j];
+ WasmValType vt, src, dst;
+ switch (in->kind) {
+ case WASM_INSN_F32_CONST:
+ wasm_stack_push(c, &stack, WASM_VAL_F32);
+ break;
+ case WASM_INSN_F64_CONST:
+ wasm_stack_push(c, &stack, WASM_VAL_F64);
+ break;
+ case WASM_INSN_I32_CONST:
+ wasm_stack_push(c, &stack, WASM_VAL_I32);
+ break;
+ case WASM_INSN_I64_CONST:
+ wasm_stack_push(c, &stack, WASM_VAL_I64);
+ break;
+ case WASM_INSN_LOCAL_GET:
+ if (in->imm < 0 ||
+ (uint64_t)in->imm >= (uint64_t)f->nparams + f->nlocals)
+ wasm_error(c, wasm_loc(0, 0), "wasm: local index out of range");
+ wasm_stack_push(c, &stack,
+ wasm_func_local_type(f, (uint32_t)in->imm));
+ break;
+ case WASM_INSN_LOCAL_SET:
+ case WASM_INSN_LOCAL_TEE:
+ if (in->imm < 0 ||
+ (uint64_t)in->imm >= (uint64_t)f->nparams + f->nlocals)
+ wasm_error(c, wasm_loc(0, 0), "wasm: local index out of range");
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ wasm_func_local_type(f, (uint32_t)in->imm), "local");
+ if (in->kind == WASM_INSN_LOCAL_TEE)
+ wasm_stack_push(c, &stack,
+ wasm_func_local_type(f, (uint32_t)in->imm));
+ break;
+ case WASM_INSN_CALL:
+ case WASM_INSN_RETURN_CALL:
+ if (in->imm < 0 || (uint64_t)in->imm >= m->nfuncs)
+ wasm_error(c, wasm_loc(0, 0), "wasm: call index out of range");
+ if (in->kind == WASM_INSN_RETURN_CALL) {
+ wasm_require_feature(c, m, WASM_FEATURE_TAIL_CALLS, "tail calls",
+ "return_call");
+ if (m->funcs[in->imm].nresults != f->nresults ||
+ (f->nresults &&
+ m->funcs[in->imm].results[0] != f->results[0]))
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: return_call result type mismatch");
+ }
+ for (uint32_t k = 0; k < m->funcs[in->imm].nparams; ++k) {
+ uint32_t param = m->funcs[in->imm].nparams - 1u - k;
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ m->funcs[in->imm].params[param], "call argument");
+ }
+ if (in->kind == WASM_INSN_RETURN_CALL) {
+ wasm_mark_unreachable(&stack, control, ncontrol);
+ } else if (m->funcs[in->imm].nresults) {
+ wasm_stack_push(c, &stack, m->funcs[in->imm].results[0]);
+ }
+ break;
+ case WASM_INSN_CALL_INDIRECT: {
+ WasmFuncType* t;
+ if (in->imm < 0 || (uint64_t)in->imm >= m->ntypes)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: call_indirect type index out of range");
+ if (in->align >= m->ntables)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: call_indirect table index out of range");
+ t = &m->types[in->imm];
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32,
+ "call_indirect index");
+ for (uint32_t k = 0; k < t->nparams; ++k) {
+ uint32_t param = t->nparams - 1u - k;
+ wasm_stack_pop(c, &stack, control, ncontrol, t->params[param],
+ "call_indirect argument");
+ }
+ if (t->nresults) wasm_stack_push(c, &stack, t->results[0]);
+ break;
+ }
+ case WASM_INSN_RETURN_CALL_INDIRECT: {
+ WasmFuncType* t;
+ wasm_require_feature(c, m, WASM_FEATURE_TAIL_CALLS, "tail calls",
+ "return_call_indirect");
+ if (in->imm < 0 || (uint64_t)in->imm >= m->ntypes)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: return_call_indirect type index out of range");
+ if (in->align >= m->ntables)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: return_call_indirect table index out of range");
+ t = &m->types[in->imm];
+ if (t->nresults != f->nresults ||
+ (f->nresults && t->results[0] != f->results[0]))
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: return_call_indirect result type mismatch");
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32,
+ "return_call_indirect index");
+ for (uint32_t k = 0; k < t->nparams; ++k) {
+ uint32_t param = t->nparams - 1u - k;
+ wasm_stack_pop(c, &stack, control, ncontrol, t->params[param],
+ "return_call_indirect argument");
+ }
+ wasm_mark_unreachable(&stack, control, ncontrol);
+ break;
+ }
+ case WASM_INSN_REF_NULL:
+ wasm_require_feature(c, m, WASM_FEATURE_TYPED_FUNC_REFS,
+ "typed function references", "ref.null");
+ if (!wasm_is_ref_type((WasmValType)in->imm))
+ wasm_error(c, wasm_loc(0, 0), "wasm: bad ref.null type");
+ if ((WasmValType)in->imm != WASM_VAL_FUNCREF)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: unsupported reference type");
+ wasm_stack_push(c, &stack, (WasmValType)in->imm);
+ break;
+ case WASM_INSN_REF_FUNC:
+ wasm_require_feature(c, m, WASM_FEATURE_TYPED_FUNC_REFS,
+ "typed function references", "ref.func");
+ if (in->imm < 0 || (uint64_t)in->imm >= m->nfuncs)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: ref.func index out of range");
+ wasm_stack_push(c, &stack, WASM_VAL_FUNCREF);
+ break;
+ case WASM_INSN_REF_IS_NULL:
+ wasm_require_feature(c, m, WASM_FEATURE_TYPED_FUNC_REFS,
+ "typed function references", "ref.is_null");
+ wasm_stack_pop_ref(c, &stack, control, ncontrol, "ref.is_null");
+ wasm_stack_push(c, &stack, WASM_VAL_I32);
+ break;
+ case WASM_INSN_CALL_REF:
+ case WASM_INSN_RETURN_CALL_REF: {
+ WasmFuncType* t;
+ wasm_require_feature(c, m, WASM_FEATURE_TYPED_FUNC_REFS,
+ "typed function references", "call_ref");
+ if (in->kind == WASM_INSN_RETURN_CALL_REF)
+ wasm_require_feature(c, m, WASM_FEATURE_TAIL_CALLS, "tail calls",
+ "return_call_ref");
+ if (in->imm < 0 || (uint64_t)in->imm >= m->ntypes)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: call_ref type index out of range");
+ t = &m->types[in->imm];
+ if (in->kind == WASM_INSN_RETURN_CALL_REF &&
+ (t->nresults != f->nresults ||
+ (f->nresults && t->results[0] != f->results[0])))
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: return_call_ref result type mismatch");
+ wasm_stack_pop_ref(c, &stack, control, ncontrol, "call_ref callee");
+ for (uint32_t k = 0; k < t->nparams; ++k) {
+ uint32_t param = t->nparams - 1u - k;
+ wasm_stack_pop(c, &stack, control, ncontrol, t->params[param],
+ "call_ref argument");
+ }
+ if (in->kind == WASM_INSN_RETURN_CALL_REF)
+ wasm_mark_unreachable(&stack, control, ncontrol);
+ else if (t->nresults)
+ wasm_stack_push(c, &stack, t->results[0]);
+ break;
+ }
+ case WASM_INSN_GLOBAL_GET:
+ if (in->imm < 0 || (uint64_t)in->imm >= m->nglobals)
+ wasm_error(c, wasm_loc(0, 0), "wasm: global index out of range");
+ wasm_stack_push(c, &stack, m->globals[in->imm].type);
+ break;
+ case WASM_INSN_GLOBAL_SET:
+ if (in->imm < 0 || (uint64_t)in->imm >= m->nglobals)
+ wasm_error(c, wasm_loc(0, 0), "wasm: global index out of range");
+ if (!m->globals[in->imm].mutable_)
+ wasm_error(c, wasm_loc(0, 0), "wasm: global is immutable");
+ wasm_stack_pop(c, &stack, control, ncontrol, m->globals[in->imm].type,
+ "global");
+ break;
+ case WASM_INSN_RETURN:
+ if (f->nresults)
+ wasm_stack_pop(c, &stack, control, ncontrol, f->results[0],
+ "return");
+ wasm_mark_unreachable(&stack, control, ncontrol);
+ break;
+ case WASM_INSN_DROP:
+ wasm_stack_pop(c, &stack, control, ncontrol, 0, "drop");
+ break;
+ case WASM_INSN_I32_EQZ:
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "eqz");
+ wasm_stack_push(c, &stack, WASM_VAL_I32);
+ break;
+ case WASM_INSN_I64_EQZ:
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I64, "eqz");
+ wasm_stack_push(c, &stack, WASM_VAL_I32);
+ break;
+ case WASM_INSN_BLOCK:
+ case WASM_INSN_LOOP:
+ if (ncontrol >= 65u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: control stack too deep");
+ control[ncontrol].kind = in->kind;
+ control[ncontrol].height = stack.depth;
+ control[ncontrol].seen_else = 0;
+ control[ncontrol].unreachable = 0;
+ ncontrol++;
+ break;
+ case WASM_INSN_IF:
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "if");
+ if (ncontrol >= 65u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: control stack too deep");
+ control[ncontrol].kind = in->kind;
+ control[ncontrol].height = stack.depth;
+ control[ncontrol].seen_else = 0;
+ control[ncontrol].unreachable = 0;
+ ncontrol++;
+ break;
+ case WASM_INSN_ELSE:
+ if (ncontrol <= 1u || control[ncontrol - 1u].kind != WASM_INSN_IF)
+ wasm_error(c, wasm_loc(0, 0), "wasm: else without if");
+ if (!control[ncontrol - 1u].unreachable &&
+ stack.depth != control[ncontrol - 1u].height)
+ wasm_error(c, wasm_loc(0, 0), "wasm: if branch result mismatch");
+ stack.depth = control[ncontrol - 1u].height;
+ control[ncontrol - 1u].seen_else = 1;
+ control[ncontrol - 1u].unreachable = 0;
+ break;
+ case WASM_INSN_END:
+ if (ncontrol <= 1u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: end without block");
+ if (!control[ncontrol - 1u].unreachable &&
+ stack.depth != control[ncontrol - 1u].height)
+ wasm_error(c, wasm_loc(0, 0), "wasm: block result mismatch");
+ stack.depth = control[ncontrol - 1u].height;
+ ncontrol--;
+ break;
+ case WASM_INSN_BR:
+ if (in->imm < 0 || (uint64_t)in->imm >= ncontrol - 1u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: branch depth out of range");
+ wasm_mark_unreachable(&stack, control, ncontrol);
+ break;
+ case WASM_INSN_BR_IF:
+ if (in->imm < 0 || (uint64_t)in->imm >= ncontrol - 1u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: branch depth out of range");
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "br_if");
+ break;
+ case WASM_INSN_BR_TABLE:
+ if (in->ntargets == 0)
+ wasm_error(c, wasm_loc(0, 0), "wasm: br_table without targets");
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32,
+ "br_table selector");
+ for (uint32_t k = 0; k < in->ntargets; ++k)
+ if (in->targets[k] >= ncontrol - 1u)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: br_table depth out of range");
+ wasm_mark_unreachable(&stack, control, ncontrol);
+ break;
+ case WASM_INSN_SELECT: {
+ WasmValType rhs, lhs;
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "select");
+ if (stack.depth <= control[ncontrol - 1u].height &&
+ control[ncontrol - 1u].unreachable) {
+ in->type = WASM_VAL_I32;
+ break;
+ }
+ if (stack.depth < control[ncontrol - 1u].height + 2u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: operand stack underflow");
+ rhs = stack.vals[--stack.depth];
+ lhs = stack.vals[--stack.depth];
+ if (lhs != rhs)
+ wasm_error(c, wasm_loc(0, 0), "wasm: select type mismatch");
+ in->type = (uint8_t)lhs;
+ wasm_stack_push(c, &stack, lhs);
+ break;
+ }
+ case WASM_INSN_MEMORY_SIZE:
+ if (in->memidx >= m->nmemories)
+ wasm_error(c, wasm_loc(0, 0), "wasm: memory.size without memory");
+ wasm_stack_push(c, &stack,
+ m->memories[in->memidx].is64 ? WASM_VAL_I64
+ : WASM_VAL_I32);
+ break;
+ case WASM_INSN_MEMORY_GROW:
+ if (in->memidx >= m->nmemories)
+ wasm_error(c, wasm_loc(0, 0), "wasm: memory.grow without memory");
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ m->memories[in->memidx].is64 ? WASM_VAL_I64
+ : WASM_VAL_I32,
+ "memory.grow");
+ wasm_stack_push(c, &stack,
+ m->memories[in->memidx].is64 ? WASM_VAL_I64
+ : WASM_VAL_I32);
+ break;
+ case WASM_INSN_ATOMIC_FENCE:
+ wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads",
+ "atomic.fence");
+ break;
+ case WASM_INSN_I32_ATOMIC_LOAD:
+ case WASM_INSN_I64_ATOMIC_LOAD:
+ case WASM_INSN_I32_ATOMIC_LOAD8_U:
+ case WASM_INSN_I32_ATOMIC_LOAD16_U:
+ case WASM_INSN_I64_ATOMIC_LOAD8_U:
+ case WASM_INSN_I64_ATOMIC_LOAD16_U:
+ case WASM_INSN_I64_ATOMIC_LOAD32_U:
+ wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads",
+ "atomic load");
+ if (in->memidx >= m->nmemories)
+ wasm_error(c, wasm_loc(0, 0), "wasm: atomic load without memory");
+ if (!m->memories[in->memidx].shared)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: atomic load requires shared memory");
+ if (in->align && in->align > wasm_mem_width(in->kind))
+ wasm_error(c, wasm_loc(0, 0), "wasm: bad atomic alignment");
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ m->memories[in->memidx].is64 ? WASM_VAL_I64
+ : WASM_VAL_I32,
+ "atomic load");
+ wasm_stack_push(c, &stack, wasm_atomic_value_type(in->kind));
+ break;
+ case WASM_INSN_I32_ATOMIC_STORE:
+ case WASM_INSN_I64_ATOMIC_STORE:
+ case WASM_INSN_I32_ATOMIC_STORE8:
+ case WASM_INSN_I32_ATOMIC_STORE16:
+ case WASM_INSN_I64_ATOMIC_STORE8:
+ case WASM_INSN_I64_ATOMIC_STORE16:
+ case WASM_INSN_I64_ATOMIC_STORE32:
+ wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads",
+ "atomic store");
+ if (in->memidx >= m->nmemories)
+ wasm_error(c, wasm_loc(0, 0), "wasm: atomic store without memory");
+ if (!m->memories[in->memidx].shared)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: atomic store requires shared memory");
+ if (in->align && in->align > wasm_mem_width(in->kind))
+ wasm_error(c, wasm_loc(0, 0), "wasm: bad atomic alignment");
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ wasm_atomic_value_type(in->kind), "atomic store");
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ m->memories[in->memidx].is64 ? WASM_VAL_I64
+ : WASM_VAL_I32,
+ "atomic store");
+ break;
+ case WASM_INSN_I32_ATOMIC_RMW_ADD:
+ case WASM_INSN_I64_ATOMIC_RMW_ADD:
+ case WASM_INSN_I32_ATOMIC_RMW_SUB:
+ case WASM_INSN_I64_ATOMIC_RMW_SUB:
+ case WASM_INSN_I32_ATOMIC_RMW_AND:
+ case WASM_INSN_I64_ATOMIC_RMW_AND:
+ case WASM_INSN_I32_ATOMIC_RMW_OR:
+ case WASM_INSN_I64_ATOMIC_RMW_OR:
+ case WASM_INSN_I32_ATOMIC_RMW_XOR:
+ case WASM_INSN_I64_ATOMIC_RMW_XOR:
+ case WASM_INSN_I32_ATOMIC_RMW_XCHG:
+ case WASM_INSN_I64_ATOMIC_RMW_XCHG:
+ wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads",
+ "atomic rmw");
+ if (in->memidx >= m->nmemories)
+ wasm_error(c, wasm_loc(0, 0), "wasm: atomic rmw without memory");
+ if (!m->memories[in->memidx].shared)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: atomic rmw requires shared memory");
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ wasm_atomic_value_type(in->kind), "atomic rmw");
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ m->memories[in->memidx].is64 ? WASM_VAL_I64
+ : WASM_VAL_I32,
+ "atomic rmw");
+ wasm_stack_push(c, &stack, wasm_atomic_value_type(in->kind));
+ break;
+ case WASM_INSN_I32_ATOMIC_RMW_CMPXCHG:
+ case WASM_INSN_I64_ATOMIC_RMW_CMPXCHG:
+ wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads",
+ "atomic cmpxchg");
+ if (in->memidx >= m->nmemories)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: atomic cmpxchg without memory");
+ if (!m->memories[in->memidx].shared)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: atomic cmpxchg requires shared memory");
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ wasm_atomic_value_type(in->kind), "atomic cmpxchg");
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ wasm_atomic_value_type(in->kind), "atomic cmpxchg");
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ m->memories[in->memidx].is64 ? WASM_VAL_I64
+ : WASM_VAL_I32,
+ "atomic cmpxchg");
+ wasm_stack_push(c, &stack, wasm_atomic_value_type(in->kind));
+ break;
+ case WASM_INSN_I32_ATOMIC_WAIT:
+ case WASM_INSN_I64_ATOMIC_WAIT:
+ wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads",
+ "atomic wait");
+ if (in->memidx >= m->nmemories)
+ wasm_error(c, wasm_loc(0, 0), "wasm: atomic wait without memory");
+ if (!m->memories[in->memidx].shared)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: atomic wait requires shared memory");
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I64,
+ "atomic wait timeout");
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ wasm_atomic_value_type(in->kind),
+ "atomic wait expected");
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ m->memories[in->memidx].is64 ? WASM_VAL_I64
+ : WASM_VAL_I32,
+ "atomic wait address");
+ wasm_stack_push(c, &stack, WASM_VAL_I32);
+ break;
+ case WASM_INSN_MEMORY_ATOMIC_NOTIFY:
+ wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads",
+ "atomic notify");
+ if (in->memidx >= m->nmemories)
+ wasm_error(c, wasm_loc(0, 0), "wasm: atomic notify without memory");
+ if (!m->memories[in->memidx].shared)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: atomic notify requires shared memory");
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32,
+ "atomic notify count");
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ m->memories[in->memidx].is64 ? WASM_VAL_I64
+ : WASM_VAL_I32,
+ "atomic notify address");
+ wasm_stack_push(c, &stack, WASM_VAL_I32);
+ break;
+ case WASM_INSN_I32_LOAD:
+ case WASM_INSN_I64_LOAD:
+ case WASM_INSN_I32_LOAD8_S:
+ case WASM_INSN_I32_LOAD8_U:
+ case WASM_INSN_I32_LOAD16_S:
+ case WASM_INSN_I32_LOAD16_U:
+ case WASM_INSN_I64_LOAD8_S:
+ case WASM_INSN_I64_LOAD8_U:
+ case WASM_INSN_I64_LOAD16_S:
+ case WASM_INSN_I64_LOAD16_U:
+ case WASM_INSN_I64_LOAD32_S:
+ case WASM_INSN_I64_LOAD32_U:
+ if (in->memidx >= m->nmemories)
+ wasm_error(c, wasm_loc(0, 0), "wasm: load without memory");
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ m->memories[in->memidx].is64 ? WASM_VAL_I64
+ : WASM_VAL_I32,
+ "load");
+ wasm_stack_push(c, &stack, wasm_load_result_type(in->kind));
+ break;
+ case WASM_INSN_I32_STORE:
+ case WASM_INSN_I64_STORE:
+ case WASM_INSN_I32_STORE8:
+ case WASM_INSN_I32_STORE16:
+ case WASM_INSN_I64_STORE8:
+ case WASM_INSN_I64_STORE16:
+ case WASM_INSN_I64_STORE32:
+ if (in->memidx >= m->nmemories)
+ wasm_error(c, wasm_loc(0, 0), "wasm: store without memory");
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ wasm_store_value_type(in->kind), "store");
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ m->memories[in->memidx].is64 ? WASM_VAL_I64
+ : WASM_VAL_I32,
+ "store");
+ break;
+ case WASM_INSN_UNREACHABLE:
+ wasm_mark_unreachable(&stack, control, ncontrol);
+ break;
+ case WASM_INSN_NOP:
+ break;
+ default:
+ if (wasm_int_unop_kind(in->kind, &vt)) {
+ wasm_stack_pop(c, &stack, control, ncontrol, vt, "unary operand");
+ wasm_stack_push(c, &stack, vt);
+ break;
+ }
+ if (wasm_fp_binop_kind(in->kind, &vt)) {
+ wasm_stack_pop(c, &stack, control, ncontrol, vt, "fp operand");
+ wasm_stack_pop(c, &stack, control, ncontrol, vt, "fp operand");
+ wasm_stack_push(c, &stack, vt);
+ break;
+ }
+ if (wasm_fp_cmp_kind(in->kind, &vt)) {
+ wasm_stack_pop(c, &stack, control, ncontrol, vt, "fp compare");
+ wasm_stack_pop(c, &stack, control, ncontrol, vt, "fp compare");
+ wasm_stack_push(c, &stack, WASM_VAL_I32);
+ break;
+ }
+ if (wasm_conversion_kind(in->kind, &src, &dst)) {
+ wasm_stack_pop(c, &stack, control, ncontrol, src, "conversion");
+ wasm_stack_push(c, &stack, dst);
+ break;
+ }
+ {
+ CfreeCgIntCmpOp cmp;
+ WasmValType rhs, lhs;
+ rhs = stack.depth > control[ncontrol - 1u].height
+ ? stack.vals[stack.depth - 1u]
+ : WASM_VAL_I32;
+ wasm_stack_pop(c, &stack, control, ncontrol, 0, "operand");
+ lhs = stack.depth > control[ncontrol - 1u].height
+ ? stack.vals[stack.depth - 1u]
+ : rhs;
+ wasm_stack_pop(c, &stack, control, ncontrol, rhs, "operand");
+ if (lhs != rhs)
+ wasm_error(c, wasm_loc(0, 0), "wasm: operand type mismatch");
+ wasm_stack_push(
+ c, &stack,
+ wasm_int_cmp_op(in->kind, &cmp) ? WASM_VAL_I32 : lhs);
+ break;
+ }
+ }
+ }
+ if (ncontrol != 1u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: unterminated control block");
+ if (!control[0].unreachable) {
+ if (f->nresults) {
+ if (stack.depth != 1u || stack.vals[0] != f->results[0])
+ wasm_error(c, wasm_loc(0, 0), "wasm: function result type mismatch");
+ } else if (stack.depth != 0) {
+ wasm_error(c, wasm_loc(0, 0), "wasm: function leaves extra values");
+ }
+ }
+ }
+}
+
diff --git a/lang/wasm/wasm.c b/lang/wasm/wasm.c
@@ -1,8312 +1,11 @@
-#include "wasm.h"
-
-#include <cfree/cg.h>
-#include <cfree/frontend.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-typedef enum WasmValType {
- WASM_VAL_I32 = 0x7f,
- WASM_VAL_I64 = 0x7e,
- WASM_VAL_F32 = 0x7d,
- WASM_VAL_F64 = 0x7c,
- WASM_VAL_FUNCREF = 0x70,
- WASM_VAL_EXTERNREF = 0x6f,
-} WasmValType;
-
-typedef enum WasmFeatureSet {
- WASM_FEATURE_THREADS = 1u << 0,
- WASM_FEATURE_TYPED_FUNC_REFS = 1u << 1,
- WASM_FEATURE_TAIL_CALLS = 1u << 2,
- WASM_FEATURE_MULTI_MEMORY = 1u << 3,
- WASM_FEATURE_MEMORY64 = 1u << 4,
-} WasmFeatureSet;
-
-typedef enum WasmInsnKind {
- WASM_INSN_UNREACHABLE,
- WASM_INSN_NOP,
- WASM_INSN_BLOCK,
- WASM_INSN_LOOP,
- WASM_INSN_IF,
- WASM_INSN_ELSE,
- WASM_INSN_END,
- WASM_INSN_BR,
- WASM_INSN_BR_IF,
- WASM_INSN_BR_TABLE,
- WASM_INSN_SELECT,
- WASM_INSN_F32_CONST,
- WASM_INSN_F64_CONST,
- WASM_INSN_I32_CONST,
- WASM_INSN_I64_CONST,
- WASM_INSN_LOCAL_GET,
- WASM_INSN_LOCAL_SET,
- WASM_INSN_LOCAL_TEE,
- WASM_INSN_CALL,
- WASM_INSN_CALL_INDIRECT,
- WASM_INSN_RETURN_CALL,
- WASM_INSN_RETURN_CALL_INDIRECT,
- WASM_INSN_REF_NULL,
- WASM_INSN_REF_FUNC,
- WASM_INSN_REF_IS_NULL,
- WASM_INSN_CALL_REF,
- WASM_INSN_RETURN_CALL_REF,
- WASM_INSN_GLOBAL_GET,
- WASM_INSN_GLOBAL_SET,
- WASM_INSN_RETURN,
- WASM_INSN_DROP,
- WASM_INSN_I32_LOAD,
- WASM_INSN_I64_LOAD,
- WASM_INSN_I32_LOAD8_S,
- WASM_INSN_I32_LOAD8_U,
- WASM_INSN_I32_LOAD16_S,
- WASM_INSN_I32_LOAD16_U,
- WASM_INSN_I64_LOAD8_S,
- WASM_INSN_I64_LOAD8_U,
- WASM_INSN_I64_LOAD16_S,
- WASM_INSN_I64_LOAD16_U,
- WASM_INSN_I64_LOAD32_S,
- WASM_INSN_I64_LOAD32_U,
- WASM_INSN_I32_STORE,
- WASM_INSN_I64_STORE,
- WASM_INSN_I32_STORE8,
- WASM_INSN_I32_STORE16,
- WASM_INSN_I64_STORE8,
- WASM_INSN_I64_STORE16,
- WASM_INSN_I64_STORE32,
- WASM_INSN_MEMORY_SIZE,
- WASM_INSN_MEMORY_GROW,
- WASM_INSN_ATOMIC_FENCE,
- WASM_INSN_I32_ATOMIC_LOAD,
- WASM_INSN_I64_ATOMIC_LOAD,
- WASM_INSN_I32_ATOMIC_LOAD8_U,
- WASM_INSN_I32_ATOMIC_LOAD16_U,
- WASM_INSN_I64_ATOMIC_LOAD8_U,
- WASM_INSN_I64_ATOMIC_LOAD16_U,
- WASM_INSN_I64_ATOMIC_LOAD32_U,
- WASM_INSN_I32_ATOMIC_STORE,
- WASM_INSN_I64_ATOMIC_STORE,
- WASM_INSN_I32_ATOMIC_STORE8,
- WASM_INSN_I32_ATOMIC_STORE16,
- WASM_INSN_I64_ATOMIC_STORE8,
- WASM_INSN_I64_ATOMIC_STORE16,
- WASM_INSN_I64_ATOMIC_STORE32,
- WASM_INSN_I32_ATOMIC_RMW_ADD,
- WASM_INSN_I64_ATOMIC_RMW_ADD,
- WASM_INSN_I32_ATOMIC_RMW_SUB,
- WASM_INSN_I64_ATOMIC_RMW_SUB,
- WASM_INSN_I32_ATOMIC_RMW_AND,
- WASM_INSN_I64_ATOMIC_RMW_AND,
- WASM_INSN_I32_ATOMIC_RMW_OR,
- WASM_INSN_I64_ATOMIC_RMW_OR,
- WASM_INSN_I32_ATOMIC_RMW_XOR,
- WASM_INSN_I64_ATOMIC_RMW_XOR,
- WASM_INSN_I32_ATOMIC_RMW_XCHG,
- WASM_INSN_I64_ATOMIC_RMW_XCHG,
- WASM_INSN_I32_ATOMIC_RMW_CMPXCHG,
- WASM_INSN_I64_ATOMIC_RMW_CMPXCHG,
- WASM_INSN_I32_ATOMIC_WAIT,
- WASM_INSN_I64_ATOMIC_WAIT,
- WASM_INSN_MEMORY_ATOMIC_NOTIFY,
- WASM_INSN_I32_ADD,
- WASM_INSN_I32_SUB,
- WASM_INSN_I32_MUL,
- WASM_INSN_I32_DIV_S,
- WASM_INSN_I32_DIV_U,
- WASM_INSN_I32_REM_S,
- WASM_INSN_I32_REM_U,
- WASM_INSN_I32_AND,
- WASM_INSN_I32_OR,
- WASM_INSN_I32_XOR,
- WASM_INSN_I32_SHL,
- WASM_INSN_I32_SHR_S,
- WASM_INSN_I32_SHR_U,
- WASM_INSN_I32_ROTL,
- WASM_INSN_I32_ROTR,
- WASM_INSN_I32_CLZ,
- WASM_INSN_I32_CTZ,
- WASM_INSN_I32_POPCNT,
- WASM_INSN_I32_EQZ,
- WASM_INSN_I32_EQ,
- WASM_INSN_I32_NE,
- WASM_INSN_I32_LT_S,
- WASM_INSN_I32_LT_U,
- WASM_INSN_I32_GT_S,
- WASM_INSN_I32_GT_U,
- WASM_INSN_I32_LE_S,
- WASM_INSN_I32_LE_U,
- WASM_INSN_I32_GE_S,
- WASM_INSN_I32_GE_U,
- WASM_INSN_I64_ADD,
- WASM_INSN_I64_SUB,
- WASM_INSN_I64_MUL,
- WASM_INSN_I64_DIV_S,
- WASM_INSN_I64_DIV_U,
- WASM_INSN_I64_REM_S,
- WASM_INSN_I64_REM_U,
- WASM_INSN_I64_AND,
- WASM_INSN_I64_OR,
- WASM_INSN_I64_XOR,
- WASM_INSN_I64_SHL,
- WASM_INSN_I64_SHR_S,
- WASM_INSN_I64_SHR_U,
- WASM_INSN_I64_ROTL,
- WASM_INSN_I64_ROTR,
- WASM_INSN_I64_CLZ,
- WASM_INSN_I64_CTZ,
- WASM_INSN_I64_POPCNT,
- WASM_INSN_I64_EQZ,
- WASM_INSN_I64_EQ,
- WASM_INSN_I64_NE,
- WASM_INSN_I64_LT_S,
- WASM_INSN_I64_LT_U,
- WASM_INSN_I64_GT_S,
- WASM_INSN_I64_GT_U,
- WASM_INSN_I64_LE_S,
- WASM_INSN_I64_LE_U,
- WASM_INSN_I64_GE_S,
- WASM_INSN_I64_GE_U,
- WASM_INSN_F32_ADD,
- WASM_INSN_F32_SUB,
- WASM_INSN_F32_MUL,
- WASM_INSN_F32_DIV,
- WASM_INSN_F32_EQ,
- WASM_INSN_F32_NE,
- WASM_INSN_F32_LT,
- WASM_INSN_F32_GT,
- WASM_INSN_F32_LE,
- WASM_INSN_F32_GE,
- WASM_INSN_F64_ADD,
- WASM_INSN_F64_SUB,
- WASM_INSN_F64_MUL,
- WASM_INSN_F64_DIV,
- WASM_INSN_F64_EQ,
- WASM_INSN_F64_NE,
- WASM_INSN_F64_LT,
- WASM_INSN_F64_GT,
- WASM_INSN_F64_LE,
- WASM_INSN_F64_GE,
- WASM_INSN_I32_WRAP_I64,
- WASM_INSN_I32_TRUNC_F32_S,
- WASM_INSN_I32_TRUNC_F32_U,
- WASM_INSN_I32_TRUNC_F64_S,
- WASM_INSN_I32_TRUNC_F64_U,
- WASM_INSN_I64_EXTEND_I32_S,
- WASM_INSN_I64_EXTEND_I32_U,
- WASM_INSN_I64_TRUNC_F32_S,
- WASM_INSN_I64_TRUNC_F32_U,
- WASM_INSN_I64_TRUNC_F64_S,
- WASM_INSN_I64_TRUNC_F64_U,
- WASM_INSN_F32_CONVERT_I32_S,
- WASM_INSN_F32_CONVERT_I32_U,
- WASM_INSN_F32_CONVERT_I64_S,
- WASM_INSN_F32_CONVERT_I64_U,
- WASM_INSN_F32_DEMOTE_F64,
- WASM_INSN_F64_CONVERT_I32_S,
- WASM_INSN_F64_CONVERT_I32_U,
- WASM_INSN_F64_CONVERT_I64_S,
- WASM_INSN_F64_CONVERT_I64_U,
- WASM_INSN_F64_PROMOTE_F32,
- WASM_INSN_I32_REINTERPRET_F32,
- WASM_INSN_I64_REINTERPRET_F64,
- WASM_INSN_F32_REINTERPRET_I32,
- WASM_INSN_F64_REINTERPRET_I64,
-} WasmInsnKind;
-
-typedef struct WasmInsn {
- uint8_t kind;
- uint8_t type;
- int64_t imm;
- double fp;
- uint32_t align;
- uint32_t memidx;
- uint64_t offset64;
- uint32_t ntargets;
- uint32_t targets[16];
-} WasmInsn;
-
-typedef struct WasmFunc {
- char* name;
- uint32_t typeidx;
- int has_typeidx;
- int is_import;
- char* import_module;
- char* import_name;
- WasmValType params[16];
- uint32_t nparams;
- WasmValType locals[32];
- uint32_t nlocals;
- char* local_names[48];
- WasmValType results[1];
- uint32_t nresults;
- char* export_name;
- WasmInsn* insns;
- uint32_t ninsns;
- uint32_t cap_insns;
-} WasmFunc;
-
-typedef struct WasmFuncType {
- char* name;
- WasmValType params[16];
- uint32_t nparams;
- WasmValType results[1];
- uint32_t nresults;
-} WasmFuncType;
-
-typedef struct WasmMemory {
- char* name;
- uint64_t min_pages;
- uint64_t max_pages;
- int has_max;
- int is64;
- int shared;
- int is_import;
- char* import_module;
- char* import_name;
- char* export_name;
- uint8_t* data;
- uint64_t data_len;
- uint64_t data_init_len;
-} WasmMemory;
-
-typedef struct WasmTable {
- char* name;
- WasmValType elem_type;
- uint32_t min;
- uint32_t max;
- int has_max;
- int is_import;
- char* import_module;
- char* import_name;
- char* export_name;
-} WasmTable;
-
-typedef struct WasmGlobal {
- char* name;
- WasmValType type;
- uint8_t mutable_;
- WasmInsn init;
- int is_import;
- char* import_module;
- char* import_name;
- char* export_name;
-} WasmGlobal;
-
-typedef struct WasmElemSegment {
- uint32_t tableidx;
- int64_t offset;
- uint32_t funcs[64];
- uint32_t nfuncs;
-} WasmElemSegment;
-
-typedef struct WasmExport {
- char* name;
- uint8_t kind;
- uint32_t index;
-} WasmExport;
-
-typedef struct WasmCustom {
- char* name;
- uint8_t* data;
- uint32_t len;
-} WasmCustom;
-
-typedef struct WasmModule {
- CfreeHeap* heap;
- WasmFuncType* types;
- uint32_t ntypes;
- uint32_t cap_types;
- WasmFunc* funcs;
- uint32_t nfuncs;
- uint32_t cap_funcs;
- WasmMemory* memories;
- uint32_t nmemories;
- uint32_t cap_memories;
- WasmTable* tables;
- uint32_t ntables;
- uint32_t cap_tables;
- WasmGlobal* globals;
- uint32_t nglobals;
- uint32_t cap_globals;
- WasmElemSegment* elems;
- uint32_t nelems;
- uint32_t cap_elems;
- WasmExport* exports;
- uint32_t nexports;
- uint32_t cap_exports;
- WasmCustom* customs;
- uint32_t ncustoms;
- uint32_t cap_customs;
- uint32_t start_func;
- int has_start;
- int has_target_features;
- uint32_t features;
-} WasmModule;
-
-typedef struct WasmTok {
- const char* p;
- size_t len;
- uint32_t line;
- uint32_t col;
- uint8_t kind;
-} WasmTok;
-
-enum {
- WT_EOF = 0,
- WT_LPAREN,
- WT_RPAREN,
- WT_ATOM,
- WT_STRING,
-};
-
-typedef struct WatParser {
- CfreeCompiler* c;
- const char* name;
- const char* src;
- size_t len;
- size_t pos;
- uint32_t line;
- uint32_t col;
- WasmTok tok;
- WasmModule* module;
-} WatParser;
-
-typedef struct BinReader {
- CfreeCompiler* c;
- const uint8_t* data;
- size_t len;
- size_t pos;
- WasmModule* module;
-} BinReader;
-
-static CfreeSrcLoc wasm_loc(uint32_t line, uint32_t col) {
- CfreeSrcLoc loc;
- loc.file_id = 0;
- loc.line = line;
- loc.col = col;
- return loc;
-}
-
-static void wasm_error(CfreeCompiler* c, CfreeSrcLoc loc, const char* fmt,
- ...) {
- va_list ap;
- va_start(ap, fmt);
- cfree_frontend_vfatal(c, loc, fmt, ap);
-}
-
-static void* wasm_realloc(CfreeHeap* h, void* p, size_t old_n, size_t new_n) {
- return h->realloc(h, p, old_n ? old_n : 1u, new_n ? new_n : 1u,
- _Alignof(max_align_t));
-}
-
-static char* wasm_strdup(CfreeHeap* h, const char* s, size_t len) {
- char* out = (char*)h->alloc(h, len + 1u, 1);
- if (!out) return NULL;
- if (len) memcpy(out, s, len);
- out[len] = '\0';
- return out;
-}
-
-static void wasm_free_str(CfreeHeap* h, char** s) {
- if (*s) {
- h->free(h, *s, strlen(*s) + 1u);
- *s = NULL;
- }
-}
-
-static void wasm_module_init(WasmModule* m, CfreeHeap* heap) {
- memset(m, 0, sizeof *m);
- m->heap = heap;
- m->features = WASM_FEATURE_THREADS | WASM_FEATURE_TYPED_FUNC_REFS |
- WASM_FEATURE_TAIL_CALLS | WASM_FEATURE_MULTI_MEMORY |
- WASM_FEATURE_MEMORY64;
-}
-
-static void wasm_module_free(WasmModule* m) {
- uint32_t i;
- if (!m || !m->heap) return;
- for (i = 0; i < m->ntypes; ++i) wasm_free_str(m->heap, &m->types[i].name);
- if (m->types)
- m->heap->free(m->heap, m->types, sizeof(*m->types) * m->cap_types);
- for (i = 0; i < m->nfuncs; ++i) {
- WasmFunc* f = &m->funcs[i];
- wasm_free_str(m->heap, &f->name);
- wasm_free_str(m->heap, &f->import_module);
- wasm_free_str(m->heap, &f->import_name);
- wasm_free_str(m->heap, &f->export_name);
- for (uint32_t j = 0; j < f->nparams + f->nlocals; ++j)
- wasm_free_str(m->heap, &f->local_names[j]);
- if (f->insns)
- m->heap->free(m->heap, f->insns, sizeof(*f->insns) * f->cap_insns);
- }
- if (m->funcs)
- m->heap->free(m->heap, m->funcs, sizeof(*m->funcs) * m->cap_funcs);
- for (i = 0; i < m->nmemories; ++i) {
- wasm_free_str(m->heap, &m->memories[i].name);
- wasm_free_str(m->heap, &m->memories[i].import_module);
- wasm_free_str(m->heap, &m->memories[i].import_name);
- wasm_free_str(m->heap, &m->memories[i].export_name);
- if (m->memories[i].data)
- m->heap->free(m->heap, m->memories[i].data,
- (size_t)m->memories[i].data_len);
- }
- if (m->memories)
- m->heap->free(m->heap, m->memories, sizeof(*m->memories) * m->cap_memories);
- for (i = 0; i < m->ntables; ++i) {
- wasm_free_str(m->heap, &m->tables[i].name);
- wasm_free_str(m->heap, &m->tables[i].import_module);
- wasm_free_str(m->heap, &m->tables[i].import_name);
- wasm_free_str(m->heap, &m->tables[i].export_name);
- }
- if (m->tables)
- m->heap->free(m->heap, m->tables, sizeof(*m->tables) * m->cap_tables);
- for (i = 0; i < m->nglobals; ++i) {
- wasm_free_str(m->heap, &m->globals[i].name);
- wasm_free_str(m->heap, &m->globals[i].import_module);
- wasm_free_str(m->heap, &m->globals[i].import_name);
- wasm_free_str(m->heap, &m->globals[i].export_name);
- }
- if (m->globals)
- m->heap->free(m->heap, m->globals, sizeof(*m->globals) * m->cap_globals);
- if (m->elems)
- m->heap->free(m->heap, m->elems, sizeof(*m->elems) * m->cap_elems);
- for (i = 0; i < m->nexports; ++i) wasm_free_str(m->heap, &m->exports[i].name);
- if (m->exports)
- m->heap->free(m->heap, m->exports, sizeof(*m->exports) * m->cap_exports);
- for (i = 0; i < m->ncustoms; ++i) {
- wasm_free_str(m->heap, &m->customs[i].name);
- if (m->customs[i].data)
- m->heap->free(m->heap, m->customs[i].data, m->customs[i].len);
- }
- if (m->customs)
- m->heap->free(m->heap, m->customs, sizeof(*m->customs) * m->cap_customs);
- memset(m, 0, sizeof *m);
-}
-
-static void wasm_memory_ensure(CfreeCompiler* c, WasmModule* m,
- uint32_t memidx, uint64_t min_len) {
- WasmMemory* mem;
- uint64_t old_len, new_len;
- void* p;
- if (memidx >= m->nmemories)
- wasm_error(c, wasm_loc(0, 0), "wasm: data segment without memory");
- mem = &m->memories[memidx];
- old_len = mem->data_len;
- new_len = old_len;
- if (new_len < min_len) new_len = min_len;
- if (mem->min_pages && new_len < mem->min_pages * 65536u)
- new_len = mem->min_pages * 65536u;
- if (new_len > SIZE_MAX)
- wasm_error(c, wasm_loc(0, 0), "wasm: data segment too large");
- if (new_len == old_len) return;
- p = wasm_realloc(m->heap, mem->data, (size_t)old_len, (size_t)new_len);
- if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
- mem->data = (uint8_t*)p;
- memset(mem->data + old_len, 0, (size_t)(new_len - old_len));
- mem->data_len = new_len;
-}
-
-static WasmMemory* wasm_add_memory(CfreeCompiler* c, WasmModule* m) {
- WasmMemory* mem;
- if (m->nmemories == m->cap_memories) {
- uint32_t new_cap = m->cap_memories ? m->cap_memories * 2u : 2u;
- void* p =
- wasm_realloc(m->heap, m->memories,
- sizeof(*m->memories) * m->cap_memories,
- sizeof(*m->memories) * new_cap);
- if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
- m->memories = (WasmMemory*)p;
- memset(m->memories + m->cap_memories, 0,
- sizeof(*m->memories) * (new_cap - m->cap_memories));
- m->cap_memories = new_cap;
- }
- mem = &m->memories[m->nmemories++];
- memset(mem, 0, sizeof *mem);
- return mem;
-}
-
-static WasmFunc* wasm_add_func(CfreeCompiler* c, WasmModule* m) {
- WasmFunc* f;
- if (m->nfuncs == m->cap_funcs) {
- uint32_t new_cap = m->cap_funcs ? m->cap_funcs * 2u : 4u;
- void* p = wasm_realloc(m->heap, m->funcs, sizeof(*m->funcs) * m->cap_funcs,
- sizeof(*m->funcs) * new_cap);
- if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
- m->funcs = (WasmFunc*)p;
- memset(m->funcs + m->cap_funcs, 0,
- sizeof(*m->funcs) * (new_cap - m->cap_funcs));
- m->cap_funcs = new_cap;
- }
- f = &m->funcs[m->nfuncs++];
- memset(f, 0, sizeof *f);
- return f;
-}
-
-static WasmFuncType* wasm_add_type(CfreeCompiler* c, WasmModule* m) {
- WasmFuncType* t;
- if (m->ntypes == m->cap_types) {
- uint32_t new_cap = m->cap_types ? m->cap_types * 2u : 8u;
- void* p = wasm_realloc(m->heap, m->types, sizeof(*m->types) * m->cap_types,
- sizeof(*m->types) * new_cap);
- if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
- m->types = (WasmFuncType*)p;
- memset(m->types + m->cap_types, 0,
- sizeof(*m->types) * (new_cap - m->cap_types));
- m->cap_types = new_cap;
- }
- t = &m->types[m->ntypes++];
- memset(t, 0, sizeof *t);
- return t;
-}
-
-static uint32_t wasm_intern_func_type(CfreeCompiler* c, WasmModule* m,
- const WasmFunc* f) {
- uint32_t i;
- for (i = 0; i < m->ntypes; ++i) {
- WasmFuncType* t = &m->types[i];
- if (t->nparams == f->nparams && t->nresults == f->nresults &&
- memcmp(t->params, f->params, sizeof(t->params[0]) * t->nparams) == 0 &&
- memcmp(t->results, f->results, sizeof(t->results[0]) * t->nresults) ==
- 0)
- return i;
- }
- {
- WasmFuncType* t = wasm_add_type(c, m);
- t->nparams = f->nparams;
- memcpy(t->params, f->params, sizeof(t->params[0]) * f->nparams);
- t->nresults = f->nresults;
- memcpy(t->results, f->results, sizeof(t->results[0]) * f->nresults);
- return m->ntypes - 1u;
- }
-}
-
-static WasmTable* wasm_add_table(CfreeCompiler* c, WasmModule* m) {
- WasmTable* t;
- if (m->ntables == m->cap_tables) {
- uint32_t new_cap = m->cap_tables ? m->cap_tables * 2u : 2u;
- void* p =
- wasm_realloc(m->heap, m->tables, sizeof(*m->tables) * m->cap_tables,
- sizeof(*m->tables) * new_cap);
- if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
- m->tables = (WasmTable*)p;
- memset(m->tables + m->cap_tables, 0,
- sizeof(*m->tables) * (new_cap - m->cap_tables));
- m->cap_tables = new_cap;
- }
- t = &m->tables[m->ntables++];
- memset(t, 0, sizeof *t);
- return t;
-}
-
-static WasmGlobal* wasm_add_global(CfreeCompiler* c, WasmModule* m) {
- WasmGlobal* g;
- if (m->nglobals == m->cap_globals) {
- uint32_t new_cap = m->cap_globals ? m->cap_globals * 2u : 4u;
- void* p =
- wasm_realloc(m->heap, m->globals, sizeof(*m->globals) * m->cap_globals,
- sizeof(*m->globals) * new_cap);
- if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
- m->globals = (WasmGlobal*)p;
- memset(m->globals + m->cap_globals, 0,
- sizeof(*m->globals) * (new_cap - m->cap_globals));
- m->cap_globals = new_cap;
- }
- g = &m->globals[m->nglobals++];
- memset(g, 0, sizeof *g);
- return g;
-}
-
-static WasmElemSegment* wasm_add_elem(CfreeCompiler* c, WasmModule* m) {
- WasmElemSegment* e;
- if (m->nelems == m->cap_elems) {
- uint32_t new_cap = m->cap_elems ? m->cap_elems * 2u : 4u;
- void* p = wasm_realloc(m->heap, m->elems, sizeof(*m->elems) * m->cap_elems,
- sizeof(*m->elems) * new_cap);
- if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
- m->elems = (WasmElemSegment*)p;
- memset(m->elems + m->cap_elems, 0,
- sizeof(*m->elems) * (new_cap - m->cap_elems));
- m->cap_elems = new_cap;
- }
- e = &m->elems[m->nelems++];
- memset(e, 0, sizeof *e);
- return e;
-}
-
-static WasmExport* wasm_add_export(CfreeCompiler* c, WasmModule* m) {
- WasmExport* e;
- if (m->nexports == m->cap_exports) {
- uint32_t new_cap = m->cap_exports ? m->cap_exports * 2u : 8u;
- void* p =
- wasm_realloc(m->heap, m->exports, sizeof(*m->exports) * m->cap_exports,
- sizeof(*m->exports) * new_cap);
- if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
- m->exports = (WasmExport*)p;
- memset(m->exports + m->cap_exports, 0,
- sizeof(*m->exports) * (new_cap - m->cap_exports));
- m->cap_exports = new_cap;
- }
- e = &m->exports[m->nexports++];
- memset(e, 0, sizeof *e);
- return e;
-}
-
-static WasmCustom* wasm_add_custom(CfreeCompiler* c, WasmModule* m) {
- WasmCustom* cs;
- if (m->ncustoms == m->cap_customs) {
- uint32_t new_cap = m->cap_customs ? m->cap_customs * 2u : 4u;
- void* p =
- wasm_realloc(m->heap, m->customs, sizeof(*m->customs) * m->cap_customs,
- sizeof(*m->customs) * new_cap);
- if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
- m->customs = (WasmCustom*)p;
- memset(m->customs + m->cap_customs, 0,
- sizeof(*m->customs) * (new_cap - m->cap_customs));
- m->cap_customs = new_cap;
- }
- cs = &m->customs[m->ncustoms++];
- memset(cs, 0, sizeof *cs);
- return cs;
-}
-
-static void wasm_func_add_insn(CfreeCompiler* c, WasmModule* m, WasmFunc* f,
- WasmInsnKind kind, int64_t imm) {
- if (f->ninsns == f->cap_insns) {
- uint32_t new_cap = f->cap_insns ? f->cap_insns * 2u : 16u;
- void* p = wasm_realloc(m->heap, f->insns, sizeof(*f->insns) * f->cap_insns,
- sizeof(*f->insns) * new_cap);
- if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
- f->insns = (WasmInsn*)p;
- f->cap_insns = new_cap;
- }
- f->insns[f->ninsns].kind = (uint8_t)kind;
- f->insns[f->ninsns].type = 0;
- f->insns[f->ninsns].imm = imm;
- f->insns[f->ninsns].fp = 0.0;
- f->insns[f->ninsns].align = 0;
- f->insns[f->ninsns].memidx = 0;
- f->insns[f->ninsns].offset64 = imm < 0 ? 0 : (uint64_t)imm;
- f->insns[f->ninsns].ntargets = 0;
- f->ninsns++;
-}
-
-static void wasm_func_add_mem_insn(CfreeCompiler* c, WasmModule* m, WasmFunc* f,
- WasmInsnKind kind, uint32_t align,
- uint64_t offset, uint32_t memidx) {
- wasm_func_add_insn(c, m, f, kind, offset > INT64_MAX ? INT64_MAX
- : (int64_t)offset);
- f->insns[f->ninsns - 1u].align = align;
- f->insns[f->ninsns - 1u].offset64 = offset;
- f->insns[f->ninsns - 1u].memidx = memidx;
-}
-
-static void wasm_func_add_fp_insn(CfreeCompiler* c, WasmModule* m, WasmFunc* f,
- WasmInsnKind kind, double value) {
- wasm_func_add_insn(c, m, f, kind, 0);
- f->insns[f->ninsns - 1u].fp = value;
-}
-
-static int tok_is(WasmTok t, const char* s) {
- size_t n = strlen(s);
- return t.kind == WT_ATOM && t.len == n && memcmp(t.p, s, n) == 0;
-}
-
-static int wasm_name_eq(const char* name, WasmTok t) {
- size_t n;
- if (!name || t.kind != WT_ATOM) return 0;
- n = strlen(name);
- return t.len == n && memcmp(name, t.p, n) == 0;
-}
-
-static int wat_hex(char ch) {
- if (ch >= '0' && ch <= '9') return ch - '0';
- if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
- if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
- return -1;
-}
-
-static char* wat_dup_string(WatParser* p, WasmTok t, size_t* len_out) {
- char* out;
- size_t i, n = 0;
- if (t.kind != WT_STRING)
- wasm_error(p->c, wasm_loc(t.line, t.col), "wasm wat: expected string");
- out = (char*)p->module->heap->alloc(p->module->heap, t.len + 1u, 1);
- if (!out) wasm_error(p->c, wasm_loc(t.line, t.col), "wasm: out of memory");
- for (i = 0; i < t.len; ++i) {
- char ch = t.p[i];
- if (ch != '\\') {
- out[n++] = ch;
- continue;
- }
- if (++i >= t.len)
- wasm_error(p->c, wasm_loc(t.line, t.col),
- "wasm wat: unterminated string escape");
- ch = t.p[i];
- switch (ch) {
- case 'n':
- out[n++] = '\n';
- break;
- case 'r':
- out[n++] = '\r';
- break;
- case 't':
- out[n++] = '\t';
- break;
- case '"':
- case '\'':
- case '\\':
- out[n++] = ch;
- break;
- default: {
- int hi = wat_hex(ch);
- int lo = (i + 1u < t.len) ? wat_hex(t.p[i + 1u]) : -1;
- if (hi < 0 || lo < 0)
- wasm_error(p->c, wasm_loc(t.line, t.col),
- "wasm wat: unsupported string escape");
- out[n++] = (char)((hi << 4) | lo);
- i++;
- break;
- }
- }
- }
- out[n] = '\0';
- if (len_out) *len_out = n;
- return out;
-}
-
-static void wat_next(WatParser* p) {
- const char* s = p->src;
- while (p->pos < p->len) {
- char ch = s[p->pos];
- if (ch == '\n') {
- p->pos++;
- p->line++;
- p->col = 1;
- continue;
- }
- if (ch == ' ' || ch == '\t' || ch == '\r') {
- p->pos++;
- p->col++;
- continue;
- }
- if (ch == ';' && p->pos + 1u < p->len && s[p->pos + 1u] == ';') {
- while (p->pos < p->len && s[p->pos] != '\n') {
- p->pos++;
- p->col++;
- }
- continue;
- }
- if (ch == '(' && p->pos + 1u < p->len && s[p->pos + 1u] == ';') {
- uint32_t depth = 1;
- p->pos += 2u;
- p->col += 2u;
- while (depth && p->pos < p->len) {
- if (s[p->pos] == '\n') {
- p->pos++;
- p->line++;
- p->col = 1;
- } else if (s[p->pos] == '(' && p->pos + 1u < p->len &&
- s[p->pos + 1u] == ';') {
- p->pos += 2u;
- p->col += 2u;
- depth++;
- } else if (s[p->pos] == ';' && p->pos + 1u < p->len &&
- s[p->pos + 1u] == ')') {
- p->pos += 2u;
- p->col += 2u;
- depth--;
- } else {
- p->pos++;
- p->col++;
- }
- }
- if (depth)
- wasm_error(p->c, wasm_loc(p->line, p->col),
- "wasm wat: unterminated block comment");
- continue;
- }
- break;
- }
- p->tok.p = s + p->pos;
- p->tok.len = 0;
- p->tok.line = p->line;
- p->tok.col = p->col;
- p->tok.kind = WT_EOF;
- if (p->pos >= p->len) return;
- if (s[p->pos] == '(') {
- p->tok.kind = WT_LPAREN;
- p->tok.len = 1;
- p->pos++;
- p->col++;
- return;
- }
- if (s[p->pos] == ')') {
- p->tok.kind = WT_RPAREN;
- p->tok.len = 1;
- p->pos++;
- p->col++;
- return;
- }
- if (s[p->pos] == '"') {
- size_t start = ++p->pos;
- p->col++;
- p->tok.kind = WT_STRING;
- p->tok.p = s + start;
- while (p->pos < p->len && s[p->pos] != '"') {
- if (s[p->pos] == '\\') {
- p->pos++;
- p->col++;
- if (p->pos >= p->len) break;
- } else if ((unsigned char)s[p->pos] < 0x20) {
- wasm_error(p->c, wasm_loc(p->line, p->col),
- "wasm wat: unsupported string escape/control character");
- }
- p->pos++;
- p->col++;
- }
- if (p->pos >= p->len)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: unterminated string");
- p->tok.len = (size_t)(s + p->pos - p->tok.p);
- p->pos++;
- p->col++;
- return;
- }
- p->tok.kind = WT_ATOM;
- while (p->pos < p->len) {
- char ch = s[p->pos];
- if (ch == '(' || ch == ')' || ch == ' ' || ch == '\t' || ch == '\r' ||
- ch == '\n')
- break;
- p->pos++;
- p->col++;
- }
- p->tok.len = (size_t)(s + p->pos - p->tok.p);
-}
-
-static void wat_expect(WatParser* p, uint8_t kind, const char* what) {
- if (p->tok.kind != kind)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), "wasm wat: expected %s",
- what);
- wat_next(p);
-}
-
-static int wat_parse_i64(WatParser* p, int64_t* out) {
- const char* s = p->tok.p;
- size_t n = p->tok.len, i = 0;
- uint64_t v = 0;
- int neg = 0;
- unsigned base = 10;
- if (p->tok.kind != WT_ATOM || n == 0) return 0;
- if (s[0] == '-' || s[0] == '+') {
- neg = s[0] == '-';
- i = 1;
- }
- if (i + 2u <= n && s[i] == '0' && (s[i + 1u] == 'x' || s[i + 1u] == 'X')) {
- base = 16;
- i += 2u;
- }
- if (i == n) return 0;
- for (; i < n; ++i) {
- int hd;
- unsigned d;
- if (s[i] == '_') continue;
- hd = wat_hex(s[i]);
- if (hd < 0 || (unsigned)hd >= base) return 0;
- d = (unsigned)hd;
- if (v > (UINT64_MAX - d) / base) return 0;
- v = v * base + d;
- }
- *out = neg ? -(int64_t)v : (int64_t)v;
- return 1;
-}
-
-static int wat_parse_f64(WatParser* p, double* out) {
- char buf[128];
- char* end = NULL;
- if (p->tok.kind != WT_ATOM || p->tok.len == 0 || p->tok.len >= sizeof buf)
- return 0;
- memcpy(buf, p->tok.p, p->tok.len);
- buf[p->tok.len] = '\0';
- *out = strtod(buf, &end);
- return end && *end == '\0';
-}
-
-static int wat_val_type(WasmTok t, WasmValType* out) {
- if (tok_is(t, "i32")) {
- *out = WASM_VAL_I32;
- return 1;
- }
- if (tok_is(t, "i64")) {
- *out = WASM_VAL_I64;
- return 1;
- }
- if (tok_is(t, "f32")) {
- *out = WASM_VAL_F32;
- return 1;
- }
- if (tok_is(t, "f64")) {
- *out = WASM_VAL_F64;
- return 1;
- }
- if (tok_is(t, "funcref")) {
- *out = WASM_VAL_FUNCREF;
- return 1;
- }
- if (tok_is(t, "externref")) {
- *out = WASM_VAL_EXTERNREF;
- return 1;
- }
- return 0;
-}
-
-static int wasm_is_num_type(WasmValType vt) {
- return vt == WASM_VAL_I32 || vt == WASM_VAL_I64 || vt == WASM_VAL_F32 ||
- vt == WASM_VAL_F64;
-}
-
-static int wasm_is_ref_type(WasmValType vt) {
- return vt == WASM_VAL_FUNCREF || vt == WASM_VAL_EXTERNREF;
-}
-
-static int wasm_is_frontend_value_type(WasmValType vt) {
- return wasm_is_num_type(vt) || vt == WASM_VAL_FUNCREF;
-}
-
-static int wasm_feature_enabled(const WasmModule* m, WasmFeatureSet feature) {
- return (m->features & (uint32_t)feature) != 0;
-}
-
-static void wasm_require_feature(CfreeCompiler* c, const WasmModule* m,
- WasmFeatureSet feature,
- const char* feature_name,
- const char* what) {
- if (!wasm_feature_enabled(m, feature))
- wasm_error(c, wasm_loc(0, 0), "wasm: %s requires %s", what,
- feature_name);
-}
-
-static void wat_require_feature(WatParser* p, WasmFeatureSet feature,
- const char* feature_name, const char* what) {
- if (!wasm_feature_enabled(p->module, feature))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: %s requires %s", what, feature_name);
-}
-
-static uint8_t wasm_export_kind_from_tok(WasmTok t) {
- if (tok_is(t, "func")) return 0;
- if (tok_is(t, "table")) return 1;
- if (tok_is(t, "memory")) return 2;
- if (tok_is(t, "global")) return 3;
- return 0xffu;
-}
-
-static void wat_skip_list(WatParser* p) {
- uint32_t depth = 1;
- while (depth && p->tok.kind != WT_EOF) {
- if (p->tok.kind == WT_LPAREN)
- depth++;
- else if (p->tok.kind == WT_RPAREN)
- depth--;
- wat_next(p);
- }
-}
-
-static int wat_instr_kind(WasmTok t, WasmInsnKind* out, int* has_imm) {
- *has_imm = 0;
- if (tok_is(t, "unreachable")) {
- *out = WASM_INSN_UNREACHABLE;
- return 1;
- }
- if (tok_is(t, "nop")) {
- *out = WASM_INSN_NOP;
- return 1;
- }
- if (tok_is(t, "block")) {
- *out = WASM_INSN_BLOCK;
- return 1;
- }
- if (tok_is(t, "loop")) {
- *out = WASM_INSN_LOOP;
- return 1;
- }
- if (tok_is(t, "if")) {
- *out = WASM_INSN_IF;
- return 1;
- }
- if (tok_is(t, "else")) {
- *out = WASM_INSN_ELSE;
- return 1;
- }
- if (tok_is(t, "end")) {
- *out = WASM_INSN_END;
- return 1;
- }
- if (tok_is(t, "br")) {
- *out = WASM_INSN_BR;
- *has_imm = 1;
- return 1;
- }
- if (tok_is(t, "br_if")) {
- *out = WASM_INSN_BR_IF;
- *has_imm = 1;
- return 1;
- }
- if (tok_is(t, "br_table")) {
- *out = WASM_INSN_BR_TABLE;
- return 1;
- }
- if (tok_is(t, "select")) {
- *out = WASM_INSN_SELECT;
- return 1;
- }
- if (tok_is(t, "f32.const")) {
- *out = WASM_INSN_F32_CONST;
- *has_imm = 1;
- return 1;
- }
- if (tok_is(t, "f64.const")) {
- *out = WASM_INSN_F64_CONST;
- *has_imm = 1;
- return 1;
- }
- if (tok_is(t, "i32.const")) {
- *out = WASM_INSN_I32_CONST;
- *has_imm = 1;
- return 1;
- }
- if (tok_is(t, "i64.const")) {
- *out = WASM_INSN_I64_CONST;
- *has_imm = 1;
- return 1;
- }
- if (tok_is(t, "local.get")) {
- *out = WASM_INSN_LOCAL_GET;
- *has_imm = 1;
- return 1;
- }
- if (tok_is(t, "local.set")) {
- *out = WASM_INSN_LOCAL_SET;
- *has_imm = 1;
- return 1;
- }
- if (tok_is(t, "local.tee")) {
- *out = WASM_INSN_LOCAL_TEE;
- *has_imm = 1;
- return 1;
- }
- if (tok_is(t, "call")) {
- *out = WASM_INSN_CALL;
- *has_imm = 1;
- return 1;
- }
- if (tok_is(t, "call_indirect")) {
- *out = WASM_INSN_CALL_INDIRECT;
- return 1;
- }
- if (tok_is(t, "call_ref")) {
- *out = WASM_INSN_CALL_REF;
- return 1;
- }
- if (tok_is(t, "return_call")) {
- *out = WASM_INSN_RETURN_CALL;
- *has_imm = 1;
- return 1;
- }
- if (tok_is(t, "return_call_indirect")) {
- *out = WASM_INSN_RETURN_CALL_INDIRECT;
- return 1;
- }
- if (tok_is(t, "return_call_ref")) {
- *out = WASM_INSN_RETURN_CALL_REF;
- return 1;
- }
- if (tok_is(t, "ref.null")) {
- *out = WASM_INSN_REF_NULL;
- *has_imm = 1;
- return 1;
- }
- if (tok_is(t, "ref.func")) {
- *out = WASM_INSN_REF_FUNC;
- *has_imm = 1;
- return 1;
- }
- if (tok_is(t, "ref.is_null")) {
- *out = WASM_INSN_REF_IS_NULL;
- return 1;
- }
- if (tok_is(t, "global.get")) {
- *out = WASM_INSN_GLOBAL_GET;
- *has_imm = 1;
- return 1;
- }
- if (tok_is(t, "global.set")) {
- *out = WASM_INSN_GLOBAL_SET;
- *has_imm = 1;
- return 1;
- }
- if (tok_is(t, "return")) {
- *out = WASM_INSN_RETURN;
- return 1;
- }
- if (tok_is(t, "drop")) {
- *out = WASM_INSN_DROP;
- return 1;
- }
- if (tok_is(t, "atomic.fence")) {
- *out = WASM_INSN_ATOMIC_FENCE;
- return 1;
- }
- if (tok_is(t, "i32.load")) {
- *out = WASM_INSN_I32_LOAD;
- return 1;
- }
- if (tok_is(t, "i32.atomic.load")) {
- *out = WASM_INSN_I32_ATOMIC_LOAD;
- return 1;
- }
- if (tok_is(t, "i64.load")) {
- *out = WASM_INSN_I64_LOAD;
- return 1;
- }
- if (tok_is(t, "i64.atomic.load")) {
- *out = WASM_INSN_I64_ATOMIC_LOAD;
- return 1;
- }
- if (tok_is(t, "i32.load8_s")) {
- *out = WASM_INSN_I32_LOAD8_S;
- return 1;
- }
- if (tok_is(t, "i32.load8_u")) {
- *out = WASM_INSN_I32_LOAD8_U;
- return 1;
- }
- if (tok_is(t, "i32.atomic.load8_u")) {
- *out = WASM_INSN_I32_ATOMIC_LOAD8_U;
- return 1;
- }
- if (tok_is(t, "i32.load16_s")) {
- *out = WASM_INSN_I32_LOAD16_S;
- return 1;
- }
- if (tok_is(t, "i32.load16_u")) {
- *out = WASM_INSN_I32_LOAD16_U;
- return 1;
- }
- if (tok_is(t, "i32.atomic.load16_u")) {
- *out = WASM_INSN_I32_ATOMIC_LOAD16_U;
- return 1;
- }
- if (tok_is(t, "i64.load8_s")) {
- *out = WASM_INSN_I64_LOAD8_S;
- return 1;
- }
- if (tok_is(t, "i64.load8_u")) {
- *out = WASM_INSN_I64_LOAD8_U;
- return 1;
- }
- if (tok_is(t, "i64.atomic.load8_u")) {
- *out = WASM_INSN_I64_ATOMIC_LOAD8_U;
- return 1;
- }
- if (tok_is(t, "i64.load16_s")) {
- *out = WASM_INSN_I64_LOAD16_S;
- return 1;
- }
- if (tok_is(t, "i64.load16_u")) {
- *out = WASM_INSN_I64_LOAD16_U;
- return 1;
- }
- if (tok_is(t, "i64.atomic.load16_u")) {
- *out = WASM_INSN_I64_ATOMIC_LOAD16_U;
- return 1;
- }
- if (tok_is(t, "i64.load32_s")) {
- *out = WASM_INSN_I64_LOAD32_S;
- return 1;
- }
- if (tok_is(t, "i64.load32_u")) {
- *out = WASM_INSN_I64_LOAD32_U;
- return 1;
- }
- if (tok_is(t, "i64.atomic.load32_u")) {
- *out = WASM_INSN_I64_ATOMIC_LOAD32_U;
- return 1;
- }
- if (tok_is(t, "i32.store")) {
- *out = WASM_INSN_I32_STORE;
- return 1;
- }
- if (tok_is(t, "i32.atomic.store")) {
- *out = WASM_INSN_I32_ATOMIC_STORE;
- return 1;
- }
- if (tok_is(t, "i64.store")) {
- *out = WASM_INSN_I64_STORE;
- return 1;
- }
- if (tok_is(t, "i64.atomic.store")) {
- *out = WASM_INSN_I64_ATOMIC_STORE;
- return 1;
- }
- if (tok_is(t, "i32.store8")) {
- *out = WASM_INSN_I32_STORE8;
- return 1;
- }
- if (tok_is(t, "i32.atomic.store8")) {
- *out = WASM_INSN_I32_ATOMIC_STORE8;
- return 1;
- }
- if (tok_is(t, "i32.store16")) {
- *out = WASM_INSN_I32_STORE16;
- return 1;
- }
- if (tok_is(t, "i32.atomic.store16")) {
- *out = WASM_INSN_I32_ATOMIC_STORE16;
- return 1;
- }
- if (tok_is(t, "i64.store8")) {
- *out = WASM_INSN_I64_STORE8;
- return 1;
- }
- if (tok_is(t, "i64.atomic.store8")) {
- *out = WASM_INSN_I64_ATOMIC_STORE8;
- return 1;
- }
- if (tok_is(t, "i64.store16")) {
- *out = WASM_INSN_I64_STORE16;
- return 1;
- }
- if (tok_is(t, "i64.atomic.store16")) {
- *out = WASM_INSN_I64_ATOMIC_STORE16;
- return 1;
- }
- if (tok_is(t, "i64.store32")) {
- *out = WASM_INSN_I64_STORE32;
- return 1;
- }
- if (tok_is(t, "i64.atomic.store32")) {
- *out = WASM_INSN_I64_ATOMIC_STORE32;
- return 1;
- }
- if (tok_is(t, "i32.atomic.rmw.add")) {
- *out = WASM_INSN_I32_ATOMIC_RMW_ADD;
- return 1;
- }
- if (tok_is(t, "i64.atomic.rmw.add")) {
- *out = WASM_INSN_I64_ATOMIC_RMW_ADD;
- return 1;
- }
- if (tok_is(t, "i32.atomic.rmw.sub")) {
- *out = WASM_INSN_I32_ATOMIC_RMW_SUB;
- return 1;
- }
- if (tok_is(t, "i64.atomic.rmw.sub")) {
- *out = WASM_INSN_I64_ATOMIC_RMW_SUB;
- return 1;
- }
- if (tok_is(t, "i32.atomic.rmw.and")) {
- *out = WASM_INSN_I32_ATOMIC_RMW_AND;
- return 1;
- }
- if (tok_is(t, "i64.atomic.rmw.and")) {
- *out = WASM_INSN_I64_ATOMIC_RMW_AND;
- return 1;
- }
- if (tok_is(t, "i32.atomic.rmw.or")) {
- *out = WASM_INSN_I32_ATOMIC_RMW_OR;
- return 1;
- }
- if (tok_is(t, "i64.atomic.rmw.or")) {
- *out = WASM_INSN_I64_ATOMIC_RMW_OR;
- return 1;
- }
- if (tok_is(t, "i32.atomic.rmw.xor")) {
- *out = WASM_INSN_I32_ATOMIC_RMW_XOR;
- return 1;
- }
- if (tok_is(t, "i64.atomic.rmw.xor")) {
- *out = WASM_INSN_I64_ATOMIC_RMW_XOR;
- return 1;
- }
- if (tok_is(t, "i32.atomic.rmw.xchg")) {
- *out = WASM_INSN_I32_ATOMIC_RMW_XCHG;
- return 1;
- }
- if (tok_is(t, "i64.atomic.rmw.xchg")) {
- *out = WASM_INSN_I64_ATOMIC_RMW_XCHG;
- return 1;
- }
- if (tok_is(t, "i32.atomic.rmw.cmpxchg")) {
- *out = WASM_INSN_I32_ATOMIC_RMW_CMPXCHG;
- return 1;
- }
- if (tok_is(t, "i64.atomic.rmw.cmpxchg")) {
- *out = WASM_INSN_I64_ATOMIC_RMW_CMPXCHG;
- return 1;
- }
- if (tok_is(t, "memory.atomic.wait32") || tok_is(t, "i32.atomic.wait")) {
- *out = WASM_INSN_I32_ATOMIC_WAIT;
- return 1;
- }
- if (tok_is(t, "memory.atomic.wait64") || tok_is(t, "i64.atomic.wait")) {
- *out = WASM_INSN_I64_ATOMIC_WAIT;
- return 1;
- }
- if (tok_is(t, "memory.atomic.notify") || tok_is(t, "atomic.notify")) {
- *out = WASM_INSN_MEMORY_ATOMIC_NOTIFY;
- return 1;
- }
- if (tok_is(t, "memory.size")) {
- *out = WASM_INSN_MEMORY_SIZE;
- return 1;
- }
- if (tok_is(t, "memory.grow")) {
- *out = WASM_INSN_MEMORY_GROW;
- return 1;
- }
- if (tok_is(t, "i32.add")) {
- *out = WASM_INSN_I32_ADD;
- return 1;
- }
- if (tok_is(t, "i32.sub")) {
- *out = WASM_INSN_I32_SUB;
- return 1;
- }
- if (tok_is(t, "i32.mul")) {
- *out = WASM_INSN_I32_MUL;
- return 1;
- }
- if (tok_is(t, "i32.div_s")) {
- *out = WASM_INSN_I32_DIV_S;
- return 1;
- }
- if (tok_is(t, "i32.div_u")) {
- *out = WASM_INSN_I32_DIV_U;
- return 1;
- }
- if (tok_is(t, "i32.rem_s")) {
- *out = WASM_INSN_I32_REM_S;
- return 1;
- }
- if (tok_is(t, "i32.rem_u")) {
- *out = WASM_INSN_I32_REM_U;
- return 1;
- }
- if (tok_is(t, "i32.and")) {
- *out = WASM_INSN_I32_AND;
- return 1;
- }
- if (tok_is(t, "i32.or")) {
- *out = WASM_INSN_I32_OR;
- return 1;
- }
- if (tok_is(t, "i32.xor")) {
- *out = WASM_INSN_I32_XOR;
- return 1;
- }
- if (tok_is(t, "i32.shl")) {
- *out = WASM_INSN_I32_SHL;
- return 1;
- }
- if (tok_is(t, "i32.shr_s")) {
- *out = WASM_INSN_I32_SHR_S;
- return 1;
- }
- if (tok_is(t, "i32.shr_u")) {
- *out = WASM_INSN_I32_SHR_U;
- return 1;
- }
- if (tok_is(t, "i32.rotl")) {
- *out = WASM_INSN_I32_ROTL;
- return 1;
- }
- if (tok_is(t, "i32.rotr")) {
- *out = WASM_INSN_I32_ROTR;
- return 1;
- }
- if (tok_is(t, "i32.clz")) {
- *out = WASM_INSN_I32_CLZ;
- return 1;
- }
- if (tok_is(t, "i32.ctz")) {
- *out = WASM_INSN_I32_CTZ;
- return 1;
- }
- if (tok_is(t, "i32.popcnt")) {
- *out = WASM_INSN_I32_POPCNT;
- return 1;
- }
- if (tok_is(t, "i32.eqz")) {
- *out = WASM_INSN_I32_EQZ;
- return 1;
- }
- if (tok_is(t, "i32.eq")) {
- *out = WASM_INSN_I32_EQ;
- return 1;
- }
- if (tok_is(t, "i32.ne")) {
- *out = WASM_INSN_I32_NE;
- return 1;
- }
- if (tok_is(t, "i32.lt_s")) {
- *out = WASM_INSN_I32_LT_S;
- return 1;
- }
- if (tok_is(t, "i32.lt_u")) {
- *out = WASM_INSN_I32_LT_U;
- return 1;
- }
- if (tok_is(t, "i32.gt_s")) {
- *out = WASM_INSN_I32_GT_S;
- return 1;
- }
- if (tok_is(t, "i32.gt_u")) {
- *out = WASM_INSN_I32_GT_U;
- return 1;
- }
- if (tok_is(t, "i32.le_s")) {
- *out = WASM_INSN_I32_LE_S;
- return 1;
- }
- if (tok_is(t, "i32.le_u")) {
- *out = WASM_INSN_I32_LE_U;
- return 1;
- }
- if (tok_is(t, "i32.ge_s")) {
- *out = WASM_INSN_I32_GE_S;
- return 1;
- }
- if (tok_is(t, "i32.ge_u")) {
- *out = WASM_INSN_I32_GE_U;
- return 1;
- }
- if (tok_is(t, "i64.add")) {
- *out = WASM_INSN_I64_ADD;
- return 1;
- }
- if (tok_is(t, "i64.sub")) {
- *out = WASM_INSN_I64_SUB;
- return 1;
- }
- if (tok_is(t, "i64.mul")) {
- *out = WASM_INSN_I64_MUL;
- return 1;
- }
- if (tok_is(t, "i64.div_s")) {
- *out = WASM_INSN_I64_DIV_S;
- return 1;
- }
- if (tok_is(t, "i64.div_u")) {
- *out = WASM_INSN_I64_DIV_U;
- return 1;
- }
- if (tok_is(t, "i64.rem_s")) {
- *out = WASM_INSN_I64_REM_S;
- return 1;
- }
- if (tok_is(t, "i64.rem_u")) {
- *out = WASM_INSN_I64_REM_U;
- return 1;
- }
- if (tok_is(t, "i64.and")) {
- *out = WASM_INSN_I64_AND;
- return 1;
- }
- if (tok_is(t, "i64.or")) {
- *out = WASM_INSN_I64_OR;
- return 1;
- }
- if (tok_is(t, "i64.xor")) {
- *out = WASM_INSN_I64_XOR;
- return 1;
- }
- if (tok_is(t, "i64.shl")) {
- *out = WASM_INSN_I64_SHL;
- return 1;
- }
- if (tok_is(t, "i64.shr_s")) {
- *out = WASM_INSN_I64_SHR_S;
- return 1;
- }
- if (tok_is(t, "i64.shr_u")) {
- *out = WASM_INSN_I64_SHR_U;
- return 1;
- }
- if (tok_is(t, "i64.rotl")) {
- *out = WASM_INSN_I64_ROTL;
- return 1;
- }
- if (tok_is(t, "i64.rotr")) {
- *out = WASM_INSN_I64_ROTR;
- return 1;
- }
- if (tok_is(t, "i64.clz")) {
- *out = WASM_INSN_I64_CLZ;
- return 1;
- }
- if (tok_is(t, "i64.ctz")) {
- *out = WASM_INSN_I64_CTZ;
- return 1;
- }
- if (tok_is(t, "i64.popcnt")) {
- *out = WASM_INSN_I64_POPCNT;
- return 1;
- }
- if (tok_is(t, "i64.eqz")) {
- *out = WASM_INSN_I64_EQZ;
- return 1;
- }
- if (tok_is(t, "i64.eq")) {
- *out = WASM_INSN_I64_EQ;
- return 1;
- }
- if (tok_is(t, "i64.ne")) {
- *out = WASM_INSN_I64_NE;
- return 1;
- }
- if (tok_is(t, "i64.lt_s")) {
- *out = WASM_INSN_I64_LT_S;
- return 1;
- }
- if (tok_is(t, "i64.lt_u")) {
- *out = WASM_INSN_I64_LT_U;
- return 1;
- }
- if (tok_is(t, "i64.gt_s")) {
- *out = WASM_INSN_I64_GT_S;
- return 1;
- }
- if (tok_is(t, "i64.gt_u")) {
- *out = WASM_INSN_I64_GT_U;
- return 1;
- }
- if (tok_is(t, "i64.le_s")) {
- *out = WASM_INSN_I64_LE_S;
- return 1;
- }
- if (tok_is(t, "i64.le_u")) {
- *out = WASM_INSN_I64_LE_U;
- return 1;
- }
- if (tok_is(t, "i64.ge_s")) {
- *out = WASM_INSN_I64_GE_S;
- return 1;
- }
- if (tok_is(t, "i64.ge_u")) {
- *out = WASM_INSN_I64_GE_U;
- return 1;
- }
- if (tok_is(t, "f32.add")) {
- *out = WASM_INSN_F32_ADD;
- return 1;
- }
- if (tok_is(t, "f32.sub")) {
- *out = WASM_INSN_F32_SUB;
- return 1;
- }
- if (tok_is(t, "f32.mul")) {
- *out = WASM_INSN_F32_MUL;
- return 1;
- }
- if (tok_is(t, "f32.div")) {
- *out = WASM_INSN_F32_DIV;
- return 1;
- }
- if (tok_is(t, "f32.eq")) {
- *out = WASM_INSN_F32_EQ;
- return 1;
- }
- if (tok_is(t, "f32.ne")) {
- *out = WASM_INSN_F32_NE;
- return 1;
- }
- if (tok_is(t, "f32.lt")) {
- *out = WASM_INSN_F32_LT;
- return 1;
- }
- if (tok_is(t, "f32.gt")) {
- *out = WASM_INSN_F32_GT;
- return 1;
- }
- if (tok_is(t, "f32.le")) {
- *out = WASM_INSN_F32_LE;
- return 1;
- }
- if (tok_is(t, "f32.ge")) {
- *out = WASM_INSN_F32_GE;
- return 1;
- }
- if (tok_is(t, "f64.add")) {
- *out = WASM_INSN_F64_ADD;
- return 1;
- }
- if (tok_is(t, "f64.sub")) {
- *out = WASM_INSN_F64_SUB;
- return 1;
- }
- if (tok_is(t, "f64.mul")) {
- *out = WASM_INSN_F64_MUL;
- return 1;
- }
- if (tok_is(t, "f64.div")) {
- *out = WASM_INSN_F64_DIV;
- return 1;
- }
- if (tok_is(t, "f64.eq")) {
- *out = WASM_INSN_F64_EQ;
- return 1;
- }
- if (tok_is(t, "f64.ne")) {
- *out = WASM_INSN_F64_NE;
- return 1;
- }
- if (tok_is(t, "f64.lt")) {
- *out = WASM_INSN_F64_LT;
- return 1;
- }
- if (tok_is(t, "f64.gt")) {
- *out = WASM_INSN_F64_GT;
- return 1;
- }
- if (tok_is(t, "f64.le")) {
- *out = WASM_INSN_F64_LE;
- return 1;
- }
- if (tok_is(t, "f64.ge")) {
- *out = WASM_INSN_F64_GE;
- return 1;
- }
- if (tok_is(t, "i32.wrap_i64")) {
- *out = WASM_INSN_I32_WRAP_I64;
- return 1;
- }
- if (tok_is(t, "i32.trunc_f32_s")) {
- *out = WASM_INSN_I32_TRUNC_F32_S;
- return 1;
- }
- if (tok_is(t, "i32.trunc_f32_u")) {
- *out = WASM_INSN_I32_TRUNC_F32_U;
- return 1;
- }
- if (tok_is(t, "i32.trunc_f64_s")) {
- *out = WASM_INSN_I32_TRUNC_F64_S;
- return 1;
- }
- if (tok_is(t, "i32.trunc_f64_u")) {
- *out = WASM_INSN_I32_TRUNC_F64_U;
- return 1;
- }
- if (tok_is(t, "i64.extend_i32_s")) {
- *out = WASM_INSN_I64_EXTEND_I32_S;
- return 1;
- }
- if (tok_is(t, "i64.extend_i32_u")) {
- *out = WASM_INSN_I64_EXTEND_I32_U;
- return 1;
- }
- if (tok_is(t, "i64.trunc_f32_s")) {
- *out = WASM_INSN_I64_TRUNC_F32_S;
- return 1;
- }
- if (tok_is(t, "i64.trunc_f32_u")) {
- *out = WASM_INSN_I64_TRUNC_F32_U;
- return 1;
- }
- if (tok_is(t, "i64.trunc_f64_s")) {
- *out = WASM_INSN_I64_TRUNC_F64_S;
- return 1;
- }
- if (tok_is(t, "i64.trunc_f64_u")) {
- *out = WASM_INSN_I64_TRUNC_F64_U;
- return 1;
- }
- if (tok_is(t, "f32.convert_i32_s")) {
- *out = WASM_INSN_F32_CONVERT_I32_S;
- return 1;
- }
- if (tok_is(t, "f32.convert_i32_u")) {
- *out = WASM_INSN_F32_CONVERT_I32_U;
- return 1;
- }
- if (tok_is(t, "f32.convert_i64_s")) {
- *out = WASM_INSN_F32_CONVERT_I64_S;
- return 1;
- }
- if (tok_is(t, "f32.convert_i64_u")) {
- *out = WASM_INSN_F32_CONVERT_I64_U;
- return 1;
- }
- if (tok_is(t, "f32.demote_f64")) {
- *out = WASM_INSN_F32_DEMOTE_F64;
- return 1;
- }
- if (tok_is(t, "f64.convert_i32_s")) {
- *out = WASM_INSN_F64_CONVERT_I32_S;
- return 1;
- }
- if (tok_is(t, "f64.convert_i32_u")) {
- *out = WASM_INSN_F64_CONVERT_I32_U;
- return 1;
- }
- if (tok_is(t, "f64.convert_i64_s")) {
- *out = WASM_INSN_F64_CONVERT_I64_S;
- return 1;
- }
- if (tok_is(t, "f64.convert_i64_u")) {
- *out = WASM_INSN_F64_CONVERT_I64_U;
- return 1;
- }
- if (tok_is(t, "f64.promote_f32")) {
- *out = WASM_INSN_F64_PROMOTE_F32;
- return 1;
- }
- if (tok_is(t, "i32.reinterpret_f32")) {
- *out = WASM_INSN_I32_REINTERPRET_F32;
- return 1;
- }
- if (tok_is(t, "i64.reinterpret_f64")) {
- *out = WASM_INSN_I64_REINTERPRET_F64;
- return 1;
- }
- if (tok_is(t, "f32.reinterpret_i32")) {
- *out = WASM_INSN_F32_REINTERPRET_I32;
- return 1;
- }
- if (tok_is(t, "f64.reinterpret_i64")) {
- *out = WASM_INSN_F64_REINTERPRET_I64;
- return 1;
- }
- return 0;
-}
-
-static void wat_parse_func_index(WatParser* p, int64_t* out) {
- uint32_t i;
- if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
- for (i = 0; i < p->module->nfuncs; ++i) {
- if (wasm_name_eq(p->module->funcs[i].name, p->tok)) {
- *out = i;
- return;
- }
- }
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: unknown function name");
- }
- if (!wat_parse_i64(p, out))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected instruction immediate");
-}
-
-static void wat_parse_type_index(WatParser* p, int64_t* out) {
- uint32_t i;
- if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
- for (i = 0; i < p->module->ntypes; ++i) {
- if (wasm_name_eq(p->module->types[i].name, p->tok)) {
- *out = i;
- return;
- }
- }
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: unknown type name");
- }
- if (!wat_parse_i64(p, out))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected type index");
-}
-
-static void wat_parse_local_index(WatParser* p, WasmFunc* f, int64_t* out) {
- uint32_t i, nlocals = f->nparams + f->nlocals;
- if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
- for (i = 0; i < nlocals; ++i) {
- if (wasm_name_eq(f->local_names[i], p->tok)) {
- *out = i;
- return;
- }
- }
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: unknown local name");
- }
- if (!wat_parse_i64(p, out))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected instruction immediate");
-}
-
-static void wat_parse_global_index(WatParser* p, int64_t* out) {
- uint32_t i;
- if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
- for (i = 0; i < p->module->nglobals; ++i) {
- if (wasm_name_eq(p->module->globals[i].name, p->tok)) {
- *out = i;
- return;
- }
- }
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: unknown global name");
- }
- if (!wat_parse_i64(p, out))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected global index");
-}
-
-static void wat_parse_instr_imm(WatParser* p, WasmFunc* f, WasmInsnKind kind,
- int64_t* out) {
- switch (kind) {
- case WASM_INSN_CALL:
- case WASM_INSN_RETURN_CALL:
- wat_parse_func_index(p, out);
- break;
- case WASM_INSN_GLOBAL_GET:
- case WASM_INSN_GLOBAL_SET:
- wat_parse_global_index(p, out);
- break;
- case WASM_INSN_LOCAL_GET:
- case WASM_INSN_LOCAL_SET:
- case WASM_INSN_LOCAL_TEE:
- wat_parse_local_index(p, f, out);
- break;
- default:
- if (!wat_parse_i64(p, out))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected instruction immediate");
- break;
- }
-}
-
-static int wasm_insn_is_load(WasmInsnKind kind) {
- return kind == WASM_INSN_I32_LOAD || kind == WASM_INSN_I64_LOAD ||
- kind == WASM_INSN_I32_LOAD8_S || kind == WASM_INSN_I32_LOAD8_U ||
- kind == WASM_INSN_I32_LOAD16_S || kind == WASM_INSN_I32_LOAD16_U ||
- kind == WASM_INSN_I64_LOAD8_S || kind == WASM_INSN_I64_LOAD8_U ||
- kind == WASM_INSN_I64_LOAD16_S || kind == WASM_INSN_I64_LOAD16_U ||
- kind == WASM_INSN_I64_LOAD32_S || kind == WASM_INSN_I64_LOAD32_U;
-}
-
-static int wasm_insn_is_store(WasmInsnKind kind) {
- return kind == WASM_INSN_I32_STORE || kind == WASM_INSN_I64_STORE ||
- kind == WASM_INSN_I32_STORE8 || kind == WASM_INSN_I32_STORE16 ||
- kind == WASM_INSN_I64_STORE8 || kind == WASM_INSN_I64_STORE16 ||
- kind == WASM_INSN_I64_STORE32;
-}
-
-static int wasm_insn_is_atomic_load(WasmInsnKind kind) {
- return kind == WASM_INSN_I32_ATOMIC_LOAD ||
- kind == WASM_INSN_I64_ATOMIC_LOAD ||
- kind == WASM_INSN_I32_ATOMIC_LOAD8_U ||
- kind == WASM_INSN_I32_ATOMIC_LOAD16_U ||
- kind == WASM_INSN_I64_ATOMIC_LOAD8_U ||
- kind == WASM_INSN_I64_ATOMIC_LOAD16_U ||
- kind == WASM_INSN_I64_ATOMIC_LOAD32_U;
-}
-
-static int wasm_insn_is_atomic_store(WasmInsnKind kind) {
- return kind == WASM_INSN_I32_ATOMIC_STORE ||
- kind == WASM_INSN_I64_ATOMIC_STORE ||
- kind == WASM_INSN_I32_ATOMIC_STORE8 ||
- kind == WASM_INSN_I32_ATOMIC_STORE16 ||
- kind == WASM_INSN_I64_ATOMIC_STORE8 ||
- kind == WASM_INSN_I64_ATOMIC_STORE16 ||
- kind == WASM_INSN_I64_ATOMIC_STORE32;
-}
-
-static int wasm_insn_is_atomic_rmw(WasmInsnKind kind) {
- return kind == WASM_INSN_I32_ATOMIC_RMW_ADD ||
- kind == WASM_INSN_I64_ATOMIC_RMW_ADD ||
- kind == WASM_INSN_I32_ATOMIC_RMW_SUB ||
- kind == WASM_INSN_I64_ATOMIC_RMW_SUB ||
- kind == WASM_INSN_I32_ATOMIC_RMW_AND ||
- kind == WASM_INSN_I64_ATOMIC_RMW_AND ||
- kind == WASM_INSN_I32_ATOMIC_RMW_OR ||
- kind == WASM_INSN_I64_ATOMIC_RMW_OR ||
- kind == WASM_INSN_I32_ATOMIC_RMW_XOR ||
- kind == WASM_INSN_I64_ATOMIC_RMW_XOR ||
- kind == WASM_INSN_I32_ATOMIC_RMW_XCHG ||
- kind == WASM_INSN_I64_ATOMIC_RMW_XCHG;
-}
-
-static int wasm_insn_is_atomic_cmpxchg(WasmInsnKind kind) {
- return kind == WASM_INSN_I32_ATOMIC_RMW_CMPXCHG ||
- kind == WASM_INSN_I64_ATOMIC_RMW_CMPXCHG;
-}
-
-static int wasm_insn_is_atomic_wait_notify(WasmInsnKind kind) {
- return kind == WASM_INSN_I32_ATOMIC_WAIT ||
- kind == WASM_INSN_I64_ATOMIC_WAIT ||
- kind == WASM_INSN_MEMORY_ATOMIC_NOTIFY;
-}
-
-static int wasm_insn_is_atomic_mem(WasmInsnKind kind) {
- return wasm_insn_is_atomic_load(kind) || wasm_insn_is_atomic_store(kind) ||
- wasm_insn_is_atomic_rmw(kind) || wasm_insn_is_atomic_cmpxchg(kind) ||
- wasm_insn_is_atomic_wait_notify(kind);
-}
-
-static int wasm_insn_is_mem(WasmInsnKind kind) {
- return wasm_insn_is_load(kind) || wasm_insn_is_store(kind) ||
- wasm_insn_is_atomic_mem(kind);
-}
-
-static int wat_atom_prefix(WasmTok t, const char* prefix) {
- size_t n = strlen(prefix);
- return t.kind == WT_ATOM && t.len >= n && memcmp(t.p, prefix, n) == 0;
-}
-
-static int wat_parse_u32_atom(WasmTok t, uint32_t* out) {
- uint64_t v = 0;
- size_t i = 0;
- unsigned base = 10;
- if (t.kind != WT_ATOM || t.len == 0) return 0;
- if (i + 2u <= t.len && t.p[i] == '0' &&
- (t.p[i + 1u] == 'x' || t.p[i + 1u] == 'X')) {
- base = 16;
- i += 2u;
- }
- if (i == t.len) return 0;
- for (; i < t.len; ++i) {
- int hd;
- if (t.p[i] == '_') continue;
- hd = wat_hex(t.p[i]);
- if (hd < 0 || (unsigned)hd >= base) return 0;
- if (v > (UINT32_MAX - (uint32_t)hd) / base) return 0;
- v = v * base + (uint32_t)hd;
- }
- *out = (uint32_t)v;
- return 1;
-}
-
-static int wat_parse_u64_atom(WasmTok t, uint64_t* out) {
- uint64_t v = 0;
- size_t i = 0;
- unsigned base = 10;
- if (t.kind != WT_ATOM || t.len == 0) return 0;
- if (i + 2u <= t.len && t.p[i] == '0' &&
- (t.p[i + 1u] == 'x' || t.p[i + 1u] == 'X')) {
- base = 16;
- i += 2u;
- }
- if (i == t.len) return 0;
- for (; i < t.len; ++i) {
- int hd;
- if (t.p[i] == '_') continue;
- hd = wat_hex(t.p[i]);
- if (hd < 0 || (unsigned)hd >= base) return 0;
- if (v > (UINT64_MAX - (uint64_t)hd) / base) return 0;
- v = v * base + (uint64_t)hd;
- }
- *out = v;
- return 1;
-}
-
-static void wat_parse_memory_index(WatParser* p, uint32_t* out) {
- uint32_t i;
- int64_t idx;
- if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
- for (i = 0; i < p->module->nmemories; ++i) {
- if (wasm_name_eq(p->module->memories[i].name, p->tok)) {
- *out = i;
- return;
- }
- }
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: unknown memory name");
- }
- if (!wat_parse_i64(p, &idx) || idx < 0 || idx > UINT32_MAX)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected memory index");
- *out = (uint32_t)idx;
-}
-
-static void wat_parse_mem_attrs(WatParser* p, uint32_t* align,
- uint64_t* offset, uint32_t* memidx) {
- while (p->tok.kind == WT_ATOM) {
- WasmTok val;
- if (wat_atom_prefix(p->tok, "align=")) {
- val = p->tok;
- val.p += 6;
- val.len -= 6;
- if (!wat_parse_u32_atom(val, align))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: bad memory alignment");
- wat_next(p);
- } else if (wat_atom_prefix(p->tok, "offset=")) {
- val = p->tok;
- val.p += 7;
- val.len -= 7;
- if (!wat_parse_u64_atom(val, offset))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: bad memory offset");
- wat_next(p);
- } else if (wat_atom_prefix(p->tok, "memory=")) {
- val = p->tok;
- val.p += 7;
- val.len -= 7;
- if (!wat_parse_u32_atom(val, memidx))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: bad memory index");
- wat_next(p);
- } else if (wat_atom_prefix(p->tok, "mem=")) {
- val = p->tok;
- val.p += 4;
- val.len -= 4;
- if (!wat_parse_u32_atom(val, memidx))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: bad memory index");
- wat_next(p);
- } else {
- break;
- }
- }
-}
-
-static void wat_reject_inline_result(WatParser* p, const char* what) {
- if (p->tok.kind != WT_LPAREN) return;
- wat_next(p);
- if (!tok_is(p->tok, "result")) {
- p->pos = (size_t)(p->tok.p - p->src);
- p->line = p->tok.line;
- p->col = p->tok.col;
- p->tok.kind = WT_LPAREN;
- p->tok.p = p->src + p->pos - 1u;
- p->tok.len = 1;
- p->tok.line = p->line;
- p->tok.col = p->col - 1u;
- return;
- }
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: %s results are unsupported", what);
-}
-
-static void wat_parse_instr(WatParser* p, WasmFunc* f);
-
-static void wat_check_instr_feature(WatParser* p, WasmInsnKind kind) {
- if (kind == WASM_INSN_RETURN_CALL ||
- kind == WASM_INSN_RETURN_CALL_INDIRECT ||
- kind == WASM_INSN_RETURN_CALL_REF)
- wat_require_feature(p, WASM_FEATURE_TAIL_CALLS, "tail calls",
- "tail-call instruction");
- if (kind == WASM_INSN_REF_NULL || kind == WASM_INSN_REF_FUNC ||
- kind == WASM_INSN_REF_IS_NULL || kind == WASM_INSN_CALL_REF ||
- kind == WASM_INSN_RETURN_CALL_REF)
- wat_require_feature(p, WASM_FEATURE_TYPED_FUNC_REFS,
- "typed function references",
- "typed-reference instruction");
- if (kind == WASM_INSN_ATOMIC_FENCE || wasm_insn_is_atomic_mem(kind))
- wat_require_feature(p, WASM_FEATURE_THREADS, "threads",
- "atomic instruction");
-}
-
-static uint32_t wat_parse_call_indirect_type(WatParser* p) {
- int64_t typeidx;
- wat_expect(p, WT_LPAREN, "'('");
- if (!tok_is(p->tok, "type"))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected call_indirect type");
- wat_next(p);
- wat_parse_type_index(p, &typeidx);
- if (typeidx < 0 || (uint64_t)typeidx >= p->module->ntypes)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: type index out of range");
- wat_next(p);
- wat_expect(p, WT_RPAREN, "')'");
- return (uint32_t)typeidx;
-}
-
-static void wat_parse_ref_null_type(WatParser* p, int64_t* out) {
- if (tok_is(p->tok, "func") || tok_is(p->tok, "nofunc") ||
- tok_is(p->tok, "funcref")) {
- *out = WASM_VAL_FUNCREF;
- return;
- }
- if (tok_is(p->tok, "extern") || tok_is(p->tok, "noextern") ||
- tok_is(p->tok, "externref")) {
- *out = WASM_VAL_EXTERNREF;
- return;
- }
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected reference type");
-}
-
-static void wat_parse_instr_list(WatParser* p, WasmFunc* f) {
- WasmInsnKind kind;
- int has_imm;
- int64_t imm = 0;
- WasmTok head;
- wat_expect(p, WT_LPAREN, "'('");
- head = p->tok;
- if (tok_is(head, "block") || tok_is(head, "loop")) {
- kind = tok_is(head, "block") ? WASM_INSN_BLOCK : WASM_INSN_LOOP;
- wat_next(p);
- if (p->tok.kind == WT_LPAREN) {
- wat_next(p);
- if (tok_is(p->tok, "result")) {
- wat_next(p);
- if (!tok_is(p->tok, "i32") && !tok_is(p->tok, "i64"))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: unsupported block result type");
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: block results are unsupported");
- }
- p->pos = (size_t)(p->tok.p - p->src);
- p->line = p->tok.line;
- p->col = p->tok.col;
- p->tok.kind = WT_LPAREN;
- p->tok.p = p->src + p->pos - 1u;
- p->tok.len = 1;
- p->tok.line = p->line;
- p->tok.col = p->col - 1u;
- }
- wasm_func_add_insn(p->c, p->module, f, kind, 0);
- while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF)
- wat_parse_instr(p, f);
- wasm_func_add_insn(p->c, p->module, f, WASM_INSN_END, 0);
- wat_expect(p, WT_RPAREN, "')'");
- return;
- }
- if (tok_is(head, "if")) {
- wat_next(p);
- while (p->tok.kind == WT_LPAREN) {
- WasmTok save_head;
- size_t save_pos = p->pos;
- uint32_t save_line = p->line, save_col = p->col;
- wat_next(p);
- save_head = p->tok;
- p->pos = save_pos;
- p->line = save_line;
- p->col = save_col;
- p->tok.kind = WT_LPAREN;
- p->tok.p = p->src + p->pos - 1u;
- p->tok.len = 1;
- p->tok.line = save_line;
- p->tok.col = save_col - 1u;
- if (tok_is(save_head, "then") || tok_is(save_head, "else")) break;
- if (tok_is(save_head, "result"))
- wasm_error(p->c, wasm_loc(save_head.line, save_head.col),
- "wasm wat: if results are unsupported");
- wat_parse_instr(p, f);
- }
- wasm_func_add_insn(p->c, p->module, f, WASM_INSN_IF, 0);
- if (p->tok.kind == WT_LPAREN) {
- wat_next(p);
- if (!tok_is(p->tok, "then"))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected then");
- wat_next(p);
- while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF)
- wat_parse_instr(p, f);
- wat_expect(p, WT_RPAREN, "')'");
- }
- if (p->tok.kind == WT_LPAREN) {
- wat_next(p);
- if (!tok_is(p->tok, "else"))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected else");
- wasm_func_add_insn(p->c, p->module, f, WASM_INSN_ELSE, 0);
- wat_next(p);
- while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF)
- wat_parse_instr(p, f);
- wat_expect(p, WT_RPAREN, "')'");
- }
- wasm_func_add_insn(p->c, p->module, f, WASM_INSN_END, 0);
- wat_expect(p, WT_RPAREN, "')'");
- return;
- }
- if (!wat_instr_kind(head, &kind, &has_imm))
- wasm_error(p->c, wasm_loc(head.line, head.col),
- "wasm wat: unsupported instruction");
- wat_check_instr_feature(p, kind);
- wat_next(p);
- if (wasm_insn_is_mem(kind)) {
- uint32_t align = 0, memidx = 0;
- uint64_t offset = 0;
- wat_parse_mem_attrs(p, &align, &offset, &memidx);
- while (p->tok.kind == WT_LPAREN) {
- wat_next(p);
- if (tok_is(p->tok, "memory")) {
- wat_next(p);
- wat_parse_memory_index(p, &memidx);
- wat_next(p);
- wat_expect(p, WT_RPAREN, "')'");
- } else {
- p->pos = (size_t)(p->tok.p - p->src);
- p->line = p->tok.line;
- p->col = p->tok.col;
- p->tok.kind = WT_LPAREN;
- p->tok.p = p->src + p->pos - 1u;
- p->tok.len = 1;
- p->tok.line = p->line;
- p->tok.col = p->col - 1u;
- wat_parse_instr(p, f);
- }
- }
- wasm_func_add_mem_insn(p->c, p->module, f, kind, align, offset, memidx);
- wat_expect(p, WT_RPAREN, "')'");
- return;
- }
- if (kind == WASM_INSN_BR_TABLE) {
- WasmInsn* in;
- uint32_t n = 0;
- while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
- int64_t target;
- if (n >= 16u)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: too many br_table targets");
- if (!wat_parse_i64(p, &target) || target < 0 || target > UINT32_MAX)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: bad br_table target");
- wasm_func_add_insn(p->c, p->module, f,
- n ? WASM_INSN_NOP : WASM_INSN_BR_TABLE, 0);
- in = &f->insns[f->ninsns - 1u - n];
- in->targets[n++] = (uint32_t)target;
- wat_next(p);
- }
- f->ninsns -= n - 1u;
- f->insns[f->ninsns - 1u].ntargets = n;
- wat_expect(p, WT_RPAREN, "')'");
- return;
- }
- if (kind == WASM_INSN_CALL_INDIRECT ||
- kind == WASM_INSN_RETURN_CALL_INDIRECT) {
- uint32_t typeidx = wat_parse_call_indirect_type(p);
- while (p->tok.kind == WT_LPAREN) wat_parse_instr(p, f);
- wasm_func_add_insn(p->c, p->module, f, kind, typeidx);
- wat_expect(p, WT_RPAREN, "')'");
- return;
- }
- if (kind == WASM_INSN_CALL_REF || kind == WASM_INSN_RETURN_CALL_REF) {
- uint32_t typeidx = wat_parse_call_indirect_type(p);
- while (p->tok.kind == WT_LPAREN) wat_parse_instr(p, f);
- wasm_func_add_insn(p->c, p->module, f, kind, typeidx);
- wat_expect(p, WT_RPAREN, "')'");
- return;
- }
- if (kind == WASM_INSN_REF_NULL) {
- wat_parse_ref_null_type(p, &imm);
- wat_next(p);
- wasm_func_add_insn(p->c, p->module, f, kind, imm);
- wat_expect(p, WT_RPAREN, "')'");
- return;
- }
- if (kind == WASM_INSN_REF_FUNC) {
- wat_parse_func_index(p, &imm);
- wat_next(p);
- wasm_func_add_insn(p->c, p->module, f, kind, imm);
- wat_expect(p, WT_RPAREN, "')'");
- return;
- }
- if (kind == WASM_INSN_MEMORY_SIZE || kind == WASM_INSN_MEMORY_GROW) {
- uint32_t memidx = 0;
- while (p->tok.kind == WT_LPAREN) {
- wat_next(p);
- if (!tok_is(p->tok, "memory")) {
- p->pos = (size_t)(p->tok.p - p->src);
- p->line = p->tok.line;
- p->col = p->tok.col;
- p->tok.kind = WT_LPAREN;
- p->tok.p = p->src + p->pos - 1u;
- p->tok.len = 1;
- p->tok.line = p->line;
- p->tok.col = p->col - 1u;
- wat_parse_instr(p, f);
- continue;
- }
- wat_next(p);
- wat_parse_memory_index(p, &memidx);
- wat_next(p);
- wat_expect(p, WT_RPAREN, "')'");
- }
- {
- WasmInsnKind next_kind;
- int next_has_imm;
- if (p->tok.kind == WT_ATOM &&
- !wat_instr_kind(p->tok, &next_kind, &next_has_imm)) {
- wat_parse_memory_index(p, &memidx);
- wat_next(p);
- }
- }
- wasm_func_add_insn(p->c, p->module, f,
- kind == WASM_INSN_MEMORY_GROW ? WASM_INSN_MEMORY_GROW
- : WASM_INSN_MEMORY_SIZE,
- 0);
- f->insns[f->ninsns - 1u].memidx = memidx;
- wat_expect(p, WT_RPAREN, "')'");
- return;
- }
- if (has_imm) {
- if (kind == WASM_INSN_F32_CONST || kind == WASM_INSN_F64_CONST) {
- double fv;
- if (!wat_parse_f64(p, &fv))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected float immediate");
- wat_next(p);
- while (p->tok.kind == WT_LPAREN) wat_parse_instr(p, f);
- wasm_func_add_fp_insn(p->c, p->module, f, kind, fv);
- wat_expect(p, WT_RPAREN, "')'");
- return;
- } else {
- wat_parse_instr_imm(p, f, kind, &imm);
- wat_next(p);
- }
- }
- while (p->tok.kind == WT_LPAREN) wat_parse_instr(p, f);
- wasm_func_add_insn(p->c, p->module, f, kind, imm);
- wat_expect(p, WT_RPAREN, "')'");
-}
-
-static void wat_parse_instr(WatParser* p, WasmFunc* f) {
- WasmInsnKind kind;
- int has_imm;
- if (p->tok.kind == WT_LPAREN) {
- wat_parse_instr_list(p, f);
- return;
- }
- if (!wat_instr_kind(p->tok, &kind, &has_imm))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: unsupported instruction");
- wat_check_instr_feature(p, kind);
- wat_next(p);
- if (kind == WASM_INSN_BLOCK || kind == WASM_INSN_LOOP)
- wat_reject_inline_result(p, "block");
- else if (kind == WASM_INSN_IF)
- wat_reject_inline_result(p, "if");
- if (wasm_insn_is_mem(kind)) {
- uint32_t align = 0, memidx = 0;
- uint64_t offset = 0;
- wat_parse_mem_attrs(p, &align, &offset, &memidx);
- wasm_func_add_mem_insn(p->c, p->module, f, kind, align, offset, memidx);
- return;
- }
- if (kind == WASM_INSN_BR_TABLE) {
- WasmInsn* in;
- uint32_t n = 0;
- wasm_func_add_insn(p->c, p->module, f, WASM_INSN_BR_TABLE, 0);
- in = &f->insns[f->ninsns - 1u];
- while (p->tok.kind == WT_ATOM) {
- WasmInsnKind next_kind;
- int next_has_imm;
- int64_t target;
- if (wat_instr_kind(p->tok, &next_kind, &next_has_imm)) break;
- if (n >= 16u)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: too many br_table targets");
- if (!wat_parse_i64(p, &target) || target < 0 || target > UINT32_MAX)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: bad br_table target");
- in->targets[n++] = (uint32_t)target;
- wat_next(p);
- }
- in->ntargets = n;
- return;
- }
- if (kind == WASM_INSN_CALL_INDIRECT ||
- kind == WASM_INSN_RETURN_CALL_INDIRECT) {
- uint32_t typeidx = wat_parse_call_indirect_type(p);
- wasm_func_add_insn(p->c, p->module, f, kind, typeidx);
- return;
- }
- if (kind == WASM_INSN_CALL_REF || kind == WASM_INSN_RETURN_CALL_REF) {
- uint32_t typeidx = wat_parse_call_indirect_type(p);
- wasm_func_add_insn(p->c, p->module, f, kind, typeidx);
- return;
- }
- if (kind == WASM_INSN_REF_NULL) {
- int64_t imm;
- wat_parse_ref_null_type(p, &imm);
- wasm_func_add_insn(p->c, p->module, f, kind, imm);
- wat_next(p);
- return;
- }
- if (kind == WASM_INSN_REF_FUNC) {
- int64_t imm;
- wat_parse_func_index(p, &imm);
- wasm_func_add_insn(p->c, p->module, f, kind, imm);
- wat_next(p);
- return;
- }
- if (kind == WASM_INSN_MEMORY_SIZE || kind == WASM_INSN_MEMORY_GROW) {
- uint32_t memidx = 0;
- WasmInsnKind next_kind;
- int next_has_imm;
- if (p->tok.kind == WT_ATOM && !wat_instr_kind(p->tok, &next_kind,
- &next_has_imm)) {
- wat_parse_memory_index(p, &memidx);
- wat_next(p);
- }
- wasm_func_add_insn(p->c, p->module, f, kind, 0);
- f->insns[f->ninsns - 1u].memidx = memidx;
- return;
- }
- if (has_imm) {
- if (kind == WASM_INSN_F32_CONST || kind == WASM_INSN_F64_CONST) {
- double fv;
- if (!wat_parse_f64(p, &fv))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected float immediate");
- wasm_func_add_fp_insn(p->c, p->module, f, kind, fv);
- wat_next(p);
- } else {
- int64_t imm;
- wat_parse_instr_imm(p, f, kind, &imm);
- wasm_func_add_insn(p->c, p->module, f, kind, imm);
- wat_next(p);
- }
- } else {
- wasm_func_add_insn(p->c, p->module, f, kind, 0);
- }
-}
-
-static void wat_parse_func(WatParser* p) {
- WasmFunc* f = wasm_add_func(p->c, p->module);
- uint32_t checked_params = 0;
- uint32_t checked_results = 0;
- wat_expect(p, WT_LPAREN, "'('");
- if (!tok_is(p->tok, "func"))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected func");
- wat_next(p);
- if (p->tok.kind == WT_ATOM && p->tok.len > 0 && p->tok.p[0] == '$') {
- f->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
- if (!f->name)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- wat_next(p);
- }
- while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
- if (p->tok.kind == WT_LPAREN) {
- wat_next(p);
- if (tok_is(p->tok, "export")) {
- wat_next(p);
- if (p->tok.kind != WT_STRING)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected export string");
- f->export_name = wat_dup_string(p, p->tok, NULL);
- {
- WasmExport* ex = wasm_add_export(p->c, p->module);
- ex->name = wasm_strdup(p->module->heap, f->export_name,
- strlen(f->export_name));
- if (!ex->name)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- ex->kind = 0;
- ex->index = p->module->nfuncs - 1u;
- }
- wat_next(p);
- wat_expect(p, WT_RPAREN, "')'");
- } else if (tok_is(p->tok, "type")) {
- int64_t typeidx;
- wat_next(p);
- wat_parse_type_index(p, &typeidx);
- if (typeidx < 0 || (uint64_t)typeidx >= p->module->ntypes)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: type index out of range");
- f->typeidx = (uint32_t)typeidx;
- f->has_typeidx = 1;
- f->nparams = p->module->types[typeidx].nparams;
- memcpy(f->params, p->module->types[typeidx].params,
- sizeof(f->params[0]) * f->nparams);
- f->nresults = p->module->types[typeidx].nresults;
- memcpy(f->results, p->module->types[typeidx].results,
- sizeof(f->results[0]) * f->nresults);
- wat_next(p);
- wat_expect(p, WT_RPAREN, "')'");
- } else if (tok_is(p->tok, "param")) {
- WasmTok pending_name;
- int have_name = 0;
- memset(&pending_name, 0, sizeof pending_name);
- wat_next(p);
- while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
- WasmValType vt;
- if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
- pending_name = p->tok;
- have_name = 1;
- wat_next(p);
- continue;
- }
- if (!wat_val_type(p->tok, &vt))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected parameter type");
- if (!wasm_is_frontend_value_type(vt))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: unsupported parameter type");
- if (f->has_typeidx) {
- if (checked_params >= f->nparams || f->params[checked_params] != vt)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: parameter does not match type");
- if (have_name) {
- f->local_names[checked_params] = wasm_strdup(
- p->module->heap, pending_name.p, pending_name.len);
- if (!f->local_names[checked_params])
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- have_name = 0;
- }
- checked_params++;
- wat_next(p);
- continue;
- }
- if (f->nparams >= 16u)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: too many parameters");
- if (have_name) {
- f->local_names[f->nparams] =
- wasm_strdup(p->module->heap, pending_name.p, pending_name.len);
- if (!f->local_names[f->nparams])
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- have_name = 0;
- }
- f->params[f->nparams++] = vt;
- wat_next(p);
- }
- wat_expect(p, WT_RPAREN, "')'");
- } else if (tok_is(p->tok, "result")) {
- WasmValType vt;
- wat_next(p);
- if (!wat_val_type(p->tok, &vt))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected result type");
- if (!wasm_is_frontend_value_type(vt))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: unsupported result type");
- if (f->has_typeidx) {
- if (checked_results >= f->nresults ||
- f->results[checked_results] != vt)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: result does not match type");
- checked_results++;
- } else {
- f->results[0] = vt;
- f->nresults = 1;
- }
- wat_next(p);
- wat_expect(p, WT_RPAREN, "')'");
- } else if (tok_is(p->tok, "local")) {
- WasmTok pending_name;
- int have_name = 0;
- memset(&pending_name, 0, sizeof pending_name);
- wat_next(p);
- while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
- WasmValType vt;
- if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
- pending_name = p->tok;
- have_name = 1;
- wat_next(p);
- continue;
- }
- if (!wat_val_type(p->tok, &vt))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected local type");
- if (f->nlocals >= 32u)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: too many locals");
- if (have_name) {
- uint32_t index = f->nparams + f->nlocals;
- f->local_names[index] =
- wasm_strdup(p->module->heap, pending_name.p, pending_name.len);
- if (!f->local_names[index])
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- have_name = 0;
- }
- f->locals[f->nlocals++] = vt;
- wat_next(p);
- }
- wat_expect(p, WT_RPAREN, "')'");
- } else {
- p->pos = (size_t)(p->tok.p - p->src);
- p->line = p->tok.line;
- p->col = p->tok.col;
- p->tok.kind = WT_LPAREN;
- p->tok.p = p->src + p->pos - 1u;
- p->tok.len = 1;
- p->tok.line = p->line;
- p->tok.col = p->col - 1u;
- wat_parse_instr(p, f);
- }
- } else {
- wat_parse_instr(p, f);
- }
- }
- if (!f->has_typeidx)
- f->typeidx = wasm_intern_func_type(p->c, p->module, f);
- else if (checked_params && checked_params != f->nparams)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: parameter list does not match type");
- wat_expect(p, WT_RPAREN, "')'");
-}
-
-static void wat_parse_type_field(WatParser* p) {
- WasmFuncType* t = wasm_add_type(p->c, p->module);
- if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
- t->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
- if (!t->name)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- wat_next(p);
- }
- wat_expect(p, WT_LPAREN, "'('");
- if (!tok_is(p->tok, "func"))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected func type");
- wat_next(p);
- while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
- wat_expect(p, WT_LPAREN, "'('");
- if (tok_is(p->tok, "param")) {
- wat_next(p);
- while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
- WasmValType vt;
- if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
- wat_next(p);
- continue;
- }
- if (!wat_val_type(p->tok, &vt) || !wasm_is_num_type(vt))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected parameter type");
- if (t->nparams >= 16u)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: too many parameters");
- t->params[t->nparams++] = vt;
- wat_next(p);
- }
- } else if (tok_is(p->tok, "result")) {
- wat_next(p);
- while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
- WasmValType vt;
- if (!wat_val_type(p->tok, &vt) || !wasm_is_num_type(vt))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected result type");
- if (t->nresults >= 1u)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: multi-result unsupported");
- t->results[t->nresults++] = vt;
- wat_next(p);
- }
- } else {
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected type field");
- }
- wat_expect(p, WT_RPAREN, "')'");
- }
- wat_expect(p, WT_RPAREN, "')'");
- wat_expect(p, WT_RPAREN, "')'");
-}
-
-static void wat_parse_export_field(WatParser* p) {
- char* name;
- int64_t idx = 0;
- uint8_t kind;
- if (p->tok.kind != WT_STRING)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected export string");
- name = wat_dup_string(p, p->tok, NULL);
- wat_next(p);
- wat_expect(p, WT_LPAREN, "'('");
- kind = wasm_export_kind_from_tok(p->tok);
- if (kind == 0xffu)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected export kind");
- wat_next(p);
- if (kind == 0)
- wat_parse_func_index(p, &idx);
- else if (!wat_parse_i64(p, &idx))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected export index");
- if (idx < 0 || idx > UINT32_MAX)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: export index out of range");
- {
- WasmExport* ex = wasm_add_export(p->c, p->module);
- ex->name = name;
- ex->kind = kind;
- ex->index = (uint32_t)idx;
- }
- if (kind == 0 && (uint64_t)idx < p->module->nfuncs) {
- wasm_free_str(p->module->heap, &p->module->funcs[idx].export_name);
- p->module->funcs[idx].export_name =
- wasm_strdup(p->module->heap, name, strlen(name));
- if (!p->module->funcs[idx].export_name)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- } else if (kind == 2 && (uint64_t)idx < p->module->nmemories) {
- wasm_free_str(p->module->heap,
- &p->module->memories[(uint32_t)idx].export_name);
- p->module->memories[(uint32_t)idx].export_name =
- wasm_strdup(p->module->heap, name, strlen(name));
- if (!p->module->memories[(uint32_t)idx].export_name)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- } else if (kind == 1 && (uint64_t)idx < p->module->ntables) {
- wasm_free_str(p->module->heap, &p->module->tables[idx].export_name);
- p->module->tables[idx].export_name =
- wasm_strdup(p->module->heap, name, strlen(name));
- if (!p->module->tables[idx].export_name)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- } else if (kind == 3 && (uint64_t)idx < p->module->nglobals) {
- wasm_free_str(p->module->heap, &p->module->globals[idx].export_name);
- p->module->globals[idx].export_name =
- wasm_strdup(p->module->heap, name, strlen(name));
- if (!p->module->globals[idx].export_name)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- }
- wat_next(p);
- wat_expect(p, WT_RPAREN, "')'");
- wat_expect(p, WT_RPAREN, "')'");
-}
-
-static void wat_parse_memory_field(WatParser* p) {
- int64_t min_pages, max_pages;
- WasmMemory* mem = wasm_add_memory(p->c, p->module);
- if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
- mem->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
- if (!mem->name)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- wat_next(p);
- }
- while (p->tok.kind == WT_ATOM && (tok_is(p->tok, "i64") ||
- tok_is(p->tok, "shared"))) {
- if (tok_is(p->tok, "i64"))
- mem->is64 = 1;
- else
- mem->shared = 1;
- wat_next(p);
- }
- if (!wat_parse_i64(p, &min_pages) || min_pages < 0)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected memory minimum");
- mem->min_pages = (uint64_t)min_pages;
- wat_next(p);
- while (p->tok.kind == WT_ATOM && (tok_is(p->tok, "i64") ||
- tok_is(p->tok, "shared"))) {
- if (tok_is(p->tok, "i64"))
- mem->is64 = 1;
- else
- mem->shared = 1;
- wat_next(p);
- }
- if (p->tok.kind != WT_RPAREN) {
- if (!wat_parse_i64(p, &max_pages) || max_pages < min_pages)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: bad memory maximum");
- mem->has_max = 1;
- mem->max_pages = (uint64_t)max_pages;
- wat_next(p);
- while (p->tok.kind == WT_ATOM && (tok_is(p->tok, "i64") ||
- tok_is(p->tok, "shared"))) {
- if (tok_is(p->tok, "i64"))
- mem->is64 = 1;
- else
- mem->shared = 1;
- wat_next(p);
- }
- }
- wasm_memory_ensure(p->c, p->module, p->module->nmemories - 1u,
- mem->min_pages * 65536u);
- wat_expect(p, WT_RPAREN, "')'");
-}
-
-static void wat_parse_memory_limits(WatParser* p, WasmMemory* mem) {
- int64_t lo, hi;
- while (p->tok.kind == WT_ATOM && (tok_is(p->tok, "i64") ||
- tok_is(p->tok, "shared"))) {
- if (tok_is(p->tok, "i64"))
- mem->is64 = 1;
- else
- mem->shared = 1;
- wat_next(p);
- }
- if (!wat_parse_i64(p, &lo) || lo < 0)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected memory minimum");
- mem->min_pages = (uint64_t)lo;
- wat_next(p);
- while (p->tok.kind == WT_ATOM && (tok_is(p->tok, "i64") ||
- tok_is(p->tok, "shared"))) {
- if (tok_is(p->tok, "i64"))
- mem->is64 = 1;
- else
- mem->shared = 1;
- wat_next(p);
- }
- if (p->tok.kind != WT_RPAREN) {
- if (!wat_parse_i64(p, &hi) || hi < lo)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: bad memory maximum");
- mem->has_max = 1;
- mem->max_pages = (uint64_t)hi;
- wat_next(p);
- while (p->tok.kind == WT_ATOM && (tok_is(p->tok, "i64") ||
- tok_is(p->tok, "shared"))) {
- if (tok_is(p->tok, "i64"))
- mem->is64 = 1;
- else
- mem->shared = 1;
- wat_next(p);
- }
- }
-}
-
-static void wat_parse_table_limits_and_type(WatParser* p, WasmTable* t) {
- int64_t lo, hi;
- if (!wat_parse_i64(p, &lo) || lo < 0 || lo > UINT32_MAX)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected table minimum");
- t->min = (uint32_t)lo;
- wat_next(p);
- if (p->tok.kind == WT_ATOM) {
- WasmValType maybe_type;
- if (!wat_val_type(p->tok, &maybe_type)) {
- if (!wat_parse_i64(p, &hi) || hi < lo || hi > UINT32_MAX)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: bad table maximum");
- t->has_max = 1;
- t->max = (uint32_t)hi;
- wat_next(p);
- }
- }
- if (!wat_val_type(p->tok, &t->elem_type))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected table element type");
- wat_next(p);
-}
-
-static void wat_parse_import_field(WatParser* p) {
- char *mod, *name;
- if (p->tok.kind != WT_STRING)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected import module string");
- mod = wat_dup_string(p, p->tok, NULL);
- wat_next(p);
- if (p->tok.kind != WT_STRING)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected import name string");
- name = wat_dup_string(p, p->tok, NULL);
- wat_next(p);
- wat_expect(p, WT_LPAREN, "'('");
- if (tok_is(p->tok, "func")) {
- WasmFunc* f = wasm_add_func(p->c, p->module);
- f->is_import = 1;
- f->import_module = mod;
- f->import_name = name;
- wat_next(p);
- if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
- f->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
- if (!f->name)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- wat_next(p);
- }
- while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
- wat_expect(p, WT_LPAREN, "'('");
- if (tok_is(p->tok, "type")) {
- int64_t typeidx;
- wat_next(p);
- wat_parse_type_index(p, &typeidx);
- if (typeidx < 0 || (uint64_t)typeidx >= p->module->ntypes)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: type index out of range");
- f->typeidx = (uint32_t)typeidx;
- f->has_typeidx = 1;
- f->nparams = p->module->types[typeidx].nparams;
- memcpy(f->params, p->module->types[typeidx].params,
- sizeof(f->params[0]) * f->nparams);
- f->nresults = p->module->types[typeidx].nresults;
- memcpy(f->results, p->module->types[typeidx].results,
- sizeof(f->results[0]) * f->nresults);
- wat_next(p);
- } else if (tok_is(p->tok, "param")) {
- wat_next(p);
- while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
- WasmValType vt;
- if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
- wat_next(p);
- continue;
- }
- if (!wat_val_type(p->tok, &vt) || !wasm_is_frontend_value_type(vt))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected parameter type");
- if (f->nparams >= 16u)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: too many parameters");
- f->params[f->nparams++] = vt;
- wat_next(p);
- }
- } else if (tok_is(p->tok, "result")) {
- wat_next(p);
- while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
- WasmValType vt;
- if (!wat_val_type(p->tok, &vt) || !wasm_is_frontend_value_type(vt))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected result type");
- if (f->nresults >= 1u)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: multi-result unsupported");
- f->results[f->nresults++] = vt;
- wat_next(p);
- }
- } else {
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected import func type field");
- }
- wat_expect(p, WT_RPAREN, "')'");
- }
- if (!f->has_typeidx) f->typeidx = wasm_intern_func_type(p->c, p->module, f);
- } else if (tok_is(p->tok, "memory")) {
- WasmMemory* mem = wasm_add_memory(p->c, p->module);
- mem->is_import = 1;
- mem->import_module = mod;
- mem->import_name = name;
- wat_next(p);
- if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
- mem->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
- if (!mem->name)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- wat_next(p);
- }
- wat_parse_memory_limits(p, mem);
- } else if (tok_is(p->tok, "table")) {
- WasmTable* t = wasm_add_table(p->c, p->module);
- t->is_import = 1;
- t->import_module = mod;
- t->import_name = name;
- wat_next(p);
- if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
- t->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
- if (!t->name)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- wat_next(p);
- }
- wat_parse_table_limits_and_type(p, t);
- } else if (tok_is(p->tok, "global")) {
- WasmGlobal* g = wasm_add_global(p->c, p->module);
- g->is_import = 1;
- g->import_module = mod;
- g->import_name = name;
- wat_next(p);
- if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
- g->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
- if (!g->name)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- wat_next(p);
- }
- if (p->tok.kind == WT_LPAREN) {
- wat_next(p);
- if (!tok_is(p->tok, "mut"))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected mut");
- g->mutable_ = 1;
- wat_next(p);
- if (!wat_val_type(p->tok, &g->type) || !wasm_is_num_type(g->type))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected global type");
- wat_next(p);
- wat_expect(p, WT_RPAREN, "')'");
- } else {
- if (!wat_val_type(p->tok, &g->type) || !wasm_is_num_type(g->type))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected global type");
- wat_next(p);
- }
- } else {
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: unsupported import kind");
- }
- wat_expect(p, WT_RPAREN, "')'");
- wat_expect(p, WT_RPAREN, "')'");
-}
-
-static void wat_parse_table_field(WatParser* p) {
- WasmTable* t = wasm_add_table(p->c, p->module);
- if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
- t->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
- if (!t->name)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- wat_next(p);
- }
- wat_parse_table_limits_and_type(p, t);
- wat_expect(p, WT_RPAREN, "')'");
-}
-
-static void wat_parse_const_expr(WatParser* p, WasmInsn* out) {
- WasmInsnKind kind;
- int has_imm;
- memset(out, 0, sizeof *out);
- wat_expect(p, WT_LPAREN, "'('");
- if (!wat_instr_kind(p->tok, &kind, &has_imm) ||
- (kind != WASM_INSN_I32_CONST && kind != WASM_INSN_I64_CONST &&
- kind != WASM_INSN_F32_CONST && kind != WASM_INSN_F64_CONST))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected constant expression");
- out->kind = (uint8_t)kind;
- wat_next(p);
- if (kind == WASM_INSN_F32_CONST || kind == WASM_INSN_F64_CONST) {
- if (!wat_parse_f64(p, &out->fp))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected float immediate");
- } else if (!wat_parse_i64(p, &out->imm)) {
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected integer immediate");
- }
- wat_next(p);
- wat_expect(p, WT_RPAREN, "')'");
-}
-
-static void wat_parse_global_field(WatParser* p) {
- WasmGlobal* g = wasm_add_global(p->c, p->module);
- if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
- g->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
- if (!g->name)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- wat_next(p);
- }
- if (p->tok.kind == WT_LPAREN) {
- wat_next(p);
- if (tok_is(p->tok, "export")) {
- wat_next(p);
- if (p->tok.kind != WT_STRING)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected export string");
- g->export_name = wat_dup_string(p, p->tok, NULL);
- {
- WasmExport* ex = wasm_add_export(p->c, p->module);
- ex->name = wasm_strdup(p->module->heap, g->export_name,
- strlen(g->export_name));
- if (!ex->name)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- ex->kind = 3;
- ex->index = p->module->nglobals - 1u;
- }
- wat_next(p);
- wat_expect(p, WT_RPAREN, "')'");
- } else if (tok_is(p->tok, "mut")) {
- g->mutable_ = 1;
- wat_next(p);
- if (!wat_val_type(p->tok, &g->type) || !wasm_is_num_type(g->type))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected global type");
- wat_next(p);
- wat_expect(p, WT_RPAREN, "')'");
- } else {
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected global type");
- }
- } else {
- if (!wat_val_type(p->tok, &g->type) || !wasm_is_num_type(g->type))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected global type");
- wat_next(p);
- }
- if (!g->type)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: missing global type");
- wat_parse_const_expr(p, &g->init);
- wat_expect(p, WT_RPAREN, "')'");
-}
-
-static void wat_parse_elem_field(WatParser* p) {
- WasmElemSegment* e = wasm_add_elem(p->c, p->module);
- e->tableidx = 0;
- if (p->tok.kind == WT_ATOM) {
- int64_t tableidx;
- if (wat_parse_i64(p, &tableidx)) {
- if (tableidx < 0 || tableidx > UINT32_MAX)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: table index out of range");
- e->tableidx = (uint32_t)tableidx;
- wat_next(p);
- }
- }
- {
- WasmInsn off;
- wat_parse_const_expr(p, &off);
- if (off.kind != WASM_INSN_I32_CONST || off.imm < 0)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: element offset must be i32.const");
- e->offset = off.imm;
- }
- if (tok_is(p->tok, "func")) wat_next(p);
- while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
- int64_t idx;
- if (e->nfuncs >= 64u)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: too many element functions");
- wat_parse_func_index(p, &idx);
- if (idx < 0 || idx > UINT32_MAX)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: element function index out of range");
- e->funcs[e->nfuncs++] = (uint32_t)idx;
- wat_next(p);
- }
- wat_expect(p, WT_RPAREN, "')'");
-}
-
-static void wat_parse_start_field(WatParser* p) {
- int64_t idx;
- wat_parse_func_index(p, &idx);
- if (idx < 0 || idx > UINT32_MAX)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: start function index out of range");
- p->module->has_start = 1;
- p->module->start_func = (uint32_t)idx;
- wat_next(p);
- wat_expect(p, WT_RPAREN, "')'");
-}
-
-static void wat_parse_custom_field(WatParser* p) {
- WasmCustom* cs;
- size_t n = 0;
- char* bytes;
- if (p->tok.kind != WT_STRING)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected custom section name");
- cs = wasm_add_custom(p->c, p->module);
- cs->name = wat_dup_string(p, p->tok, NULL);
- if (strcmp(cs->name, "target_features") == 0 ||
- strcmp(cs->name, "target-feature") == 0)
- p->module->has_target_features = 1;
- wat_next(p);
- if (p->tok.kind == WT_STRING) {
- bytes = wat_dup_string(p, p->tok, &n);
- cs->data = (uint8_t*)p->module->heap->alloc(p->module->heap, n ? n : 1u, 1);
- if (!cs->data)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm: out of memory");
- memcpy(cs->data, bytes, n);
- cs->len = (uint32_t)n;
- p->module->heap->free(p->module->heap, bytes, p->tok.len + 1u);
- wat_next(p);
- }
- wat_expect(p, WT_RPAREN, "')'");
-}
-
-static void wat_parse_data_field(WatParser* p) {
- int64_t offset = 0;
- uint32_t memidx = 0;
- char* bytes;
- size_t nbytes;
- size_t alloc_len;
- if (p->tok.kind == WT_ATOM) {
- WasmInsnKind next_kind;
- int next_has_imm;
- if (p->tok.len && p->tok.p[0] == '$') {
- wat_next(p);
- } else if (!wat_instr_kind(p->tok, &next_kind, &next_has_imm)) {
- wat_parse_memory_index(p, &memidx);
- wat_next(p);
- } else {
- wat_next(p);
- }
- }
- if (p->tok.kind == WT_LPAREN) {
- size_t save_pos = p->pos;
- uint32_t save_line = p->line, save_col = p->col;
- wat_next(p);
- if (tok_is(p->tok, "memory")) {
- wat_next(p);
- wat_parse_memory_index(p, &memidx);
- wat_next(p);
- wat_expect(p, WT_RPAREN, "')'");
- } else {
- p->pos = save_pos;
- p->line = save_line;
- p->col = save_col;
- p->tok.kind = WT_LPAREN;
- p->tok.p = p->src + p->pos - 1u;
- p->tok.len = 1;
- p->tok.line = save_line;
- p->tok.col = save_col - 1u;
- }
- }
- wat_expect(p, WT_LPAREN, "'('");
- if (!tok_is(p->tok, "i32.const") && !tok_is(p->tok, "i64.const"))
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected const data offset");
- wat_next(p);
- if (!wat_parse_i64(p, &offset) || offset < 0 || offset > UINT32_MAX)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: bad data offset");
- wat_next(p);
- wat_expect(p, WT_RPAREN, "')'");
- if (p->tok.kind != WT_STRING)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: expected data string");
- alloc_len = p->tok.len + 1u;
- bytes = wat_dup_string(p, p->tok, &nbytes);
- if ((uint64_t)offset + nbytes > UINT32_MAX)
- wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: data segment too large");
- wasm_memory_ensure(p->c, p->module, memidx,
- (uint64_t)offset + (uint64_t)nbytes);
- memcpy(p->module->memories[memidx].data + (uint32_t)offset, bytes, nbytes);
- if ((uint64_t)offset + nbytes > p->module->memories[memidx].data_init_len)
- p->module->memories[memidx].data_init_len =
- (uint64_t)offset + (uint64_t)nbytes;
- p->module->heap->free(p->module->heap, bytes, alloc_len);
- wat_next(p);
- wat_expect(p, WT_RPAREN, "')'");
-}
-
-static void wat_parse_module(CfreeCompiler* c, const CfreeBytes* input,
- WasmModule* out) {
- WatParser p;
- memset(&p, 0, sizeof p);
- p.c = c;
- p.name = input->name;
- p.src = (const char*)input->data;
- p.len = input->len;
- p.line = 1;
- p.col = 1;
- p.module = out;
- wat_next(&p);
- wat_expect(&p, WT_LPAREN, "'('");
- if (!tok_is(p.tok, "module"))
- wasm_error(c, wasm_loc(p.tok.line, p.tok.col), "wasm wat: expected module");
- wat_next(&p);
- while (p.tok.kind != WT_RPAREN && p.tok.kind != WT_EOF) {
- if (p.tok.kind != WT_LPAREN)
- wasm_error(c, wasm_loc(p.tok.line, p.tok.col),
- "wasm wat: expected module field");
- wat_next(&p);
- if (tok_is(p.tok, "type")) {
- wat_next(&p);
- wat_parse_type_field(&p);
- } else if (tok_is(p.tok, "func")) {
- p.pos = (size_t)(p.tok.p - p.src);
- p.line = p.tok.line;
- p.col = p.tok.col;
- p.tok.kind = WT_LPAREN;
- p.tok.p = p.src + p.pos - 1u;
- p.tok.len = 1;
- p.tok.line = p.line;
- p.tok.col = p.col - 1u;
- wat_parse_func(&p);
- } else if (tok_is(p.tok, "export")) {
- wat_next(&p);
- wat_parse_export_field(&p);
- } else if (tok_is(p.tok, "memory")) {
- wat_next(&p);
- wat_parse_memory_field(&p);
- } else if (tok_is(p.tok, "data")) {
- wat_next(&p);
- wat_parse_data_field(&p);
- } else if (tok_is(p.tok, "import")) {
- wat_next(&p);
- wat_parse_import_field(&p);
- } else if (tok_is(p.tok, "table")) {
- wat_next(&p);
- wat_parse_table_field(&p);
- } else if (tok_is(p.tok, "global")) {
- wat_next(&p);
- wat_parse_global_field(&p);
- } else if (tok_is(p.tok, "elem")) {
- wat_next(&p);
- wat_parse_elem_field(&p);
- } else if (tok_is(p.tok, "start")) {
- wat_next(&p);
- wat_parse_start_field(&p);
- } else if (tok_is(p.tok, "custom") || tok_is(p.tok, "@custom")) {
- wat_next(&p);
- wat_parse_custom_field(&p);
- } else {
- wat_skip_list(&p);
- }
- }
- wat_expect(&p, WT_RPAREN, "')'");
- if (p.tok.kind != WT_EOF)
- wasm_error(c, wasm_loc(p.tok.line, p.tok.col),
- "wasm wat: trailing tokens after module");
-}
-
-static uint8_t bin_u8(BinReader* r) {
- if (r->pos >= r->len)
- wasm_error(r->c, wasm_loc(0, 0), "wasm: unexpected end of file");
- return r->data[r->pos++];
-}
-
-static uint32_t bin_uleb(BinReader* r) {
- uint32_t result = 0, shift = 0;
- uint32_t nbytes = 0;
- for (;;) {
- uint8_t b = bin_u8(r);
- if (nbytes++ >= 5u || (shift == 28u && (b & 0xf0u)))
- wasm_error(r->c, wasm_loc(0, 0), "wasm: invalid uleb128");
- result |= (uint32_t)(b & 0x7fu) << shift;
- if (!(b & 0x80u)) return result;
- shift += 7u;
- }
-}
-
-static uint64_t bin_uleb64(BinReader* r) {
- uint64_t result = 0;
- uint32_t shift = 0;
- uint32_t nbytes = 0;
- for (;;) {
- uint8_t b = bin_u8(r);
- if (nbytes++ >= 10u || (shift == 63u && (b & 0xfeu)))
- wasm_error(r->c, wasm_loc(0, 0), "wasm: invalid uleb128");
- result |= (uint64_t)(b & 0x7fu) << shift;
- if (!(b & 0x80u)) return result;
- shift += 7u;
- }
-}
-
-static int64_t bin_sleb(BinReader* r, uint32_t bits) {
- int64_t result = 0;
- uint32_t shift = 0;
- uint32_t max_bytes = (bits + 6u) / 7u;
- uint32_t nbytes = 0;
- uint8_t b;
- do {
- if (nbytes++ >= max_bytes)
- wasm_error(r->c, wasm_loc(0, 0), "wasm: invalid sleb128");
- b = bin_u8(r);
- result |= (int64_t)(b & 0x7fu) << shift;
- shift += 7u;
- } while (b & 0x80u);
- if (shift < bits && (b & 0x40u)) result |= -((int64_t)1 << shift);
- return result;
-}
-
-static double bin_f32(BinReader* r) {
- uint32_t bits = 0;
- float f;
- for (uint32_t i = 0; i < 4u; ++i) bits |= (uint32_t)bin_u8(r) << (i * 8u);
- memcpy(&f, &bits, sizeof f);
- return (double)f;
-}
-
-static double bin_f64(BinReader* r) {
- uint64_t bits = 0;
- double d;
- for (uint32_t i = 0; i < 8u; ++i) bits |= (uint64_t)bin_u8(r) << (i * 8u);
- memcpy(&d, &bits, sizeof d);
- return d;
-}
-
-static void bin_memarg(BinReader* r, uint32_t* align, uint64_t* offset,
- uint32_t* memidx) {
- uint32_t a = bin_uleb(r);
- *memidx = 0;
- if (a >= 64u && a < 128u) {
- *align = a - 64u;
- *memidx = bin_uleb(r);
- } else if (a < 64u) {
- *align = a;
- } else {
- wasm_error(r->c, wasm_loc(0, 0), "wasm: bad memory alignment");
- }
- *offset = bin_uleb64(r);
-}
-
-static void bin_need(BinReader* r, size_t n) {
- if (n > r->len || r->pos > r->len - n)
- wasm_error(r->c, wasm_loc(0, 0), "wasm: section length out of bounds");
-}
-
-static char* bin_name(BinReader* r, CfreeHeap* h, uint32_t* len_out) {
- uint32_t n = bin_uleb(r);
- char* s;
- bin_need(r, n);
- s = wasm_strdup(h, (const char*)(r->data + r->pos), n);
- if (!s) wasm_error(r->c, wasm_loc(0, 0), "wasm: out of memory");
- r->pos += n;
- if (len_out) *len_out = n;
- return s;
-}
-
-static WasmValType bin_val_type(BinReader* r, int refs_ok) {
- uint8_t b = bin_u8(r);
- switch (b) {
- case WASM_VAL_I32:
- case WASM_VAL_I64:
- case WASM_VAL_F32:
- case WASM_VAL_F64:
- return (WasmValType)b;
- case WASM_VAL_FUNCREF:
- case WASM_VAL_EXTERNREF:
- if (refs_ok) return (WasmValType)b;
- break;
- default:
- break;
- }
- wasm_error(r->c, wasm_loc(0, 0), "wasm: unsupported value type 0x%02x", b);
- return WASM_VAL_I32;
-}
-
-static void wasm_decode_binary(CfreeCompiler* c, const CfreeBytes* input,
- WasmModule* out) {
- BinReader r;
- uint32_t nfunc_types = 0;
- uint8_t last_id = 0;
- memset(&r, 0, sizeof r);
- r.c = c;
- r.data = input->data;
- r.len = input->len;
- r.module = out;
- if (r.len < 8 || memcmp(r.data, "\0asm\1\0\0\0", 8) != 0)
- wasm_error(c, wasm_loc(0, 0), "wasm: bad magic or version");
- r.pos = 8;
- while (r.pos < r.len) {
- uint8_t id = bin_u8(&r);
- uint32_t size = bin_uleb(&r);
- size_t end;
- bin_need(&r, size);
- end = r.pos + size;
- if (id != 0 && id <= last_id)
- wasm_error(c, wasm_loc(0, 0), "wasm: sections out of order");
- if (id != 0) last_id = id;
- if (id == 0) {
- uint32_t name_len = bin_uleb(&r);
- bin_need(&r, name_len);
- if (name_len == 7u && memcmp(r.data + r.pos, "linking", 7) == 0)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: relocatable object metadata is not frontend input");
- {
- WasmCustom* cs = wasm_add_custom(c, out);
- cs->name =
- wasm_strdup(out->heap, (const char*)(r.data + r.pos), name_len);
- if (!cs->name) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
- r.pos += name_len;
- if (strcmp(cs->name, "target_features") == 0 ||
- strcmp(cs->name, "target-feature") == 0)
- out->has_target_features = 1;
- cs->len = (uint32_t)(end - r.pos);
- if (cs->len) {
- cs->data = (uint8_t*)out->heap->alloc(out->heap, cs->len, 1);
- if (!cs->data) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
- memcpy(cs->data, r.data + r.pos, cs->len);
- }
- }
- r.pos = end;
- continue;
- }
- if (id == 1) {
- uint32_t i, count = bin_uleb(&r);
- if (count > 64u) wasm_error(c, wasm_loc(0, 0), "wasm: too many types");
- for (i = 0; i < count; ++i) {
- WasmFuncType* t = wasm_add_type(c, out);
- uint32_t j, nparam, nresult;
- if (bin_u8(&r) != 0x60u)
- wasm_error(c, wasm_loc(0, 0), "wasm: expected function type");
- nparam = bin_uleb(&r);
- if (nparam > 16u)
- wasm_error(c, wasm_loc(0, 0), "wasm: too many parameters");
- t->nparams = nparam;
- for (j = 0; j < nparam; ++j) t->params[j] = bin_val_type(&r, 1);
- nresult = bin_uleb(&r);
- if (nresult > 1u)
- wasm_error(c, wasm_loc(0, 0), "wasm: multi-result unsupported");
- t->nresults = nresult;
- for (j = 0; j < nresult; ++j) t->results[j] = bin_val_type(&r, 1);
- }
- } else if (id == 2) {
- uint32_t i, count = bin_uleb(&r);
- for (i = 0; i < count; ++i) {
- char* mod = bin_name(&r, out->heap, NULL);
- char* name = bin_name(&r, out->heap, NULL);
- uint8_t kind = bin_u8(&r);
- if (kind == 0) {
- uint32_t typeidx = bin_uleb(&r);
- WasmFunc* f;
- if (typeidx >= out->ntypes)
- wasm_error(c, wasm_loc(0, 0), "wasm: bad import type index");
- f = wasm_add_func(c, out);
- f->is_import = 1;
- f->import_module = mod;
- f->import_name = name;
- f->typeidx = typeidx;
- f->has_typeidx = 1;
- f->nparams = out->types[typeidx].nparams;
- memcpy(f->params, out->types[typeidx].params,
- sizeof(f->params[0]) * f->nparams);
- f->nresults = out->types[typeidx].nresults;
- memcpy(f->results, out->types[typeidx].results,
- sizeof(f->results[0]) * f->nresults);
- } else if (kind == 1) {
- WasmTable* t = wasm_add_table(c, out);
- t->is_import = 1;
- t->import_module = mod;
- t->import_name = name;
- t->elem_type = bin_val_type(&r, 1);
- {
- uint32_t flags = bin_uleb(&r);
- if (flags & ~1u)
- wasm_error(c, wasm_loc(0, 0), "wasm: unsupported table limits");
- t->has_max = (flags & 1u) != 0;
- t->min = bin_uleb(&r);
- if (t->has_max) t->max = bin_uleb(&r);
- }
- } else if (kind == 2) {
- uint32_t flags;
- WasmMemory* mem = wasm_add_memory(c, out);
- mem->is_import = 1;
- mem->import_module = mod;
- mem->import_name = name;
- flags = bin_uleb(&r);
- if (flags & ~7u)
- wasm_error(c, wasm_loc(0, 0), "wasm: unsupported memory limits");
- mem->has_max = (flags & 1u) != 0;
- mem->shared = (flags & 2u) != 0;
- mem->is64 = (flags & 4u) != 0;
- mem->min_pages = mem->is64 ? bin_uleb64(&r) : bin_uleb(&r);
- if (mem->has_max)
- mem->max_pages = mem->is64 ? bin_uleb64(&r) : bin_uleb(&r);
- } else if (kind == 3) {
- WasmGlobal* g = wasm_add_global(c, out);
- g->is_import = 1;
- g->import_module = mod;
- g->import_name = name;
- g->type = bin_val_type(&r, 0);
- g->mutable_ = bin_u8(&r);
- if (g->mutable_ > 1u)
- wasm_error(c, wasm_loc(0, 0), "wasm: bad global mutability");
- } else {
- wasm_error(c, wasm_loc(0, 0), "wasm: unsupported import kind");
- }
- }
- } else if (id == 3) {
- uint32_t i, count = bin_uleb(&r);
- if (count > 64u)
- wasm_error(c, wasm_loc(0, 0), "wasm: too many functions");
- for (i = 0; i < count; ++i) {
- uint32_t typeidx = bin_uleb(&r);
- WasmFunc* f;
- if (typeidx >= out->ntypes)
- wasm_error(c, wasm_loc(0, 0), "wasm: bad function type index");
- f = wasm_add_func(c, out);
- f->typeidx = typeidx;
- f->has_typeidx = 1;
- f->nparams = out->types[typeidx].nparams;
- memcpy(f->params, out->types[typeidx].params,
- sizeof(f->params[0]) * f->nparams);
- f->nresults = out->types[typeidx].nresults;
- memcpy(f->results, out->types[typeidx].results,
- sizeof(f->results[0]) * f->nresults);
- nfunc_types++;
- }
- } else if (id == 5) {
- uint32_t count = bin_uleb(&r);
- for (uint32_t mi = 0; mi < count; ++mi) {
- uint32_t flags = bin_uleb(&r);
- WasmMemory* mem = wasm_add_memory(c, out);
- if (flags & ~7u)
- wasm_error(c, wasm_loc(0, 0), "wasm: unsupported memory limits");
- mem->has_max = (flags & 1u) != 0;
- mem->shared = (flags & 2u) != 0;
- mem->is64 = (flags & 4u) != 0;
- mem->min_pages = mem->is64 ? bin_uleb64(&r) : bin_uleb(&r);
- if (mem->has_max)
- mem->max_pages = mem->is64 ? bin_uleb64(&r) : bin_uleb(&r);
- wasm_memory_ensure(c, out, out->nmemories - 1u,
- mem->min_pages * 65536u);
- }
- } else if (id == 4) {
- uint32_t i, count = bin_uleb(&r);
- for (i = 0; i < count; ++i) {
- WasmTable* t = wasm_add_table(c, out);
- uint32_t flags;
- t->elem_type = bin_val_type(&r, 1);
- flags = bin_uleb(&r);
- if (flags & ~1u)
- wasm_error(c, wasm_loc(0, 0), "wasm: unsupported table limits");
- t->has_max = (flags & 1u) != 0;
- t->min = bin_uleb(&r);
- if (t->has_max) t->max = bin_uleb(&r);
- }
- } else if (id == 6) {
- uint32_t i, count = bin_uleb(&r);
- for (i = 0; i < count; ++i) {
- WasmGlobal* g = wasm_add_global(c, out);
- uint8_t op;
- g->type = bin_val_type(&r, 0);
- g->mutable_ = bin_u8(&r);
- if (g->mutable_ > 1u)
- wasm_error(c, wasm_loc(0, 0), "wasm: bad global mutability");
- op = bin_u8(&r);
- switch (op) {
- case 0x41:
- g->init.kind = WASM_INSN_I32_CONST;
- g->init.imm = bin_sleb(&r, 32);
- break;
- case 0x42:
- g->init.kind = WASM_INSN_I64_CONST;
- g->init.imm = bin_sleb(&r, 64);
- break;
- case 0x43:
- g->init.kind = WASM_INSN_F32_CONST;
- g->init.fp = bin_f32(&r);
- break;
- case 0x44:
- g->init.kind = WASM_INSN_F64_CONST;
- g->init.fp = bin_f64(&r);
- break;
- default:
- wasm_error(c, wasm_loc(0, 0),
- "wasm: unsupported global initializer");
- }
- if (bin_u8(&r) != 0x0bu)
- wasm_error(c, wasm_loc(0, 0), "wasm: malformed global initializer");
- }
- } else if (id == 7) {
- uint32_t i, count = bin_uleb(&r);
- for (i = 0; i < count; ++i) {
- char* name;
- uint8_t kind;
- uint32_t idx;
- WasmExport* ex;
- name = bin_name(&r, out->heap, NULL);
- kind = bin_u8(&r);
- idx = bin_uleb(&r);
- if (kind > 3u) wasm_error(c, wasm_loc(0, 0), "wasm: bad export kind");
- ex = wasm_add_export(c, out);
- ex->name = name;
- ex->kind = kind;
- ex->index = idx;
- if (kind == 0 && idx < out->nfuncs) {
- WasmFunc* f = &out->funcs[idx];
- wasm_free_str(out->heap, &f->export_name);
- f->export_name = wasm_strdup(out->heap, name, strlen(name));
- if (!f->export_name)
- wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
- } else if (kind == 1 && idx < out->ntables) {
- wasm_free_str(out->heap, &out->tables[idx].export_name);
- out->tables[idx].export_name =
- wasm_strdup(out->heap, name, strlen(name));
- } else if (kind == 2 && idx < out->nmemories) {
- wasm_free_str(out->heap, &out->memories[idx].export_name);
- out->memories[idx].export_name =
- wasm_strdup(out->heap, name, strlen(name));
- } else if (kind == 3 && idx < out->nglobals) {
- wasm_free_str(out->heap, &out->globals[idx].export_name);
- out->globals[idx].export_name =
- wasm_strdup(out->heap, name, strlen(name));
- }
- }
- } else if (id == 8) {
- out->has_start = 1;
- out->start_func = bin_uleb(&r);
- } else if (id == 9) {
- uint32_t i, count = bin_uleb(&r);
- for (i = 0; i < count; ++i) {
- WasmElemSegment* e = wasm_add_elem(c, out);
- uint32_t flags = bin_uleb(&r);
- uint32_t n;
- if (flags != 0)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: unsupported element segment kind");
- e->tableidx = 0;
- if (bin_u8(&r) != 0x41)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: expected i32.const element offset");
- e->offset = bin_sleb(&r, 32);
- if (bin_u8(&r) != 0x0b || e->offset < 0)
- wasm_error(c, wasm_loc(0, 0), "wasm: bad element offset");
- n = bin_uleb(&r);
- if (n > 64u)
- wasm_error(c, wasm_loc(0, 0), "wasm: too many element functions");
- for (uint32_t k = 0; k < n; ++k) e->funcs[e->nfuncs++] = bin_uleb(&r);
- }
- } else if (id == 10) {
- uint32_t i, count = bin_uleb(&r);
- uint32_t func_index = 0;
- if (count != nfunc_types)
- wasm_error(c, wasm_loc(0, 0), "wasm: function/code count mismatch");
- for (i = 0; i < count; ++i) {
- uint32_t body_size = bin_uleb(&r);
- size_t body_end;
- uint32_t local_groups, j;
- uint32_t control_depth = 0;
- int saw_body_end = 0;
- WasmFunc* f;
- while (func_index < out->nfuncs && out->funcs[func_index].is_import)
- func_index++;
- if (func_index >= out->nfuncs)
- wasm_error(c, wasm_loc(0, 0), "wasm: code body without function");
- f = &out->funcs[func_index++];
- bin_need(&r, body_size);
- body_end = r.pos + body_size;
- local_groups = bin_uleb(&r);
- for (j = 0; j < local_groups; ++j) {
- uint32_t k, nlocals = bin_uleb(&r);
- WasmValType vt = bin_val_type(&r, 1);
- if (nlocals > 32u || f->nlocals > 32u - nlocals)
- wasm_error(c, wasm_loc(0, 0), "wasm: too many locals");
- for (k = 0; k < nlocals; ++k) f->locals[f->nlocals++] = vt;
- }
- while (r.pos < body_end) {
- uint8_t op = bin_u8(&r);
- if (op == 0x0bu) {
- if (!control_depth) {
- saw_body_end = 1;
- break;
- }
- control_depth--;
- wasm_func_add_insn(c, out, f, WASM_INSN_END, 0);
- continue;
- }
- switch (op) {
- case 0x00:
- wasm_func_add_insn(c, out, f, WASM_INSN_UNREACHABLE, 0);
- break;
- case 0x01:
- wasm_func_add_insn(c, out, f, WASM_INSN_NOP, 0);
- break;
- case 0x02:
- if (bin_u8(&r) != 0x40u)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: block results are unsupported");
- control_depth++;
- wasm_func_add_insn(c, out, f, WASM_INSN_BLOCK, 0);
- break;
- case 0x03:
- if (bin_u8(&r) != 0x40u)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: loop results are unsupported");
- control_depth++;
- wasm_func_add_insn(c, out, f, WASM_INSN_LOOP, 0);
- break;
- case 0x04:
- if (bin_u8(&r) != 0x40u)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: if results are unsupported");
- control_depth++;
- wasm_func_add_insn(c, out, f, WASM_INSN_IF, 0);
- break;
- case 0x05:
- wasm_func_add_insn(c, out, f, WASM_INSN_ELSE, 0);
- break;
- case 0x0c:
- wasm_func_add_insn(c, out, f, WASM_INSN_BR, bin_uleb(&r));
- break;
- case 0x0d:
- wasm_func_add_insn(c, out, f, WASM_INSN_BR_IF, bin_uleb(&r));
- break;
- case 0x0e: {
- WasmInsn* in;
- uint32_t n = bin_uleb(&r);
- if (n >= 16u)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: too many br_table targets");
- wasm_func_add_insn(c, out, f, WASM_INSN_BR_TABLE, 0);
- in = &f->insns[f->ninsns - 1u];
- for (uint32_t k = 0; k < n; ++k) in->targets[k] = bin_uleb(&r);
- in->targets[n] = bin_uleb(&r);
- in->ntargets = n + 1u;
- break;
- }
- case 0x0f:
- wasm_func_add_insn(c, out, f, WASM_INSN_RETURN, 0);
- break;
- case 0x1a:
- wasm_func_add_insn(c, out, f, WASM_INSN_DROP, 0);
- break;
- case 0x1b:
- wasm_func_add_insn(c, out, f, WASM_INSN_SELECT, 0);
- break;
- case 0x28:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_LOAD, ma, mo, mi); }
- break;
- case 0x29:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD, ma, mo, mi); }
- break;
- case 0x2c:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_LOAD8_S, ma, mo, mi); }
- break;
- case 0x2d:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_LOAD8_U, ma, mo, mi); }
- break;
- case 0x2e:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_LOAD16_S, ma, mo, mi); }
- break;
- case 0x2f:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_LOAD16_U, ma, mo, mi); }
- break;
- case 0x30:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD8_S, ma, mo, mi); }
- break;
- case 0x31:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD8_U, ma, mo, mi); }
- break;
- case 0x32:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD16_S, ma, mo, mi); }
- break;
- case 0x33:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD16_U, ma, mo, mi); }
- break;
- case 0x34:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD32_S, ma, mo, mi); }
- break;
- case 0x35:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD32_U, ma, mo, mi); }
- break;
- case 0x36:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_STORE, ma, mo, mi); }
- break;
- case 0x37:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_STORE, ma, mo, mi); }
- break;
- case 0x3a:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_STORE8, ma, mo, mi); }
- break;
- case 0x3b:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_STORE16, ma, mo, mi); }
- break;
- case 0x3c:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_STORE8, ma, mo, mi); }
- break;
- case 0x3d:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_STORE16, ma, mo, mi); }
- break;
- case 0x3e:
- { uint32_t ma, mi; uint64_t mo; bin_memarg(&r, &ma, &mo, &mi); wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_STORE32, ma, mo, mi); }
- break;
- case 0x3f:
- wasm_func_add_insn(c, out, f, WASM_INSN_MEMORY_SIZE, 0);
- f->insns[f->ninsns - 1u].memidx = bin_uleb(&r);
- break;
- case 0x40:
- wasm_func_add_insn(c, out, f, WASM_INSN_MEMORY_GROW, 0);
- f->insns[f->ninsns - 1u].memidx = bin_uleb(&r);
- break;
- case 0x10:
- wasm_func_add_insn(c, out, f, WASM_INSN_CALL, bin_uleb(&r));
- break;
- case 0x11: {
- WasmInsn* in;
- wasm_func_add_insn(c, out, f, WASM_INSN_CALL_INDIRECT,
- bin_uleb(&r));
- in = &f->insns[f->ninsns - 1u];
- in->align = bin_uleb(&r);
- break;
- }
- case 0x12:
- wasm_func_add_insn(c, out, f, WASM_INSN_RETURN_CALL,
- bin_uleb(&r));
- break;
- case 0x13: {
- WasmInsn* in;
- wasm_func_add_insn(c, out, f, WASM_INSN_RETURN_CALL_INDIRECT,
- bin_uleb(&r));
- in = &f->insns[f->ninsns - 1u];
- in->align = bin_uleb(&r);
- break;
- }
- case 0x14:
- wasm_func_add_insn(c, out, f, WASM_INSN_CALL_REF,
- bin_uleb(&r));
- break;
- case 0x15:
- wasm_func_add_insn(c, out, f, WASM_INSN_RETURN_CALL_REF,
- bin_uleb(&r));
- break;
- case 0x20:
- wasm_func_add_insn(c, out, f, WASM_INSN_LOCAL_GET, bin_uleb(&r));
- break;
- case 0x21:
- wasm_func_add_insn(c, out, f, WASM_INSN_LOCAL_SET, bin_uleb(&r));
- break;
- case 0x22:
- wasm_func_add_insn(c, out, f, WASM_INSN_LOCAL_TEE, bin_uleb(&r));
- break;
- case 0x23:
- wasm_func_add_insn(c, out, f, WASM_INSN_GLOBAL_GET, bin_uleb(&r));
- break;
- case 0x24:
- wasm_func_add_insn(c, out, f, WASM_INSN_GLOBAL_SET, bin_uleb(&r));
- break;
- case 0x41:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_CONST,
- bin_sleb(&r, 32));
- break;
- case 0x42:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_CONST,
- bin_sleb(&r, 64));
- break;
- case 0x43:
- wasm_func_add_fp_insn(c, out, f, WASM_INSN_F32_CONST,
- bin_f32(&r));
- break;
- case 0x44:
- wasm_func_add_fp_insn(c, out, f, WASM_INSN_F64_CONST,
- bin_f64(&r));
- break;
- case 0xd0:
- wasm_func_add_insn(c, out, f, WASM_INSN_REF_NULL,
- bin_val_type(&r, 1));
- break;
- case 0xd1:
- wasm_func_add_insn(c, out, f, WASM_INSN_REF_IS_NULL, 0);
- break;
- case 0xd2:
- wasm_func_add_insn(c, out, f, WASM_INSN_REF_FUNC,
- bin_uleb(&r));
- break;
- case 0x45:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_EQZ, 0);
- break;
- case 0x46:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_EQ, 0);
- break;
- case 0x47:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_NE, 0);
- break;
- case 0x48:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_LT_S, 0);
- break;
- case 0x49:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_LT_U, 0);
- break;
- case 0x4a:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_GT_S, 0);
- break;
- case 0x4b:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_GT_U, 0);
- break;
- case 0x4c:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_LE_S, 0);
- break;
- case 0x4d:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_LE_U, 0);
- break;
- case 0x4e:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_GE_S, 0);
- break;
- case 0x4f:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_GE_U, 0);
- break;
- case 0x50:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_EQZ, 0);
- break;
- case 0x51:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_EQ, 0);
- break;
- case 0x52:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_NE, 0);
- break;
- case 0x53:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_LT_S, 0);
- break;
- case 0x54:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_LT_U, 0);
- break;
- case 0x55:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_GT_S, 0);
- break;
- case 0x56:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_GT_U, 0);
- break;
- case 0x57:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_LE_S, 0);
- break;
- case 0x58:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_LE_U, 0);
- break;
- case 0x59:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_GE_S, 0);
- break;
- case 0x5a:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_GE_U, 0);
- break;
- case 0x6a:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_ADD, 0);
- break;
- case 0x6b:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_SUB, 0);
- break;
- case 0x6c:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_MUL, 0);
- break;
- case 0x6d:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_DIV_S, 0);
- break;
- case 0x6e:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_DIV_U, 0);
- break;
- case 0x6f:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_REM_S, 0);
- break;
- case 0x70:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_REM_U, 0);
- break;
- case 0x71:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_AND, 0);
- break;
- case 0x72:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_OR, 0);
- break;
- case 0x73:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_XOR, 0);
- break;
- case 0x74:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_SHL, 0);
- break;
- case 0x75:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_SHR_S, 0);
- break;
- case 0x76:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_SHR_U, 0);
- break;
- case 0x67:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_CLZ, 0);
- break;
- case 0x68:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_CTZ, 0);
- break;
- case 0x69:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_POPCNT, 0);
- break;
- case 0x77:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_ROTL, 0);
- break;
- case 0x78:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_ROTR, 0);
- break;
- case 0x79:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_CLZ, 0);
- break;
- case 0x7a:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_CTZ, 0);
- break;
- case 0x7b:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_POPCNT, 0);
- break;
- case 0x7c:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_ADD, 0);
- break;
- case 0x7d:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_SUB, 0);
- break;
- case 0x7e:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_MUL, 0);
- break;
- case 0x7f:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_DIV_S, 0);
- break;
- case 0x80:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_DIV_U, 0);
- break;
- case 0x81:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_REM_S, 0);
- break;
- case 0x82:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_REM_U, 0);
- break;
- case 0x83:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_AND, 0);
- break;
- case 0x84:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_OR, 0);
- break;
- case 0x85:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_XOR, 0);
- break;
- case 0x86:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_SHL, 0);
- break;
- case 0x87:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_SHR_S, 0);
- break;
- case 0x88:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_SHR_U, 0);
- break;
- case 0x89:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_ROTL, 0);
- break;
- case 0x8a:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_ROTR, 0);
- break;
- case 0x92:
- wasm_func_add_insn(c, out, f, WASM_INSN_F32_ADD, 0);
- break;
- case 0x93:
- wasm_func_add_insn(c, out, f, WASM_INSN_F32_SUB, 0);
- break;
- case 0x94:
- wasm_func_add_insn(c, out, f, WASM_INSN_F32_MUL, 0);
- break;
- case 0x95:
- wasm_func_add_insn(c, out, f, WASM_INSN_F32_DIV, 0);
- break;
- case 0xa0:
- wasm_func_add_insn(c, out, f, WASM_INSN_F64_ADD, 0);
- break;
- case 0xa1:
- wasm_func_add_insn(c, out, f, WASM_INSN_F64_SUB, 0);
- break;
- case 0xa2:
- wasm_func_add_insn(c, out, f, WASM_INSN_F64_MUL, 0);
- break;
- case 0xa3:
- wasm_func_add_insn(c, out, f, WASM_INSN_F64_DIV, 0);
- break;
- case 0x5b:
- wasm_func_add_insn(c, out, f, WASM_INSN_F32_EQ, 0);
- break;
- case 0x5c:
- wasm_func_add_insn(c, out, f, WASM_INSN_F32_NE, 0);
- break;
- case 0x5d:
- wasm_func_add_insn(c, out, f, WASM_INSN_F32_LT, 0);
- break;
- case 0x5e:
- wasm_func_add_insn(c, out, f, WASM_INSN_F32_GT, 0);
- break;
- case 0x5f:
- wasm_func_add_insn(c, out, f, WASM_INSN_F32_LE, 0);
- break;
- case 0x60:
- wasm_func_add_insn(c, out, f, WASM_INSN_F32_GE, 0);
- break;
- case 0x61:
- wasm_func_add_insn(c, out, f, WASM_INSN_F64_EQ, 0);
- break;
- case 0x62:
- wasm_func_add_insn(c, out, f, WASM_INSN_F64_NE, 0);
- break;
- case 0x63:
- wasm_func_add_insn(c, out, f, WASM_INSN_F64_LT, 0);
- break;
- case 0x64:
- wasm_func_add_insn(c, out, f, WASM_INSN_F64_GT, 0);
- break;
- case 0x65:
- wasm_func_add_insn(c, out, f, WASM_INSN_F64_LE, 0);
- break;
- case 0x66:
- wasm_func_add_insn(c, out, f, WASM_INSN_F64_GE, 0);
- break;
- case 0xa7:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_WRAP_I64, 0);
- break;
- case 0xa8:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_TRUNC_F32_S, 0);
- break;
- case 0xa9:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_TRUNC_F32_U, 0);
- break;
- case 0xaa:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_TRUNC_F64_S, 0);
- break;
- case 0xab:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_TRUNC_F64_U, 0);
- break;
- case 0xac:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_EXTEND_I32_S, 0);
- break;
- case 0xad:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_EXTEND_I32_U, 0);
- break;
- case 0xae:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_TRUNC_F32_S, 0);
- break;
- case 0xaf:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_TRUNC_F32_U, 0);
- break;
- case 0xb0:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_TRUNC_F64_S, 0);
- break;
- case 0xb1:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_TRUNC_F64_U, 0);
- break;
- case 0xb2:
- wasm_func_add_insn(c, out, f, WASM_INSN_F32_CONVERT_I32_S, 0);
- break;
- case 0xb3:
- wasm_func_add_insn(c, out, f, WASM_INSN_F32_CONVERT_I32_U, 0);
- break;
- case 0xb4:
- wasm_func_add_insn(c, out, f, WASM_INSN_F32_CONVERT_I64_S, 0);
- break;
- case 0xb5:
- wasm_func_add_insn(c, out, f, WASM_INSN_F32_CONVERT_I64_U, 0);
- break;
- case 0xb6:
- wasm_func_add_insn(c, out, f, WASM_INSN_F32_DEMOTE_F64, 0);
- break;
- case 0xb7:
- wasm_func_add_insn(c, out, f, WASM_INSN_F64_CONVERT_I32_S, 0);
- break;
- case 0xb8:
- wasm_func_add_insn(c, out, f, WASM_INSN_F64_CONVERT_I32_U, 0);
- break;
- case 0xb9:
- wasm_func_add_insn(c, out, f, WASM_INSN_F64_CONVERT_I64_S, 0);
- break;
- case 0xba:
- wasm_func_add_insn(c, out, f, WASM_INSN_F64_CONVERT_I64_U, 0);
- break;
- case 0xbb:
- wasm_func_add_insn(c, out, f, WASM_INSN_F64_PROMOTE_F32, 0);
- break;
- case 0xbc:
- wasm_func_add_insn(c, out, f, WASM_INSN_I32_REINTERPRET_F32, 0);
- break;
- case 0xbd:
- wasm_func_add_insn(c, out, f, WASM_INSN_I64_REINTERPRET_F64, 0);
- break;
- case 0xbe:
- wasm_func_add_insn(c, out, f, WASM_INSN_F32_REINTERPRET_I32, 0);
- break;
- case 0xbf:
- wasm_func_add_insn(c, out, f, WASM_INSN_F64_REINTERPRET_I64, 0);
- break;
- case 0xfe: {
- uint32_t sub = bin_uleb(&r);
- uint32_t ma, mi;
- uint64_t mo;
- switch (sub) {
- case 0x00:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_MEMORY_ATOMIC_NOTIFY, ma,
- mo, mi);
- break;
- case 0x01:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_ATOMIC_WAIT,
- ma, mo, mi);
- break;
- case 0x02:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_ATOMIC_WAIT,
- ma, mo, mi);
- break;
- case 0x03:
- if (bin_u8(&r) != 0)
- wasm_error(c, wasm_loc(0, 0), "wasm: bad atomic.fence");
- wasm_func_add_insn(c, out, f, WASM_INSN_ATOMIC_FENCE, 0);
- break;
- case 0x10:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_ATOMIC_LOAD,
- ma, mo, mi);
- break;
- case 0x11:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_ATOMIC_LOAD,
- ma, mo, mi);
- break;
- case 0x12:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I32_ATOMIC_LOAD8_U, ma, mo,
- mi);
- break;
- case 0x13:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I32_ATOMIC_LOAD16_U, ma, mo,
- mi);
- break;
- case 0x14:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I64_ATOMIC_LOAD8_U, ma, mo,
- mi);
- break;
- case 0x15:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I64_ATOMIC_LOAD16_U, ma, mo,
- mi);
- break;
- case 0x16:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I64_ATOMIC_LOAD32_U, ma, mo,
- mi);
- break;
- case 0x17:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_ATOMIC_STORE,
- ma, mo, mi);
- break;
- case 0x18:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_ATOMIC_STORE,
- ma, mo, mi);
- break;
- case 0x19:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I32_ATOMIC_STORE8, ma, mo,
- mi);
- break;
- case 0x1a:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I32_ATOMIC_STORE16, ma, mo,
- mi);
- break;
- case 0x1b:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I64_ATOMIC_STORE8, ma, mo,
- mi);
- break;
- case 0x1c:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I64_ATOMIC_STORE16, ma, mo,
- mi);
- break;
- case 0x1d:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I64_ATOMIC_STORE32, ma, mo,
- mi);
- break;
- case 0x1e:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I32_ATOMIC_RMW_ADD, ma, mo,
- mi);
- break;
- case 0x1f:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I64_ATOMIC_RMW_ADD, ma, mo,
- mi);
- break;
- case 0x25:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I32_ATOMIC_RMW_SUB, ma, mo,
- mi);
- break;
- case 0x26:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I64_ATOMIC_RMW_SUB, ma, mo,
- mi);
- break;
- case 0x2c:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I32_ATOMIC_RMW_AND, ma, mo,
- mi);
- break;
- case 0x2d:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I64_ATOMIC_RMW_AND, ma, mo,
- mi);
- break;
- case 0x33:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I32_ATOMIC_RMW_OR, ma, mo,
- mi);
- break;
- case 0x34:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I64_ATOMIC_RMW_OR, ma, mo,
- mi);
- break;
- case 0x3a:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I32_ATOMIC_RMW_XOR, ma, mo,
- mi);
- break;
- case 0x3b:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I64_ATOMIC_RMW_XOR, ma, mo,
- mi);
- break;
- case 0x41:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I32_ATOMIC_RMW_XCHG, ma, mo,
- mi);
- break;
- case 0x42:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I64_ATOMIC_RMW_XCHG, ma, mo,
- mi);
- break;
- case 0x48:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I32_ATOMIC_RMW_CMPXCHG, ma,
- mo, mi);
- break;
- case 0x49:
- bin_memarg(&r, &ma, &mo, &mi);
- wasm_func_add_mem_insn(c, out, f,
- WASM_INSN_I64_ATOMIC_RMW_CMPXCHG, ma,
- mo, mi);
- break;
- default:
- wasm_error(c, wasm_loc(0, 0),
- "wasm: unsupported threads opcode 0x%x", sub);
- }
- break;
- }
- default:
- wasm_error(c, wasm_loc(0, 0), "wasm: unsupported opcode 0x%02x",
- op);
- }
- }
- if (!saw_body_end)
- wasm_error(c, wasm_loc(0, 0), "wasm: function body missing end");
- r.pos = body_end;
- }
- } else if (id == 11) {
- uint32_t i, count = bin_uleb(&r);
- for (i = 0; i < count; ++i) {
- uint32_t flags = bin_uleb(&r);
- int64_t offset = 0;
- uint32_t memidx = 0;
- uint32_t n;
- if (flags == 2u)
- memidx = bin_uleb(&r);
- else if (flags != 0)
- wasm_error(c, wasm_loc(0, 0), "wasm: passive data unsupported");
- {
- uint8_t op = bin_u8(&r);
- if (op == 0x41)
- offset = bin_sleb(&r, 32);
- else if (op == 0x42)
- offset = bin_sleb(&r, 64);
- else
- wasm_error(c, wasm_loc(0, 0),
- "wasm: expected const data offset");
- }
- if (bin_u8(&r) != 0x0b || offset < 0)
- wasm_error(c, wasm_loc(0, 0), "wasm: bad data offset");
- n = bin_uleb(&r);
- bin_need(&r, n);
- wasm_memory_ensure(c, out, memidx, (uint64_t)offset + n);
- memcpy(out->memories[memidx].data + (uint32_t)offset, r.data + r.pos,
- n);
- if ((uint64_t)offset + n > out->memories[memidx].data_init_len)
- out->memories[memidx].data_init_len = (uint64_t)offset + n;
- r.pos += n;
- }
- } else if (id == 12) {
- wasm_error(c, wasm_loc(0, 0),
- "wasm: data count sections are unsupported");
- } else {
- r.pos = end;
- }
- if (r.pos != end)
- wasm_error(c, wasm_loc(0, 0), "wasm: malformed section length");
- }
-}
-
-static int wasm_is_binary(const CfreeBytes* input) {
- return input->len >= 4u && input->data[0] == 0x00 && input->data[1] == 0x61 &&
- input->data[2] == 0x73 && input->data[3] == 0x6d;
-}
-
-static CfreeCgTypeId wasm_cg_type(CfreeCompiler* c, CfreeCgBuiltinTypes b,
- WasmValType vt) {
- switch (vt) {
- case WASM_VAL_I32:
- return b.id[CFREE_CG_BUILTIN_I32];
- case WASM_VAL_I64:
- return b.id[CFREE_CG_BUILTIN_I64];
- case WASM_VAL_F32:
- return b.id[CFREE_CG_BUILTIN_F32];
- case WASM_VAL_F64:
- return b.id[CFREE_CG_BUILTIN_F64];
- case WASM_VAL_FUNCREF:
- case WASM_VAL_EXTERNREF:
- return cfree_cg_type_ptr(c, b.id[CFREE_CG_BUILTIN_VOID], 0);
- }
- wasm_error(c, wasm_loc(0, 0), "wasm: unsupported value type");
- return b.id[CFREE_CG_BUILTIN_I32];
-}
-
-static WasmValType wasm_func_local_type(const WasmFunc* f, uint32_t index) {
- if (index < f->nparams) return f->params[index];
- return f->locals[index - f->nparams];
-}
-
-static CfreeCgMemAccess wasm_cg_mem(CfreeCompiler* c, CfreeCgBuiltinTypes b,
- WasmValType vt) {
- CfreeCgMemAccess mem;
- memset(&mem, 0, sizeof mem);
- mem.type = wasm_cg_type(c, b, vt);
- return mem;
-}
-
-static CfreeCgMemAccess wasm_cg_mem_type(CfreeCgTypeId ty) {
- CfreeCgMemAccess mem;
- memset(&mem, 0, sizeof mem);
- mem.type = ty;
- return mem;
-}
-
-static void wasm_cg_push_zero(CfreeCompiler* c, CfreeCg* cg,
- CfreeCgBuiltinTypes b, WasmValType vt) {
- CfreeCgTypeId ty = wasm_cg_type(c, b, vt);
- if (wasm_is_ref_type(vt))
- cfree_cg_push_null(cg, ty);
- else if (vt == WASM_VAL_F32 || vt == WASM_VAL_F64)
- cfree_cg_push_float(cg, 0.0, ty);
- else
- cfree_cg_push_int(cg, 0, ty);
-}
-
-static CfreeCgTypeId wasm_load_storage_type(CfreeCgBuiltinTypes b,
- uint8_t kind) {
- switch (kind) {
- case WASM_INSN_I32_LOAD8_S:
- case WASM_INSN_I32_LOAD8_U:
- case WASM_INSN_I64_LOAD8_S:
- case WASM_INSN_I64_LOAD8_U:
- return b.id[CFREE_CG_BUILTIN_I8];
- case WASM_INSN_I32_LOAD16_S:
- case WASM_INSN_I32_LOAD16_U:
- case WASM_INSN_I64_LOAD16_S:
- case WASM_INSN_I64_LOAD16_U:
- return b.id[CFREE_CG_BUILTIN_I16];
- case WASM_INSN_I64_LOAD32_S:
- case WASM_INSN_I64_LOAD32_U:
- case WASM_INSN_I32_LOAD:
- return b.id[CFREE_CG_BUILTIN_I32];
- default:
- return b.id[CFREE_CG_BUILTIN_I64];
- }
-}
-
-static CfreeCgTypeId wasm_store_storage_type(CfreeCgBuiltinTypes b,
- uint8_t kind) {
- switch (kind) {
- case WASM_INSN_I32_STORE8:
- case WASM_INSN_I64_STORE8:
- return b.id[CFREE_CG_BUILTIN_I8];
- case WASM_INSN_I32_STORE16:
- case WASM_INSN_I64_STORE16:
- return b.id[CFREE_CG_BUILTIN_I16];
- case WASM_INSN_I64_STORE32:
- case WASM_INSN_I32_STORE:
- return b.id[CFREE_CG_BUILTIN_I32];
- default:
- return b.id[CFREE_CG_BUILTIN_I64];
- }
-}
-
-static uint32_t wasm_mem_width(uint8_t kind) {
- switch (kind) {
- case WASM_INSN_I32_LOAD8_S:
- case WASM_INSN_I32_LOAD8_U:
- case WASM_INSN_I64_LOAD8_S:
- case WASM_INSN_I64_LOAD8_U:
- case WASM_INSN_I32_STORE8:
- case WASM_INSN_I64_STORE8:
- case WASM_INSN_I32_ATOMIC_LOAD8_U:
- case WASM_INSN_I64_ATOMIC_LOAD8_U:
- case WASM_INSN_I32_ATOMIC_STORE8:
- case WASM_INSN_I64_ATOMIC_STORE8:
- return 1;
- case WASM_INSN_I32_LOAD16_S:
- case WASM_INSN_I32_LOAD16_U:
- case WASM_INSN_I64_LOAD16_S:
- case WASM_INSN_I64_LOAD16_U:
- case WASM_INSN_I32_STORE16:
- case WASM_INSN_I64_STORE16:
- case WASM_INSN_I32_ATOMIC_LOAD16_U:
- case WASM_INSN_I64_ATOMIC_LOAD16_U:
- case WASM_INSN_I32_ATOMIC_STORE16:
- case WASM_INSN_I64_ATOMIC_STORE16:
- return 2;
- case WASM_INSN_I32_LOAD:
- case WASM_INSN_I64_LOAD32_S:
- case WASM_INSN_I64_LOAD32_U:
- case WASM_INSN_I32_STORE:
- case WASM_INSN_I64_STORE32:
- case WASM_INSN_I32_ATOMIC_LOAD:
- case WASM_INSN_I64_ATOMIC_LOAD32_U:
- case WASM_INSN_I32_ATOMIC_STORE:
- case WASM_INSN_I64_ATOMIC_STORE32:
- case WASM_INSN_I32_ATOMIC_RMW_ADD:
- case WASM_INSN_I32_ATOMIC_RMW_SUB:
- case WASM_INSN_I32_ATOMIC_RMW_AND:
- case WASM_INSN_I32_ATOMIC_RMW_OR:
- case WASM_INSN_I32_ATOMIC_RMW_XOR:
- case WASM_INSN_I32_ATOMIC_RMW_XCHG:
- case WASM_INSN_I32_ATOMIC_RMW_CMPXCHG:
- case WASM_INSN_I32_ATOMIC_WAIT:
- case WASM_INSN_MEMORY_ATOMIC_NOTIFY:
- return 4;
- default:
- return 8;
- }
-}
-
-typedef enum WasmTrapKind {
- WASM_TRAP_UNREACHABLE,
- WASM_TRAP_DIVISION,
- WASM_TRAP_INVALID_CONVERSION,
- WASM_TRAP_BOUNDS,
- WASM_TRAP_TABLE,
- WASM_TRAP_SIGNATURE,
- WASM_TRAP_COUNT,
-} WasmTrapKind;
-
-typedef struct WasmCgRuntime {
- CfreeCgTypeId i8_ptr_ty;
- CfreeCgTypeId void_ptr_ty;
- CfreeCgTypeId memory_ty;
- CfreeCgTypeId func_import_ty;
- CfreeCgTypeId global_import_ty;
- CfreeCgTypeId table_entry_ty;
- CfreeCgTypeId table_entry_ptr_ty;
- CfreeCgTypeId table_ty;
- CfreeCgTypeId instance_ty;
- CfreeCgTypeId instance_ptr_ty;
- uint32_t memory_field[64];
- uint32_t memory_data_field;
- uint32_t memory_pages_field;
- uint32_t memory_max_pages_field;
- uint32_t memory_flags_field;
- uint32_t func_import_field[64];
- uint32_t func_ref_entry_field[64];
- uint32_t global_field[64];
- uint32_t global_import_addr_field;
- uint32_t table_field[64];
- uint32_t table_entries_field[64];
- uint32_t table_entry_fn_field;
- uint32_t table_entry_typeidx_field;
- uint32_t table_entries_ptr_field;
- uint32_t table_len_field;
- uint32_t table_max_field;
- CfreeCgTypeId trap_func_ty;
- CfreeCgSym trap_syms[WASM_TRAP_COUNT];
-} WasmCgRuntime;
-
-static const char* wasm_trap_name(WasmTrapKind kind) {
- switch (kind) {
- case WASM_TRAP_UNREACHABLE:
- return "__cfree_wasm_trap_unreachable";
- case WASM_TRAP_DIVISION:
- return "__cfree_wasm_trap_division";
- case WASM_TRAP_INVALID_CONVERSION:
- return "__cfree_wasm_trap_invalid_conversion";
- case WASM_TRAP_BOUNDS:
- return "__cfree_wasm_trap_bounds";
- case WASM_TRAP_TABLE:
- return "__cfree_wasm_trap_table";
- case WASM_TRAP_SIGNATURE:
- return "__cfree_wasm_trap_signature";
- default:
- return "__cfree_wasm_trap";
- }
-}
-
-static void wasm_cg_emit_raw_trap(CfreeCg* cg) {
- cfree_cg_intrinsic(cg, CFREE_CG_INTRIN_TRAP, 0, CFREE_CG_TYPE_NONE);
- cfree_cg_unreachable(cg);
-}
-
-static void wasm_cg_trap(CfreeCg* cg, const WasmCgRuntime* rt,
- WasmTrapKind kind) {
- if (rt && kind < WASM_TRAP_COUNT && rt->trap_syms[kind])
- cfree_cg_call_symbol(cg, rt->trap_syms[kind], 0,
- (CfreeCgCallAttrs){.flags = CFREE_CG_CALL_COLD});
- else
- cfree_cg_intrinsic(cg, CFREE_CG_INTRIN_TRAP, 0, CFREE_CG_TYPE_NONE);
- cfree_cg_unreachable(cg);
-}
-
-static void wasm_cg_trap_unreachable(CfreeCg* cg, const WasmCgRuntime* rt) {
- wasm_cg_trap(cg, rt, WASM_TRAP_UNREACHABLE);
-}
-static void wasm_cg_trap_division(CfreeCg* cg, const WasmCgRuntime* rt) {
- wasm_cg_trap(cg, rt, WASM_TRAP_DIVISION);
-}
-static void wasm_cg_trap_invalid_conversion(CfreeCg* cg,
- const WasmCgRuntime* rt) {
- wasm_cg_trap(cg, rt, WASM_TRAP_INVALID_CONVERSION);
-}
-static void wasm_cg_trap_bounds(CfreeCg* cg, const WasmCgRuntime* rt) {
- wasm_cg_trap(cg, rt, WASM_TRAP_BOUNDS);
-}
-static void wasm_cg_trap_table(CfreeCg* cg, const WasmCgRuntime* rt) {
- wasm_cg_trap(cg, rt, WASM_TRAP_TABLE);
-}
-static void wasm_cg_trap_signature(CfreeCg* cg, const WasmCgRuntime* rt) {
- wasm_cg_trap(cg, rt, WASM_TRAP_SIGNATURE);
-}
-
-static void wasm_indexed_name(char* name, size_t cap, const char* prefix,
- uint32_t index) {
- size_t pos = 0;
- uint32_t n = index, div = 1000000000u;
- while (*prefix && pos + 1u < cap) name[pos++] = *prefix++;
- while (div > 1u && n / div == 0) div /= 10u;
- while (div && pos + 1u < cap) {
- name[pos++] = (char)('0' + (n / div) % 10u);
- div /= 10u;
- }
- name[pos] = '\0';
-}
-
-static void wasm_cg_build_runtime(CfreeCompiler* c, CfreeCgBuiltinTypes b,
- const WasmModule* m, WasmCgRuntime* rt) {
- CfreeCgField memory_fields[4];
- CfreeCgField func_import_fields[1];
- CfreeCgField global_import_fields[1];
- CfreeCgField table_entry_fields[2];
- CfreeCgField table_fields[3];
- CfreeCgField instance_fields[256];
- uint32_t nfields = 0;
- memset(rt, 0, sizeof *rt);
- rt->i8_ptr_ty = cfree_cg_type_ptr(c, b.id[CFREE_CG_BUILTIN_I8], 0);
- rt->void_ptr_ty = rt->i8_ptr_ty;
- memset(memory_fields, 0, sizeof memory_fields);
- memory_fields[0].name = cfree_sym_intern(c, "data");
- memory_fields[0].type = rt->i8_ptr_ty;
- memory_fields[1].name = cfree_sym_intern(c, "pages");
- memory_fields[1].type = b.id[CFREE_CG_BUILTIN_I64];
- memory_fields[2].name = cfree_sym_intern(c, "max_pages");
- memory_fields[2].type = b.id[CFREE_CG_BUILTIN_I64];
- memory_fields[3].name = cfree_sym_intern(c, "flags");
- memory_fields[3].type = b.id[CFREE_CG_BUILTIN_I32];
- rt->memory_ty = cfree_cg_type_record(
- c, cfree_sym_intern(c, "CfreeWasmMemory"), memory_fields, 4);
- rt->memory_data_field = 0;
- rt->memory_pages_field = 1;
- rt->memory_max_pages_field = 2;
- rt->memory_flags_field = 3;
- memset(func_import_fields, 0, sizeof func_import_fields);
- func_import_fields[0].name = cfree_sym_intern(c, "fn");
- func_import_fields[0].type = rt->void_ptr_ty;
- rt->func_import_ty = cfree_cg_type_record(
- c, cfree_sym_intern(c, "CfreeWasmFuncImport"), func_import_fields, 1);
- memset(global_import_fields, 0, sizeof global_import_fields);
- global_import_fields[0].name = cfree_sym_intern(c, "addr");
- global_import_fields[0].type = rt->void_ptr_ty;
- rt->global_import_ty = cfree_cg_type_record(
- c, cfree_sym_intern(c, "CfreeWasmGlobalImport"), global_import_fields, 1);
- memset(table_entry_fields, 0, sizeof table_entry_fields);
- table_entry_fields[0].name = cfree_sym_intern(c, "fn");
- table_entry_fields[0].type = rt->void_ptr_ty;
- table_entry_fields[1].name = cfree_sym_intern(c, "typeidx");
- table_entry_fields[1].type = b.id[CFREE_CG_BUILTIN_I32];
- rt->table_entry_ty = cfree_cg_type_record(
- c, cfree_sym_intern(c, "CfreeWasmTableEntry"), table_entry_fields, 2);
- rt->table_entry_ptr_ty = cfree_cg_type_ptr(c, rt->table_entry_ty, 0);
- rt->table_entry_fn_field = 0;
- rt->table_entry_typeidx_field = 1;
- memset(table_fields, 0, sizeof table_fields);
- table_fields[0].name = cfree_sym_intern(c, "entries");
- table_fields[0].type = rt->table_entry_ptr_ty;
- table_fields[1].name = cfree_sym_intern(c, "len");
- table_fields[1].type = b.id[CFREE_CG_BUILTIN_I32];
- table_fields[2].name = cfree_sym_intern(c, "max");
- table_fields[2].type = b.id[CFREE_CG_BUILTIN_I32];
- rt->table_ty = cfree_cg_type_record(c, cfree_sym_intern(c, "CfreeWasmTable"),
- table_fields, 3);
- rt->table_entries_ptr_field = 0;
- rt->table_len_field = 1;
- rt->table_max_field = 2;
- memset(instance_fields, 0, sizeof instance_fields);
- for (uint32_t i = 0; i < m->nmemories; ++i) {
- char name[40];
- if (nfields >= 256u)
- wasm_error(c, wasm_loc(0, 0), "wasm: instance layout too large");
- if (i == 0)
- memcpy(name, "memory", sizeof "memory");
- else
- wasm_indexed_name(name, sizeof name, "memory_", i);
- rt->memory_field[i] = nfields;
- instance_fields[nfields].name = cfree_sym_intern(c, name);
- instance_fields[nfields].type = rt->memory_ty;
- nfields++;
- }
- for (uint32_t i = 0; i < m->nfuncs; ++i) {
- char name[40];
- if (!m->funcs[i].is_import) continue;
- if (nfields >= 256u)
- wasm_error(c, wasm_loc(0, 0), "wasm: instance layout too large");
- wasm_indexed_name(name, sizeof name, "import_func_", i);
- rt->func_import_field[i] = nfields;
- instance_fields[nfields].name = cfree_sym_intern(c, name);
- instance_fields[nfields].type = rt->func_import_ty;
- nfields++;
- }
- for (uint32_t i = 0; i < m->nfuncs; ++i) {
- char name[40];
- if (nfields >= 256u)
- wasm_error(c, wasm_loc(0, 0), "wasm: instance layout too large");
- wasm_indexed_name(name, sizeof name, "func_ref_", i);
- rt->func_ref_entry_field[i] = nfields;
- instance_fields[nfields].name = cfree_sym_intern(c, name);
- instance_fields[nfields].type = rt->table_entry_ty;
- nfields++;
- }
- for (uint32_t i = 0; i < m->nglobals; ++i) {
- char name[32];
- if (nfields >= 256u)
- wasm_error(c, wasm_loc(0, 0), "wasm: instance layout too large");
- wasm_indexed_name(name, sizeof name,
- m->globals[i].is_import ? "import_global_" : "global_",
- i);
- rt->global_field[i] = nfields;
- instance_fields[nfields].name = cfree_sym_intern(c, name);
- instance_fields[nfields].type =
- m->globals[i].is_import ? rt->global_import_ty
- : wasm_cg_type(c, b, m->globals[i].type);
- nfields++;
- }
- rt->global_import_addr_field = 0;
- for (uint32_t i = 0; i < m->ntables; ++i) {
- char name[40];
- uint32_t max = m->tables[i].has_max ? m->tables[i].max : m->tables[i].min;
- if (nfields + 2u > 256u)
- wasm_error(c, wasm_loc(0, 0), "wasm: instance layout too large");
- wasm_indexed_name(name, sizeof name, "table_", i);
- rt->table_field[i] = nfields;
- instance_fields[nfields].name = cfree_sym_intern(c, name);
- instance_fields[nfields].type = rt->table_ty;
- nfields++;
- wasm_indexed_name(name, sizeof name, "table_entries_", i);
- rt->table_entries_field[i] = nfields;
- instance_fields[nfields].name = cfree_sym_intern(c, name);
- instance_fields[nfields].type =
- cfree_cg_type_array(c, rt->table_entry_ty, max ? max : 1u);
- nfields++;
- }
- rt->instance_ty = cfree_cg_type_record(
- c, cfree_sym_intern(c, "CfreeWasmInstance"), instance_fields, nfields);
- rt->instance_ptr_ty = cfree_cg_type_ptr(c, rt->instance_ty, 0);
-}
-
-static void wasm_cg_push_instance_lvalue(CfreeCg* cg, const WasmCgRuntime* rt,
- CfreeCgLocal instance_local) {
- cfree_cg_push_local(cg, instance_local);
- cfree_cg_load(cg, wasm_cg_mem_type(rt->instance_ptr_ty));
- cfree_cg_indirect(cg);
-}
-
-static void wasm_cg_push_memory_lvalue(CfreeCg* cg, const WasmCgRuntime* rt,
- CfreeCgLocal instance_local,
- uint32_t memidx) {
- wasm_cg_push_instance_lvalue(cg, rt, instance_local);
- cfree_cg_field(cg, rt->memory_field[memidx]);
-}
-
-static void wasm_cg_push_memory_data_ptr(CfreeCg* cg, const WasmCgRuntime* rt,
- CfreeCgLocal instance_local,
- uint32_t memidx) {
- wasm_cg_push_memory_lvalue(cg, rt, instance_local, memidx);
- cfree_cg_field(cg, rt->memory_data_field);
- cfree_cg_load(cg, wasm_cg_mem_type(rt->i8_ptr_ty));
-}
-
-static void wasm_cg_push_memory_pages_lvalue(CfreeCg* cg,
- const WasmCgRuntime* rt,
- CfreeCgLocal instance_local,
- uint32_t memidx) {
- wasm_cg_push_memory_lvalue(cg, rt, instance_local, memidx);
- cfree_cg_field(cg, rt->memory_pages_field);
-}
-
-static void wasm_cg_push_memory_max_lvalue(CfreeCg* cg, const WasmCgRuntime* rt,
- CfreeCgLocal instance_local,
- uint32_t memidx) {
- wasm_cg_push_memory_lvalue(cg, rt, instance_local, memidx);
- cfree_cg_field(cg, rt->memory_max_pages_field);
-}
-
-static void wasm_cg_push_global_lvalue(CfreeCg* cg, const WasmCgRuntime* rt,
- CfreeCgLocal instance_local,
- uint32_t global_index) {
- wasm_cg_push_instance_lvalue(cg, rt, instance_local);
- cfree_cg_field(cg, rt->global_field[global_index]);
-}
-
-static void wasm_cg_push_import_func_lvalue(CfreeCg* cg,
- const WasmCgRuntime* rt,
- CfreeCgLocal instance_local,
- uint32_t func_index) {
- wasm_cg_push_instance_lvalue(cg, rt, instance_local);
- cfree_cg_field(cg, rt->func_import_field[func_index]);
-}
-
-static void wasm_cg_push_import_func_ptr(CfreeCg* cg, const WasmCgRuntime* rt,
- CfreeCgLocal instance_local,
- uint32_t func_index) {
- wasm_cg_push_import_func_lvalue(cg, rt, instance_local, func_index);
- cfree_cg_field(cg, 0);
- cfree_cg_load(cg, wasm_cg_mem_type(rt->void_ptr_ty));
-}
-
-static void wasm_cg_push_func_ref_lvalue(CfreeCg* cg, const WasmCgRuntime* rt,
- CfreeCgLocal instance_local,
- uint32_t func_index) {
- wasm_cg_push_instance_lvalue(cg, rt, instance_local);
- cfree_cg_field(cg, rt->func_ref_entry_field[func_index]);
-}
-
-static void wasm_cg_push_global_value_lvalue(CfreeCompiler* c, CfreeCg* cg,
- CfreeCgBuiltinTypes b,
- const WasmCgRuntime* rt,
- CfreeCgLocal instance_local,
- const WasmModule* m,
- uint32_t global_index) {
- wasm_cg_push_global_lvalue(cg, rt, instance_local, global_index);
- if (m->globals[global_index].is_import) {
- CfreeCgTypeId ptr_ty = cfree_cg_type_ptr(
- c, wasm_cg_type(c, b, m->globals[global_index].type), 0);
- cfree_cg_field(cg, rt->global_import_addr_field);
- cfree_cg_load(cg, wasm_cg_mem_type(rt->void_ptr_ty));
- cfree_cg_dup(cg);
- cfree_cg_push_null(cg, rt->void_ptr_ty);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_NE);
- {
- CfreeCgLabel ok = cfree_cg_label_new(cg);
- cfree_cg_branch_true(cg, ok);
- wasm_cg_trap_table(cg, rt);
- cfree_cg_label_place(cg, ok);
- }
- cfree_cg_bitcast(cg, ptr_ty);
- cfree_cg_indirect(cg);
- }
-}
-
-static void wasm_cg_push_table_lvalue(CfreeCg* cg, const WasmCgRuntime* rt,
- CfreeCgLocal instance_local,
- uint32_t table_index) {
- wasm_cg_push_instance_lvalue(cg, rt, instance_local);
- cfree_cg_field(cg, rt->table_field[table_index]);
-}
-
-static void wasm_cg_push_table_entries_array_lvalue(CfreeCg* cg,
- const WasmCgRuntime* rt,
- CfreeCgLocal instance_local,
- uint32_t table_index) {
- wasm_cg_push_instance_lvalue(cg, rt, instance_local);
- cfree_cg_field(cg, rt->table_entries_field[table_index]);
-}
-
-static void wasm_cg_push_table_entry_lvalue(CfreeCg* cg,
- const WasmCgRuntime* rt,
- CfreeCgLocal instance_local,
- uint32_t table_index,
- CfreeCgLocal index_local,
- CfreeCgMemAccess index_mem) {
- wasm_cg_push_table_lvalue(cg, rt, instance_local, table_index);
- cfree_cg_field(cg, rt->table_entries_ptr_field);
- cfree_cg_load(cg, wasm_cg_mem_type(rt->table_entry_ptr_ty));
- cfree_cg_push_local(cg, index_local);
- cfree_cg_load(cg, index_mem);
- cfree_cg_index(cg, 0);
-}
-
-static void wasm_cg_memory_check(CfreeCompiler* c, CfreeCg* cg,
- CfreeCgBuiltinTypes b, const WasmModule* m,
- const WasmCgRuntime* rt,
- CfreeCgLocal instance_local,
- const WasmInsn* in) {
- uint32_t width = wasm_mem_width(in->kind);
- uint64_t end = in->offset64 + width;
- CfreeCgLabel ok = cfree_cg_label_new(cg);
- uint32_t max_pages =
- (uint32_t)(m->memories[in->memidx].has_max
- ? m->memories[in->memidx].max_pages
- : m->memories[in->memidx].min_pages);
- if (end > (uint64_t)max_pages * 65536u) {
- wasm_cg_trap_bounds(cg, rt);
- return;
- }
- (void)c;
- cfree_cg_dup(cg);
- wasm_cg_push_memory_pages_lvalue(cg, rt, instance_local, in->memidx);
- cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
- cfree_cg_push_int(cg, 65536u, b.id[CFREE_CG_BUILTIN_I64]);
- cfree_cg_int_binop(cg, CFREE_CG_INT_MUL, 0);
- cfree_cg_push_int(cg, end, b.id[CFREE_CG_BUILTIN_I64]);
- cfree_cg_int_binop(cg, CFREE_CG_INT_SUB, 0);
- if (!m->memories[in->memidx].is64) {
- cfree_cg_swap(cg);
- cfree_cg_zext(cg, b.id[CFREE_CG_BUILTIN_I64]);
- cfree_cg_swap(cg);
- }
- cfree_cg_int_cmp(cg, CFREE_CG_INT_LE_U);
- cfree_cg_branch_true(cg, ok);
- wasm_cg_trap_bounds(cg, rt);
- cfree_cg_label_place(cg, ok);
-}
-
-static void wasm_cg_memory_lvalue(CfreeCg* cg, const WasmCgRuntime* rt,
- CfreeCgLocal instance_local,
- uint32_t memidx, uint64_t offset) {
- wasm_cg_push_memory_data_ptr(cg, rt, instance_local, memidx);
- cfree_cg_swap(cg);
- cfree_cg_index(cg, offset);
-}
-
-static void wasm_cg_rotate(CfreeCompiler* c, CfreeCg* cg, CfreeCgBuiltinTypes b,
- WasmValType vt, int right) {
- CfreeCgTypeId ty = wasm_cg_type(c, b, vt);
- CfreeCgMemAccess mem = wasm_cg_mem(c, b, vt);
- CfreeCgLocalAttrs attrs;
- CfreeCgLocal lhs, rhs;
- uint32_t mask = vt == WASM_VAL_I32 ? 31u : 63u;
- memset(&attrs, 0, sizeof attrs);
- attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
- rhs = cfree_cg_local(cg, ty, attrs);
- lhs = cfree_cg_local(cg, ty, attrs);
- cfree_cg_push_local(cg, rhs);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, mem);
- cfree_cg_push_local(cg, lhs);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, mem);
-
- cfree_cg_push_local(cg, lhs);
- cfree_cg_load(cg, mem);
- cfree_cg_push_local(cg, rhs);
- cfree_cg_load(cg, mem);
- cfree_cg_push_int(cg, mask, ty);
- cfree_cg_int_binop(cg, CFREE_CG_INT_AND, 0);
- cfree_cg_int_binop(cg, right ? CFREE_CG_INT_LSHR : CFREE_CG_INT_SHL, 0);
-
- cfree_cg_push_local(cg, lhs);
- cfree_cg_load(cg, mem);
- cfree_cg_push_int(cg, 0, ty);
- cfree_cg_push_local(cg, rhs);
- cfree_cg_load(cg, mem);
- cfree_cg_int_binop(cg, CFREE_CG_INT_SUB, 0);
- cfree_cg_push_int(cg, mask, ty);
- cfree_cg_int_binop(cg, CFREE_CG_INT_AND, 0);
- cfree_cg_int_binop(cg, right ? CFREE_CG_INT_SHL : CFREE_CG_INT_LSHR, 0);
- cfree_cg_int_binop(cg, CFREE_CG_INT_OR, 0);
-}
-
-static void wasm_cg_checked_divrem(CfreeCompiler* c, CfreeCg* cg,
- CfreeCgBuiltinTypes b,
- const WasmCgRuntime* rt, WasmValType vt,
- CfreeCgIntBinOp op) {
- CfreeCgTypeId ty = wasm_cg_type(c, b, vt);
- CfreeCgMemAccess mem = wasm_cg_mem(c, b, vt);
- CfreeCgLocalAttrs attrs;
- CfreeCgLocal lhs, rhs;
- CfreeCgLabel ok = cfree_cg_label_new(cg);
- memset(&attrs, 0, sizeof attrs);
- attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
- rhs = cfree_cg_local(cg, ty, attrs);
- lhs = cfree_cg_local(cg, ty, attrs);
- cfree_cg_push_local(cg, rhs);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, mem);
- cfree_cg_push_local(cg, lhs);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, mem);
- cfree_cg_push_local(cg, rhs);
- cfree_cg_load(cg, mem);
- cfree_cg_push_int(cg, 0, ty);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_NE);
- cfree_cg_branch_true(cg, ok);
- wasm_cg_trap_division(cg, rt);
- cfree_cg_label_place(cg, ok);
- if (op == CFREE_CG_INT_SDIV) {
- CfreeCgLabel no_overflow = cfree_cg_label_new(cg);
- uint64_t min_val = vt == WASM_VAL_I32 ? UINT64_C(0x80000000)
- : UINT64_C(0x8000000000000000);
- cfree_cg_push_local(cg, lhs);
- cfree_cg_load(cg, mem);
- cfree_cg_push_int(cg, min_val, ty);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_NE);
- cfree_cg_branch_true(cg, no_overflow);
- cfree_cg_push_local(cg, rhs);
- cfree_cg_load(cg, mem);
- cfree_cg_push_int(cg, UINT64_MAX, ty);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_NE);
- cfree_cg_branch_true(cg, no_overflow);
- wasm_cg_trap_division(cg, rt);
- cfree_cg_label_place(cg, no_overflow);
- }
- cfree_cg_push_local(cg, lhs);
- cfree_cg_load(cg, mem);
- cfree_cg_push_local(cg, rhs);
- cfree_cg_load(cg, mem);
- cfree_cg_int_binop(cg, op, 0);
-}
-
-static void wasm_cg_checked_trunc(CfreeCompiler* c, CfreeCg* cg,
- CfreeCgBuiltinTypes b,
- const WasmCgRuntime* rt, WasmValType src,
- WasmValType dst, int is_unsigned) {
- CfreeCgTypeId src_ty = wasm_cg_type(c, b, src);
- CfreeCgTypeId dst_ty = wasm_cg_type(c, b, dst);
- CfreeCgTypeId bit_ty = b.id[src == WASM_VAL_F32 ? CFREE_CG_BUILTIN_I32
- : CFREE_CG_BUILTIN_I64];
- CfreeCgMemAccess src_mem = wasm_cg_mem(c, b, src);
- CfreeCgMemAccess bit_mem = wasm_cg_mem_type(bit_ty);
- CfreeCgLocalAttrs attrs;
- CfreeCgLocal value, bits, abs_bits;
- uint64_t abs_mask = src == WASM_VAL_F32 ? UINT64_C(0x7fffffff)
- : UINT64_C(0x7fffffffffffffff);
- uint64_t sign_mask = src == WASM_VAL_F32 ? UINT64_C(0x80000000)
- : UINT64_C(0x8000000000000000);
- uint64_t inf_bits = src == WASM_VAL_F32 ? UINT64_C(0x7f800000)
- : UINT64_C(0x7ff0000000000000);
- uint64_t limit_bits;
- memset(&attrs, 0, sizeof attrs);
- attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
- value = cfree_cg_local(cg, src_ty, attrs);
- bits = cfree_cg_local(cg, bit_ty, attrs);
- abs_bits = cfree_cg_local(cg, bit_ty, attrs);
- cfree_cg_push_local(cg, value);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, src_mem);
-
- cfree_cg_push_local(cg, bits);
- cfree_cg_push_local(cg, value);
- cfree_cg_load(cg, src_mem);
- cfree_cg_bitcast(cg, bit_ty);
- cfree_cg_store(cg, bit_mem);
-
- cfree_cg_push_local(cg, abs_bits);
- cfree_cg_push_local(cg, bits);
- cfree_cg_load(cg, bit_mem);
- cfree_cg_push_int(cg, abs_mask, bit_ty);
- cfree_cg_int_binop(cg, CFREE_CG_INT_AND, 0);
- cfree_cg_store(cg, bit_mem);
-
- {
- CfreeCgLabel finite = cfree_cg_label_new(cg);
- cfree_cg_push_local(cg, abs_bits);
- cfree_cg_load(cg, bit_mem);
- cfree_cg_push_int(cg, inf_bits, bit_ty);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_LE_U);
- cfree_cg_branch_true(cg, finite);
- wasm_cg_trap_invalid_conversion(cg, rt);
- cfree_cg_label_place(cg, finite);
- }
-
- if (src == WASM_VAL_F32) {
- if (dst == WASM_VAL_I32)
- limit_bits = is_unsigned ? UINT64_C(0x4f800000) : UINT64_C(0x4f000000);
- else
- limit_bits = is_unsigned ? UINT64_C(0x5f800000) : UINT64_C(0x5f000000);
- } else {
- if (dst == WASM_VAL_I32)
- limit_bits = is_unsigned ? UINT64_C(0x41f0000000000000)
- : UINT64_C(0x41e0000000000000);
- else
- limit_bits = is_unsigned ? UINT64_C(0x43f0000000000000)
- : UINT64_C(0x43e0000000000000);
- }
-
- if (is_unsigned) {
- CfreeCgLabel nonnegative = cfree_cg_label_new(cg);
- cfree_cg_push_local(cg, abs_bits);
- cfree_cg_load(cg, bit_mem);
- cfree_cg_push_int(cg, 0, bit_ty);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_EQ);
- cfree_cg_branch_true(cg, nonnegative);
- cfree_cg_push_local(cg, bits);
- cfree_cg_load(cg, bit_mem);
- cfree_cg_push_int(cg, sign_mask, bit_ty);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_LT_U);
- cfree_cg_branch_true(cg, nonnegative);
- wasm_cg_trap_invalid_conversion(cg, rt);
- cfree_cg_label_place(cg, nonnegative);
-
- {
- CfreeCgLabel in_range = cfree_cg_label_new(cg);
- cfree_cg_push_local(cg, abs_bits);
- cfree_cg_load(cg, bit_mem);
- cfree_cg_push_int(cg, limit_bits, bit_ty);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_LT_U);
- cfree_cg_branch_true(cg, in_range);
- wasm_cg_trap_invalid_conversion(cg, rt);
- cfree_cg_label_place(cg, in_range);
- }
- } else {
- CfreeCgLabel negative = cfree_cg_label_new(cg);
- CfreeCgLabel in_range = cfree_cg_label_new(cg);
- cfree_cg_push_local(cg, bits);
- cfree_cg_load(cg, bit_mem);
- cfree_cg_push_int(cg, sign_mask, bit_ty);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_GE_U);
- cfree_cg_branch_true(cg, negative);
- cfree_cg_push_local(cg, abs_bits);
- cfree_cg_load(cg, bit_mem);
- cfree_cg_push_int(cg, limit_bits, bit_ty);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_LT_U);
- cfree_cg_branch_true(cg, in_range);
- wasm_cg_trap_invalid_conversion(cg, rt);
- cfree_cg_label_place(cg, negative);
- cfree_cg_push_local(cg, abs_bits);
- cfree_cg_load(cg, bit_mem);
- cfree_cg_push_int(cg, limit_bits, bit_ty);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_LE_U);
- cfree_cg_branch_true(cg, in_range);
- wasm_cg_trap_invalid_conversion(cg, rt);
- cfree_cg_label_place(cg, in_range);
- }
-
- cfree_cg_push_local(cg, value);
- cfree_cg_load(cg, src_mem);
- if (is_unsigned)
- cfree_cg_float_to_uint(cg, dst_ty, CFREE_CG_ROUND_TOWARD_ZERO);
- else
- cfree_cg_float_to_sint(cg, dst_ty, CFREE_CG_ROUND_TOWARD_ZERO);
-}
-
-static int wasm_int_cmp_op(uint8_t kind, CfreeCgIntCmpOp* out) {
- switch (kind) {
- case WASM_INSN_I32_EQ:
- case WASM_INSN_I64_EQ:
- *out = CFREE_CG_INT_EQ;
- return 1;
- case WASM_INSN_I32_NE:
- case WASM_INSN_I64_NE:
- *out = CFREE_CG_INT_NE;
- return 1;
- case WASM_INSN_I32_LT_S:
- case WASM_INSN_I64_LT_S:
- *out = CFREE_CG_INT_LT_S;
- return 1;
- case WASM_INSN_I32_LT_U:
- case WASM_INSN_I64_LT_U:
- *out = CFREE_CG_INT_LT_U;
- return 1;
- case WASM_INSN_I32_GT_S:
- case WASM_INSN_I64_GT_S:
- *out = CFREE_CG_INT_GT_S;
- return 1;
- case WASM_INSN_I32_GT_U:
- case WASM_INSN_I64_GT_U:
- *out = CFREE_CG_INT_GT_U;
- return 1;
- case WASM_INSN_I32_LE_S:
- case WASM_INSN_I64_LE_S:
- *out = CFREE_CG_INT_LE_S;
- return 1;
- case WASM_INSN_I32_LE_U:
- case WASM_INSN_I64_LE_U:
- *out = CFREE_CG_INT_LE_U;
- return 1;
- case WASM_INSN_I32_GE_S:
- case WASM_INSN_I64_GE_S:
- *out = CFREE_CG_INT_GE_S;
- return 1;
- case WASM_INSN_I32_GE_U:
- case WASM_INSN_I64_GE_U:
- *out = CFREE_CG_INT_GE_U;
- return 1;
- default:
- return 0;
- }
-}
-
-static int wasm_fp_binop(uint8_t kind, CfreeCgFpBinOp* out) {
- switch (kind) {
- case WASM_INSN_F32_ADD:
- case WASM_INSN_F64_ADD:
- *out = CFREE_CG_FP_ADD;
- return 1;
- case WASM_INSN_F32_SUB:
- case WASM_INSN_F64_SUB:
- *out = CFREE_CG_FP_SUB;
- return 1;
- case WASM_INSN_F32_MUL:
- case WASM_INSN_F64_MUL:
- *out = CFREE_CG_FP_MUL;
- return 1;
- case WASM_INSN_F32_DIV:
- case WASM_INSN_F64_DIV:
- *out = CFREE_CG_FP_DIV;
- return 1;
- default:
- return 0;
- }
-}
-
-static int wasm_fp_cmp_op(uint8_t kind, CfreeCgFpCmpOp* out) {
- switch (kind) {
- case WASM_INSN_F32_EQ:
- case WASM_INSN_F64_EQ:
- *out = CFREE_CG_FP_OEQ;
- return 1;
- case WASM_INSN_F32_NE:
- case WASM_INSN_F64_NE:
- *out = CFREE_CG_FP_ONE;
- return 1;
- case WASM_INSN_F32_LT:
- case WASM_INSN_F64_LT:
- *out = CFREE_CG_FP_OLT;
- return 1;
- case WASM_INSN_F32_GT:
- case WASM_INSN_F64_GT:
- *out = CFREE_CG_FP_OGT;
- return 1;
- case WASM_INSN_F32_LE:
- case WASM_INSN_F64_LE:
- *out = CFREE_CG_FP_OLE;
- return 1;
- case WASM_INSN_F32_GE:
- case WASM_INSN_F64_GE:
- *out = CFREE_CG_FP_OGE;
- return 1;
- default:
- return 0;
- }
-}
-
-static WasmValType wasm_load_result_type(uint8_t kind) {
- switch (kind) {
- case WASM_INSN_I64_LOAD:
- case WASM_INSN_I64_LOAD8_S:
- case WASM_INSN_I64_LOAD8_U:
- case WASM_INSN_I64_LOAD16_S:
- case WASM_INSN_I64_LOAD16_U:
- case WASM_INSN_I64_LOAD32_S:
- case WASM_INSN_I64_LOAD32_U:
- case WASM_INSN_I64_ATOMIC_LOAD:
- case WASM_INSN_I64_ATOMIC_LOAD8_U:
- case WASM_INSN_I64_ATOMIC_LOAD16_U:
- case WASM_INSN_I64_ATOMIC_LOAD32_U:
- return WASM_VAL_I64;
- default:
- return WASM_VAL_I32;
- }
-}
-
-static WasmValType wasm_store_value_type(uint8_t kind) {
- switch (kind) {
- case WASM_INSN_I64_STORE:
- case WASM_INSN_I64_STORE8:
- case WASM_INSN_I64_STORE16:
- case WASM_INSN_I64_STORE32:
- case WASM_INSN_I64_ATOMIC_STORE:
- case WASM_INSN_I64_ATOMIC_STORE8:
- case WASM_INSN_I64_ATOMIC_STORE16:
- case WASM_INSN_I64_ATOMIC_STORE32:
- return WASM_VAL_I64;
- default:
- return WASM_VAL_I32;
- }
-}
-
-static WasmValType wasm_atomic_value_type(uint8_t kind) {
- switch (kind) {
- case WASM_INSN_I64_ATOMIC_LOAD:
- case WASM_INSN_I64_ATOMIC_LOAD8_U:
- case WASM_INSN_I64_ATOMIC_LOAD16_U:
- case WASM_INSN_I64_ATOMIC_LOAD32_U:
- case WASM_INSN_I64_ATOMIC_STORE:
- case WASM_INSN_I64_ATOMIC_STORE8:
- case WASM_INSN_I64_ATOMIC_STORE16:
- case WASM_INSN_I64_ATOMIC_STORE32:
- case WASM_INSN_I64_ATOMIC_RMW_ADD:
- case WASM_INSN_I64_ATOMIC_RMW_SUB:
- case WASM_INSN_I64_ATOMIC_RMW_AND:
- case WASM_INSN_I64_ATOMIC_RMW_OR:
- case WASM_INSN_I64_ATOMIC_RMW_XOR:
- case WASM_INSN_I64_ATOMIC_RMW_XCHG:
- case WASM_INSN_I64_ATOMIC_RMW_CMPXCHG:
- case WASM_INSN_I64_ATOMIC_WAIT:
- return WASM_VAL_I64;
- default:
- return WASM_VAL_I32;
- }
-}
-
-static CfreeCgAtomicOp wasm_atomic_rmw_op(uint8_t kind) {
- switch (kind) {
- case WASM_INSN_I32_ATOMIC_RMW_ADD:
- case WASM_INSN_I64_ATOMIC_RMW_ADD:
- return CFREE_CG_ATOMIC_ADD;
- case WASM_INSN_I32_ATOMIC_RMW_SUB:
- case WASM_INSN_I64_ATOMIC_RMW_SUB:
- return CFREE_CG_ATOMIC_SUB;
- case WASM_INSN_I32_ATOMIC_RMW_AND:
- case WASM_INSN_I64_ATOMIC_RMW_AND:
- return CFREE_CG_ATOMIC_AND;
- case WASM_INSN_I32_ATOMIC_RMW_OR:
- case WASM_INSN_I64_ATOMIC_RMW_OR:
- return CFREE_CG_ATOMIC_OR;
- case WASM_INSN_I32_ATOMIC_RMW_XOR:
- case WASM_INSN_I64_ATOMIC_RMW_XOR:
- return CFREE_CG_ATOMIC_XOR;
- default:
- return CFREE_CG_ATOMIC_XCHG;
- }
-}
-
-static int wasm_int_unop_kind(uint8_t kind, WasmValType* vt) {
- if (kind == WASM_INSN_I32_CLZ || kind == WASM_INSN_I32_CTZ ||
- kind == WASM_INSN_I32_POPCNT) {
- *vt = WASM_VAL_I32;
- return 1;
- }
- if (kind == WASM_INSN_I64_CLZ || kind == WASM_INSN_I64_CTZ ||
- kind == WASM_INSN_I64_POPCNT) {
- *vt = WASM_VAL_I64;
- return 1;
- }
- return 0;
-}
-
-static int wasm_fp_binop_kind(uint8_t kind, WasmValType* vt) {
- if (kind == WASM_INSN_F32_ADD || kind == WASM_INSN_F32_SUB ||
- kind == WASM_INSN_F32_MUL || kind == WASM_INSN_F32_DIV) {
- *vt = WASM_VAL_F32;
- return 1;
- }
- if (kind == WASM_INSN_F64_ADD || kind == WASM_INSN_F64_SUB ||
- kind == WASM_INSN_F64_MUL || kind == WASM_INSN_F64_DIV) {
- *vt = WASM_VAL_F64;
- return 1;
- }
- return 0;
-}
-
-static int wasm_fp_cmp_kind(uint8_t kind, WasmValType* vt) {
- if (kind == WASM_INSN_F32_EQ || kind == WASM_INSN_F32_NE ||
- kind == WASM_INSN_F32_LT || kind == WASM_INSN_F32_GT ||
- kind == WASM_INSN_F32_LE || kind == WASM_INSN_F32_GE) {
- *vt = WASM_VAL_F32;
- return 1;
- }
- if (kind == WASM_INSN_F64_EQ || kind == WASM_INSN_F64_NE ||
- kind == WASM_INSN_F64_LT || kind == WASM_INSN_F64_GT ||
- kind == WASM_INSN_F64_LE || kind == WASM_INSN_F64_GE) {
- *vt = WASM_VAL_F64;
- return 1;
- }
- return 0;
-}
-
-static int wasm_conversion_kind(uint8_t kind, WasmValType* src,
- WasmValType* dst) {
- switch (kind) {
- case WASM_INSN_I32_WRAP_I64:
- *src = WASM_VAL_I64;
- *dst = WASM_VAL_I32;
- return 1;
- case WASM_INSN_I64_EXTEND_I32_S:
- case WASM_INSN_I64_EXTEND_I32_U:
- *src = WASM_VAL_I32;
- *dst = WASM_VAL_I64;
- return 1;
- case WASM_INSN_I32_TRUNC_F32_S:
- case WASM_INSN_I32_TRUNC_F32_U:
- *src = WASM_VAL_F32;
- *dst = WASM_VAL_I32;
- return 1;
- case WASM_INSN_I32_TRUNC_F64_S:
- case WASM_INSN_I32_TRUNC_F64_U:
- *src = WASM_VAL_F64;
- *dst = WASM_VAL_I32;
- return 1;
- case WASM_INSN_I64_TRUNC_F32_S:
- case WASM_INSN_I64_TRUNC_F32_U:
- *src = WASM_VAL_F32;
- *dst = WASM_VAL_I64;
- return 1;
- case WASM_INSN_I64_TRUNC_F64_S:
- case WASM_INSN_I64_TRUNC_F64_U:
- *src = WASM_VAL_F64;
- *dst = WASM_VAL_I64;
- return 1;
- case WASM_INSN_F32_CONVERT_I32_S:
- case WASM_INSN_F32_CONVERT_I32_U:
- *src = WASM_VAL_I32;
- *dst = WASM_VAL_F32;
- return 1;
- case WASM_INSN_F32_CONVERT_I64_S:
- case WASM_INSN_F32_CONVERT_I64_U:
- *src = WASM_VAL_I64;
- *dst = WASM_VAL_F32;
- return 1;
- case WASM_INSN_F64_CONVERT_I32_S:
- case WASM_INSN_F64_CONVERT_I32_U:
- *src = WASM_VAL_I32;
- *dst = WASM_VAL_F64;
- return 1;
- case WASM_INSN_F64_CONVERT_I64_S:
- case WASM_INSN_F64_CONVERT_I64_U:
- *src = WASM_VAL_I64;
- *dst = WASM_VAL_F64;
- return 1;
- case WASM_INSN_F32_DEMOTE_F64:
- *src = WASM_VAL_F64;
- *dst = WASM_VAL_F32;
- return 1;
- case WASM_INSN_F64_PROMOTE_F32:
- *src = WASM_VAL_F32;
- *dst = WASM_VAL_F64;
- return 1;
- case WASM_INSN_I32_REINTERPRET_F32:
- *src = WASM_VAL_F32;
- *dst = WASM_VAL_I32;
- return 1;
- case WASM_INSN_I64_REINTERPRET_F64:
- *src = WASM_VAL_F64;
- *dst = WASM_VAL_I64;
- return 1;
- case WASM_INSN_F32_REINTERPRET_I32:
- *src = WASM_VAL_I32;
- *dst = WASM_VAL_F32;
- return 1;
- case WASM_INSN_F64_REINTERPRET_I64:
- *src = WASM_VAL_I64;
- *dst = WASM_VAL_F64;
- return 1;
- default:
- return 0;
- }
-}
-
-typedef struct WasmValStack {
- WasmValType vals[256];
- uint32_t depth;
-} WasmValStack;
-
-typedef struct WasmControlFrame {
- uint8_t kind;
- uint32_t height;
- int seen_else;
- int unreachable;
-} WasmControlFrame;
-
-static WasmValType wasm_global_init_type(const WasmInsn* in) {
- switch (in->kind) {
- case WASM_INSN_I32_CONST:
- return WASM_VAL_I32;
- case WASM_INSN_I64_CONST:
- return WASM_VAL_I64;
- case WASM_INSN_F32_CONST:
- return WASM_VAL_F32;
- case WASM_INSN_F64_CONST:
- return WASM_VAL_F64;
- default:
- return 0;
- }
-}
-
-static void wasm_stack_push(CfreeCompiler* c, WasmValStack* s, WasmValType vt) {
- if (s->depth >= 256u)
- wasm_error(c, wasm_loc(0, 0), "wasm: operand stack too deep");
- s->vals[s->depth++] = vt;
-}
-
-static int wasm_stack_pop(CfreeCompiler* c, WasmValStack* s,
- WasmControlFrame* frames, uint32_t nframes,
- WasmValType expected, const char* what) {
- WasmControlFrame* top = &frames[nframes - 1u];
- if (s->depth <= top->height) {
- if (top->unreachable) return 1;
- wasm_error(c, wasm_loc(0, 0), "wasm: operand stack underflow");
- }
- if (expected && s->vals[s->depth - 1u] != expected)
- wasm_error(c, wasm_loc(0, 0), "wasm: %s type mismatch", what);
- s->depth--;
- return 1;
-}
-
-static WasmValType wasm_stack_pop_any(CfreeCompiler* c, WasmValStack* s,
- WasmControlFrame* frames,
- uint32_t nframes, const char* what) {
- WasmControlFrame* top = &frames[nframes - 1u];
- WasmValType vt;
- if (s->depth <= top->height) {
- if (top->unreachable) return WASM_VAL_I32;
- wasm_error(c, wasm_loc(0, 0), "wasm: operand stack underflow");
- }
- vt = s->vals[s->depth - 1u];
- if (!vt) wasm_error(c, wasm_loc(0, 0), "wasm: %s type mismatch", what);
- s->depth--;
- return vt;
-}
-
-static void wasm_stack_pop_ref(CfreeCompiler* c, WasmValStack* s,
- WasmControlFrame* frames, uint32_t nframes,
- const char* what) {
- WasmValType vt = wasm_stack_pop_any(c, s, frames, nframes, what);
- if (!wasm_is_ref_type(vt))
- wasm_error(c, wasm_loc(0, 0), "wasm: %s type mismatch", what);
-}
-
-static void wasm_mark_unreachable(WasmValStack* s, WasmControlFrame* frames,
- uint32_t nframes) {
- WasmControlFrame* top = &frames[nframes - 1u];
- s->depth = top->height;
- top->unreachable = 1;
-}
-
-static void wasm_validate(WasmModule* m, CfreeCompiler* c) {
- uint32_t i, j;
- for (i = 0; i < m->ntypes; ++i) {
- for (j = 0; j < m->types[i].nparams; ++j)
- if (!wasm_is_frontend_value_type(m->types[i].params[j]))
- wasm_error(c, wasm_loc(0, 0), "wasm: unsupported parameter type");
- for (j = 0; j < m->types[i].nresults; ++j)
- if (!wasm_is_frontend_value_type(m->types[i].results[j]))
- wasm_error(c, wasm_loc(0, 0), "wasm: unsupported result type");
- }
- for (i = 0; i < m->nmemories; ++i) {
- if (m->memories[i].has_max &&
- m->memories[i].max_pages < m->memories[i].min_pages)
- wasm_error(c, wasm_loc(0, 0), "wasm: memory maximum below minimum");
- if (m->memories[i].shared && !m->memories[i].has_max)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: shared memory requires maximum");
- }
- for (i = 0; i < m->ntables; ++i) {
- if (m->tables[i].elem_type != WASM_VAL_FUNCREF)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: reference type is unsupported for tables");
- if (m->tables[i].has_max && m->tables[i].max < m->tables[i].min)
- wasm_error(c, wasm_loc(0, 0), "wasm: table maximum below minimum");
- }
- for (i = 0; i < m->nglobals; ++i) {
- WasmGlobal* g = &m->globals[i];
- if (!wasm_is_num_type(g->type))
- wasm_error(c, wasm_loc(0, 0), "wasm: unsupported global type");
- if (!g->is_import && wasm_global_init_type(&g->init) != g->type)
- wasm_error(c, wasm_loc(0, 0), "wasm: global initializer type mismatch");
- }
- for (i = 0; i < m->nexports; ++i) {
- WasmExport* ex = &m->exports[i];
- if ((ex->kind == 0 && ex->index >= m->nfuncs) ||
- (ex->kind == 1 && ex->index >= m->ntables) ||
- (ex->kind == 2 && ex->index >= m->nmemories) ||
- (ex->kind == 3 && ex->index >= m->nglobals))
- wasm_error(c, wasm_loc(0, 0), "wasm: export index out of range");
- }
- if (m->has_start) {
- if (m->start_func >= m->nfuncs)
- wasm_error(c, wasm_loc(0, 0), "wasm: start function index out of range");
- if (m->funcs[m->start_func].nparams || m->funcs[m->start_func].nresults)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: start function must have no params or results");
- }
- for (i = 0; i < m->nelems; ++i) {
- uint32_t table_min;
- if (m->elems[i].tableidx >= m->ntables)
- wasm_error(c, wasm_loc(0, 0), "wasm: element table index out of range");
- table_min = m->tables[m->elems[i].tableidx].min;
- if (m->elems[i].offset < 0 ||
- (uint64_t)m->elems[i].offset + m->elems[i].nfuncs > table_min)
- wasm_error(c, wasm_loc(0, 0), "wasm: element segment out of range");
- for (j = 0; j < m->elems[i].nfuncs; ++j)
- if (m->elems[i].funcs[j] >= m->nfuncs)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: element function index out of range");
- }
- for (i = 0; i < m->nfuncs; ++i) {
- WasmFunc* f = &m->funcs[i];
- WasmValStack stack;
- WasmControlFrame control[65];
- uint32_t ncontrol = 1;
- memset(&stack, 0, sizeof stack);
- memset(control, 0, sizeof control);
- control[0].kind = 0xffu;
- control[0].height = 0;
- if (f->is_import) {
- if (f->ninsns)
- wasm_error(c, wasm_loc(0, 0), "wasm: imported function has body");
- continue;
- }
- if (f->nresults > 1u)
- wasm_error(c, wasm_loc(0, 0), "wasm: multi-result unsupported");
- for (j = 0; j < f->ninsns; ++j) {
- WasmInsn* in = &f->insns[j];
- WasmValType vt, src, dst;
- switch (in->kind) {
- case WASM_INSN_F32_CONST:
- wasm_stack_push(c, &stack, WASM_VAL_F32);
- break;
- case WASM_INSN_F64_CONST:
- wasm_stack_push(c, &stack, WASM_VAL_F64);
- break;
- case WASM_INSN_I32_CONST:
- wasm_stack_push(c, &stack, WASM_VAL_I32);
- break;
- case WASM_INSN_I64_CONST:
- wasm_stack_push(c, &stack, WASM_VAL_I64);
- break;
- case WASM_INSN_LOCAL_GET:
- if (in->imm < 0 ||
- (uint64_t)in->imm >= (uint64_t)f->nparams + f->nlocals)
- wasm_error(c, wasm_loc(0, 0), "wasm: local index out of range");
- wasm_stack_push(c, &stack,
- wasm_func_local_type(f, (uint32_t)in->imm));
- break;
- case WASM_INSN_LOCAL_SET:
- case WASM_INSN_LOCAL_TEE:
- if (in->imm < 0 ||
- (uint64_t)in->imm >= (uint64_t)f->nparams + f->nlocals)
- wasm_error(c, wasm_loc(0, 0), "wasm: local index out of range");
- wasm_stack_pop(c, &stack, control, ncontrol,
- wasm_func_local_type(f, (uint32_t)in->imm), "local");
- if (in->kind == WASM_INSN_LOCAL_TEE)
- wasm_stack_push(c, &stack,
- wasm_func_local_type(f, (uint32_t)in->imm));
- break;
- case WASM_INSN_CALL:
- case WASM_INSN_RETURN_CALL:
- if (in->imm < 0 || (uint64_t)in->imm >= m->nfuncs)
- wasm_error(c, wasm_loc(0, 0), "wasm: call index out of range");
- if (in->kind == WASM_INSN_RETURN_CALL) {
- wasm_require_feature(c, m, WASM_FEATURE_TAIL_CALLS, "tail calls",
- "return_call");
- if (m->funcs[in->imm].nresults != f->nresults ||
- (f->nresults &&
- m->funcs[in->imm].results[0] != f->results[0]))
- wasm_error(c, wasm_loc(0, 0),
- "wasm: return_call result type mismatch");
- }
- for (uint32_t k = 0; k < m->funcs[in->imm].nparams; ++k) {
- uint32_t param = m->funcs[in->imm].nparams - 1u - k;
- wasm_stack_pop(c, &stack, control, ncontrol,
- m->funcs[in->imm].params[param], "call argument");
- }
- if (in->kind == WASM_INSN_RETURN_CALL) {
- wasm_mark_unreachable(&stack, control, ncontrol);
- } else if (m->funcs[in->imm].nresults) {
- wasm_stack_push(c, &stack, m->funcs[in->imm].results[0]);
- }
- break;
- case WASM_INSN_CALL_INDIRECT: {
- WasmFuncType* t;
- if (in->imm < 0 || (uint64_t)in->imm >= m->ntypes)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: call_indirect type index out of range");
- if (in->align >= m->ntables)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: call_indirect table index out of range");
- t = &m->types[in->imm];
- wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32,
- "call_indirect index");
- for (uint32_t k = 0; k < t->nparams; ++k) {
- uint32_t param = t->nparams - 1u - k;
- wasm_stack_pop(c, &stack, control, ncontrol, t->params[param],
- "call_indirect argument");
- }
- if (t->nresults) wasm_stack_push(c, &stack, t->results[0]);
- break;
- }
- case WASM_INSN_RETURN_CALL_INDIRECT: {
- WasmFuncType* t;
- wasm_require_feature(c, m, WASM_FEATURE_TAIL_CALLS, "tail calls",
- "return_call_indirect");
- if (in->imm < 0 || (uint64_t)in->imm >= m->ntypes)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: return_call_indirect type index out of range");
- if (in->align >= m->ntables)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: return_call_indirect table index out of range");
- t = &m->types[in->imm];
- if (t->nresults != f->nresults ||
- (f->nresults && t->results[0] != f->results[0]))
- wasm_error(c, wasm_loc(0, 0),
- "wasm: return_call_indirect result type mismatch");
- wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32,
- "return_call_indirect index");
- for (uint32_t k = 0; k < t->nparams; ++k) {
- uint32_t param = t->nparams - 1u - k;
- wasm_stack_pop(c, &stack, control, ncontrol, t->params[param],
- "return_call_indirect argument");
- }
- wasm_mark_unreachable(&stack, control, ncontrol);
- break;
- }
- case WASM_INSN_REF_NULL:
- wasm_require_feature(c, m, WASM_FEATURE_TYPED_FUNC_REFS,
- "typed function references", "ref.null");
- if (!wasm_is_ref_type((WasmValType)in->imm))
- wasm_error(c, wasm_loc(0, 0), "wasm: bad ref.null type");
- if ((WasmValType)in->imm != WASM_VAL_FUNCREF)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: unsupported reference type");
- wasm_stack_push(c, &stack, (WasmValType)in->imm);
- break;
- case WASM_INSN_REF_FUNC:
- wasm_require_feature(c, m, WASM_FEATURE_TYPED_FUNC_REFS,
- "typed function references", "ref.func");
- if (in->imm < 0 || (uint64_t)in->imm >= m->nfuncs)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: ref.func index out of range");
- wasm_stack_push(c, &stack, WASM_VAL_FUNCREF);
- break;
- case WASM_INSN_REF_IS_NULL:
- wasm_require_feature(c, m, WASM_FEATURE_TYPED_FUNC_REFS,
- "typed function references", "ref.is_null");
- wasm_stack_pop_ref(c, &stack, control, ncontrol, "ref.is_null");
- wasm_stack_push(c, &stack, WASM_VAL_I32);
- break;
- case WASM_INSN_CALL_REF:
- case WASM_INSN_RETURN_CALL_REF: {
- WasmFuncType* t;
- wasm_require_feature(c, m, WASM_FEATURE_TYPED_FUNC_REFS,
- "typed function references", "call_ref");
- if (in->kind == WASM_INSN_RETURN_CALL_REF)
- wasm_require_feature(c, m, WASM_FEATURE_TAIL_CALLS, "tail calls",
- "return_call_ref");
- if (in->imm < 0 || (uint64_t)in->imm >= m->ntypes)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: call_ref type index out of range");
- t = &m->types[in->imm];
- if (in->kind == WASM_INSN_RETURN_CALL_REF &&
- (t->nresults != f->nresults ||
- (f->nresults && t->results[0] != f->results[0])))
- wasm_error(c, wasm_loc(0, 0),
- "wasm: return_call_ref result type mismatch");
- wasm_stack_pop_ref(c, &stack, control, ncontrol, "call_ref callee");
- for (uint32_t k = 0; k < t->nparams; ++k) {
- uint32_t param = t->nparams - 1u - k;
- wasm_stack_pop(c, &stack, control, ncontrol, t->params[param],
- "call_ref argument");
- }
- if (in->kind == WASM_INSN_RETURN_CALL_REF)
- wasm_mark_unreachable(&stack, control, ncontrol);
- else if (t->nresults)
- wasm_stack_push(c, &stack, t->results[0]);
- break;
- }
- case WASM_INSN_GLOBAL_GET:
- if (in->imm < 0 || (uint64_t)in->imm >= m->nglobals)
- wasm_error(c, wasm_loc(0, 0), "wasm: global index out of range");
- wasm_stack_push(c, &stack, m->globals[in->imm].type);
- break;
- case WASM_INSN_GLOBAL_SET:
- if (in->imm < 0 || (uint64_t)in->imm >= m->nglobals)
- wasm_error(c, wasm_loc(0, 0), "wasm: global index out of range");
- if (!m->globals[in->imm].mutable_)
- wasm_error(c, wasm_loc(0, 0), "wasm: global is immutable");
- wasm_stack_pop(c, &stack, control, ncontrol, m->globals[in->imm].type,
- "global");
- break;
- case WASM_INSN_RETURN:
- if (f->nresults)
- wasm_stack_pop(c, &stack, control, ncontrol, f->results[0],
- "return");
- wasm_mark_unreachable(&stack, control, ncontrol);
- break;
- case WASM_INSN_DROP:
- wasm_stack_pop(c, &stack, control, ncontrol, 0, "drop");
- break;
- case WASM_INSN_I32_EQZ:
- wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "eqz");
- wasm_stack_push(c, &stack, WASM_VAL_I32);
- break;
- case WASM_INSN_I64_EQZ:
- wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I64, "eqz");
- wasm_stack_push(c, &stack, WASM_VAL_I32);
- break;
- case WASM_INSN_BLOCK:
- case WASM_INSN_LOOP:
- if (ncontrol >= 65u)
- wasm_error(c, wasm_loc(0, 0), "wasm: control stack too deep");
- control[ncontrol].kind = in->kind;
- control[ncontrol].height = stack.depth;
- control[ncontrol].seen_else = 0;
- control[ncontrol].unreachable = 0;
- ncontrol++;
- break;
- case WASM_INSN_IF:
- wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "if");
- if (ncontrol >= 65u)
- wasm_error(c, wasm_loc(0, 0), "wasm: control stack too deep");
- control[ncontrol].kind = in->kind;
- control[ncontrol].height = stack.depth;
- control[ncontrol].seen_else = 0;
- control[ncontrol].unreachable = 0;
- ncontrol++;
- break;
- case WASM_INSN_ELSE:
- if (ncontrol <= 1u || control[ncontrol - 1u].kind != WASM_INSN_IF)
- wasm_error(c, wasm_loc(0, 0), "wasm: else without if");
- if (!control[ncontrol - 1u].unreachable &&
- stack.depth != control[ncontrol - 1u].height)
- wasm_error(c, wasm_loc(0, 0), "wasm: if branch result mismatch");
- stack.depth = control[ncontrol - 1u].height;
- control[ncontrol - 1u].seen_else = 1;
- control[ncontrol - 1u].unreachable = 0;
- break;
- case WASM_INSN_END:
- if (ncontrol <= 1u)
- wasm_error(c, wasm_loc(0, 0), "wasm: end without block");
- if (!control[ncontrol - 1u].unreachable &&
- stack.depth != control[ncontrol - 1u].height)
- wasm_error(c, wasm_loc(0, 0), "wasm: block result mismatch");
- stack.depth = control[ncontrol - 1u].height;
- ncontrol--;
- break;
- case WASM_INSN_BR:
- if (in->imm < 0 || (uint64_t)in->imm >= ncontrol - 1u)
- wasm_error(c, wasm_loc(0, 0), "wasm: branch depth out of range");
- wasm_mark_unreachable(&stack, control, ncontrol);
- break;
- case WASM_INSN_BR_IF:
- if (in->imm < 0 || (uint64_t)in->imm >= ncontrol - 1u)
- wasm_error(c, wasm_loc(0, 0), "wasm: branch depth out of range");
- wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "br_if");
- break;
- case WASM_INSN_BR_TABLE:
- if (in->ntargets == 0)
- wasm_error(c, wasm_loc(0, 0), "wasm: br_table without targets");
- wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32,
- "br_table selector");
- for (uint32_t k = 0; k < in->ntargets; ++k)
- if (in->targets[k] >= ncontrol - 1u)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: br_table depth out of range");
- wasm_mark_unreachable(&stack, control, ncontrol);
- break;
- case WASM_INSN_SELECT: {
- WasmValType rhs, lhs;
- wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "select");
- if (stack.depth <= control[ncontrol - 1u].height &&
- control[ncontrol - 1u].unreachable) {
- in->type = WASM_VAL_I32;
- break;
- }
- if (stack.depth < control[ncontrol - 1u].height + 2u)
- wasm_error(c, wasm_loc(0, 0), "wasm: operand stack underflow");
- rhs = stack.vals[--stack.depth];
- lhs = stack.vals[--stack.depth];
- if (lhs != rhs)
- wasm_error(c, wasm_loc(0, 0), "wasm: select type mismatch");
- in->type = (uint8_t)lhs;
- wasm_stack_push(c, &stack, lhs);
- break;
- }
- case WASM_INSN_MEMORY_SIZE:
- if (in->memidx >= m->nmemories)
- wasm_error(c, wasm_loc(0, 0), "wasm: memory.size without memory");
- wasm_stack_push(c, &stack,
- m->memories[in->memidx].is64 ? WASM_VAL_I64
- : WASM_VAL_I32);
- break;
- case WASM_INSN_MEMORY_GROW:
- if (in->memidx >= m->nmemories)
- wasm_error(c, wasm_loc(0, 0), "wasm: memory.grow without memory");
- wasm_stack_pop(c, &stack, control, ncontrol,
- m->memories[in->memidx].is64 ? WASM_VAL_I64
- : WASM_VAL_I32,
- "memory.grow");
- wasm_stack_push(c, &stack,
- m->memories[in->memidx].is64 ? WASM_VAL_I64
- : WASM_VAL_I32);
- break;
- case WASM_INSN_ATOMIC_FENCE:
- wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads",
- "atomic.fence");
- break;
- case WASM_INSN_I32_ATOMIC_LOAD:
- case WASM_INSN_I64_ATOMIC_LOAD:
- case WASM_INSN_I32_ATOMIC_LOAD8_U:
- case WASM_INSN_I32_ATOMIC_LOAD16_U:
- case WASM_INSN_I64_ATOMIC_LOAD8_U:
- case WASM_INSN_I64_ATOMIC_LOAD16_U:
- case WASM_INSN_I64_ATOMIC_LOAD32_U:
- wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads",
- "atomic load");
- if (in->memidx >= m->nmemories)
- wasm_error(c, wasm_loc(0, 0), "wasm: atomic load without memory");
- if (!m->memories[in->memidx].shared)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: atomic load requires shared memory");
- if (in->align && in->align > wasm_mem_width(in->kind))
- wasm_error(c, wasm_loc(0, 0), "wasm: bad atomic alignment");
- wasm_stack_pop(c, &stack, control, ncontrol,
- m->memories[in->memidx].is64 ? WASM_VAL_I64
- : WASM_VAL_I32,
- "atomic load");
- wasm_stack_push(c, &stack, wasm_atomic_value_type(in->kind));
- break;
- case WASM_INSN_I32_ATOMIC_STORE:
- case WASM_INSN_I64_ATOMIC_STORE:
- case WASM_INSN_I32_ATOMIC_STORE8:
- case WASM_INSN_I32_ATOMIC_STORE16:
- case WASM_INSN_I64_ATOMIC_STORE8:
- case WASM_INSN_I64_ATOMIC_STORE16:
- case WASM_INSN_I64_ATOMIC_STORE32:
- wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads",
- "atomic store");
- if (in->memidx >= m->nmemories)
- wasm_error(c, wasm_loc(0, 0), "wasm: atomic store without memory");
- if (!m->memories[in->memidx].shared)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: atomic store requires shared memory");
- if (in->align && in->align > wasm_mem_width(in->kind))
- wasm_error(c, wasm_loc(0, 0), "wasm: bad atomic alignment");
- wasm_stack_pop(c, &stack, control, ncontrol,
- wasm_atomic_value_type(in->kind), "atomic store");
- wasm_stack_pop(c, &stack, control, ncontrol,
- m->memories[in->memidx].is64 ? WASM_VAL_I64
- : WASM_VAL_I32,
- "atomic store");
- break;
- case WASM_INSN_I32_ATOMIC_RMW_ADD:
- case WASM_INSN_I64_ATOMIC_RMW_ADD:
- case WASM_INSN_I32_ATOMIC_RMW_SUB:
- case WASM_INSN_I64_ATOMIC_RMW_SUB:
- case WASM_INSN_I32_ATOMIC_RMW_AND:
- case WASM_INSN_I64_ATOMIC_RMW_AND:
- case WASM_INSN_I32_ATOMIC_RMW_OR:
- case WASM_INSN_I64_ATOMIC_RMW_OR:
- case WASM_INSN_I32_ATOMIC_RMW_XOR:
- case WASM_INSN_I64_ATOMIC_RMW_XOR:
- case WASM_INSN_I32_ATOMIC_RMW_XCHG:
- case WASM_INSN_I64_ATOMIC_RMW_XCHG:
- wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads",
- "atomic rmw");
- if (in->memidx >= m->nmemories)
- wasm_error(c, wasm_loc(0, 0), "wasm: atomic rmw without memory");
- if (!m->memories[in->memidx].shared)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: atomic rmw requires shared memory");
- wasm_stack_pop(c, &stack, control, ncontrol,
- wasm_atomic_value_type(in->kind), "atomic rmw");
- wasm_stack_pop(c, &stack, control, ncontrol,
- m->memories[in->memidx].is64 ? WASM_VAL_I64
- : WASM_VAL_I32,
- "atomic rmw");
- wasm_stack_push(c, &stack, wasm_atomic_value_type(in->kind));
- break;
- case WASM_INSN_I32_ATOMIC_RMW_CMPXCHG:
- case WASM_INSN_I64_ATOMIC_RMW_CMPXCHG:
- wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads",
- "atomic cmpxchg");
- if (in->memidx >= m->nmemories)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: atomic cmpxchg without memory");
- if (!m->memories[in->memidx].shared)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: atomic cmpxchg requires shared memory");
- wasm_stack_pop(c, &stack, control, ncontrol,
- wasm_atomic_value_type(in->kind), "atomic cmpxchg");
- wasm_stack_pop(c, &stack, control, ncontrol,
- wasm_atomic_value_type(in->kind), "atomic cmpxchg");
- wasm_stack_pop(c, &stack, control, ncontrol,
- m->memories[in->memidx].is64 ? WASM_VAL_I64
- : WASM_VAL_I32,
- "atomic cmpxchg");
- wasm_stack_push(c, &stack, wasm_atomic_value_type(in->kind));
- break;
- case WASM_INSN_I32_ATOMIC_WAIT:
- case WASM_INSN_I64_ATOMIC_WAIT:
- wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads",
- "atomic wait");
- if (in->memidx >= m->nmemories)
- wasm_error(c, wasm_loc(0, 0), "wasm: atomic wait without memory");
- if (!m->memories[in->memidx].shared)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: atomic wait requires shared memory");
- wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I64,
- "atomic wait timeout");
- wasm_stack_pop(c, &stack, control, ncontrol,
- wasm_atomic_value_type(in->kind),
- "atomic wait expected");
- wasm_stack_pop(c, &stack, control, ncontrol,
- m->memories[in->memidx].is64 ? WASM_VAL_I64
- : WASM_VAL_I32,
- "atomic wait address");
- wasm_stack_push(c, &stack, WASM_VAL_I32);
- break;
- case WASM_INSN_MEMORY_ATOMIC_NOTIFY:
- wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads",
- "atomic notify");
- if (in->memidx >= m->nmemories)
- wasm_error(c, wasm_loc(0, 0), "wasm: atomic notify without memory");
- if (!m->memories[in->memidx].shared)
- wasm_error(c, wasm_loc(0, 0),
- "wasm: atomic notify requires shared memory");
- wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32,
- "atomic notify count");
- wasm_stack_pop(c, &stack, control, ncontrol,
- m->memories[in->memidx].is64 ? WASM_VAL_I64
- : WASM_VAL_I32,
- "atomic notify address");
- wasm_stack_push(c, &stack, WASM_VAL_I32);
- break;
- case WASM_INSN_I32_LOAD:
- case WASM_INSN_I64_LOAD:
- case WASM_INSN_I32_LOAD8_S:
- case WASM_INSN_I32_LOAD8_U:
- case WASM_INSN_I32_LOAD16_S:
- case WASM_INSN_I32_LOAD16_U:
- case WASM_INSN_I64_LOAD8_S:
- case WASM_INSN_I64_LOAD8_U:
- case WASM_INSN_I64_LOAD16_S:
- case WASM_INSN_I64_LOAD16_U:
- case WASM_INSN_I64_LOAD32_S:
- case WASM_INSN_I64_LOAD32_U:
- if (in->memidx >= m->nmemories)
- wasm_error(c, wasm_loc(0, 0), "wasm: load without memory");
- wasm_stack_pop(c, &stack, control, ncontrol,
- m->memories[in->memidx].is64 ? WASM_VAL_I64
- : WASM_VAL_I32,
- "load");
- wasm_stack_push(c, &stack, wasm_load_result_type(in->kind));
- break;
- case WASM_INSN_I32_STORE:
- case WASM_INSN_I64_STORE:
- case WASM_INSN_I32_STORE8:
- case WASM_INSN_I32_STORE16:
- case WASM_INSN_I64_STORE8:
- case WASM_INSN_I64_STORE16:
- case WASM_INSN_I64_STORE32:
- if (in->memidx >= m->nmemories)
- wasm_error(c, wasm_loc(0, 0), "wasm: store without memory");
- wasm_stack_pop(c, &stack, control, ncontrol,
- wasm_store_value_type(in->kind), "store");
- wasm_stack_pop(c, &stack, control, ncontrol,
- m->memories[in->memidx].is64 ? WASM_VAL_I64
- : WASM_VAL_I32,
- "store");
- break;
- case WASM_INSN_UNREACHABLE:
- wasm_mark_unreachable(&stack, control, ncontrol);
- break;
- case WASM_INSN_NOP:
- break;
- default:
- if (wasm_int_unop_kind(in->kind, &vt)) {
- wasm_stack_pop(c, &stack, control, ncontrol, vt, "unary operand");
- wasm_stack_push(c, &stack, vt);
- break;
- }
- if (wasm_fp_binop_kind(in->kind, &vt)) {
- wasm_stack_pop(c, &stack, control, ncontrol, vt, "fp operand");
- wasm_stack_pop(c, &stack, control, ncontrol, vt, "fp operand");
- wasm_stack_push(c, &stack, vt);
- break;
- }
- if (wasm_fp_cmp_kind(in->kind, &vt)) {
- wasm_stack_pop(c, &stack, control, ncontrol, vt, "fp compare");
- wasm_stack_pop(c, &stack, control, ncontrol, vt, "fp compare");
- wasm_stack_push(c, &stack, WASM_VAL_I32);
- break;
- }
- if (wasm_conversion_kind(in->kind, &src, &dst)) {
- wasm_stack_pop(c, &stack, control, ncontrol, src, "conversion");
- wasm_stack_push(c, &stack, dst);
- break;
- }
- {
- CfreeCgIntCmpOp cmp;
- WasmValType rhs, lhs;
- rhs = stack.depth > control[ncontrol - 1u].height
- ? stack.vals[stack.depth - 1u]
- : WASM_VAL_I32;
- wasm_stack_pop(c, &stack, control, ncontrol, 0, "operand");
- lhs = stack.depth > control[ncontrol - 1u].height
- ? stack.vals[stack.depth - 1u]
- : rhs;
- wasm_stack_pop(c, &stack, control, ncontrol, rhs, "operand");
- if (lhs != rhs)
- wasm_error(c, wasm_loc(0, 0), "wasm: operand type mismatch");
- wasm_stack_push(
- c, &stack,
- wasm_int_cmp_op(in->kind, &cmp) ? WASM_VAL_I32 : lhs);
- break;
- }
- }
- }
- if (ncontrol != 1u)
- wasm_error(c, wasm_loc(0, 0), "wasm: unterminated control block");
- if (!control[0].unreachable) {
- if (f->nresults) {
- if (stack.depth != 1u || stack.vals[0] != f->results[0])
- wasm_error(c, wasm_loc(0, 0), "wasm: function result type mismatch");
- } else if (stack.depth != 0) {
- wasm_error(c, wasm_loc(0, 0), "wasm: function leaves extra values");
- }
- }
- }
-}
-
-static void wasm_cg_call_func(CfreeCompiler* c, CfreeCg* cg,
- CfreeCgBuiltinTypes b, const WasmFunc* f,
- const WasmCgRuntime* rt, CfreeCgSym sym,
- CfreeCgTypeId func_type,
- CfreeCgLocal instance_local,
- uint32_t func_index, int must_tail) {
- CfreeCgLocalAttrs attrs;
- CfreeCgLocal args[16];
- CfreeCgLocal callee = CFREE_CG_LOCAL_NONE;
- memset(&attrs, 0, sizeof attrs);
- attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
- if (f->nparams > 16u)
- wasm_error(c, wasm_loc(0, 0), "wasm: too many call arguments");
- for (uint32_t p = 0; p < f->nparams; ++p) {
- uint32_t param = f->nparams - 1u - p;
- args[param] =
- cfree_cg_local(cg, wasm_cg_type(c, b, f->params[param]), attrs);
- cfree_cg_push_local(cg, args[param]);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, wasm_cg_mem(c, b, f->params[param]));
- }
- if (f->is_import) {
- CfreeCgLabel ok = cfree_cg_label_new(cg);
- callee = cfree_cg_local(cg, rt->void_ptr_ty, attrs);
- cfree_cg_push_local(cg, callee);
- wasm_cg_push_import_func_ptr(cg, rt, instance_local, func_index);
- cfree_cg_store(cg, wasm_cg_mem_type(rt->void_ptr_ty));
- cfree_cg_push_local(cg, callee);
- cfree_cg_load(cg, wasm_cg_mem_type(rt->void_ptr_ty));
- cfree_cg_push_null(cg, rt->void_ptr_ty);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_NE);
- cfree_cg_branch_true(cg, ok);
- wasm_cg_trap_table(cg, rt);
- cfree_cg_label_place(cg, ok);
- cfree_cg_push_local(cg, callee);
- cfree_cg_load(cg, wasm_cg_mem_type(rt->void_ptr_ty));
- cfree_cg_bitcast(cg, cfree_cg_type_ptr(c, func_type, 0));
- }
- cfree_cg_push_local(cg, instance_local);
- cfree_cg_load(cg, wasm_cg_mem_type(rt->instance_ptr_ty));
- for (uint32_t p = 0; p < f->nparams; ++p) {
- cfree_cg_push_local(cg, args[p]);
- cfree_cg_load(cg, wasm_cg_mem(c, b, f->params[p]));
- }
- if (f->is_import)
- cfree_cg_call(cg, f->nparams + 1u, func_type,
- (CfreeCgCallAttrs){
- .tail = must_tail ? CFREE_CG_TAIL_MUST
- : CFREE_CG_TAIL_DEFAULT});
- else
- cfree_cg_call_symbol(cg, sym, f->nparams + 1u,
- (CfreeCgCallAttrs){
- .tail = must_tail ? CFREE_CG_TAIL_MUST
- : CFREE_CG_TAIL_DEFAULT});
- if (must_tail) {
- if (f->nresults) wasm_cg_push_zero(c, cg, b, f->results[0]);
- cfree_cg_unreachable(cg);
- }
-}
-
-static void wasm_emit_cg(CfreeCompiler* c, const CfreeCodeOptions* code_opts,
- CfreeObjBuilder* out, const WasmModule* m) {
- CfreeCg* cg = NULL;
- CfreeStatus cg_st = cfree_cg_new(c, out, code_opts, &cg);
- if (cg_st != CFREE_OK)
- wasm_error(c, wasm_loc(0, 0), "wasm: failed to initialize codegen");
- CfreeCgBuiltinTypes b = cfree_cg_builtin_types(c);
- WasmCgRuntime rt;
- CfreeCgSym syms[64];
- CfreeCgSym init_sym = CFREE_CG_SYM_NONE;
- CfreeCgTypeId func_types[64];
- CfreeCgLocal locals[48];
- uint32_t i, j;
- if (!cg) wasm_error(c, wasm_loc(0, 0), "wasm: failed to initialize codegen");
- if (m->nfuncs > 64u)
- wasm_error(c, wasm_loc(0, 0), "wasm: too many functions");
- if (m->nglobals > 64u)
- wasm_error(c, wasm_loc(0, 0), "wasm: too many globals");
- wasm_cg_build_runtime(c, b, m, &rt);
- memset(syms, 0, sizeof syms);
- memset(func_types, 0, sizeof func_types);
- {
- CfreeCgFuncSig sig;
- CfreeCgDecl decl;
- memset(&sig, 0, sizeof sig);
- sig.ret = b.id[CFREE_CG_BUILTIN_VOID];
- sig.call_conv = CFREE_CG_CC_TARGET_C;
- rt.trap_func_ty = cfree_cg_type_func(c, sig);
- if (!rt.trap_func_ty)
- wasm_error(c, wasm_loc(0, 0), "wasm: failed to create trap type");
- for (uint32_t k = 0; k < WASM_TRAP_COUNT; ++k) {
- CfreeSym source_name = cfree_sym_intern(c, wasm_trap_name(k));
- memset(&decl, 0, sizeof decl);
- decl.kind = CFREE_CG_DECL_FUNC;
- decl.linkage_name = cfree_cg_c_linkage_name(c, source_name);
- decl.display_name = source_name;
- decl.type = rt.trap_func_ty;
- decl.sym.bind = CFREE_SB_LOCAL;
- decl.as.func.flags = CFREE_CG_FUNC_NORETURN | CFREE_CG_FUNC_COLD;
- rt.trap_syms[k] = cfree_cg_decl(cg, decl);
- if (!rt.trap_syms[k])
- wasm_error(c, wasm_loc(0, 0), "wasm: failed to declare trap helper");
- }
- }
- {
- CfreeCgDecl decl;
- CfreeCgFuncParam init_param;
- CfreeCgFuncSig sig;
- memset(&init_param, 0, sizeof init_param);
- init_param.type = rt.instance_ptr_ty;
- memset(&sig, 0, sizeof sig);
- sig.ret = b.id[CFREE_CG_BUILTIN_VOID];
- sig.params = &init_param;
- sig.nparams = 1;
- sig.call_conv = CFREE_CG_CC_TARGET_C;
- memset(&decl, 0, sizeof decl);
- decl.kind = CFREE_CG_DECL_FUNC;
- decl.linkage_name =
- cfree_cg_c_linkage_name(c, cfree_sym_intern(c, "__cfree_wasm_init"));
- decl.display_name = decl.linkage_name;
- decl.type = cfree_cg_type_func(c, sig);
- decl.sym.bind = CFREE_SB_GLOBAL;
- init_sym = cfree_cg_decl(cg, decl);
- }
- for (i = 0; i < m->nfuncs; ++i) {
- const WasmFunc* f = &m->funcs[i];
- CfreeCgFuncParam cg_params[17];
- CfreeCgFuncSig sig;
- CfreeCgDecl decl;
- char local_name[40];
- CfreeSym source_name;
- memset(&cg_params[0], 0, sizeof cg_params[0]);
- cg_params[0].type = rt.instance_ptr_ty;
- for (j = 0; j < f->nparams; ++j) {
- memset(&cg_params[j + 1u], 0, sizeof cg_params[j + 1u]);
- cg_params[j + 1u].type = wasm_cg_type(c, b, f->params[j]);
- }
- memset(&sig, 0, sizeof sig);
- sig.ret = f->nresults ? wasm_cg_type(c, b, f->results[0])
- : b.id[CFREE_CG_BUILTIN_VOID];
- sig.params = cg_params;
- sig.nparams = f->nparams + 1u;
- sig.call_conv = CFREE_CG_CC_TARGET_C;
- func_types[i] = cfree_cg_type_func(c, sig);
- if (!func_types[i])
- wasm_error(c, wasm_loc(0, 0), "wasm: failed to create function type");
- if (f->is_import) {
- syms[i] = CFREE_CG_SYM_NONE;
- continue;
- }
- if (f->export_name) {
- source_name = cfree_sym_intern(c, f->export_name);
- } else {
- size_t pos = 0;
- const char prefix[] = "__cfree_wasm_func_";
- uint32_t n = i, div = 1000000000u;
- memcpy(local_name, prefix, sizeof(prefix) - 1u);
- pos = sizeof(prefix) - 1u;
- while (div > 1u && n / div == 0) div /= 10u;
- while (div) {
- local_name[pos++] = (char)('0' + (n / div) % 10u);
- div /= 10u;
- }
- local_name[pos] = '\0';
- source_name = cfree_sym_intern(c, local_name);
- }
- memset(&decl, 0, sizeof decl);
- decl.kind = CFREE_CG_DECL_FUNC;
- decl.linkage_name = cfree_cg_c_linkage_name(c, source_name);
- decl.display_name = source_name;
- decl.type = func_types[i];
- decl.sym.bind = f->export_name ? CFREE_SB_GLOBAL : CFREE_SB_LOCAL;
- syms[i] = cfree_cg_decl(cg, decl);
- if (!syms[i])
- wasm_error(c, wasm_loc(0, 0), "wasm: failed to declare function");
- }
- for (uint32_t k = 0; k < WASM_TRAP_COUNT; ++k) {
- cfree_cg_func_begin(cg, rt.trap_syms[k]);
- wasm_cg_emit_raw_trap(cg);
- cfree_cg_func_end(cg);
- }
- if (init_sym) {
- CfreeCgLocalAttrs attrs;
- CfreeCgLocal instance_local;
- memset(&attrs, 0, sizeof attrs);
- cfree_cg_func_begin(cg, init_sym);
- instance_local = cfree_cg_param(cg, 0, rt.instance_ptr_ty, attrs);
- for (i = 0; i < m->nmemories; ++i) {
- const WasmMemory* mem = &m->memories[i];
- uint64_t max_pages = mem->has_max ? mem->max_pages : mem->min_pages;
- uint32_t flags = (mem->shared ? 1u : 0u) | (mem->is64 ? 2u : 0u);
- wasm_cg_push_memory_pages_lvalue(cg, &rt, instance_local, i);
- cfree_cg_push_int(cg, mem->min_pages, b.id[CFREE_CG_BUILTIN_I64]);
- cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
- wasm_cg_push_memory_max_lvalue(cg, &rt, instance_local, i);
- cfree_cg_push_int(cg, max_pages, b.id[CFREE_CG_BUILTIN_I64]);
- cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
- wasm_cg_push_memory_lvalue(cg, &rt, instance_local, i);
- cfree_cg_field(cg, rt.memory_flags_field);
- cfree_cg_push_int(cg, flags, b.id[CFREE_CG_BUILTIN_I32]);
- cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
- if (mem->data_init_len) {
- CfreeCgSym data_sym =
- cfree_cg_const_data(cg, mem->data, mem->data_init_len, 16,
- b.id[CFREE_CG_BUILTIN_I8]);
- CfreeCgMemAccess byte_mem =
- wasm_cg_mem_type(b.id[CFREE_CG_BUILTIN_I8]);
- wasm_cg_push_memory_data_ptr(cg, &rt, instance_local, i);
- cfree_cg_push_symbol_addr(cg, data_sym, 0);
- cfree_cg_memcpy(cg, m->memories[i].data_init_len, byte_mem, byte_mem);
- }
- }
- for (i = 0; i < m->nfuncs; ++i) {
- wasm_cg_push_func_ref_lvalue(cg, &rt, instance_local, i);
- cfree_cg_field(cg, rt.table_entry_fn_field);
- if (m->funcs[i].is_import) {
- wasm_cg_push_import_func_ptr(cg, &rt, instance_local, i);
- } else {
- cfree_cg_push_symbol_addr(cg, syms[i], 0);
- cfree_cg_bitcast(cg, rt.void_ptr_ty);
- }
- cfree_cg_store(cg, wasm_cg_mem_type(rt.void_ptr_ty));
- wasm_cg_push_func_ref_lvalue(cg, &rt, instance_local, i);
- cfree_cg_field(cg, rt.table_entry_typeidx_field);
- cfree_cg_push_int(cg, m->funcs[i].typeidx, b.id[CFREE_CG_BUILTIN_I32]);
- cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
- }
- for (i = 0; i < m->ntables; ++i) {
- const WasmTable* t = &m->tables[i];
- uint32_t max = t->has_max ? t->max : t->min;
- wasm_cg_push_table_lvalue(cg, &rt, instance_local, i);
- cfree_cg_field(cg, rt.table_entries_ptr_field);
- wasm_cg_push_table_entries_array_lvalue(cg, &rt, instance_local, i);
- cfree_cg_push_int(cg, 0, b.id[CFREE_CG_BUILTIN_I32]);
- cfree_cg_index(cg, 0);
- cfree_cg_addr(cg);
- cfree_cg_store(cg, wasm_cg_mem_type(rt.table_entry_ptr_ty));
- wasm_cg_push_table_lvalue(cg, &rt, instance_local, i);
- cfree_cg_field(cg, rt.table_len_field);
- cfree_cg_push_int(cg, t->min, b.id[CFREE_CG_BUILTIN_I32]);
- cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
- wasm_cg_push_table_lvalue(cg, &rt, instance_local, i);
- cfree_cg_field(cg, rt.table_max_field);
- cfree_cg_push_int(cg, max, b.id[CFREE_CG_BUILTIN_I32]);
- cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
- }
- for (i = 0; i < m->nelems; ++i) {
- const WasmElemSegment* seg = &m->elems[i];
- for (j = 0; j < seg->nfuncs; ++j) {
- uint32_t slot = (uint32_t)(seg->offset + j);
- uint32_t funcidx = seg->funcs[j];
- CfreeCgLocalAttrs tmp_attrs;
- CfreeCgLocal slot_local;
- memset(&tmp_attrs, 0, sizeof tmp_attrs);
- tmp_attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
- slot_local = cfree_cg_local(cg, b.id[CFREE_CG_BUILTIN_I32], tmp_attrs);
- cfree_cg_push_local(cg, slot_local);
- cfree_cg_push_int(cg, slot, b.id[CFREE_CG_BUILTIN_I32]);
- cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
- wasm_cg_push_table_entry_lvalue(cg, &rt, instance_local, seg->tableidx,
- slot_local,
- wasm_cg_mem(c, b, WASM_VAL_I32));
- cfree_cg_field(cg, rt.table_entry_fn_field);
- if (m->funcs[funcidx].is_import) {
- wasm_cg_push_import_func_ptr(cg, &rt, instance_local, funcidx);
- } else {
- cfree_cg_push_symbol_addr(cg, syms[funcidx], 0);
- cfree_cg_bitcast(cg, rt.void_ptr_ty);
- }
- cfree_cg_store(cg, wasm_cg_mem_type(rt.void_ptr_ty));
- wasm_cg_push_table_entry_lvalue(cg, &rt, instance_local, seg->tableidx,
- slot_local,
- wasm_cg_mem(c, b, WASM_VAL_I32));
- cfree_cg_field(cg, rt.table_entry_typeidx_field);
- cfree_cg_push_int(cg, m->funcs[funcidx].typeidx,
- b.id[CFREE_CG_BUILTIN_I32]);
- cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
- }
- }
- for (i = 0; i < m->nglobals; ++i) {
- const WasmGlobal* g = &m->globals[i];
- if (g->is_import) continue;
- wasm_cg_push_global_lvalue(cg, &rt, instance_local, i);
- if (g->type == WASM_VAL_F32 || g->type == WASM_VAL_F64)
- cfree_cg_push_float(cg, g->init.fp, wasm_cg_type(c, b, g->type));
- else
- cfree_cg_push_int(cg, (uint64_t)g->init.imm,
- wasm_cg_type(c, b, g->type));
- cfree_cg_store(cg, wasm_cg_mem(c, b, g->type));
- }
- if (m->has_start)
- wasm_cg_call_func(c, cg, b, &m->funcs[m->start_func], &rt,
- syms[m->start_func], func_types[m->start_func],
- instance_local, m->start_func, 0);
- cfree_cg_ret_void(cg);
- cfree_cg_func_end(cg);
- }
- for (i = 0; i < m->nfuncs; ++i) {
- const WasmFunc* f = &m->funcs[i];
- struct {
- uint8_t kind;
- int seen_else;
- CfreeCgLabel start;
- CfreeCgLabel end;
- CfreeCgLabel else_label;
- } control[64];
- uint32_t ncontrol = 0;
- CfreeCgLocal instance_local;
- if (f->is_import) continue;
- cfree_cg_func_begin(cg, syms[i]);
- {
- CfreeCgLocalAttrs attrs;
- memset(&attrs, 0, sizeof attrs);
- instance_local = cfree_cg_param(cg, 0, rt.instance_ptr_ty, attrs);
- }
- for (j = 0; j < f->nparams; ++j) {
- CfreeCgLocalAttrs attrs;
- memset(&attrs, 0, sizeof attrs);
- locals[j] =
- cfree_cg_param(cg, j + 1u, wasm_cg_type(c, b, f->params[j]), attrs);
- }
- for (j = 0; j < f->nlocals; ++j) {
- CfreeCgLocalAttrs attrs;
- memset(&attrs, 0, sizeof attrs);
- attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
- locals[f->nparams + j] =
- cfree_cg_local(cg, wasm_cg_type(c, b, f->locals[j]), attrs);
- cfree_cg_push_local(cg, locals[f->nparams + j]);
- wasm_cg_push_zero(c, cg, b, f->locals[j]);
- cfree_cg_store(cg, wasm_cg_mem(c, b, f->locals[j]));
- }
- for (j = 0; j < f->ninsns; ++j) {
- WasmInsn in = f->insns[j];
- switch (in.kind) {
- case WASM_INSN_UNREACHABLE:
- wasm_cg_trap_unreachable(cg, &rt);
- break;
- case WASM_INSN_NOP:
- break;
- case WASM_INSN_BLOCK:
- if (ncontrol >= 64u)
- wasm_error(c, wasm_loc(0, 0), "wasm: control stack too deep");
- memset(&control[ncontrol], 0, sizeof control[ncontrol]);
- control[ncontrol].kind = WASM_INSN_BLOCK;
- control[ncontrol].end = cfree_cg_label_new(cg);
- ncontrol++;
- break;
- case WASM_INSN_LOOP:
- if (ncontrol >= 64u)
- wasm_error(c, wasm_loc(0, 0), "wasm: control stack too deep");
- memset(&control[ncontrol], 0, sizeof control[ncontrol]);
- control[ncontrol].kind = WASM_INSN_LOOP;
- control[ncontrol].start = cfree_cg_label_new(cg);
- control[ncontrol].end = cfree_cg_label_new(cg);
- cfree_cg_label_place(cg, control[ncontrol].start);
- ncontrol++;
- break;
- case WASM_INSN_IF:
- if (ncontrol >= 64u)
- wasm_error(c, wasm_loc(0, 0), "wasm: control stack too deep");
- memset(&control[ncontrol], 0, sizeof control[ncontrol]);
- control[ncontrol].kind = WASM_INSN_IF;
- control[ncontrol].else_label = cfree_cg_label_new(cg);
- control[ncontrol].end = cfree_cg_label_new(cg);
- cfree_cg_branch_false(cg, control[ncontrol].else_label);
- ncontrol++;
- break;
- case WASM_INSN_ELSE:
- if (!ncontrol || control[ncontrol - 1u].kind != WASM_INSN_IF)
- wasm_error(c, wasm_loc(0, 0), "wasm: else without if");
- cfree_cg_jump(cg, control[ncontrol - 1u].end);
- cfree_cg_label_place(cg, control[ncontrol - 1u].else_label);
- control[ncontrol - 1u].seen_else = 1;
- break;
- case WASM_INSN_END:
- if (!ncontrol)
- wasm_error(c, wasm_loc(0, 0), "wasm: end without block");
- ncontrol--;
- if (control[ncontrol].kind == WASM_INSN_IF &&
- !control[ncontrol].seen_else)
- cfree_cg_label_place(cg, control[ncontrol].else_label);
- cfree_cg_label_place(cg, control[ncontrol].end);
- break;
- case WASM_INSN_BR: {
- uint32_t depth = (uint32_t)in.imm;
- uint32_t idx;
- if (depth >= ncontrol)
- wasm_error(c, wasm_loc(0, 0), "wasm: branch depth out of range");
- idx = ncontrol - 1u - depth;
- cfree_cg_jump(cg, control[idx].kind == WASM_INSN_LOOP
- ? control[idx].start
- : control[idx].end);
- break;
- }
- case WASM_INSN_BR_IF: {
- uint32_t depth = (uint32_t)in.imm;
- uint32_t idx;
- if (depth >= ncontrol)
- wasm_error(c, wasm_loc(0, 0), "wasm: branch depth out of range");
- idx = ncontrol - 1u - depth;
- cfree_cg_branch_true(cg, control[idx].kind == WASM_INSN_LOOP
- ? control[idx].start
- : control[idx].end);
- break;
- }
- case WASM_INSN_BR_TABLE: {
- CfreeCgSwitchCase cases[15];
- CfreeCgSwitch sw;
- memset(cases, 0, sizeof cases);
- if (in.ntargets == 0 || in.ntargets > 16u)
- wasm_error(c, wasm_loc(0, 0), "wasm: bad br_table target count");
- for (uint32_t k = 0; k + 1u < in.ntargets; ++k) {
- uint32_t idx;
- if (in.targets[k] >= ncontrol)
- wasm_error(c, wasm_loc(0, 0), "wasm: branch depth out of range");
- idx = ncontrol - 1u - in.targets[k];
- cases[k].value = k;
- cases[k].label = control[idx].kind == WASM_INSN_LOOP
- ? control[idx].start
- : control[idx].end;
- }
- if (in.targets[in.ntargets - 1u] >= ncontrol)
- wasm_error(c, wasm_loc(0, 0), "wasm: branch depth out of range");
- memset(&sw, 0, sizeof sw);
- sw.selector_type = b.id[CFREE_CG_BUILTIN_I32];
- sw.cases = cases;
- sw.ncases = in.ntargets - 1u;
- sw.default_label =
- control[ncontrol - 1u - in.targets[in.ntargets - 1u]].kind ==
- WASM_INSN_LOOP
- ? control[ncontrol - 1u - in.targets[in.ntargets - 1u]].start
- : control[ncontrol - 1u - in.targets[in.ntargets - 1u]].end;
- sw.hint = CFREE_CG_SWITCH_BRANCH_CHAIN;
- cfree_cg_switch(cg, sw);
- break;
- }
- case WASM_INSN_SELECT: {
- CfreeCgTypeId ty = wasm_cg_type(c, b, (WasmValType)in.type);
- CfreeCgMemAccess mem = wasm_cg_mem(c, b, (WasmValType)in.type);
- CfreeCgLocalAttrs attrs;
- CfreeCgLocal lhs, rhs, cond, result;
- CfreeCgLabel else_label = cfree_cg_label_new(cg);
- CfreeCgLabel end_label = cfree_cg_label_new(cg);
- memset(&attrs, 0, sizeof attrs);
- attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
- lhs = cfree_cg_local(cg, ty, attrs);
- rhs = cfree_cg_local(cg, ty, attrs);
- result = cfree_cg_local(cg, ty, attrs);
- cond = cfree_cg_local(cg, b.id[CFREE_CG_BUILTIN_I32], attrs);
-
- cfree_cg_push_local(cg, cond);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
- cfree_cg_push_local(cg, rhs);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, mem);
- cfree_cg_push_local(cg, lhs);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, mem);
-
- cfree_cg_push_local(cg, cond);
- cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
- cfree_cg_branch_false(cg, else_label);
- cfree_cg_push_local(cg, result);
- cfree_cg_push_local(cg, lhs);
- cfree_cg_load(cg, mem);
- cfree_cg_store(cg, mem);
- cfree_cg_jump(cg, end_label);
- cfree_cg_label_place(cg, else_label);
- cfree_cg_push_local(cg, result);
- cfree_cg_push_local(cg, rhs);
- cfree_cg_load(cg, mem);
- cfree_cg_store(cg, mem);
- cfree_cg_label_place(cg, end_label);
- cfree_cg_push_local(cg, result);
- cfree_cg_load(cg, mem);
- } break;
- case WASM_INSN_I32_CONST:
- cfree_cg_push_int(cg, (uint64_t)(uint32_t)in.imm,
- b.id[CFREE_CG_BUILTIN_I32]);
- break;
- case WASM_INSN_I64_CONST:
- cfree_cg_push_int(cg, (uint64_t)in.imm, b.id[CFREE_CG_BUILTIN_I64]);
- break;
- case WASM_INSN_F32_CONST:
- cfree_cg_push_float(cg, in.fp, b.id[CFREE_CG_BUILTIN_F32]);
- break;
- case WASM_INSN_F64_CONST:
- cfree_cg_push_float(cg, in.fp, b.id[CFREE_CG_BUILTIN_F64]);
- break;
- case WASM_INSN_LOCAL_GET: {
- uint32_t index = (uint32_t)in.imm;
- cfree_cg_push_local(cg, locals[index]);
- cfree_cg_load(cg, wasm_cg_mem(c, b, wasm_func_local_type(f, index)));
- break;
- }
- case WASM_INSN_LOCAL_SET: {
- uint32_t index = (uint32_t)in.imm;
- cfree_cg_push_local(cg, locals[index]);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, wasm_cg_mem(c, b, wasm_func_local_type(f, index)));
- break;
- }
- case WASM_INSN_LOCAL_TEE: {
- uint32_t index = (uint32_t)in.imm;
- cfree_cg_dup(cg);
- cfree_cg_push_local(cg, locals[index]);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, wasm_cg_mem(c, b, wasm_func_local_type(f, index)));
- break;
- }
- case WASM_INSN_CALL:
- case WASM_INSN_RETURN_CALL:
- wasm_cg_call_func(c, cg, b, &m->funcs[in.imm], &rt, syms[in.imm],
- func_types[in.imm], instance_local,
- (uint32_t)in.imm,
- in.kind == WASM_INSN_RETURN_CALL);
- break;
- case WASM_INSN_CALL_INDIRECT:
- case WASM_INSN_RETURN_CALL_INDIRECT: {
- const WasmFuncType* t = &m->types[in.imm];
- CfreeCgLocalAttrs attrs;
- CfreeCgLocal selector, callee, args[16], result = CFREE_CG_LOCAL_NONE;
- CfreeCgLabel ok;
- CfreeCgMemAccess i32_mem = wasm_cg_mem(c, b, WASM_VAL_I32);
- CfreeCgFuncParam indirect_params[17];
- CfreeCgFuncSig indirect_sig;
- CfreeCgTypeId indirect_func_type;
- memset(&attrs, 0, sizeof attrs);
- attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
- if (t->nparams > 16u)
- wasm_error(c, wasm_loc(0, 0), "wasm: too many call_indirect args");
- memset(indirect_params, 0, sizeof indirect_params);
- indirect_params[0].type = rt.instance_ptr_ty;
- for (uint32_t p = 0; p < t->nparams; ++p)
- indirect_params[p + 1u].type = wasm_cg_type(c, b, t->params[p]);
- memset(&indirect_sig, 0, sizeof indirect_sig);
- indirect_sig.ret = t->nresults ? wasm_cg_type(c, b, t->results[0])
- : b.id[CFREE_CG_BUILTIN_VOID];
- indirect_sig.params = indirect_params;
- indirect_sig.nparams = t->nparams + 1u;
- indirect_sig.call_conv = CFREE_CG_CC_TARGET_C;
- indirect_func_type = cfree_cg_type_func(c, indirect_sig);
- selector = cfree_cg_local(cg, b.id[CFREE_CG_BUILTIN_I32], attrs);
- callee = cfree_cg_local(cg, rt.void_ptr_ty, attrs);
- if (t->nresults)
- result =
- cfree_cg_local(cg, wasm_cg_type(c, b, t->results[0]), attrs);
- cfree_cg_push_local(cg, selector);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, i32_mem);
- for (uint32_t p = 0; p < t->nparams; ++p) {
- uint32_t param = t->nparams - 1u - p;
- args[param] =
- cfree_cg_local(cg, wasm_cg_type(c, b, t->params[param]), attrs);
- cfree_cg_push_local(cg, args[param]);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, wasm_cg_mem(c, b, t->params[param]));
- }
-
- ok = cfree_cg_label_new(cg);
- cfree_cg_push_local(cg, selector);
- cfree_cg_load(cg, i32_mem);
- wasm_cg_push_table_lvalue(cg, &rt, instance_local, in.align);
- cfree_cg_field(cg, rt.table_len_field);
- cfree_cg_load(cg, i32_mem);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_LT_U);
- cfree_cg_branch_true(cg, ok);
- wasm_cg_trap_table(cg, &rt);
- cfree_cg_label_place(cg, ok);
-
- ok = cfree_cg_label_new(cg);
- cfree_cg_push_local(cg, callee);
- wasm_cg_push_table_entry_lvalue(cg, &rt, instance_local, in.align,
- selector, i32_mem);
- cfree_cg_field(cg, rt.table_entry_fn_field);
- cfree_cg_load(cg, wasm_cg_mem_type(rt.void_ptr_ty));
- cfree_cg_store(cg, wasm_cg_mem_type(rt.void_ptr_ty));
- cfree_cg_push_local(cg, callee);
- cfree_cg_load(cg, wasm_cg_mem_type(rt.void_ptr_ty));
- cfree_cg_push_null(cg, rt.void_ptr_ty);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_NE);
- cfree_cg_branch_true(cg, ok);
- wasm_cg_trap_table(cg, &rt);
- cfree_cg_label_place(cg, ok);
-
- ok = cfree_cg_label_new(cg);
- wasm_cg_push_table_entry_lvalue(cg, &rt, instance_local, in.align,
- selector, i32_mem);
- cfree_cg_field(cg, rt.table_entry_typeidx_field);
- cfree_cg_load(cg, i32_mem);
- cfree_cg_push_int(cg, (uint32_t)in.imm, b.id[CFREE_CG_BUILTIN_I32]);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_EQ);
- cfree_cg_branch_true(cg, ok);
- wasm_cg_trap_signature(cg, &rt);
- cfree_cg_label_place(cg, ok);
-
- cfree_cg_push_local(cg, callee);
- cfree_cg_load(cg, wasm_cg_mem_type(rt.void_ptr_ty));
- cfree_cg_bitcast(cg, cfree_cg_type_ptr(c, indirect_func_type, 0));
- cfree_cg_push_local(cg, instance_local);
- cfree_cg_load(cg, wasm_cg_mem_type(rt.instance_ptr_ty));
- for (uint32_t p = 0; p < t->nparams; ++p) {
- cfree_cg_push_local(cg, args[p]);
- cfree_cg_load(cg, wasm_cg_mem(c, b, t->params[p]));
- }
- cfree_cg_call(cg, t->nparams + 1u, indirect_func_type,
- (CfreeCgCallAttrs){
- .tail = in.kind == WASM_INSN_RETURN_CALL_INDIRECT
- ? CFREE_CG_TAIL_MUST
- : CFREE_CG_TAIL_DEFAULT});
- if (in.kind == WASM_INSN_RETURN_CALL_INDIRECT) {
- if (t->nresults) wasm_cg_push_zero(c, cg, b, t->results[0]);
- cfree_cg_unreachable(cg);
- } else if (t->nresults) {
- cfree_cg_push_local(cg, result);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, wasm_cg_mem(c, b, t->results[0]));
- cfree_cg_push_local(cg, result);
- cfree_cg_load(cg, wasm_cg_mem(c, b, t->results[0]));
- }
- break;
- }
- case WASM_INSN_REF_NULL:
- cfree_cg_push_null(cg, rt.void_ptr_ty);
- break;
- case WASM_INSN_REF_FUNC:
- wasm_cg_push_func_ref_lvalue(cg, &rt, instance_local,
- (uint32_t)in.imm);
- cfree_cg_addr(cg);
- cfree_cg_bitcast(cg, rt.void_ptr_ty);
- break;
- case WASM_INSN_REF_IS_NULL:
- cfree_cg_push_null(cg, rt.void_ptr_ty);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_EQ);
- break;
- case WASM_INSN_CALL_REF:
- case WASM_INSN_RETURN_CALL_REF: {
- const WasmFuncType* t = &m->types[in.imm];
- CfreeCgLocalAttrs attrs;
- CfreeCgLocal callee_ref, callee, args[16],
- result = CFREE_CG_LOCAL_NONE;
- CfreeCgLabel ok;
- CfreeCgMemAccess ref_mem = wasm_cg_mem_type(rt.void_ptr_ty);
- CfreeCgMemAccess i32_mem = wasm_cg_mem(c, b, WASM_VAL_I32);
- CfreeCgFuncParam ref_params[17];
- CfreeCgFuncSig ref_sig;
- CfreeCgTypeId ref_func_type;
- memset(&attrs, 0, sizeof attrs);
- attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
- if (t->nparams > 16u)
- wasm_error(c, wasm_loc(0, 0), "wasm: too many call_ref args");
- callee_ref = cfree_cg_local(cg, rt.void_ptr_ty, attrs);
- callee = cfree_cg_local(cg, rt.void_ptr_ty, attrs);
- cfree_cg_push_local(cg, callee_ref);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, ref_mem);
- for (uint32_t p = 0; p < t->nparams; ++p) {
- uint32_t param = t->nparams - 1u - p;
- args[param] =
- cfree_cg_local(cg, wasm_cg_type(c, b, t->params[param]), attrs);
- cfree_cg_push_local(cg, args[param]);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, wasm_cg_mem(c, b, t->params[param]));
- }
- ok = cfree_cg_label_new(cg);
- cfree_cg_push_local(cg, callee_ref);
- cfree_cg_load(cg, ref_mem);
- cfree_cg_push_null(cg, rt.void_ptr_ty);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_NE);
- cfree_cg_branch_true(cg, ok);
- wasm_cg_trap_table(cg, &rt);
- cfree_cg_label_place(cg, ok);
-
- ok = cfree_cg_label_new(cg);
- cfree_cg_push_local(cg, callee_ref);
- cfree_cg_load(cg, ref_mem);
- cfree_cg_bitcast(cg, rt.table_entry_ptr_ty);
- cfree_cg_indirect(cg);
- cfree_cg_field(cg, rt.table_entry_typeidx_field);
- cfree_cg_load(cg, i32_mem);
- cfree_cg_push_int(cg, (uint32_t)in.imm, b.id[CFREE_CG_BUILTIN_I32]);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_EQ);
- cfree_cg_branch_true(cg, ok);
- wasm_cg_trap_signature(cg, &rt);
- cfree_cg_label_place(cg, ok);
-
- cfree_cg_push_local(cg, callee);
- cfree_cg_push_local(cg, callee_ref);
- cfree_cg_load(cg, ref_mem);
- cfree_cg_bitcast(cg, rt.table_entry_ptr_ty);
- cfree_cg_indirect(cg);
- cfree_cg_field(cg, rt.table_entry_fn_field);
- cfree_cg_load(cg, ref_mem);
- cfree_cg_store(cg, ref_mem);
-
- memset(ref_params, 0, sizeof ref_params);
- ref_params[0].type = rt.instance_ptr_ty;
- for (uint32_t p = 0; p < t->nparams; ++p)
- ref_params[p + 1u].type = wasm_cg_type(c, b, t->params[p]);
- memset(&ref_sig, 0, sizeof ref_sig);
- ref_sig.ret = t->nresults ? wasm_cg_type(c, b, t->results[0])
- : b.id[CFREE_CG_BUILTIN_VOID];
- ref_sig.params = ref_params;
- ref_sig.nparams = t->nparams + 1u;
- ref_sig.call_conv = CFREE_CG_CC_TARGET_C;
- ref_func_type = cfree_cg_type_func(c, ref_sig);
- if (t->nresults)
- result =
- cfree_cg_local(cg, wasm_cg_type(c, b, t->results[0]), attrs);
- cfree_cg_push_local(cg, callee);
- cfree_cg_load(cg, ref_mem);
- cfree_cg_bitcast(cg, cfree_cg_type_ptr(c, ref_func_type, 0));
- cfree_cg_push_local(cg, instance_local);
- cfree_cg_load(cg, wasm_cg_mem_type(rt.instance_ptr_ty));
- for (uint32_t p = 0; p < t->nparams; ++p) {
- cfree_cg_push_local(cg, args[p]);
- cfree_cg_load(cg, wasm_cg_mem(c, b, t->params[p]));
- }
- cfree_cg_call(cg, t->nparams + 1u, ref_func_type,
- (CfreeCgCallAttrs){
- .tail = in.kind == WASM_INSN_RETURN_CALL_REF
- ? CFREE_CG_TAIL_MUST
- : CFREE_CG_TAIL_DEFAULT});
- if (in.kind == WASM_INSN_RETURN_CALL_REF) {
- if (t->nresults) wasm_cg_push_zero(c, cg, b, t->results[0]);
- cfree_cg_unreachable(cg);
- } else if (t->nresults) {
- cfree_cg_push_local(cg, result);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, wasm_cg_mem(c, b, t->results[0]));
- cfree_cg_push_local(cg, result);
- cfree_cg_load(cg, wasm_cg_mem(c, b, t->results[0]));
- }
- break;
- }
- case WASM_INSN_GLOBAL_GET: {
- uint32_t index = (uint32_t)in.imm;
- wasm_cg_push_global_value_lvalue(c, cg, b, &rt, instance_local, m,
- index);
- cfree_cg_load(cg, wasm_cg_mem(c, b, m->globals[index].type));
- break;
- }
- case WASM_INSN_GLOBAL_SET: {
- uint32_t index = (uint32_t)in.imm;
- wasm_cg_push_global_value_lvalue(c, cg, b, &rt, instance_local, m,
- index);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, wasm_cg_mem(c, b, m->globals[index].type));
- break;
- }
- case WASM_INSN_RETURN:
- if (f->nresults)
- cfree_cg_ret(cg);
- else
- cfree_cg_ret_void(cg);
- break;
- case WASM_INSN_DROP:
- cfree_cg_drop(cg);
- break;
- case WASM_INSN_MEMORY_SIZE:
- wasm_cg_push_memory_pages_lvalue(cg, &rt, instance_local,
- in.memidx);
- cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
- if (!m->memories[in.memidx].is64)
- cfree_cg_trunc(cg, b.id[CFREE_CG_BUILTIN_I32]);
- break;
- case WASM_INSN_MEMORY_GROW: {
- CfreeCgLocalAttrs attrs;
- CfreeCgLocal delta, old_pages, grow_result;
- CfreeCgLabel fail = cfree_cg_label_new(cg);
- CfreeCgLabel done = cfree_cg_label_new(cg);
- WasmValType page_vt =
- m->memories[in.memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32;
- CfreeCgTypeId page_ty = wasm_cg_type(c, b, page_vt);
- memset(&attrs, 0, sizeof attrs);
- attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
- delta = cfree_cg_local(cg, page_ty, attrs);
- old_pages = cfree_cg_local(cg, b.id[CFREE_CG_BUILTIN_I64], attrs);
- grow_result = cfree_cg_local(cg, page_ty, attrs);
- cfree_cg_push_local(cg, delta);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, wasm_cg_mem(c, b, page_vt));
- cfree_cg_push_local(cg, old_pages);
- wasm_cg_push_memory_pages_lvalue(cg, &rt, instance_local,
- in.memidx);
- cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
- cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
-
- cfree_cg_push_local(cg, delta);
- cfree_cg_load(cg, wasm_cg_mem(c, b, page_vt));
- if (!m->memories[in.memidx].is64)
- cfree_cg_zext(cg, b.id[CFREE_CG_BUILTIN_I64]);
- wasm_cg_push_memory_max_lvalue(cg, &rt, instance_local, in.memidx);
- cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
- cfree_cg_push_local(cg, old_pages);
- cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
- cfree_cg_int_binop(cg, CFREE_CG_INT_SUB, 0);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_LE_U);
- cfree_cg_branch_false(cg, fail);
-
- wasm_cg_push_memory_pages_lvalue(cg, &rt, instance_local,
- in.memidx);
- cfree_cg_push_local(cg, old_pages);
- cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
- cfree_cg_push_local(cg, delta);
- cfree_cg_load(cg, wasm_cg_mem(c, b, page_vt));
- if (!m->memories[in.memidx].is64)
- cfree_cg_zext(cg, b.id[CFREE_CG_BUILTIN_I64]);
- cfree_cg_int_binop(cg, CFREE_CG_INT_ADD, 0);
- cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
- cfree_cg_push_local(cg, grow_result);
- cfree_cg_push_local(cg, old_pages);
- cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
- if (!m->memories[in.memidx].is64)
- cfree_cg_trunc(cg, b.id[CFREE_CG_BUILTIN_I32]);
- cfree_cg_store(cg, wasm_cg_mem(c, b, page_vt));
- cfree_cg_jump(cg, done);
- cfree_cg_label_place(cg, fail);
- cfree_cg_push_local(cg, grow_result);
- cfree_cg_push_int(cg, UINT64_MAX, page_ty);
- cfree_cg_store(cg, wasm_cg_mem(c, b, page_vt));
- cfree_cg_label_place(cg, done);
- cfree_cg_push_local(cg, grow_result);
- cfree_cg_load(cg, wasm_cg_mem(c, b, page_vt));
- break;
- }
- case WASM_INSN_ATOMIC_FENCE:
- cfree_cg_atomic_fence(cg, CFREE_CG_MO_SEQ_CST);
- break;
- case WASM_INSN_I32_ATOMIC_LOAD:
- case WASM_INSN_I64_ATOMIC_LOAD:
- case WASM_INSN_I32_ATOMIC_LOAD8_U:
- case WASM_INSN_I32_ATOMIC_LOAD16_U:
- case WASM_INSN_I64_ATOMIC_LOAD8_U:
- case WASM_INSN_I64_ATOMIC_LOAD16_U:
- case WASM_INSN_I64_ATOMIC_LOAD32_U: {
- CfreeCgTypeId ty = wasm_cg_type(c, b, wasm_atomic_value_type(in.kind));
- CfreeCgMemAccess mem = wasm_cg_mem_type(ty);
- mem.align = in.align;
- wasm_cg_memory_check(c, cg, b, m, &rt, instance_local, &in);
- wasm_cg_memory_lvalue(cg, &rt, instance_local, in.memidx,
- in.offset64);
- cfree_cg_addr(cg);
- cfree_cg_atomic_load(cg, mem, CFREE_CG_MO_SEQ_CST);
- break;
- }
- case WASM_INSN_I32_ATOMIC_STORE:
- case WASM_INSN_I64_ATOMIC_STORE:
- case WASM_INSN_I32_ATOMIC_STORE8:
- case WASM_INSN_I32_ATOMIC_STORE16:
- case WASM_INSN_I64_ATOMIC_STORE8:
- case WASM_INSN_I64_ATOMIC_STORE16:
- case WASM_INSN_I64_ATOMIC_STORE32: {
- WasmValType vt = wasm_atomic_value_type(in.kind);
- CfreeCgTypeId ty = wasm_cg_type(c, b, vt);
- CfreeCgMemAccess mem = wasm_cg_mem_type(ty);
- CfreeCgLocalAttrs attrs;
- CfreeCgLocal value_tmp;
- memset(&attrs, 0, sizeof attrs);
- attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
- mem.align = in.align;
- value_tmp = cfree_cg_local(cg, ty, attrs);
- cfree_cg_push_local(cg, value_tmp);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, mem);
- wasm_cg_memory_check(c, cg, b, m, &rt, instance_local, &in);
- wasm_cg_memory_lvalue(cg, &rt, instance_local, in.memidx,
- in.offset64);
- cfree_cg_addr(cg);
- cfree_cg_push_local(cg, value_tmp);
- cfree_cg_load(cg, mem);
- cfree_cg_atomic_store(cg, mem, CFREE_CG_MO_SEQ_CST);
- break;
- }
- case WASM_INSN_I32_ATOMIC_RMW_ADD:
- case WASM_INSN_I64_ATOMIC_RMW_ADD:
- case WASM_INSN_I32_ATOMIC_RMW_SUB:
- case WASM_INSN_I64_ATOMIC_RMW_SUB:
- case WASM_INSN_I32_ATOMIC_RMW_AND:
- case WASM_INSN_I64_ATOMIC_RMW_AND:
- case WASM_INSN_I32_ATOMIC_RMW_OR:
- case WASM_INSN_I64_ATOMIC_RMW_OR:
- case WASM_INSN_I32_ATOMIC_RMW_XOR:
- case WASM_INSN_I64_ATOMIC_RMW_XOR:
- case WASM_INSN_I32_ATOMIC_RMW_XCHG:
- case WASM_INSN_I64_ATOMIC_RMW_XCHG: {
- WasmValType vt = wasm_atomic_value_type(in.kind);
- CfreeCgTypeId ty = wasm_cg_type(c, b, vt);
- CfreeCgMemAccess mem = wasm_cg_mem_type(ty);
- CfreeCgLocalAttrs attrs;
- CfreeCgLocal value_tmp;
- memset(&attrs, 0, sizeof attrs);
- attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
- mem.align = in.align;
- value_tmp = cfree_cg_local(cg, ty, attrs);
- cfree_cg_push_local(cg, value_tmp);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, mem);
- wasm_cg_memory_check(c, cg, b, m, &rt, instance_local, &in);
- wasm_cg_memory_lvalue(cg, &rt, instance_local, in.memidx,
- in.offset64);
- cfree_cg_addr(cg);
- cfree_cg_push_local(cg, value_tmp);
- cfree_cg_load(cg, mem);
- cfree_cg_atomic_rmw(cg, mem, wasm_atomic_rmw_op(in.kind),
- CFREE_CG_MO_SEQ_CST);
- break;
- }
- case WASM_INSN_I32_ATOMIC_RMW_CMPXCHG:
- case WASM_INSN_I64_ATOMIC_RMW_CMPXCHG: {
- WasmValType vt = wasm_atomic_value_type(in.kind);
- CfreeCgTypeId ty = wasm_cg_type(c, b, vt);
- CfreeCgMemAccess mem = wasm_cg_mem_type(ty);
- CfreeCgLocalAttrs attrs;
- CfreeCgLocal expected_tmp, desired_tmp;
- memset(&attrs, 0, sizeof attrs);
- attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
- mem.align = in.align;
- desired_tmp = cfree_cg_local(cg, ty, attrs);
- expected_tmp = cfree_cg_local(cg, ty, attrs);
- cfree_cg_push_local(cg, desired_tmp);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, mem);
- cfree_cg_push_local(cg, expected_tmp);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, mem);
- wasm_cg_memory_check(c, cg, b, m, &rt, instance_local, &in);
- wasm_cg_memory_lvalue(cg, &rt, instance_local, in.memidx,
- in.offset64);
- cfree_cg_addr(cg);
- cfree_cg_push_local(cg, expected_tmp);
- cfree_cg_load(cg, mem);
- cfree_cg_push_local(cg, desired_tmp);
- cfree_cg_load(cg, mem);
- cfree_cg_atomic_cmpxchg(cg, mem, CFREE_CG_MO_SEQ_CST,
- CFREE_CG_MO_SEQ_CST, 0);
- cfree_cg_drop(cg);
- break;
- }
- case WASM_INSN_I32_ATOMIC_WAIT:
- case WASM_INSN_I64_ATOMIC_WAIT: {
- WasmValType vt = wasm_atomic_value_type(in.kind);
- CfreeCgTypeId ty = wasm_cg_type(c, b, vt);
- CfreeCgMemAccess mem = wasm_cg_mem_type(ty);
- CfreeCgLocalAttrs attrs;
- CfreeCgLocal timeout_tmp, expected_tmp, result_tmp;
- CfreeCgLabel equal = cfree_cg_label_new(cg);
- CfreeCgLabel done = cfree_cg_label_new(cg);
- memset(&attrs, 0, sizeof attrs);
- attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
- mem.align = in.align;
- timeout_tmp = cfree_cg_local(cg, b.id[CFREE_CG_BUILTIN_I64], attrs);
- expected_tmp = cfree_cg_local(cg, ty, attrs);
- result_tmp = cfree_cg_local(cg, b.id[CFREE_CG_BUILTIN_I32], attrs);
- cfree_cg_push_local(cg, timeout_tmp);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I64));
- cfree_cg_push_local(cg, expected_tmp);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, mem);
- wasm_cg_memory_check(c, cg, b, m, &rt, instance_local, &in);
- wasm_cg_memory_lvalue(cg, &rt, instance_local, in.memidx,
- in.offset64);
- cfree_cg_addr(cg);
- cfree_cg_atomic_load(cg, mem, CFREE_CG_MO_SEQ_CST);
- cfree_cg_push_local(cg, expected_tmp);
- cfree_cg_load(cg, mem);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_EQ);
- cfree_cg_branch_true(cg, equal);
- cfree_cg_push_local(cg, result_tmp);
- cfree_cg_push_int(cg, 1, b.id[CFREE_CG_BUILTIN_I32]);
- cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
- cfree_cg_jump(cg, done);
- cfree_cg_label_place(cg, equal);
- (void)timeout_tmp;
- cfree_cg_push_local(cg, result_tmp);
- cfree_cg_push_int(cg, 2, b.id[CFREE_CG_BUILTIN_I32]);
- cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
- cfree_cg_label_place(cg, done);
- cfree_cg_push_local(cg, result_tmp);
- cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I32));
- break;
- }
- case WASM_INSN_MEMORY_ATOMIC_NOTIFY: {
- CfreeCgMemAccess i32_mem = wasm_cg_mem(c, b, WASM_VAL_I32);
- CfreeCgLocalAttrs attrs;
- CfreeCgLocal count_tmp;
- memset(&attrs, 0, sizeof attrs);
- attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
- count_tmp = cfree_cg_local(cg, b.id[CFREE_CG_BUILTIN_I32], attrs);
- cfree_cg_push_local(cg, count_tmp);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, i32_mem);
- wasm_cg_memory_check(c, cg, b, m, &rt, instance_local, &in);
- cfree_cg_drop(cg);
- cfree_cg_push_int(cg, 0, b.id[CFREE_CG_BUILTIN_I32]);
- break;
- }
- case WASM_INSN_I32_LOAD:
- case WASM_INSN_I64_LOAD:
- case WASM_INSN_I32_LOAD8_S:
- case WASM_INSN_I32_LOAD8_U:
- case WASM_INSN_I32_LOAD16_S:
- case WASM_INSN_I32_LOAD16_U:
- case WASM_INSN_I64_LOAD8_S:
- case WASM_INSN_I64_LOAD8_U:
- case WASM_INSN_I64_LOAD16_S:
- case WASM_INSN_I64_LOAD16_U:
- case WASM_INSN_I64_LOAD32_S:
- case WASM_INSN_I64_LOAD32_U: {
- CfreeCgTypeId storage = wasm_load_storage_type(b, in.kind);
- CfreeCgTypeId result =
- wasm_cg_type(c, b, wasm_load_result_type(in.kind));
- CfreeCgMemAccess mem;
- memset(&mem, 0, sizeof mem);
- mem.type = storage;
- mem.align = in.align;
- wasm_cg_memory_check(c, cg, b, m, &rt, instance_local, &in);
- wasm_cg_memory_lvalue(cg, &rt, instance_local, in.memidx,
- in.offset64);
- cfree_cg_load(cg, mem);
- if (storage != result) {
- if (in.kind == WASM_INSN_I32_LOAD8_S ||
- in.kind == WASM_INSN_I32_LOAD16_S ||
- in.kind == WASM_INSN_I64_LOAD8_S ||
- in.kind == WASM_INSN_I64_LOAD16_S ||
- in.kind == WASM_INSN_I64_LOAD32_S)
- cfree_cg_sext(cg, result);
- else
- cfree_cg_zext(cg, result);
- }
- break;
- }
- case WASM_INSN_I32_STORE:
- case WASM_INSN_I64_STORE:
- case WASM_INSN_I32_STORE8:
- case WASM_INSN_I32_STORE16:
- case WASM_INSN_I64_STORE8:
- case WASM_INSN_I64_STORE16:
- case WASM_INSN_I64_STORE32: {
- CfreeCgTypeId storage = wasm_store_storage_type(b, in.kind);
- CfreeCgTypeId value_type =
- wasm_cg_type(c, b, wasm_store_value_type(in.kind));
- CfreeCgMemAccess mem;
- CfreeCgLocalAttrs attrs;
- CfreeCgLocal addr_tmp, value_tmp;
- WasmValType addr_vt =
- m->memories[in.memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32;
- memset(&mem, 0, sizeof mem);
- mem.type = storage;
- mem.align = in.align;
- if (storage != value_type) cfree_cg_trunc(cg, storage);
- memset(&attrs, 0, sizeof attrs);
- attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP;
- value_tmp = cfree_cg_local(cg, storage, attrs);
- addr_tmp = cfree_cg_local(cg, wasm_cg_type(c, b, addr_vt), attrs);
- cfree_cg_push_local(cg, value_tmp);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, mem);
- wasm_cg_memory_check(c, cg, b, m, &rt, instance_local, &in);
- cfree_cg_push_local(cg, addr_tmp);
- cfree_cg_swap(cg);
- cfree_cg_store(cg, wasm_cg_mem(c, b, addr_vt));
- cfree_cg_push_local(cg, addr_tmp);
- cfree_cg_load(cg, wasm_cg_mem(c, b, addr_vt));
- wasm_cg_memory_lvalue(cg, &rt, instance_local, in.memidx,
- in.offset64);
- cfree_cg_push_local(cg, value_tmp);
- cfree_cg_load(cg, mem);
- cfree_cg_store(cg, mem);
- break;
- }
- case WASM_INSN_I32_ADD:
- case WASM_INSN_I64_ADD:
- cfree_cg_int_binop(cg, CFREE_CG_INT_ADD, 0);
- break;
- case WASM_INSN_I32_SUB:
- case WASM_INSN_I64_SUB:
- cfree_cg_int_binop(cg, CFREE_CG_INT_SUB, 0);
- break;
- case WASM_INSN_I32_MUL:
- case WASM_INSN_I64_MUL:
- cfree_cg_int_binop(cg, CFREE_CG_INT_MUL, 0);
- break;
- case WASM_INSN_I32_DIV_S:
- wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I32,
- CFREE_CG_INT_SDIV);
- break;
- case WASM_INSN_I32_DIV_U:
- wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I32,
- CFREE_CG_INT_UDIV);
- break;
- case WASM_INSN_I32_REM_S:
- wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I32,
- CFREE_CG_INT_SREM);
- break;
- case WASM_INSN_I32_REM_U:
- wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I32,
- CFREE_CG_INT_UREM);
- break;
- case WASM_INSN_I64_DIV_S:
- wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I64,
- CFREE_CG_INT_SDIV);
- break;
- case WASM_INSN_I64_DIV_U:
- wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I64,
- CFREE_CG_INT_UDIV);
- break;
- case WASM_INSN_I64_REM_S:
- wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I64,
- CFREE_CG_INT_SREM);
- break;
- case WASM_INSN_I64_REM_U:
- wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I64,
- CFREE_CG_INT_UREM);
- break;
- case WASM_INSN_I32_AND:
- case WASM_INSN_I64_AND:
- cfree_cg_int_binop(cg, CFREE_CG_INT_AND, 0);
- break;
- case WASM_INSN_I32_OR:
- case WASM_INSN_I64_OR:
- cfree_cg_int_binop(cg, CFREE_CG_INT_OR, 0);
- break;
- case WASM_INSN_I32_XOR:
- case WASM_INSN_I64_XOR:
- cfree_cg_int_binop(cg, CFREE_CG_INT_XOR, 0);
- break;
- case WASM_INSN_I32_SHL:
- case WASM_INSN_I64_SHL:
- cfree_cg_int_binop(cg, CFREE_CG_INT_SHL, 0);
- break;
- case WASM_INSN_I32_SHR_S:
- case WASM_INSN_I64_SHR_S:
- cfree_cg_int_binop(cg, CFREE_CG_INT_ASHR, 0);
- break;
- case WASM_INSN_I32_SHR_U:
- case WASM_INSN_I64_SHR_U:
- cfree_cg_int_binop(cg, CFREE_CG_INT_LSHR, 0);
- break;
- case WASM_INSN_I32_ROTL:
- wasm_cg_rotate(c, cg, b, WASM_VAL_I32, 0);
- break;
- case WASM_INSN_I32_ROTR:
- wasm_cg_rotate(c, cg, b, WASM_VAL_I32, 1);
- break;
- case WASM_INSN_I64_ROTL:
- wasm_cg_rotate(c, cg, b, WASM_VAL_I64, 0);
- break;
- case WASM_INSN_I64_ROTR:
- wasm_cg_rotate(c, cg, b, WASM_VAL_I64, 1);
- break;
- case WASM_INSN_I32_CLZ:
- case WASM_INSN_I64_CLZ:
- cfree_cg_intrinsic(cg, CFREE_CG_INTRIN_CLZ, 1,
- in.kind == WASM_INSN_I32_CLZ
- ? b.id[CFREE_CG_BUILTIN_I32]
- : b.id[CFREE_CG_BUILTIN_I64]);
- break;
- case WASM_INSN_I32_CTZ:
- case WASM_INSN_I64_CTZ:
- cfree_cg_intrinsic(cg, CFREE_CG_INTRIN_CTZ, 1,
- in.kind == WASM_INSN_I32_CTZ
- ? b.id[CFREE_CG_BUILTIN_I32]
- : b.id[CFREE_CG_BUILTIN_I64]);
- break;
- case WASM_INSN_I32_POPCNT:
- case WASM_INSN_I64_POPCNT:
- cfree_cg_intrinsic(cg, CFREE_CG_INTRIN_POPCOUNT, 1,
- in.kind == WASM_INSN_I32_POPCNT
- ? b.id[CFREE_CG_BUILTIN_I32]
- : b.id[CFREE_CG_BUILTIN_I64]);
- break;
- case WASM_INSN_I32_EQZ:
- cfree_cg_push_int(cg, 0, b.id[CFREE_CG_BUILTIN_I32]);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_EQ);
- cfree_cg_zext(cg, b.id[CFREE_CG_BUILTIN_I32]);
- break;
- case WASM_INSN_I64_EQZ:
- cfree_cg_push_int(cg, 0, b.id[CFREE_CG_BUILTIN_I64]);
- cfree_cg_int_cmp(cg, CFREE_CG_INT_EQ);
- cfree_cg_zext(cg, b.id[CFREE_CG_BUILTIN_I32]);
- break;
- case WASM_INSN_I32_WRAP_I64:
- cfree_cg_trunc(cg, b.id[CFREE_CG_BUILTIN_I32]);
- break;
- case WASM_INSN_I64_EXTEND_I32_S:
- cfree_cg_sext(cg, b.id[CFREE_CG_BUILTIN_I64]);
- break;
- case WASM_INSN_I64_EXTEND_I32_U:
- cfree_cg_zext(cg, b.id[CFREE_CG_BUILTIN_I64]);
- break;
- case WASM_INSN_I32_TRUNC_F32_S:
- case WASM_INSN_I32_TRUNC_F64_S:
- wasm_cg_checked_trunc(c, cg, b, &rt,
- in.kind == WASM_INSN_I32_TRUNC_F32_S
- ? WASM_VAL_F32
- : WASM_VAL_F64,
- WASM_VAL_I32, 0);
- break;
- case WASM_INSN_I32_TRUNC_F32_U:
- case WASM_INSN_I32_TRUNC_F64_U:
- wasm_cg_checked_trunc(c, cg, b, &rt,
- in.kind == WASM_INSN_I32_TRUNC_F32_U
- ? WASM_VAL_F32
- : WASM_VAL_F64,
- WASM_VAL_I32, 1);
- break;
- case WASM_INSN_I64_TRUNC_F32_S:
- case WASM_INSN_I64_TRUNC_F64_S:
- wasm_cg_checked_trunc(c, cg, b, &rt,
- in.kind == WASM_INSN_I64_TRUNC_F32_S
- ? WASM_VAL_F32
- : WASM_VAL_F64,
- WASM_VAL_I64, 0);
- break;
- case WASM_INSN_I64_TRUNC_F32_U:
- case WASM_INSN_I64_TRUNC_F64_U:
- wasm_cg_checked_trunc(c, cg, b, &rt,
- in.kind == WASM_INSN_I64_TRUNC_F32_U
- ? WASM_VAL_F32
- : WASM_VAL_F64,
- WASM_VAL_I64, 1);
- break;
- case WASM_INSN_F32_CONVERT_I32_S:
- case WASM_INSN_F32_CONVERT_I64_S:
- cfree_cg_sint_to_float(cg, b.id[CFREE_CG_BUILTIN_F32],
- CFREE_CG_ROUND_DEFAULT);
- break;
- case WASM_INSN_F32_CONVERT_I32_U:
- case WASM_INSN_F32_CONVERT_I64_U:
- cfree_cg_uint_to_float(cg, b.id[CFREE_CG_BUILTIN_F32],
- CFREE_CG_ROUND_DEFAULT);
- break;
- case WASM_INSN_F64_CONVERT_I32_S:
- case WASM_INSN_F64_CONVERT_I64_S:
- cfree_cg_sint_to_float(cg, b.id[CFREE_CG_BUILTIN_F64],
- CFREE_CG_ROUND_DEFAULT);
- break;
- case WASM_INSN_F64_CONVERT_I32_U:
- case WASM_INSN_F64_CONVERT_I64_U:
- cfree_cg_uint_to_float(cg, b.id[CFREE_CG_BUILTIN_F64],
- CFREE_CG_ROUND_DEFAULT);
- break;
- case WASM_INSN_F32_DEMOTE_F64:
- cfree_cg_fptrunc(cg, b.id[CFREE_CG_BUILTIN_F32]);
- break;
- case WASM_INSN_F64_PROMOTE_F32:
- cfree_cg_fpext(cg, b.id[CFREE_CG_BUILTIN_F64]);
- break;
- case WASM_INSN_I32_REINTERPRET_F32:
- cfree_cg_bitcast(cg, b.id[CFREE_CG_BUILTIN_I32]);
- break;
- case WASM_INSN_I64_REINTERPRET_F64:
- cfree_cg_bitcast(cg, b.id[CFREE_CG_BUILTIN_I64]);
- break;
- case WASM_INSN_F32_REINTERPRET_I32:
- cfree_cg_bitcast(cg, b.id[CFREE_CG_BUILTIN_F32]);
- break;
- case WASM_INSN_F64_REINTERPRET_I64:
- cfree_cg_bitcast(cg, b.id[CFREE_CG_BUILTIN_F64]);
- break;
- default: {
- CfreeCgIntCmpOp cmp;
- CfreeCgFpBinOp fp_bin;
- CfreeCgFpCmpOp fp_cmp;
- if (wasm_int_cmp_op(in.kind, &cmp)) {
- cfree_cg_int_cmp(cg, cmp);
- cfree_cg_zext(cg, b.id[CFREE_CG_BUILTIN_I32]);
- } else if (wasm_fp_binop(in.kind, &fp_bin)) {
- cfree_cg_fp_binop(cg, fp_bin, CFREE_CG_FP_NONE);
- } else if (wasm_fp_cmp_op(in.kind, &fp_cmp)) {
- cfree_cg_fp_cmp(cg, fp_cmp);
- cfree_cg_zext(cg, b.id[CFREE_CG_BUILTIN_I32]);
- } else {
- wasm_error(c, wasm_loc(0, 0), "wasm: unsupported instruction");
- }
- break;
- }
- }
- }
- if (f->nresults)
- cfree_cg_ret(cg);
- else
- cfree_cg_ret_void(cg);
- cfree_cg_func_end(cg);
- }
- cfree_cg_free(cg);
-}
-
-static void write_byte(CfreeWriter* w, uint8_t b) { w->write(w, &b, 1); }
-
-static void write_uleb(CfreeWriter* w, uint64_t v) {
- do {
- uint8_t b = (uint8_t)(v & 0x7fu);
- v >>= 7u;
- if (v) b |= 0x80u;
- write_byte(w, b);
- } while (v);
-}
-
-static void write_sleb(CfreeWriter* w, int64_t v) {
- int more = 1;
- while (more) {
- uint8_t b = (uint8_t)(v & 0x7f);
- int sign = b & 0x40;
- v >>= 7;
- if ((v == 0 && !sign) || (v == -1 && sign))
- more = 0;
- else
- b |= 0x80u;
- write_byte(w, b);
- }
-}
-
-static void write_f32(CfreeWriter* w, double value) {
- float f = (float)value;
- uint32_t bits;
- memcpy(&bits, &f, sizeof bits);
- for (uint32_t i = 0; i < 4u; ++i) write_byte(w, (uint8_t)(bits >> (i * 8u)));
-}
-
-static void write_f64(CfreeWriter* w, double value) {
- uint64_t bits;
- memcpy(&bits, &value, sizeof bits);
- for (uint32_t i = 0; i < 8u; ++i) write_byte(w, (uint8_t)(bits >> (i * 8u)));
-}
-
-static void write_name(CfreeWriter* w, const char* s) {
- size_t n = strlen(s);
- write_uleb(w, n);
- w->write(w, s, n);
-}
-
-static void encode_section(CfreeHeap* h, CfreeWriter* out, uint8_t id,
- void (*fn)(CfreeWriter*, const WasmModule*),
- const WasmModule* m) {
- CfreeWriter* tmp = NULL;
- size_t len;
- const uint8_t* bytes;
- if (cfree_writer_mem(h, &tmp) != CFREE_OK) return;
- fn(tmp, m);
- bytes = cfree_writer_mem_bytes(tmp, &len);
- write_byte(out, id);
- write_uleb(out, len);
- out->write(out, bytes, len);
- cfree_writer_close(tmp);
-}
-
-static void enc_type(CfreeWriter* w, const WasmModule* m) {
- uint32_t i, j;
- write_uleb(w, m->ntypes);
- for (i = 0; i < m->ntypes; ++i) {
- const WasmFuncType* f = &m->types[i];
- write_byte(w, 0x60);
- write_uleb(w, f->nparams);
- for (j = 0; j < f->nparams; ++j) write_byte(w, (uint8_t)f->params[j]);
- write_uleb(w, f->nresults);
- for (j = 0; j < f->nresults; ++j) write_byte(w, (uint8_t)f->results[j]);
- }
-}
-
-static void write_memory_limits(CfreeWriter* w, const WasmMemory* mem) {
- uint32_t flags = (mem->has_max ? 1u : 0u) | (mem->shared ? 2u : 0u) |
- (mem->is64 ? 4u : 0u);
- write_uleb(w, flags);
- write_uleb(w, mem->min_pages);
- if (mem->has_max) write_uleb(w, mem->max_pages);
-}
-
-static void write_limits(CfreeWriter* w, uint32_t min, uint32_t max,
- int has_max) {
- write_uleb(w, has_max ? 1 : 0);
- write_uleb(w, min);
- if (has_max) write_uleb(w, max);
-}
-
-static void enc_import(CfreeWriter* w, const WasmModule* m) {
- uint32_t i, n = 0;
- for (i = 0; i < m->nfuncs; ++i)
- if (m->funcs[i].is_import) n++;
- for (i = 0; i < m->ntables; ++i)
- if (m->tables[i].is_import) n++;
- for (i = 0; i < m->nmemories; ++i)
- if (m->memories[i].is_import) n++;
- for (i = 0; i < m->nglobals; ++i)
- if (m->globals[i].is_import) n++;
- write_uleb(w, n);
- for (i = 0; i < m->nfuncs; ++i) {
- const WasmFunc* f = &m->funcs[i];
- if (!f->is_import) continue;
- write_name(w, f->import_module ? f->import_module : "");
- write_name(w, f->import_name ? f->import_name : "");
- write_byte(w, 0);
- write_uleb(w, f->typeidx);
- }
- for (i = 0; i < m->ntables; ++i) {
- const WasmTable* t = &m->tables[i];
- if (!t->is_import) continue;
- write_name(w, t->import_module ? t->import_module : "");
- write_name(w, t->import_name ? t->import_name : "");
- write_byte(w, 1);
- write_byte(w, (uint8_t)t->elem_type);
- write_limits(w, t->min, t->max, t->has_max);
- }
- for (i = 0; i < m->nmemories; ++i) {
- const WasmMemory* mem = &m->memories[i];
- if (!mem->is_import) continue;
- write_name(w, mem->import_module ? mem->import_module : "");
- write_name(w, mem->import_name ? mem->import_name : "");
- write_byte(w, 2);
- write_memory_limits(w, mem);
- }
- for (i = 0; i < m->nglobals; ++i) {
- const WasmGlobal* g = &m->globals[i];
- if (!g->is_import) continue;
- write_name(w, g->import_module ? g->import_module : "");
- write_name(w, g->import_name ? g->import_name : "");
- write_byte(w, 3);
- write_byte(w, (uint8_t)g->type);
- write_byte(w, g->mutable_);
- }
-}
-
-static int wasm_module_has_imports(const WasmModule* m) {
- uint32_t i;
- for (i = 0; i < m->nfuncs; ++i)
- if (m->funcs[i].is_import) return 1;
- for (i = 0; i < m->ntables; ++i)
- if (m->tables[i].is_import) return 1;
- for (i = 0; i < m->nmemories; ++i)
- if (m->memories[i].is_import) return 1;
- for (i = 0; i < m->nglobals; ++i)
- if (m->globals[i].is_import) return 1;
- return 0;
-}
-
-static void enc_func(CfreeWriter* w, const WasmModule* m) {
- uint32_t i;
- uint32_t n = 0;
- for (i = 0; i < m->nfuncs; ++i)
- if (!m->funcs[i].is_import) n++;
- write_uleb(w, n);
- for (i = 0; i < m->nfuncs; ++i)
- if (!m->funcs[i].is_import) write_uleb(w, m->funcs[i].typeidx);
-}
-
-static void enc_export(CfreeWriter* w, const WasmModule* m) {
- uint32_t i;
- write_uleb(w, m->nexports);
- for (i = 0; i < m->nexports; ++i) {
- write_name(w, m->exports[i].name ? m->exports[i].name : "");
- write_byte(w, m->exports[i].kind);
- write_uleb(w, m->exports[i].index);
- }
-}
-
-static void enc_memory(CfreeWriter* w, const WasmModule* m) {
- uint32_t i, n = 0;
- for (i = 0; i < m->nmemories; ++i)
- if (!m->memories[i].is_import) n++;
- write_uleb(w, n);
- for (i = 0; i < m->nmemories; ++i)
- if (!m->memories[i].is_import) write_memory_limits(w, &m->memories[i]);
-}
-
-static uint8_t wasm_opcode(uint8_t kind);
-
-static void enc_table(CfreeWriter* w, const WasmModule* m) {
- uint32_t i, n = 0;
- for (i = 0; i < m->ntables; ++i)
- if (!m->tables[i].is_import) n++;
- write_uleb(w, n);
- for (i = 0; i < m->ntables; ++i) {
- const WasmTable* t = &m->tables[i];
- if (t->is_import) continue;
- write_byte(w, (uint8_t)t->elem_type);
- write_limits(w, t->min, t->max, t->has_max);
- }
-}
-
-static void enc_global(CfreeWriter* w, const WasmModule* m) {
- uint32_t i, n = 0;
- for (i = 0; i < m->nglobals; ++i)
- if (!m->globals[i].is_import) n++;
- write_uleb(w, n);
- for (i = 0; i < m->nglobals; ++i) {
- const WasmGlobal* g = &m->globals[i];
- if (g->is_import) continue;
- write_byte(w, (uint8_t)g->type);
- write_byte(w, g->mutable_);
- write_byte(w, wasm_opcode(g->init.kind));
- if (g->init.kind == WASM_INSN_I32_CONST ||
- g->init.kind == WASM_INSN_I64_CONST)
- write_sleb(w, g->init.imm);
- else if (g->init.kind == WASM_INSN_F32_CONST)
- write_f32(w, g->init.fp);
- else if (g->init.kind == WASM_INSN_F64_CONST)
- write_f64(w, g->init.fp);
- write_byte(w, 0x0b);
- }
-}
-
-static uint8_t wasm_opcode(uint8_t kind) {
- switch (kind) {
- case WASM_INSN_UNREACHABLE:
- return 0x00;
- case WASM_INSN_NOP:
- return 0x01;
- case WASM_INSN_BLOCK:
- return 0x02;
- case WASM_INSN_LOOP:
- return 0x03;
- case WASM_INSN_IF:
- return 0x04;
- case WASM_INSN_ELSE:
- return 0x05;
- case WASM_INSN_END:
- return 0x0b;
- case WASM_INSN_BR:
- return 0x0c;
- case WASM_INSN_BR_IF:
- return 0x0d;
- case WASM_INSN_BR_TABLE:
- return 0x0e;
- case WASM_INSN_I32_CONST:
- return 0x41;
- case WASM_INSN_I64_CONST:
- return 0x42;
- case WASM_INSN_F32_CONST:
- return 0x43;
- case WASM_INSN_F64_CONST:
- return 0x44;
- case WASM_INSN_LOCAL_GET:
- return 0x20;
- case WASM_INSN_LOCAL_SET:
- return 0x21;
- case WASM_INSN_LOCAL_TEE:
- return 0x22;
- case WASM_INSN_CALL:
- return 0x10;
- case WASM_INSN_CALL_INDIRECT:
- return 0x11;
- case WASM_INSN_RETURN_CALL:
- return 0x12;
- case WASM_INSN_RETURN_CALL_INDIRECT:
- return 0x13;
- case WASM_INSN_CALL_REF:
- return 0x14;
- case WASM_INSN_RETURN_CALL_REF:
- return 0x15;
- case WASM_INSN_REF_NULL:
- return 0xd0;
- case WASM_INSN_REF_IS_NULL:
- return 0xd1;
- case WASM_INSN_REF_FUNC:
- return 0xd2;
- case WASM_INSN_GLOBAL_GET:
- return 0x23;
- case WASM_INSN_GLOBAL_SET:
- return 0x24;
- case WASM_INSN_RETURN:
- return 0x0f;
- case WASM_INSN_DROP:
- return 0x1a;
- case WASM_INSN_SELECT:
- return 0x1b;
- case WASM_INSN_I32_LOAD:
- return 0x28;
- case WASM_INSN_I64_LOAD:
- return 0x29;
- case WASM_INSN_I32_LOAD8_S:
- return 0x2c;
- case WASM_INSN_I32_LOAD8_U:
- return 0x2d;
- case WASM_INSN_I32_LOAD16_S:
- return 0x2e;
- case WASM_INSN_I32_LOAD16_U:
- return 0x2f;
- case WASM_INSN_I64_LOAD8_S:
- return 0x30;
- case WASM_INSN_I64_LOAD8_U:
- return 0x31;
- case WASM_INSN_I64_LOAD16_S:
- return 0x32;
- case WASM_INSN_I64_LOAD16_U:
- return 0x33;
- case WASM_INSN_I64_LOAD32_S:
- return 0x34;
- case WASM_INSN_I64_LOAD32_U:
- return 0x35;
- case WASM_INSN_I32_STORE:
- return 0x36;
- case WASM_INSN_I64_STORE:
- return 0x37;
- case WASM_INSN_I32_STORE8:
- return 0x3a;
- case WASM_INSN_I32_STORE16:
- return 0x3b;
- case WASM_INSN_I64_STORE8:
- return 0x3c;
- case WASM_INSN_I64_STORE16:
- return 0x3d;
- case WASM_INSN_I64_STORE32:
- return 0x3e;
- case WASM_INSN_MEMORY_SIZE:
- return 0x3f;
- case WASM_INSN_MEMORY_GROW:
- return 0x40;
- case WASM_INSN_I32_EQZ:
- return 0x45;
- case WASM_INSN_I32_EQ:
- return 0x46;
- case WASM_INSN_I32_NE:
- return 0x47;
- case WASM_INSN_I32_LT_S:
- return 0x48;
- case WASM_INSN_I32_LT_U:
- return 0x49;
- case WASM_INSN_I32_GT_S:
- return 0x4a;
- case WASM_INSN_I32_GT_U:
- return 0x4b;
- case WASM_INSN_I32_LE_S:
- return 0x4c;
- case WASM_INSN_I32_LE_U:
- return 0x4d;
- case WASM_INSN_I32_GE_S:
- return 0x4e;
- case WASM_INSN_I32_GE_U:
- return 0x4f;
- case WASM_INSN_I64_EQZ:
- return 0x50;
- case WASM_INSN_I64_EQ:
- return 0x51;
- case WASM_INSN_I64_NE:
- return 0x52;
- case WASM_INSN_I64_LT_S:
- return 0x53;
- case WASM_INSN_I64_LT_U:
- return 0x54;
- case WASM_INSN_I64_GT_S:
- return 0x55;
- case WASM_INSN_I64_GT_U:
- return 0x56;
- case WASM_INSN_I64_LE_S:
- return 0x57;
- case WASM_INSN_I64_LE_U:
- return 0x58;
- case WASM_INSN_I64_GE_S:
- return 0x59;
- case WASM_INSN_I64_GE_U:
- return 0x5a;
- case WASM_INSN_I32_ADD:
- return 0x6a;
- case WASM_INSN_I32_SUB:
- return 0x6b;
- case WASM_INSN_I32_MUL:
- return 0x6c;
- case WASM_INSN_I32_DIV_S:
- return 0x6d;
- case WASM_INSN_I32_DIV_U:
- return 0x6e;
- case WASM_INSN_I32_REM_S:
- return 0x6f;
- case WASM_INSN_I32_REM_U:
- return 0x70;
- case WASM_INSN_I32_AND:
- return 0x71;
- case WASM_INSN_I32_OR:
- return 0x72;
- case WASM_INSN_I32_XOR:
- return 0x73;
- case WASM_INSN_I32_SHL:
- return 0x74;
- case WASM_INSN_I32_SHR_S:
- return 0x75;
- case WASM_INSN_I32_SHR_U:
- return 0x76;
- case WASM_INSN_I32_CLZ:
- return 0x67;
- case WASM_INSN_I32_CTZ:
- return 0x68;
- case WASM_INSN_I32_POPCNT:
- return 0x69;
- case WASM_INSN_I32_ROTL:
- return 0x77;
- case WASM_INSN_I32_ROTR:
- return 0x78;
- case WASM_INSN_I64_CLZ:
- return 0x79;
- case WASM_INSN_I64_CTZ:
- return 0x7a;
- case WASM_INSN_I64_POPCNT:
- return 0x7b;
- case WASM_INSN_I64_ADD:
- return 0x7c;
- case WASM_INSN_I64_SUB:
- return 0x7d;
- case WASM_INSN_I64_MUL:
- return 0x7e;
- case WASM_INSN_I64_DIV_S:
- return 0x7f;
- case WASM_INSN_I64_DIV_U:
- return 0x80;
- case WASM_INSN_I64_REM_S:
- return 0x81;
- case WASM_INSN_I64_REM_U:
- return 0x82;
- case WASM_INSN_I64_AND:
- return 0x83;
- case WASM_INSN_I64_OR:
- return 0x84;
- case WASM_INSN_I64_XOR:
- return 0x85;
- case WASM_INSN_I64_SHL:
- return 0x86;
- case WASM_INSN_I64_SHR_S:
- return 0x87;
- case WASM_INSN_I64_SHR_U:
- return 0x88;
- case WASM_INSN_I64_ROTL:
- return 0x89;
- case WASM_INSN_I64_ROTR:
- return 0x8a;
- case WASM_INSN_F32_ADD:
- return 0x92;
- case WASM_INSN_F32_SUB:
- return 0x93;
- case WASM_INSN_F32_MUL:
- return 0x94;
- case WASM_INSN_F32_DIV:
- return 0x95;
- case WASM_INSN_F64_ADD:
- return 0xa0;
- case WASM_INSN_F64_SUB:
- return 0xa1;
- case WASM_INSN_F64_MUL:
- return 0xa2;
- case WASM_INSN_F64_DIV:
- return 0xa3;
- case WASM_INSN_F32_EQ:
- return 0x5b;
- case WASM_INSN_F32_NE:
- return 0x5c;
- case WASM_INSN_F32_LT:
- return 0x5d;
- case WASM_INSN_F32_GT:
- return 0x5e;
- case WASM_INSN_F32_LE:
- return 0x5f;
- case WASM_INSN_F32_GE:
- return 0x60;
- case WASM_INSN_F64_EQ:
- return 0x61;
- case WASM_INSN_F64_NE:
- return 0x62;
- case WASM_INSN_F64_LT:
- return 0x63;
- case WASM_INSN_F64_GT:
- return 0x64;
- case WASM_INSN_F64_LE:
- return 0x65;
- case WASM_INSN_F64_GE:
- return 0x66;
- case WASM_INSN_I32_WRAP_I64:
- return 0xa7;
- case WASM_INSN_I32_TRUNC_F32_S:
- return 0xa8;
- case WASM_INSN_I32_TRUNC_F32_U:
- return 0xa9;
- case WASM_INSN_I32_TRUNC_F64_S:
- return 0xaa;
- case WASM_INSN_I32_TRUNC_F64_U:
- return 0xab;
- case WASM_INSN_I64_EXTEND_I32_S:
- return 0xac;
- case WASM_INSN_I64_EXTEND_I32_U:
- return 0xad;
- case WASM_INSN_I64_TRUNC_F32_S:
- return 0xae;
- case WASM_INSN_I64_TRUNC_F32_U:
- return 0xaf;
- case WASM_INSN_I64_TRUNC_F64_S:
- return 0xb0;
- case WASM_INSN_I64_TRUNC_F64_U:
- return 0xb1;
- case WASM_INSN_F32_CONVERT_I32_S:
- return 0xb2;
- case WASM_INSN_F32_CONVERT_I32_U:
- return 0xb3;
- case WASM_INSN_F32_CONVERT_I64_S:
- return 0xb4;
- case WASM_INSN_F32_CONVERT_I64_U:
- return 0xb5;
- case WASM_INSN_F32_DEMOTE_F64:
- return 0xb6;
- case WASM_INSN_F64_CONVERT_I32_S:
- return 0xb7;
- case WASM_INSN_F64_CONVERT_I32_U:
- return 0xb8;
- case WASM_INSN_F64_CONVERT_I64_S:
- return 0xb9;
- case WASM_INSN_F64_CONVERT_I64_U:
- return 0xba;
- case WASM_INSN_F64_PROMOTE_F32:
- return 0xbb;
- case WASM_INSN_I32_REINTERPRET_F32:
- return 0xbc;
- case WASM_INSN_I64_REINTERPRET_F64:
- return 0xbd;
- case WASM_INSN_F32_REINTERPRET_I32:
- return 0xbe;
- case WASM_INSN_F64_REINTERPRET_I64:
- return 0xbf;
- }
- return 0;
-}
-
-static uint32_t wasm_threads_subopcode(uint8_t kind) {
- switch (kind) {
- case WASM_INSN_MEMORY_ATOMIC_NOTIFY:
- return 0x00;
- case WASM_INSN_I32_ATOMIC_WAIT:
- return 0x01;
- case WASM_INSN_I64_ATOMIC_WAIT:
- return 0x02;
- case WASM_INSN_ATOMIC_FENCE:
- return 0x03;
- case WASM_INSN_I32_ATOMIC_LOAD:
- return 0x10;
- case WASM_INSN_I64_ATOMIC_LOAD:
- return 0x11;
- case WASM_INSN_I32_ATOMIC_LOAD8_U:
- return 0x12;
- case WASM_INSN_I32_ATOMIC_LOAD16_U:
- return 0x13;
- case WASM_INSN_I64_ATOMIC_LOAD8_U:
- return 0x14;
- case WASM_INSN_I64_ATOMIC_LOAD16_U:
- return 0x15;
- case WASM_INSN_I64_ATOMIC_LOAD32_U:
- return 0x16;
- case WASM_INSN_I32_ATOMIC_STORE:
- return 0x17;
- case WASM_INSN_I64_ATOMIC_STORE:
- return 0x18;
- case WASM_INSN_I32_ATOMIC_STORE8:
- return 0x19;
- case WASM_INSN_I32_ATOMIC_STORE16:
- return 0x1a;
- case WASM_INSN_I64_ATOMIC_STORE8:
- return 0x1b;
- case WASM_INSN_I64_ATOMIC_STORE16:
- return 0x1c;
- case WASM_INSN_I64_ATOMIC_STORE32:
- return 0x1d;
- case WASM_INSN_I32_ATOMIC_RMW_ADD:
- return 0x1e;
- case WASM_INSN_I64_ATOMIC_RMW_ADD:
- return 0x1f;
- case WASM_INSN_I32_ATOMIC_RMW_SUB:
- return 0x25;
- case WASM_INSN_I64_ATOMIC_RMW_SUB:
- return 0x26;
- case WASM_INSN_I32_ATOMIC_RMW_AND:
- return 0x2c;
- case WASM_INSN_I64_ATOMIC_RMW_AND:
- return 0x2d;
- case WASM_INSN_I32_ATOMIC_RMW_OR:
- return 0x33;
- case WASM_INSN_I64_ATOMIC_RMW_OR:
- return 0x34;
- case WASM_INSN_I32_ATOMIC_RMW_XOR:
- return 0x3a;
- case WASM_INSN_I64_ATOMIC_RMW_XOR:
- return 0x3b;
- case WASM_INSN_I32_ATOMIC_RMW_XCHG:
- return 0x41;
- case WASM_INSN_I64_ATOMIC_RMW_XCHG:
- return 0x42;
- case WASM_INSN_I32_ATOMIC_RMW_CMPXCHG:
- return 0x48;
- case WASM_INSN_I64_ATOMIC_RMW_CMPXCHG:
- return 0x49;
- default:
- return UINT32_MAX;
- }
-}
-
-static void enc_code(CfreeWriter* w, const WasmModule* m) {
- uint32_t i, j;
- uint32_t n = 0;
- for (i = 0; i < m->nfuncs; ++i)
- if (!m->funcs[i].is_import) n++;
- write_uleb(w, n);
- for (i = 0; i < m->nfuncs; ++i) {
- CfreeWriter* body = NULL;
- size_t len;
- const uint8_t* bytes;
- if (cfree_writer_mem(m->heap, &body) != CFREE_OK) continue;
- if (m->funcs[i].is_import) {
- cfree_writer_close(body);
- continue;
- }
- if (m->funcs[i].nlocals) {
- uint32_t group_count = 0;
- WasmValType prev = 0;
- for (j = 0; j < m->funcs[i].nlocals; ++j) {
- if (j == 0 || m->funcs[i].locals[j] != prev) {
- group_count++;
- prev = m->funcs[i].locals[j];
- }
- }
- write_uleb(body, group_count);
- for (j = 0; j < m->funcs[i].nlocals;) {
- uint32_t k = j + 1u;
- while (k < m->funcs[i].nlocals &&
- m->funcs[i].locals[k] == m->funcs[i].locals[j])
- k++;
- write_uleb(body, k - j);
- write_byte(body, (uint8_t)m->funcs[i].locals[j]);
- j = k;
- }
- } else {
- write_uleb(body, 0);
- }
- for (j = 0; j < m->funcs[i].ninsns; ++j) {
- WasmInsn in = m->funcs[i].insns[j];
- uint8_t op = wasm_opcode(in.kind);
- uint32_t threads_op = wasm_threads_subopcode(in.kind);
- if (threads_op != UINT32_MAX) {
- write_byte(body, 0xfe);
- write_uleb(body, threads_op);
- } else {
- write_byte(body, op);
- }
- if (in.kind == WASM_INSN_ATOMIC_FENCE) {
- write_byte(body, 0);
- } else if (in.kind == WASM_INSN_BLOCK || in.kind == WASM_INSN_LOOP ||
- in.kind == WASM_INSN_IF)
- write_byte(body, 0x40);
- else if (in.kind == WASM_INSN_I32_CONST || in.kind == WASM_INSN_I64_CONST)
- write_sleb(body, in.imm);
- else if (in.kind == WASM_INSN_F32_CONST)
- write_f32(body, in.fp);
- else if (in.kind == WASM_INSN_F64_CONST)
- write_f64(body, in.fp);
- else if (in.kind == WASM_INSN_LOCAL_GET ||
- in.kind == WASM_INSN_LOCAL_SET ||
- in.kind == WASM_INSN_LOCAL_TEE ||
- in.kind == WASM_INSN_GLOBAL_GET ||
- in.kind == WASM_INSN_GLOBAL_SET || in.kind == WASM_INSN_CALL ||
- in.kind == WASM_INSN_RETURN_CALL ||
- in.kind == WASM_INSN_CALL_REF ||
- in.kind == WASM_INSN_RETURN_CALL_REF ||
- in.kind == WASM_INSN_REF_NULL ||
- in.kind == WASM_INSN_REF_FUNC ||
- in.kind == WASM_INSN_BR || in.kind == WASM_INSN_BR_IF)
- write_uleb(body, (uint64_t)in.imm);
- else if (in.kind == WASM_INSN_CALL_INDIRECT ||
- in.kind == WASM_INSN_RETURN_CALL_INDIRECT) {
- write_uleb(body, (uint64_t)in.imm);
- write_uleb(body, in.align);
- } else if (in.kind == WASM_INSN_BR_TABLE) {
- write_uleb(body, in.ntargets ? in.ntargets - 1u : 0u);
- for (uint32_t k = 0; k < in.ntargets; ++k)
- write_uleb(body, in.targets[k]);
- } else if (wasm_insn_is_mem(in.kind)) {
- write_uleb(body, in.memidx ? in.align + 64u : in.align);
- if (in.memidx) write_uleb(body, in.memidx);
- write_uleb(body, in.offset64);
- } else if (in.kind == WASM_INSN_MEMORY_SIZE ||
- in.kind == WASM_INSN_MEMORY_GROW) {
- write_uleb(body, in.memidx);
- }
- }
- write_byte(body, 0x0b);
- bytes = cfree_writer_mem_bytes(body, &len);
- write_uleb(w, len);
- w->write(w, bytes, len);
- cfree_writer_close(body);
- }
-}
-
-static void enc_data(CfreeWriter* w, const WasmModule* m) {
- uint32_t i, n = 0;
- for (i = 0; i < m->nmemories; ++i)
- if (m->memories[i].data_init_len) n++;
- write_uleb(w, n);
- for (i = 0; i < m->nmemories; ++i) {
- const WasmMemory* mem = &m->memories[i];
- if (!mem->data_init_len) continue;
- write_uleb(w, i ? 2u : 0u);
- if (i) write_uleb(w, i);
- write_byte(w, mem->is64 ? 0x42 : 0x41);
- write_sleb(w, 0);
- write_byte(w, 0x0b);
- write_uleb(w, mem->data_init_len);
- w->write(w, mem->data, (size_t)mem->data_init_len);
- }
-}
-
-static void enc_elem(CfreeWriter* w, const WasmModule* m) {
- uint32_t i, j;
- write_uleb(w, m->nelems);
- for (i = 0; i < m->nelems; ++i) {
- const WasmElemSegment* e = &m->elems[i];
- write_uleb(w, 0);
- write_byte(w, 0x41);
- write_sleb(w, e->offset);
- write_byte(w, 0x0b);
- write_uleb(w, e->nfuncs);
- for (j = 0; j < e->nfuncs; ++j) write_uleb(w, e->funcs[j]);
- }
-}
-
-static void enc_start(CfreeWriter* w, const WasmModule* m) {
- write_uleb(w, m->start_func);
-}
-
-static void encode_custom(CfreeHeap* h, CfreeWriter* out,
- const WasmCustom* cs) {
- CfreeWriter* tmp = NULL;
- size_t len;
- const uint8_t* bytes;
- if (cfree_writer_mem(h, &tmp) != CFREE_OK) return;
- write_name(tmp, cs->name ? cs->name : "");
- if (cs->len) tmp->write(tmp, cs->data, cs->len);
- bytes = cfree_writer_mem_bytes(tmp, &len);
- write_byte(out, 0);
- write_uleb(out, len);
- out->write(out, bytes, len);
- cfree_writer_close(tmp);
-}
-
-static void wasm_encode(CfreeCompiler* c, const WasmModule* m,
- CfreeWriter* out) {
- static const uint8_t magic[] = {0x00, 0x61, 0x73, 0x6d,
- 0x01, 0x00, 0x00, 0x00};
- CfreeHeap* h = cfree_compiler_context(c)->heap;
- out->write(out, magic, sizeof magic);
- for (uint32_t i = 0; i < m->ncustoms; ++i)
- encode_custom(h, out, &m->customs[i]);
- encode_section(h, out, 1, enc_type, m);
- if (wasm_module_has_imports(m)) encode_section(h, out, 2, enc_import, m);
- encode_section(h, out, 3, enc_func, m);
- if (m->ntables) encode_section(h, out, 4, enc_table, m);
- {
- int has_defined_memory = 0;
- for (uint32_t i = 0; i < m->nmemories; ++i)
- if (!m->memories[i].is_import) has_defined_memory = 1;
- if (has_defined_memory) encode_section(h, out, 5, enc_memory, m);
- }
- if (m->nglobals) encode_section(h, out, 6, enc_global, m);
- encode_section(h, out, 7, enc_export, m);
- if (m->has_start) encode_section(h, out, 8, enc_start, m);
- if (m->nelems) encode_section(h, out, 9, enc_elem, m);
- encode_section(h, out, 10, enc_code, m);
- {
- int has_data = 0;
- for (uint32_t i = 0; i < m->nmemories; ++i)
- if (m->memories[i].data_init_len) has_data = 1;
- if (has_data) encode_section(h, out, 11, enc_data, m);
- }
-}
+#include "wasm_internal.h"
static void wasm_parse_any(CfreeCompiler* c, const CfreeBytes* input,
WasmModule* m) {
if (wasm_is_binary(input))
wasm_decode_binary(c, input, m);
else
- wat_parse_module(c, input, m);
+ wasm_parse_wat(c, input, m);
wasm_validate(m, c);
}
@@ -8332,7 +31,7 @@ int cfree_wasm_wat_to_wasm(CfreeCompiler* c, const CfreeBytes* input,
CfreeWriter* out) {
WasmModule m;
wasm_module_init(&m, cfree_compiler_context(c)->heap);
- wat_parse_module(c, input, &m);
+ wasm_parse_wat(c, input, &m);
wasm_validate(&m, c);
wasm_encode(c, &m, out);
wasm_module_free(&m);
diff --git a/lang/wasm/wasm_internal.h b/lang/wasm/wasm_internal.h
@@ -0,0 +1,416 @@
+#ifndef CFREE_LANG_WASM_INTERNAL_H
+#define CFREE_LANG_WASM_INTERNAL_H
+
+#include "wasm.h"
+
+#include <cfree/cg.h>
+#include <cfree/frontend.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef enum WasmValType {
+ WASM_VAL_I32 = 0x7f,
+ WASM_VAL_I64 = 0x7e,
+ WASM_VAL_F32 = 0x7d,
+ WASM_VAL_F64 = 0x7c,
+ WASM_VAL_FUNCREF = 0x70,
+ WASM_VAL_EXTERNREF = 0x6f,
+} WasmValType;
+
+typedef enum WasmFeatureSet {
+ WASM_FEATURE_THREADS = 1u << 0,
+ WASM_FEATURE_TYPED_FUNC_REFS = 1u << 1,
+ WASM_FEATURE_TAIL_CALLS = 1u << 2,
+ WASM_FEATURE_MULTI_MEMORY = 1u << 3,
+ WASM_FEATURE_MEMORY64 = 1u << 4,
+} WasmFeatureSet;
+
+typedef enum WasmInsnKind {
+ WASM_INSN_UNREACHABLE,
+ WASM_INSN_NOP,
+ WASM_INSN_BLOCK,
+ WASM_INSN_LOOP,
+ WASM_INSN_IF,
+ WASM_INSN_ELSE,
+ WASM_INSN_END,
+ WASM_INSN_BR,
+ WASM_INSN_BR_IF,
+ WASM_INSN_BR_TABLE,
+ WASM_INSN_SELECT,
+ WASM_INSN_F32_CONST,
+ WASM_INSN_F64_CONST,
+ WASM_INSN_I32_CONST,
+ WASM_INSN_I64_CONST,
+ WASM_INSN_LOCAL_GET,
+ WASM_INSN_LOCAL_SET,
+ WASM_INSN_LOCAL_TEE,
+ WASM_INSN_CALL,
+ WASM_INSN_CALL_INDIRECT,
+ WASM_INSN_RETURN_CALL,
+ WASM_INSN_RETURN_CALL_INDIRECT,
+ WASM_INSN_REF_NULL,
+ WASM_INSN_REF_FUNC,
+ WASM_INSN_REF_IS_NULL,
+ WASM_INSN_CALL_REF,
+ WASM_INSN_RETURN_CALL_REF,
+ WASM_INSN_GLOBAL_GET,
+ WASM_INSN_GLOBAL_SET,
+ WASM_INSN_RETURN,
+ WASM_INSN_DROP,
+ WASM_INSN_I32_LOAD,
+ WASM_INSN_I64_LOAD,
+ WASM_INSN_I32_LOAD8_S,
+ WASM_INSN_I32_LOAD8_U,
+ WASM_INSN_I32_LOAD16_S,
+ WASM_INSN_I32_LOAD16_U,
+ WASM_INSN_I64_LOAD8_S,
+ WASM_INSN_I64_LOAD8_U,
+ WASM_INSN_I64_LOAD16_S,
+ WASM_INSN_I64_LOAD16_U,
+ WASM_INSN_I64_LOAD32_S,
+ WASM_INSN_I64_LOAD32_U,
+ WASM_INSN_I32_STORE,
+ WASM_INSN_I64_STORE,
+ WASM_INSN_I32_STORE8,
+ WASM_INSN_I32_STORE16,
+ WASM_INSN_I64_STORE8,
+ WASM_INSN_I64_STORE16,
+ WASM_INSN_I64_STORE32,
+ WASM_INSN_MEMORY_SIZE,
+ WASM_INSN_MEMORY_GROW,
+ WASM_INSN_ATOMIC_FENCE,
+ WASM_INSN_I32_ATOMIC_LOAD,
+ WASM_INSN_I64_ATOMIC_LOAD,
+ WASM_INSN_I32_ATOMIC_LOAD8_U,
+ WASM_INSN_I32_ATOMIC_LOAD16_U,
+ WASM_INSN_I64_ATOMIC_LOAD8_U,
+ WASM_INSN_I64_ATOMIC_LOAD16_U,
+ WASM_INSN_I64_ATOMIC_LOAD32_U,
+ WASM_INSN_I32_ATOMIC_STORE,
+ WASM_INSN_I64_ATOMIC_STORE,
+ WASM_INSN_I32_ATOMIC_STORE8,
+ WASM_INSN_I32_ATOMIC_STORE16,
+ WASM_INSN_I64_ATOMIC_STORE8,
+ WASM_INSN_I64_ATOMIC_STORE16,
+ WASM_INSN_I64_ATOMIC_STORE32,
+ WASM_INSN_I32_ATOMIC_RMW_ADD,
+ WASM_INSN_I64_ATOMIC_RMW_ADD,
+ WASM_INSN_I32_ATOMIC_RMW_SUB,
+ WASM_INSN_I64_ATOMIC_RMW_SUB,
+ WASM_INSN_I32_ATOMIC_RMW_AND,
+ WASM_INSN_I64_ATOMIC_RMW_AND,
+ WASM_INSN_I32_ATOMIC_RMW_OR,
+ WASM_INSN_I64_ATOMIC_RMW_OR,
+ WASM_INSN_I32_ATOMIC_RMW_XOR,
+ WASM_INSN_I64_ATOMIC_RMW_XOR,
+ WASM_INSN_I32_ATOMIC_RMW_XCHG,
+ WASM_INSN_I64_ATOMIC_RMW_XCHG,
+ WASM_INSN_I32_ATOMIC_RMW_CMPXCHG,
+ WASM_INSN_I64_ATOMIC_RMW_CMPXCHG,
+ WASM_INSN_I32_ATOMIC_WAIT,
+ WASM_INSN_I64_ATOMIC_WAIT,
+ WASM_INSN_MEMORY_ATOMIC_NOTIFY,
+ WASM_INSN_I32_ADD,
+ WASM_INSN_I32_SUB,
+ WASM_INSN_I32_MUL,
+ WASM_INSN_I32_DIV_S,
+ WASM_INSN_I32_DIV_U,
+ WASM_INSN_I32_REM_S,
+ WASM_INSN_I32_REM_U,
+ WASM_INSN_I32_AND,
+ WASM_INSN_I32_OR,
+ WASM_INSN_I32_XOR,
+ WASM_INSN_I32_SHL,
+ WASM_INSN_I32_SHR_S,
+ WASM_INSN_I32_SHR_U,
+ WASM_INSN_I32_ROTL,
+ WASM_INSN_I32_ROTR,
+ WASM_INSN_I32_CLZ,
+ WASM_INSN_I32_CTZ,
+ WASM_INSN_I32_POPCNT,
+ WASM_INSN_I32_EQZ,
+ WASM_INSN_I32_EQ,
+ WASM_INSN_I32_NE,
+ WASM_INSN_I32_LT_S,
+ WASM_INSN_I32_LT_U,
+ WASM_INSN_I32_GT_S,
+ WASM_INSN_I32_GT_U,
+ WASM_INSN_I32_LE_S,
+ WASM_INSN_I32_LE_U,
+ WASM_INSN_I32_GE_S,
+ WASM_INSN_I32_GE_U,
+ WASM_INSN_I64_ADD,
+ WASM_INSN_I64_SUB,
+ WASM_INSN_I64_MUL,
+ WASM_INSN_I64_DIV_S,
+ WASM_INSN_I64_DIV_U,
+ WASM_INSN_I64_REM_S,
+ WASM_INSN_I64_REM_U,
+ WASM_INSN_I64_AND,
+ WASM_INSN_I64_OR,
+ WASM_INSN_I64_XOR,
+ WASM_INSN_I64_SHL,
+ WASM_INSN_I64_SHR_S,
+ WASM_INSN_I64_SHR_U,
+ WASM_INSN_I64_ROTL,
+ WASM_INSN_I64_ROTR,
+ WASM_INSN_I64_CLZ,
+ WASM_INSN_I64_CTZ,
+ WASM_INSN_I64_POPCNT,
+ WASM_INSN_I64_EQZ,
+ WASM_INSN_I64_EQ,
+ WASM_INSN_I64_NE,
+ WASM_INSN_I64_LT_S,
+ WASM_INSN_I64_LT_U,
+ WASM_INSN_I64_GT_S,
+ WASM_INSN_I64_GT_U,
+ WASM_INSN_I64_LE_S,
+ WASM_INSN_I64_LE_U,
+ WASM_INSN_I64_GE_S,
+ WASM_INSN_I64_GE_U,
+ WASM_INSN_F32_ADD,
+ WASM_INSN_F32_SUB,
+ WASM_INSN_F32_MUL,
+ WASM_INSN_F32_DIV,
+ WASM_INSN_F32_EQ,
+ WASM_INSN_F32_NE,
+ WASM_INSN_F32_LT,
+ WASM_INSN_F32_GT,
+ WASM_INSN_F32_LE,
+ WASM_INSN_F32_GE,
+ WASM_INSN_F64_ADD,
+ WASM_INSN_F64_SUB,
+ WASM_INSN_F64_MUL,
+ WASM_INSN_F64_DIV,
+ WASM_INSN_F64_EQ,
+ WASM_INSN_F64_NE,
+ WASM_INSN_F64_LT,
+ WASM_INSN_F64_GT,
+ WASM_INSN_F64_LE,
+ WASM_INSN_F64_GE,
+ WASM_INSN_I32_WRAP_I64,
+ WASM_INSN_I32_TRUNC_F32_S,
+ WASM_INSN_I32_TRUNC_F32_U,
+ WASM_INSN_I32_TRUNC_F64_S,
+ WASM_INSN_I32_TRUNC_F64_U,
+ WASM_INSN_I64_EXTEND_I32_S,
+ WASM_INSN_I64_EXTEND_I32_U,
+ WASM_INSN_I64_TRUNC_F32_S,
+ WASM_INSN_I64_TRUNC_F32_U,
+ WASM_INSN_I64_TRUNC_F64_S,
+ WASM_INSN_I64_TRUNC_F64_U,
+ WASM_INSN_F32_CONVERT_I32_S,
+ WASM_INSN_F32_CONVERT_I32_U,
+ WASM_INSN_F32_CONVERT_I64_S,
+ WASM_INSN_F32_CONVERT_I64_U,
+ WASM_INSN_F32_DEMOTE_F64,
+ WASM_INSN_F64_CONVERT_I32_S,
+ WASM_INSN_F64_CONVERT_I32_U,
+ WASM_INSN_F64_CONVERT_I64_S,
+ WASM_INSN_F64_CONVERT_I64_U,
+ WASM_INSN_F64_PROMOTE_F32,
+ WASM_INSN_I32_REINTERPRET_F32,
+ WASM_INSN_I64_REINTERPRET_F64,
+ WASM_INSN_F32_REINTERPRET_I32,
+ WASM_INSN_F64_REINTERPRET_I64,
+} WasmInsnKind;
+
+typedef struct WasmInsn {
+ uint8_t kind;
+ uint8_t type;
+ int64_t imm;
+ double fp;
+ uint32_t align;
+ uint32_t memidx;
+ uint64_t offset64;
+ uint32_t ntargets;
+ uint32_t targets[16];
+} WasmInsn;
+
+typedef struct WasmFunc {
+ char* name;
+ uint32_t typeidx;
+ int has_typeidx;
+ int is_import;
+ char* import_module;
+ char* import_name;
+ WasmValType params[16];
+ uint32_t nparams;
+ WasmValType locals[32];
+ uint32_t nlocals;
+ char* local_names[48];
+ WasmValType results[1];
+ uint32_t nresults;
+ char* export_name;
+ WasmInsn* insns;
+ uint32_t ninsns;
+ uint32_t cap_insns;
+} WasmFunc;
+
+typedef struct WasmFuncType {
+ char* name;
+ WasmValType params[16];
+ uint32_t nparams;
+ WasmValType results[1];
+ uint32_t nresults;
+} WasmFuncType;
+
+typedef struct WasmMemory {
+ char* name;
+ uint64_t min_pages;
+ uint64_t max_pages;
+ int has_max;
+ int is64;
+ int shared;
+ int is_import;
+ char* import_module;
+ char* import_name;
+ char* export_name;
+ uint8_t* data;
+ uint64_t data_len;
+ uint64_t data_init_len;
+} WasmMemory;
+
+typedef struct WasmTable {
+ char* name;
+ WasmValType elem_type;
+ uint32_t min;
+ uint32_t max;
+ int has_max;
+ int is_import;
+ char* import_module;
+ char* import_name;
+ char* export_name;
+} WasmTable;
+
+typedef struct WasmGlobal {
+ char* name;
+ WasmValType type;
+ uint8_t mutable_;
+ WasmInsn init;
+ int is_import;
+ char* import_module;
+ char* import_name;
+ char* export_name;
+} WasmGlobal;
+
+typedef struct WasmElemSegment {
+ uint32_t tableidx;
+ int64_t offset;
+ uint32_t funcs[64];
+ uint32_t nfuncs;
+} WasmElemSegment;
+
+typedef struct WasmExport {
+ char* name;
+ uint8_t kind;
+ uint32_t index;
+} WasmExport;
+
+typedef struct WasmCustom {
+ char* name;
+ uint8_t* data;
+ uint32_t len;
+} WasmCustom;
+
+typedef struct WasmModule {
+ CfreeHeap* heap;
+ WasmFuncType* types;
+ uint32_t ntypes;
+ uint32_t cap_types;
+ WasmFunc* funcs;
+ uint32_t nfuncs;
+ uint32_t cap_funcs;
+ WasmMemory* memories;
+ uint32_t nmemories;
+ uint32_t cap_memories;
+ WasmTable* tables;
+ uint32_t ntables;
+ uint32_t cap_tables;
+ WasmGlobal* globals;
+ uint32_t nglobals;
+ uint32_t cap_globals;
+ WasmElemSegment* elems;
+ uint32_t nelems;
+ uint32_t cap_elems;
+ WasmExport* exports;
+ uint32_t nexports;
+ uint32_t cap_exports;
+ WasmCustom* customs;
+ uint32_t ncustoms;
+ uint32_t cap_customs;
+ uint32_t start_func;
+ int has_start;
+ int has_target_features;
+ uint32_t features;
+} WasmModule;
+
+CfreeSrcLoc wasm_loc(uint32_t line, uint32_t col);
+void wasm_error(CfreeCompiler* c, CfreeSrcLoc loc, const char* fmt, ...);
+void* wasm_realloc(CfreeHeap* h, void* p, size_t old_n, size_t new_n);
+char* wasm_strdup(CfreeHeap* h, const char* s, size_t len);
+void wasm_free_str(CfreeHeap* h, char** s);
+
+void wasm_module_init(WasmModule* m, CfreeHeap* heap);
+void wasm_module_free(WasmModule* m);
+void wasm_memory_ensure(CfreeCompiler* c, WasmModule* m, uint32_t memidx,
+ uint64_t needed);
+WasmMemory* wasm_add_memory(CfreeCompiler* c, WasmModule* m);
+WasmFunc* wasm_add_func(CfreeCompiler* c, WasmModule* m);
+WasmFuncType* wasm_add_type(CfreeCompiler* c, WasmModule* m);
+uint32_t wasm_intern_func_type(CfreeCompiler* c, WasmModule* m,
+ const WasmFunc* f);
+WasmTable* wasm_add_table(CfreeCompiler* c, WasmModule* m);
+WasmGlobal* wasm_add_global(CfreeCompiler* c, WasmModule* m);
+WasmElemSegment* wasm_add_elem(CfreeCompiler* c, WasmModule* m);
+WasmExport* wasm_add_export(CfreeCompiler* c, WasmModule* m);
+WasmCustom* wasm_add_custom(CfreeCompiler* c, WasmModule* m);
+void wasm_func_add_insn(CfreeCompiler* c, WasmModule* m, WasmFunc* f,
+ WasmInsnKind kind, int64_t imm);
+void wasm_func_add_mem_insn(CfreeCompiler* c, WasmModule* m, WasmFunc* f,
+ WasmInsnKind kind, uint32_t align,
+ uint64_t offset, uint32_t memidx);
+void wasm_func_add_fp_insn(CfreeCompiler* c, WasmModule* m, WasmFunc* f,
+ WasmInsnKind kind, double value);
+
+int wasm_is_num_type(WasmValType vt);
+int wasm_is_ref_type(WasmValType vt);
+int wasm_is_frontend_value_type(WasmValType vt);
+int wasm_feature_enabled(const WasmModule* m, WasmFeatureSet feature);
+void wasm_require_feature(CfreeCompiler* c, const WasmModule* m,
+ WasmFeatureSet feature, const char* feature_name,
+ const char* what);
+int wasm_insn_is_load(WasmInsnKind kind);
+int wasm_insn_is_store(WasmInsnKind kind);
+int wasm_insn_is_atomic_load(WasmInsnKind kind);
+int wasm_insn_is_atomic_store(WasmInsnKind kind);
+int wasm_insn_is_atomic_rmw(WasmInsnKind kind);
+int wasm_insn_is_atomic_cmpxchg(WasmInsnKind kind);
+int wasm_insn_is_atomic_wait_notify(WasmInsnKind kind);
+int wasm_insn_is_atomic_mem(WasmInsnKind kind);
+int wasm_insn_is_mem(WasmInsnKind kind);
+WasmValType wasm_func_local_type(const WasmFunc* f, uint32_t index);
+uint32_t wasm_mem_width(uint8_t kind);
+int wasm_int_cmp_op(uint8_t kind, CfreeCgIntCmpOp* out);
+WasmValType wasm_load_result_type(uint8_t kind);
+WasmValType wasm_store_value_type(uint8_t kind);
+WasmValType wasm_atomic_value_type(uint8_t kind);
+CfreeCgAtomicOp wasm_atomic_rmw_op(uint8_t kind);
+int wasm_int_unop_kind(uint8_t kind, WasmValType* vt);
+int wasm_fp_binop_kind(uint8_t kind, WasmValType* vt);
+int wasm_fp_cmp_kind(uint8_t kind, WasmValType* vt);
+int wasm_conversion_kind(uint8_t kind, WasmValType* src, WasmValType* dst);
+
+void wasm_parse_wat(CfreeCompiler* c, const CfreeBytes* input, WasmModule* out);
+void wasm_decode_binary(CfreeCompiler* c, const CfreeBytes* input,
+ WasmModule* out);
+int wasm_is_binary(const CfreeBytes* input);
+void wasm_validate(WasmModule* m, CfreeCompiler* c);
+void wasm_emit_cg(CfreeCompiler* c, const CfreeCodeOptions* code_opts,
+ CfreeObjBuilder* out, const WasmModule* m);
+void wasm_encode(CfreeCompiler* c, const WasmModule* m, CfreeWriter* out);
+
+#endif
diff --git a/lang/wasm/wat.c b/lang/wasm/wat.c
@@ -0,0 +1,2548 @@
+#include "wasm_internal.h"
+
+typedef struct WasmTok {
+ const char* p;
+ size_t len;
+ uint32_t line;
+ uint32_t col;
+ uint8_t kind;
+} WasmTok;
+
+enum {
+ WT_EOF = 0,
+ WT_LPAREN,
+ WT_RPAREN,
+ WT_ATOM,
+ WT_STRING,
+};
+
+typedef struct WatParser {
+ CfreeCompiler* c;
+ const char* name;
+ const char* src;
+ size_t len;
+ size_t pos;
+ uint32_t line;
+ uint32_t col;
+ WasmTok tok;
+ WasmModule* module;
+} WatParser;
+
+static int tok_is(WasmTok t, const char* s) {
+ size_t n = strlen(s);
+ return t.kind == WT_ATOM && t.len == n && memcmp(t.p, s, n) == 0;
+}
+
+static int wasm_name_eq(const char* name, WasmTok t) {
+ size_t n;
+ if (!name || t.kind != WT_ATOM) return 0;
+ n = strlen(name);
+ return t.len == n && memcmp(name, t.p, n) == 0;
+}
+
+static int wat_hex(char ch) {
+ if (ch >= '0' && ch <= '9') return ch - '0';
+ if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
+ if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
+ return -1;
+}
+
+static char* wat_dup_string(WatParser* p, WasmTok t, size_t* len_out) {
+ char* out;
+ size_t i, n = 0;
+ if (t.kind != WT_STRING)
+ wasm_error(p->c, wasm_loc(t.line, t.col), "wasm wat: expected string");
+ out = (char*)p->module->heap->alloc(p->module->heap, t.len + 1u, 1);
+ if (!out) wasm_error(p->c, wasm_loc(t.line, t.col), "wasm: out of memory");
+ for (i = 0; i < t.len; ++i) {
+ char ch = t.p[i];
+ if (ch != '\\') {
+ out[n++] = ch;
+ continue;
+ }
+ if (++i >= t.len)
+ wasm_error(p->c, wasm_loc(t.line, t.col),
+ "wasm wat: unterminated string escape");
+ ch = t.p[i];
+ switch (ch) {
+ case 'n':
+ out[n++] = '\n';
+ break;
+ case 'r':
+ out[n++] = '\r';
+ break;
+ case 't':
+ out[n++] = '\t';
+ break;
+ case '"':
+ case '\'':
+ case '\\':
+ out[n++] = ch;
+ break;
+ default: {
+ int hi = wat_hex(ch);
+ int lo = (i + 1u < t.len) ? wat_hex(t.p[i + 1u]) : -1;
+ if (hi < 0 || lo < 0)
+ wasm_error(p->c, wasm_loc(t.line, t.col),
+ "wasm wat: unsupported string escape");
+ out[n++] = (char)((hi << 4) | lo);
+ i++;
+ break;
+ }
+ }
+ }
+ out[n] = '\0';
+ if (len_out) *len_out = n;
+ return out;
+}
+
+static void wat_next(WatParser* p) {
+ const char* s = p->src;
+ while (p->pos < p->len) {
+ char ch = s[p->pos];
+ if (ch == '\n') {
+ p->pos++;
+ p->line++;
+ p->col = 1;
+ continue;
+ }
+ if (ch == ' ' || ch == '\t' || ch == '\r') {
+ p->pos++;
+ p->col++;
+ continue;
+ }
+ if (ch == ';' && p->pos + 1u < p->len && s[p->pos + 1u] == ';') {
+ while (p->pos < p->len && s[p->pos] != '\n') {
+ p->pos++;
+ p->col++;
+ }
+ continue;
+ }
+ if (ch == '(' && p->pos + 1u < p->len && s[p->pos + 1u] == ';') {
+ uint32_t depth = 1;
+ p->pos += 2u;
+ p->col += 2u;
+ while (depth && p->pos < p->len) {
+ if (s[p->pos] == '\n') {
+ p->pos++;
+ p->line++;
+ p->col = 1;
+ } else if (s[p->pos] == '(' && p->pos + 1u < p->len &&
+ s[p->pos + 1u] == ';') {
+ p->pos += 2u;
+ p->col += 2u;
+ depth++;
+ } else if (s[p->pos] == ';' && p->pos + 1u < p->len &&
+ s[p->pos + 1u] == ')') {
+ p->pos += 2u;
+ p->col += 2u;
+ depth--;
+ } else {
+ p->pos++;
+ p->col++;
+ }
+ }
+ if (depth)
+ wasm_error(p->c, wasm_loc(p->line, p->col),
+ "wasm wat: unterminated block comment");
+ continue;
+ }
+ break;
+ }
+ p->tok.p = s + p->pos;
+ p->tok.len = 0;
+ p->tok.line = p->line;
+ p->tok.col = p->col;
+ p->tok.kind = WT_EOF;
+ if (p->pos >= p->len) return;
+ if (s[p->pos] == '(') {
+ p->tok.kind = WT_LPAREN;
+ p->tok.len = 1;
+ p->pos++;
+ p->col++;
+ return;
+ }
+ if (s[p->pos] == ')') {
+ p->tok.kind = WT_RPAREN;
+ p->tok.len = 1;
+ p->pos++;
+ p->col++;
+ return;
+ }
+ if (s[p->pos] == '"') {
+ size_t start = ++p->pos;
+ p->col++;
+ p->tok.kind = WT_STRING;
+ p->tok.p = s + start;
+ while (p->pos < p->len && s[p->pos] != '"') {
+ if (s[p->pos] == '\\') {
+ p->pos++;
+ p->col++;
+ if (p->pos >= p->len) break;
+ } else if ((unsigned char)s[p->pos] < 0x20) {
+ wasm_error(p->c, wasm_loc(p->line, p->col),
+ "wasm wat: unsupported string escape/control character");
+ }
+ p->pos++;
+ p->col++;
+ }
+ if (p->pos >= p->len)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: unterminated string");
+ p->tok.len = (size_t)(s + p->pos - p->tok.p);
+ p->pos++;
+ p->col++;
+ return;
+ }
+ p->tok.kind = WT_ATOM;
+ while (p->pos < p->len) {
+ char ch = s[p->pos];
+ if (ch == '(' || ch == ')' || ch == ' ' || ch == '\t' || ch == '\r' ||
+ ch == '\n')
+ break;
+ p->pos++;
+ p->col++;
+ }
+ p->tok.len = (size_t)(s + p->pos - p->tok.p);
+}
+
+static void wat_expect(WatParser* p, uint8_t kind, const char* what) {
+ if (p->tok.kind != kind)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), "wasm wat: expected %s",
+ what);
+ wat_next(p);
+}
+
+static int wat_parse_i64(WatParser* p, int64_t* out) {
+ const char* s = p->tok.p;
+ size_t n = p->tok.len, i = 0;
+ uint64_t v = 0;
+ int neg = 0;
+ unsigned base = 10;
+ if (p->tok.kind != WT_ATOM || n == 0) return 0;
+ if (s[0] == '-' || s[0] == '+') {
+ neg = s[0] == '-';
+ i = 1;
+ }
+ if (i + 2u <= n && s[i] == '0' && (s[i + 1u] == 'x' || s[i + 1u] == 'X')) {
+ base = 16;
+ i += 2u;
+ }
+ if (i == n) return 0;
+ for (; i < n; ++i) {
+ int hd;
+ unsigned d;
+ if (s[i] == '_') continue;
+ hd = wat_hex(s[i]);
+ if (hd < 0 || (unsigned)hd >= base) return 0;
+ d = (unsigned)hd;
+ if (v > (UINT64_MAX - d) / base) return 0;
+ v = v * base + d;
+ }
+ *out = neg ? -(int64_t)v : (int64_t)v;
+ return 1;
+}
+
+static int wat_parse_f64(WatParser* p, double* out) {
+ char buf[128];
+ char* end = NULL;
+ if (p->tok.kind != WT_ATOM || p->tok.len == 0 || p->tok.len >= sizeof buf)
+ return 0;
+ memcpy(buf, p->tok.p, p->tok.len);
+ buf[p->tok.len] = '\0';
+ *out = strtod(buf, &end);
+ return end && *end == '\0';
+}
+
+static int wat_val_type(WasmTok t, WasmValType* out) {
+ if (tok_is(t, "i32")) {
+ *out = WASM_VAL_I32;
+ return 1;
+ }
+ if (tok_is(t, "i64")) {
+ *out = WASM_VAL_I64;
+ return 1;
+ }
+ if (tok_is(t, "f32")) {
+ *out = WASM_VAL_F32;
+ return 1;
+ }
+ if (tok_is(t, "f64")) {
+ *out = WASM_VAL_F64;
+ return 1;
+ }
+ if (tok_is(t, "funcref")) {
+ *out = WASM_VAL_FUNCREF;
+ return 1;
+ }
+ if (tok_is(t, "externref")) {
+ *out = WASM_VAL_EXTERNREF;
+ return 1;
+ }
+ return 0;
+}
+
+static void wat_require_feature(WatParser* p, WasmFeatureSet feature,
+ const char* feature_name, const char* what) {
+ if (!wasm_feature_enabled(p->module, feature))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: %s requires %s", what, feature_name);
+}
+
+static uint8_t wasm_export_kind_from_tok(WasmTok t) {
+ if (tok_is(t, "func")) return 0;
+ if (tok_is(t, "table")) return 1;
+ if (tok_is(t, "memory")) return 2;
+ if (tok_is(t, "global")) return 3;
+ return 0xffu;
+}
+
+static void wat_skip_list(WatParser* p) {
+ uint32_t depth = 1;
+ while (depth && p->tok.kind != WT_EOF) {
+ if (p->tok.kind == WT_LPAREN)
+ depth++;
+ else if (p->tok.kind == WT_RPAREN)
+ depth--;
+ wat_next(p);
+ }
+}
+
+static int wat_instr_kind(WasmTok t, WasmInsnKind* out, int* has_imm) {
+ *has_imm = 0;
+ if (tok_is(t, "unreachable")) {
+ *out = WASM_INSN_UNREACHABLE;
+ return 1;
+ }
+ if (tok_is(t, "nop")) {
+ *out = WASM_INSN_NOP;
+ return 1;
+ }
+ if (tok_is(t, "block")) {
+ *out = WASM_INSN_BLOCK;
+ return 1;
+ }
+ if (tok_is(t, "loop")) {
+ *out = WASM_INSN_LOOP;
+ return 1;
+ }
+ if (tok_is(t, "if")) {
+ *out = WASM_INSN_IF;
+ return 1;
+ }
+ if (tok_is(t, "else")) {
+ *out = WASM_INSN_ELSE;
+ return 1;
+ }
+ if (tok_is(t, "end")) {
+ *out = WASM_INSN_END;
+ return 1;
+ }
+ if (tok_is(t, "br")) {
+ *out = WASM_INSN_BR;
+ *has_imm = 1;
+ return 1;
+ }
+ if (tok_is(t, "br_if")) {
+ *out = WASM_INSN_BR_IF;
+ *has_imm = 1;
+ return 1;
+ }
+ if (tok_is(t, "br_table")) {
+ *out = WASM_INSN_BR_TABLE;
+ return 1;
+ }
+ if (tok_is(t, "select")) {
+ *out = WASM_INSN_SELECT;
+ return 1;
+ }
+ if (tok_is(t, "f32.const")) {
+ *out = WASM_INSN_F32_CONST;
+ *has_imm = 1;
+ return 1;
+ }
+ if (tok_is(t, "f64.const")) {
+ *out = WASM_INSN_F64_CONST;
+ *has_imm = 1;
+ return 1;
+ }
+ if (tok_is(t, "i32.const")) {
+ *out = WASM_INSN_I32_CONST;
+ *has_imm = 1;
+ return 1;
+ }
+ if (tok_is(t, "i64.const")) {
+ *out = WASM_INSN_I64_CONST;
+ *has_imm = 1;
+ return 1;
+ }
+ if (tok_is(t, "local.get")) {
+ *out = WASM_INSN_LOCAL_GET;
+ *has_imm = 1;
+ return 1;
+ }
+ if (tok_is(t, "local.set")) {
+ *out = WASM_INSN_LOCAL_SET;
+ *has_imm = 1;
+ return 1;
+ }
+ if (tok_is(t, "local.tee")) {
+ *out = WASM_INSN_LOCAL_TEE;
+ *has_imm = 1;
+ return 1;
+ }
+ if (tok_is(t, "call")) {
+ *out = WASM_INSN_CALL;
+ *has_imm = 1;
+ return 1;
+ }
+ if (tok_is(t, "call_indirect")) {
+ *out = WASM_INSN_CALL_INDIRECT;
+ return 1;
+ }
+ if (tok_is(t, "call_ref")) {
+ *out = WASM_INSN_CALL_REF;
+ return 1;
+ }
+ if (tok_is(t, "return_call")) {
+ *out = WASM_INSN_RETURN_CALL;
+ *has_imm = 1;
+ return 1;
+ }
+ if (tok_is(t, "return_call_indirect")) {
+ *out = WASM_INSN_RETURN_CALL_INDIRECT;
+ return 1;
+ }
+ if (tok_is(t, "return_call_ref")) {
+ *out = WASM_INSN_RETURN_CALL_REF;
+ return 1;
+ }
+ if (tok_is(t, "ref.null")) {
+ *out = WASM_INSN_REF_NULL;
+ *has_imm = 1;
+ return 1;
+ }
+ if (tok_is(t, "ref.func")) {
+ *out = WASM_INSN_REF_FUNC;
+ *has_imm = 1;
+ return 1;
+ }
+ if (tok_is(t, "ref.is_null")) {
+ *out = WASM_INSN_REF_IS_NULL;
+ return 1;
+ }
+ if (tok_is(t, "global.get")) {
+ *out = WASM_INSN_GLOBAL_GET;
+ *has_imm = 1;
+ return 1;
+ }
+ if (tok_is(t, "global.set")) {
+ *out = WASM_INSN_GLOBAL_SET;
+ *has_imm = 1;
+ return 1;
+ }
+ if (tok_is(t, "return")) {
+ *out = WASM_INSN_RETURN;
+ return 1;
+ }
+ if (tok_is(t, "drop")) {
+ *out = WASM_INSN_DROP;
+ return 1;
+ }
+ if (tok_is(t, "atomic.fence")) {
+ *out = WASM_INSN_ATOMIC_FENCE;
+ return 1;
+ }
+ if (tok_is(t, "i32.load")) {
+ *out = WASM_INSN_I32_LOAD;
+ return 1;
+ }
+ if (tok_is(t, "i32.atomic.load")) {
+ *out = WASM_INSN_I32_ATOMIC_LOAD;
+ return 1;
+ }
+ if (tok_is(t, "i64.load")) {
+ *out = WASM_INSN_I64_LOAD;
+ return 1;
+ }
+ if (tok_is(t, "i64.atomic.load")) {
+ *out = WASM_INSN_I64_ATOMIC_LOAD;
+ return 1;
+ }
+ if (tok_is(t, "i32.load8_s")) {
+ *out = WASM_INSN_I32_LOAD8_S;
+ return 1;
+ }
+ if (tok_is(t, "i32.load8_u")) {
+ *out = WASM_INSN_I32_LOAD8_U;
+ return 1;
+ }
+ if (tok_is(t, "i32.atomic.load8_u")) {
+ *out = WASM_INSN_I32_ATOMIC_LOAD8_U;
+ return 1;
+ }
+ if (tok_is(t, "i32.load16_s")) {
+ *out = WASM_INSN_I32_LOAD16_S;
+ return 1;
+ }
+ if (tok_is(t, "i32.load16_u")) {
+ *out = WASM_INSN_I32_LOAD16_U;
+ return 1;
+ }
+ if (tok_is(t, "i32.atomic.load16_u")) {
+ *out = WASM_INSN_I32_ATOMIC_LOAD16_U;
+ return 1;
+ }
+ if (tok_is(t, "i64.load8_s")) {
+ *out = WASM_INSN_I64_LOAD8_S;
+ return 1;
+ }
+ if (tok_is(t, "i64.load8_u")) {
+ *out = WASM_INSN_I64_LOAD8_U;
+ return 1;
+ }
+ if (tok_is(t, "i64.atomic.load8_u")) {
+ *out = WASM_INSN_I64_ATOMIC_LOAD8_U;
+ return 1;
+ }
+ if (tok_is(t, "i64.load16_s")) {
+ *out = WASM_INSN_I64_LOAD16_S;
+ return 1;
+ }
+ if (tok_is(t, "i64.load16_u")) {
+ *out = WASM_INSN_I64_LOAD16_U;
+ return 1;
+ }
+ if (tok_is(t, "i64.atomic.load16_u")) {
+ *out = WASM_INSN_I64_ATOMIC_LOAD16_U;
+ return 1;
+ }
+ if (tok_is(t, "i64.load32_s")) {
+ *out = WASM_INSN_I64_LOAD32_S;
+ return 1;
+ }
+ if (tok_is(t, "i64.load32_u")) {
+ *out = WASM_INSN_I64_LOAD32_U;
+ return 1;
+ }
+ if (tok_is(t, "i64.atomic.load32_u")) {
+ *out = WASM_INSN_I64_ATOMIC_LOAD32_U;
+ return 1;
+ }
+ if (tok_is(t, "i32.store")) {
+ *out = WASM_INSN_I32_STORE;
+ return 1;
+ }
+ if (tok_is(t, "i32.atomic.store")) {
+ *out = WASM_INSN_I32_ATOMIC_STORE;
+ return 1;
+ }
+ if (tok_is(t, "i64.store")) {
+ *out = WASM_INSN_I64_STORE;
+ return 1;
+ }
+ if (tok_is(t, "i64.atomic.store")) {
+ *out = WASM_INSN_I64_ATOMIC_STORE;
+ return 1;
+ }
+ if (tok_is(t, "i32.store8")) {
+ *out = WASM_INSN_I32_STORE8;
+ return 1;
+ }
+ if (tok_is(t, "i32.atomic.store8")) {
+ *out = WASM_INSN_I32_ATOMIC_STORE8;
+ return 1;
+ }
+ if (tok_is(t, "i32.store16")) {
+ *out = WASM_INSN_I32_STORE16;
+ return 1;
+ }
+ if (tok_is(t, "i32.atomic.store16")) {
+ *out = WASM_INSN_I32_ATOMIC_STORE16;
+ return 1;
+ }
+ if (tok_is(t, "i64.store8")) {
+ *out = WASM_INSN_I64_STORE8;
+ return 1;
+ }
+ if (tok_is(t, "i64.atomic.store8")) {
+ *out = WASM_INSN_I64_ATOMIC_STORE8;
+ return 1;
+ }
+ if (tok_is(t, "i64.store16")) {
+ *out = WASM_INSN_I64_STORE16;
+ return 1;
+ }
+ if (tok_is(t, "i64.atomic.store16")) {
+ *out = WASM_INSN_I64_ATOMIC_STORE16;
+ return 1;
+ }
+ if (tok_is(t, "i64.store32")) {
+ *out = WASM_INSN_I64_STORE32;
+ return 1;
+ }
+ if (tok_is(t, "i64.atomic.store32")) {
+ *out = WASM_INSN_I64_ATOMIC_STORE32;
+ return 1;
+ }
+ if (tok_is(t, "i32.atomic.rmw.add")) {
+ *out = WASM_INSN_I32_ATOMIC_RMW_ADD;
+ return 1;
+ }
+ if (tok_is(t, "i64.atomic.rmw.add")) {
+ *out = WASM_INSN_I64_ATOMIC_RMW_ADD;
+ return 1;
+ }
+ if (tok_is(t, "i32.atomic.rmw.sub")) {
+ *out = WASM_INSN_I32_ATOMIC_RMW_SUB;
+ return 1;
+ }
+ if (tok_is(t, "i64.atomic.rmw.sub")) {
+ *out = WASM_INSN_I64_ATOMIC_RMW_SUB;
+ return 1;
+ }
+ if (tok_is(t, "i32.atomic.rmw.and")) {
+ *out = WASM_INSN_I32_ATOMIC_RMW_AND;
+ return 1;
+ }
+ if (tok_is(t, "i64.atomic.rmw.and")) {
+ *out = WASM_INSN_I64_ATOMIC_RMW_AND;
+ return 1;
+ }
+ if (tok_is(t, "i32.atomic.rmw.or")) {
+ *out = WASM_INSN_I32_ATOMIC_RMW_OR;
+ return 1;
+ }
+ if (tok_is(t, "i64.atomic.rmw.or")) {
+ *out = WASM_INSN_I64_ATOMIC_RMW_OR;
+ return 1;
+ }
+ if (tok_is(t, "i32.atomic.rmw.xor")) {
+ *out = WASM_INSN_I32_ATOMIC_RMW_XOR;
+ return 1;
+ }
+ if (tok_is(t, "i64.atomic.rmw.xor")) {
+ *out = WASM_INSN_I64_ATOMIC_RMW_XOR;
+ return 1;
+ }
+ if (tok_is(t, "i32.atomic.rmw.xchg")) {
+ *out = WASM_INSN_I32_ATOMIC_RMW_XCHG;
+ return 1;
+ }
+ if (tok_is(t, "i64.atomic.rmw.xchg")) {
+ *out = WASM_INSN_I64_ATOMIC_RMW_XCHG;
+ return 1;
+ }
+ if (tok_is(t, "i32.atomic.rmw.cmpxchg")) {
+ *out = WASM_INSN_I32_ATOMIC_RMW_CMPXCHG;
+ return 1;
+ }
+ if (tok_is(t, "i64.atomic.rmw.cmpxchg")) {
+ *out = WASM_INSN_I64_ATOMIC_RMW_CMPXCHG;
+ return 1;
+ }
+ if (tok_is(t, "memory.atomic.wait32") || tok_is(t, "i32.atomic.wait")) {
+ *out = WASM_INSN_I32_ATOMIC_WAIT;
+ return 1;
+ }
+ if (tok_is(t, "memory.atomic.wait64") || tok_is(t, "i64.atomic.wait")) {
+ *out = WASM_INSN_I64_ATOMIC_WAIT;
+ return 1;
+ }
+ if (tok_is(t, "memory.atomic.notify") || tok_is(t, "atomic.notify")) {
+ *out = WASM_INSN_MEMORY_ATOMIC_NOTIFY;
+ return 1;
+ }
+ if (tok_is(t, "memory.size")) {
+ *out = WASM_INSN_MEMORY_SIZE;
+ return 1;
+ }
+ if (tok_is(t, "memory.grow")) {
+ *out = WASM_INSN_MEMORY_GROW;
+ return 1;
+ }
+ if (tok_is(t, "i32.add")) {
+ *out = WASM_INSN_I32_ADD;
+ return 1;
+ }
+ if (tok_is(t, "i32.sub")) {
+ *out = WASM_INSN_I32_SUB;
+ return 1;
+ }
+ if (tok_is(t, "i32.mul")) {
+ *out = WASM_INSN_I32_MUL;
+ return 1;
+ }
+ if (tok_is(t, "i32.div_s")) {
+ *out = WASM_INSN_I32_DIV_S;
+ return 1;
+ }
+ if (tok_is(t, "i32.div_u")) {
+ *out = WASM_INSN_I32_DIV_U;
+ return 1;
+ }
+ if (tok_is(t, "i32.rem_s")) {
+ *out = WASM_INSN_I32_REM_S;
+ return 1;
+ }
+ if (tok_is(t, "i32.rem_u")) {
+ *out = WASM_INSN_I32_REM_U;
+ return 1;
+ }
+ if (tok_is(t, "i32.and")) {
+ *out = WASM_INSN_I32_AND;
+ return 1;
+ }
+ if (tok_is(t, "i32.or")) {
+ *out = WASM_INSN_I32_OR;
+ return 1;
+ }
+ if (tok_is(t, "i32.xor")) {
+ *out = WASM_INSN_I32_XOR;
+ return 1;
+ }
+ if (tok_is(t, "i32.shl")) {
+ *out = WASM_INSN_I32_SHL;
+ return 1;
+ }
+ if (tok_is(t, "i32.shr_s")) {
+ *out = WASM_INSN_I32_SHR_S;
+ return 1;
+ }
+ if (tok_is(t, "i32.shr_u")) {
+ *out = WASM_INSN_I32_SHR_U;
+ return 1;
+ }
+ if (tok_is(t, "i32.rotl")) {
+ *out = WASM_INSN_I32_ROTL;
+ return 1;
+ }
+ if (tok_is(t, "i32.rotr")) {
+ *out = WASM_INSN_I32_ROTR;
+ return 1;
+ }
+ if (tok_is(t, "i32.clz")) {
+ *out = WASM_INSN_I32_CLZ;
+ return 1;
+ }
+ if (tok_is(t, "i32.ctz")) {
+ *out = WASM_INSN_I32_CTZ;
+ return 1;
+ }
+ if (tok_is(t, "i32.popcnt")) {
+ *out = WASM_INSN_I32_POPCNT;
+ return 1;
+ }
+ if (tok_is(t, "i32.eqz")) {
+ *out = WASM_INSN_I32_EQZ;
+ return 1;
+ }
+ if (tok_is(t, "i32.eq")) {
+ *out = WASM_INSN_I32_EQ;
+ return 1;
+ }
+ if (tok_is(t, "i32.ne")) {
+ *out = WASM_INSN_I32_NE;
+ return 1;
+ }
+ if (tok_is(t, "i32.lt_s")) {
+ *out = WASM_INSN_I32_LT_S;
+ return 1;
+ }
+ if (tok_is(t, "i32.lt_u")) {
+ *out = WASM_INSN_I32_LT_U;
+ return 1;
+ }
+ if (tok_is(t, "i32.gt_s")) {
+ *out = WASM_INSN_I32_GT_S;
+ return 1;
+ }
+ if (tok_is(t, "i32.gt_u")) {
+ *out = WASM_INSN_I32_GT_U;
+ return 1;
+ }
+ if (tok_is(t, "i32.le_s")) {
+ *out = WASM_INSN_I32_LE_S;
+ return 1;
+ }
+ if (tok_is(t, "i32.le_u")) {
+ *out = WASM_INSN_I32_LE_U;
+ return 1;
+ }
+ if (tok_is(t, "i32.ge_s")) {
+ *out = WASM_INSN_I32_GE_S;
+ return 1;
+ }
+ if (tok_is(t, "i32.ge_u")) {
+ *out = WASM_INSN_I32_GE_U;
+ return 1;
+ }
+ if (tok_is(t, "i64.add")) {
+ *out = WASM_INSN_I64_ADD;
+ return 1;
+ }
+ if (tok_is(t, "i64.sub")) {
+ *out = WASM_INSN_I64_SUB;
+ return 1;
+ }
+ if (tok_is(t, "i64.mul")) {
+ *out = WASM_INSN_I64_MUL;
+ return 1;
+ }
+ if (tok_is(t, "i64.div_s")) {
+ *out = WASM_INSN_I64_DIV_S;
+ return 1;
+ }
+ if (tok_is(t, "i64.div_u")) {
+ *out = WASM_INSN_I64_DIV_U;
+ return 1;
+ }
+ if (tok_is(t, "i64.rem_s")) {
+ *out = WASM_INSN_I64_REM_S;
+ return 1;
+ }
+ if (tok_is(t, "i64.rem_u")) {
+ *out = WASM_INSN_I64_REM_U;
+ return 1;
+ }
+ if (tok_is(t, "i64.and")) {
+ *out = WASM_INSN_I64_AND;
+ return 1;
+ }
+ if (tok_is(t, "i64.or")) {
+ *out = WASM_INSN_I64_OR;
+ return 1;
+ }
+ if (tok_is(t, "i64.xor")) {
+ *out = WASM_INSN_I64_XOR;
+ return 1;
+ }
+ if (tok_is(t, "i64.shl")) {
+ *out = WASM_INSN_I64_SHL;
+ return 1;
+ }
+ if (tok_is(t, "i64.shr_s")) {
+ *out = WASM_INSN_I64_SHR_S;
+ return 1;
+ }
+ if (tok_is(t, "i64.shr_u")) {
+ *out = WASM_INSN_I64_SHR_U;
+ return 1;
+ }
+ if (tok_is(t, "i64.rotl")) {
+ *out = WASM_INSN_I64_ROTL;
+ return 1;
+ }
+ if (tok_is(t, "i64.rotr")) {
+ *out = WASM_INSN_I64_ROTR;
+ return 1;
+ }
+ if (tok_is(t, "i64.clz")) {
+ *out = WASM_INSN_I64_CLZ;
+ return 1;
+ }
+ if (tok_is(t, "i64.ctz")) {
+ *out = WASM_INSN_I64_CTZ;
+ return 1;
+ }
+ if (tok_is(t, "i64.popcnt")) {
+ *out = WASM_INSN_I64_POPCNT;
+ return 1;
+ }
+ if (tok_is(t, "i64.eqz")) {
+ *out = WASM_INSN_I64_EQZ;
+ return 1;
+ }
+ if (tok_is(t, "i64.eq")) {
+ *out = WASM_INSN_I64_EQ;
+ return 1;
+ }
+ if (tok_is(t, "i64.ne")) {
+ *out = WASM_INSN_I64_NE;
+ return 1;
+ }
+ if (tok_is(t, "i64.lt_s")) {
+ *out = WASM_INSN_I64_LT_S;
+ return 1;
+ }
+ if (tok_is(t, "i64.lt_u")) {
+ *out = WASM_INSN_I64_LT_U;
+ return 1;
+ }
+ if (tok_is(t, "i64.gt_s")) {
+ *out = WASM_INSN_I64_GT_S;
+ return 1;
+ }
+ if (tok_is(t, "i64.gt_u")) {
+ *out = WASM_INSN_I64_GT_U;
+ return 1;
+ }
+ if (tok_is(t, "i64.le_s")) {
+ *out = WASM_INSN_I64_LE_S;
+ return 1;
+ }
+ if (tok_is(t, "i64.le_u")) {
+ *out = WASM_INSN_I64_LE_U;
+ return 1;
+ }
+ if (tok_is(t, "i64.ge_s")) {
+ *out = WASM_INSN_I64_GE_S;
+ return 1;
+ }
+ if (tok_is(t, "i64.ge_u")) {
+ *out = WASM_INSN_I64_GE_U;
+ return 1;
+ }
+ if (tok_is(t, "f32.add")) {
+ *out = WASM_INSN_F32_ADD;
+ return 1;
+ }
+ if (tok_is(t, "f32.sub")) {
+ *out = WASM_INSN_F32_SUB;
+ return 1;
+ }
+ if (tok_is(t, "f32.mul")) {
+ *out = WASM_INSN_F32_MUL;
+ return 1;
+ }
+ if (tok_is(t, "f32.div")) {
+ *out = WASM_INSN_F32_DIV;
+ return 1;
+ }
+ if (tok_is(t, "f32.eq")) {
+ *out = WASM_INSN_F32_EQ;
+ return 1;
+ }
+ if (tok_is(t, "f32.ne")) {
+ *out = WASM_INSN_F32_NE;
+ return 1;
+ }
+ if (tok_is(t, "f32.lt")) {
+ *out = WASM_INSN_F32_LT;
+ return 1;
+ }
+ if (tok_is(t, "f32.gt")) {
+ *out = WASM_INSN_F32_GT;
+ return 1;
+ }
+ if (tok_is(t, "f32.le")) {
+ *out = WASM_INSN_F32_LE;
+ return 1;
+ }
+ if (tok_is(t, "f32.ge")) {
+ *out = WASM_INSN_F32_GE;
+ return 1;
+ }
+ if (tok_is(t, "f64.add")) {
+ *out = WASM_INSN_F64_ADD;
+ return 1;
+ }
+ if (tok_is(t, "f64.sub")) {
+ *out = WASM_INSN_F64_SUB;
+ return 1;
+ }
+ if (tok_is(t, "f64.mul")) {
+ *out = WASM_INSN_F64_MUL;
+ return 1;
+ }
+ if (tok_is(t, "f64.div")) {
+ *out = WASM_INSN_F64_DIV;
+ return 1;
+ }
+ if (tok_is(t, "f64.eq")) {
+ *out = WASM_INSN_F64_EQ;
+ return 1;
+ }
+ if (tok_is(t, "f64.ne")) {
+ *out = WASM_INSN_F64_NE;
+ return 1;
+ }
+ if (tok_is(t, "f64.lt")) {
+ *out = WASM_INSN_F64_LT;
+ return 1;
+ }
+ if (tok_is(t, "f64.gt")) {
+ *out = WASM_INSN_F64_GT;
+ return 1;
+ }
+ if (tok_is(t, "f64.le")) {
+ *out = WASM_INSN_F64_LE;
+ return 1;
+ }
+ if (tok_is(t, "f64.ge")) {
+ *out = WASM_INSN_F64_GE;
+ return 1;
+ }
+ if (tok_is(t, "i32.wrap_i64")) {
+ *out = WASM_INSN_I32_WRAP_I64;
+ return 1;
+ }
+ if (tok_is(t, "i32.trunc_f32_s")) {
+ *out = WASM_INSN_I32_TRUNC_F32_S;
+ return 1;
+ }
+ if (tok_is(t, "i32.trunc_f32_u")) {
+ *out = WASM_INSN_I32_TRUNC_F32_U;
+ return 1;
+ }
+ if (tok_is(t, "i32.trunc_f64_s")) {
+ *out = WASM_INSN_I32_TRUNC_F64_S;
+ return 1;
+ }
+ if (tok_is(t, "i32.trunc_f64_u")) {
+ *out = WASM_INSN_I32_TRUNC_F64_U;
+ return 1;
+ }
+ if (tok_is(t, "i64.extend_i32_s")) {
+ *out = WASM_INSN_I64_EXTEND_I32_S;
+ return 1;
+ }
+ if (tok_is(t, "i64.extend_i32_u")) {
+ *out = WASM_INSN_I64_EXTEND_I32_U;
+ return 1;
+ }
+ if (tok_is(t, "i64.trunc_f32_s")) {
+ *out = WASM_INSN_I64_TRUNC_F32_S;
+ return 1;
+ }
+ if (tok_is(t, "i64.trunc_f32_u")) {
+ *out = WASM_INSN_I64_TRUNC_F32_U;
+ return 1;
+ }
+ if (tok_is(t, "i64.trunc_f64_s")) {
+ *out = WASM_INSN_I64_TRUNC_F64_S;
+ return 1;
+ }
+ if (tok_is(t, "i64.trunc_f64_u")) {
+ *out = WASM_INSN_I64_TRUNC_F64_U;
+ return 1;
+ }
+ if (tok_is(t, "f32.convert_i32_s")) {
+ *out = WASM_INSN_F32_CONVERT_I32_S;
+ return 1;
+ }
+ if (tok_is(t, "f32.convert_i32_u")) {
+ *out = WASM_INSN_F32_CONVERT_I32_U;
+ return 1;
+ }
+ if (tok_is(t, "f32.convert_i64_s")) {
+ *out = WASM_INSN_F32_CONVERT_I64_S;
+ return 1;
+ }
+ if (tok_is(t, "f32.convert_i64_u")) {
+ *out = WASM_INSN_F32_CONVERT_I64_U;
+ return 1;
+ }
+ if (tok_is(t, "f32.demote_f64")) {
+ *out = WASM_INSN_F32_DEMOTE_F64;
+ return 1;
+ }
+ if (tok_is(t, "f64.convert_i32_s")) {
+ *out = WASM_INSN_F64_CONVERT_I32_S;
+ return 1;
+ }
+ if (tok_is(t, "f64.convert_i32_u")) {
+ *out = WASM_INSN_F64_CONVERT_I32_U;
+ return 1;
+ }
+ if (tok_is(t, "f64.convert_i64_s")) {
+ *out = WASM_INSN_F64_CONVERT_I64_S;
+ return 1;
+ }
+ if (tok_is(t, "f64.convert_i64_u")) {
+ *out = WASM_INSN_F64_CONVERT_I64_U;
+ return 1;
+ }
+ if (tok_is(t, "f64.promote_f32")) {
+ *out = WASM_INSN_F64_PROMOTE_F32;
+ return 1;
+ }
+ if (tok_is(t, "i32.reinterpret_f32")) {
+ *out = WASM_INSN_I32_REINTERPRET_F32;
+ return 1;
+ }
+ if (tok_is(t, "i64.reinterpret_f64")) {
+ *out = WASM_INSN_I64_REINTERPRET_F64;
+ return 1;
+ }
+ if (tok_is(t, "f32.reinterpret_i32")) {
+ *out = WASM_INSN_F32_REINTERPRET_I32;
+ return 1;
+ }
+ if (tok_is(t, "f64.reinterpret_i64")) {
+ *out = WASM_INSN_F64_REINTERPRET_I64;
+ return 1;
+ }
+ return 0;
+}
+
+static void wat_parse_func_index(WatParser* p, int64_t* out) {
+ uint32_t i;
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ for (i = 0; i < p->module->nfuncs; ++i) {
+ if (wasm_name_eq(p->module->funcs[i].name, p->tok)) {
+ *out = i;
+ return;
+ }
+ }
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: unknown function name");
+ }
+ if (!wat_parse_i64(p, out))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected instruction immediate");
+}
+
+static void wat_parse_type_index(WatParser* p, int64_t* out) {
+ uint32_t i;
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ for (i = 0; i < p->module->ntypes; ++i) {
+ if (wasm_name_eq(p->module->types[i].name, p->tok)) {
+ *out = i;
+ return;
+ }
+ }
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: unknown type name");
+ }
+ if (!wat_parse_i64(p, out))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected type index");
+}
+
+static void wat_parse_local_index(WatParser* p, WasmFunc* f, int64_t* out) {
+ uint32_t i, nlocals = f->nparams + f->nlocals;
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ for (i = 0; i < nlocals; ++i) {
+ if (wasm_name_eq(f->local_names[i], p->tok)) {
+ *out = i;
+ return;
+ }
+ }
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: unknown local name");
+ }
+ if (!wat_parse_i64(p, out))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected instruction immediate");
+}
+
+static void wat_parse_global_index(WatParser* p, int64_t* out) {
+ uint32_t i;
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ for (i = 0; i < p->module->nglobals; ++i) {
+ if (wasm_name_eq(p->module->globals[i].name, p->tok)) {
+ *out = i;
+ return;
+ }
+ }
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: unknown global name");
+ }
+ if (!wat_parse_i64(p, out))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected global index");
+}
+
+static void wat_parse_instr_imm(WatParser* p, WasmFunc* f, WasmInsnKind kind,
+ int64_t* out) {
+ switch (kind) {
+ case WASM_INSN_CALL:
+ case WASM_INSN_RETURN_CALL:
+ wat_parse_func_index(p, out);
+ break;
+ case WASM_INSN_GLOBAL_GET:
+ case WASM_INSN_GLOBAL_SET:
+ wat_parse_global_index(p, out);
+ break;
+ case WASM_INSN_LOCAL_GET:
+ case WASM_INSN_LOCAL_SET:
+ case WASM_INSN_LOCAL_TEE:
+ wat_parse_local_index(p, f, out);
+ break;
+ default:
+ if (!wat_parse_i64(p, out))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected instruction immediate");
+ break;
+ }
+}
+
+
+static int wat_atom_prefix(WasmTok t, const char* prefix) {
+ size_t n = strlen(prefix);
+ return t.kind == WT_ATOM && t.len >= n && memcmp(t.p, prefix, n) == 0;
+}
+
+static int wat_parse_u32_atom(WasmTok t, uint32_t* out) {
+ uint64_t v = 0;
+ size_t i = 0;
+ unsigned base = 10;
+ if (t.kind != WT_ATOM || t.len == 0) return 0;
+ if (i + 2u <= t.len && t.p[i] == '0' &&
+ (t.p[i + 1u] == 'x' || t.p[i + 1u] == 'X')) {
+ base = 16;
+ i += 2u;
+ }
+ if (i == t.len) return 0;
+ for (; i < t.len; ++i) {
+ int hd;
+ if (t.p[i] == '_') continue;
+ hd = wat_hex(t.p[i]);
+ if (hd < 0 || (unsigned)hd >= base) return 0;
+ if (v > (UINT32_MAX - (uint32_t)hd) / base) return 0;
+ v = v * base + (uint32_t)hd;
+ }
+ *out = (uint32_t)v;
+ return 1;
+}
+
+static int wat_parse_u64_atom(WasmTok t, uint64_t* out) {
+ uint64_t v = 0;
+ size_t i = 0;
+ unsigned base = 10;
+ if (t.kind != WT_ATOM || t.len == 0) return 0;
+ if (i + 2u <= t.len && t.p[i] == '0' &&
+ (t.p[i + 1u] == 'x' || t.p[i + 1u] == 'X')) {
+ base = 16;
+ i += 2u;
+ }
+ if (i == t.len) return 0;
+ for (; i < t.len; ++i) {
+ int hd;
+ if (t.p[i] == '_') continue;
+ hd = wat_hex(t.p[i]);
+ if (hd < 0 || (unsigned)hd >= base) return 0;
+ if (v > (UINT64_MAX - (uint64_t)hd) / base) return 0;
+ v = v * base + (uint64_t)hd;
+ }
+ *out = v;
+ return 1;
+}
+
+static void wat_parse_memory_index(WatParser* p, uint32_t* out) {
+ uint32_t i;
+ int64_t idx;
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ for (i = 0; i < p->module->nmemories; ++i) {
+ if (wasm_name_eq(p->module->memories[i].name, p->tok)) {
+ *out = i;
+ return;
+ }
+ }
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: unknown memory name");
+ }
+ if (!wat_parse_i64(p, &idx) || idx < 0 || idx > UINT32_MAX)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected memory index");
+ *out = (uint32_t)idx;
+}
+
+static void wat_parse_mem_attrs(WatParser* p, uint32_t* align,
+ uint64_t* offset, uint32_t* memidx) {
+ while (p->tok.kind == WT_ATOM) {
+ WasmTok val;
+ if (wat_atom_prefix(p->tok, "align=")) {
+ val = p->tok;
+ val.p += 6;
+ val.len -= 6;
+ if (!wat_parse_u32_atom(val, align))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: bad memory alignment");
+ wat_next(p);
+ } else if (wat_atom_prefix(p->tok, "offset=")) {
+ val = p->tok;
+ val.p += 7;
+ val.len -= 7;
+ if (!wat_parse_u64_atom(val, offset))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: bad memory offset");
+ wat_next(p);
+ } else if (wat_atom_prefix(p->tok, "memory=")) {
+ val = p->tok;
+ val.p += 7;
+ val.len -= 7;
+ if (!wat_parse_u32_atom(val, memidx))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: bad memory index");
+ wat_next(p);
+ } else if (wat_atom_prefix(p->tok, "mem=")) {
+ val = p->tok;
+ val.p += 4;
+ val.len -= 4;
+ if (!wat_parse_u32_atom(val, memidx))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: bad memory index");
+ wat_next(p);
+ } else {
+ break;
+ }
+ }
+}
+
+static void wat_reject_inline_result(WatParser* p, const char* what) {
+ if (p->tok.kind != WT_LPAREN) return;
+ wat_next(p);
+ if (!tok_is(p->tok, "result")) {
+ p->pos = (size_t)(p->tok.p - p->src);
+ p->line = p->tok.line;
+ p->col = p->tok.col;
+ p->tok.kind = WT_LPAREN;
+ p->tok.p = p->src + p->pos - 1u;
+ p->tok.len = 1;
+ p->tok.line = p->line;
+ p->tok.col = p->col - 1u;
+ return;
+ }
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: %s results are unsupported", what);
+}
+
+static void wat_parse_instr(WatParser* p, WasmFunc* f);
+
+static void wat_check_instr_feature(WatParser* p, WasmInsnKind kind) {
+ if (kind == WASM_INSN_RETURN_CALL ||
+ kind == WASM_INSN_RETURN_CALL_INDIRECT ||
+ kind == WASM_INSN_RETURN_CALL_REF)
+ wat_require_feature(p, WASM_FEATURE_TAIL_CALLS, "tail calls",
+ "tail-call instruction");
+ if (kind == WASM_INSN_REF_NULL || kind == WASM_INSN_REF_FUNC ||
+ kind == WASM_INSN_REF_IS_NULL || kind == WASM_INSN_CALL_REF ||
+ kind == WASM_INSN_RETURN_CALL_REF)
+ wat_require_feature(p, WASM_FEATURE_TYPED_FUNC_REFS,
+ "typed function references",
+ "typed-reference instruction");
+ if (kind == WASM_INSN_ATOMIC_FENCE || wasm_insn_is_atomic_mem(kind))
+ wat_require_feature(p, WASM_FEATURE_THREADS, "threads",
+ "atomic instruction");
+}
+
+static uint32_t wat_parse_call_indirect_type(WatParser* p) {
+ int64_t typeidx;
+ wat_expect(p, WT_LPAREN, "'('");
+ if (!tok_is(p->tok, "type"))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected call_indirect type");
+ wat_next(p);
+ wat_parse_type_index(p, &typeidx);
+ if (typeidx < 0 || (uint64_t)typeidx >= p->module->ntypes)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: type index out of range");
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+ return (uint32_t)typeidx;
+}
+
+static void wat_parse_ref_null_type(WatParser* p, int64_t* out) {
+ if (tok_is(p->tok, "func") || tok_is(p->tok, "nofunc") ||
+ tok_is(p->tok, "funcref")) {
+ *out = WASM_VAL_FUNCREF;
+ return;
+ }
+ if (tok_is(p->tok, "extern") || tok_is(p->tok, "noextern") ||
+ tok_is(p->tok, "externref")) {
+ *out = WASM_VAL_EXTERNREF;
+ return;
+ }
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected reference type");
+}
+
+static void wat_parse_instr_list(WatParser* p, WasmFunc* f) {
+ WasmInsnKind kind;
+ int has_imm;
+ int64_t imm = 0;
+ WasmTok head;
+ wat_expect(p, WT_LPAREN, "'('");
+ head = p->tok;
+ if (tok_is(head, "block") || tok_is(head, "loop")) {
+ kind = tok_is(head, "block") ? WASM_INSN_BLOCK : WASM_INSN_LOOP;
+ wat_next(p);
+ if (p->tok.kind == WT_LPAREN) {
+ wat_next(p);
+ if (tok_is(p->tok, "result")) {
+ wat_next(p);
+ if (!tok_is(p->tok, "i32") && !tok_is(p->tok, "i64"))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: unsupported block result type");
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: block results are unsupported");
+ }
+ p->pos = (size_t)(p->tok.p - p->src);
+ p->line = p->tok.line;
+ p->col = p->tok.col;
+ p->tok.kind = WT_LPAREN;
+ p->tok.p = p->src + p->pos - 1u;
+ p->tok.len = 1;
+ p->tok.line = p->line;
+ p->tok.col = p->col - 1u;
+ }
+ wasm_func_add_insn(p->c, p->module, f, kind, 0);
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF)
+ wat_parse_instr(p, f);
+ wasm_func_add_insn(p->c, p->module, f, WASM_INSN_END, 0);
+ wat_expect(p, WT_RPAREN, "')'");
+ return;
+ }
+ if (tok_is(head, "if")) {
+ wat_next(p);
+ while (p->tok.kind == WT_LPAREN) {
+ WasmTok save_head;
+ size_t save_pos = p->pos;
+ uint32_t save_line = p->line, save_col = p->col;
+ wat_next(p);
+ save_head = p->tok;
+ p->pos = save_pos;
+ p->line = save_line;
+ p->col = save_col;
+ p->tok.kind = WT_LPAREN;
+ p->tok.p = p->src + p->pos - 1u;
+ p->tok.len = 1;
+ p->tok.line = save_line;
+ p->tok.col = save_col - 1u;
+ if (tok_is(save_head, "then") || tok_is(save_head, "else")) break;
+ if (tok_is(save_head, "result"))
+ wasm_error(p->c, wasm_loc(save_head.line, save_head.col),
+ "wasm wat: if results are unsupported");
+ wat_parse_instr(p, f);
+ }
+ wasm_func_add_insn(p->c, p->module, f, WASM_INSN_IF, 0);
+ if (p->tok.kind == WT_LPAREN) {
+ wat_next(p);
+ if (!tok_is(p->tok, "then"))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected then");
+ wat_next(p);
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF)
+ wat_parse_instr(p, f);
+ wat_expect(p, WT_RPAREN, "')'");
+ }
+ if (p->tok.kind == WT_LPAREN) {
+ wat_next(p);
+ if (!tok_is(p->tok, "else"))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected else");
+ wasm_func_add_insn(p->c, p->module, f, WASM_INSN_ELSE, 0);
+ wat_next(p);
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF)
+ wat_parse_instr(p, f);
+ wat_expect(p, WT_RPAREN, "')'");
+ }
+ wasm_func_add_insn(p->c, p->module, f, WASM_INSN_END, 0);
+ wat_expect(p, WT_RPAREN, "')'");
+ return;
+ }
+ if (!wat_instr_kind(head, &kind, &has_imm))
+ wasm_error(p->c, wasm_loc(head.line, head.col),
+ "wasm wat: unsupported instruction");
+ wat_check_instr_feature(p, kind);
+ wat_next(p);
+ if (wasm_insn_is_mem(kind)) {
+ uint32_t align = 0, memidx = 0;
+ uint64_t offset = 0;
+ wat_parse_mem_attrs(p, &align, &offset, &memidx);
+ while (p->tok.kind == WT_LPAREN) {
+ wat_next(p);
+ if (tok_is(p->tok, "memory")) {
+ wat_next(p);
+ wat_parse_memory_index(p, &memidx);
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+ } else {
+ p->pos = (size_t)(p->tok.p - p->src);
+ p->line = p->tok.line;
+ p->col = p->tok.col;
+ p->tok.kind = WT_LPAREN;
+ p->tok.p = p->src + p->pos - 1u;
+ p->tok.len = 1;
+ p->tok.line = p->line;
+ p->tok.col = p->col - 1u;
+ wat_parse_instr(p, f);
+ }
+ }
+ wasm_func_add_mem_insn(p->c, p->module, f, kind, align, offset, memidx);
+ wat_expect(p, WT_RPAREN, "')'");
+ return;
+ }
+ if (kind == WASM_INSN_BR_TABLE) {
+ WasmInsn* in;
+ uint32_t n = 0;
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
+ int64_t target;
+ if (n >= 16u)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: too many br_table targets");
+ if (!wat_parse_i64(p, &target) || target < 0 || target > UINT32_MAX)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: bad br_table target");
+ wasm_func_add_insn(p->c, p->module, f,
+ n ? WASM_INSN_NOP : WASM_INSN_BR_TABLE, 0);
+ in = &f->insns[f->ninsns - 1u - n];
+ in->targets[n++] = (uint32_t)target;
+ wat_next(p);
+ }
+ f->ninsns -= n - 1u;
+ f->insns[f->ninsns - 1u].ntargets = n;
+ wat_expect(p, WT_RPAREN, "')'");
+ return;
+ }
+ if (kind == WASM_INSN_CALL_INDIRECT ||
+ kind == WASM_INSN_RETURN_CALL_INDIRECT) {
+ uint32_t typeidx = wat_parse_call_indirect_type(p);
+ while (p->tok.kind == WT_LPAREN) wat_parse_instr(p, f);
+ wasm_func_add_insn(p->c, p->module, f, kind, typeidx);
+ wat_expect(p, WT_RPAREN, "')'");
+ return;
+ }
+ if (kind == WASM_INSN_CALL_REF || kind == WASM_INSN_RETURN_CALL_REF) {
+ uint32_t typeidx = wat_parse_call_indirect_type(p);
+ while (p->tok.kind == WT_LPAREN) wat_parse_instr(p, f);
+ wasm_func_add_insn(p->c, p->module, f, kind, typeidx);
+ wat_expect(p, WT_RPAREN, "')'");
+ return;
+ }
+ if (kind == WASM_INSN_REF_NULL) {
+ wat_parse_ref_null_type(p, &imm);
+ wat_next(p);
+ wasm_func_add_insn(p->c, p->module, f, kind, imm);
+ wat_expect(p, WT_RPAREN, "')'");
+ return;
+ }
+ if (kind == WASM_INSN_REF_FUNC) {
+ wat_parse_func_index(p, &imm);
+ wat_next(p);
+ wasm_func_add_insn(p->c, p->module, f, kind, imm);
+ wat_expect(p, WT_RPAREN, "')'");
+ return;
+ }
+ if (kind == WASM_INSN_MEMORY_SIZE || kind == WASM_INSN_MEMORY_GROW) {
+ uint32_t memidx = 0;
+ while (p->tok.kind == WT_LPAREN) {
+ wat_next(p);
+ if (!tok_is(p->tok, "memory")) {
+ p->pos = (size_t)(p->tok.p - p->src);
+ p->line = p->tok.line;
+ p->col = p->tok.col;
+ p->tok.kind = WT_LPAREN;
+ p->tok.p = p->src + p->pos - 1u;
+ p->tok.len = 1;
+ p->tok.line = p->line;
+ p->tok.col = p->col - 1u;
+ wat_parse_instr(p, f);
+ continue;
+ }
+ wat_next(p);
+ wat_parse_memory_index(p, &memidx);
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+ }
+ {
+ WasmInsnKind next_kind;
+ int next_has_imm;
+ if (p->tok.kind == WT_ATOM &&
+ !wat_instr_kind(p->tok, &next_kind, &next_has_imm)) {
+ wat_parse_memory_index(p, &memidx);
+ wat_next(p);
+ }
+ }
+ wasm_func_add_insn(p->c, p->module, f,
+ kind == WASM_INSN_MEMORY_GROW ? WASM_INSN_MEMORY_GROW
+ : WASM_INSN_MEMORY_SIZE,
+ 0);
+ f->insns[f->ninsns - 1u].memidx = memidx;
+ wat_expect(p, WT_RPAREN, "')'");
+ return;
+ }
+ if (has_imm) {
+ if (kind == WASM_INSN_F32_CONST || kind == WASM_INSN_F64_CONST) {
+ double fv;
+ if (!wat_parse_f64(p, &fv))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected float immediate");
+ wat_next(p);
+ while (p->tok.kind == WT_LPAREN) wat_parse_instr(p, f);
+ wasm_func_add_fp_insn(p->c, p->module, f, kind, fv);
+ wat_expect(p, WT_RPAREN, "')'");
+ return;
+ } else {
+ wat_parse_instr_imm(p, f, kind, &imm);
+ wat_next(p);
+ }
+ }
+ while (p->tok.kind == WT_LPAREN) wat_parse_instr(p, f);
+ wasm_func_add_insn(p->c, p->module, f, kind, imm);
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_instr(WatParser* p, WasmFunc* f) {
+ WasmInsnKind kind;
+ int has_imm;
+ if (p->tok.kind == WT_LPAREN) {
+ wat_parse_instr_list(p, f);
+ return;
+ }
+ if (!wat_instr_kind(p->tok, &kind, &has_imm))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: unsupported instruction");
+ wat_check_instr_feature(p, kind);
+ wat_next(p);
+ if (kind == WASM_INSN_BLOCK || kind == WASM_INSN_LOOP)
+ wat_reject_inline_result(p, "block");
+ else if (kind == WASM_INSN_IF)
+ wat_reject_inline_result(p, "if");
+ if (wasm_insn_is_mem(kind)) {
+ uint32_t align = 0, memidx = 0;
+ uint64_t offset = 0;
+ wat_parse_mem_attrs(p, &align, &offset, &memidx);
+ wasm_func_add_mem_insn(p->c, p->module, f, kind, align, offset, memidx);
+ return;
+ }
+ if (kind == WASM_INSN_BR_TABLE) {
+ WasmInsn* in;
+ uint32_t n = 0;
+ wasm_func_add_insn(p->c, p->module, f, WASM_INSN_BR_TABLE, 0);
+ in = &f->insns[f->ninsns - 1u];
+ while (p->tok.kind == WT_ATOM) {
+ WasmInsnKind next_kind;
+ int next_has_imm;
+ int64_t target;
+ if (wat_instr_kind(p->tok, &next_kind, &next_has_imm)) break;
+ if (n >= 16u)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: too many br_table targets");
+ if (!wat_parse_i64(p, &target) || target < 0 || target > UINT32_MAX)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: bad br_table target");
+ in->targets[n++] = (uint32_t)target;
+ wat_next(p);
+ }
+ in->ntargets = n;
+ return;
+ }
+ if (kind == WASM_INSN_CALL_INDIRECT ||
+ kind == WASM_INSN_RETURN_CALL_INDIRECT) {
+ uint32_t typeidx = wat_parse_call_indirect_type(p);
+ wasm_func_add_insn(p->c, p->module, f, kind, typeidx);
+ return;
+ }
+ if (kind == WASM_INSN_CALL_REF || kind == WASM_INSN_RETURN_CALL_REF) {
+ uint32_t typeidx = wat_parse_call_indirect_type(p);
+ wasm_func_add_insn(p->c, p->module, f, kind, typeidx);
+ return;
+ }
+ if (kind == WASM_INSN_REF_NULL) {
+ int64_t imm;
+ wat_parse_ref_null_type(p, &imm);
+ wasm_func_add_insn(p->c, p->module, f, kind, imm);
+ wat_next(p);
+ return;
+ }
+ if (kind == WASM_INSN_REF_FUNC) {
+ int64_t imm;
+ wat_parse_func_index(p, &imm);
+ wasm_func_add_insn(p->c, p->module, f, kind, imm);
+ wat_next(p);
+ return;
+ }
+ if (kind == WASM_INSN_MEMORY_SIZE || kind == WASM_INSN_MEMORY_GROW) {
+ uint32_t memidx = 0;
+ WasmInsnKind next_kind;
+ int next_has_imm;
+ if (p->tok.kind == WT_ATOM && !wat_instr_kind(p->tok, &next_kind,
+ &next_has_imm)) {
+ wat_parse_memory_index(p, &memidx);
+ wat_next(p);
+ }
+ wasm_func_add_insn(p->c, p->module, f, kind, 0);
+ f->insns[f->ninsns - 1u].memidx = memidx;
+ return;
+ }
+ if (has_imm) {
+ if (kind == WASM_INSN_F32_CONST || kind == WASM_INSN_F64_CONST) {
+ double fv;
+ if (!wat_parse_f64(p, &fv))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected float immediate");
+ wasm_func_add_fp_insn(p->c, p->module, f, kind, fv);
+ wat_next(p);
+ } else {
+ int64_t imm;
+ wat_parse_instr_imm(p, f, kind, &imm);
+ wasm_func_add_insn(p->c, p->module, f, kind, imm);
+ wat_next(p);
+ }
+ } else {
+ wasm_func_add_insn(p->c, p->module, f, kind, 0);
+ }
+}
+
+static void wat_parse_func(WatParser* p) {
+ WasmFunc* f = wasm_add_func(p->c, p->module);
+ uint32_t checked_params = 0;
+ uint32_t checked_results = 0;
+ wat_expect(p, WT_LPAREN, "'('");
+ if (!tok_is(p->tok, "func"))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected func");
+ wat_next(p);
+ if (p->tok.kind == WT_ATOM && p->tok.len > 0 && p->tok.p[0] == '$') {
+ f->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
+ if (!f->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ wat_next(p);
+ }
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
+ if (p->tok.kind == WT_LPAREN) {
+ wat_next(p);
+ if (tok_is(p->tok, "export")) {
+ wat_next(p);
+ if (p->tok.kind != WT_STRING)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected export string");
+ f->export_name = wat_dup_string(p, p->tok, NULL);
+ {
+ WasmExport* ex = wasm_add_export(p->c, p->module);
+ ex->name = wasm_strdup(p->module->heap, f->export_name,
+ strlen(f->export_name));
+ if (!ex->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ ex->kind = 0;
+ ex->index = p->module->nfuncs - 1u;
+ }
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+ } else if (tok_is(p->tok, "type")) {
+ int64_t typeidx;
+ wat_next(p);
+ wat_parse_type_index(p, &typeidx);
+ if (typeidx < 0 || (uint64_t)typeidx >= p->module->ntypes)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: type index out of range");
+ f->typeidx = (uint32_t)typeidx;
+ f->has_typeidx = 1;
+ f->nparams = p->module->types[typeidx].nparams;
+ memcpy(f->params, p->module->types[typeidx].params,
+ sizeof(f->params[0]) * f->nparams);
+ f->nresults = p->module->types[typeidx].nresults;
+ memcpy(f->results, p->module->types[typeidx].results,
+ sizeof(f->results[0]) * f->nresults);
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+ } else if (tok_is(p->tok, "param")) {
+ WasmTok pending_name;
+ int have_name = 0;
+ memset(&pending_name, 0, sizeof pending_name);
+ wat_next(p);
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
+ WasmValType vt;
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ pending_name = p->tok;
+ have_name = 1;
+ wat_next(p);
+ continue;
+ }
+ if (!wat_val_type(p->tok, &vt))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected parameter type");
+ if (!wasm_is_frontend_value_type(vt))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: unsupported parameter type");
+ if (f->has_typeidx) {
+ if (checked_params >= f->nparams || f->params[checked_params] != vt)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: parameter does not match type");
+ if (have_name) {
+ f->local_names[checked_params] = wasm_strdup(
+ p->module->heap, pending_name.p, pending_name.len);
+ if (!f->local_names[checked_params])
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ have_name = 0;
+ }
+ checked_params++;
+ wat_next(p);
+ continue;
+ }
+ if (f->nparams >= 16u)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: too many parameters");
+ if (have_name) {
+ f->local_names[f->nparams] =
+ wasm_strdup(p->module->heap, pending_name.p, pending_name.len);
+ if (!f->local_names[f->nparams])
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ have_name = 0;
+ }
+ f->params[f->nparams++] = vt;
+ wat_next(p);
+ }
+ wat_expect(p, WT_RPAREN, "')'");
+ } else if (tok_is(p->tok, "result")) {
+ WasmValType vt;
+ wat_next(p);
+ if (!wat_val_type(p->tok, &vt))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected result type");
+ if (!wasm_is_frontend_value_type(vt))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: unsupported result type");
+ if (f->has_typeidx) {
+ if (checked_results >= f->nresults ||
+ f->results[checked_results] != vt)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: result does not match type");
+ checked_results++;
+ } else {
+ f->results[0] = vt;
+ f->nresults = 1;
+ }
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+ } else if (tok_is(p->tok, "local")) {
+ WasmTok pending_name;
+ int have_name = 0;
+ memset(&pending_name, 0, sizeof pending_name);
+ wat_next(p);
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
+ WasmValType vt;
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ pending_name = p->tok;
+ have_name = 1;
+ wat_next(p);
+ continue;
+ }
+ if (!wat_val_type(p->tok, &vt))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected local type");
+ if (f->nlocals >= 32u)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: too many locals");
+ if (have_name) {
+ uint32_t index = f->nparams + f->nlocals;
+ f->local_names[index] =
+ wasm_strdup(p->module->heap, pending_name.p, pending_name.len);
+ if (!f->local_names[index])
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ have_name = 0;
+ }
+ f->locals[f->nlocals++] = vt;
+ wat_next(p);
+ }
+ wat_expect(p, WT_RPAREN, "')'");
+ } else {
+ p->pos = (size_t)(p->tok.p - p->src);
+ p->line = p->tok.line;
+ p->col = p->tok.col;
+ p->tok.kind = WT_LPAREN;
+ p->tok.p = p->src + p->pos - 1u;
+ p->tok.len = 1;
+ p->tok.line = p->line;
+ p->tok.col = p->col - 1u;
+ wat_parse_instr(p, f);
+ }
+ } else {
+ wat_parse_instr(p, f);
+ }
+ }
+ if (!f->has_typeidx)
+ f->typeidx = wasm_intern_func_type(p->c, p->module, f);
+ else if (checked_params && checked_params != f->nparams)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: parameter list does not match type");
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_type_field(WatParser* p) {
+ WasmFuncType* t = wasm_add_type(p->c, p->module);
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ t->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
+ if (!t->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ wat_next(p);
+ }
+ wat_expect(p, WT_LPAREN, "'('");
+ if (!tok_is(p->tok, "func"))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected func type");
+ wat_next(p);
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
+ wat_expect(p, WT_LPAREN, "'('");
+ if (tok_is(p->tok, "param")) {
+ wat_next(p);
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
+ WasmValType vt;
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ wat_next(p);
+ continue;
+ }
+ if (!wat_val_type(p->tok, &vt) || !wasm_is_num_type(vt))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected parameter type");
+ if (t->nparams >= 16u)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: too many parameters");
+ t->params[t->nparams++] = vt;
+ wat_next(p);
+ }
+ } else if (tok_is(p->tok, "result")) {
+ wat_next(p);
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
+ WasmValType vt;
+ if (!wat_val_type(p->tok, &vt) || !wasm_is_num_type(vt))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected result type");
+ if (t->nresults >= 1u)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: multi-result unsupported");
+ t->results[t->nresults++] = vt;
+ wat_next(p);
+ }
+ } else {
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected type field");
+ }
+ wat_expect(p, WT_RPAREN, "')'");
+ }
+ wat_expect(p, WT_RPAREN, "')'");
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_export_field(WatParser* p) {
+ char* name;
+ int64_t idx = 0;
+ uint8_t kind;
+ if (p->tok.kind != WT_STRING)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected export string");
+ name = wat_dup_string(p, p->tok, NULL);
+ wat_next(p);
+ wat_expect(p, WT_LPAREN, "'('");
+ kind = wasm_export_kind_from_tok(p->tok);
+ if (kind == 0xffu)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected export kind");
+ wat_next(p);
+ if (kind == 0)
+ wat_parse_func_index(p, &idx);
+ else if (!wat_parse_i64(p, &idx))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected export index");
+ if (idx < 0 || idx > UINT32_MAX)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: export index out of range");
+ {
+ WasmExport* ex = wasm_add_export(p->c, p->module);
+ ex->name = name;
+ ex->kind = kind;
+ ex->index = (uint32_t)idx;
+ }
+ if (kind == 0 && (uint64_t)idx < p->module->nfuncs) {
+ wasm_free_str(p->module->heap, &p->module->funcs[idx].export_name);
+ p->module->funcs[idx].export_name =
+ wasm_strdup(p->module->heap, name, strlen(name));
+ if (!p->module->funcs[idx].export_name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ } else if (kind == 2 && (uint64_t)idx < p->module->nmemories) {
+ wasm_free_str(p->module->heap,
+ &p->module->memories[(uint32_t)idx].export_name);
+ p->module->memories[(uint32_t)idx].export_name =
+ wasm_strdup(p->module->heap, name, strlen(name));
+ if (!p->module->memories[(uint32_t)idx].export_name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ } else if (kind == 1 && (uint64_t)idx < p->module->ntables) {
+ wasm_free_str(p->module->heap, &p->module->tables[idx].export_name);
+ p->module->tables[idx].export_name =
+ wasm_strdup(p->module->heap, name, strlen(name));
+ if (!p->module->tables[idx].export_name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ } else if (kind == 3 && (uint64_t)idx < p->module->nglobals) {
+ wasm_free_str(p->module->heap, &p->module->globals[idx].export_name);
+ p->module->globals[idx].export_name =
+ wasm_strdup(p->module->heap, name, strlen(name));
+ if (!p->module->globals[idx].export_name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ }
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_memory_field(WatParser* p) {
+ int64_t min_pages, max_pages;
+ WasmMemory* mem = wasm_add_memory(p->c, p->module);
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ mem->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
+ if (!mem->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ wat_next(p);
+ }
+ while (p->tok.kind == WT_ATOM && (tok_is(p->tok, "i64") ||
+ tok_is(p->tok, "shared"))) {
+ if (tok_is(p->tok, "i64"))
+ mem->is64 = 1;
+ else
+ mem->shared = 1;
+ wat_next(p);
+ }
+ if (!wat_parse_i64(p, &min_pages) || min_pages < 0)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected memory minimum");
+ mem->min_pages = (uint64_t)min_pages;
+ wat_next(p);
+ while (p->tok.kind == WT_ATOM && (tok_is(p->tok, "i64") ||
+ tok_is(p->tok, "shared"))) {
+ if (tok_is(p->tok, "i64"))
+ mem->is64 = 1;
+ else
+ mem->shared = 1;
+ wat_next(p);
+ }
+ if (p->tok.kind != WT_RPAREN) {
+ if (!wat_parse_i64(p, &max_pages) || max_pages < min_pages)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: bad memory maximum");
+ mem->has_max = 1;
+ mem->max_pages = (uint64_t)max_pages;
+ wat_next(p);
+ while (p->tok.kind == WT_ATOM && (tok_is(p->tok, "i64") ||
+ tok_is(p->tok, "shared"))) {
+ if (tok_is(p->tok, "i64"))
+ mem->is64 = 1;
+ else
+ mem->shared = 1;
+ wat_next(p);
+ }
+ }
+ wasm_memory_ensure(p->c, p->module, p->module->nmemories - 1u,
+ mem->min_pages * 65536u);
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_memory_limits(WatParser* p, WasmMemory* mem) {
+ int64_t lo, hi;
+ while (p->tok.kind == WT_ATOM && (tok_is(p->tok, "i64") ||
+ tok_is(p->tok, "shared"))) {
+ if (tok_is(p->tok, "i64"))
+ mem->is64 = 1;
+ else
+ mem->shared = 1;
+ wat_next(p);
+ }
+ if (!wat_parse_i64(p, &lo) || lo < 0)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected memory minimum");
+ mem->min_pages = (uint64_t)lo;
+ wat_next(p);
+ while (p->tok.kind == WT_ATOM && (tok_is(p->tok, "i64") ||
+ tok_is(p->tok, "shared"))) {
+ if (tok_is(p->tok, "i64"))
+ mem->is64 = 1;
+ else
+ mem->shared = 1;
+ wat_next(p);
+ }
+ if (p->tok.kind != WT_RPAREN) {
+ if (!wat_parse_i64(p, &hi) || hi < lo)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: bad memory maximum");
+ mem->has_max = 1;
+ mem->max_pages = (uint64_t)hi;
+ wat_next(p);
+ while (p->tok.kind == WT_ATOM && (tok_is(p->tok, "i64") ||
+ tok_is(p->tok, "shared"))) {
+ if (tok_is(p->tok, "i64"))
+ mem->is64 = 1;
+ else
+ mem->shared = 1;
+ wat_next(p);
+ }
+ }
+}
+
+static void wat_parse_table_limits_and_type(WatParser* p, WasmTable* t) {
+ int64_t lo, hi;
+ if (!wat_parse_i64(p, &lo) || lo < 0 || lo > UINT32_MAX)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected table minimum");
+ t->min = (uint32_t)lo;
+ wat_next(p);
+ if (p->tok.kind == WT_ATOM) {
+ WasmValType maybe_type;
+ if (!wat_val_type(p->tok, &maybe_type)) {
+ if (!wat_parse_i64(p, &hi) || hi < lo || hi > UINT32_MAX)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: bad table maximum");
+ t->has_max = 1;
+ t->max = (uint32_t)hi;
+ wat_next(p);
+ }
+ }
+ if (!wat_val_type(p->tok, &t->elem_type))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected table element type");
+ wat_next(p);
+}
+
+static void wat_parse_import_field(WatParser* p) {
+ char *mod, *name;
+ if (p->tok.kind != WT_STRING)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected import module string");
+ mod = wat_dup_string(p, p->tok, NULL);
+ wat_next(p);
+ if (p->tok.kind != WT_STRING)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected import name string");
+ name = wat_dup_string(p, p->tok, NULL);
+ wat_next(p);
+ wat_expect(p, WT_LPAREN, "'('");
+ if (tok_is(p->tok, "func")) {
+ WasmFunc* f = wasm_add_func(p->c, p->module);
+ f->is_import = 1;
+ f->import_module = mod;
+ f->import_name = name;
+ wat_next(p);
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ f->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
+ if (!f->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ wat_next(p);
+ }
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
+ wat_expect(p, WT_LPAREN, "'('");
+ if (tok_is(p->tok, "type")) {
+ int64_t typeidx;
+ wat_next(p);
+ wat_parse_type_index(p, &typeidx);
+ if (typeidx < 0 || (uint64_t)typeidx >= p->module->ntypes)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: type index out of range");
+ f->typeidx = (uint32_t)typeidx;
+ f->has_typeidx = 1;
+ f->nparams = p->module->types[typeidx].nparams;
+ memcpy(f->params, p->module->types[typeidx].params,
+ sizeof(f->params[0]) * f->nparams);
+ f->nresults = p->module->types[typeidx].nresults;
+ memcpy(f->results, p->module->types[typeidx].results,
+ sizeof(f->results[0]) * f->nresults);
+ wat_next(p);
+ } else if (tok_is(p->tok, "param")) {
+ wat_next(p);
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
+ WasmValType vt;
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ wat_next(p);
+ continue;
+ }
+ if (!wat_val_type(p->tok, &vt) || !wasm_is_frontend_value_type(vt))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected parameter type");
+ if (f->nparams >= 16u)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: too many parameters");
+ f->params[f->nparams++] = vt;
+ wat_next(p);
+ }
+ } else if (tok_is(p->tok, "result")) {
+ wat_next(p);
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
+ WasmValType vt;
+ if (!wat_val_type(p->tok, &vt) || !wasm_is_frontend_value_type(vt))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected result type");
+ if (f->nresults >= 1u)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: multi-result unsupported");
+ f->results[f->nresults++] = vt;
+ wat_next(p);
+ }
+ } else {
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected import func type field");
+ }
+ wat_expect(p, WT_RPAREN, "')'");
+ }
+ if (!f->has_typeidx) f->typeidx = wasm_intern_func_type(p->c, p->module, f);
+ } else if (tok_is(p->tok, "memory")) {
+ WasmMemory* mem = wasm_add_memory(p->c, p->module);
+ mem->is_import = 1;
+ mem->import_module = mod;
+ mem->import_name = name;
+ wat_next(p);
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ mem->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
+ if (!mem->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ wat_next(p);
+ }
+ wat_parse_memory_limits(p, mem);
+ } else if (tok_is(p->tok, "table")) {
+ WasmTable* t = wasm_add_table(p->c, p->module);
+ t->is_import = 1;
+ t->import_module = mod;
+ t->import_name = name;
+ wat_next(p);
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ t->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
+ if (!t->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ wat_next(p);
+ }
+ wat_parse_table_limits_and_type(p, t);
+ } else if (tok_is(p->tok, "global")) {
+ WasmGlobal* g = wasm_add_global(p->c, p->module);
+ g->is_import = 1;
+ g->import_module = mod;
+ g->import_name = name;
+ wat_next(p);
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ g->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
+ if (!g->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ wat_next(p);
+ }
+ if (p->tok.kind == WT_LPAREN) {
+ wat_next(p);
+ if (!tok_is(p->tok, "mut"))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected mut");
+ g->mutable_ = 1;
+ wat_next(p);
+ if (!wat_val_type(p->tok, &g->type) || !wasm_is_num_type(g->type))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected global type");
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+ } else {
+ if (!wat_val_type(p->tok, &g->type) || !wasm_is_num_type(g->type))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected global type");
+ wat_next(p);
+ }
+ } else {
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: unsupported import kind");
+ }
+ wat_expect(p, WT_RPAREN, "')'");
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_table_field(WatParser* p) {
+ WasmTable* t = wasm_add_table(p->c, p->module);
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ t->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
+ if (!t->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ wat_next(p);
+ }
+ wat_parse_table_limits_and_type(p, t);
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_const_expr(WatParser* p, WasmInsn* out) {
+ WasmInsnKind kind;
+ int has_imm;
+ memset(out, 0, sizeof *out);
+ wat_expect(p, WT_LPAREN, "'('");
+ if (!wat_instr_kind(p->tok, &kind, &has_imm) ||
+ (kind != WASM_INSN_I32_CONST && kind != WASM_INSN_I64_CONST &&
+ kind != WASM_INSN_F32_CONST && kind != WASM_INSN_F64_CONST))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected constant expression");
+ out->kind = (uint8_t)kind;
+ wat_next(p);
+ if (kind == WASM_INSN_F32_CONST || kind == WASM_INSN_F64_CONST) {
+ if (!wat_parse_f64(p, &out->fp))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected float immediate");
+ } else if (!wat_parse_i64(p, &out->imm)) {
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected integer immediate");
+ }
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_global_field(WatParser* p) {
+ WasmGlobal* g = wasm_add_global(p->c, p->module);
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ g->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
+ if (!g->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ wat_next(p);
+ }
+ if (p->tok.kind == WT_LPAREN) {
+ wat_next(p);
+ if (tok_is(p->tok, "export")) {
+ wat_next(p);
+ if (p->tok.kind != WT_STRING)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected export string");
+ g->export_name = wat_dup_string(p, p->tok, NULL);
+ {
+ WasmExport* ex = wasm_add_export(p->c, p->module);
+ ex->name = wasm_strdup(p->module->heap, g->export_name,
+ strlen(g->export_name));
+ if (!ex->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ ex->kind = 3;
+ ex->index = p->module->nglobals - 1u;
+ }
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+ } else if (tok_is(p->tok, "mut")) {
+ g->mutable_ = 1;
+ wat_next(p);
+ if (!wat_val_type(p->tok, &g->type) || !wasm_is_num_type(g->type))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected global type");
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+ } else {
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected global type");
+ }
+ } else {
+ if (!wat_val_type(p->tok, &g->type) || !wasm_is_num_type(g->type))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected global type");
+ wat_next(p);
+ }
+ if (!g->type)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: missing global type");
+ wat_parse_const_expr(p, &g->init);
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_elem_field(WatParser* p) {
+ WasmElemSegment* e = wasm_add_elem(p->c, p->module);
+ e->tableidx = 0;
+ if (p->tok.kind == WT_ATOM) {
+ int64_t tableidx;
+ if (wat_parse_i64(p, &tableidx)) {
+ if (tableidx < 0 || tableidx > UINT32_MAX)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: table index out of range");
+ e->tableidx = (uint32_t)tableidx;
+ wat_next(p);
+ }
+ }
+ {
+ WasmInsn off;
+ wat_parse_const_expr(p, &off);
+ if (off.kind != WASM_INSN_I32_CONST || off.imm < 0)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: element offset must be i32.const");
+ e->offset = off.imm;
+ }
+ if (tok_is(p->tok, "func")) wat_next(p);
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
+ int64_t idx;
+ if (e->nfuncs >= 64u)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: too many element functions");
+ wat_parse_func_index(p, &idx);
+ if (idx < 0 || idx > UINT32_MAX)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: element function index out of range");
+ e->funcs[e->nfuncs++] = (uint32_t)idx;
+ wat_next(p);
+ }
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_start_field(WatParser* p) {
+ int64_t idx;
+ wat_parse_func_index(p, &idx);
+ if (idx < 0 || idx > UINT32_MAX)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: start function index out of range");
+ p->module->has_start = 1;
+ p->module->start_func = (uint32_t)idx;
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_custom_field(WatParser* p) {
+ WasmCustom* cs;
+ size_t n = 0;
+ char* bytes;
+ if (p->tok.kind != WT_STRING)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected custom section name");
+ cs = wasm_add_custom(p->c, p->module);
+ cs->name = wat_dup_string(p, p->tok, NULL);
+ if (strcmp(cs->name, "target_features") == 0 ||
+ strcmp(cs->name, "target-feature") == 0)
+ p->module->has_target_features = 1;
+ wat_next(p);
+ if (p->tok.kind == WT_STRING) {
+ bytes = wat_dup_string(p, p->tok, &n);
+ cs->data = (uint8_t*)p->module->heap->alloc(p->module->heap, n ? n : 1u, 1);
+ if (!cs->data)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ memcpy(cs->data, bytes, n);
+ cs->len = (uint32_t)n;
+ p->module->heap->free(p->module->heap, bytes, p->tok.len + 1u);
+ wat_next(p);
+ }
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_data_field(WatParser* p) {
+ int64_t offset = 0;
+ uint32_t memidx = 0;
+ char* bytes;
+ size_t nbytes;
+ size_t alloc_len;
+ if (p->tok.kind == WT_ATOM) {
+ WasmInsnKind next_kind;
+ int next_has_imm;
+ if (p->tok.len && p->tok.p[0] == '$') {
+ wat_next(p);
+ } else if (!wat_instr_kind(p->tok, &next_kind, &next_has_imm)) {
+ wat_parse_memory_index(p, &memidx);
+ wat_next(p);
+ } else {
+ wat_next(p);
+ }
+ }
+ if (p->tok.kind == WT_LPAREN) {
+ size_t save_pos = p->pos;
+ uint32_t save_line = p->line, save_col = p->col;
+ wat_next(p);
+ if (tok_is(p->tok, "memory")) {
+ wat_next(p);
+ wat_parse_memory_index(p, &memidx);
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+ } else {
+ p->pos = save_pos;
+ p->line = save_line;
+ p->col = save_col;
+ p->tok.kind = WT_LPAREN;
+ p->tok.p = p->src + p->pos - 1u;
+ p->tok.len = 1;
+ p->tok.line = save_line;
+ p->tok.col = save_col - 1u;
+ }
+ }
+ wat_expect(p, WT_LPAREN, "'('");
+ if (!tok_is(p->tok, "i32.const") && !tok_is(p->tok, "i64.const"))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected const data offset");
+ wat_next(p);
+ if (!wat_parse_i64(p, &offset) || offset < 0 || offset > UINT32_MAX)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: bad data offset");
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+ if (p->tok.kind != WT_STRING)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected data string");
+ alloc_len = p->tok.len + 1u;
+ bytes = wat_dup_string(p, p->tok, &nbytes);
+ if ((uint64_t)offset + nbytes > UINT32_MAX)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: data segment too large");
+ wasm_memory_ensure(p->c, p->module, memidx,
+ (uint64_t)offset + (uint64_t)nbytes);
+ memcpy(p->module->memories[memidx].data + (uint32_t)offset, bytes, nbytes);
+ if ((uint64_t)offset + nbytes > p->module->memories[memidx].data_init_len)
+ p->module->memories[memidx].data_init_len =
+ (uint64_t)offset + (uint64_t)nbytes;
+ p->module->heap->free(p->module->heap, bytes, alloc_len);
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+void wasm_parse_wat(CfreeCompiler* c, const CfreeBytes* input,
+ WasmModule* out) {
+ WatParser p;
+ memset(&p, 0, sizeof p);
+ p.c = c;
+ p.name = input->name;
+ p.src = (const char*)input->data;
+ p.len = input->len;
+ p.line = 1;
+ p.col = 1;
+ p.module = out;
+ wat_next(&p);
+ wat_expect(&p, WT_LPAREN, "'('");
+ if (!tok_is(p.tok, "module"))
+ wasm_error(c, wasm_loc(p.tok.line, p.tok.col), "wasm wat: expected module");
+ wat_next(&p);
+ while (p.tok.kind != WT_RPAREN && p.tok.kind != WT_EOF) {
+ if (p.tok.kind != WT_LPAREN)
+ wasm_error(c, wasm_loc(p.tok.line, p.tok.col),
+ "wasm wat: expected module field");
+ wat_next(&p);
+ if (tok_is(p.tok, "type")) {
+ wat_next(&p);
+ wat_parse_type_field(&p);
+ } else if (tok_is(p.tok, "func")) {
+ p.pos = (size_t)(p.tok.p - p.src);
+ p.line = p.tok.line;
+ p.col = p.tok.col;
+ p.tok.kind = WT_LPAREN;
+ p.tok.p = p.src + p.pos - 1u;
+ p.tok.len = 1;
+ p.tok.line = p.line;
+ p.tok.col = p.col - 1u;
+ wat_parse_func(&p);
+ } else if (tok_is(p.tok, "export")) {
+ wat_next(&p);
+ wat_parse_export_field(&p);
+ } else if (tok_is(p.tok, "memory")) {
+ wat_next(&p);
+ wat_parse_memory_field(&p);
+ } else if (tok_is(p.tok, "data")) {
+ wat_next(&p);
+ wat_parse_data_field(&p);
+ } else if (tok_is(p.tok, "import")) {
+ wat_next(&p);
+ wat_parse_import_field(&p);
+ } else if (tok_is(p.tok, "table")) {
+ wat_next(&p);
+ wat_parse_table_field(&p);
+ } else if (tok_is(p.tok, "global")) {
+ wat_next(&p);
+ wat_parse_global_field(&p);
+ } else if (tok_is(p.tok, "elem")) {
+ wat_next(&p);
+ wat_parse_elem_field(&p);
+ } else if (tok_is(p.tok, "start")) {
+ wat_next(&p);
+ wat_parse_start_field(&p);
+ } else if (tok_is(p.tok, "custom") || tok_is(p.tok, "@custom")) {
+ wat_next(&p);
+ wat_parse_custom_field(&p);
+ } else {
+ wat_skip_list(&p);
+ }
+ }
+ wat_expect(&p, WT_RPAREN, "')'");
+ if (p.tok.kind != WT_EOF)
+ wasm_error(c, wasm_loc(p.tok.line, p.tok.col),
+ "wasm wat: trailing tokens after module");
+}