diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/stack_machine_instruction.h | 1 | ||||
-rw-r--r-- | tools/translate.c | 625 | ||||
-rw-r--r-- | tools/translate_xmacro.h | 1 | ||||
-rw-r--r-- | tools/wasm.h | 1 |
4 files changed, 408 insertions, 220 deletions
diff --git a/tools/stack_machine_instruction.h b/tools/stack_machine_instruction.h index ea65334..003fa19 100644 --- a/tools/stack_machine_instruction.h +++ b/tools/stack_machine_instruction.h @@ -146,6 +146,7 @@ Y(nop, 0x0001) /* 0000_0000_0000_0001 */ Y(swap, 0x0002) /* 0000_0000_0000_0010 */ X(set_sp, 0x4000) /* 0100_0000_0xxx_xxxx */ X(jump, 0x4080) /* 0100_0000_1xxx_xxxx */ +X(add_sp, 0x4100) /* 0100_0001_0xxx_xxxx */ Y(tee, 0x1000) /* 0001_0000_0000_0000 */ Y(get_frame, 0x1001) /* 0001_0000_0000_0001 */ X(const, 0x5000) /* 0101_0000_0xxx_xxxx */ diff --git a/tools/translate.c b/tools/translate.c index 3a4496d..64ed407 100644 --- a/tools/translate.c +++ b/tools/translate.c @@ -7,6 +7,13 @@ struct target { struct target *prev; }; +struct label { + struct label *prev; + struct target *target; + struct resulttype *arity; + uint32_t values_on_stack; +}; + struct types { struct types *prev; char type; /* should be one of VALTYPE_* constants from wasm.h */ @@ -18,6 +25,7 @@ struct translation { struct function *function; struct module *module; struct types *types_stack; + struct label *labels; }; struct end_markers { @@ -53,6 +61,18 @@ static void put_type(struct types *type) } } +static uint32_t stack_size(struct types *types_stack) +{ + uint32_t count = 0; + + while (types_stack) { + count++; + types_stack = types_stack->prev; + } + + return count; +} + static void free_types_stack(struct types *top) { struct types *tmp; @@ -67,94 +87,196 @@ static void free_types_stack(struct types *top) static int translate_expr(struct translation *data, struct resulttype *args, struct resulttype *results, const struct end_markers *end_markers, - char *marker_found); + char *marker_found, bool continuation_at_start); + +static struct target *add_target(struct module *module); + +static int parse_blocktype(FILE *handle, struct resulttype *args, + struct resulttype *results, char *storage, + struct module *module); /* All functions, that go into one of function pointer arrays, start with _ */ -/** DEFINE INSTRUCTION TRANSLATION FUNCTIONS **/ +/** DEFINE ARGUMENT TYPECHECK FUNCTIONS **/ -/* Translate complex - those routines have to be defined manually */ -#define TC(wasm_opcode, name, argtypes, restype) +static int _argcheck_empty(struct types **types_stack) +{ + return 0; +} -static int parse_blocktype(FILE *handle, struct resulttype *args, - struct resulttype *results, char *storage, - struct module *module) +static int argcheck_generic_noremove(struct types *types_stack, char expected) { - int readval; - uint32_t typeidx; + char *name; - readval = fgetc(handle); + name = + expected == VALTYPE_F64 ? "f64" : + expected == VALTYPE_F32 ? "f32" : + expected == VALTYPE_I64 ? "i64" : + "i32"; - if (readval == EOF) { - PRERR(MSG_EOF); + if (!types_stack) { + PRERR("Expected %s on stack, got nothing\n", name); return -1; } - if (readval == 0x40) { - /* Blocktype is empty (no arguments, no result values) */ - *args = (struct resulttype) {.count = 0, .types = NULL}; - *results = *args; - return 0; + if (types_stack->type != VALTYPE_I32) { + PRERR("Expected %s (0x%02hhx) on stack, got 0x%02hhx\n", + name, expected, types_stack->type); + return -1; } - /* - * A nonnegative array index encoded as signed number in LEB - * shall have 0 as the second (most significant) bit of the first byte. - * Otherwise, it can't be array index, but might be a simple value type. - */ - if (readval & (1 << 6)) { - if (!is_valid_valtype(readval)) - goto fail; + return 0; +} - *args = (struct resulttype) {.count = 0, .types = NULL}; - *storage = readval; - *results = (struct resulttype) {.count = 1, .types = storage}; - return 0; - } +static int argcheck_generic(struct types **types_stack, char expected) +{ + struct types *top_type; - /* - * We know for sure it's a nonnegative number, we can just use leb_u32 - * decoding function (encoding as signed or unsigned is the same in this - * particular case). - */ - ungetc(readval, handle); + if (argcheck_generic_noremove(*types_stack, expected)) + return -1; - if (leb_u32(handle, &typeidx)) { - PRERR(MSG_BAD_NUM); - goto fail; + top_type = *types_stack; + *types_stack = top_type->prev; + + if (*types_stack) + get_type(*types_stack); + + put_type(top_type); + + return 0; +} + +static int _argcheck_i32(struct types **types_stack) +{ + return argcheck_generic(types_stack, VALTYPE_I32); +} + +static int _argcheck_i32_i32(struct types **types_stack) +{ + int i; + + for (i = 0; i < 2; i++) { + if (argcheck_generic(types_stack, VALTYPE_I32)) + return -1; } - if (typeidx <= module->functypes_count) { - PRERR(MSG_BAD_IDX("type index")); - goto fail; + return 0; +} + +static int _argcheck_custom(struct types **types_stack) +{ + return 0; /* Translation function will handle argument checks */ +} + +/** DEFINE RESULT TYPECHECK FUNCTIONS **/ + +static int _rescheck_empty(struct types **types_stack) +{ + return 0; +} + +static int rescheck_generic(struct types **types_stack, char returned) +{ + struct types *top; + + top = malloc(sizeof(struct types)); + + if (!top) { + PRERR(MSG_ALLOC_FAIL(sizeof(struct types))); + return -1; } - *args = module->functypes[typeidx].args; - *results = module->functypes[typeidx].results; + *top = (struct types) {.prev = *types_stack, .type = returned, .refs = 1}; + *types_stack = top; + return 0; +} -fail: - PRERR("Couldn't parse blocktype\n"); - return -1; +static int _rescheck_i32(struct types **types_stack) +{ + return rescheck_generic(types_stack, VALTYPE_I32); } -static struct target *add_target(struct module *module) +static int _rescheck_custom(struct types **types_stack) { - struct target *tgt; + return 0; +} - tgt = malloc(sizeof(struct target)); +/** DEFINE TYPECHECK FUNCTION POINTER ARRAY **/ - if (!tgt) { - PRERR(MSG_ALLOC_FAIL(sizeof(struct target))); - return NULL; +/* Translate complex */ +#define TC(wasm_opcode, name, argtypes, restype) \ + [wasm_opcode] = {.argcheck = _argcheck_##argtypes, \ + .rescheck = _rescheck_##restype}, + +/* Translate Simple */ +#define TS(wasm_opcode, sm_instr, argtypes, restype) \ + TC(wasm_opcode, dummy, argtypes, restype) + +/* Translate load/store */ +#define TLS(wasm_opcode, sm_instr, argtypes, restype) \ + TC(wasm_opcode, dummy, argtypes, restype) + +struct typecheck { + int (*argcheck) (struct types **), (*rescheck) (struct types **); +}; + +static struct typecheck typecheck_routines[256] = { +#include "translate_xmacro.h" +}; + +#undef TS +#undef TLS +#undef TC + +/** DEFINE CUSTOM TYPECHECK FUNCTIONS **/ + +/* + * Each of these is called by its respective translate function. In some cases + * it was not feasible to move some instruction's typecheck to a separate + * function, so there are less functions here than instructions with declared + * "custom" typechecking. + */ + +static int typecheck_call(struct translation *data, struct function *callee) +{ + uint32_t i; + + i = callee->type->args.count; + + while (i--) { + if (argcheck_generic(&data->types_stack, + callee->type->args.types[i])) + return -1; } - tgt->instr = NULL; - tgt->prev = module->targets; - module->targets = tgt; - return tgt; + for (i = 0; i < callee->type->results.count; i++) { + if (rescheck_generic(&data->types_stack, + callee->type->results.types[i])) + return -1; + } + + return 0; } +static int typecheck_local_get(struct translation *data, uint32_t localidx) +{ + uint32_t args_count = data->function->type->args.count; + char type = localidx < args_count ? + data->function->type->args.types[localidx] : + data->function->locals[localidx - args_count]; + + if (rescheck_generic(&data->types_stack, type)) + return -1; + + return 0; +} + +/** DEFINE INSTRUCTION TRANSLATION FUNCTIONS **/ + +/* Translate complex - those routines have to be defined manually */ +#define TC(wasm_opcode, name, argtypes, restype) + static int _translate_if(struct translation *data) { struct types *backed_stack; @@ -191,7 +313,7 @@ static int _translate_if(struct translation *data) get_type(backed_stack); retval = translate_expr(data, &block_args, &block_results, - &if_end_markers, &marker_found); + &if_end_markers, &marker_found, false); put_type(data->types_stack); data->types_stack = backed_stack; @@ -208,7 +330,7 @@ static int _translate_if(struct translation *data) ungetc(WASM_END, data->handle); if (translate_expr(data, &block_args, &block_results, - &else_end_markers, NULL)) + &else_end_markers, NULL, false)) goto fail; else_end->instr = data->function->translated_body->prev; @@ -221,7 +343,88 @@ fail: return -1; } -static int typecheck_call(struct translation *data, struct function *callee); +static int _translate_br(struct translation *data) +{ + uint32_t labelidx, i; + struct label *label; + uint32_t arity, values_on_stack; + uint32_t shift, offset_src, offset_dst; + struct instruction **expr = &data->function->translated_body; + + if (leb_u32(data->handle, &labelidx)) { + PRERR(MSG_BAD_NUM); + goto fail; + } + + label = data->labels; + i = labelidx; + + while (i--) { + label = label->prev; + + if (!label) { + PRERR(MSG_BAD_IDX("labelidx")); + goto fail; + } + } + + values_on_stack = stack_size(data->types_stack); + arity = label->arity ? label->arity->count : 0; + + if (arity > values_on_stack) { + PRERR("Need %lu values on stack to branch, only have %lu\n", + (unsigned long) arity, (unsigned long) values_on_stack); + goto fail; + } + + i = arity; + + while (i--) { + if (argcheck_generic(&data->types_stack, + label->arity->types[i])) + goto fail; + } + + shift = data->labels->values_on_stack + values_on_stack - + (label->values_on_stack + arity); + offset_dst = label->values_on_stack + 2; + offset_src = offset_dst + shift; + + if (!shift) + goto values_moved; + + if (i_load (im(STACK_FRAME_BACKUP_ADDR), expr) || + i_tee ( expr) || + i_load_p(im(-4 * (offset_dst - 1)), expr) || + i_swap ( expr) || + i_load_p(im(-4 * offset_dst), expr) || + i_add_sp(im(-4 * (shift + 2)), expr)) + goto fail; + + for (i = 0; i < arity; i++) { + if (i == 0 && i_load(im(STACK_FRAME_BACKUP_ADDR), expr)) + goto fail; + + if (i + 1 != arity && (i_tee(expr))) + goto fail; + + if (i_load_p(im(-4 * (offset_src + i)), expr)) + goto fail; + + if (i + 1 != arity && (i_swap(expr))) + goto fail; + } + +values_moved: + if (i_jump(ptr_after(&label->target->instr), expr)) + goto fail; + + return 0; + +fail: + PRERR("Couldn't translate br instruction\n"); + return -1; +} static int _translate_call(struct translation *data) { @@ -358,210 +561,162 @@ static int (*translation_routines[256]) (struct translation *) = { #undef TLS #undef TC -/** DEFINE ARGUMENT TYPECHECK FUNCTIONS **/ - -static int _argcheck_empty(struct types **types_stack) -{ - return 0; -} +/** REST OF THE CODE **/ -static int argcheck_generic_noremove(struct types *types_stack, char expected) +static int translate_instr(struct translation *data, uint8_t wasm_opcode) { - char *name; - - name = - expected == VALTYPE_F64 ? "f64" : - expected == VALTYPE_F32 ? "f32" : - expected == VALTYPE_I64 ? "i64" : - "i32"; - - if (!types_stack) { - PRERR("Expected %s on stack, got nothing\n", name); - return -1; - } + struct typecheck *tc_routines; - if (types_stack->type != VALTYPE_I32) { - PRERR("Expected %s (0x%02hhx) on stack, got 0x%02hhx\n", - name, expected, types_stack->type); + if (!translation_routines[wasm_opcode]) { + PRERR("Unknown Wasm opcode: 0x%02x\n", wasm_opcode); return -1; } - return 0; -} - -static int argcheck_generic(struct types **types_stack, char expected) -{ - struct types *top_type; + tc_routines = typecheck_routines + wasm_opcode; - if (argcheck_generic_noremove(*types_stack, expected)) + if (tc_routines->argcheck(&data->types_stack) || + tc_routines->rescheck(&data->types_stack) || + translation_routines[wasm_opcode](data)) return -1; - top_type = *types_stack; - *types_stack = top_type->prev; - - if (*types_stack) - get_type(*types_stack); - - put_type(top_type); - return 0; } -static int _argcheck_i32(struct types **types_stack) +static int parse_blocktype(FILE *handle, struct resulttype *args, + struct resulttype *results, char *storage, + struct module *module) { - return argcheck_generic(types_stack, VALTYPE_I32); -} + int readval; + uint32_t typeidx; -static int _argcheck_i32_i32(struct types **types_stack) -{ - int i; + readval = fgetc(handle); - for (i = 0; i < 2; i++) { - if (argcheck_generic(types_stack, VALTYPE_I32)) - return -1; + if (readval == EOF) { + PRERR(MSG_EOF); + return -1; } - return 0; -} - -static int _argcheck_custom(struct types **types_stack) -{ - return 0; /* Translation function will handle argument checks */ -} - -/** DEFINE RESULT TYPECHECK FUNCTIONS **/ + if (readval == 0x40) { + /* Blocktype is empty (no arguments, no result values) */ + *args = (struct resulttype) {.count = 0, .types = NULL}; + *results = *args; + return 0; + } -static int _rescheck_empty(struct types **types_stack) -{ - return 0; -} + /* + * A nonnegative array index encoded as signed number in LEB + * shall have 0 as the second (most significant) bit of the first byte. + * Otherwise, it can't be array index, but might be a simple value type. + */ + if (readval & (1 << 6)) { + if (!is_valid_valtype(readval)) + goto fail; -static int rescheck_generic(struct types **types_stack, char returned) -{ - struct types *top; + *args = (struct resulttype) {.count = 0, .types = NULL}; + *storage = readval; + *results = (struct resulttype) {.count = 1, .types = storage}; + return 0; + } - top = malloc(sizeof(struct types)); + /* + * We know for sure it's a nonnegative number, we can just use leb_u32 + * decoding function (encoding as signed or unsigned is the same in this + * particular case). + */ + ungetc(readval, handle); - if (!top) { - PRERR(MSG_ALLOC_FAIL(sizeof(struct types))); - return -1; + if (leb_u32(handle, &typeidx)) { + PRERR(MSG_BAD_NUM); + goto fail; } - *top = (struct types) {.prev = *types_stack, .type = returned, .refs = 1}; - *types_stack = top; + if (typeidx <= module->functypes_count) { + PRERR(MSG_BAD_IDX("type index")); + goto fail; + } + *args = module->functypes[typeidx].args; + *results = module->functypes[typeidx].results; return 0; -} - -static int _rescheck_i32(struct types **types_stack) -{ - return rescheck_generic(types_stack, VALTYPE_I32); -} -static int _rescheck_custom(struct types **types_stack) -{ - return 0; +fail: + PRERR("Couldn't parse blocktype\n"); + return -1; } -/** DEFINE TYPECHECK FUNCTION POINTER ARRAY **/ - -/* Translate complex */ -#define TC(wasm_opcode, name, argtypes, restype) \ - [wasm_opcode] = {.argcheck = _argcheck_##argtypes, \ - .rescheck = _rescheck_##restype}, - -/* Translate Simple */ -#define TS(wasm_opcode, sm_instr, argtypes, restype) \ - TC(wasm_opcode, dummy, argtypes, restype) - -/* Translate load/store */ -#define TLS(wasm_opcode, sm_instr, argtypes, restype) \ - TC(wasm_opcode, dummy, argtypes, restype) - -struct typecheck { - int (*argcheck) (struct types **), (*rescheck) (struct types **); -}; - -static struct typecheck typecheck_routines[256] = { -#include "translate_xmacro.h" -}; - -#undef TS -#undef TLS -#undef TC - -/** DEFINE CUSTOM TYPECHECK FUNCTIONS **/ - -static int typecheck_call(struct translation *data, struct function *callee) +static struct target *add_target(struct module *module) { - uint32_t i; - - i = callee->type->args.count; + struct target *tgt; - while (i--) { - if (argcheck_generic(&data->types_stack, - callee->type->args.types[i])) - return -1; - } + tgt = malloc(sizeof(struct target)); - for (i = 0; i < callee->type->results.count; i++) { - if (rescheck_generic(&data->types_stack, - callee->type->results.types[i])) - return -1; + if (!tgt) { + PRERR(MSG_ALLOC_FAIL(sizeof(struct target))); + return NULL; } - return 0; + tgt->instr = NULL; + tgt->prev = module->targets; + module->targets = tgt; + return tgt; } -static int typecheck_local_get(struct translation *data, uint32_t localidx) +static struct label *add_label(struct translation *data, struct target *target, + struct resulttype *arity) { - uint32_t args_count = data->function->type->args.count; - char type = localidx < args_count ? - data->function->type->args.types[localidx] : - data->function->locals[localidx - args_count]; + struct label *lbl; - if (rescheck_generic(&data->types_stack, type)) - return -1; + lbl = malloc(sizeof(struct label)); - return 0; -} - -/** REST OF THE CODE **/ - -static int translate_instr(struct translation *data, uint8_t wasm_opcode) -{ - struct typecheck *tc_routines; - - if (!translation_routines[wasm_opcode]) { - PRERR("Unknown Wasm opcode: 0x%02x\n", wasm_opcode); - return -1; + if (!lbl) { + PRERR(MSG_ALLOC_FAIL(sizeof(struct label))); + return NULL; } - tc_routines = typecheck_routines + wasm_opcode; + lbl->prev = data->labels; + lbl->target = target; + lbl->arity = arity; + lbl->values_on_stack = data->labels ? data->labels->values_on_stack : 0; + lbl->values_on_stack += stack_size(data->types_stack); - if (tc_routines->argcheck(&data->types_stack) || - tc_routines->rescheck(&data->types_stack) || - translation_routines[wasm_opcode](data)) - return -1; + data->labels = lbl; - return 0; + return lbl; } static int translate_expr(struct translation *data, struct resulttype *args, struct resulttype *results, const struct end_markers *end_markers, - char *marker_found) + char *marker_found, bool continuation_at_start) { - struct types **tmp, *types_stack_rest; - uint32_t i; + struct target *continuation; + struct label *label = NULL; + struct types **tmp, *types_stack_rest = NULL; + uint32_t i, rescount = results ? results->count : 0; int wasm_opcode; + bool last_instruction_was_branch = false; + int retval = -1; + + continuation = add_target(data->module); + + if (!continuation) + goto fail; + + if (continuation_at_start) + continuation->instr = data->function->translated_body->prev; + + label = add_label(data, continuation, + continuation_at_start ? args : results); + + if (!label) + goto fail; tmp = &data->types_stack; i = args ? args->count : 0; while (i--) { if (argcheck_generic_noremove(*tmp, args->types[i])) - return -1; + goto fail; tmp = &(*tmp)->prev; } @@ -580,19 +735,40 @@ static int translate_expr(struct translation *data, struct resulttype *args, i = end_markers->count; while (i--) { - if (wasm_opcode == end_markers->codes[i]) { - if (marker_found) - *marker_found = wasm_opcode; - + if (wasm_opcode == end_markers->codes[i]) goto block_end; - } } if (translate_instr(data, wasm_opcode)) goto fail; + + /* WASM_BR_TABLE will also appear here once implemented */ + last_instruction_was_branch = + wasm_opcode == WASM_BR; } block_end: + if (marker_found) + *marker_found = wasm_opcode; + + if (!continuation_at_start) + continuation->instr = data->function->translated_body->prev; + + /* + * Types on stack don't seem to matter, if last instruction was an + * unconditional branch anyway. However, we need to make the types stack + * appear ok to our caller. + */ + if (last_instruction_was_branch) { + put_type(data->types_stack); + data->types_stack = NULL; + + for (i = 0; i < rescount; i++) { + if (rescheck_generic(&data->types_stack, + results->types[i])) + goto fail; + } + } tmp = &data->types_stack; i = results ? results->count : 0; @@ -610,13 +786,21 @@ block_end: } *tmp = types_stack_rest; - return 0; + types_stack_rest = NULL; + retval = 0; fail: - put_type(data->types_stack); - data->types_stack = types_stack_rest; + if (label) { + data->labels = label->prev; + free(label); + } - return -1; + if (types_stack_rest) { + put_type(data->types_stack); + data->types_stack = types_stack_rest; + } + + return retval; } int translate(FILE *handle, struct function *function, struct module *module) @@ -634,7 +818,8 @@ int translate(FILE *handle, struct function *function, struct module *module) struct translation data = {.handle = handle, .function = function, .module = module, - .types_stack = NULL}; + .types_stack = NULL, + .labels = NULL}; int retval = -1; if (locals_count + (uint64_t) args_count > STACK_TOP_ADDR * 4) { @@ -657,7 +842,7 @@ int translate(FILE *handle, struct function *function, struct module *module) /* actual function body */ if (translate_expr(&data, NULL, &function->type->results, - &function_end_markers, NULL)) + &function_end_markers, NULL, false)) goto fail; /* function epilogue */ diff --git a/tools/translate_xmacro.h b/tools/translate_xmacro.h index b271e75..f68f317 100644 --- a/tools/translate_xmacro.h +++ b/tools/translate_xmacro.h @@ -20,6 +20,7 @@ TLS(WASM_I32_STORE16, storew_p, i32_i32, empty) * another way and only check for the i32 condition value here. */ TC (WASM_IF, if, i32, custom) +TC (WASM_BR, br, custom, custom) TC (WASM_CALL, call, custom, custom) TC (WASM_LOCAL_GET, local_get, empty, custom) TC (WASM_I32_CONST, const, empty, i32) diff --git a/tools/wasm.h b/tools/wasm.h index bf85490..0c36c11 100644 --- a/tools/wasm.h +++ b/tools/wasm.h @@ -25,6 +25,7 @@ #define WASM_IF 0x04 #define WASM_ELSE 0x05 #define WASM_END 0x0B +#define WASM_BR 0x0C #define WASM_CALL 0x10 #define WASM_LOCAL_GET 0x20 |