commit cfea70242e17a07c66a75f0a48768f1bf16c8b4c
parent edfad2f38c20b36c2a9dff1014b5e0e54f433842
Author: William Casarin <jb55@jb55.com>
Date: Sat, 17 Jul 2021 10:13:25 -0700
globals, memory, interp_store
Signed-off-by: William Casarin <jb55@jb55.com>
Diffstat:
M | src/cursor.h | | | 20 | ++++++++++++++++++-- |
M | src/wasm.c | | | 336 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- |
M | src/wasm.h | | | 12 | +++++++----- |
3 files changed, 332 insertions(+), 36 deletions(-)
diff --git a/src/cursor.h b/src/cursor.h
@@ -28,6 +28,12 @@ static inline void reset_cursor(struct cursor *cursor)
cursor->p = cursor->start;
}
+static inline void wipe_cursor(struct cursor *cursor)
+{
+ reset_cursor(cursor);
+ memset(cursor->start, 0, cursor->end - cursor->start);
+}
+
static inline void make_cursor(u8 *start, u8 *end, struct cursor *cursor)
{
cursor->start = start;
@@ -55,7 +61,7 @@ static inline int cursor_eof(struct cursor *c)
return c->p == c->end;
}
-static inline void *cursor_alloc(struct cursor *mem, unsigned long size)
+static inline void *cursor_malloc(struct cursor *mem, unsigned long size)
{
void *ret;
@@ -64,12 +70,22 @@ static inline void *cursor_alloc(struct cursor *mem, unsigned long size)
}
ret = mem->p;
- memset(ret, 0, size);
mem->p += size;
return ret;
}
+static inline void *cursor_alloc(struct cursor *mem, unsigned long size)
+{
+ void *ret;
+ if (!(ret = cursor_malloc(mem, size))) {
+ return 0;
+ }
+
+ memset(ret, 0, size);
+ return ret;
+}
+
static inline int cursor_slice(struct cursor *mem, struct cursor *slice, size_t size)
{
u8 *p;
diff --git a/src/wasm.c b/src/wasm.c
@@ -58,9 +58,12 @@ static const char *valtype_name(enum valtype valtype)
case val_i64: return "i64";
case val_f32: return "f32";
case val_f64: return "f64";
+ case val_ref_null: return "null";
+ case val_ref_func: return "func";
+ case val_ref_extern: return "extern";
}
- return "unk";
+ return "?";
}
static INLINE struct local *get_locals(struct func *func, int *num_locals)
@@ -70,6 +73,8 @@ static INLINE struct local *get_locals(struct func *func, int *num_locals)
*num_locals = func->wasm_func->num_locals;
return func->wasm_func->locals;
case func_type_builtin:
+ if (func->builtin == NULL)
+ return NULL;
*num_locals = func->builtin->num_locals;
return func->builtin->locals;
}
@@ -141,19 +146,57 @@ static INLINE struct val *stack_topval(struct wasm_interp *interp)
return cursor_topval(&interp->stack);
}
-static INLINE int stack_pop_i32(struct wasm_interp *interp, int *i)
+static INLINE int cursor_pop_i32(struct cursor *stack, int *i)
{
struct val val;
- if (unlikely(!cursor_popval(&interp->stack, &val)))
- return interp_error(interp, "couldn't pop val");
- if (unlikely(val.type != val_i32)) {
- return interp_error(interp, "popped type %s instead of i32",
- valtype_name(val.type));
- }
+ if (unlikely(!cursor_popval(stack, &val)))
+ return 0;
+ if (unlikely(val.type != val_i32))
+ return 0;
*i = val.i32;
return 1;
}
+static INLINE int is_number_type(enum valtype vt)
+{
+ switch (vt) {
+ case val_i32:
+ case val_i64:
+ case val_f32:
+ case val_f64:
+ return 1;
+ case val_ref_null:
+ case val_ref_func:
+ case val_ref_extern:
+ return 0;
+ }
+
+ return 0;
+}
+
+static INLINE int cursor_pop_number(struct cursor *stack, struct val *val)
+{
+ if (unlikely(!cursor_popval(stack, val))) {
+ return 0;
+ }
+
+ if (unlikely(!is_number_type(val->type))) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static INLINE int stack_pop_i32(struct wasm_interp *interp, int *i)
+{
+ return cursor_pop_i32(&interp->stack, i);
+}
+
+static INLINE int stack_pop_number(struct wasm_interp *interp, struct val *val)
+{
+ return cursor_pop_number(&interp->stack, val);
+}
+
static int builtin_get_args(struct wasm_interp *interp)
{
struct val *argv, *argv_buf;
@@ -167,6 +210,11 @@ static int builtin_get_args(struct wasm_interp *interp)
return 1;
}
+static int builtin_get_args_sizes(struct wasm_interp *interp)
+{
+ return interp_error(interp, "implement get_args_sizes");
+}
+
static INLINE int cursor_pushval(struct cursor *cur, struct val *val)
{
return cursor_push(cur, (u8*)val, sizeof(*val));
@@ -191,13 +239,16 @@ static INLINE int stack_push_i32(struct wasm_interp *interp, int i)
return cursor_push_i32(&interp->stack, i);
}
+/*
static int builtin_get_args_prep(struct wasm_interp *interp)
{
return stack_push_i32(interp, 1) && stack_push_i32(interp, 2);
}
+*/
static struct builtin BUILTINS[] = {
- { .name = "args_get", .fn = builtin_get_args, .prepare_args = builtin_get_args_prep },
+ { .name = "args_get", .fn = builtin_get_args },
+ { .name = "args_sizes_get", .fn = builtin_get_args_sizes },
};
static const int NUM_BUILTINS = sizeof(BUILTINS) / sizeof(*BUILTINS);
@@ -206,11 +257,13 @@ static int parse_instr(struct expr_parser *parser, u8 tag, struct instr *op);
static INLINE int is_valtype(unsigned char byte)
{
- switch ((enum valtype)byte) {
- case val_i32:
- case val_i64:
- case val_f32:
- case val_f64:
+ switch (byte) {
+ case 0x7F: // i32
+ case 0x7E: // i64
+ case 0x7D: // f32
+ case 0x7C: // f64
+ case 0x70: // funcref
+ case 0x6F: // externref
return 1;
}
@@ -363,12 +416,17 @@ static char *instr_name(enum instr_tag tag)
static void print_val(struct val *val)
{
switch (val->type) {
- case val_i32: printf("%d", val->i32); break;
- case val_i64: printf("%llu", val->i64); break;
- case val_f32: printf("%f", val->f32); break;
- case val_f64: printf("%f", val->f64); break;
+ case val_i32: printf("%d:", val->i32); break;
+ case val_i64: printf("%llu:", val->i64); break;
+ case val_f32: printf("%f:", val->f32); break;
+ case val_f64: printf("%f:", val->f64); break;
+
+ case val_ref_null:
+ case val_ref_func:
+ case val_ref_extern:
+ break;
}
- printf(":%s\n", valtype_name(val->type));
+ printf("%s\n", valtype_name(val->type));
}
static INLINE int was_section_parsed(struct module *module,
@@ -1406,7 +1464,13 @@ static int parse_global(struct wasm_parser *p,
struct global *global)
{
struct expr_parser parser;
- make_const_expr_parser(p, &parser);
+ struct cursor stack;
+
+ stack.start = p->mem.p;
+ stack.p = p->mem.p;
+ stack.end = p->mem.end;
+
+ make_const_expr_evaluator(&p->errs, &p->cur, &stack, &parser);
if (!parse_globaltype(p, &global->type)) {
return parse_err(p, "type");
@@ -1416,6 +1480,10 @@ static int parse_global(struct wasm_parser *p,
return parse_err(p, "init code");
}
+ if (!cursor_popval(&stack, &global->val)) {
+ return parse_err(p, "couldn't eval global expr");
+ }
+
return 1;
}
@@ -3193,9 +3261,60 @@ static int interp_global_get(struct wasm_interp *interp)
return 1;
}
-static INLINE int active_pages(struct wasm_interp *interp)
+static INLINE u8 *global_init_state(struct wasm_interp *interp, int ind)
{
- return cursor_count(&interp->memory, WASM_PAGE_SIZE);
+ u8 *p;
+
+ if (unlikely(!(p = index_cursor(&interp->global_init, ind, 1)))) {
+ interp_error(interp, "global ind %d oob", ind);
+ return NULL;
+ }
+
+ return p;
+}
+
+static struct val *get_global(struct wasm_interp *interp, int ind)
+{
+ struct globalsec *globsec;
+ struct global *global;
+ struct val *val;
+ u8 *init;
+
+ if (unlikely(!was_section_parsed(interp->module, section_global))) {
+ interp_error(interp,
+ "can't get global %d, no global section parsed!", ind);
+ return NULL;
+ }
+
+ globsec = &interp->module->global_section;
+
+ if (unlikely(!(val = index_cursor(&interp->globals, ind, sizeof(*val))))) {
+ interp_error(interp,
+ "invalid global index %d (max %d)",
+ ind, globsec->num_globals-1);
+ return NULL;
+ }
+
+ if (unlikely(!(init = global_init_state(interp, ind)))) {
+ interp_error(interp,
+ "couldn't get global init state for global %d", ind);
+ return NULL;
+ }
+
+ /* global is already initialized, return it */
+ if (*init == 1) {
+ return val;
+ }
+
+ /* initialize global then return it */
+ global = &interp->module->global_section.globals[ind];
+
+ /* copy initialized global from module to global instance */
+ memcpy(val, &global->val, sizeof(*val));
+
+ *init = 1;
+
+ return val;
}
static INLINE int has_memory_section(struct module *module)
@@ -3204,6 +3323,109 @@ static INLINE int has_memory_section(struct module *module)
module->memory_section.num_mems > 0;
}
+static INLINE int bitwidth(enum valtype vt)
+{
+ switch (vt) {
+ case val_i32:
+ case val_f32:
+ return 32;
+
+ case val_i64:
+ case val_f64:
+ return 64;
+
+ /* invalid? */
+ case val_ref_null:
+ case val_ref_func:
+ case val_ref_extern:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int interp_store(struct wasm_interp *interp, int N)
+{
+ struct cursor *code;
+ struct val c;
+ struct memarg memarg;
+ int i, offset, bw, n, size;
+
+ if (unlikely(!has_memory_section(interp->module))) {
+ return interp_error(interp, "no memory section");
+ }
+
+ if (unlikely(!(code = interp_codeptr(interp)))) {
+ return interp_error(interp, "codeptr");
+ }
+
+ if (unlikely(!parse_memarg(code, &memarg))) {
+ return interp_error(interp, "memarg");
+ }
+
+ if (unlikely(!stack_pop_number(interp, &c))) {
+ return interp_error(interp, "pop stack");
+ }
+
+ if (unlikely(!stack_pop_i32(interp, &i))) {
+ return interp_error(interp, "pop stack");
+ }
+
+ offset = i + memarg.offset;
+ bw = bitwidth(c.type);
+ n = c.i32;
+
+ if (N == 0) {
+ N = bw;
+ } else {
+ n %= 1 << N;
+ }
+
+ size = N/8;
+
+ assert(interp->memory.p > interp->memory.start);
+ if (interp->memory.start + offset + size > interp->memory.p) {
+ return interp_error(interp,
+ "mem store oob off:%d size:%d mem:%d", offset, size,
+ interp->memory.p - interp->memory.start);
+ }
+
+ memcpy(interp->memory.start + offset, &n, size);
+
+ return 1;
+}
+
+static int interp_global_set(struct wasm_interp *interp)
+{
+ struct val *global, setval;
+ int global_ind;
+ struct cursor *code;
+
+ if (unlikely(!(code = interp_codeptr(interp)))) {
+ return interp_error(interp, "codeptr");
+ }
+
+ if (unlikely(!leb128_read(code, (unsigned int*)&global_ind))) {
+ return interp_error(interp, "read global ind");
+ }
+
+ if (unlikely(!(global = get_global(interp, global_ind)))) {
+ return interp_error(interp, "couldn't get global %d", global_ind);
+ }
+
+ if (unlikely(!stack_popval(interp, &setval))) {
+ return interp_error(interp, "couldn't pop stack value");
+ }
+
+ memcpy(global, &setval, sizeof(setval));
+
+ return 1;
+}
+
+static INLINE int active_pages(struct wasm_interp *interp)
+{
+ return cursor_count(&interp->memory, WASM_PAGE_SIZE);
+}
static int interp_memory_grow(struct wasm_interp *interp)
{
@@ -3280,12 +3502,28 @@ static int interp_instr(struct wasm_interp *interp, u8 tag)
case i_local_set: return interp_local_set(interp);
case i_local_tee: return interp_local_tee(interp);
case i_global_get: return interp_global_get(interp);
+ case i_global_set: return interp_global_set(interp);
case i_i32_eqz: return interp_i32_eqz(interp);
case i_i32_add: return interp_i32_add(interp);
case i_i32_sub: return interp_i32_sub(interp);
case i_i32_const: return interp_i32_const(interp);
case i_i32_gt_u: return interp_i32_gt_u(interp);
case i_i32_lt_s: return interp_i32_lt_s(interp);
+
+ case i_i32_store:
+ case i_f32_store:
+ case i_i64_store32:
+ return interp_store(interp, 32);
+ case i_i64_store:
+ case i_f64_store:
+ return interp_store(interp, 64);
+ case i_i32_store8:
+ case i_i64_store8:
+ return interp_store(interp, 8);
+ case i_i64_store16:
+ case i_i32_store16:
+ return interp_store(interp, 16);
+
case i_if: return interp_if(interp);
case i_end: return pop_label_checkpoint(interp);
case i_call: return interp_call(interp);
@@ -3401,7 +3639,8 @@ int wasm_interp_init(struct wasm_interp *interp, struct module *module)
unsigned int ok, fns, errors_size, stack_size, locals_size, offsets_size,
callframes_size, resolver_size, labels_size, num_labels_size,
labels_capacity, num_labels_elemsize, memsize, memory_pages_size,
- resolver_offsets_size, num_mems;
+ resolver_offsets_size, num_mems, globals_size, num_globals,
+ global_init_size;
memset(interp, 0, sizeof(*interp));
interp->module = module;
@@ -3413,6 +3652,12 @@ int wasm_interp_init(struct wasm_interp *interp, struct module *module)
labels_capacity = fns * MAX_LABELS;
num_labels_elemsize = sizeof(u16);
+ num_mems = was_section_parsed(module, section_memory)?
+ module->memory_section.num_mems : 0;
+
+ num_globals = was_section_parsed(module, section_global)?
+ module->global_section.num_globals : 0;
+
// TODO: make memory limits configurable
errors_size = 0xFFF;
stack_size = sizeof(struct val) * 0xFF;
@@ -3423,9 +3668,8 @@ int wasm_interp_init(struct wasm_interp *interp, struct module *module)
resolver_offsets_size = offsets_size;
callframes_size = sizeof(struct callframe) * 0xFF;
resolver_size = sizeof(struct resolver) * MAX_LABELS;
-
- num_mems = was_section_parsed(interp->module, section_memory)?
- interp->module->memory_section.num_mems : 0;
+ globals_size = sizeof(struct val) * num_globals;
+ global_init_size = num_globals;
if (num_mems > 1) {
printf("more than one memory instance is not supported\n");
@@ -3435,7 +3679,7 @@ int wasm_interp_init(struct wasm_interp *interp, struct module *module)
interp->labels.elem_size = sizeof(struct label);
interp->num_labels.elem_size = num_labels_elemsize;
- memory_pages_size = 0x8000UL * WASM_PAGE_SIZE;
+ memory_pages_size = 256 * WASM_PAGE_SIZE;
memsize =
errors_size +
@@ -3446,6 +3690,8 @@ int wasm_interp_init(struct wasm_interp *interp, struct module *module)
offsets_size +
resolver_offsets_size +
callframes_size +
+ globals_size +
+ global_init_size +
resolver_size;
mem = calloc(1, memsize);
@@ -3464,11 +3710,16 @@ int wasm_interp_init(struct wasm_interp *interp, struct module *module)
cursor_slice(&interp->mem, &interp->callframes, callframes_size) &&
cursor_slice(&interp->mem, &interp->resolver_stack, resolver_size) &&
cursor_slice(&interp->mem, &interp->resolver_stack, resolver_size) &&
+ cursor_slice(&interp->mem, &interp->globals, globals_size) &&
+ cursor_slice(&interp->mem, &interp->global_init, global_init_size) &&
array_alloc(&interp->mem, &interp->labels, labels_capacity) &&
array_alloc(&interp->mem, &interp->num_labels, fns);
+ /* init memory pages */
+ assert((interp->mem.end - interp->mem.start) == memsize);
+
if (!ok) {
- return interp_error(interp, "not enough memory 1");
+ return interp_error(interp, "not enough memory");
}
return 1;
@@ -3485,6 +3736,28 @@ void wasm_interp_free(struct wasm_interp *interp)
free(interp->memory.start);
}
+static int reset_memory(struct wasm_interp *interp)
+{
+ int pages, num_mems;
+
+ num_mems = was_section_parsed(interp->module, section_memory)?
+ interp->module->memory_section.num_mems : 0;
+
+ reset_cursor(&interp->memory);
+
+ if (num_mems == 1) {
+ pages = interp->module->memory_section.mems[0].min;
+ if (!cursor_malloc(&interp->memory, pages * WASM_PAGE_SIZE)) {
+ return interp_error(interp,
+ "could not alloc %d memory pages",
+ pages);
+ }
+ assert(interp->memory.p > interp->memory.start);
+ }
+
+ return 1;
+}
+
int interp_wasm_module(struct wasm_interp *interp)
{
int func;
@@ -3502,7 +3775,12 @@ int interp_wasm_module(struct wasm_interp *interp)
reset_cursor(&interp->resolver_offsets);
reset_cursor(&interp->errors.cur);
reset_cursor(&interp->callframes);
- reset_cursor(&interp->memory);
+
+ if (!reset_memory(interp))
+ return interp_error(interp, "reset memory");
+
+ wipe_cursor(&interp->globals);
+ wipe_cursor(&interp->global_init);
// don't reset labels for perf!
diff --git a/src/wasm.h b/src/wasm.h
@@ -15,10 +15,10 @@ static const unsigned char WASM_MAGIC[] = {0,'a','s','m'};
#include "error.h"
enum valtype {
- val_i32 = 0x7F,
- val_i64 = 0x7E,
- val_f32 = 0x7D,
- val_f64 = 0x7C,
+ val_i32,
+ val_i64,
+ val_f32,
+ val_f64,
val_ref_null,
val_ref_func,
val_ref_extern,
@@ -192,7 +192,6 @@ struct global {
struct globaltype type;
struct expr init;
struct val val;
- u8 evaluated;
};
struct local {
@@ -501,6 +500,9 @@ struct wasm_interp {
struct cursor stack; /* struct val */
struct cursor mem; /* u8/mixed */
struct cursor resolver_offsets; /* int */
+ struct cursor globals; /* struct val */
+ struct cursor global_init; /* u8 */
+
struct cursor memory; /* memory pages (65536 blocks) */
struct array labels; /* struct labels */