kit

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

commit e4068e8e473f46e36b336a686cba4a1262ac23fc
parent 472d97ae4494c11c424d81095648ee83c1a50dd2
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon, 18 May 2026 07:38:59 -0700

Add staged wasm proposal frontend support

Diffstat:
Mdoc/WASM.md | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mdriver/run.c | 9++++++---
Mlang/wasm/runtime_abi.h | 10++++++++--
Mlang/wasm/wasm.c | 1070++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mtest/link/harness/jit_runner.c | 9++++++---
Atest/wasm/cases/atomic_memory.expect | 1+
Atest/wasm/cases/atomic_memory.wat | 9+++++++++
Atest/wasm/cases/memory64.expect | 1+
Atest/wasm/cases/memory64.wat | 13+++++++++++++
Atest/wasm/cases/multi_memory.expect | 1+
Atest/wasm/cases/multi_memory.wat | 13+++++++++++++
Atest/wasm/cases/tail_call.expect | 1+
Atest/wasm/cases/tail_call.wat | 19+++++++++++++++++++
Mtest/wasm/harness/start_wasm.c | 11++++++++---
14 files changed, 979 insertions(+), 303 deletions(-)

diff --git a/doc/WASM.md b/doc/WASM.md @@ -627,24 +627,69 @@ the instance; the instance parameter remains part of the C-facing contract. ### Instruction Coverage -Frontend v1 should target the MVP numeric/control/memory subset: +Frontend v1 should target the MVP numeric/control/memory subset first: - numeric constants, integer and FP arithmetic/comparison/conversion, `select`, `drop`, `unreachable`. - `local.get/set/tee`, `global.get/set`. - `block`, `loop`, `if`, `else`, `br`, `br_if`, `br_table`, `return`. - direct `call`; `call_indirect` after table representation exists. -- memory load/store, `memory.size`, `memory.grow`, and active data segments. +- memory load/store, `memory.size`, `memory.grow`, and active data segments + for memory index 0. -Defer or reject: +Then extend the same frontend path for these proposals, behind explicit +`WasmFeatureSet` bits and targeted fixtures: -- reference types beyond basic `funcref` for tables. -- exceptions/tags, GC, typed function references, tail-call input lowering, - threads/atomics, SIMD, multiple memories, memory64, and component model. +- threads: shared memory limits, atomic loads/stores/RMW/cmpxchg, + `memory.atomic.wait*`, `memory.atomic.notify`, and `atomic.fence`. +- typed function references: typed `funcref` tables, `ref.null`, `ref.func`, + `ref.is_null`, `call_ref`, and validation of reference subtyping needed by + calls and table operations. +- tail calls: `return_call`, `return_call_indirect`, and typed-reference tail + calls when typed function refs are enabled. +- multi-memory: memory imports/definitions beyond index 0, memory-indexed + memory ops, and per-memory data initialization. +- memory64: 64-bit memory limits, i64 memory indices, i64 `memory.size` and + `memory.grow`, and 64-bit bounds checking in the instance runtime. + +Still defer or reject: + +- reference types beyond the typed-function-reference subset. +- exceptions/tags, GC, SIMD, and component model. Validation should run before lowering. A malformed module should never reach CG emission. +### Frontend Proposal Support + +Proposal support belongs in the Wasm frontend, validator, native lowering, and +runtime instance model before it belongs in the Wasm target backend. A module +that uses a proposal must be decoded into the shared `WasmModule`, validated +under an enabled `WasmFeatureSet`, and either lowered semantically to native CG +or rejected with a diagnostic naming the missing feature. + +Do not add proposal-specific global state. Threads and shared memories still +hang off `CfreeWasmInstance`; wait/notify and blocking behavior go through +runtime hooks owned by the instance or embedder. Multi-memory and memory64 +should replace single-memory helper assumptions with indexed memory helpers +rather than adding parallel special cases. + +Lower tail-call instructions through the existing CG tail-call surface: +`return_call` and `return_call_indirect` are required-tail operations and should +use `CFREE_CG_TAIL_MUST`. If the selected native target cannot guarantee the +tail-call shape, diagnose before emission instead of silently compiling them as +ordinary calls. + +Typed function references should share the existing table and `call_indirect` +runtime checks where possible, but the type identity must come from Wasm +function types, not C wrapper signatures. `call_ref` must trap on null refs and +must validate the referenced function type before lowering the call. + +Memory64 is a frontend/runtime property, not permission to use native pointer +width as the Wasm address type. The lowered address calculation remains i64, +then the runtime checks that the current host allocation can represent the +effective range before converting to a host pointer. + ## Phasing ### Phase 0: Format Spine @@ -790,9 +835,13 @@ slots and runtime table storage remain open work. boundary diagnostics. - [x] Parse float literals for `f32.const` and `f64.const`. - [x] Parse module-level type definitions and `(func (type N) ...)`. -- [x] Parse memories and active data segments for the frontend subset. +- [x] Parse memories and active data segments for the frontend subset, + including indexed memories and memory64 declarations. - [x] Parse imports, tables, globals, elements, start, and custom/name sections. +- [x] Parse staged proposal syntax for memory-indexed memory ops, + `memory.size`/`memory.grow`, `return_call`, `return_call_indirect`, shared + memories, atomic load/store aliases, and `atomic.fence`. - [ ] Preserve source locations through validation and lowering diagnostics. ### Binary Reader and Encoder @@ -804,10 +853,12 @@ slots and runtime table storage remain open work. - [x] Validate section length, ordering, count, and index-space edge cases with direct format tests. - [x] Decode and encode memories and active data segments for the frontend - subset. + subset, including multi-memory memargs/data segments and memory64 limits. - [x] Decode and encode imports, tables, globals, elements, start, and custom/name sections; target-feature custom sections are preserved as raw metadata. +- [x] Decode and encode tail-call opcodes for frontend input and WAT-to-Wasm + roundtrips. - [x] Preserve unknown custom sections in the frontend WAT-to-Wasm/binary module path. - [x] Add deterministic fixtures for malformed LEB128 and truncated bodies. @@ -825,9 +876,42 @@ slots and runtime table storage remain open work. - [x] Validate branch result arity and `br_table`. - [x] Validate memory/table/global/data/element indices, limits, active/passive segment rules, and start function signature. -- [ ] Centralize feature gating in a `WasmFeatureSet`. -- [ ] Add clear diagnostics for every deferred proposal: SIMD, threads, - exceptions, GC, typed function refs, tail calls, multi-memory, and memory64. +- [x] Add internal `WasmFeatureSet` bits for staged frontend proposal support. +- [ ] Centralize WAT/binary feature gates and diagnostics around + `WasmFeatureSet`. +- [ ] Add clear diagnostics for proposals still outside the frontend support + plan: SIMD, exceptions, GC, and component model. + +### Frontend Proposals + +- [x] Add `WasmFeatureSet` bits for threads, typed function refs, tail calls, + multi-memory, and memory64. +- [ ] Add WAT parser gates, binary opcode/section gates, and negative fixtures + for threads, typed function refs, tail calls, multi-memory, and memory64. +- [x] Implement staged threads parsing for shared memories, atomic load/store + aliases, and `atomic.fence`. +- [ ] Implement full threads parsing and validation: atomic RMW/cmpxchg, + wait/notify, legal alignment/type checks, and shared-memory-only rejection. +- [ ] Lower threads proposal operations through CG atomics and instance/runtime + wait/notify hooks without process-global synchronization state. +- [ ] Implement typed function references parsing and validation: typed refs, + `ref.null`, `ref.func`, `ref.is_null`, `call_ref`, and reference-aware table + checks. +- [ ] Lower typed function references through nullable runtime function + reference values and Wasm type-id checks, preserving trap behavior. +- [x] Implement tail-call parsing and validation for `return_call` and + `return_call_indirect`. +- [ ] Implement typed-reference tail calls where enabled. +- [ ] Lower tail calls through `CFREE_CG_TAIL_MUST` and diagnose unsupported + native target tail-call shapes before emission. +- [x] Implement multi-memory parsing, validation, encoding, and runtime + instance layout for more than one imported or defined memory. +- [x] Lower memory-indexed loads/stores, `memory.size`, `memory.grow`, and data + initialization against the selected memory. +- [x] Implement memory64 parsing, validation, encoding, and runtime metadata + for 64-bit limits and i64 memory indices. +- [x] Lower memory64 addressing and bounds checks with i64 arithmetic before + converting checked addresses to host pointers. ### Native Lowering @@ -852,6 +936,13 @@ slots and runtime table storage remain open work. - [x] Lower single linear memory load/store operations, `memory.size`, and active data initialization. - [x] Implement checked single-memory loads/stores and growable storage. +- [x] Lower indexed multi-memory load/store operations, `memory.size`, + `memory.grow`, and active data initialization. +- [x] Lower memory64 address operands and bounds checks using i64 arithmetic. +- [x] Lower `return_call` and `return_call_indirect` with validated result + shape; required native must-tail lowering remains open. +- [x] Lower staged atomic load/store aliases and `atomic.fence` for the current + single-threaded runner semantics. - [x] Implement numeric globals for native lowering. - [x] Implement imported function declarations for native lowering. - [x] Implement tables, active elements, and `call_indirect`. @@ -885,6 +976,8 @@ slots and runtime table storage remain open work. without process-global Wasm state. - [x] Add frontend runner tests for instance-owned import slots and runtime table state once those slots exist. +- [x] Extend `CfreeWasmMemory` and runner/start harness prefixes for 64-bit + page counts, memory flags, and multiple instance-owned memories. ### Wasm Target Backend diff --git a/driver/run.c b/driver/run.c @@ -410,8 +410,9 @@ typedef int (*WasmMainFn)(void*); typedef struct RunWasmMemoryPrefix { uint8_t* data; - uint32_t pages; - uint32_t max_pages; + uint64_t pages; + uint64_t max_pages; + uint32_t flags; } RunWasmMemoryPrefix; static int run_call_wasm_entry(RunOptions* ro, CfreeJit* jit, void* entry, @@ -437,7 +438,9 @@ static int run_call_wasm_entry(RunOptions* ro, CfreeJit* jit, void* entry, *rc_out = 1; return 1; } - ((RunWasmMemoryPrefix*)instance)->data = memory; + for (uint32_t i = 0; i < 8u; ++i) + ((RunWasmMemoryPrefix*)instance)[i].data = + memory + i * (16u * 1024u * 1024u / 8u); init_u.p = init_sym; entry_u.p = entry; init_u.fn(instance); diff --git a/lang/wasm/runtime_abi.h b/lang/wasm/runtime_abi.h @@ -5,10 +5,16 @@ typedef struct CfreeWasmMemory { uint8_t* data; - uint32_t pages; - uint32_t max_pages; + uint64_t pages; + uint64_t max_pages; + uint32_t flags; } CfreeWasmMemory; +enum { + CFREE_WASM_MEMORY_SHARED = 1u << 0, + CFREE_WASM_MEMORY_64 = 1u << 1, +}; + typedef struct CfreeWasmFuncImport { void* fn; } CfreeWasmFuncImport; diff --git a/lang/wasm/wasm.c b/lang/wasm/wasm.c @@ -17,6 +17,14 @@ typedef enum WasmValType { 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, @@ -38,6 +46,8 @@ typedef enum WasmInsnKind { WASM_INSN_LOCAL_TEE, WASM_INSN_CALL, WASM_INSN_CALL_INDIRECT, + WASM_INSN_RETURN_CALL, + WASM_INSN_RETURN_CALL_INDIRECT, WASM_INSN_GLOBAL_GET, WASM_INSN_GLOBAL_SET, WASM_INSN_RETURN, @@ -174,6 +184,8 @@ typedef struct WasmInsn { int64_t imm; double fp; uint32_t align; + uint32_t memidx; + uint64_t offset64; uint32_t ntargets; uint32_t targets[16]; } WasmInsn; @@ -208,16 +220,18 @@ typedef struct WasmFuncType { typedef struct WasmMemory { char* name; - uint32_t min_pages; - uint32_t max_pages; + 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; - uint32_t data_len; - uint32_t data_init_len; + uint64_t data_len; + uint64_t data_init_len; } WasmMemory; typedef struct WasmTable { @@ -270,8 +284,9 @@ typedef struct WasmModule { WasmFunc* funcs; uint32_t nfuncs; uint32_t cap_funcs; - WasmMemory memory; - int has_memory; + WasmMemory* memories; + uint32_t nmemories; + uint32_t cap_memories; WasmTable* tables; uint32_t ntables; uint32_t cap_tables; @@ -290,6 +305,7 @@ typedef struct WasmModule { uint32_t start_func; int has_start; int has_target_features; + uint32_t features; } WasmModule; typedef struct WasmTok { @@ -366,6 +382,9 @@ static void wasm_free_str(CfreeHeap* h, char** s) { 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) { @@ -387,12 +406,17 @@ static void wasm_module_free(WasmModule* m) { } if (m->funcs) m->heap->free(m->heap, m->funcs, sizeof(*m->funcs) * m->cap_funcs); - wasm_free_str(m->heap, &m->memory.name); - wasm_free_str(m->heap, &m->memory.import_module); - wasm_free_str(m->heap, &m->memory.import_name); - wasm_free_str(m->heap, &m->memory.export_name); - if (m->memory.data) - m->heap->free(m->heap, m->memory.data, m->memory.data_len); + 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); @@ -425,22 +449,45 @@ static void wasm_module_free(WasmModule* m) { } static void wasm_memory_ensure(CfreeCompiler* c, WasmModule* m, - uint32_t min_len) { - uint32_t old_len, new_len; + uint32_t memidx, uint64_t min_len) { + WasmMemory* mem; + uint64_t old_len, new_len; void* p; - if (!m->has_memory) + if (memidx >= m->nmemories) wasm_error(c, wasm_loc(0, 0), "wasm: data segment without memory"); - old_len = m->memory.data_len; + mem = &m->memories[memidx]; + old_len = mem->data_len; new_len = old_len; if (new_len < min_len) new_len = min_len; - if (m->memory.min_pages && new_len < m->memory.min_pages * 65536u) - new_len = m->memory.min_pages * 65536u; + 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, m->memory.data, old_len, new_len); + 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"); - m->memory.data = (uint8_t*)p; - memset(m->memory.data + old_len, 0, new_len - old_len); - m->memory.data_len = new_len; + 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) { @@ -602,15 +649,20 @@ static void wasm_func_add_insn(CfreeCompiler* c, WasmModule* m, WasmFunc* f, 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, - uint32_t offset) { - wasm_func_add_insn(c, m, f, kind, (int64_t)offset); + 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, @@ -989,6 +1041,15 @@ static int wat_instr_kind(WasmTok t, WasmInsnKind* out, int* has_imm) { *out = WASM_INSN_CALL_INDIRECT; 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, "global.get")) { *out = WASM_INSN_GLOBAL_GET; *has_imm = 1; @@ -1007,14 +1068,26 @@ static int wat_instr_kind(WasmTok t, WasmInsnKind* out, int* has_imm) { *out = WASM_INSN_DROP; return 1; } + if (tok_is(t, "atomic.fence")) { + *out = WASM_INSN_NOP; + 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_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_LOAD; + return 1; + } if (tok_is(t, "i32.load8_s")) { *out = WASM_INSN_I32_LOAD8_S; return 1; @@ -1023,6 +1096,10 @@ static int wat_instr_kind(WasmTok t, WasmInsnKind* out, int* has_imm) { *out = WASM_INSN_I32_LOAD8_U; return 1; } + if (tok_is(t, "i32.atomic.load8_u")) { + *out = WASM_INSN_I32_LOAD8_U; + return 1; + } if (tok_is(t, "i32.load16_s")) { *out = WASM_INSN_I32_LOAD16_S; return 1; @@ -1031,6 +1108,10 @@ static int wat_instr_kind(WasmTok t, WasmInsnKind* out, int* has_imm) { *out = WASM_INSN_I32_LOAD16_U; return 1; } + if (tok_is(t, "i32.atomic.load16_u")) { + *out = WASM_INSN_I32_LOAD16_U; + return 1; + } if (tok_is(t, "i64.load8_s")) { *out = WASM_INSN_I64_LOAD8_S; return 1; @@ -1039,6 +1120,10 @@ static int wat_instr_kind(WasmTok t, WasmInsnKind* out, int* has_imm) { *out = WASM_INSN_I64_LOAD8_U; return 1; } + if (tok_is(t, "i64.atomic.load8_u")) { + *out = WASM_INSN_I64_LOAD8_U; + return 1; + } if (tok_is(t, "i64.load16_s")) { *out = WASM_INSN_I64_LOAD16_S; return 1; @@ -1047,6 +1132,10 @@ static int wat_instr_kind(WasmTok t, WasmInsnKind* out, int* has_imm) { *out = WASM_INSN_I64_LOAD16_U; return 1; } + if (tok_is(t, "i64.atomic.load16_u")) { + *out = WASM_INSN_I64_LOAD16_U; + return 1; + } if (tok_is(t, "i64.load32_s")) { *out = WASM_INSN_I64_LOAD32_S; return 1; @@ -1055,34 +1144,66 @@ static int wat_instr_kind(WasmTok t, WasmInsnKind* out, int* has_imm) { *out = WASM_INSN_I64_LOAD32_U; return 1; } + if (tok_is(t, "i64.atomic.load32_u")) { + *out = WASM_INSN_I64_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_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_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_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_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_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_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_STORE32; + return 1; + } if (tok_is(t, "memory.size")) { *out = WASM_INSN_MEMORY_SIZE; return 1; @@ -1578,6 +1699,7 @@ 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: @@ -1645,8 +1767,50 @@ static int wat_parse_u32_atom(WasmTok t, uint32_t* out) { 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, - uint32_t* offset) { + uint64_t* offset, uint32_t* memidx) { while (p->tok.kind == WT_ATOM) { WasmTok val; if (wat_atom_prefix(p->tok, "align=")) { @@ -1661,10 +1825,26 @@ static void wat_parse_mem_attrs(WatParser* p, uint32_t* align, val = p->tok; val.p += 7; val.len -= 7; - if (!wat_parse_u32_atom(val, offset)) + 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; } @@ -1796,10 +1976,29 @@ static void wat_parse_instr_list(WatParser* p, WasmFunc* f) { "wasm wat: unsupported instruction"); wat_next(p); if (wasm_insn_is_mem(kind)) { - uint32_t align = 0, offset = 0; - wat_parse_mem_attrs(p, &align, &offset); - while (p->tok.kind == WT_LPAREN) wat_parse_instr(p, f); - wasm_func_add_mem_insn(p->c, p->module, f, kind, align, offset); + 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; } @@ -1825,13 +2024,52 @@ static void wat_parse_instr_list(WatParser* p, WasmFunc* f) { wat_expect(p, WT_RPAREN, "')'"); return; } - if (kind == WASM_INSN_CALL_INDIRECT) { + 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_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; @@ -1869,9 +2107,10 @@ static void wat_parse_instr(WatParser* p, WasmFunc* f) { else if (kind == WASM_INSN_IF) wat_reject_inline_result(p, "if"); if (wasm_insn_is_mem(kind)) { - uint32_t align = 0, offset = 0; - wat_parse_mem_attrs(p, &align, &offset); - wasm_func_add_mem_insn(p->c, p->module, f, kind, align, offset); + 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) { @@ -1896,11 +2135,25 @@ static void wat_parse_instr(WatParser* p, WasmFunc* f) { in->ntargets = n; return; } - if (kind == WASM_INSN_CALL_INDIRECT) { + 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_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; @@ -2192,11 +2445,12 @@ static void wat_parse_export_field(WatParser* p) { 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 && idx == 0 && p->module->has_memory) { - wasm_free_str(p->module->heap, &p->module->memory.export_name); - p->module->memory.export_name = + } 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->memory.export_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) { @@ -2221,44 +2475,94 @@ static void wat_parse_export_field(WatParser* p) { static void wat_parse_memory_field(WatParser* p) { int64_t min_pages, max_pages; - if (p->module->has_memory) - wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), - "wasm wat: multiple memories are unsupported"); - if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') wat_next(p); - if (!wat_parse_i64(p, &min_pages) || min_pages < 0 || min_pages > UINT16_MAX) + 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"); - p->module->has_memory = 1; - p->module->memory.min_pages = (uint32_t)min_pages; + 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 || - max_pages > UINT16_MAX) + 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"); - p->module->memory.has_max = 1; - p->module->memory.max_pages = (uint32_t)max_pages; + 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->memory.min_pages * 65536u); + wasm_memory_ensure(p->c, p->module, p->module->nmemories - 1u, + mem->min_pages * 65536u); wat_expect(p, WT_RPAREN, "')'"); } -static void wat_parse_limits(WatParser* p, uint32_t* min, uint32_t* max, - int* has_max, const char* what) { +static void wat_parse_memory_limits(WatParser* p, WasmMemory* mem) { int64_t lo, hi; - if (!wat_parse_i64(p, &lo) || lo < 0 || lo > UINT32_MAX) + 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 %s minimum", what); - *min = (uint32_t)lo; + "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 || hi > UINT32_MAX) + if (!wat_parse_i64(p, &hi) || hi < lo) wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), - "wasm wat: bad %s maximum", what); - *has_max = 1; - *max = (uint32_t)hi; + "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); + } } } @@ -2368,25 +2672,19 @@ static void wat_parse_import_field(WatParser* p) { } if (!f->has_typeidx) f->typeidx = wasm_intern_func_type(p->c, p->module, f); } else if (tok_is(p->tok, "memory")) { - if (p->module->has_memory) - wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), - "wasm wat: multiple memories are unsupported"); - p->module->has_memory = 1; - p->module->memory.is_import = 1; - p->module->memory.import_module = mod; - p->module->memory.import_name = name; + 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] == '$') { - p->module->memory.name = - wasm_strdup(p->module->heap, p->tok.p, p->tok.len); - if (!p->module->memory.name) + 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_limits(p, &p->module->memory.min_pages, - &p->module->memory.max_pages, &p->module->memory.has_max, - "memory"); + 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; @@ -2609,14 +2907,46 @@ static void wat_parse_custom_field(WatParser* p) { 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) wat_next(p); + 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")) + 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 i32.const data offset"); + "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), @@ -2631,10 +2961,12 @@ static void wat_parse_data_field(WatParser* p) { 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, (uint32_t)offset + (uint32_t)nbytes); - memcpy(p->module->memory.data + (uint32_t)offset, bytes, nbytes); - if ((uint64_t)offset + nbytes > p->module->memory.data_init_len) - p->module->memory.data_init_len = (uint32_t)((uint64_t)offset + nbytes); + 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, "')'"); @@ -2730,6 +3062,20 @@ static uint32_t bin_uleb(BinReader* r) { } } +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; @@ -2763,6 +3109,21 @@ static double bin_f64(BinReader* r) { 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"); @@ -2903,19 +3264,19 @@ static void wasm_decode_binary(CfreeCompiler* c, const CfreeBytesInput* input, } } else if (kind == 2) { uint32_t flags; - if (out->has_memory) - wasm_error(c, wasm_loc(0, 0), - "wasm: multiple memories unsupported"); - out->has_memory = 1; - out->memory.is_import = 1; - out->memory.import_module = mod; - out->memory.import_name = name; + 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 & ~1u) + if (flags & ~7u) wasm_error(c, wasm_loc(0, 0), "wasm: unsupported memory limits"); - out->memory.has_max = (flags & 1u) != 0; - out->memory.min_pages = bin_uleb(&r); - if (out->memory.has_max) out->memory.max_pages = bin_uleb(&r); + 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; @@ -2951,22 +3312,19 @@ static void wasm_decode_binary(CfreeCompiler* c, const CfreeBytesInput* input, } } else if (id == 5) { uint32_t count = bin_uleb(&r); - uint8_t flags; - if (count > 1u) - wasm_error(c, wasm_loc(0, 0), "wasm: multiple memories unsupported"); - if (count == 1u) { - if (out->has_memory) - wasm_error(c, wasm_loc(0, 0), "wasm: multiple memories unsupported"); - flags = bin_u8(&r); - out->has_memory = 1; - out->memory.min_pages = bin_uleb(&r); - if (flags & 1u) { - out->memory.has_max = 1; - out->memory.max_pages = bin_uleb(&r); - } - if (flags & ~1u) + 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"); - wasm_memory_ensure(c, out, out->memory.min_pages * 65536u); + 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); @@ -3040,9 +3398,10 @@ static void wasm_decode_binary(CfreeCompiler* c, const CfreeBytesInput* input, 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 == 0 && out->has_memory) { - wasm_free_str(out->heap, &out->memory.export_name); - out->memory.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 = @@ -3171,90 +3530,69 @@ static void wasm_decode_binary(CfreeCompiler* c, const CfreeBytesInput* input, wasm_func_add_insn(c, out, f, WASM_INSN_SELECT, 0); break; case 0x28: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_LOAD, - bin_uleb(&r), bin_uleb(&r)); + { 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: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD, - bin_uleb(&r), bin_uleb(&r)); + { 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: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_LOAD8_S, - bin_uleb(&r), bin_uleb(&r)); + { 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: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_LOAD8_U, - bin_uleb(&r), bin_uleb(&r)); + { 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: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_LOAD16_S, - bin_uleb(&r), bin_uleb(&r)); + { 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: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_LOAD16_U, - bin_uleb(&r), bin_uleb(&r)); + { 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: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD8_S, - bin_uleb(&r), bin_uleb(&r)); + { 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: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD8_U, - bin_uleb(&r), bin_uleb(&r)); + { 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: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD16_S, - bin_uleb(&r), bin_uleb(&r)); + { 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: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD16_U, - bin_uleb(&r), bin_uleb(&r)); + { 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: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD32_S, - bin_uleb(&r), bin_uleb(&r)); + { 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: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_LOAD32_U, - bin_uleb(&r), bin_uleb(&r)); + { 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: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_STORE, - bin_uleb(&r), bin_uleb(&r)); + { 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: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_STORE, - bin_uleb(&r), bin_uleb(&r)); + { 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: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_STORE8, - bin_uleb(&r), bin_uleb(&r)); + { 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: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I32_STORE16, - bin_uleb(&r), bin_uleb(&r)); + { 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: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_STORE8, - bin_uleb(&r), bin_uleb(&r)); + { 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: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_STORE16, - bin_uleb(&r), bin_uleb(&r)); + { 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: - wasm_func_add_mem_insn(c, out, f, WASM_INSN_I64_STORE32, - bin_uleb(&r), bin_uleb(&r)); + { 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: - if (bin_u8(&r) != 0) - wasm_error(c, wasm_loc(0, 0), "wasm: bad memory.size index"); wasm_func_add_insn(c, out, f, WASM_INSN_MEMORY_SIZE, 0); + f->insns[f->ninsns - 1u].memidx = bin_uleb(&r); break; case 0x40: - if (bin_u8(&r) != 0) - wasm_error(c, wasm_loc(0, 0), "wasm: bad memory.grow index"); 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)); @@ -3267,6 +3605,18 @@ static void wasm_decode_binary(CfreeCompiler* c, const CfreeBytesInput* input, 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 0x20: wasm_func_add_insn(c, out, f, WASM_INSN_LOCAL_GET, bin_uleb(&r)); break; @@ -3620,21 +3970,32 @@ static void wasm_decode_binary(CfreeCompiler* c, const CfreeBytesInput* input, uint32_t i, count = bin_uleb(&r); for (i = 0; i < count; ++i) { uint32_t flags = bin_uleb(&r); - int64_t offset; + int64_t offset = 0; + uint32_t memidx = 0; uint32_t n; - if (flags != 0) + if (flags == 2u) + memidx = bin_uleb(&r); + else if (flags != 0) wasm_error(c, wasm_loc(0, 0), "wasm: passive data unsupported"); - if (bin_u8(&r) != 0x41) - wasm_error(c, wasm_loc(0, 0), "wasm: expected i32.const data offset"); - offset = bin_sleb(&r, 32); + { + 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, (uint32_t)offset + n); - memcpy(out->memory.data + (uint32_t)offset, r.data + r.pos, n); - if ((uint64_t)offset + n > out->memory.data_init_len) - out->memory.data_init_len = (uint32_t)((uint64_t)offset + 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) { @@ -3788,10 +4149,11 @@ typedef struct WasmCgRuntime { CfreeCgTypeId table_ty; CfreeCgTypeId instance_ty; CfreeCgTypeId instance_ptr_ty; - uint32_t memory_field; + 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 global_field[64]; uint32_t global_import_addr_field; @@ -3875,7 +4237,7 @@ static void wasm_indexed_name(char* name, size_t cap, const char* prefix, static void wasm_cg_build_runtime(CfreeCompiler* c, CfreeCgBuiltinTypes b, const WasmModule* m, WasmCgRuntime* rt) { - CfreeCgField memory_fields[3]; + CfreeCgField memory_fields[4]; CfreeCgField func_import_fields[1]; CfreeCgField global_import_fields[1]; CfreeCgField table_entry_fields[2]; @@ -3889,15 +4251,17 @@ static void wasm_cg_build_runtime(CfreeCompiler* c, CfreeCgBuiltinTypes b, 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_I32]; + 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_I32]; + 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, 3); - rt->memory_field = 0; + 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; @@ -3931,9 +4295,19 @@ static void wasm_cg_build_runtime(CfreeCompiler* c, CfreeCgBuiltinTypes b, rt->table_len_field = 1; rt->table_max_field = 2; memset(instance_fields, 0, sizeof instance_fields); - instance_fields[nfields].name = cfree_sym_intern(c, "memory"); - instance_fields[nfields].type = rt->memory_ty; - nfields++; + 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; @@ -3990,28 +4364,32 @@ static void wasm_cg_push_instance_lvalue(CfreeCg* cg, const WasmCgRuntime* rt, } static void wasm_cg_push_memory_lvalue(CfreeCg* cg, const WasmCgRuntime* rt, - CfreeCgLocal instance_local) { + CfreeCgLocal instance_local, + uint32_t memidx) { wasm_cg_push_instance_lvalue(cg, rt, instance_local); - cfree_cg_field(cg, rt->memory_field); + cfree_cg_field(cg, rt->memory_field[memidx]); } static void wasm_cg_push_memory_data_ptr(CfreeCg* cg, const WasmCgRuntime* rt, - CfreeCgLocal instance_local) { - wasm_cg_push_memory_lvalue(cg, rt, instance_local); + 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) { - wasm_cg_push_memory_lvalue(cg, rt, instance_local); + 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) { - wasm_cg_push_memory_lvalue(cg, rt, instance_local); + 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); } @@ -4099,22 +4477,29 @@ static void wasm_cg_memory_check(CfreeCompiler* c, CfreeCg* cg, CfreeCgLocal instance_local, const WasmInsn* in) { uint32_t width = wasm_mem_width(in->kind); - uint64_t end = (uint64_t)(uint32_t)in->imm + width; + uint64_t end = in->offset64 + width; CfreeCgLabel ok = cfree_cg_label_new(cg); uint32_t max_pages = - m->memory.has_max ? m->memory.max_pages : m->memory.min_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); - cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); - cfree_cg_push_int(cg, 65536u, b.id[CFREE_CG_BUILTIN_I32]); + 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_I32]); + 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); @@ -4123,8 +4508,8 @@ static void wasm_cg_memory_check(CfreeCompiler* c, CfreeCg* cg, static void wasm_cg_memory_lvalue(CfreeCg* cg, const WasmCgRuntime* rt, CfreeCgLocal instance_local, - uint32_t offset) { - wasm_cg_push_memory_data_ptr(cg, rt, 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); } @@ -4657,9 +5042,14 @@ static void wasm_validate(WasmModule* m, CfreeCompiler* c) { if (!wasm_is_num_type(m->types[i].results[j])) wasm_error(c, wasm_loc(0, 0), "wasm: unsupported result type"); } - if (m->has_memory && m->memory.has_max && - m->memory.max_pages < m->memory.min_pages) - wasm_error(c, wasm_loc(0, 0), "wasm: memory maximum below minimum"); + 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), @@ -4678,7 +5068,7 @@ static void wasm_validate(WasmModule* m, CfreeCompiler* c) { WasmExport* ex = &m->exports[i]; if ((ex->kind == 0 && ex->index >= m->nfuncs) || (ex->kind == 1 && ex->index >= m->ntables) || - (ex->kind == 2 && (!m->has_memory || ex->index != 0)) || + (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"); } @@ -4753,15 +5143,26 @@ static void wasm_validate(WasmModule* m, CfreeCompiler* c) { 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) { + 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 (m->funcs[in->imm].nresults) + 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; @@ -4782,6 +5183,29 @@ static void wasm_validate(WasmModule* m, CfreeCompiler* c) { if (t->nresults) wasm_stack_push(c, &stack, t->results[0]); break; } + case WASM_INSN_RETURN_CALL_INDIRECT: { + WasmFuncType* t; + 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_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"); @@ -4891,16 +5315,22 @@ static void wasm_validate(WasmModule* m, CfreeCompiler* c) { break; } case WASM_INSN_MEMORY_SIZE: - if (!m->has_memory) + if (in->memidx >= m->nmemories) wasm_error(c, wasm_loc(0, 0), "wasm: memory.size without memory"); - wasm_stack_push(c, &stack, WASM_VAL_I32); + wasm_stack_push(c, &stack, + m->memories[in->memidx].is64 ? WASM_VAL_I64 + : WASM_VAL_I32); break; case WASM_INSN_MEMORY_GROW: - if (!m->has_memory) + if (in->memidx >= m->nmemories) wasm_error(c, wasm_loc(0, 0), "wasm: memory.grow without memory"); - wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, + 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, WASM_VAL_I32); + wasm_stack_push(c, &stack, + m->memories[in->memidx].is64 ? WASM_VAL_I64 + : WASM_VAL_I32); break; case WASM_INSN_I32_LOAD: case WASM_INSN_I64_LOAD: @@ -4914,9 +5344,12 @@ static void wasm_validate(WasmModule* m, CfreeCompiler* c) { case WASM_INSN_I64_LOAD16_U: case WASM_INSN_I64_LOAD32_S: case WASM_INSN_I64_LOAD32_U: - if (!m->has_memory) + if (in->memidx >= m->nmemories) wasm_error(c, wasm_loc(0, 0), "wasm: load without memory"); - wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "load"); + 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: @@ -4926,11 +5359,14 @@ static void wasm_validate(WasmModule* m, CfreeCompiler* c) { case WASM_INSN_I64_STORE8: case WASM_INSN_I64_STORE16: case WASM_INSN_I64_STORE32: - if (!m->has_memory) + 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, WASM_VAL_I32, "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); @@ -4998,7 +5434,7 @@ static void wasm_cg_call_func(CfreeCompiler* c, CfreeCg* cg, const WasmCgRuntime* rt, CfreeCgSym sym, CfreeCgTypeId func_type, CfreeCgLocal instance_local, - uint32_t func_index) { + uint32_t func_index, int must_tail) { CfreeCgLocalAttrs attrs; CfreeCgLocal args[16]; CfreeCgLocal callee = CFREE_CG_LOCAL_NONE; @@ -5038,9 +5474,21 @@ static void wasm_cg_call_func(CfreeCompiler* c, CfreeCg* cg, 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){0}); + cfree_cg_call(cg, f->nparams + 1u, func_type, + (CfreeCgCallAttrs){ + .tail = CFREE_CG_TAIL_DEFAULT}); else - cfree_cg_call_symbol(cg, sym, f->nparams + 1u, (CfreeCgCallAttrs){0}); + cfree_cg_call_symbol(cg, sym, f->nparams + 1u, + (CfreeCgCallAttrs){ + .tail = CFREE_CG_TAIL_DEFAULT}); + if (must_tail) { + if (f->nresults) + cfree_cg_ret(cg); + else + cfree_cg_ret_void(cg); + 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 CfreeCompileOptions* opts, @@ -5167,23 +5615,29 @@ static void wasm_emit_cg(CfreeCompiler* c, const CfreeCompileOptions* opts, memset(&attrs, 0, sizeof attrs); cfree_cg_func_begin(cg, init_sym); instance_local = cfree_cg_param(cg, 0, rt.instance_ptr_ty, attrs); - if (m->has_memory) { - uint32_t max_pages = - m->memory.has_max ? m->memory.max_pages : m->memory.min_pages; - wasm_cg_push_memory_pages_lvalue(cg, &rt, instance_local); - cfree_cg_push_int(cg, m->memory.min_pages, b.id[CFREE_CG_BUILTIN_I32]); + 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)); - wasm_cg_push_memory_max_lvalue(cg, &rt, instance_local); - cfree_cg_push_int(cg, max_pages, b.id[CFREE_CG_BUILTIN_I32]); - cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); - if (m->memory.data_init_len) { + if (mem->data_init_len) { CfreeCgSym data_sym = - cfree_cg_const_data(cg, m->memory.data, m->memory.data_init_len, 16, + cfree_cg_const_data(cg, mem->data, mem->data_init_len, 16, b.id[CFREE_CG_BUILTIN_I8]); - CfreeCgMemAccess mem = wasm_cg_mem_type(b.id[CFREE_CG_BUILTIN_I8]); - wasm_cg_push_memory_data_ptr(cg, &rt, instance_local); + 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->memory.data_init_len, mem, mem); + cfree_cg_memcpy(cg, m->memories[i].data_init_len, byte_mem, byte_mem); } } for (i = 0; i < m->ntables; ++i) { @@ -5252,7 +5706,7 @@ static void wasm_emit_cg(CfreeCompiler* c, const CfreeCompileOptions* opts, 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); + instance_local, m->start_func, 0); cfree_cg_ret_void(cg); cfree_cg_func_end(cg); } @@ -5471,9 +5925,11 @@ static void wasm_emit_cg(CfreeCompiler* c, const CfreeCompileOptions* opts, 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); + (uint32_t)in.imm, + in.kind == WASM_INSN_RETURN_CALL); break; case WASM_INSN_CALL_INDIRECT: { const WasmFuncType* t = &m->types[in.imm]; @@ -5563,8 +6019,15 @@ static void wasm_emit_cg(CfreeCompiler* c, const CfreeCompileOptions* opts, cfree_cg_load(cg, wasm_cg_mem(c, b, t->params[p])); } cfree_cg_call(cg, t->nparams + 1u, indirect_func_type, - (CfreeCgCallAttrs){0}); - if (t->nresults) { + (CfreeCgCallAttrs){.tail = CFREE_CG_TAIL_DEFAULT}); + if (in.kind == WASM_INSN_RETURN_CALL_INDIRECT) { + if (t->nresults) + cfree_cg_ret(cg); + else + cfree_cg_ret_void(cg); + 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])); @@ -5598,57 +6061,70 @@ static void wasm_emit_cg(CfreeCompiler* c, const CfreeCompileOptions* opts, cfree_cg_drop(cg); break; case WASM_INSN_MEMORY_SIZE: - wasm_cg_push_memory_pages_lvalue(cg, &rt, instance_local); - cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); + 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); - CfreeCgTypeId i32 = b.id[CFREE_CG_BUILTIN_I32]; + 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, i32, attrs); - old_pages = cfree_cg_local(cg, i32, attrs); - grow_result = cfree_cg_local(cg, i32, attrs); + 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, WASM_VAL_I32)); + 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); - cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); - cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); + 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, WASM_VAL_I32)); - wasm_cg_push_memory_max_lvalue(cg, &rt, instance_local); - cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); + 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_I32)); + 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); + 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_I32)); + 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, WASM_VAL_I32)); + 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_I32)); + 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_I32)); - cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); + 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_C(0xffffffff), i32); - cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); + 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, WASM_VAL_I32)); + cfree_cg_load(cg, wasm_cg_mem(c, b, page_vt)); break; } case WASM_INSN_I32_LOAD: @@ -5671,7 +6147,8 @@ static void wasm_emit_cg(CfreeCompiler* c, const CfreeCompileOptions* opts, 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, (uint32_t)in.imm); + 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 || @@ -5698,6 +6175,8 @@ static void wasm_emit_cg(CfreeCompiler* c, const CfreeCompileOptions* opts, 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; @@ -5705,17 +6184,18 @@ static void wasm_emit_cg(CfreeCompiler* c, const CfreeCompileOptions* opts, 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, b.id[CFREE_CG_BUILTIN_I32], 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, WASM_VAL_I32)); + 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, WASM_VAL_I32)); - wasm_cg_memory_lvalue(cg, &rt, instance_local, (uint32_t)in.imm); + 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); @@ -6011,9 +6491,17 @@ static void enc_type(CfreeWriter* w, const WasmModule* m) { } } +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_byte(w, has_max ? 1 : 0); + write_uleb(w, has_max ? 1 : 0); write_uleb(w, min); if (has_max) write_uleb(w, max); } @@ -6024,7 +6512,8 @@ static void enc_import(CfreeWriter* w, const WasmModule* m) { if (m->funcs[i].is_import) n++; for (i = 0; i < m->ntables; ++i) if (m->tables[i].is_import) n++; - if (m->has_memory && m->memory.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); @@ -6045,12 +6534,13 @@ static void enc_import(CfreeWriter* w, const WasmModule* m) { write_byte(w, (uint8_t)t->elem_type); write_limits(w, t->min, t->max, t->has_max); } - if (m->has_memory && m->memory.is_import) { - write_name(w, m->memory.import_module ? m->memory.import_module : ""); - write_name(w, m->memory.import_name ? m->memory.import_name : ""); + 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_limits(w, m->memory.min_pages, m->memory.max_pages, - m->memory.has_max); + write_memory_limits(w, mem); } for (i = 0; i < m->nglobals; ++i) { const WasmGlobal* g = &m->globals[i]; @@ -6069,7 +6559,8 @@ static int wasm_module_has_imports(const WasmModule* m) { if (m->funcs[i].is_import) return 1; for (i = 0; i < m->ntables; ++i) if (m->tables[i].is_import) return 1; - if (m->has_memory && m->memory.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; @@ -6096,14 +6587,12 @@ static void enc_export(CfreeWriter* w, const WasmModule* m) { } static void enc_memory(CfreeWriter* w, const WasmModule* m) { - if (!m->has_memory) { - write_uleb(w, 0); - return; - } - write_uleb(w, 1); - write_byte(w, m->memory.has_max ? 1 : 0); - write_uleb(w, m->memory.min_pages); - if (m->memory.has_max) write_uleb(w, m->memory.max_pages); + 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); @@ -6183,6 +6672,10 @@ static uint8_t wasm_opcode(uint8_t kind) { 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_GLOBAL_GET: return 0x23; case WASM_INSN_GLOBAL_SET: @@ -6499,9 +6992,11 @@ static void enc_code(CfreeWriter* w, const WasmModule* m) { 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_BR || in.kind == WASM_INSN_BR_IF) write_uleb(body, (uint64_t)in.imm); - else if (in.kind == WASM_INSN_CALL_INDIRECT) { + 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) { @@ -6509,11 +7004,12 @@ static void enc_code(CfreeWriter* w, const WasmModule* m) { 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.align); - write_uleb(body, (uint64_t)in.imm); + 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_byte(body, 0); + write_uleb(body, in.memidx); } } write_byte(body, 0x0b); @@ -6525,17 +7021,21 @@ static void enc_code(CfreeWriter* w, const WasmModule* m) { } static void enc_data(CfreeWriter* w, const WasmModule* m) { - if (!m->has_memory || !m->memory.data_init_len) { - write_uleb(w, 0); - return; + 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); } - write_uleb(w, 1); - write_uleb(w, 0); - write_byte(w, 0x41); - write_sleb(w, 0); - write_byte(w, 0x0b); - write_uleb(w, m->memory.data_init_len); - w->write(w, m->memory.data, m->memory.data_init_len); } static void enc_elem(CfreeWriter* w, const WasmModule* m) { @@ -6583,15 +7083,23 @@ static void wasm_encode(CfreeCompiler* c, const WasmModule* 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); - if (m->has_memory && !m->memory.is_import) - encode_section(h, out, 5, enc_memory, 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); - if (m->has_memory && m->memory.data_init_len) - encode_section(h, out, 11, enc_data, 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); + } } static void wasm_parse_any(CfreeCompiler* c, const CfreeBytesInput* input, diff --git a/test/link/harness/jit_runner.c b/test/link/harness/jit_runner.c @@ -334,8 +334,9 @@ static CfreeJitTls g_jit_tls = {jit_tls_ctx_new, jit_tls_ctx_destroy, NULL}; typedef struct WasmRunnerMemoryPrefix { uint8_t* data; - uint32_t pages; - uint32_t max_pages; + uint64_t pages; + uint64_t max_pages; + uint32_t flags; } WasmRunnerMemoryPrefix; typedef void (*WasmRunnerInitFn)(void*); @@ -539,7 +540,9 @@ int main(int argc, char** argv) { cfree_compiler_free(c); return 1; } - ((WasmRunnerMemoryPrefix*)instance)->data = memory; + for (uint32_t i = 0; i < 8u; ++i) + ((WasmRunnerMemoryPrefix*)instance)[i].data = + memory + i * (16u * 1024u * 1024u / 8u); ((WasmRunnerInitFn)wasm_init)(instance); result = ((WasmRunnerMainFn)entry)(instance); free(memory); diff --git a/test/wasm/cases/atomic_memory.expect b/test/wasm/cases/atomic_memory.expect @@ -0,0 +1 @@ +42 diff --git a/test/wasm/cases/atomic_memory.wat b/test/wasm/cases/atomic_memory.wat @@ -0,0 +1,9 @@ +(module + (memory 1 1 shared) + (func (export "test_main") (result i32) + i32.const 0 + i32.const 42 + i32.atomic.store + atomic.fence + i32.const 0 + i32.atomic.load)) diff --git a/test/wasm/cases/memory64.expect b/test/wasm/cases/memory64.expect @@ -0,0 +1 @@ +42 diff --git a/test/wasm/cases/memory64.wat b/test/wasm/cases/memory64.wat @@ -0,0 +1,13 @@ +(module + (memory i64 1 1) + (func (export "test_main") (result i32) + i64.const 8 + i32.const 41 + i32.store + i64.const 0 + memory.grow + drop + i64.const 8 + i32.load + i32.const 1 + i32.add)) diff --git a/test/wasm/cases/multi_memory.expect b/test/wasm/cases/multi_memory.expect @@ -0,0 +1 @@ +42 diff --git a/test/wasm/cases/multi_memory.wat b/test/wasm/cases/multi_memory.wat @@ -0,0 +1,13 @@ +(module + (memory 1 1) + (memory 1 1) + (data (memory 1) (i32.const 0) "\07") + (func (export "test_main") (result i32) + i32.const 4 + i32.const 35 + i32.store memory=0 + i32.const 0 + i32.load8_u memory=1 + i32.const 4 + i32.load + i32.add)) diff --git a/test/wasm/cases/tail_call.expect b/test/wasm/cases/tail_call.expect @@ -0,0 +1 @@ +42 diff --git a/test/wasm/cases/tail_call.wat b/test/wasm/cases/tail_call.wat @@ -0,0 +1,19 @@ +(module + (func $count (param $n i32) (param $acc i32) (result i32) + local.get $n + i32.eqz + if + local.get $acc + return + end + local.get $n + i32.const 1 + i32.sub + local.get $acc + i32.const 1 + i32.add + return_call $count) + (func (export "test_main") (result i32) + i32.const 42 + i32.const 0 + call $count)) diff --git a/test/wasm/harness/start_wasm.c b/test/wasm/harness/start_wasm.c @@ -10,10 +10,12 @@ extern int test_main(void *); typedef struct WasmStartMemoryPrefix { unsigned char *data; - unsigned int pages; - unsigned int max_pages; + unsigned long long pages; + unsigned long long max_pages; + unsigned int flags; } WasmStartMemoryPrefix; +#define WASM_START_MEMORY_PREFIX_COUNT 8u #define WASM_START_INSTANCE_SIZE (64u * 1024u) #define WASM_START_MEMORY_SIZE (16u * 1024u * 1024u) #define WASM_START_PROT_READ 1 @@ -123,7 +125,10 @@ void _start(void) { void *memory = start_mmap(WASM_START_MEMORY_SIZE); if (!instance || !memory) do_exit(1); - ((WasmStartMemoryPrefix *)instance)->data = (unsigned char *)memory; + for (unsigned int i = 0; i < WASM_START_MEMORY_PREFIX_COUNT; ++i) + ((WasmStartMemoryPrefix *)instance)[i].data = + (unsigned char *)memory + i * (WASM_START_MEMORY_SIZE / + WASM_START_MEMORY_PREFIX_COUNT); __cfree_wasm_init(instance); do_exit(test_main(instance)); }