kit

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

commit de7b25ff43c391fe820d41f84e1b0bca8ba9d360
parent e8e6581c9b695d8e265a180dc64d27e73938815f
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sun, 17 May 2026 23:43:34 -0700

wasm: add instance runtime runner path

Diffstat:
Mdriver/run.c | 45++++++++++++++++++++++++++++++++++++++++++++-
Alang/wasm/runtime_abi.h | 19+++++++++++++++++++
Mlang/wasm/wasm.c | 979++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mlang/wasm/wasm.h | 2++
Mtest/link/harness/jit_runner.c | 31+++++++++++++++++++++++++++++--
Atest/wasm/harness/start_wasm.c | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/wasm/run.sh | 18+++++++++---------
7 files changed, 859 insertions(+), 364 deletions(-)

diff --git a/driver/run.c b/driver/run.c @@ -1,4 +1,5 @@ #include <stdint.h> +#include <stdlib.h> #include "cflags.h" #include "driver.h" @@ -404,6 +405,47 @@ static int run_compile_and_jit(RunOptions* o, CfreePipeline* pipe, } typedef int (*MainFn)(int, char**); +typedef void (*WasmInitFn)(void*); +typedef int (*WasmMainFn)(void*); + +typedef struct RunWasmMemoryPrefix { + uint8_t* data; + uint32_t pages; + uint32_t max_pages; +} RunWasmMemoryPrefix; + +static int run_call_wasm_entry(RunOptions* ro, CfreeJit* jit, void* entry, + int* rc_out) { + void* init_sym = cfree_jit_lookup(jit, "__cfree_wasm_init"); + uint8_t* instance; + uint8_t* memory; + union { + void* p; + WasmInitFn fn; + } init_u; + union { + void* p; + WasmMainFn fn; + } entry_u; + if (!init_sym) return 0; + instance = (uint8_t*)driver_alloc_zeroed(ro->env, 64u * 1024u); + memory = (uint8_t*)driver_alloc_zeroed(ro->env, 16u * 1024u * 1024u); + if (!instance || !memory) { + driver_errf(RUN_TOOL, "out of memory"); + driver_free(ro->env, memory, 16u * 1024u * 1024u); + driver_free(ro->env, instance, 64u * 1024u); + *rc_out = 1; + return 1; + } + ((RunWasmMemoryPrefix*)instance)->data = memory; + init_u.p = init_sym; + entry_u.p = entry; + init_u.fn(instance); + *rc_out = entry_u.fn(instance); + driver_free(ro->env, memory, 16u * 1024u * 1024u); + driver_free(ro->env, instance, 64u * 1024u); + return 1; +} int driver_run(int argc, char** argv) { DriverEnv env; @@ -490,7 +532,8 @@ int driver_run(int argc, char** argv) { } run_metrics_begin(metrics, "run.entry_call"); - rc = entry_fn((int)ro.prog_argc, ro.prog_argv); + if (!run_call_wasm_entry(&ro, jit, sym, &rc)) + rc = entry_fn((int)ro.prog_argc, ro.prog_argv); run_metrics_end(metrics, "run.entry_call"); cfree_jit_free(jit); diff --git a/lang/wasm/runtime_abi.h b/lang/wasm/runtime_abi.h @@ -0,0 +1,19 @@ +#ifndef CFREE_LANG_WASM_RUNTIME_ABI_H +#define CFREE_LANG_WASM_RUNTIME_ABI_H + +#include <stdint.h> + +typedef struct CfreeWasmMemory { + uint8_t *data; + uint32_t pages; + uint32_t max_pages; +} CfreeWasmMemory; + +/* Opaque to C callers for now. The compiler emits a module-specific instance + * layout with CfreeWasmMemory first when the module has linear memory, followed + * by lowered global slots. */ +typedef struct CfreeWasmInstance CfreeWasmInstance; + +typedef void (*CfreeWasmInitFn)(CfreeWasmInstance *); + +#endif diff --git a/lang/wasm/wasm.c b/lang/wasm/wasm.c @@ -217,6 +217,7 @@ typedef struct WasmMemory { char *export_name; uint8_t *data; uint32_t data_len; + uint32_t data_init_len; } WasmMemory; typedef struct WasmTable { @@ -493,8 +494,8 @@ static uint32_t wasm_intern_func_type(CfreeCompiler *c, WasmModule *m, WasmFuncType *t = &m->types[i]; if (t->nparams == f->nparams && t->nresults == f->nresults && memcmp(t->params, f->params, sizeof(t->params[0]) * t->nparams) == 0 && - memcmp(t->results, f->results, - sizeof(t->results[0]) * t->nresults) == 0) + memcmp(t->results, f->results, sizeof(t->results[0]) * t->nresults) == + 0) return i; } { @@ -511,9 +512,9 @@ static WasmTable *wasm_add_table(CfreeCompiler *c, WasmModule *m) { WasmTable *t; if (m->ntables == m->cap_tables) { uint32_t new_cap = m->cap_tables ? m->cap_tables * 2u : 2u; - void *p = wasm_realloc(m->heap, m->tables, - sizeof(*m->tables) * m->cap_tables, - sizeof(*m->tables) * new_cap); + void *p = + wasm_realloc(m->heap, m->tables, sizeof(*m->tables) * m->cap_tables, + sizeof(*m->tables) * new_cap); if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory"); m->tables = (WasmTable *)p; @@ -530,9 +531,9 @@ static WasmGlobal *wasm_add_global(CfreeCompiler *c, WasmModule *m) { WasmGlobal *g; if (m->nglobals == m->cap_globals) { uint32_t new_cap = m->cap_globals ? m->cap_globals * 2u : 4u; - void *p = wasm_realloc(m->heap, m->globals, - sizeof(*m->globals) * m->cap_globals, - sizeof(*m->globals) * new_cap); + void *p = + wasm_realloc(m->heap, m->globals, sizeof(*m->globals) * m->cap_globals, + sizeof(*m->globals) * new_cap); if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory"); m->globals = (WasmGlobal *)p; @@ -567,9 +568,9 @@ static WasmExport *wasm_add_export(CfreeCompiler *c, WasmModule *m) { WasmExport *e; if (m->nexports == m->cap_exports) { uint32_t new_cap = m->cap_exports ? m->cap_exports * 2u : 8u; - void *p = wasm_realloc(m->heap, m->exports, - sizeof(*m->exports) * m->cap_exports, - sizeof(*m->exports) * new_cap); + void *p = + wasm_realloc(m->heap, m->exports, sizeof(*m->exports) * m->cap_exports, + sizeof(*m->exports) * new_cap); if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory"); m->exports = (WasmExport *)p; @@ -586,9 +587,9 @@ static WasmCustom *wasm_add_custom(CfreeCompiler *c, WasmModule *m) { WasmCustom *cs; if (m->ncustoms == m->cap_customs) { uint32_t new_cap = m->cap_customs ? m->cap_customs * 2u : 4u; - void *p = wasm_realloc(m->heap, m->customs, - sizeof(*m->customs) * m->cap_customs, - sizeof(*m->customs) * new_cap); + void *p = + wasm_realloc(m->heap, m->customs, sizeof(*m->customs) * m->cap_customs, + sizeof(*m->customs) * new_cap); if (!p) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory"); m->customs = (WasmCustom *)p; @@ -621,16 +622,15 @@ static void wasm_func_add_insn(CfreeCompiler *c, WasmModule *m, WasmFunc *f, f->ninsns++; } -static void wasm_func_add_mem_insn(CfreeCompiler *c, WasmModule *m, - WasmFunc *f, WasmInsnKind kind, - uint32_t align, uint32_t offset) { +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); f->insns[f->ninsns - 1u].align = align; } -static void wasm_func_add_fp_insn(CfreeCompiler *c, WasmModule *m, - WasmFunc *f, WasmInsnKind kind, - double value) { +static void wasm_func_add_fp_insn(CfreeCompiler *c, WasmModule *m, WasmFunc *f, + WasmInsnKind kind, double value) { wasm_func_add_insn(c, m, f, kind, 0); f->insns[f->ninsns - 1u].fp = value; } @@ -823,8 +823,8 @@ static void wat_next(WatParser *p) { static void wat_expect(WatParser *p, uint8_t kind, const char *what) { if (p->tok.kind != kind) - wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), - "wasm wat: expected %s", what); + wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), "wasm wat: expected %s", + what); wat_next(p); } @@ -1356,51 +1356,186 @@ static int wat_instr_kind(WasmTok t, WasmInsnKind *out, int *has_imm) { *out = WASM_INSN_I64_GE_U; return 1; } - if (tok_is(t, "f32.add")) { *out = WASM_INSN_F32_ADD; return 1; } - if (tok_is(t, "f32.sub")) { *out = WASM_INSN_F32_SUB; return 1; } - if (tok_is(t, "f32.mul")) { *out = WASM_INSN_F32_MUL; return 1; } - if (tok_is(t, "f32.div")) { *out = WASM_INSN_F32_DIV; return 1; } - if (tok_is(t, "f32.eq")) { *out = WASM_INSN_F32_EQ; return 1; } - if (tok_is(t, "f32.ne")) { *out = WASM_INSN_F32_NE; return 1; } - if (tok_is(t, "f32.lt")) { *out = WASM_INSN_F32_LT; return 1; } - if (tok_is(t, "f32.gt")) { *out = WASM_INSN_F32_GT; return 1; } - if (tok_is(t, "f32.le")) { *out = WASM_INSN_F32_LE; return 1; } - if (tok_is(t, "f32.ge")) { *out = WASM_INSN_F32_GE; return 1; } - if (tok_is(t, "f64.add")) { *out = WASM_INSN_F64_ADD; return 1; } - if (tok_is(t, "f64.sub")) { *out = WASM_INSN_F64_SUB; return 1; } - if (tok_is(t, "f64.mul")) { *out = WASM_INSN_F64_MUL; return 1; } - if (tok_is(t, "f64.div")) { *out = WASM_INSN_F64_DIV; return 1; } - if (tok_is(t, "f64.eq")) { *out = WASM_INSN_F64_EQ; return 1; } - if (tok_is(t, "f64.ne")) { *out = WASM_INSN_F64_NE; return 1; } - if (tok_is(t, "f64.lt")) { *out = WASM_INSN_F64_LT; return 1; } - if (tok_is(t, "f64.gt")) { *out = WASM_INSN_F64_GT; return 1; } - if (tok_is(t, "f64.le")) { *out = WASM_INSN_F64_LE; return 1; } - if (tok_is(t, "f64.ge")) { *out = WASM_INSN_F64_GE; return 1; } - if (tok_is(t, "i32.wrap_i64")) { *out = WASM_INSN_I32_WRAP_I64; return 1; } - if (tok_is(t, "i32.trunc_f32_s")) { *out = WASM_INSN_I32_TRUNC_F32_S; return 1; } - if (tok_is(t, "i32.trunc_f32_u")) { *out = WASM_INSN_I32_TRUNC_F32_U; return 1; } - if (tok_is(t, "i32.trunc_f64_s")) { *out = WASM_INSN_I32_TRUNC_F64_S; return 1; } - if (tok_is(t, "i32.trunc_f64_u")) { *out = WASM_INSN_I32_TRUNC_F64_U; return 1; } - if (tok_is(t, "i64.extend_i32_s")) { *out = WASM_INSN_I64_EXTEND_I32_S; return 1; } - if (tok_is(t, "i64.extend_i32_u")) { *out = WASM_INSN_I64_EXTEND_I32_U; return 1; } - if (tok_is(t, "i64.trunc_f32_s")) { *out = WASM_INSN_I64_TRUNC_F32_S; return 1; } - if (tok_is(t, "i64.trunc_f32_u")) { *out = WASM_INSN_I64_TRUNC_F32_U; return 1; } - if (tok_is(t, "i64.trunc_f64_s")) { *out = WASM_INSN_I64_TRUNC_F64_S; return 1; } - if (tok_is(t, "i64.trunc_f64_u")) { *out = WASM_INSN_I64_TRUNC_F64_U; return 1; } - if (tok_is(t, "f32.convert_i32_s")) { *out = WASM_INSN_F32_CONVERT_I32_S; return 1; } - if (tok_is(t, "f32.convert_i32_u")) { *out = WASM_INSN_F32_CONVERT_I32_U; return 1; } - if (tok_is(t, "f32.convert_i64_s")) { *out = WASM_INSN_F32_CONVERT_I64_S; return 1; } - if (tok_is(t, "f32.convert_i64_u")) { *out = WASM_INSN_F32_CONVERT_I64_U; return 1; } - if (tok_is(t, "f32.demote_f64")) { *out = WASM_INSN_F32_DEMOTE_F64; return 1; } - if (tok_is(t, "f64.convert_i32_s")) { *out = WASM_INSN_F64_CONVERT_I32_S; return 1; } - if (tok_is(t, "f64.convert_i32_u")) { *out = WASM_INSN_F64_CONVERT_I32_U; return 1; } - if (tok_is(t, "f64.convert_i64_s")) { *out = WASM_INSN_F64_CONVERT_I64_S; return 1; } - if (tok_is(t, "f64.convert_i64_u")) { *out = WASM_INSN_F64_CONVERT_I64_U; return 1; } - if (tok_is(t, "f64.promote_f32")) { *out = WASM_INSN_F64_PROMOTE_F32; return 1; } - if (tok_is(t, "i32.reinterpret_f32")) { *out = WASM_INSN_I32_REINTERPRET_F32; return 1; } - if (tok_is(t, "i64.reinterpret_f64")) { *out = WASM_INSN_I64_REINTERPRET_F64; return 1; } - if (tok_is(t, "f32.reinterpret_i32")) { *out = WASM_INSN_F32_REINTERPRET_I32; return 1; } - if (tok_is(t, "f64.reinterpret_i64")) { *out = WASM_INSN_F64_REINTERPRET_I64; return 1; } + if (tok_is(t, "f32.add")) { + *out = WASM_INSN_F32_ADD; + return 1; + } + if (tok_is(t, "f32.sub")) { + *out = WASM_INSN_F32_SUB; + return 1; + } + if (tok_is(t, "f32.mul")) { + *out = WASM_INSN_F32_MUL; + return 1; + } + if (tok_is(t, "f32.div")) { + *out = WASM_INSN_F32_DIV; + return 1; + } + if (tok_is(t, "f32.eq")) { + *out = WASM_INSN_F32_EQ; + return 1; + } + if (tok_is(t, "f32.ne")) { + *out = WASM_INSN_F32_NE; + return 1; + } + if (tok_is(t, "f32.lt")) { + *out = WASM_INSN_F32_LT; + return 1; + } + if (tok_is(t, "f32.gt")) { + *out = WASM_INSN_F32_GT; + return 1; + } + if (tok_is(t, "f32.le")) { + *out = WASM_INSN_F32_LE; + return 1; + } + if (tok_is(t, "f32.ge")) { + *out = WASM_INSN_F32_GE; + return 1; + } + if (tok_is(t, "f64.add")) { + *out = WASM_INSN_F64_ADD; + return 1; + } + if (tok_is(t, "f64.sub")) { + *out = WASM_INSN_F64_SUB; + return 1; + } + if (tok_is(t, "f64.mul")) { + *out = WASM_INSN_F64_MUL; + return 1; + } + if (tok_is(t, "f64.div")) { + *out = WASM_INSN_F64_DIV; + return 1; + } + if (tok_is(t, "f64.eq")) { + *out = WASM_INSN_F64_EQ; + return 1; + } + if (tok_is(t, "f64.ne")) { + *out = WASM_INSN_F64_NE; + return 1; + } + if (tok_is(t, "f64.lt")) { + *out = WASM_INSN_F64_LT; + return 1; + } + if (tok_is(t, "f64.gt")) { + *out = WASM_INSN_F64_GT; + return 1; + } + if (tok_is(t, "f64.le")) { + *out = WASM_INSN_F64_LE; + return 1; + } + if (tok_is(t, "f64.ge")) { + *out = WASM_INSN_F64_GE; + return 1; + } + if (tok_is(t, "i32.wrap_i64")) { + *out = WASM_INSN_I32_WRAP_I64; + return 1; + } + if (tok_is(t, "i32.trunc_f32_s")) { + *out = WASM_INSN_I32_TRUNC_F32_S; + return 1; + } + if (tok_is(t, "i32.trunc_f32_u")) { + *out = WASM_INSN_I32_TRUNC_F32_U; + return 1; + } + if (tok_is(t, "i32.trunc_f64_s")) { + *out = WASM_INSN_I32_TRUNC_F64_S; + return 1; + } + if (tok_is(t, "i32.trunc_f64_u")) { + *out = WASM_INSN_I32_TRUNC_F64_U; + return 1; + } + if (tok_is(t, "i64.extend_i32_s")) { + *out = WASM_INSN_I64_EXTEND_I32_S; + return 1; + } + if (tok_is(t, "i64.extend_i32_u")) { + *out = WASM_INSN_I64_EXTEND_I32_U; + return 1; + } + if (tok_is(t, "i64.trunc_f32_s")) { + *out = WASM_INSN_I64_TRUNC_F32_S; + return 1; + } + if (tok_is(t, "i64.trunc_f32_u")) { + *out = WASM_INSN_I64_TRUNC_F32_U; + return 1; + } + if (tok_is(t, "i64.trunc_f64_s")) { + *out = WASM_INSN_I64_TRUNC_F64_S; + return 1; + } + if (tok_is(t, "i64.trunc_f64_u")) { + *out = WASM_INSN_I64_TRUNC_F64_U; + return 1; + } + if (tok_is(t, "f32.convert_i32_s")) { + *out = WASM_INSN_F32_CONVERT_I32_S; + return 1; + } + if (tok_is(t, "f32.convert_i32_u")) { + *out = WASM_INSN_F32_CONVERT_I32_U; + return 1; + } + if (tok_is(t, "f32.convert_i64_s")) { + *out = WASM_INSN_F32_CONVERT_I64_S; + return 1; + } + if (tok_is(t, "f32.convert_i64_u")) { + *out = WASM_INSN_F32_CONVERT_I64_U; + return 1; + } + if (tok_is(t, "f32.demote_f64")) { + *out = WASM_INSN_F32_DEMOTE_F64; + return 1; + } + if (tok_is(t, "f64.convert_i32_s")) { + *out = WASM_INSN_F64_CONVERT_I32_S; + return 1; + } + if (tok_is(t, "f64.convert_i32_u")) { + *out = WASM_INSN_F64_CONVERT_I32_U; + return 1; + } + if (tok_is(t, "f64.convert_i64_s")) { + *out = WASM_INSN_F64_CONVERT_I64_S; + return 1; + } + if (tok_is(t, "f64.convert_i64_u")) { + *out = WASM_INSN_F64_CONVERT_I64_U; + return 1; + } + if (tok_is(t, "f64.promote_f32")) { + *out = WASM_INSN_F64_PROMOTE_F32; + return 1; + } + if (tok_is(t, "i32.reinterpret_f32")) { + *out = WASM_INSN_I32_REINTERPRET_F32; + return 1; + } + if (tok_is(t, "i64.reinterpret_f64")) { + *out = WASM_INSN_I64_REINTERPRET_F64; + return 1; + } + if (tok_is(t, "f32.reinterpret_i32")) { + *out = WASM_INSN_F32_REINTERPRET_I32; + return 1; + } + if (tok_is(t, "f64.reinterpret_i64")) { + *out = WASM_INSN_F64_REINTERPRET_I64; + return 1; + } return 0; } @@ -1720,9 +1855,8 @@ static void wat_parse_instr_list(WatParser *p, WasmFunc *f) { if (!wat_parse_i64(p, &target) || target < 0 || target > UINT32_MAX) wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), "wasm wat: bad br_table target"); - wasm_func_add_insn(p->c, p->module, f, n ? WASM_INSN_NOP - : WASM_INSN_BR_TABLE, - 0); + wasm_func_add_insn(p->c, p->module, f, + n ? WASM_INSN_NOP : WASM_INSN_BR_TABLE, 0); in = &f->insns[f->ninsns - 1u - n]; in->targets[n++] = (uint32_t)target; wat_next(p); @@ -1909,9 +2043,8 @@ static void wat_parse_func(WatParser *p) { wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), "wasm wat: parameter does not match type"); if (have_name) { - f->local_names[checked_params] = - wasm_strdup(p->module->heap, pending_name.p, - pending_name.len); + f->local_names[checked_params] = wasm_strdup( + p->module->heap, pending_name.p, pending_name.len); if (!f->local_names[checked_params]) wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), "wasm: out of memory"); @@ -1946,7 +2079,8 @@ static void wat_parse_func(WatParser *p) { wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), "wasm wat: unsupported result type"); if (f->has_typeidx) { - if (checked_results >= f->nresults || f->results[checked_results] != vt) + if (checked_results >= f->nresults || + f->results[checked_results] != vt) wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), "wasm wat: result does not match type"); checked_results++; @@ -2098,29 +2232,29 @@ static void wat_parse_export_field(WatParser *p) { } if (kind == 0 && (uint64_t)idx < p->module->nfuncs) { wasm_free_str(p->module->heap, &p->module->funcs[idx].export_name); - p->module->funcs[idx].export_name = wasm_strdup(p->module->heap, name, - strlen(name)); + p->module->funcs[idx].export_name = + wasm_strdup(p->module->heap, name, strlen(name)); if (!p->module->funcs[idx].export_name) wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), "wasm: out of memory"); } else if (kind == 2 && idx == 0 && p->module->has_memory) { wasm_free_str(p->module->heap, &p->module->memory.export_name); - p->module->memory.export_name = wasm_strdup(p->module->heap, name, - strlen(name)); + p->module->memory.export_name = + wasm_strdup(p->module->heap, name, strlen(name)); if (!p->module->memory.export_name) wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), "wasm: out of memory"); } else if (kind == 1 && (uint64_t)idx < p->module->ntables) { wasm_free_str(p->module->heap, &p->module->tables[idx].export_name); - p->module->tables[idx].export_name = wasm_strdup(p->module->heap, name, - strlen(name)); + p->module->tables[idx].export_name = + wasm_strdup(p->module->heap, name, strlen(name)); if (!p->module->tables[idx].export_name) wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), "wasm: out of memory"); } else if (kind == 3 && (uint64_t)idx < p->module->nglobals) { wasm_free_str(p->module->heap, &p->module->globals[idx].export_name); - p->module->globals[idx].export_name = wasm_strdup(p->module->heap, name, - strlen(name)); + p->module->globals[idx].export_name = + wasm_strdup(p->module->heap, name, strlen(name)); if (!p->module->globals[idx].export_name) wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), "wasm: out of memory"); @@ -2137,8 +2271,7 @@ static void wat_parse_memory_field(WatParser *p) { "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) + if (!wat_parse_i64(p, &min_pages) || min_pages < 0 || min_pages > UINT16_MAX) wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), "wasm wat: expected memory minimum"); p->module->has_memory = 1; @@ -2299,8 +2432,8 @@ static void wat_parse_import_field(WatParser *p) { wat_next(p); } wat_parse_limits(p, &p->module->memory.min_pages, - &p->module->memory.max_pages, - &p->module->memory.has_max, "memory"); + &p->module->memory.max_pages, &p->module->memory.has_max, + "memory"); } else if (tok_is(p->tok, "table")) { WasmTable *t = wasm_add_table(p->c, p->module); t->is_import = 1; @@ -2510,7 +2643,8 @@ static void wat_parse_custom_field(WatParser *p) { wat_next(p); if (p->tok.kind == WT_STRING) { bytes = wat_dup_string(p, p->tok, &n); - cs->data = (uint8_t *)p->module->heap->alloc(p->module->heap, n ? n : 1u, 1); + cs->data = + (uint8_t *)p->module->heap->alloc(p->module->heap, n ? n : 1u, 1); if (!cs->data) wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), "wasm: out of memory"); @@ -2549,6 +2683,9 @@ static void wat_parse_data_field(WatParser *p) { "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); p->module->heap->free(p->module->heap, bytes, alloc_len); wat_next(p); wat_expect(p, WT_RPAREN, "')'"); @@ -2750,8 +2887,8 @@ static void wasm_decode_binary(CfreeCompiler *c, const CfreeBytesInput *input, "wasm: relocatable object metadata is not frontend input"); { WasmCustom *cs = wasm_add_custom(c, out); - cs->name = wasm_strdup(out->heap, (const char *)(r.data + r.pos), - name_len); + cs->name = + wasm_strdup(out->heap, (const char *)(r.data + r.pos), name_len); if (!cs->name) wasm_error(c, wasm_loc(0, 0), "wasm: out of memory"); r.pos += name_len; @@ -2832,7 +2969,8 @@ 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"); + 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; @@ -2938,7 +3076,8 @@ static void wasm_decode_binary(CfreeCompiler *c, const CfreeBytesInput *input, g->init.fp = bin_f64(&r); break; default: - wasm_error(c, wasm_loc(0, 0), "wasm: unsupported global initializer"); + wasm_error(c, wasm_loc(0, 0), + "wasm: unsupported global initializer"); } if (bin_u8(&r) != 0x0bu) wasm_error(c, wasm_loc(0, 0), "wasm: malformed global initializer"); @@ -2967,15 +3106,15 @@ static void wasm_decode_binary(CfreeCompiler *c, const CfreeBytesInput *input, wasm_error(c, wasm_loc(0, 0), "wasm: out of memory"); } else if (kind == 1 && idx < out->ntables) { wasm_free_str(out->heap, &out->tables[idx].export_name); - out->tables[idx].export_name = wasm_strdup(out->heap, name, - strlen(name)); + 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 == 3 && idx < out->nglobals) { wasm_free_str(out->heap, &out->globals[idx].export_name); - out->globals[idx].export_name = wasm_strdup(out->heap, name, - strlen(name)); + out->globals[idx].export_name = + wasm_strdup(out->heap, name, strlen(name)); } } } else if (id == 8) { @@ -3034,8 +3173,7 @@ static void wasm_decode_binary(CfreeCompiler *c, const CfreeBytesInput *input, while (r.pos < body_end) { uint8_t op = bin_u8(&r); if (op == 0x0bu) { - if (!control_depth) - { + if (!control_depth) { saw_body_end = 1; break; } @@ -3084,7 +3222,8 @@ static void wasm_decode_binary(CfreeCompiler *c, const CfreeBytesInput *input, WasmInsn *in; uint32_t n = bin_uleb(&r); if (n >= 16u) - wasm_error(c, wasm_loc(0, 0), "wasm: too many br_table targets"); + wasm_error(c, wasm_loc(0, 0), + "wasm: too many br_table targets"); wasm_func_add_insn(c, out, f, WASM_INSN_BR_TABLE, 0); in = &f->insns[f->ninsns - 1u]; for (uint32_t k = 0; k < n; ++k) @@ -3565,10 +3704,13 @@ static void wasm_decode_binary(CfreeCompiler *c, const CfreeBytesInput *input, 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); r.pos += n; } } else if (id == 12) { - wasm_error(c, wasm_loc(0, 0), "wasm: data count sections are unsupported"); + wasm_error(c, wasm_loc(0, 0), + "wasm: data count sections are unsupported"); } else { r.pos = end; } @@ -3615,6 +3757,13 @@ static CfreeCgMemAccess wasm_cg_mem(CfreeCompiler *c, CfreeCgBuiltinTypes b, return mem; } +static CfreeCgMemAccess wasm_cg_mem_type(CfreeCgTypeId ty) { + CfreeCgMemAccess mem; + memset(&mem, 0, sizeof mem); + mem.type = ty; + return mem; +} + static void wasm_cg_push_zero(CfreeCompiler *c, CfreeCg *cg, CfreeCgBuiltinTypes b, WasmValType vt) { CfreeCgTypeId ty = wasm_cg_type(c, b, vt); @@ -3695,22 +3844,125 @@ static void wasm_cg_trap(CfreeCg *cg) { cfree_cg_unreachable(cg); } +typedef struct WasmCgRuntime { + CfreeCgTypeId i8_ptr_ty; + CfreeCgTypeId memory_ty; + CfreeCgTypeId instance_ty; + CfreeCgTypeId instance_ptr_ty; + uint32_t memory_field; + uint32_t memory_data_field; + uint32_t memory_pages_field; + uint32_t memory_max_pages_field; + uint32_t globals_field0; +} WasmCgRuntime; + +static void wasm_cg_build_runtime(CfreeCompiler *c, CfreeCgBuiltinTypes b, + const WasmModule *m, WasmCgRuntime *rt) { + CfreeCgField memory_fields[3]; + CfreeCgField instance_fields[65]; + uint32_t nfields = 0; + memset(rt, 0, sizeof *rt); + rt->i8_ptr_ty = cfree_cg_type_ptr(c, b.id[CFREE_CG_BUILTIN_I8], 0); + memset(memory_fields, 0, sizeof memory_fields); + memory_fields[0].name = cfree_sym_intern(c, "data"); + memory_fields[0].type = rt->i8_ptr_ty; + memory_fields[1].name = cfree_sym_intern(c, "pages"); + memory_fields[1].type = b.id[CFREE_CG_BUILTIN_I32]; + memory_fields[2].name = cfree_sym_intern(c, "max_pages"); + memory_fields[2].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; + rt->memory_data_field = 0; + rt->memory_pages_field = 1; + rt->memory_max_pages_field = 2; + memset(instance_fields, 0, sizeof instance_fields); + if (m->has_memory) { + instance_fields[nfields].name = cfree_sym_intern(c, "memory"); + instance_fields[nfields].type = rt->memory_ty; + nfields++; + } + rt->globals_field0 = nfields; + for (uint32_t i = 0; i < m->nglobals; ++i) { + char name[32]; + size_t pos = 0; + uint32_t n = i, div = 1000000000u; + const char prefix[] = "global_"; + memcpy(name, prefix, sizeof(prefix) - 1u); + pos = sizeof(prefix) - 1u; + while (div > 1u && n / div == 0) + div /= 10u; + while (div) { + name[pos++] = (char)('0' + (n / div) % 10u); + div /= 10u; + } + name[pos] = '\0'; + instance_fields[nfields].name = cfree_sym_intern(c, name); + instance_fields[nfields].type = wasm_cg_type(c, b, m->globals[i].type); + nfields++; + } + rt->instance_ty = cfree_cg_type_record( + c, cfree_sym_intern(c, "CfreeWasmInstance"), instance_fields, nfields); + rt->instance_ptr_ty = cfree_cg_type_ptr(c, rt->instance_ty, 0); +} + +static void wasm_cg_push_instance_lvalue(CfreeCg *cg, const WasmCgRuntime *rt, + CfreeCgLocal instance_local) { + cfree_cg_push_local(cg, instance_local); + cfree_cg_load(cg, wasm_cg_mem_type(rt->instance_ptr_ty)); + cfree_cg_indirect(cg); +} + +static void wasm_cg_push_memory_lvalue(CfreeCg *cg, const WasmCgRuntime *rt, + CfreeCgLocal instance_local) { + wasm_cg_push_instance_lvalue(cg, rt, instance_local); + cfree_cg_field(cg, rt->memory_field); +} + +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); + 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); + 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); + cfree_cg_field(cg, rt->memory_max_pages_field); +} + +static void wasm_cg_push_global_lvalue(CfreeCg *cg, const WasmCgRuntime *rt, + CfreeCgLocal instance_local, + uint32_t global_index) { + wasm_cg_push_instance_lvalue(cg, rt, instance_local); + cfree_cg_field(cg, rt->globals_field0 + global_index); +} + static void wasm_cg_memory_check(CfreeCompiler *c, CfreeCg *cg, CfreeCgBuiltinTypes b, const WasmModule *m, - CfreeCgSym memory_pages_sym, + const WasmCgRuntime *rt, + CfreeCgLocal instance_local, const WasmInsn *in) { uint32_t width = wasm_mem_width(in->kind); uint64_t end = (uint64_t)(uint32_t)in->imm + 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 max_pages = + m->memory.has_max ? m->memory.max_pages : m->memory.min_pages; if (end > (uint64_t)max_pages * 65536u) { wasm_cg_trap(cg); return; } (void)c; cfree_cg_dup(cg); - cfree_cg_push_symbol_lvalue(cg, memory_pages_sym, 0); + 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]); cfree_cg_int_binop(cg, CFREE_CG_INT_MUL, 0); @@ -3722,9 +3974,10 @@ static void wasm_cg_memory_check(CfreeCompiler *c, CfreeCg *cg, cfree_cg_label_place(cg, ok); } -static void wasm_cg_memory_lvalue(CfreeCg *cg, CfreeCgSym memory_sym, +static void wasm_cg_memory_lvalue(CfreeCg *cg, const WasmCgRuntime *rt, + CfreeCgLocal instance_local, uint32_t offset) { - cfree_cg_push_symbol_lvalue(cg, memory_sym, 0); + wasm_cg_push_memory_data_ptr(cg, rt, instance_local); cfree_cg_swap(cg); cfree_cg_index(cg, offset); } @@ -3867,16 +4120,20 @@ static int wasm_fp_binop(uint8_t kind, CfreeCgFpBinOp *out) { switch (kind) { case WASM_INSN_F32_ADD: case WASM_INSN_F64_ADD: - *out = CFREE_CG_FP_ADD; return 1; + *out = CFREE_CG_FP_ADD; + return 1; case WASM_INSN_F32_SUB: case WASM_INSN_F64_SUB: - *out = CFREE_CG_FP_SUB; return 1; + *out = CFREE_CG_FP_SUB; + return 1; case WASM_INSN_F32_MUL: case WASM_INSN_F64_MUL: - *out = CFREE_CG_FP_MUL; return 1; + *out = CFREE_CG_FP_MUL; + return 1; case WASM_INSN_F32_DIV: case WASM_INSN_F64_DIV: - *out = CFREE_CG_FP_DIV; return 1; + *out = CFREE_CG_FP_DIV; + return 1; default: return 0; } @@ -3886,22 +4143,28 @@ static int wasm_fp_cmp_op(uint8_t kind, CfreeCgFpCmpOp *out) { switch (kind) { case WASM_INSN_F32_EQ: case WASM_INSN_F64_EQ: - *out = CFREE_CG_FP_OEQ; return 1; + *out = CFREE_CG_FP_OEQ; + return 1; case WASM_INSN_F32_NE: case WASM_INSN_F64_NE: - *out = CFREE_CG_FP_ONE; return 1; + *out = CFREE_CG_FP_ONE; + return 1; case WASM_INSN_F32_LT: case WASM_INSN_F64_LT: - *out = CFREE_CG_FP_OLT; return 1; + *out = CFREE_CG_FP_OLT; + return 1; case WASM_INSN_F32_GT: case WASM_INSN_F64_GT: - *out = CFREE_CG_FP_OGT; return 1; + *out = CFREE_CG_FP_OGT; + return 1; case WASM_INSN_F32_LE: case WASM_INSN_F64_LE: - *out = CFREE_CG_FP_OLE; return 1; + *out = CFREE_CG_FP_OLE; + return 1; case WASM_INSN_F32_GE: case WASM_INSN_F64_GE: - *out = CFREE_CG_FP_OGE; return 1; + *out = CFREE_CG_FP_OGE; + return 1; default: return 0; } @@ -3982,46 +4245,78 @@ static int wasm_conversion_kind(uint8_t kind, WasmValType *src, WasmValType *dst) { switch (kind) { case WASM_INSN_I32_WRAP_I64: - *src = WASM_VAL_I64; *dst = WASM_VAL_I32; return 1; + *src = WASM_VAL_I64; + *dst = WASM_VAL_I32; + return 1; case WASM_INSN_I64_EXTEND_I32_S: case WASM_INSN_I64_EXTEND_I32_U: - *src = WASM_VAL_I32; *dst = WASM_VAL_I64; return 1; + *src = WASM_VAL_I32; + *dst = WASM_VAL_I64; + return 1; case WASM_INSN_I32_TRUNC_F32_S: case WASM_INSN_I32_TRUNC_F32_U: - *src = WASM_VAL_F32; *dst = WASM_VAL_I32; return 1; + *src = WASM_VAL_F32; + *dst = WASM_VAL_I32; + return 1; case WASM_INSN_I32_TRUNC_F64_S: case WASM_INSN_I32_TRUNC_F64_U: - *src = WASM_VAL_F64; *dst = WASM_VAL_I32; return 1; + *src = WASM_VAL_F64; + *dst = WASM_VAL_I32; + return 1; case WASM_INSN_I64_TRUNC_F32_S: case WASM_INSN_I64_TRUNC_F32_U: - *src = WASM_VAL_F32; *dst = WASM_VAL_I64; return 1; + *src = WASM_VAL_F32; + *dst = WASM_VAL_I64; + return 1; case WASM_INSN_I64_TRUNC_F64_S: case WASM_INSN_I64_TRUNC_F64_U: - *src = WASM_VAL_F64; *dst = WASM_VAL_I64; return 1; + *src = WASM_VAL_F64; + *dst = WASM_VAL_I64; + return 1; case WASM_INSN_F32_CONVERT_I32_S: case WASM_INSN_F32_CONVERT_I32_U: - *src = WASM_VAL_I32; *dst = WASM_VAL_F32; return 1; + *src = WASM_VAL_I32; + *dst = WASM_VAL_F32; + return 1; case WASM_INSN_F32_CONVERT_I64_S: case WASM_INSN_F32_CONVERT_I64_U: - *src = WASM_VAL_I64; *dst = WASM_VAL_F32; return 1; + *src = WASM_VAL_I64; + *dst = WASM_VAL_F32; + return 1; case WASM_INSN_F64_CONVERT_I32_S: case WASM_INSN_F64_CONVERT_I32_U: - *src = WASM_VAL_I32; *dst = WASM_VAL_F64; return 1; + *src = WASM_VAL_I32; + *dst = WASM_VAL_F64; + return 1; case WASM_INSN_F64_CONVERT_I64_S: case WASM_INSN_F64_CONVERT_I64_U: - *src = WASM_VAL_I64; *dst = WASM_VAL_F64; return 1; + *src = WASM_VAL_I64; + *dst = WASM_VAL_F64; + return 1; case WASM_INSN_F32_DEMOTE_F64: - *src = WASM_VAL_F64; *dst = WASM_VAL_F32; return 1; + *src = WASM_VAL_F64; + *dst = WASM_VAL_F32; + return 1; case WASM_INSN_F64_PROMOTE_F32: - *src = WASM_VAL_F32; *dst = WASM_VAL_F64; return 1; + *src = WASM_VAL_F32; + *dst = WASM_VAL_F64; + return 1; case WASM_INSN_I32_REINTERPRET_F32: - *src = WASM_VAL_F32; *dst = WASM_VAL_I32; return 1; + *src = WASM_VAL_F32; + *dst = WASM_VAL_I32; + return 1; case WASM_INSN_I64_REINTERPRET_F64: - *src = WASM_VAL_F64; *dst = WASM_VAL_I64; return 1; + *src = WASM_VAL_F64; + *dst = WASM_VAL_I64; + return 1; case WASM_INSN_F32_REINTERPRET_I32: - *src = WASM_VAL_I32; *dst = WASM_VAL_F32; return 1; + *src = WASM_VAL_I32; + *dst = WASM_VAL_F32; + return 1; case WASM_INSN_F64_REINTERPRET_I64: - *src = WASM_VAL_I64; *dst = WASM_VAL_F64; return 1; + *src = WASM_VAL_I64; + *dst = WASM_VAL_F64; + return 1; default: return 0; } @@ -4054,8 +4349,7 @@ static WasmValType wasm_global_init_type(const WasmInsn *in) { } } -static void wasm_stack_push(CfreeCompiler *c, WasmValStack *s, - WasmValType vt) { +static void wasm_stack_push(CfreeCompiler *c, WasmValStack *s, WasmValType vt) { if (s->depth >= 256u) wasm_error(c, wasm_loc(0, 0), "wasm: operand stack too deep"); s->vals[s->depth++] = vt; @@ -4153,15 +4447,24 @@ static void wasm_validate(WasmModule *m, CfreeCompiler *c) { WasmInsn *in = &f->insns[j]; WasmValType vt, src, dst; switch (in->kind) { - case WASM_INSN_F32_CONST: wasm_stack_push(c, &stack, WASM_VAL_F32); break; - case WASM_INSN_F64_CONST: wasm_stack_push(c, &stack, WASM_VAL_F64); break; - case WASM_INSN_I32_CONST: wasm_stack_push(c, &stack, WASM_VAL_I32); break; - case WASM_INSN_I64_CONST: wasm_stack_push(c, &stack, WASM_VAL_I64); break; + case WASM_INSN_F32_CONST: + wasm_stack_push(c, &stack, WASM_VAL_F32); + break; + case WASM_INSN_F64_CONST: + wasm_stack_push(c, &stack, WASM_VAL_F64); + break; + case WASM_INSN_I32_CONST: + wasm_stack_push(c, &stack, WASM_VAL_I32); + break; + case WASM_INSN_I64_CONST: + wasm_stack_push(c, &stack, WASM_VAL_I64); + break; case WASM_INSN_LOCAL_GET: if (in->imm < 0 || (uint64_t)in->imm >= (uint64_t)f->nparams + f->nlocals) wasm_error(c, wasm_loc(0, 0), "wasm: local index out of range"); - wasm_stack_push(c, &stack, wasm_func_local_type(f, (uint32_t)in->imm)); + wasm_stack_push(c, &stack, + wasm_func_local_type(f, (uint32_t)in->imm)); break; case WASM_INSN_LOCAL_SET: case WASM_INSN_LOCAL_TEE: @@ -4171,7 +4474,8 @@ static void wasm_validate(WasmModule *m, CfreeCompiler *c) { wasm_stack_pop(c, &stack, control, ncontrol, wasm_func_local_type(f, (uint32_t)in->imm), "local"); if (in->kind == WASM_INSN_LOCAL_TEE) - wasm_stack_push(c, &stack, wasm_func_local_type(f, (uint32_t)in->imm)); + wasm_stack_push(c, &stack, + wasm_func_local_type(f, (uint32_t)in->imm)); break; case WASM_INSN_CALL: if (in->imm < 0 || (uint64_t)in->imm >= m->nfuncs) @@ -4290,7 +4594,8 @@ static void wasm_validate(WasmModule *m, CfreeCompiler *c) { "br_table selector"); for (uint32_t k = 0; k < in->ntargets; ++k) if (in->targets[k] >= ncontrol - 1u) - wasm_error(c, wasm_loc(0, 0), "wasm: br_table depth out of range"); + wasm_error(c, wasm_loc(0, 0), + "wasm: br_table depth out of range"); wasm_mark_unreachable(&stack, control, ncontrol); break; case WASM_INSN_SELECT: { @@ -4394,9 +4699,9 @@ static void wasm_validate(WasmModule *m, CfreeCompiler *c) { wasm_stack_pop(c, &stack, control, ncontrol, rhs, "operand"); if (lhs != rhs) wasm_error(c, wasm_loc(0, 0), "wasm: operand type mismatch"); - wasm_stack_push(c, &stack, - wasm_int_cmp_op(in->kind, &cmp) ? WASM_VAL_I32 - : lhs); + wasm_stack_push( + c, &stack, + wasm_int_cmp_op(in->kind, &cmp) ? WASM_VAL_I32 : lhs); break; } } @@ -4414,17 +4719,42 @@ static void wasm_validate(WasmModule *m, CfreeCompiler *c) { } } +static void wasm_cg_call_func(CfreeCompiler *c, CfreeCg *cg, + CfreeCgBuiltinTypes b, const WasmFunc *f, + const WasmCgRuntime *rt, CfreeCgSym sym, + CfreeCgLocal instance_local) { + CfreeCgLocalAttrs attrs; + CfreeCgLocal args[16]; + memset(&attrs, 0, sizeof attrs); + attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP; + if (f->nparams > 16u) + wasm_error(c, wasm_loc(0, 0), "wasm: too many call arguments"); + for (uint32_t p = 0; p < f->nparams; ++p) { + uint32_t param = f->nparams - 1u - p; + args[param] = + cfree_cg_local(cg, wasm_cg_type(c, b, f->params[param]), attrs); + cfree_cg_push_local(cg, args[param]); + cfree_cg_swap(cg); + cfree_cg_store(cg, wasm_cg_mem(c, b, f->params[param])); + } + cfree_cg_push_local(cg, instance_local); + cfree_cg_load(cg, wasm_cg_mem_type(rt->instance_ptr_ty)); + for (uint32_t p = 0; p < f->nparams; ++p) { + cfree_cg_push_local(cg, args[p]); + cfree_cg_load(cg, wasm_cg_mem(c, b, f->params[p])); + } + cfree_cg_call_symbol(cg, sym, f->nparams + 1u, (CfreeCgCallAttrs){0}); +} + static void wasm_emit_cg(CfreeCompiler *c, const CfreeCompileOptions *opts, CfreeObjBuilder *out, const WasmModule *m) { CfreeCg *cg = cfree_cg_new(c, out, opts); CfreeCgBuiltinTypes b = cfree_cg_builtin_types(c); + WasmCgRuntime rt; CfreeCgSym syms[64]; + CfreeCgSym init_sym = CFREE_CG_SYM_NONE; CfreeCgTypeId func_types[64]; - CfreeCgSym global_syms[64]; CfreeCgLocal locals[48]; - CfreeCgSym memory_sym = CFREE_CG_SYM_NONE; - CfreeCgSym memory_pages_sym = CFREE_CG_SYM_NONE; - CfreeCgSym start_flag_sym = CFREE_CG_SYM_NONE; uint32_t i, j; if (!cg) wasm_error(c, wasm_loc(0, 0), "wasm: failed to initialize codegen"); @@ -4432,129 +4762,47 @@ static void wasm_emit_cg(CfreeCompiler *c, const CfreeCompileOptions *opts, wasm_error(c, wasm_loc(0, 0), "wasm: too many functions"); if (m->nglobals > 64u) wasm_error(c, wasm_loc(0, 0), "wasm: too many globals"); - if (m->has_memory && m->memory.is_import) - wasm_error(c, wasm_loc(0, 0), - "wasm: imported memories are not lowered yet"); + wasm_cg_build_runtime(c, b, m, &rt); memset(syms, 0, sizeof syms); memset(func_types, 0, sizeof func_types); - memset(global_syms, 0, sizeof global_syms); - if (m->has_memory) { - CfreeCgDecl decl; - CfreeCgDataDefAttrs attrs; - uint32_t max_pages = m->memory.has_max ? m->memory.max_pages - : m->memory.min_pages; - uint32_t memory_len = max_pages ? max_pages * 65536u : 1u; - CfreeCgTypeId mem_ty = - cfree_cg_type_array(c, b.id[CFREE_CG_BUILTIN_I8], memory_len); - memset(&decl, 0, sizeof decl); - decl.kind = CFREE_CG_DECL_OBJECT; - decl.linkage_name = cfree_sym_intern(c, "__cfree_wasm_memory"); - decl.display_name = decl.linkage_name; - decl.type = mem_ty; - decl.sym.bind = CFREE_SB_LOCAL; - decl.as.object.align = 16; - memory_sym = cfree_cg_decl(cg, decl); - memset(&attrs, 0, sizeof attrs); - attrs.align = 16; - cfree_cg_data_begin(cg, memory_sym, attrs); - if (m->memory.data_len) { - cfree_cg_data_bytes(cg, m->memory.data, m->memory.data_len); - if (memory_len > m->memory.data_len) - cfree_cg_data_zero(cg, memory_len - m->memory.data_len); - } else { - cfree_cg_data_zero(cg, memory_len); - } - cfree_cg_data_end(cg); - - memset(&decl, 0, sizeof decl); - decl.kind = CFREE_CG_DECL_OBJECT; - decl.linkage_name = cfree_sym_intern(c, "__cfree_wasm_memory_pages"); - decl.display_name = decl.linkage_name; - decl.type = b.id[CFREE_CG_BUILTIN_I32]; - decl.sym.bind = CFREE_SB_LOCAL; - decl.as.object.align = 4; - memory_pages_sym = cfree_cg_decl(cg, decl); - memset(&attrs, 0, sizeof attrs); - attrs.align = 4; - cfree_cg_data_begin(cg, memory_pages_sym, attrs); - cfree_cg_data_int(cg, m->memory.min_pages, b.id[CFREE_CG_BUILTIN_I32]); - cfree_cg_data_end(cg); - } - for (i = 0; i < m->nglobals; ++i) { - const WasmGlobal *g = &m->globals[i]; - CfreeCgDecl decl; - CfreeCgDataDefAttrs attrs; - CfreeSym name; - char local_name[40]; - if (g->is_import && g->import_name) { - name = cfree_sym_intern(c, g->import_name); - } else { - size_t pos = 0; - const char prefix[] = "__cfree_wasm_global_"; - uint32_t n = i, div = 1000000000u; - memcpy(local_name, prefix, sizeof(prefix) - 1u); - pos = sizeof(prefix) - 1u; - while (div > 1u && n / div == 0) - div /= 10u; - while (div) { - local_name[pos++] = (char)('0' + (n / div) % 10u); - div /= 10u; - } - local_name[pos] = '\0'; - name = cfree_sym_intern(c, local_name); - } - memset(&decl, 0, sizeof decl); - decl.kind = CFREE_CG_DECL_OBJECT; - decl.linkage_name = g->is_import ? cfree_cg_c_linkage_name(c, name) : name; - decl.display_name = name; - decl.type = wasm_cg_type(c, b, g->type); - decl.sym.bind = g->is_import ? CFREE_SB_GLOBAL : CFREE_SB_LOCAL; - decl.as.object.align = cfree_cg_type_align(c, decl.type); - global_syms[i] = cfree_cg_decl(cg, decl); - if (g->is_import) - continue; - memset(&attrs, 0, sizeof attrs); - attrs.align = decl.as.object.align; - cfree_cg_data_begin(cg, global_syms[i], attrs); - if (g->type == WASM_VAL_F32 || g->type == WASM_VAL_F64) - cfree_cg_data_float(cg, g->init.fp, decl.type); - else - cfree_cg_data_int(cg, (uint64_t)g->init.imm, decl.type); - cfree_cg_data_end(cg); - } - if (m->has_start) { + { CfreeCgDecl decl; - CfreeCgDataDefAttrs attrs; + CfreeCgFuncParam init_param; + CfreeCgFuncSig sig; + memset(&init_param, 0, sizeof init_param); + init_param.type = rt.instance_ptr_ty; + memset(&sig, 0, sizeof sig); + sig.ret = b.id[CFREE_CG_BUILTIN_VOID]; + sig.params = &init_param; + sig.nparams = 1; + sig.call_conv = CFREE_CG_CC_TARGET_C; memset(&decl, 0, sizeof decl); - decl.kind = CFREE_CG_DECL_OBJECT; - decl.linkage_name = cfree_sym_intern(c, "__cfree_wasm_start_ran"); + decl.kind = CFREE_CG_DECL_FUNC; + decl.linkage_name = + cfree_cg_c_linkage_name(c, cfree_sym_intern(c, "__cfree_wasm_init")); decl.display_name = decl.linkage_name; - decl.type = b.id[CFREE_CG_BUILTIN_I32]; - decl.sym.bind = CFREE_SB_LOCAL; - decl.as.object.align = 4; - start_flag_sym = cfree_cg_decl(cg, decl); - memset(&attrs, 0, sizeof attrs); - attrs.align = 4; - cfree_cg_data_begin(cg, start_flag_sym, attrs); - cfree_cg_data_int(cg, 0, b.id[CFREE_CG_BUILTIN_I32]); - cfree_cg_data_end(cg); + decl.type = cfree_cg_type_func(c, sig); + decl.sym.bind = CFREE_SB_GLOBAL; + init_sym = cfree_cg_decl(cg, decl); } for (i = 0; i < m->nfuncs; ++i) { const WasmFunc *f = &m->funcs[i]; - CfreeCgFuncParam cg_params[16]; + CfreeCgFuncParam cg_params[17]; CfreeCgFuncSig sig; CfreeCgDecl decl; char local_name[40]; CfreeSym source_name; + memset(&cg_params[0], 0, sizeof cg_params[0]); + cg_params[0].type = rt.instance_ptr_ty; for (j = 0; j < f->nparams; ++j) { - memset(&cg_params[j], 0, sizeof cg_params[j]); - cg_params[j].type = wasm_cg_type(c, b, f->params[j]); + memset(&cg_params[j + 1u], 0, sizeof cg_params[j + 1u]); + cg_params[j + 1u].type = wasm_cg_type(c, b, f->params[j]); } memset(&sig, 0, sizeof sig); sig.ret = f->nresults ? wasm_cg_type(c, b, f->results[0]) : b.id[CFREE_CG_BUILTIN_VOID]; sig.params = cg_params; - sig.nparams = f->nparams; + sig.nparams = f->nparams + 1u; sig.call_conv = CFREE_CG_CC_TARGET_C; func_types[i] = cfree_cg_type_func(c, sig); if (!func_types[i]) @@ -4583,12 +4831,55 @@ static void wasm_emit_cg(CfreeCompiler *c, const CfreeCompileOptions *opts, decl.linkage_name = cfree_cg_c_linkage_name(c, source_name); decl.display_name = source_name; decl.type = func_types[i]; - decl.sym.bind = (f->export_name || f->is_import) ? CFREE_SB_GLOBAL - : CFREE_SB_LOCAL; + decl.sym.bind = + (f->export_name || f->is_import) ? CFREE_SB_GLOBAL : CFREE_SB_LOCAL; syms[i] = cfree_cg_decl(cg, decl); if (!syms[i]) wasm_error(c, wasm_loc(0, 0), "wasm: failed to declare function"); } + if (init_sym) { + CfreeCgLocalAttrs attrs; + CfreeCgLocal instance_local; + memset(&attrs, 0, sizeof attrs); + cfree_cg_func_begin(cg, init_sym); + instance_local = cfree_cg_param(cg, 0, rt.instance_ptr_ty, attrs); + 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]); + 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) { + CfreeCgSym data_sym = + cfree_cg_const_data(cg, m->memory.data, m->memory.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); + cfree_cg_push_symbol_addr(cg, data_sym, 0); + cfree_cg_memcpy(cg, m->memory.data_init_len, mem, mem); + } + } + for (i = 0; i < m->nglobals; ++i) { + const WasmGlobal *g = &m->globals[i]; + if (g->is_import) + continue; + wasm_cg_push_global_lvalue(cg, &rt, instance_local, i); + if (g->type == WASM_VAL_F32 || g->type == WASM_VAL_F64) + cfree_cg_push_float(cg, g->init.fp, wasm_cg_type(c, b, g->type)); + else + cfree_cg_push_int(cg, (uint64_t)g->init.imm, + wasm_cg_type(c, b, g->type)); + cfree_cg_store(cg, wasm_cg_mem(c, b, g->type)); + } + if (m->has_start) + wasm_cg_call_func(c, cg, b, &m->funcs[m->start_func], &rt, + syms[m->start_func], instance_local); + cfree_cg_ret_void(cg); + cfree_cg_func_end(cg); + } for (i = 0; i < m->nfuncs; ++i) { const WasmFunc *f = &m->funcs[i]; struct { @@ -4599,14 +4890,20 @@ static void wasm_emit_cg(CfreeCompiler *c, const CfreeCompileOptions *opts, CfreeCgLabel else_label; } control[64]; uint32_t ncontrol = 0; + CfreeCgLocal instance_local; if (f->is_import) continue; cfree_cg_func_begin(cg, syms[i]); + { + CfreeCgLocalAttrs attrs; + memset(&attrs, 0, sizeof attrs); + instance_local = cfree_cg_param(cg, 0, rt.instance_ptr_ty, attrs); + } for (j = 0; j < f->nparams; ++j) { CfreeCgLocalAttrs attrs; memset(&attrs, 0, sizeof attrs); - locals[j] = cfree_cg_param(cg, j, wasm_cg_type(c, b, f->params[j]), - attrs); + locals[j] = + cfree_cg_param(cg, j + 1u, wasm_cg_type(c, b, f->params[j]), attrs); } for (j = 0; j < f->nlocals; ++j) { CfreeCgLocalAttrs attrs; @@ -4618,19 +4915,6 @@ static void wasm_emit_cg(CfreeCompiler *c, const CfreeCompileOptions *opts, wasm_cg_push_zero(c, cg, b, f->locals[j]); cfree_cg_store(cg, wasm_cg_mem(c, b, f->locals[j])); } - if (m->has_start && f->export_name && i != m->start_func) { - CfreeCgLabel started = cfree_cg_label_new(cg); - cfree_cg_push_symbol_lvalue(cg, start_flag_sym, 0); - cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); - cfree_cg_push_int(cg, 0, b.id[CFREE_CG_BUILTIN_I32]); - cfree_cg_int_cmp(cg, CFREE_CG_INT_NE); - cfree_cg_branch_true(cg, started); - cfree_cg_push_symbol_lvalue(cg, start_flag_sym, 0); - cfree_cg_push_int(cg, 1, b.id[CFREE_CG_BUILTIN_I32]); - cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); - cfree_cg_call_symbol(cg, syms[m->start_func], 0, (CfreeCgCallAttrs){0}); - cfree_cg_label_place(cg, started); - } for (j = 0; j < f->ninsns; ++j) { WasmInsn in = f->insns[j]; switch (in.kind) { @@ -4736,49 +5020,47 @@ static void wasm_emit_cg(CfreeCompiler *c, const CfreeCompileOptions *opts, cfree_cg_switch(cg, sw); break; } - case WASM_INSN_SELECT: - { - CfreeCgTypeId ty = wasm_cg_type(c, b, (WasmValType)in.type); - CfreeCgMemAccess mem = wasm_cg_mem(c, b, (WasmValType)in.type); - CfreeCgLocalAttrs attrs; - CfreeCgLocal lhs, rhs, cond, result; - CfreeCgLabel else_label = cfree_cg_label_new(cg); - CfreeCgLabel end_label = cfree_cg_label_new(cg); - memset(&attrs, 0, sizeof attrs); - attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP; - lhs = cfree_cg_local(cg, ty, attrs); - rhs = cfree_cg_local(cg, ty, attrs); - result = cfree_cg_local(cg, ty, attrs); - cond = cfree_cg_local(cg, b.id[CFREE_CG_BUILTIN_I32], attrs); - - cfree_cg_push_local(cg, cond); - cfree_cg_swap(cg); - cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); - cfree_cg_push_local(cg, rhs); - cfree_cg_swap(cg); - cfree_cg_store(cg, mem); - cfree_cg_push_local(cg, lhs); - cfree_cg_swap(cg); - cfree_cg_store(cg, mem); + case WASM_INSN_SELECT: { + CfreeCgTypeId ty = wasm_cg_type(c, b, (WasmValType)in.type); + CfreeCgMemAccess mem = wasm_cg_mem(c, b, (WasmValType)in.type); + CfreeCgLocalAttrs attrs; + CfreeCgLocal lhs, rhs, cond, result; + CfreeCgLabel else_label = cfree_cg_label_new(cg); + CfreeCgLabel end_label = cfree_cg_label_new(cg); + memset(&attrs, 0, sizeof attrs); + attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP; + lhs = cfree_cg_local(cg, ty, attrs); + rhs = cfree_cg_local(cg, ty, attrs); + result = cfree_cg_local(cg, ty, attrs); + cond = cfree_cg_local(cg, b.id[CFREE_CG_BUILTIN_I32], attrs); - cfree_cg_push_local(cg, cond); - cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); - cfree_cg_branch_false(cg, else_label); - cfree_cg_push_local(cg, result); - cfree_cg_push_local(cg, lhs); - cfree_cg_load(cg, mem); - cfree_cg_store(cg, mem); - cfree_cg_jump(cg, end_label); - cfree_cg_label_place(cg, else_label); - cfree_cg_push_local(cg, result); - cfree_cg_push_local(cg, rhs); - cfree_cg_load(cg, mem); - cfree_cg_store(cg, mem); - cfree_cg_label_place(cg, end_label); - cfree_cg_push_local(cg, result); - cfree_cg_load(cg, mem); - } - break; + cfree_cg_push_local(cg, cond); + cfree_cg_swap(cg); + cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); + cfree_cg_push_local(cg, rhs); + cfree_cg_swap(cg); + cfree_cg_store(cg, mem); + cfree_cg_push_local(cg, lhs); + cfree_cg_swap(cg); + cfree_cg_store(cg, mem); + + cfree_cg_push_local(cg, cond); + cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); + cfree_cg_branch_false(cg, else_label); + cfree_cg_push_local(cg, result); + cfree_cg_push_local(cg, lhs); + cfree_cg_load(cg, mem); + cfree_cg_store(cg, mem); + cfree_cg_jump(cg, end_label); + cfree_cg_label_place(cg, else_label); + cfree_cg_push_local(cg, result); + cfree_cg_push_local(cg, rhs); + cfree_cg_load(cg, mem); + cfree_cg_store(cg, mem); + cfree_cg_label_place(cg, end_label); + cfree_cg_push_local(cg, result); + cfree_cg_load(cg, mem); + } break; case WASM_INSN_I32_CONST: cfree_cg_push_int(cg, (uint64_t)(uint32_t)in.imm, b.id[CFREE_CG_BUILTIN_I32]); @@ -4795,16 +5077,14 @@ static void wasm_emit_cg(CfreeCompiler *c, const CfreeCompileOptions *opts, case WASM_INSN_LOCAL_GET: { uint32_t index = (uint32_t)in.imm; cfree_cg_push_local(cg, locals[index]); - cfree_cg_load(cg, - wasm_cg_mem(c, b, wasm_func_local_type(f, index))); + cfree_cg_load(cg, wasm_cg_mem(c, b, wasm_func_local_type(f, index))); break; } case WASM_INSN_LOCAL_SET: { uint32_t index = (uint32_t)in.imm; cfree_cg_push_local(cg, locals[index]); cfree_cg_swap(cg); - cfree_cg_store(cg, - wasm_cg_mem(c, b, wasm_func_local_type(f, index))); + cfree_cg_store(cg, wasm_cg_mem(c, b, wasm_func_local_type(f, index))); break; } case WASM_INSN_LOCAL_TEE: { @@ -4812,13 +5092,12 @@ static void wasm_emit_cg(CfreeCompiler *c, const CfreeCompileOptions *opts, cfree_cg_dup(cg); cfree_cg_push_local(cg, locals[index]); cfree_cg_swap(cg); - cfree_cg_store(cg, - wasm_cg_mem(c, b, wasm_func_local_type(f, index))); + cfree_cg_store(cg, wasm_cg_mem(c, b, wasm_func_local_type(f, index))); break; } case WASM_INSN_CALL: - cfree_cg_call_symbol(cg, syms[in.imm], m->funcs[in.imm].nparams, - (CfreeCgCallAttrs){0}); + wasm_cg_call_func(c, cg, b, &m->funcs[in.imm], &rt, syms[in.imm], + instance_local); break; case WASM_INSN_CALL_INDIRECT: { const WasmFuncType *t = &m->types[in.imm]; @@ -4835,15 +5114,15 @@ static void wasm_emit_cg(CfreeCompiler *c, const CfreeCompileOptions *opts, wasm_error(c, wasm_loc(0, 0), "wasm: too many call_indirect args"); selector = cfree_cg_local(cg, b.id[CFREE_CG_BUILTIN_I32], attrs); if (t->nresults) - result = cfree_cg_local(cg, wasm_cg_type(c, b, t->results[0]), - attrs); + result = + cfree_cg_local(cg, wasm_cg_type(c, b, t->results[0]), attrs); cfree_cg_push_local(cg, selector); cfree_cg_swap(cg); cfree_cg_store(cg, i32_mem); for (uint32_t p = 0; p < t->nparams; ++p) { uint32_t param = t->nparams - 1u - p; - args[param] = cfree_cg_local(cg, wasm_cg_type(c, b, t->params[param]), - attrs); + args[param] = + cfree_cg_local(cg, wasm_cg_type(c, b, t->params[param]), attrs); cfree_cg_push_local(cg, args[param]); cfree_cg_swap(cg); cfree_cg_store(cg, wasm_cg_mem(c, b, t->params[param])); @@ -4911,11 +5190,13 @@ static void wasm_emit_cg(CfreeCompiler *c, const CfreeCompileOptions *opts, wasm_cg_trap(cg); continue; } + cfree_cg_push_local(cg, instance_local); + cfree_cg_load(cg, wasm_cg_mem_type(rt.instance_ptr_ty)); for (uint32_t p = 0; p < t->nparams; ++p) { cfree_cg_push_local(cg, args[p]); cfree_cg_load(cg, wasm_cg_mem(c, b, t->params[p])); } - cfree_cg_call_symbol(cg, syms[funcidx], t->nparams, + cfree_cg_call_symbol(cg, syms[funcidx], t->nparams + 1u, (CfreeCgCallAttrs){0}); if (t->nresults) { cfree_cg_push_local(cg, result); @@ -4936,13 +5217,13 @@ static void wasm_emit_cg(CfreeCompiler *c, const CfreeCompileOptions *opts, } case WASM_INSN_GLOBAL_GET: { uint32_t index = (uint32_t)in.imm; - cfree_cg_push_symbol_lvalue(cg, global_syms[index], 0); + wasm_cg_push_global_lvalue(cg, &rt, instance_local, index); cfree_cg_load(cg, wasm_cg_mem(c, b, m->globals[index].type)); break; } case WASM_INSN_GLOBAL_SET: { uint32_t index = (uint32_t)in.imm; - cfree_cg_push_symbol_lvalue(cg, global_syms[index], 0); + wasm_cg_push_global_lvalue(cg, &rt, instance_local, index); cfree_cg_swap(cg); cfree_cg_store(cg, wasm_cg_mem(c, b, m->globals[index].type)); break; @@ -4957,7 +5238,7 @@ static void wasm_emit_cg(CfreeCompiler *c, const CfreeCompileOptions *opts, cfree_cg_drop(cg); break; case WASM_INSN_MEMORY_SIZE: - cfree_cg_push_symbol_lvalue(cg, memory_pages_sym, 0); + wasm_cg_push_memory_pages_lvalue(cg, &rt, instance_local); cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); break; case WASM_INSN_MEMORY_GROW: { @@ -4966,8 +5247,6 @@ static void wasm_emit_cg(CfreeCompiler *c, const CfreeCompileOptions *opts, CfreeCgLabel fail = cfree_cg_label_new(cg); CfreeCgLabel done = cfree_cg_label_new(cg); CfreeCgTypeId i32 = b.id[CFREE_CG_BUILTIN_I32]; - uint32_t max_pages = m->memory.has_max ? m->memory.max_pages - : m->memory.min_pages; memset(&attrs, 0, sizeof attrs); attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP; delta = cfree_cg_local(cg, i32, attrs); @@ -4977,20 +5256,21 @@ static void wasm_emit_cg(CfreeCompiler *c, const CfreeCompileOptions *opts, cfree_cg_swap(cg); cfree_cg_store(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); cfree_cg_push_local(cg, old_pages); - cfree_cg_push_symbol_lvalue(cg, memory_pages_sym, 0); + 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)); cfree_cg_push_local(cg, delta); cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); - cfree_cg_push_int(cg, max_pages, 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_push_local(cg, old_pages); cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); 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); - cfree_cg_push_symbol_lvalue(cg, memory_pages_sym, 0); + wasm_cg_push_memory_pages_lvalue(cg, &rt, instance_local); cfree_cg_push_local(cg, old_pages); cfree_cg_load(cg, wasm_cg_mem(c, b, WASM_VAL_I32)); cfree_cg_push_local(cg, delta); @@ -5030,8 +5310,8 @@ static void wasm_emit_cg(CfreeCompiler *c, const CfreeCompileOptions *opts, memset(&mem, 0, sizeof mem); mem.type = storage; mem.align = in.align; - wasm_cg_memory_check(c, cg, b, m, memory_pages_sym, &in); - wasm_cg_memory_lvalue(cg, memory_sym, (uint32_t)in.imm); + wasm_cg_memory_check(c, cg, b, m, &rt, instance_local, &in); + wasm_cg_memory_lvalue(cg, &rt, instance_local, (uint32_t)in.imm); cfree_cg_load(cg, mem); if (storage != result) { if (in.kind == WASM_INSN_I32_LOAD8_S || @@ -5070,18 +5350,15 @@ static void wasm_emit_cg(CfreeCompiler *c, const CfreeCompileOptions *opts, cfree_cg_push_local(cg, value_tmp); cfree_cg_swap(cg); cfree_cg_store(cg, mem); - wasm_cg_memory_check(c, cg, b, m, memory_pages_sym, &in); + 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_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_push_local(cg, value_tmp); cfree_cg_load(cg, mem); - cfree_cg_push_symbol_lvalue(cg, memory_sym, 0); - cfree_cg_rot3(cg); - cfree_cg_index(cg, (uint32_t)in.imm); - cfree_cg_swap(cg); cfree_cg_store(cg, mem); break; } @@ -5283,7 +5560,9 @@ static void wasm_emit_cg(CfreeCompiler *c, const CfreeCompileOptions *opts, cfree_cg_free(cg); } -static void write_byte(CfreeWriter *w, uint8_t b) { w->write(w, &b, 1); } +static void write_byte(CfreeWriter *w, uint8_t b) { + w->write(w, &b, 1); +} static void write_uleb(CfreeWriter *w, uint64_t v) { do { @@ -5868,21 +6147,17 @@ static void enc_code(CfreeWriter *w, const WasmModule *m) { in.kind == WASM_INSN_LOCAL_SET || in.kind == WASM_INSN_LOCAL_TEE || in.kind == WASM_INSN_GLOBAL_GET || - in.kind == WASM_INSN_GLOBAL_SET || - in.kind == WASM_INSN_CALL || - in.kind == WASM_INSN_BR || - in.kind == WASM_INSN_BR_IF) + in.kind == WASM_INSN_GLOBAL_SET || in.kind == WASM_INSN_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) { write_uleb(body, (uint64_t)in.imm); write_uleb(body, in.align); - } - else if (in.kind == WASM_INSN_BR_TABLE) { + } else if (in.kind == WASM_INSN_BR_TABLE) { write_uleb(body, in.ntargets ? in.ntargets - 1u : 0u); for (uint32_t k = 0; k < in.ntargets; ++k) write_uleb(body, in.targets[k]); - } - else if (wasm_insn_is_mem(in.kind)) { + } else if (wasm_insn_is_mem(in.kind)) { write_uleb(body, in.align); write_uleb(body, (uint64_t)in.imm); } else if (in.kind == WASM_INSN_MEMORY_SIZE || @@ -5899,7 +6174,7 @@ 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_len) { + if (!m->has_memory || !m->memory.data_init_len) { write_uleb(w, 0); return; } @@ -5908,8 +6183,8 @@ static void enc_data(CfreeWriter *w, const WasmModule *m) { write_byte(w, 0x41); write_sleb(w, 0); write_byte(w, 0x0b); - write_uleb(w, m->memory.data_len); - w->write(w, m->memory.data, m->memory.data_len); + 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) { @@ -5972,7 +6247,7 @@ static void wasm_encode(CfreeCompiler *c, const WasmModule *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_len) + if (m->has_memory && m->memory.data_init_len) encode_section(h, out, 11, enc_data, m); } diff --git a/lang/wasm/wasm.h b/lang/wasm/wasm.h @@ -3,6 +3,8 @@ #include <cfree.h> +#include "runtime_abi.h" + int cfree_wasm_compile(CfreeCompiler*, const CfreeCompileOptions*, const CfreeBytesInput* input, CfreeObjBuilder* out); void cfree_wasm_register(CfreeCompiler*); diff --git a/test/link/harness/jit_runner.c b/test/link/harness/jit_runner.c @@ -332,6 +332,15 @@ static void jit_tls_ctx_destroy(void* user, void* ctx_v) { } 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; +} WasmRunnerMemoryPrefix; + +typedef void (*WasmRunnerInitFn)(void*); +typedef int (*WasmRunnerMainFn)(void*); + int main(int argc, char** argv) { { long ps = sysconf(_SC_PAGESIZE); @@ -490,7 +499,9 @@ int main(int argc, char** argv) { return present_ok ? 0 : 1; } - int (*fn)(void) = cfree_jit_lookup(jit, "test_main"); + void* wasm_init = cfree_jit_lookup(jit, "__cfree_wasm_init"); + void* entry = cfree_jit_lookup(jit, "test_main"); + int (*fn)(void) = entry; /* AArch64 TLS local-exec setup. Build a thread-local image — * 16-byte TCB + .tdata copy + .tbss zero-fill — and point @@ -517,7 +528,23 @@ int main(int argc, char** argv) { #endif int result; - if (fn) { + if (wasm_init && entry) { + uint8_t* instance = calloc(1, 64u * 1024u); + uint8_t* memory = calloc(1, 16u * 1024u * 1024u); + if (!instance || !memory) { + free(memory); + free(instance); + cfree_jit_run_dtors(jit); + cfree_jit_free(jit); + cfree_compiler_free(c); + return 1; + } + ((WasmRunnerMemoryPrefix*)instance)->data = memory; + ((WasmRunnerInitFn)wasm_init)(instance); + result = ((WasmRunnerMainFn)entry)(instance); + free(memory); + free(instance); + } else if (fn) { #if defined(__aarch64__) || defined(__arm64__) /* msr + blr in immediate succession; the compiler must not * insert anything between. `volatile` and the "memory" diff --git a/test/wasm/harness/start_wasm.c b/test/wasm/harness/start_wasm.c @@ -0,0 +1,129 @@ +/* Freestanding _start for Wasm-input native executable tests. + * + * Wasm frontend exports use the internal instance ABI: + * __cfree_wasm_init(instance) + * test_main(instance) + */ + +extern void __cfree_wasm_init(void *); +extern int test_main(void *); + +typedef struct WasmStartMemoryPrefix { + unsigned char *data; + unsigned int pages; + unsigned int max_pages; +} WasmStartMemoryPrefix; + +#define WASM_START_INSTANCE_SIZE (64u * 1024u) +#define WASM_START_MEMORY_SIZE (16u * 1024u * 1024u) +#define WASM_START_PROT_READ 1 +#define WASM_START_PROT_WRITE 2 +#define WASM_START_MAP_PRIVATE 2 +#if defined(__APPLE__) +#define WASM_START_MAP_ANON 0x1000 +#else +#define WASM_START_MAP_ANON 0x20 +#endif + +#if defined(__APPLE__) +extern void exit(int) __attribute__((noreturn)); +extern void *mmap(void *, unsigned long, int, int, int, long); +#endif + +__attribute__((noreturn)) static void do_exit(int code) { +#if defined(__APPLE__) + exit(code); + __builtin_unreachable(); +#elif defined(__aarch64__) + register long x8 __asm__("x8") = 94; + register long x0 __asm__("x0") = code; + __asm__ volatile("svc #0" ::"r"(x8), "r"(x0) : "memory"); +#elif defined(__x86_64__) + register long rax __asm__("rax") = 231; + register long rdi __asm__("rdi") = code; + __asm__ volatile("syscall" ::"r"(rax), "r"(rdi) : "rcx", "r11", "memory"); +#elif defined(__riscv) && __riscv_xlen == 64 + register long a7 __asm__("a7") = 94; + register long a0 __asm__("a0") = code; + __asm__ volatile("ecall" ::"r"(a7), "r"(a0) : "memory"); +#else +#error "start_wasm.c: unsupported architecture" +#endif + __builtin_unreachable(); +} + +static void *start_mmap(unsigned long size) { +#if defined(__APPLE__) + void *p = mmap((void *)0, size, WASM_START_PROT_READ | WASM_START_PROT_WRITE, + WASM_START_MAP_PRIVATE | WASM_START_MAP_ANON, -1, 0); + if ((long)p == -1) + return (void *)0; + return p; +#elif defined(__aarch64__) + register long x8 __asm__("x8") = 222; + register long x0 __asm__("x0") = 0; + register long x1 __asm__("x1") = (long)size; + register long x2 __asm__("x2") = + WASM_START_PROT_READ | WASM_START_PROT_WRITE; + register long x3 __asm__("x3") = + WASM_START_MAP_PRIVATE | WASM_START_MAP_ANON; + register long x4 __asm__("x4") = -1; + register long x5 __asm__("x5") = 0; + __asm__ volatile("svc #0" + : "+r"(x0) + : "r"(x8), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5) + : "memory"); + if (x0 < 0 && x0 > -4096) + return (void *)0; + return (void *)x0; +#elif defined(__x86_64__) + register long rax __asm__("rax") = 9; + register long rdi __asm__("rdi") = 0; + register long rsi __asm__("rsi") = (long)size; + register long rdx __asm__("rdx") = + WASM_START_PROT_READ | WASM_START_PROT_WRITE; + register long r10 __asm__("r10") = + WASM_START_MAP_PRIVATE | WASM_START_MAP_ANON; + register long r8 __asm__("r8") = -1; + register long r9 __asm__("r9") = 0; + __asm__ volatile("syscall" + : "+r"(rax) + : "r"(rdi), "r"(rsi), "r"(rdx), "r"(r10), "r"(r8), "r"(r9) + : "rcx", "r11", "memory"); + if (rax < 0 && rax > -4096) + return (void *)0; + return (void *)rax; +#elif defined(__riscv) && __riscv_xlen == 64 + register long a7 __asm__("a7") = 222; + register long a0 __asm__("a0") = 0; + register long a1 __asm__("a1") = (long)size; + register long a2 __asm__("a2") = + WASM_START_PROT_READ | WASM_START_PROT_WRITE; + register long a3 __asm__("a3") = + WASM_START_MAP_PRIVATE | WASM_START_MAP_ANON; + register long a4 __asm__("a4") = -1; + register long a5 __asm__("a5") = 0; + __asm__ volatile("ecall" + : "+r"(a0) + : "r"(a7), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5) + : "memory"); + if (a0 < 0 && a0 > -4096) + return (void *)0; + return (void *)a0; +#else +#error "start_wasm.c: unsupported architecture" +#endif +} + +#if defined(__x86_64__) +__attribute__((force_align_arg_pointer)) +#endif +void _start(void) { + void *instance = start_mmap(WASM_START_INSTANCE_SIZE); + void *memory = start_mmap(WASM_START_MEMORY_SIZE); + if (!instance || !memory) + do_exit(1); + ((WasmStartMemoryPrefix *)instance)->data = (unsigned char *)memory; + __cfree_wasm_init(instance); + do_exit(test_main(instance)); +} diff --git a/test/wasm/run.sh b/test/wasm/run.sh @@ -10,7 +10,6 @@ CFREE_BIN="${CFREE:-$ROOT/build/cfree}" WASM_TOOL="$ROOT/build/test/wasm-tool" JIT_RUNNER="$ROOT/build/test/jit-runner" LINK_EXE_RUNNER="$ROOT/build/test/link-exe-runner" -LINK_TEST_DIR="$ROOT/test/link" TEST_ARCH="${CFREE_TEST_ARCH:-aa64}" TEST_OBJ="${CFREE_TEST_OBJ:-macho}" @@ -88,12 +87,13 @@ EXEC_TARGET_MOUNT_ROOT="$BUILD_DIR" # shellcheck source=../lib/exec_target.sh source "$ROOT/test/lib/exec_target.sh" -START_OBJ="$BUILD_DIR/start.o" -have_start_obj=0 +WASM_START_OBJ="$BUILD_DIR/start-wasm.o" +have_wasm_start_obj=0 if clang --target="$clang_triple" -ffreestanding -fno-stack-protector \ - -fno-builtin -nostdlib -c "$LINK_TEST_DIR/harness/start.c" \ - -o "$START_OBJ" >"$BUILD_DIR/start.out" 2>"$BUILD_DIR/start.err"; then - have_start_obj=1 + -fno-builtin -nostdlib -c "$ROOT/test/wasm/harness/start_wasm.c" \ + -o "$WASM_START_OBJ" >"$BUILD_DIR/start-wasm.out" \ + 2>"$BUILD_DIR/start-wasm.err"; then + have_wasm_start_obj=1 fi MACHO_DSO_ARGS=() @@ -173,9 +173,9 @@ for wat in "$CASES_DIR"/*.wat; do note_skip "$name/J" "host arch does not match target or no jit-runner" fi - if [ "$have_link_runner" -eq 1 ] && [ "$have_start_obj" -eq 1 ]; then + if [ "$have_link_runner" -eq 1 ] && [ "$have_wasm_start_obj" -eq 1 ]; then exe="$work/$name.exe" - if "$LINK_EXE_RUNNER" -o "$exe" "${MACHO_DSO_ARGS[@]}" "$wat_obj" "$START_OBJ" \ + if "$LINK_EXE_RUNNER" -o "$exe" "${MACHO_DSO_ARGS[@]}" "$wat_obj" "$WASM_START_OBJ" \ >"$work/link.out" 2>"$work/link.err"; then if exec_target_supported "$EXEC_TAG"; then exec_target_run "$EXEC_TAG" "$exe" "$work/exec.out" "$work/exec.err" @@ -192,7 +192,7 @@ for wat in "$CASES_DIR"/*.wat; do note_fail "$name/E link failed" fi else - note_skip "$name/E" "requires link runner and start.o" + note_skip "$name/E" "requires link runner and wasm start.o" fi done