summaryrefslogtreecommitdiff
path: root/parse.c
diff options
context:
space:
mode:
authorHanjie Wu <hanjiew@andrew.cmu.edu>2022-12-08 17:45:55 -0500
committerHanjie Wu <hanjiew@andrew.cmu.edu>2022-12-08 17:45:55 -0500
commita054755b62e660d3d8aba42fdb0a992e2d215599 (patch)
tree5f2e793527f75240800b31025f4c4c1f58be23ab /parse.c
JIT implementation
Diffstat (limited to 'parse.c')
-rw-r--r--parse.c449
1 files changed, 449 insertions, 0 deletions
diff --git a/parse.c b/parse.c
new file mode 100644
index 0000000..8b2ac81
--- /dev/null
+++ b/parse.c
@@ -0,0 +1,449 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "common.h"
+#include "vm.h"
+#include "test.h"
+#include "weewasm.h"
+#include "illegal.h"
+#include "ir.h"
+#include "disass.h"
+
+#define CHECK(x) do { if(!(x)) return -2; } while(0)
+
+// Read a string and print its length and UTF-8 chars.
+int read_and_copy_string(buffer_t* buf, const char** nameptr, const byte* sectend) {
+ uint32_t namelen = read_u32leb(buf);
+ DISASS("%u ", namelen);
+ if (nameptr != NULL) *nameptr = strndup((const char*)buf->ptr, namelen);
+ CHECK((sectend - buf->ptr) >= namelen);
+ DISASS("\"%.*s\"", namelen, buf->ptr);
+ buf->ptr += namelen;
+ return 0;
+}
+
+wasm_type_t read_value_type(buffer_t* buf) {
+ int32_t type = read_i32leb(buf);
+ DISASS(" %s", type_name(type));
+ switch (type) {
+ case WASM_TYPE_I32: return I32;
+ case WASM_TYPE_F64: return F64;
+ case WASM_TYPE_EXTERNREF: return EXTERNREF;
+ default:
+ ERR("!illegal type (%s)", type_name(type));
+ return EXTERNREF;
+ }
+}
+
+wasm_value_t read_init_expr(buffer_t* buf) {
+ wasm_value_t val = wasm_i32_value(0);
+ byte b = read_u8(buf);
+ switch (b) {
+ case WASM_OP_I32_CONST: {
+ val = wasm_i32_value(read_i32leb(buf));
+ break;
+ }
+ case WASM_OP_F64_CONST: {
+ double* p = (double*)buf->ptr;
+ buf->ptr += 8;
+ val = wasm_f64_value(*p);
+ break;
+ }
+ default: {
+ ERR("!illegal init expr bytecode 0x%02X (%s)", b, bytecode_name(b));
+ break;
+ }
+ }
+ b = read_u8(buf);
+ if (b != WASM_OP_END) ERR("!illegal init expr bytecode 0x%02X", b);
+ return val;
+}
+
+// Read minimimum and optional maximum.
+void read_limits(buffer_t* buf, wasm_limits_t* dest) {
+ byte val = read_u8(buf);
+ dest->initial = read_u32leb(buf);
+ if ((val & 1) == 1) {
+ dest->max = read_u32leb(buf);
+ dest->has_max = 1;
+ DISASS(" %u %u \t\t\t; initial and max\n", dest->initial, dest->max);
+ } else {
+ dest->max = 0xFFFFFFFFu;
+ dest->has_max = 0;
+ DISASS(" %u \t\t\t; initial\n", dest->initial);
+ }
+}
+
+// Reads a section code, skipping custom sections.
+int read_section_code(buffer_t* buf, wasm_module_t* module, uint32_t* sectlen) {
+ while (buf->ptr < buf->end) {
+ byte code = read_u8(buf);
+ if (code != 0) {
+ DISASS("%s ", section_name(code));
+ // read a normal section
+ *sectlen = read_u32leb(buf);
+ CHECK((buf->end - buf->ptr) >= *sectlen);
+ return code;
+ }
+ // print custom section
+ DISASS("custom ");
+ uint32_t sectlen = read_u32leb(buf);
+ DISASS("%u ", sectlen);
+ CHECK((buf->end - buf->ptr) >= sectlen);
+ const byte* sectend = buf->ptr + sectlen;
+ read_and_copy_string(buf, NULL, sectend);
+ DISASS("\n");
+ buf->ptr = sectend;
+ }
+ // reached the end of the file, or there was a nested error
+ return -1;
+}
+
+void read_type_decl(buffer_t* buf, wasm_sig_decl_t* dest, const byte* sectend) {
+ byte code = read_u8(buf);
+ DISASS(" %s", type_decl_name(code));
+ if (code != 0x60) ERR("!expected signature declaration (0x60), got 0x%02X", code);
+ uint32_t pcount = read_u32leb(buf);
+ DISASS(" %u", pcount);
+ dest->num_params = pcount;
+ dest->params = (wasm_type_t*)malloc(pcount * sizeof(wasm_type_t));
+ for (uint32_t i = 0; i < pcount && (buf->ptr < buf->end); i++) {
+ wasm_type_t t = read_value_type(buf);
+ dest->params[i] = t;
+ }
+ uint32_t rcount = read_u32leb(buf);
+ dest->num_results = rcount;
+ dest->results = (wasm_type_t*)malloc(rcount * sizeof(wasm_type_t));
+ DISASS(" %u", rcount);
+ if (rcount > 1) ERR("!expected result count <= 1, got %d", rcount);
+ for (uint32_t i = 0; i < rcount && (buf->ptr < buf->end); i++) {
+ wasm_type_t t = read_value_type(buf);
+ dest->results[i] = t;
+ }
+ DISASS("\n");
+}
+
+uint8_t bind_import(wasm_module_t* module, wasm_import_decl_t* decl) {
+ if (strcmp(decl->mod_name, "weewasm") != 0) ERR("!unrecognized import module: %s", decl->mod_name);
+ if (strcmp(decl->member_name, "puti") == 0) return WEEWASM_INTRINSIC_PUTI; // TODO: check signature
+ if (strcmp(decl->member_name, "putd") == 0) return WEEWASM_INTRINSIC_PUTD;
+ if (strcmp(decl->member_name, "puts") == 0) return WEEWASM_INTRINSIC_PUTS;
+ ERR("!unrecognized weewasm import: %s", decl->member_name);
+ return 0;
+}
+
+void read_import_decl(buffer_t* buf, wasm_module_t* module, uint32_t import_index, uint32_t func_index, const byte* sectend) {
+ DISASS(" ");
+ wasm_import_decl_t* dest = &module->imports[import_index];
+ read_and_copy_string(buf, &dest->mod_name, sectend);
+ DISASS(" ");
+ read_and_copy_string(buf, &dest->member_name, sectend);
+ uint32_t code = read_u8(buf);
+ DISASS(" %s", import_kind_name(code));
+ switch (code) {
+ case WASM_IMPORT_FUNC: {
+ uint32_t sig = read_u32leb(buf);
+ DISASS(" %u", sig);
+ dest->kind = FUNC;
+ dest->index = func_index;
+ wasm_func_decl_t* func = &module->funcs[func_index];
+ func->sig_index = sig;
+ func->intrinsic = bind_import(module, dest);
+ break;
+ }
+ case WASM_IMPORT_TABLE: {
+ ERR("!illegal table import");
+ break;
+ }
+ case WASM_IMPORT_MEMORY: {
+ ERR("!illegal memory import");
+ break;
+ }
+ case WASM_IMPORT_GLOBAL: {
+ ERR("!illegal global import");
+ break;
+ }
+ }
+ DISASS("\n");
+}
+
+void read_func_decl(buffer_t* buf, wasm_func_decl_t* dest, const byte* sectend) {
+ uint32_t index = read_u32leb(buf);
+ dest->sig_index = index;
+ DISASS(" %u \t\t\t; signature index\n", index);
+}
+
+void read_table_decl(buffer_t* buf, wasm_table_decl_t* dest, const byte* sectend) {
+ int32_t type = read_i32leb(buf);
+ DISASS(" %s", type_name(type));
+ if (type != WASM_TYPE_FUNCREF) ERR("!invalid table type %s", type_name(type));
+ read_limits(buf, &dest->limits);
+ DISASS("\n");
+}
+
+void read_memory_decl(buffer_t* buf, wasm_limits_t* limits, const byte* sectend) {
+ read_limits(buf, limits);
+ DISASS("\n");
+}
+
+void read_global_decl(buffer_t* buf, wasm_global_decl_t* dest, const byte* sectend) {
+ dest->type = read_value_type(buf);
+ byte mut = read_u8(buf);
+ dest->mutable = mut != 0;
+ if (mut != 0) DISASS(" mutable");
+ DISASS("\n");
+ dest->init = read_init_expr(buf);
+}
+
+void read_export_decl(buffer_t* buf, wasm_module_t* module, const byte* sectend) {
+ DISASS(" ");
+ uint32_t namelen = 0;
+ const char* name = NULL;
+ read_and_copy_string(buf, &name, sectend);
+ if (strcmp(name, "main") != 0) ERR("!expected export name \"main\", got %s", name);
+ free((char*)name); // XXX: avoid copy?
+
+ uint32_t code = read_u8(buf);
+ DISASS(" %s", import_kind_name(code));
+ uint32_t index = read_u32leb(buf);
+ DISASS(" %u \t\t\t; export index\n", index);
+ switch (code) {
+ case WASM_IMPORT_FUNC: {
+ module->main_func = (int32_t)index;
+ break;
+ }
+ case WASM_IMPORT_TABLE: {
+ ERR("!illegal table export");
+ break;
+ }
+ case WASM_IMPORT_MEMORY: {
+ ERR("!illegal memory export");
+ break;
+ }
+ case WASM_IMPORT_GLOBAL: {
+ ERR("!illegal global export");
+ break;
+ }
+ }
+}
+
+uint32_t read_offset_expr(buffer_t* buf) {
+ uint32_t val = 0;
+ while (buf->ptr < buf->end) {
+ byte b = read_u8(buf);
+ if (b == WASM_OP_I32_CONST) {
+ val = (uint32_t)read_i32leb(buf);
+ DISASS(" %u \t\t\t; offset\n", val);
+ } else if (b == WASM_OP_END) {
+ break;
+ } else {
+ DISASS("<!unexpected bytecode %02X>", b);
+ break;
+ }
+ }
+ return val;
+}
+
+void read_elems_decl(buffer_t* buf, wasm_elems_decl_t* dest, const byte* sectend) {
+ byte b = read_u32leb(buf);
+ if (b != 0) {
+ DISASS(" <!invalid elem flags %d>", b);
+ } else {
+ DISASS(" ");
+ dest->table_offset = read_offset_expr(buf);
+ uint32_t count = read_u32leb(buf);
+ dest->length = count;
+ dest->func_indexes = (uint32_t*)malloc(dest->length * sizeof(uint32_t));
+ DISASS(" %u \t\t\t; count\n ", count);
+ for (uint32_t i = 0; i < count && buf->ptr < buf->end; i++) {
+ uint32_t f = read_u32leb(buf);
+ DISASS(" %u", f);
+ dest->func_indexes[i] = f;
+ }
+ }
+ DISASS("\n");
+}
+
+void read_code_decl(buffer_t* buf, wasm_func_decl_t* dest, const byte* sectend) {
+ uint32_t len = read_u32leb(buf);
+ dest->code_start = (uint32_t)(buf->ptr - buf->start);
+ const byte* codeend = buf->ptr + len;
+ DISASS("body %u \t\t\t; length\n", len);
+ uint32_t cnt = read_u32leb(buf);
+ DISASS(" %u \t\t\t; locals count\n", cnt);
+ for (uint32_t i = 0; i < cnt; i++) {
+ uint32_t cnt = read_u32leb(buf);
+ DISASS(" %u", cnt);
+ int32_t type = read_i32leb(buf);
+ DISASS(" %s\n", type_name(type));
+ }
+ if (g_disassemble) {
+ // disassemble code
+ while (buf->ptr < codeend && buf->ptr < buf->end) {
+ print_bytecode(buf);
+ }
+ }
+ buf->ptr = codeend;
+ dest->code_end = (uint32_t)(buf->ptr - buf->start);
+}
+
+void read_data_decl(buffer_t* buf, wasm_data_decl_t* dest, const byte* sectend) {
+ byte b = read_u32leb(buf);
+ if (b != 0) {
+ DISASS(" <!invalid data flags %d>", b);
+ } else {
+ dest->mem_offset = read_offset_expr(buf);
+ uint32_t count = read_u32leb(buf);
+ DISASS(" %u \t\t\t; bytes count\n", count);
+ dest->bytes_start = (uint32_t)(buf->ptr - buf->start);
+ if (g_disassemble) {
+ print_data(buf, count);
+ } else {
+ buf->ptr += count;
+ if (buf->ptr > buf->end) {
+ ERR("!too much data, %d bytes", count);
+ buf->ptr = buf->end;
+ }
+ }
+ dest->bytes_end = (uint32_t)(buf->ptr - buf->start);
+ }
+ DISASS("\n");
+}
+
+void allocMoreItems(void** ptr, uint32_t* nptr, size_t item_size, uint32_t add) {
+ byte* curbuf = *ptr;
+ uint32_t curcnt = *nptr;
+ size_t cursize = curcnt * item_size;
+ uint32_t newcnt = curcnt + add;
+ size_t newsize = newcnt * item_size;
+ byte* newbuf = (byte*)realloc(curbuf, newsize);
+ memset(newbuf + cursize, 0, newsize - cursize);
+ *ptr = newbuf;
+ *nptr = newcnt;
+}
+
+uint32_t read_count(buffer_t* buf, uint32_t max, byte sectcode) {
+ uint32_t count = read_u32leb(buf);
+ DISASS("%u \t\t\t; count\n", count);
+ if (count > max) ERR("!expected count <= %d for %s section, got %d", max, section_name(sectcode), count);
+ return count;
+}
+
+// A macro to read the count of entries, expand internal storage, and call {read_func} in a loop.
+#define READ_ENTRIES(count_field, ptr_field, entry_type, read_func) do { \
+ uint32_t count = read_count(buf, 100000, sectcode); \
+ uint32_t j = module->count_field; \
+ allocMoreItems((void**)&module->ptr_field, &module->count_field, sizeof(entry_type), count); \
+ for (uint32_t i = 0; i < count; i++, j++) { \
+ read_func(buf, &module->ptr_field[j], sectend); \
+ } \
+ } while (0)
+
+
+// The main parsing routine.
+int parse_wasm_module(buffer_t* buf, wasm_module_t* module) {
+ ssize_t len = 0;
+
+ //==== Check magic word =============================================
+ uint32_t magic = decode_u32(buf->ptr, buf->end, &len);
+ DISASS("read_magic = %x\n", magic);
+ if (len != 4 || magic != WASM_MAGIC) {
+ ERR("!invalid magic word, expected %x\n", WASM_MAGIC);
+ return -1;
+ }
+ buf->ptr += 4;
+
+ //==== Check Wasm Version ===========================================
+ uint32_t version = decode_u32(buf->ptr, buf->end, &len);
+ DISASS("read_version = %x\n", version);
+ if (len != 4 || version != WASM_VERSION) {
+ ERR("!invalid Wasm version, expected %x\n", WASM_VERSION);
+ return -2;
+ }
+ buf->ptr += 4;
+
+ //==== Read Wasm sections ===========================================
+ while (buf->ptr < buf->end) {
+ uint32_t sectlen = 0;
+ int sectcode = read_section_code(buf, module, &sectlen);
+ if (sectcode < 0) return -3;
+ const byte* sectend = buf->ptr + sectlen;
+
+ switch (sectcode) {
+ case WASM_SECT_TYPE: {
+ READ_ENTRIES(num_sigs, sigs, wasm_sig_decl_t, read_type_decl);
+ break;
+ }
+ case WASM_SECT_IMPORT: {
+ uint32_t count = read_count(buf, 1000, sectcode);
+ uint32_t import_index = module->num_imports;
+ uint32_t func_index = module->num_funcs;
+ // all imports must be functions.
+ allocMoreItems((void**)&module->imports, &module->num_imports, sizeof(wasm_import_decl_t), count);
+ allocMoreItems((void**)&module->funcs, &module->num_funcs, sizeof(wasm_func_decl_t), count);
+ for (uint32_t i = 0; i < count; i++, import_index++, func_index++) {
+ read_import_decl(buf, module, import_index, func_index, sectend);
+ }
+ break;
+ }
+ case WASM_SECT_FUNCTION: {
+ READ_ENTRIES(num_funcs, funcs, wasm_func_decl_t, read_func_decl);
+ break;
+ }
+ case WASM_SECT_TABLE: {
+ read_count(buf, 1, sectcode);
+ if (module->table != NULL) ERR("!repeated table section");
+ module->table = (wasm_table_decl_t*)malloc(sizeof(wasm_table_decl_t));
+ read_table_decl(buf, module->table, sectend);
+ break;
+ }
+ case WASM_SECT_MEMORY: {
+ read_count(buf, 1, sectcode);
+ read_memory_decl(buf, &module->mem_limits, sectend);
+ break;
+ }
+ case WASM_SECT_GLOBAL: {
+ READ_ENTRIES(num_globals, globals, wasm_global_decl_t, read_global_decl);
+ break;
+ }
+ case WASM_SECT_EXPORT: {
+ read_count(buf, 1, sectcode);
+ read_export_decl(buf, module, sectend);
+ break;
+ }
+ case WASM_SECT_START: {
+ uint32_t entry = read_u32leb(buf);
+ module->start_func = (int32_t)entry;
+ DISASS("%u\n", entry);
+ break;
+ }
+ case WASM_SECT_ELEMENT: {
+ READ_ENTRIES(num_elems, elems, wasm_elems_decl_t, read_elems_decl);
+ break;
+ }
+ case WASM_SECT_CODE: {
+ uint32_t num_bodies = module->num_funcs - module->num_imports;
+ uint32_t count = read_count(buf, num_bodies, sectcode);
+ if (count != num_bodies) ERR("!expected %d function bodies, got %d", num_bodies, count);
+ uint32_t func_index = module->num_imports;
+ for (uint32_t i = 0; i < count; i++, func_index++) {
+ read_code_decl(buf, &module->funcs[func_index], sectend);
+ }
+ break;
+ }
+ case WASM_SECT_DATA: {
+ READ_ENTRIES(num_data, data, wasm_data_decl_t, read_data_decl);
+ break;
+ }
+ default:
+ ERR("!unknown section constant 0x%02x\n", sectcode);
+ return -4;
+ }
+ CHECK(buf->ptr == sectend); // internal check
+ }
+ return 0;
+}
+