kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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:
Alang/wasm/cg.c | 2048+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alang/wasm/decode.c | 1195+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alang/wasm/encode.c | 778+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alang/wasm/insn.c | 409+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alang/wasm/module.c | 328+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alang/wasm/validate.c | 665+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlang/wasm/wasm.c | 8307+------------------------------------------------------------------------------
Alang/wasm/wasm_internal.h | 416+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alang/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"); +}