diff options
Diffstat (limited to 'src/routines/section/code.c')
| -rw-r--r-- | src/routines/section/code.c | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/src/routines/section/code.c b/src/routines/section/code.c new file mode 100644 index 0000000..dd50c41 --- /dev/null +++ b/src/routines/section/code.c @@ -0,0 +1,271 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2024 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/opcodes.h> +#include <nw/ops.h> +#include <nw/routines.h> +#include <inttypes.h> + +static enum nw_state local_loop(struct nw_mod *); +static enum nw_state get_opcode(struct nw_mod *); +static enum nw_state entry_loop(struct nw_mod *); + +static void (*const checks[])(struct nw_mod *) = +{ + [OP_UNREACHABLE] = nwp_op_check_no_immediate, + [OP_NOP] = nwp_op_check_no_immediate, + [OP_END] = nwp_op_check_end, + [OP_BLOCK] = nwp_op_check_block, + [OP_BR_IF] = nwp_op_check_br_if, + [OP_RETURN] = nwp_op_check_no_immediate, + [OP_CALL] = nwp_op_check_call, + [OP_GET_LOCAL] = nwp_op_check_local_index, + [OP_SET_LOCAL] = nwp_op_check_local_index, + [OP_TEE_LOCAL] = nwp_op_check_local_index, + [OP_GET_GLOBAL] = nwp_op_check_global_index, + [OP_SET_GLOBAL] = nwp_op_check_global_index, + [OP_I32_LOAD] = nwp_op_check_memory_immediate, + [OP_I32_STORE] = nwp_op_check_memory_immediate, + [OP_I64_STORE] = nwp_op_check_memory_immediate, + [OP_F32_STORE] = nwp_op_check_memory_immediate, + [OP_F64_STORE] = nwp_op_check_memory_immediate, + [OP_I32_CONST] = nwp_op_check_i32_const, + [OP_I64_CONST] = nwp_op_check_i64_const, + [OP_I32_SUB] = nwp_op_check_no_immediate +}; + +static enum nw_state get_post_offset(struct nw_mod *const m) +{ + long offset; + struct nw_sm_c *const c = &m->sm.code; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&offset, cfg->user); + + if (n) + return n; + + const unsigned long consumed = offset - c->op_off; + + if (consumed && consumed >= c->rem) + { + LOG("%s: parameters exceed expected body length\n", __func__); + return NW_FATAL; + } + + c->rem -= consumed; + m->next = get_opcode; + return NW_AGAIN; +} + +static enum nw_state get_op_offset(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&c->op_off, cfg->user); + void (*const fn)(struct nw_mod *) = checks[c->op]; + + if (n) + return n; + else if (!fn) + { + LOG("%s: invalid opcode %#" PRIx8 "\n", __func__, c->op); + return NW_FATAL; + } + + c->next = get_post_offset; + fn(m); + return NW_AGAIN; +} + +static enum nw_state check_exit(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_c_fn *const fn = &c->fn; + + if (fn->blocks) + { + LOG("%s: mismatched number of blocks in function %lu\n", __func__, + (unsigned long)c->entry_i); + return NW_FATAL; + } + + c->entry_i++; + m->next = entry_loop; + return NW_AGAIN; +} + +static enum nw_state get_opcode(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + + if (!c->rem) + return check_exit(m); + + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_io io = {.buf = &c->op, .n = sizeof c->op}; + const enum nw_state n = nwp_io_read(cfg, &io); + + if (n) + return n; + else if (c->rem == 1 && c->op != OP_END) + { + LOG("%s: unexpected opcode %#" PRIx8 " at body end\n", + __func__, c->op); + return NW_FATAL; + } + else if (c->op >= sizeof checks / sizeof *checks) + { + LOG("%s: invalid opcode %#" PRIx8 "\n", __func__, c->op); + return NW_FATAL; + } + + c->rem--; + m->next = get_op_offset; + return NW_AGAIN; +} + +static enum nw_state get_rem(struct nw_mod *const m) +{ + long offset; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&offset, cfg->user); + + if (n) + return n; + + struct nw_sm_c *const c = &m->sm.code; + const unsigned long consumed = offset - c->start; + + if (consumed >= c->body_size) + { + LOG("%s: exceeded function body size\n", __func__); + return NW_FATAL; + } + + c->rem = c->body_size - consumed; + m->next = get_opcode; + return NW_AGAIN; +} + +static enum nw_state get_type(struct nw_mod *const m) +{ + nw_varint7 type; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varint7(cfg, l, &type); + + if (n) + return n; + + c->fn.local_i++; + m->next = local_loop; + return NW_AGAIN; +} + +static enum nw_state get_group_count(struct nw_mod *const m) +{ + nw_varuint32 group_count; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &group_count); + + if (n) + return n; + + c->fn.local_total += group_count; + m->next = get_type; + return NW_AGAIN; +} + +static enum nw_state local_loop(struct nw_mod *const m) +{ + const struct nw_sm_c_fn *const fn = &m->sm.code.fn; + + m->next = fn->local_i < fn->local_count ? get_group_count : get_rem; + return NW_AGAIN; +} + +static enum nw_state get_local_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_c_fn *const fn = &c->fn; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &fn->local_count); + + if (n) + return n; + + m->next = fn->local_count ? local_loop : get_rem; + return NW_AGAIN; +} + +static enum nw_state get_start(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&c->start, cfg->user); + + if (n) + return n; + + m->next = get_local_count; + return NW_AGAIN; +} + +static enum nw_state get_body_size(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &c->body_size); + + if (n) + return n; + + m->next = get_start; + return NW_AGAIN; +} + +static enum nw_state entry_loop(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + + c->fn = (const struct nw_sm_c_fn){.blocks = 1}; + m->next = c->entry_i < c->count ? get_body_size : nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &c->count); + + if (n) + return n; + + m->next = entry_loop; + return NW_AGAIN; +} + +void nwp_section_code(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + + m->next = get_count; + *c = (const struct nw_sm_c){0}; +} |
