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:
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));
}