commit 2f24c0cf853b01eb060a2fbb9e9c7ff79925df28
parent f14601d31e05d8df37e5e5c1e0740967f33b941e
Author: William Casarin <jb55@jb55.com>
Date: Sun, 4 Jul 2021 15:56:11 -0700
wasm code execution, calling functions, etc
Diffstat:
9 files changed, 443 insertions(+), 108 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,5 +1,5 @@
-CFLAGS = -Wno-error=unused-function -Ofast -std=gnu90 -Wall -Wextra -Werror \
+CFLAGS = -Wno-error=unused-function -O1 -g -std=gnu90 -Wall -Wextra -Werror \
-Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes \
-Wmissing-declarations -Wdeclaration-after-statement
@@ -29,7 +29,7 @@ wasm: $(WASMS)
wat2wasm $^ -o $@
wasm/hello-c.wasm: wasm/hello-c.c
- emcc $< -s WASM=1 -o $@
+ emcc -g $< -s WASM=1 -o $@
protoverse: src/protoverse.c $(OBJS)
@echo "ld $@"
diff --git a/src/cursor.h b/src/cursor.h
@@ -132,6 +132,11 @@ static inline int push_int(struct cursor *cursor, int i)
return push_data(cursor, (u8*)&i, sizeof(i));
}
+static inline size_t cursor_count(struct cursor *cursor, size_t elem_size)
+{
+ return (cursor->p - cursor->start)/elem_size;
+}
+
/* TODO: push_varint */
static inline int push_varint(struct cursor *cursor, int n)
{
@@ -192,7 +197,7 @@ static inline int push_u16(struct cursor *cursor, u16 i)
return push_data(cursor, (u8*)&i, sizeof(i));
}
-static inline void *index_cursor(struct cursor *cursor, u16 index, int elem_size)
+static inline void *index_cursor(struct cursor *cursor, unsigned int index, int elem_size)
{
u8 *p;
p = &cursor->start[elem_size * index];
@@ -254,4 +259,34 @@ static inline int cursor_remaining_capacity(struct cursor *cursor)
return cursor->end - cursor->p;
}
+
+#define max(a,b) ((a) > (b) ? (a) : (b))
+static inline void cursor_print_around(struct cursor *cur, int range)
+{
+ unsigned char *c;
+
+ c = max(cur->p - range, cur->start);
+ for (; c < cur->end && c < (cur->p + range); c++) {
+ if (*c < 32)
+ printf("%02x", *c);
+ else
+ printf("%c", *c);
+ }
+ printf("\n");
+
+ c = max(cur->p - range, cur->start);
+ for (; c < cur->end && c < (cur->p + range); c++) {
+ if (c == cur->p) {
+ printf("^");
+ continue;
+ }
+ if (*c < 32)
+ printf(" ");
+ else
+ printf(" ");
+ }
+ printf("\n");
+}
+#undef max
+
#endif
diff --git a/src/entity.h b/src/entity.h
@@ -18,4 +18,16 @@ struct entity {
double pos[3];
};
+static inline const char *entity_name(struct env *env, entity_id *id)
+{
+ struct entity *ent;
+
+ if (!(ent = get_resource(&env->entities, id))) {
+ return "unknown";
+ }
+
+ return ent->name;
+}
+
+
#endif /* PROTOVERSE_ENTITY_H */
diff --git a/src/net.c b/src/net.c
@@ -280,17 +280,6 @@ int packet_eq(struct packet *a, struct packet *b)
return 0;
}
-static const char *entity_name(struct env *env, entity_id *id)
-{
- struct entity *ent;
-
- if (!(ent = get_resource(&env->entities, id))) {
- return "unknown";
- }
-
- return ent->name;
-}
-
static void print_message_packet(struct env *env, struct message_packet *msg)
{
/* eventually print entity data from environment */
diff --git a/src/protoverse.c b/src/protoverse.c
@@ -5,12 +5,15 @@
#include "serve.h"
#include "client.h"
#include "wasm.h"
+#include "resource.h"
+#include "entity.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
+#define MAX_ENTITIES 1048576
#define streq(a, b) strcmp(a,b) == 0
/*
@@ -68,6 +71,17 @@ static int print_cell_tree(struct parser *parser, u16 root, int depth)
return 1;
}
+static void init_protoverse_server(struct protoverse_server *server)
+{
+ init_resource_manager(&server->env.entities, sizeof(struct entity),
+ 1024, MAX_ENTITIES, "entity");
+}
+
+static void free_protoverse_server(struct protoverse_server *server)
+{
+ destroy_resource_manager(&server->env.entities);
+}
+
static int usage(void)
{
printf("usage: protoverse <command> [args]\n\n");
@@ -120,7 +134,9 @@ int main(int argc, const char *argv[])
server.port = 1988;
server.bind = "127.0.0.1";
+ init_protoverse_server(&server);
protoverse_serve(&server);
+ free_protoverse_server(&server);
} else if (streq(cmd, "client")) {
protoverse_connect("127.0.0.1", 1988);
} else if (streq(cmd, "run")) {
diff --git a/src/serve.c b/src/serve.c
@@ -15,7 +15,6 @@
#include "io.h"
#define MAX_CACHED_FILES 12
-#define MAX_ENTITIES 1048576
int inet_aton(const char *cp, struct in_addr *inp);
@@ -91,16 +90,6 @@ static int handle_packet(int sockfd,
return 0;
}
-static void init_protoverse_server(struct protoverse_server *server)
-{
- init_resource_manager(&server->env.entities, sizeof(struct entity),
- 1024, MAX_ENTITIES, "entity");
-}
-
-static void free_protoverse_server(struct protoverse_server *server)
-{
- destroy_resource_manager(&server->env.entities);
-}
int protoverse_serve(struct protoverse_server *server)
{
@@ -119,9 +108,6 @@ int protoverse_serve(struct protoverse_server *server)
buf_ = malloc(FILEBUF_SIZE);
make_cursor(buf_, buf_ + FILEBUF_SIZE, &buf);
- /* initialize object storage, etc */
- init_protoverse_server(server);
-
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
printf("socket creation failed: %s\n", strerror(errno));
return 0;
@@ -160,5 +146,4 @@ int protoverse_serve(struct protoverse_server *server)
}
free(buf_);
- free_protoverse_server(server);
}
diff --git a/src/wasm.c b/src/wasm.c
@@ -13,6 +13,7 @@
#define interp_error(p, fmt, ...) interp_error_(p, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
#define ERR_STACK_SIZE 16
+#define NUM_LOCALS 0xFFFF
struct parse_error {
int pos;
@@ -39,13 +40,15 @@ struct wasm_parser {
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;
+ struct cursor cur; /* code */
+ struct cursor code_stack; /* struct cursor */
+ struct cursor stack; /* struct val */
+ struct cursor mem; /* u8/mixed */
+ struct cursor locals; /* struct val */
+ struct cursor locals_offsets; /* int */
+
+ struct parser_error *errors;
};
#ifdef DEBUG
@@ -88,11 +91,40 @@ static int sizeof_valtype(enum valtype valtype)
return 0;
}
-static int stack_pushval(struct cursor *cur, struct val *val)
+static const char *valtype_name(enum valtype valtype)
{
- if (!push_data(cur, (unsigned char*)&val->i32, sizeof_valtype(val->type)))
+ switch (valtype) {
+ case i32: return "i32";
+ case i64: return "i64";
+ case f32: return "f32";
+ case f64: return "f64";
+ }
+
+ return "unk";
+}
+
+static void print_val(struct val *val)
+{
+ 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 inline int cursor_popdata(struct cursor *cur, unsigned char *dest, int len)
+{
+ if (cur->p - len < cur->start)
return 0;
- return push_byte(cur, (unsigned char)val->type);
+
+ cur->p -= len;
+
+ if (dest)
+ memcpy(dest, cur->p, len);
+
+ return 1;
}
static inline int cursor_popbyte(struct cursor *cur, unsigned char *byte)
@@ -106,31 +138,55 @@ static inline int cursor_popbyte(struct cursor *cur, unsigned char *byte)
return 1;
}
-static inline int cursor_popdata(struct cursor *cur, unsigned char *dest, int len)
+static inline int cursor_popval(struct cursor *cur, struct val *val)
{
- if (cur->p - len < cur->start)
- return 0;
+ return cursor_popdata(cur, (unsigned char*)val, sizeof(*val));
+}
- cur->p = cur->p - len;
+static void print_stack(struct cursor *stack)
+{
+ struct val val;
+ int i;
+ u8 *p = stack->p;
- memcpy(dest, cur->p, len);
+ for (i = 0; stack->p > stack->start; i++) {
+ cursor_popval(stack, &val);
+ printf("[%d] ", i);
+ print_val(&val);
+ }
- return 1;
+ stack->p = p;
}
-static int stack_popval(struct cursor *cur, struct val *val)
+static inline int cursor_pushval(struct cursor *cur, struct val *val)
{
- unsigned char byte;
+ return push_data(cur, (unsigned char*)val, sizeof(*val));
+}
- if (!cursor_popbyte(cur, &byte))
- return 0;
+static inline int cursor_pushcode(struct cursor *cur, struct cursor *code)
+{
+ return push_data(cur, (unsigned char*)code, sizeof(*code));
+}
- if (!is_valtype(byte))
- return 0;
+static inline int cursor_popcode(struct cursor *cur, struct cursor *code)
+{
+ return cursor_popdata(cur, (unsigned char*)code, sizeof(*code));
+}
- val->type = (enum valtype)byte;
+static inline int cursor_popint(struct cursor *cur, int *i)
+{
+ return cursor_popdata(cur, (unsigned char *)i, sizeof(int));
+}
+
+static inline int offset_stack_top(struct cursor *cur)
+{
+ int *p = (int*)cur->p;
- return cursor_popdata(cur, (unsigned char*)&val->i32, sizeof_valtype(val->type));
+ if (cur->p == cur->start) {
+ return 0;
+ }
+
+ return p[-1];
}
static void interp_error_(struct wasm_interp *p, const char *fmt, ...)
@@ -201,18 +257,6 @@ static void print_parse_backtrace(struct wasm_parser *p)
}
}
-static const char *valtype_name(enum valtype valtype)
-{
- switch (valtype) {
- case i32: return "i32";
- case i64: return "i64";
- case f32: return "f32";
- case f64: return "f64";
- }
-
- return "unk";
-}
-
static void print_functype(struct functype *ft)
{
int i;
@@ -281,8 +325,9 @@ static void print_export_section(struct exportsec *exportsec)
printf("%d exports:\n", exportsec->num_exports);
for (i = 0; i < exportsec->num_exports; i++) {
printf(" ");
- printf("%s %s\n", exportdesc_name(exportsec->exports[i].desc),
- exportsec->exports[i].name);
+ printf("%s %s %d\n", exportdesc_name(exportsec->exports[i].desc),
+ exportsec->exports[i].name,
+ exportsec->exports[i].index);
}
}
@@ -1031,13 +1076,13 @@ static int interp_i32_add(struct wasm_interp *interp)
struct val b;
struct val c;
- if (!stack_popval(&interp->stack, &a)) {
- interp_error(interp, "pop first");
+ if (!cursor_popval(&interp->stack, &a)) {
+ interp_error(interp, "couldn't pop first val");
return 0;
}
- if (!stack_popval(&interp->stack, &b)) {
- interp_error(interp, "pop second");
+ if (!cursor_popval(&interp->stack, &b)) {
+ interp_error(interp, "couldn't pop second val");
return 0;
}
@@ -1049,24 +1094,242 @@ static int interp_i32_add(struct wasm_interp *interp)
c.type = i32;
c.i32 = a.i32 + b.i32;
- return stack_pushval(&interp->stack, &c);
+ return cursor_pushval(&interp->stack, &c);
+}
+
+static inline struct val *get_local(struct wasm_interp *interp, int ind)
+{
+ struct val *p;
+ int offset = offset_stack_top(&interp->locals_offsets);
+
+ if (!(p = index_cursor(&interp->locals, offset + ind, sizeof(struct val)))) {
+ interp_error(interp, "%d local oob %d > %ld", ind, offset + ind,
+ interp->locals.end - interp->locals.start);
+ return NULL;
+ }
+ return p;
+}
+
+static inline int count_locals(struct wasm_interp *interp)
+{
+ int offset = offset_stack_top(&interp->locals_offsets);
+ int count = cursor_count(&interp->locals, sizeof(struct val));
+ return count - offset;
+}
+
+static int set_local(struct wasm_interp *interp, int ind, struct val *val)
+{
+ struct val *local;
+ int nlocals;
+
+ nlocals = count_locals(interp);
+
+ if (ind > nlocals) {
+ /* TODO: if we hit this then we need to push empty locals up to the index */
+ interp_error(interp, "local index out of order");
+ return 0;
+ }
+
+ if (ind < nlocals) {
+ printf("memsetting local %d\n", ind);
+ if (!(local = get_local(interp, ind))) {
+ return 0;
+ }
+ memcpy(local, val, sizeof(*val));
+ return 1;
+ }
+
+ printf("pushing local %d\n", ind);
+ cursor_pushval(&interp->locals, val);
+ assert(count_locals(interp) > 0);
+ return 1;
+}
+
+static int interp_local_set(struct wasm_interp *interp)
+{
+ struct val val;
+ unsigned int index;
+
+ if (!cursor_popval(&interp->stack, &val)) {
+ interp_error(interp, "pop");
+ return 0;
+ }
+
+ if (!leb128_read(&interp->cur, &index)) {
+ interp_error(interp, "read index");
+ return 0;
+ }
+
+ if (!set_local(interp, index, &val)) {
+ interp_error(interp, "set local");
+ return 0;
+ }
+
+ return 1;
}
static int interp_local_get(struct wasm_interp *interp)
{
unsigned int index;
+ unsigned int nlocals;
+ struct val *val;
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");
+ nlocals = count_locals(interp);
+ if (index >= nlocals) {
+ interp_error(interp, "local %d not set (%d locals)", index,
+ nlocals);
return 0;
}
- return stack_pushval(&interp->stack, &interp->params[index]);
+ if (!(val = get_local(interp, index))) {
+ interp_error(interp, "get local");
+ return 0;
+ }
+
+ return cursor_pushval(&interp->stack, val);
+}
+
+static inline void make_i32_val(struct val *val, int v)
+{
+ val->type = i32;
+ val->i32 = v;
+}
+
+static inline int interp_i32_const(struct wasm_interp *interp)
+{
+ struct val val;
+ unsigned int read;
+
+ if (!leb128_read(&interp->cur, &read)) {
+ interp_error(interp, "invalid constant value");
+ return 0;
+ }
+
+ make_i32_val(&val, read);
+
+ return cursor_pushval(&interp->stack, &val);
+}
+
+static inline struct func *get_function(struct module *module, int ind)
+{
+ if (ind >= module->code_section.num_funcs) {
+ return NULL;
+ }
+
+ return &module->code_section.funcs[ind];
+}
+
+static inline struct functype *get_function_type(struct module *module, int ind)
+{
+ if (ind >= module->type_section.num_functypes) {
+ return NULL;
+ }
+
+ return &module->type_section.functypes[ind];
+}
+
+static const char *get_function_name(struct module *module, unsigned int func_index)
+{
+ struct wexport *export;
+ int i;
+
+ for (i = 0; i < module->export_section.num_exports; i++) {
+ export = &module->export_section.exports[i];
+ if (export->index == func_index) {
+ return export->name;
+ }
+ }
+
+ return "unknown";
+}
+
+static int prepare_call(struct wasm_interp *interp, int func_index)
+{
+ int i;
+ struct functype *functype;
+ struct func *func;
+ struct val val;
+ enum valtype paramtype;
+ unsigned int offset;
+
+ if (!(func = get_function(interp->module, func_index))) {
+ interp_error(interp, "function %d oob/not found (%d funcs)",
+ func_index,
+ interp->module->code_section.num_funcs);
+ return 0;
+ }
+
+ /* record locals offset for indexing locals in the next function */
+ offset = cursor_count(&interp->locals, sizeof(struct val));
+ if (!push_int(&interp->locals_offsets, offset)) {
+ interp_error(interp, "push locals offset");
+ return 0;
+ }
+
+ /* get type signature to know how many locals to push as params */
+ if (!(functype = get_function_type(interp->module, func_index))) {
+ interp_error(interp, "get function type");
+ return 0;
+ }
+
+
+ /* push params as locals */
+ for (i = 0; i < functype->params.num_valtypes; i++) {
+ paramtype = (enum valtype)functype->params.valtypes[i];
+
+ if (!cursor_popval(&interp->stack, &val)) {
+ interp_error(interp, "not enough arguments for call");
+ return 0;
+ }
+
+ if (val.type != paramtype) {
+ interp_error(interp,
+ "call parameter %d type mismatch. got %s, expected %s",
+ i+1,
+ valtype_name(val.type),
+ valtype_name(paramtype));
+ return 0;
+ }
+
+ if (!cursor_pushval(&interp->locals, &val)) {
+ interp_error(interp, "push param local");
+ return 0;
+ }
+ }
+
+ /* update current function and push it to the codestack as well */
+ make_cursor(func->code, func->code + func->code_len, &interp->cur);
+ if (!cursor_pushcode(&interp->code_stack, &interp->cur)) {
+ interp_error(interp, "oob cursor_pushcode");
+ return 0;
+ }
+
+ return 1;
+}
+
+int interp_code(struct wasm_interp *interp);
+
+static int interp_call(struct wasm_interp *interp)
+{
+ unsigned int func_index;
+
+ if (!leb128_read(&interp->cur, &func_index)) {
+ interp_error(interp, "read func index");
+ return 0;
+ }
+
+ if (!prepare_call(interp, func_index)) {
+ interp_error(interp, "prepare");
+ return 0;
+ }
+
+ /* call the function! */
+ return interp_code(interp);
}
static int interp_instr(struct wasm_interp *interp, unsigned char tag)
@@ -1075,27 +1338,37 @@ static int interp_instr(struct wasm_interp *interp, unsigned char tag)
case i_unreachable: return 1;
case i_nop: return 1;
case i_local_get: return interp_local_get(interp);
+ case i_local_set: return interp_local_set(interp);
case i_i32_add: return interp_i32_add(interp);
+ case i_i32_const: return interp_i32_const(interp);
+ case i_call: return interp_call(interp);
default:
- interp_error(interp, "unhandled instruction %x", tag);
+ interp_error(interp, "unhandled instruction 0x%x", tag);
return 0;
}
return 0;
}
-static int interp_code(struct wasm_interp *interp)
+int interp_code(struct wasm_interp *interp)
{
+ struct cursor code;
unsigned char tag;
+ int offset;
for (;;) {
if (!pull_byte(&interp->cur, &tag)) {
+ cursor_print_around(&interp->cur, 10);
interp_error(interp, "instr tag");
return 0;
}
- if (tag == i_end)
+ if (tag == i_end) {
+ cursor_popcode(&interp->code_stack, &code);
+ cursor_popint(&interp->locals_offsets, &offset);
+ copy_cursor(&code, &interp->cur);
break;
+ }
if (!interp_instr(interp, tag)) {
interp_error(interp, "interp instr");
@@ -1108,63 +1381,81 @@ static int interp_code(struct wasm_interp *interp)
#define STACK_SPACE 5242880
#define MEM_SPACE 5242880
+#define LOCALS_SPACE 5242880
-static void print_stack(struct cursor *stack)
+static int find_function(struct module *module, const char *name)
{
- struct val val;
+ struct wexport *export;
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;
+ for (i = 0; i < module->export_section.num_exports; i++) {
+ export = &module->export_section.exports[i];
+ if (!strcmp(name, export->name)) {
+ return export->index;
}
- printf(":%s\n", valtype_name(val.type));
}
+
+ return -1;
+}
+
+static int cursor_slice(struct cursor *mem, struct cursor *slice, size_t size)
+{
+ u8 *p;
+ if (!(p = cursor_alloc(mem, size))) {
+ return 0;
+ }
+ make_cursor(p, mem->p, slice);
+ return 1;
}
static int interp_module(struct module *module)
{
- int ok;
- struct func *func;
+ int ok, func;
struct wasm_interp interp;
static unsigned char *stack, *mem;
+ interp.module = module;
+
stack = malloc(STACK_SPACE);
- mem = malloc(STACK_SPACE);
+ mem = malloc(MEM_SPACE);
if (module->code_section.num_funcs == 0) {
- printf("empty module\n");
+ interp_error(&interp, "empty module");
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);
+ if (!cursor_slice(&interp.mem, &interp.locals, sizeof(struct val) * NUM_LOCALS)) {
+ interp_error(&interp, "not enough memory for locals");
+ return 0;
+ }
- /* should be done when calling function? */
- interp.params[0].type = i32;
- interp.params[0].i32 = 1;
+ if (!(cursor_slice(&interp.mem, &interp.locals_offsets, sizeof(int) * 255))) {
+ interp_error(&interp, "not enough memory for local offsets");
+ return 0;
+ }
- interp.params[1].type = i32;
- interp.params[1].i32 = 2;
+ if (!(cursor_slice(&interp.mem, &interp.code_stack, sizeof(struct cursor) * 255))) {
+ interp_error(&interp, "not enough memory for code stack");
+ return 0;
+ }
- interp.num_params = 2;
+ func = find_function(module, "start");
+ if (func == -1) {
+ interp_error(&interp, "no start function found");
+ return 0;
+ }
- ok = interp_code(&interp);
+ if (!prepare_call(&interp, func)) {
+ interp_error(&interp, "preparing start function");
+ return 0;
+ }
- if (ok) {
+ if (interp_code(&interp)) {
printf("interp success!!\n");
}
+
printf("stack:\n");
print_stack(&interp.stack);
diff --git a/wasm/hello-c.c b/wasm/hello-c.c
@@ -1,7 +1,14 @@
#include <stdio.h>
+int add(int a, int b, int c, int d)
+{
+ return a + b + c + d;
+}
+
int main(int argc, const char *argv[])
{
- printf("Hello, world?\n");
+ printf("Hello, world? %d\n", add(1,2,3,4));
return 0;
}
+
+
diff --git a/wasm/hello.wat b/wasm/hello.wat
@@ -5,7 +5,7 @@
i32.add)
(func $start (result i32)
(local i32 i32)
- i32.const 1
+ i32.const 65537
local.set 0
local.get 0
i32.const 2