aboutsummaryrefslogtreecommitdiff
path: root/src/routines/section/code.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/routines/section/code.c')
-rw-r--r--src/routines/section/code.c271
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};
+}