commit 20597cfec9018cf96532cd78970e947657d8f3e5
parent a7377ab3bfd623653d148f8cb8a623b221329c7c
Author: William Casarin <jb55@jb55.com>
Date: Sat, 28 Nov 2020 12:43:14 -0800
Initial interpreter, add function working
Signed-off-by: William Casarin <jb55@jb55.com>
Diffstat:
4 files changed, 295 insertions(+), 18 deletions(-)
diff --git a/Makefile b/Makefile
@@ -20,7 +20,7 @@ all: protoverse libprotoverse.a
@$(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $<
%.wasm: %.wat
- wat2wasm $^
+ wat2wasm $^ -o $@
protoverse: src/protoverse.c $(OBJS)
@echo "ld $@"
diff --git a/src/wasm.c b/src/wasm.c
@@ -7,8 +7,10 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
+#include <stdint.h>
#define note_error(p, fmt, ...) note_error_(p, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
+#define interp_error(p, fmt, ...) interp_error_(p, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
#define ERR_STACK_SIZE 16
@@ -18,6 +20,16 @@ struct parse_error {
struct parse_error *next;
};
+struct val {
+ enum valtype type;
+ union {
+ int i32;
+ int64_t i64;
+ float f32;
+ double f64;
+ };
+};
+
struct wasm_parser {
struct module module;
struct cursor cur;
@@ -25,6 +37,17 @@ struct wasm_parser {
struct parse_error *errors;
};
+struct wasm_interp {
+ struct module *module;
+ struct cursor cur;
+ struct cursor stack;
+ struct cursor mem;
+ struct parser_error *errors;
+
+ struct val *params;
+ int num_params;
+};
+
#ifdef DEBUG
static void log_dbg_(const char *fmt, ...)
{
@@ -39,6 +62,87 @@ static void log_dbg_(const char *fmt, ...)
#define log_dbg(...)
#endif
+static int is_valtype(unsigned char byte)
+{
+ switch ((enum valtype)byte) {
+ case i32:
+ case i64:
+ case f32:
+ case f64:
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int sizeof_valtype(enum valtype valtype)
+{
+ switch (valtype) {
+ case i32: return 4;
+ case f32: return 4;
+ case i64: return 8;
+ case f64: return 8;
+ }
+
+ return 0;
+}
+
+static int stack_pushval(struct cursor *cur, struct val *val)
+{
+ if (!push_data(cur, (unsigned char*)&val->i32, sizeof_valtype(val->type)))
+ return 0;
+ return push_byte(cur, (unsigned char)val->type);
+}
+
+static int cursor_popbyte(struct cursor *cur, unsigned char *byte)
+{
+ if (cur->p - 1 < cur->start)
+ return 0;
+
+ cur->p--;
+ *byte = *cur->p;
+
+ return 1;
+}
+
+static int cursor_popdata(struct cursor *cur, unsigned char *dest, int len)
+{
+ if (cur->p - len < cur->start)
+ return 0;
+
+ cur->p = cur->p - len;
+
+ memcpy(dest, cur->p, len);
+
+ return 1;
+}
+
+static int stack_popval(struct cursor *cur, struct val *val)
+{
+ unsigned char byte;
+
+ if (!cursor_popbyte(cur, &byte))
+ return 0;
+
+ if (!is_valtype(byte))
+ return 0;
+
+ val->type = (enum valtype)byte;
+
+ return cursor_popdata(cur, (unsigned char*)&val->i32, sizeof_valtype(val->type));
+}
+
+static void interp_error_(struct wasm_interp *p, const char *fmt, ...)
+{
+ va_list ap;
+
+ (void)p;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
static void note_error_(struct wasm_parser *p, const char *fmt, ...)
{
@@ -167,9 +271,20 @@ static void print_export_section(struct exportsec *exportsec)
}
}
+static void print_local(struct local *local)
+{
+ printf("%d %s\n", local->n, valtype_name(local->valtype));
+}
+
static void print_func(struct func *func)
{
/* todo: print locals */
+ int i;
+
+ printf("func locals (%d): \n", func->num_locals);
+ for (i = 0; i < func->num_locals; i++) {
+ print_local(&func->locals[i]);
+ }
printf("%d bytes of code\n", func->code_len);
}
@@ -190,19 +305,6 @@ static void print_module(struct module *module)
print_code_section(&module->code_section);
}
-static int is_valtype(unsigned char byte)
-{
- switch ((enum valtype)byte) {
- case i32:
- case i64:
- case f32:
- case f64:
- return 1;
- }
-
- return 0;
-}
-
/* I DONT NEED THIS (yet?) */
/*
@@ -295,7 +397,7 @@ static int parse_valtype(struct wasm_parser *p, enum valtype *valtype)
return 0;
}
- if (is_valtype(*valtype))
+ if (is_valtype((unsigned char)*valtype))
return 1;
p->cur.p = start;
@@ -471,9 +573,11 @@ static int parse_func(struct wasm_parser *p, struct func *func)
}
}
+ func->locals = locals;
+ func->num_locals = elems;
func->code_len = size - (p->cur.p - start);
- if (!pull_data_into_cursor(&p->cur, &p->mem, &func->code,
+ if (!pull_data_into_cursor(&p->cur, &p->mem, &func->code,
func->code_len)) {
note_error(p, "code oom");
return 0;
@@ -764,6 +868,154 @@ fail:
return 0;
}
+static int interp_i32_add(struct wasm_interp *interp)
+{
+ struct val a;
+ struct val b;
+ struct val c;
+
+ if (!stack_popval(&interp->stack, &a)) {
+ interp_error(interp, "pop first");
+ return 0;
+ }
+
+ if (!stack_popval(&interp->stack, &b)) {
+ interp_error(interp, "pop second");
+ return 0;
+ }
+
+ if (a.type != i32 || b.type != i32) {
+ interp_error(interp, "i32_add type mismatch");
+ return 0;
+ }
+
+ c.type = i32;
+ c.i32 = a.i32 + b.i32;
+
+ return stack_pushval(&interp->stack, &c);
+}
+
+static int interp_local_get(struct wasm_interp *interp)
+{
+ unsigned int index;
+
+ if (!leb128_read(&interp->cur, &index)) {
+ interp_error(interp, "index");
+ return 0;
+ }
+
+ if (index+1 > (unsigned int)interp->num_params) {
+ interp_error(interp, "invalid index");
+ return 0;
+ }
+
+ return stack_pushval(&interp->stack, &interp->params[index]);
+}
+
+static int interp_instr(struct wasm_interp *interp, unsigned char tag)
+{
+ switch (tag) {
+ case i_unreachable: return 1;
+ case i_nop: return 1;
+ case i_local_get: return interp_local_get(interp);
+ case i_i32_add: return interp_i32_add(interp);
+ default:
+ interp_error(interp, "unhandled instruction %x", tag);
+ return 0;
+ }
+
+ return 0;
+}
+
+static int interp_code(struct wasm_interp *interp)
+{
+ unsigned char tag;
+
+ for (;;) {
+ if (!pull_byte(&interp->cur, &tag)) {
+ interp_error(interp, "instr tag");
+ return 0;
+ }
+
+ if (tag == i_end)
+ break;
+
+ if (!interp_instr(interp, tag)) {
+ interp_error(interp, "interp instr");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+#define STACK_SPACE 5242880
+#define MEM_SPACE 5242880
+
+static void print_stack(struct cursor *stack)
+{
+ struct val val;
+ int i;
+
+ i = 0;
+
+ for (i = 0; stack->p > stack->start; i++) {
+ stack_popval(stack, &val);
+ printf("[%d] ", i);
+ switch (val.type) {
+ case i32: printf("%d", val.i32); break;
+ case i64: printf("%ld", val.i64); break;
+ case f32: printf("%f", val.f32); break;
+ case f64: printf("%f", val.f64); break;
+ }
+ printf(":%s\n", valtype_name(val.type));
+ }
+}
+
+static int interp_module(struct module *module)
+{
+ int ok;
+ struct func *func;
+ struct wasm_interp interp;
+ static unsigned char *stack, *mem;
+
+ stack = malloc(STACK_SPACE);
+ mem = malloc(STACK_SPACE);
+
+ if (module->code_section.num_funcs == 0) {
+ printf("empty module\n");
+ return 0;
+ }
+ func = &module->code_section.funcs[0];
+
+ make_cursor(func->code, func->code + func->code_len, &interp.cur);
+ make_cursor(stack, stack + STACK_SPACE, &interp.stack);
+ make_cursor(mem, mem + MEM_SPACE, &interp.mem);
+
+ interp.params = cursor_alloc(&interp.mem, sizeof(struct val) * 32);
+
+ /* should be done when calling function? */
+ interp.params[0].type = i32;
+ interp.params[0].i32 = 1;
+
+ interp.params[1].type = i32;
+ interp.params[1].i32 = 2;
+
+ interp.num_params = 2;
+
+ ok = interp_code(&interp);
+
+ if (ok) {
+ printf("interp success!!\n");
+ }
+ printf("stack:\n");
+ print_stack(&interp.stack);
+
+ free(stack);
+ free(mem);
+ return ok;
+}
+
int run_wasm(unsigned char *wasm, unsigned long len)
{
struct wasm_parser p;
@@ -779,7 +1031,16 @@ int run_wasm(unsigned char *wasm, unsigned long len)
make_cursor(wasm, wasm + len, &p.cur);
make_cursor(mem, mem + arena_size, &p.mem);
- ok = parse_wasm(&p);
+ if (!parse_wasm(&p)) {
+ free(mem);
+ return 0;
+ }
+
+ if (!interp_module(&p.module)) {
+ free(mem);
+ return 0;
+ }
+
free(mem);
return ok;
}
diff --git a/src/wasm.h b/src/wasm.h
@@ -208,7 +208,10 @@ enum instr_tag {
i_f64_ge = 0x66,
i_i32_clz = 0x67,
+
+ i_i32_add = 0x6A,
/* TODO: more instrs */
+
};
enum blocktype_tag {
diff --git a/wasm/hello.wat b/wasm/hello.wat
@@ -1 +1,14 @@
-(module)
+(module
+ (func $add (param $lhs i32) (param $rhs i32) (result i32)
+ local.get $lhs
+ local.get $rhs
+ i32.add)
+ (func $start (result i32)
+ (local i32 i32)
+ i32.const 1
+ local.set 0
+ local.get 0
+ i32.const 2
+ call $add)
+ (export "start" (func $start))
+ (export "add" (func $add)))