diff options
Diffstat (limited to 'tools/translate.c')
-rw-r--r-- | tools/translate.c | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/tools/translate.c b/tools/translate.c new file mode 100644 index 0000000..ee6ea99 --- /dev/null +++ b/tools/translate.c @@ -0,0 +1,187 @@ +#include "wasm_compile.h" +#include "stack_machine_instruction.h" + +/* WebAssembly opcodes */ +#define WASM_END 0x0B +#define WASM_LOCAL_GET 0x20 +#define WASM_I32_LOAD 0x28 +#define WASM_I32_LOAD8_S 0x2C +#define WASM_I32_LOAD8_U 0x2D +#define WASM_I32_LOAD16_S 0x2E +#define WASM_I32_LOAD16_U 0x2F +#define WASM_I32_STORE 0x36 +#define WASM_I32_STORE8 0x3A +#define WASM_I32_STORE16 0x3B +#define WASM_I64_STORE32 0x3E +#define WASM_I32_CONST 0x41 +#define WASM_I32_SUB 0x6B + +int translate(FILE *handle, struct function *function, struct module *module) +{ + struct instruction *expr = NULL; + uint32_t args_count = function->type->arguments_count; + uint32_t locals_count = function->locals_count; + uint32_t all_locals_count = args_count + locals_count; + size_t i; + int wasm_opcode; + int matched; + + if (locals_count + (uint64_t) args_count > STACK_TOP_ADDR * 4) { + PRERR("Too many locals in a function\n"); + goto fail; + } + + for (i = locals_count + 3; i; i--) { + if (i_const(im(0), &expr)) + goto fail; + } + + /* function prologue */ + if (i_get_frame( &expr) || + i_tee ( &expr) || + i_load (im(STACK_FRAME_BACKUP_ADDR), &expr) || + i_store_p (im(0x0), &expr) || + i_store (im(STACK_FRAME_BACKUP_ADDR), &expr)) + goto fail; + + /* actual function body */ + i = 0; + while (1) { + matched = 0; + + wasm_opcode = fgetc(handle); + + if (wasm_opcode == EOF) { + PRERR(MSG_EOF); + goto fail; + } + + // TODO: make a function for each instruction type, + // call them through some table... + if (wasm_opcode == WASM_I32_SUB) { + if (i_sub(&expr)) + goto fail; + + matched = 1; + } else if (wasm_opcode <= WASM_I64_STORE32 && + wasm_opcode >= WASM_I32_LOAD) { + uint32_t align, offset; + + if (leb_u32(handle, &align) || + leb_u32(handle, &offset)) { + PRERR(MSG_BAD_NUM); + goto fail; + } + + offset += MEMORY_BOTTOM_ADDR; + + // TODO: rewrite it some cleaner way +#define TRY(opcode, instr) \ + if (wasm_opcode == opcode) { \ + matched = 1; \ + \ + if (i_##instr(im(offset), &expr)) \ + goto fail; \ + } + + TRY(WASM_I32_LOAD, load_p); + TRY(WASM_I32_LOAD8_S, loadbsx_p); + TRY(WASM_I32_LOAD8_U, loadbzx_p); + TRY(WASM_I32_LOAD16_S, loadwsx_p); + TRY(WASM_I32_LOAD16_U, loadwzx_p); + TRY(WASM_I32_STORE, store_p); + TRY(WASM_I32_STORE8, storeb_p); + TRY(WASM_I32_STORE16, storew_p); + } else if (wasm_opcode == WASM_LOCAL_GET) { + uint32_t localidx; + uint64_t offset_on_frame; + + if (leb_u32(handle, &localidx)) { + PRERR(MSG_BAD_NUM); + goto fail; + } + + if (localidx >= all_locals_count) { + PRERR(MSG_BAD_IDX("localidx")); + goto fail; + } + + offset_on_frame = all_locals_count - localidx + 1; + + if (localidx >= args_count) + offset_on_frame -= 1; + + if (i_load (im(STACK_FRAME_BACKUP_ADDR), &expr) || + i_load_p(im(4 * offset_on_frame), &expr)) + goto fail; + + matched = 1; + } else if (wasm_opcode == WASM_I32_CONST) { + uint32_t constant; + + if (leb_u32(handle, &constant)) { + PRERR(MSG_BAD_NUM); + goto fail; + } + + if (i_const(im(constant), &expr)) + goto fail; + + matched = 1; + } else if (wasm_opcode == WASM_END) { + break; + } + + if (!matched) { + PRERR("Unknown Wasm opcode: %02x\n", wasm_opcode); + goto fail; + } + } + + /* function epilogue */ + if (function->type->result) { + if (i_load (im(STACK_FRAME_BACKUP_ADDR), &expr) || + i_swap ( &expr) || + i_store_p(im(4 * (2 + all_locals_count - 1)), &expr) || + i_load (im(STACK_FRAME_BACKUP_ADDR), &expr) || + i_tee ( &expr) || + i_tee ( &expr) || + i_load_p (im(4 * (1 + locals_count)), &expr) || + i_store_p(im(4 * (2 + all_locals_count - 2)), &expr) || + i_load_p (im(0), &expr) || + i_store (im(STACK_FRAME_BACKUP_ADDR), &expr)) + goto fail; + } else { + /* It's a bit shorter if we don't return anything */ + if (i_load (im(STACK_FRAME_BACKUP_ADDR), &expr) || + i_tee ( &expr) || + i_tee ( &expr) || + i_load_p (im(4 * (1 + locals_count)), &expr) || + i_store_p(im(4 * (2 + all_locals_count - 1)), &expr) || + i_load_p (im(0), &expr) || + i_store (im(STACK_FRAME_BACKUP_ADDR), &expr)) + goto fail; + + } + + i = locals_count + args_count + 2 + (function->type->result ? 0 : 1); + + while (i--) { + if (i_drop(&expr)) + goto fail; + } + + if (i_ret(&expr)) + goto fail; + + function->translated_body = expr; + + return 0; + +fail: + PRERR("Couldn't translate function to stack machine\n"); + + free_expr(expr); + + return -1; +} |