#include "wasm_compile.h" #include "wasm.h" #include "stack_machine_instruction.h" struct translation { FILE *handle; struct function *function; struct module *module; }; /* All functions, that go into function pointer array, start with _ */ /* Translate complex - those routines have to be defined manually */ #define TC(wasm_opcode, name) static int _translate_call(struct translation *data) { uint32_t funcidx; struct instruction **target; if (leb_u32(data->handle, &funcidx)) { PRERR(MSG_BAD_NUM); return -1; } if (funcidx >= data->module->functions_count) { PRERR(MSG_BAD_IDX("funcidx")); return -1; } target = &data->module->functions[funcidx].translated_body; return i_call(ptr(target), &data->function->translated_body); } static int _translate_local_get(struct translation *data) { uint32_t localidx; uint32_t args_count = data->function->type->arguments_count; uint32_t locals_count = data->function->locals_count; uint32_t all_locals_count = args_count + locals_count; uint64_t offset_on_frame; struct instruction **expr = &data->function->translated_body; if (leb_u32(data->handle, &localidx)) { PRERR(MSG_BAD_NUM); return -1; } if (localidx >= all_locals_count) { PRERR(MSG_BAD_IDX("localidx")); return -1; } offset_on_frame = all_locals_count - localidx + 1; if (localidx >= args_count) offset_on_frame -= 1; return i_load (im(STACK_FRAME_BACKUP_ADDR), expr) || i_load_p(im(4 * offset_on_frame), expr); } static int _translate_const(struct translation *data) { uint32_t constant; if (leb_u32(data->handle, &constant)) { PRERR(MSG_BAD_NUM); return -1; } return i_const(im(constant), &data->function->translated_body); } /* Translate Simple */ #define TS(wasm_opcode, sm_instr) \ static int _translate_##sm_instr(struct translation *data) \ { \ return i_##sm_instr(&data->function->translated_body); \ } /* Translate load/store */ static int translate_load_store(struct translation *data, int (*instr_routine) (struct instruction_data, struct instruction **)) { uint32_t align, offset; if (leb_u32(data->handle, &align) || leb_u32(data->handle, &offset)) { PRERR(MSG_BAD_NUM); return -1; } offset += MEMORY_BOTTOM_ADDR; return instr_routine(im(offset), &data->function->translated_body); } #define TLS(wasm_opcode, sm_instr) \ static int _translate_##sm_instr(struct translation *data) \ { \ return translate_load_store(data, i_##sm_instr); \ } /* This inclusion defines functions using macros above */ #include "translate_xmacro.h" #undef TS #undef TLS #undef TC /* Translate complex */ #define TC(wasm_opcode, name) [wasm_opcode] = _translate_##name, /* Translate Simple */ #define TS(wasm_opcode, sm_instr) TC(wasm_opcode, sm_instr) /* Translate load/store */ #define TLS(wasm_opcode, sm_instr) TC(wasm_opcode, sm_instr) /* The actual array of function pointers is defined here */ static int (*translation_routines[256]) (struct translation *) = { #include "translate_xmacro.h" }; #undef TS #undef TLS #undef TC static int translate_expr(struct translation *data, struct functype *exprtype) { int wasm_opcode; while (1) { wasm_opcode = fgetc(data->handle); if (wasm_opcode == EOF) { PRERR(MSG_EOF); return -1; } if (wasm_opcode == WASM_END) { return 0; } if (!translation_routines[wasm_opcode]) { PRERR("Unknown Wasm opcode: 0x%02x\n", wasm_opcode); return -1; } if (translation_routines[wasm_opcode](data)) return -1; } } int translate(FILE *handle, struct function *function, struct module *module) { struct instruction **expr = &function->translated_body; 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; struct translation data = {.handle = handle, .function = function, .module = module}; 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 */ if (translate_expr(&data, function->type)) 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; return 0; fail: PRERR("Couldn't translate function to stack machine\n"); free_expr(*expr); function->translated_body = NULL; return -1; }