commit 054108ff4b21710b8e569d31cbd229da801c755a
parent 8e4b7993be5ab2c798139e9113a654d86c608f39
Author: William Casarin <jb55@jb55.com>
Date: Sun, 8 Aug 2021 21:40:05 -0700
init bulk memory operations
Diffstat:
M | Makefile | | | 1 | - |
D | src/parser.c | | | 33 | --------------------------------- |
M | src/parser.h | | | 36 | +++++++++++++++++++++++++++++++++--- |
M | src/wasm.c | | | 190 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- |
M | src/wasm.h | | | 32 | +++++++++++++++++++++++++++++--- |
5 files changed, 240 insertions(+), 52 deletions(-)
diff --git a/Makefile b/Makefile
@@ -13,7 +13,6 @@ OBJS = src/io.o \
src/client.o \
src/net.o \
src/varint.o \
- src/parser.o \
src/error.o \
src/wasm.o
diff --git a/src/parser.c b/src/parser.c
@@ -1,33 +0,0 @@
-
-#include "parser.h"
-#include <stdio.h>
-
-
-int consume_bytes(struct cursor *cursor, const unsigned char *match, int len)
-{
- int i;
-
- if (cursor->p + len > cursor->end) {
- fprintf(stderr, "consume_bytes overflow\n");
- return 0;
- }
-
- for (i = 0; i < len; i++) {
- if (cursor->p[i] != match[i])
- return 0;
- }
-
- cursor->p += len;
-
- return 1;
-}
-
-int consume_byte(struct cursor *cursor, unsigned char match)
-{
- return consume_bytes(cursor, &match, 1);
-}
-
-int consume_u32(struct cursor *cursor, unsigned int match)
-{
- return consume_bytes(cursor, (unsigned char*)&match, sizeof(match));
-}
diff --git a/src/parser.h b/src/parser.h
@@ -4,9 +4,39 @@
#include "cursor.h"
-int consume_bytes(struct cursor *cur, const unsigned char *match, int len);
-int consume_byte(struct cursor *cur, const unsigned char match);
-int consume_u32(struct cursor *cur, unsigned int match);
+static int consume_bytes(struct cursor *cursor, const unsigned char *match, int len)
+{
+ int i;
+
+ if (cursor->p + len > cursor->end) {
+ fprintf(stderr, "consume_bytes overflow\n");
+ return 0;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (cursor->p[i] != match[i])
+ return 0;
+ }
+
+ cursor->p += len;
+
+ return 1;
+}
+
+static inline int consume_byte(struct cursor *cursor, unsigned char match)
+{
+ if (unlikely(cursor->p + 1 > cursor->end))
+ return 0;
+ if (*cursor->p != match)
+ return 0;
+ cursor->p++;
+ return 1;
+}
+
+static inline int consume_u32(struct cursor *cursor, unsigned int match)
+{
+ return consume_bytes(cursor, (unsigned char*)&match, sizeof(match));
+}
#endif /* CURSOR_PARSER */
diff --git a/src/wasm.c b/src/wasm.c
@@ -786,7 +786,7 @@ static char *instr_name(enum instr_tag tag)
case i_ref_null: return "ref_null";
case i_ref_func: return "ref_func";
case i_ref_is_null: return "ref_is_null";
- case i_table_op: return "table_op";
+ case i_bulk_op: return "bulk_op";
case i_table_get: return "table_get";
case i_table_set: return "table_set";
case i_selects: return "selects";
@@ -1154,6 +1154,9 @@ static void print_section(struct module *module, enum section_tag section)
case section_data:
print_data_section(&module->data_section);
break;
+ case section_data_count:
+ printf("data count %d\n", module->data_section.num_datas);
+ break;
case section_name:
printf("todo: print name section\n");
break;
@@ -1861,7 +1864,7 @@ static void print_code(u8 *code, int code_len)
}
*/
-static inline int is_const_instr(u8 tag)
+static INLINE int is_const_instr(u8 tag)
{
switch ((enum const_instr)tag) {
case ci_global_get:
@@ -1877,13 +1880,29 @@ static inline int is_const_instr(u8 tag)
return 0;
}
-static int cursor_push_nullval(struct cursor *stack)
+static INLINE int cursor_push_nullval(struct cursor *stack)
{
struct val val;
val.type = val_ref_null;
return cursor_pushval(stack, &val);
}
+static INLINE const char *bulk_op_name(struct bulk_op *op)
+{
+ switch (op->tag) {
+ case i_memory_fill: return "memory.fill";
+ case i_memory_copy: return "memory.copy";
+ case i_table_init: return "table.init";
+ case i_elem_drop: return "elem.drop";
+ case i_table_copy: return "table.copy";
+ case i_table_grow: return "table.grow";
+ case i_table_size: return "table.size";
+ case i_table_fill: return "table.fill";
+ }
+
+ return "?";
+}
+
static const char *show_instr(struct instr *instr)
{
struct cursor buf;
@@ -2128,7 +2147,9 @@ static const char *show_instr(struct instr *instr)
case i_i64_extend16_s:
case i_i64_extend32_s:
case i_ref_is_null:
- case i_table_op:
+ break;
+ case i_bulk_op:
+ cursor_push_str(&buf, bulk_op_name(&instr->bulk_op));
break;
}
@@ -2613,13 +2634,27 @@ static int parse_wdata(struct wasm_parser *p, struct wdata *data)
return 1;
}
+static int parse_data_count_section(struct wasm_parser *p, struct datasec *section)
+{
+ if (!parse_u32(&p->cur, §ion->num_datas))
+ return parse_err(p, "data count");
+ return 1;
+}
+
static int parse_data_section(struct wasm_parser *p, struct datasec *section)
{
struct wdata *data;
u32 elems, i;
- if (!parse_vector(p, sizeof(*data), &elems, (void**)&data)) {
+ if (!parse_vector(p, sizeof(*data), &elems, (void**)&data))
return parse_err(p, "datas vector");
+
+ if (was_section_parsed(&p->module, section_data_count) &&
+ elems != section->num_datas) {
+ return parse_err(p, "we got a data count section with %d "
+ "elements but the data section says it has %d "
+ "elements. what's up with that?",
+ section->num_datas, elems);
}
for (i = 0; i < elems; i++) {
@@ -2884,13 +2919,17 @@ static int parse_section_by_tag(struct wasm_parser *p, enum section_tag tag,
return 1;
case section_data:
- if (!parse_data_section(p, &p->module.data_section)) {
+ if (!parse_data_section(p, &p->module.data_section))
return parse_err(p, "data section");
- }
+ return 1;
+
+ case section_data_count:
+ if (!parse_data_count_section(p, &p->module.data_section))
+ return parse_err(p, "data count section");
return 1;
default:
- return parse_err(p, "invalid section tag");
+ return parse_err(p, "invalid section tag %d", tag);
}
return 1;
@@ -4607,6 +4646,62 @@ static int parse_call_indirect(struct cursor *code,
parse_u32(code, &call_indirect->tableidx);
}
+static int parse_bulk_op(struct cursor *code, struct errors *errs,
+ struct bulk_op *bulk_op)
+{
+ u8 tag;
+
+ if (unlikely(!pull_byte(code, &tag)))
+ return note_error(errs, code, "oob");
+
+ if (unlikely(tag < 10 || tag > 17))
+ return note_error(errs, code, "invalid bulk op %d", tag);
+
+ bulk_op->tag = tag;
+
+ switch ((enum bulk_tag)tag) {
+ case i_memory_copy:
+ if (unlikely(!consume_byte(code, 0)))
+ return note_error(errs, code, "mem idx dst 0");
+ if (unlikely(!consume_byte(code, 0)))
+ return note_error(errs, code, "mem idx src 0");
+ return 1;
+
+ case i_memory_fill:
+ if (unlikely(!consume_byte(code, 0)))
+ return note_error(errs, code, "mem idx 0");
+ return 1;
+
+ case i_table_init:
+ if (unlikely(!parse_u32(code, &bulk_op->table_init.elemidx)))
+ return note_error(errs, code, "elemidx");
+ if (unlikely(!parse_u32(code, &bulk_op->table_init.tableidx)))
+ return note_error(errs, code, "tableidx");
+ return 1;
+
+ case i_elem_drop:
+ if (unlikely(!parse_u32(code, &bulk_op->idx)))
+ return note_error(errs, code, "elemidx");
+ return 1;
+
+ case i_table_copy:
+ if (unlikely(!parse_u32(code, &bulk_op->table_copy.from)))
+ return note_error(errs, code, "elemidx");
+ if (unlikely(!parse_u32(code, &bulk_op->table_copy.to)))
+ return note_error(errs, code, "tableidx");
+ return 1;
+
+ case i_table_grow:
+ case i_table_size:
+ case i_table_fill:
+ if (unlikely(!parse_u32(code, &bulk_op->idx)))
+ return note_error(errs, code, "tableidx");
+ return 1;
+ }
+
+ return note_error(errs, code, "unhandled table op 0x%02x", tag);
+}
+
static int parse_br_table(struct cursor *code, struct errors *errs,
struct br_table *br_table)
{
@@ -4771,8 +4866,8 @@ static int parse_instr(struct expr_parser *p, u8 tag, struct instr *op)
case i_br_table:
return parse_br_table(p->code, p->errs, &op->br_table);
- case i_table_op:
- return note_error(p->errs, p->code, "parse table op");
+ case i_bulk_op:
+ return parse_bulk_op(p->code, p->errs, &op->bulk_op);
case i_call_indirect:
return parse_call_indirect(p->code, &op->call_indirect);
@@ -5965,6 +6060,77 @@ static INLINE int table_set(struct wasm_interp *interp,
return 1;
}
+static int interp_memory_copy(struct wasm_interp *interp)
+{
+ int dest, src, size;
+ u8 *data_src, *data_dest;
+
+ if (unlikely(!stack_pop_i32(interp, &size)))
+ return interp_error(interp, "size");
+
+ if (unlikely(!stack_pop_i32(interp, &src)))
+ return interp_error(interp, "byte");
+
+ if (unlikely(!stack_pop_i32(interp, &dest)))
+ return interp_error(interp, "destination");
+
+ if (!(data_dest = mem_ptr(interp, dest, size)))
+ return interp_error(interp, "memory copy dest out of bounds");
+
+ if (!(data_src = mem_ptr(interp, src, size)))
+ return interp_error(interp, "memory copy src out of bounds");
+
+ debug("memory.copy src:%d dst:%d size:%d\n",
+ src, dest, size);
+
+ memcpy(data_dest, data_src, size);
+
+ return 1;
+}
+
+static int interp_memory_fill(struct wasm_interp *interp)
+{
+ int dest, byte, size;
+ u8 *data;
+
+ if (unlikely(!stack_pop_i32(interp, &size)))
+ return interp_error(interp, "size");
+
+ if (unlikely(!stack_pop_i32(interp, &byte)))
+ return interp_error(interp, "byte");
+
+ if (unlikely(!stack_pop_i32(interp, &dest)))
+ return interp_error(interp, "destination");
+
+ if (!(data = mem_ptr(interp, dest, size)))
+ return interp_error(interp, "memory fill out of bounds");
+
+ debug("memory.fill dst:%d byte:%d size:%d\n",
+ dest, byte, size);
+
+ memset(data, byte, size);
+
+ return 1;
+}
+
+static INLINE int interp_bulk_op(struct wasm_interp *interp, struct bulk_op *op)
+{
+ switch (op->tag) {
+ case i_memory_fill: return interp_memory_fill(interp);
+ case i_memory_copy: return interp_memory_copy(interp);
+ case i_table_init:
+ case i_elem_drop:
+ case i_table_copy:
+ case i_table_grow:
+ case i_table_size:
+ case i_table_fill:
+ return interp_error(interp, "unhandled bulk op: %s",
+ bulk_op_name(op));
+ }
+
+ return interp_error(interp, "unhandled unknown bulk op: %d", op->tag);
+}
+
static int interp_table_set(struct wasm_interp *interp, u32 tableidx)
{
struct table_inst *table;
@@ -6339,7 +6505,7 @@ static int interp_instr(struct wasm_interp *interp, struct instr *instr)
case i_br_if: return interp_br_if(interp, instr->i32);
case i_memory_size: return interp_memory_size(interp, instr->memidx);
case i_memory_grow: return interp_memory_grow(interp, instr->memidx);
- case i_table_op: return interp_error(interp, "todo: interp table_op");
+ case i_bulk_op: return interp_bulk_op(interp, &instr->bulk_op);
case i_table_set: return interp_table_set(interp, instr->i32);
case i_return: return interp_return(interp);
default:
@@ -6674,7 +6840,7 @@ static int init_memory(struct wasm_interp *interp, struct wdata *data, int datai
static int init_memories(struct wasm_interp *interp)
{
struct wdata *data;
- int i;
+ u32 i;
debug("init memories\n");
diff --git a/src/wasm.h b/src/wasm.h
@@ -59,6 +59,7 @@ enum section_tag {
section_element,
section_code,
section_data,
+ section_data_count,
section_name,
num_sections,
};
@@ -299,6 +300,17 @@ struct section {
enum section_tag tag;
};
+enum bulk_tag {
+ i_memory_copy = 10,
+ i_memory_fill = 11,
+ i_table_init = 12,
+ i_elem_drop = 13,
+ i_table_copy = 14,
+ i_table_grow = 15,
+ i_table_size = 16,
+ i_table_fill = 17,
+};
+
enum instr_tag {
/* control instructions */
i_unreachable = 0x00,
@@ -506,7 +518,7 @@ enum instr_tag {
i_ref_is_null = 0xD1,
i_ref_func = 0xD2,
- i_table_op = 0xFC,
+ i_bulk_op = 0xFC,
/* TODO: more instrs */
};
@@ -556,6 +568,20 @@ struct table_init {
u32 elemidx;
};
+struct table_copy {
+ u32 from;
+ u32 to;
+};
+
+struct bulk_op {
+ enum bulk_tag tag;
+ union {
+ struct table_init table_init;
+ struct table_copy table_copy;
+ u32 idx;
+ };
+};
+
struct select_instr {
u8 *valtypes;
u32 num_valtypes;
@@ -566,7 +592,7 @@ struct instr {
int pos;
union {
struct br_table br_table;
- struct table_init table_init;
+ struct bulk_op bulk_op;
struct call_indirect call_indirect;
struct memarg memarg;
struct select_instr select;
@@ -602,7 +628,7 @@ struct wdata {
struct datasec {
struct wdata *datas;
- int num_datas;
+ u32 num_datas;
};
struct startsec {