aboutsummaryrefslogtreecommitdiff
path: root/src/op
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2023-11-26 22:43:30 +0100
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2024-04-21 01:51:24 +0200
commitf25b015e5b668028c34974bbb22faa4105c26690 (patch)
tree28f2b08c17b3585d06694ad74004d0617eadb785 /src/op
downloadnanowasm-sync-f25b015e5b668028c34974bbb22faa4105c26690.tar.gz
First commit
Diffstat (limited to 'src/op')
-rw-r--r--src/op/CMakeLists.txt13
-rw-r--r--src/op/call/CMakeLists.txt11
-rw-r--r--src/op/call/call.c79
-rw-r--r--src/op/call/call_indirect.c49
-rw-r--r--src/op/constants/CMakeLists.txt13
-rw-r--r--src/op/constants/f32_const.c38
-rw-r--r--src/op/constants/f64_const.c38
-rw-r--r--src/op/constants/i32_const.c51
-rw-r--r--src/op/constants/i64_const.c38
-rw-r--r--src/op/control_flow/CMakeLists.txt20
-rw-r--r--src/op/control_flow/block.c75
-rw-r--r--src/op/control_flow/br.c38
-rw-r--r--src/op/control_flow/br_if.c95
-rw-r--r--src/op/control_flow/br_table.c56
-rw-r--r--src/op/control_flow/else.c21
-rw-r--r--src/op/control_flow/end.c24
-rw-r--r--src/op/control_flow/if.c36
-rw-r--r--src/op/control_flow/loop.c36
-rw-r--r--src/op/control_flow/nop.c22
-rw-r--r--src/op/control_flow/return.c30
-rw-r--r--src/op/control_flow/unreachable.c25
-rw-r--r--src/op/memory/CMakeLists.txt12
-rw-r--r--src/op/memory/current_memory.c44
-rw-r--r--src/op/memory/i32_load.c76
-rw-r--r--src/op/memory/i32_store.c125
-rw-r--r--src/op/numeric/CMakeLists.txt10
-rw-r--r--src/op/numeric/i32_sub.c66
-rw-r--r--src/op/variable_access/CMakeLists.txt14
-rw-r--r--src/op/variable_access/get_global.c100
-rw-r--r--src/op/variable_access/get_local.c112
-rw-r--r--src/op/variable_access/set_global.c38
-rw-r--r--src/op/variable_access/set_local.c124
-rw-r--r--src/op/variable_access/tee_local.c38
33 files changed, 1567 insertions, 0 deletions
diff --git a/src/op/CMakeLists.txt b/src/op/CMakeLists.txt
new file mode 100644
index 0000000..7bf27de
--- /dev/null
+++ b/src/op/CMakeLists.txt
@@ -0,0 +1,13 @@
+# 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/.
+
+add_subdirectory(call)
+add_subdirectory(constants)
+add_subdirectory(control_flow)
+add_subdirectory(memory)
+add_subdirectory(numeric)
+add_subdirectory(variable_access)
diff --git a/src/op/call/CMakeLists.txt b/src/op/call/CMakeLists.txt
new file mode 100644
index 0000000..527f35d
--- /dev/null
+++ b/src/op/call/CMakeLists.txt
@@ -0,0 +1,11 @@
+# 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/.
+
+target_sources(${PROJECT_NAME} PRIVATE
+ call.c
+ call_indirect.c
+)
diff --git a/src/op/call/call.c b/src/op/call/call.c
new file mode 100644
index 0000000..19355ca
--- /dev/null
+++ b/src/op/call/call.c
@@ -0,0 +1,79 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/search.h>
+#include <nw/sections.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+static int call(struct nw_interp *const i, const varuint32 function_index)
+{
+ FILE *const f = i->f;
+ const struct nw_mod *const m = i->cfg.m;
+ struct nw_frame fr = {0};
+ struct search_fn fn;
+
+ /* TODO: treat import functions. */
+ if (search_fn(function_index, m, f, &fn))
+ {
+ LOG("%s: search_fn failed\n", __func__);
+ return -1;
+ }
+ else if (section_type_push(f, m, fn.index, &fr))
+ {
+ LOG("%s: section_type_push failed\n", __func__);
+ return -1;
+ }
+ else if (section_code_push(f, fn.start, &fr))
+ {
+ LOG("%s: section_code_push failed\n", __func__);
+ return -1;
+ }
+ else if (interp_push(i, &fr))
+ {
+ LOG("%s: interp_push failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 function_index;
+
+ if (varuint32_read(f, &function_index))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+ else if (i && call(i, function_index))
+ {
+ LOG("%s: call failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_call(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_call(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/call/call_indirect.c b/src/op/call/call_indirect.c
new file mode 100644
index 0000000..bae8a57
--- /dev/null
+++ b/src/op/call/call_indirect.c
@@ -0,0 +1,49 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stdio.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 type_index;
+ varuint1 reserved;
+
+ if (varuint32_read(f, &type_index))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+ else if (varuint1_read(f, &reserved))
+ {
+ LOG("%s: varuint1_read failed\n", __func__);
+ return 1;
+ }
+ else if (reserved)
+ {
+ LOG("%s: unexpected non-zero reserved value %u\n",
+ __func__, (unsigned)reserved);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_call_indirect(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_call_indirect(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/constants/CMakeLists.txt b/src/op/constants/CMakeLists.txt
new file mode 100644
index 0000000..e5cc8c5
--- /dev/null
+++ b/src/op/constants/CMakeLists.txt
@@ -0,0 +1,13 @@
+# 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/.
+
+target_sources(${PROJECT_NAME} PRIVATE
+ f32_const.c
+ f64_const.c
+ i32_const.c
+ i64_const.c
+)
diff --git a/src/op/constants/f32_const.c b/src/op/constants/f32_const.c
new file mode 100644
index 0000000..c0c51b9
--- /dev/null
+++ b/src/op/constants/f32_const.c
@@ -0,0 +1,38 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 value;
+
+ if (varuint32_read(f, &value))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_f32_const(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_f32_const(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/constants/f64_const.c b/src/op/constants/f64_const.c
new file mode 100644
index 0000000..d88398c
--- /dev/null
+++ b/src/op/constants/f64_const.c
@@ -0,0 +1,38 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint64 value;
+
+ if (varuint64_read(f, &value))
+ {
+ LOG("%s: varuint64_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_f64_const(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_f64_const(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/constants/i32_const.c b/src/op/constants/i32_const.c
new file mode 100644
index 0000000..19161d3
--- /dev/null
+++ b/src/op/constants/i32_const.c
@@ -0,0 +1,51 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <nw/interp.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+static int set_value(struct nw_interp *const i, const int32_t src)
+{
+ return interp_stack_push(i, &src, sizeof src);
+}
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varint32 value;
+
+ if (varint32_read(f, &value))
+ {
+ LOG("%s: varint32_read failed\n", __func__);
+ return -1;
+ }
+ else if (i && set_value(i, value))
+ {
+ LOG("%s: set_value failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_i32_const(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_i32_const(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/constants/i64_const.c b/src/op/constants/i64_const.c
new file mode 100644
index 0000000..53f4237
--- /dev/null
+++ b/src/op/constants/i64_const.c
@@ -0,0 +1,38 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varint64 value;
+
+ if (varint64_read(f, &value))
+ {
+ LOG("%s: varint32_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_i64_const(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_i64_const(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/control_flow/CMakeLists.txt b/src/op/control_flow/CMakeLists.txt
new file mode 100644
index 0000000..832f09e
--- /dev/null
+++ b/src/op/control_flow/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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/.
+
+target_sources(${PROJECT_NAME} PRIVATE
+ block.c
+ br.c
+ br_if.c
+ br_table.c
+ else.c
+ end.c
+ if.c
+ loop.c
+ nop.c
+ return.c
+ unreachable.c
+)
diff --git a/src/op/control_flow/block.c b/src/op/control_flow/block.c
new file mode 100644
index 0000000..56f72f1
--- /dev/null
+++ b/src/op/control_flow/block.c
@@ -0,0 +1,75 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+static int push(struct nw_interp *const i, FILE *const f)
+{
+ const long pc = ftell(f);
+
+ if (pc < 0)
+ {
+ LOG("%s: ftell(3): %s\n", __func__, strerror(errno));
+ i->exception = "I/O error";
+ return -1;
+ }
+
+ struct nw_frame *const fp = i->fp;
+ struct nw_block *const p = interp_stackptr(i);
+ const struct nw_block b =
+ {
+ .pc = pc,
+ .prev = fp->last_block
+ };
+
+ if (interp_stack_push(i, &b, sizeof b))
+ {
+ LOG("%s: interp_stack_push failed\n", __func__);
+ return -1;
+ }
+
+ fp->last_block = p;
+ return 0;
+}
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varint7 sig;
+
+ if (varint7_read(f, &sig))
+ {
+ LOG("%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+ else if (i && push(i, f))
+ {
+ LOG("%s: push failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_block(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_block(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/control_flow/br.c b/src/op/control_flow/br.c
new file mode 100644
index 0000000..df3aacb
--- /dev/null
+++ b/src/op/control_flow/br.c
@@ -0,0 +1,38 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 relative_depth;
+
+ if (varuint32_read(f, &relative_depth))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_br(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_br(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/control_flow/br_if.c b/src/op/control_flow/br_if.c
new file mode 100644
index 0000000..25bfdee
--- /dev/null
+++ b/src/op/control_flow/br_if.c
@@ -0,0 +1,95 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/types.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+static int branch(struct nw_interp *const i, const varuint32 relative_depth)
+{
+ const struct nw_block *b = i->fp->last_block;
+ static const char exc[] = "relative depth and block count mismatch";
+
+ for (varuint32 d = 0; b && d < relative_depth; d++)
+ b = b->prev;
+
+ if (!b)
+ {
+ LOG("%s: %s\n", __func__, exc);
+ i->exception = exc;
+ return -1;
+ }
+
+ if (fseek(i->f, b->pc, SEEK_SET))
+ {
+ LOG("%s: fseek(3): %s\n", __func__, strerror(errno));
+ i->exception = "I/O error";
+ return -1;
+ }
+
+ return 0;
+}
+
+static bool cond(struct nw_interp *const i)
+{
+ int32_t value;
+
+ if (interp_stack_pop(i, &value, sizeof value))
+ {
+ LOG("%s: interp_stack_pop failed\n", __func__);
+ return -1;
+ }
+
+ return value;
+}
+
+static int br_if(struct nw_interp *const i, const varuint32 relative_depth)
+{
+ if (cond(i) && branch(i, relative_depth))
+ {
+ LOG("%s: branch failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 relative_depth;
+
+ if (varuint32_read(f, &relative_depth))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+ else if (i && br_if(i, relative_depth))
+ {
+ LOG("%s: br_if failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_br_if(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_br_if(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/control_flow/br_table.c b/src/op/control_flow/br_table.c
new file mode 100644
index 0000000..837cdcd
--- /dev/null
+++ b/src/op/control_flow/br_table.c
@@ -0,0 +1,56 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 target_count, default_target;
+
+ if (varuint32_read(f, &target_count))
+ {
+ LOG("%s: varuint32_read target_count failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < target_count; i++)
+ {
+ varuint32 target_table;
+
+ if (varuint32_read(f, &target_table))
+ {
+ LOG("%s: varuint32_read target_table failed\n",
+ __func__);
+ return -1;
+ }
+ }
+
+ if (varuint32_read(f, &default_target))
+ {
+ LOG("%s: varuint32_read default_target failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_br_table(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_br_table(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/control_flow/else.c b/src/op/control_flow/else.c
new file mode 100644
index 0000000..fb839f0
--- /dev/null
+++ b/src/op/control_flow/else.c
@@ -0,0 +1,21 @@
+/*
+ * 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+
+int op_else(struct nw_interp *const i)
+{
+ return -1;
+}
+
+int check_else(FILE *const f)
+{
+ return 0;
+}
diff --git a/src/op/control_flow/end.c b/src/op/control_flow/end.c
new file mode 100644
index 0000000..5f193d9
--- /dev/null
+++ b/src/op/control_flow/end.c
@@ -0,0 +1,24 @@
+/*
+ * 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <stdbool.h>
+
+int op_end(struct nw_interp *const i)
+{
+ /* TODO: this is not correct. end can appear anywhere in a function. */
+ i->exit = true;
+ return 0;
+}
+
+int check_end(FILE *const f)
+{
+ return 0;
+}
diff --git a/src/op/control_flow/if.c b/src/op/control_flow/if.c
new file mode 100644
index 0000000..1d5e838
--- /dev/null
+++ b/src/op/control_flow/if.c
@@ -0,0 +1,36 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varint7 sig;
+
+ if (varint7_read(f, &sig))
+ {
+ LOG("%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_if(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_if(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/control_flow/loop.c b/src/op/control_flow/loop.c
new file mode 100644
index 0000000..5020f23
--- /dev/null
+++ b/src/op/control_flow/loop.c
@@ -0,0 +1,36 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varint7 sig;
+
+ if (varint7_read(f, &sig))
+ {
+ LOG("%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_loop(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_loop(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/control_flow/nop.c b/src/op/control_flow/nop.c
new file mode 100644
index 0000000..356b7b0
--- /dev/null
+++ b/src/op/control_flow/nop.c
@@ -0,0 +1,22 @@
+/*
+ * 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+
+int op_nop(struct nw_interp *const i)
+{
+ return 0;
+}
+
+int check_nop(FILE *const f)
+{
+ return 0;
+}
diff --git a/src/op/control_flow/return.c b/src/op/control_flow/return.c
new file mode 100644
index 0000000..abddea7
--- /dev/null
+++ b/src/op/control_flow/return.c
@@ -0,0 +1,30 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/interp.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <stdio.h>
+
+int op_return(struct nw_interp *const i)
+{
+ if (interp_pop(i))
+ {
+ LOG("%s: interp_pop failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int check_return(FILE *const f)
+{
+ return 0;
+}
diff --git a/src/op/control_flow/unreachable.c b/src/op/control_flow/unreachable.c
new file mode 100644
index 0000000..8f1982c
--- /dev/null
+++ b/src/op/control_flow/unreachable.c
@@ -0,0 +1,25 @@
+/*
+ * 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 <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+int op_unreachable(struct nw_interp *const i)
+{
+ i->exception = "Unreachable instruction";
+ i->exit = true;
+ return 1;
+}
+
+int check_unreachable(FILE *const f)
+{
+ return 0;
+}
diff --git a/src/op/memory/CMakeLists.txt b/src/op/memory/CMakeLists.txt
new file mode 100644
index 0000000..ca4ba5a
--- /dev/null
+++ b/src/op/memory/CMakeLists.txt
@@ -0,0 +1,12 @@
+# 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/.
+
+target_sources(${PROJECT_NAME} PRIVATE
+ current_memory.c
+ i32_load.c
+ i32_store.c
+)
diff --git a/src/op/memory/current_memory.c b/src/op/memory/current_memory.c
new file mode 100644
index 0000000..c67d425
--- /dev/null
+++ b/src/op/memory/current_memory.c
@@ -0,0 +1,44 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint1 reserved;
+
+ if (varuint1_read(f, &reserved))
+ {
+ LOG("%s: varuint1_read failed\n", __func__);
+ return 1;
+ }
+ else if (reserved)
+ {
+ LOG("%s: unexpected non-zero reserved value %u\n",
+ __func__, (unsigned)reserved);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_current_memory(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_current_memory(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/memory/i32_load.c b/src/op/memory/i32_load.c
new file mode 100644
index 0000000..04dbec7
--- /dev/null
+++ b/src/op/memory/i32_load.c
@@ -0,0 +1,76 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int load(struct nw_interp *const i, const varuint32 flags,
+ const varuint32 offset)
+{
+ enum {ALIGN = 2};
+ int32_t value;
+
+ if (flags != ALIGN)
+ {
+ LOG("%s: expected 32-bit alignment (%d), got %lu\n",
+ __func__, ALIGN, (unsigned long)flags);
+ i->exception = "unaligned access";
+ return -1;
+ }
+ else if (interp_heap_load(i, offset, &value, sizeof value))
+ {
+ LOG("%s: interp_heap_load failed\n", __func__);
+ return -1;
+ }
+ else if (interp_stack_push(i, &value, sizeof value))
+ {
+ LOG("%s: interp_stack_push failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varint32 flags, offset;
+
+ if (varint32_read(f, &flags))
+ {
+ LOG("%s: varint32_read flags failed\n", __func__);
+ return 1;
+ }
+ else if (varint32_read(f, &offset))
+ {
+ LOG("%s: varint32_read offset failed\n", __func__);
+ return 1;
+ }
+ else if (i && load(i, flags, offset))
+ {
+ LOG("%s: load failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_i32_load(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_i32_load(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/memory/i32_store.c b/src/op/memory/i32_store.c
new file mode 100644
index 0000000..212cd86
--- /dev/null
+++ b/src/op/memory/i32_store.c
@@ -0,0 +1,125 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <nw/interp.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int store(struct nw_interp *const i, const varuint32 flags,
+ const varuint32 offset)
+{
+ enum {ALIGN = 2};
+
+ if (flags != ALIGN)
+ {
+ LOG("%s: expected 32-bit alignment (%d), got %lu\n",
+ __func__, ALIGN, (unsigned long)flags);
+ i->exception = "unaligned access";
+ return -1;
+ }
+
+ int32_t value, addr;
+
+ if (interp_stack_pop(i, &value, sizeof value))
+ {
+ LOG("%s: inter_stack_pop value failed\n", __func__);
+ return -1;
+ }
+ else if (interp_stack_pop(i, &addr, sizeof addr))
+ {
+ LOG("%s: inter_stack_pop addr failed\n", __func__);
+ return -1;
+ }
+ else if (offset > SIZE_MAX - addr)
+ {
+ static const char exc[] = "offset address overflow";
+
+ LOG("%s: %s\n", __func__, exc);
+ i->exception = exc;
+ return -1;
+ }
+ else if (interp_heap_store(i, offset + addr, &value, sizeof value))
+ {
+ LOG("%s: interp_heap_store failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+#if 0
+ stack_i -= sizeof addr;
+
+ if (max < addr || offset >= max - addr)
+ {
+ LOG("%s: heap overflow\n", __func__);
+ i->exception = "heap overflow";
+ return -1;
+ }
+
+ const size_t raddr = offset + addr;
+
+ if (raddr % sizeof addr)
+ {
+ LOG("%s: unaligned memory address\n", __func__);
+ i->exception = "unaligned access";
+ return -1;
+ }
+ else if (stack_i < sizeof (uint32_t))
+ {
+ LOG("%s: stack underflow (value)\n", __func__);
+ i->exception = "stack underflow";
+ return -1;
+ }
+
+ const int32_t value = htoni32(*stackptr);
+ void *dst = &((char *)i->cfg.heap.buf)[raddr];
+
+ *(int32_t *)dst = value;
+ stack_i -= sizeof addr;
+ i->stack_i = stack_i;
+ return 0;
+#endif
+}
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 flags, offset;
+
+ if (varuint32_read(f, &flags))
+ {
+ LOG("%s: varint32_read flags failed\n", __func__);
+ return -1;
+ }
+ else if (varuint32_read(f, &offset))
+ {
+ LOG("%s: varint32_read offset failed\n", __func__);
+ return -1;
+ }
+ else if (i && store(i, flags, offset))
+ {
+ LOG("%s: store failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_i32_store(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_i32_store(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/numeric/CMakeLists.txt b/src/op/numeric/CMakeLists.txt
new file mode 100644
index 0000000..a259506
--- /dev/null
+++ b/src/op/numeric/CMakeLists.txt
@@ -0,0 +1,10 @@
+# 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/.
+
+target_sources(${PROJECT_NAME} PRIVATE
+ i32_sub.c
+)
diff --git a/src/op/numeric/i32_sub.c b/src/op/numeric/i32_sub.c
new file mode 100644
index 0000000..c3d1352
--- /dev/null
+++ b/src/op/numeric/i32_sub.c
@@ -0,0 +1,66 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/types.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+/* Inspired by "INT32-C. Ensure that operations on signed integers do
+ * not result in overflow". */
+static bool signed_overflow(const int32_t a, const int32_t b)
+{
+ return (b > 0 && a < INT32_MIN + b)
+ || (b < 0 && a > INT32_MAX + b);
+}
+
+int op_i32_sub(struct nw_interp *const i)
+{
+ int32_t op1, op2;
+
+ if (interp_stack_pop(i, &op2, sizeof op2))
+ {
+ LOG("%s: interp_stack_pop %s failed\n", __func__, "op2");
+ return -1;
+ }
+ else if (interp_stack_pop(i, &op1, sizeof op1))
+ {
+ LOG("%s: interp_stack_pop %s failed\n", __func__, "op1");
+ return -1;
+ }
+ else if (signed_overflow(op2, op1))
+ {
+ LOG("%s: signed integer overflow (op1=%" PRIi32 ", op2=%" PRIi32 ")\n",
+ __func__, op1, op2);
+ i->exception = "signed integer overflow";
+ return 1;
+ }
+
+ const int32_t result = op2 - op1;
+
+ if (interp_stack_push(i, &result, sizeof result))
+ {
+ LOG("%s: interp_stack_push failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int check_i32_sub(FILE *const f)
+{
+ return 0;
+}
diff --git a/src/op/variable_access/CMakeLists.txt b/src/op/variable_access/CMakeLists.txt
new file mode 100644
index 0000000..314b5bf
--- /dev/null
+++ b/src/op/variable_access/CMakeLists.txt
@@ -0,0 +1,14 @@
+# 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/.
+
+target_sources(${PROJECT_NAME} PRIVATE
+ get_global.c
+ get_local.c
+ set_global.c
+ set_local.c
+ tee_local.c
+)
diff --git a/src/op/variable_access/get_global.c b/src/op/variable_access/get_global.c
new file mode 100644
index 0000000..d38f2d0
--- /dev/null
+++ b/src/op/variable_access/get_global.c
@@ -0,0 +1,100 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/interp.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+static int get_i32(struct nw_interp *const i, const void *const src)
+{
+ return interp_stack_push(i, src, sizeof (int32_t));
+}
+
+static int get_i64(struct nw_interp *const i, const void *const src)
+{
+ return interp_stack_push(i, src, sizeof (int64_t));
+}
+
+static int get_f32(struct nw_interp *const i, const void *const src)
+{
+ return interp_stack_push(i, src, sizeof (float));
+}
+
+static int get_f64(struct nw_interp *const i, const void *const src)
+{
+ return interp_stack_push(i, src, sizeof (double));
+}
+
+static int get_global(struct nw_interp *const i, const varuint32 global_index)
+{
+ struct nw_gframe *gfr;
+ varuint32 idx;
+
+ for (idx = global_index, gfr = i->gfp; gfr; gfr = gfr->next)
+ if (idx == global_index)
+ break;
+
+ if (!gfr)
+ {
+ LOG("%s: cannot access global variable %lu\n", __func__,
+ (unsigned long)global_index);
+ i->exception = "global variable index out of bounds";
+ return -1;
+ }
+
+ static int (*const get[])(struct nw_interp *, const void *) =
+ {
+ [VALUE_TYPE_I32] = get_i32,
+ [VALUE_TYPE_I64] = get_i64,
+ [VALUE_TYPE_F32] = get_f32,
+ [VALUE_TYPE_F64] = get_f64,
+ };
+
+ if (get[gfr->type](i, gfr + 1))
+ {
+ LOG("%s: get type %s failed\n", __func__, value_type_tostr(gfr->type));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 global_index;
+
+ if (varuint32_read(f, &global_index))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+ else if (i && get_global(i, global_index))
+ {
+ LOG("%s: get_global failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_get_global(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_get_global(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/variable_access/get_local.c b/src/op/variable_access/get_local.c
new file mode 100644
index 0000000..745a2e7
--- /dev/null
+++ b/src/op/variable_access/get_local.c
@@ -0,0 +1,112 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <nw/interp.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+static int get_i32(struct nw_interp *const i, const void *const data,
+ const varuint32 idx)
+{
+ const int32_t *const src = (const int32_t *)data + idx;
+
+ return interp_stack_push(i, src, sizeof (*src));
+}
+
+static int get_i64(struct nw_interp *const i, const void *const data,
+ const varuint32 idx)
+{
+ const int64_t *const src = (const int64_t *)data + idx;
+
+ return interp_stack_push(i, src, sizeof (*src));
+}
+
+static int get_f32(struct nw_interp *const i, const void *const data,
+ const varuint32 idx)
+{
+ const float *const src = (const float *)data + idx;
+
+ return interp_stack_push(i, src, sizeof (*src));
+}
+
+static int get_f64(struct nw_interp *const i, const void *const data,
+ const varuint32 idx)
+{
+ const double *const src = (const double *)data + idx;
+
+ return interp_stack_push(i, src, sizeof (*src));
+}
+
+static int get_local(const varuint32 local_index, struct nw_interp *const i)
+{
+ struct nw_frame *fr;
+ varuint32 idx;
+
+ for (idx = local_index, fr = i->fp; fr; fr = fr->next)
+ if (idx < fr->n_locals)
+ break;
+
+ if (!fr)
+ {
+ LOG("%s: cannot access local variable %lu\n", __func__,
+ (unsigned long)local_index);
+ i->exception = "local variable index out of bounds";
+ return -1;
+ }
+
+ static int (*const get[])(struct nw_interp *, const void *, varuint32) =
+ {
+ [VALUE_TYPE_I32] = get_i32,
+ [VALUE_TYPE_I64] = get_i64,
+ [VALUE_TYPE_F32] = get_f32,
+ [VALUE_TYPE_F64] = get_f64,
+ };
+
+ if (get[fr->local_type](i, fr + 1, idx))
+ {
+ LOG("%s: get type %d failed\n", __func__, fr->local_type);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 local_index;
+
+ if (varuint32_read(f, &local_index))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+ else if (i && get_local(local_index, i))
+ {
+ LOG("%s: get_local failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_get_local(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_get_local(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/variable_access/set_global.c b/src/op/variable_access/set_global.c
new file mode 100644
index 0000000..d1229c5
--- /dev/null
+++ b/src/op/variable_access/set_global.c
@@ -0,0 +1,38 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 global_index;
+
+ if (varuint32_read(f, &global_index))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_set_global(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_set_global(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/variable_access/set_local.c b/src/op/variable_access/set_local.c
new file mode 100644
index 0000000..8cf6440
--- /dev/null
+++ b/src/op/variable_access/set_local.c
@@ -0,0 +1,124 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+static int set_i32(struct nw_interp *const i, void *const data,
+ const varuint32 idx)
+{
+ const void *const stackptr = (const char *)i->cfg.stack.buf + i->stack_i;
+ const int32_t *const src = (const int32_t *)stackptr - 1;
+ int32_t *const dst = (int32_t *)data + idx;
+
+ *dst = *src;
+ return 0;
+}
+
+static int set_i64(struct nw_interp *const i, void *const data,
+ const varuint32 idx)
+{
+ const void *const stackptr = (const char *)i->cfg.stack.buf + i->stack_i;
+ const int64_t *const src = (const int64_t *)stackptr - 1;
+ int64_t *const dst = (int64_t *)data + idx;
+
+ *dst = *src;
+ return 0;
+}
+
+static int set_f32(struct nw_interp *const i, void *const data,
+ const varuint32 idx)
+{
+ const void *const stackptr = (const char *)i->cfg.stack.buf + i->stack_i;
+ const float *const src = (const float *)stackptr - 1;
+ float *const dst = (float *)data + idx;
+
+ *dst = *src;
+ return 0;
+}
+
+static int set_f64(struct nw_interp *const i, void *const data,
+ const varuint32 idx)
+{
+ const void *const stackptr = (const char *)i->cfg.stack.buf + i->stack_i;
+ const double *const src = (const double *)stackptr - 1;
+ double *const dst = (double *)data + idx;
+
+ *dst = *src;
+ return 0;
+}
+
+static int set_local(const varuint32 local_index, struct nw_interp *const i)
+{
+ struct nw_frame *fr;
+ varuint32 idx;
+
+ for (idx = local_index, fr = i->fp; fr; fr = fr->next)
+ if (idx < fr->n_locals)
+ break;
+
+ if (!fr)
+ {
+ LOG("%s: cannot access local variable %lu\n", __func__,
+ (unsigned long)local_index);
+ i->exception = "local variable index out of bounds";
+ return -1;
+ }
+
+ static int (*const set[])(struct nw_interp *, void *, varuint32) =
+ {
+ [VALUE_TYPE_I32] = set_i32,
+ [VALUE_TYPE_I64] = set_i64,
+ [VALUE_TYPE_F32] = set_f32,
+ [VALUE_TYPE_F64] = set_f64,
+ };
+
+ if (set[fr->local_type](i, fr + 1, idx))
+ {
+ LOG("%s: set type %d failed\n", __func__, fr->local_type);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 local_index;
+
+ if (varuint32_read(f, &local_index))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+ else if (i && set_local(local_index, i))
+ {
+ LOG("%s: set_local failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_set_local(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_set_local(FILE *const f)
+{
+ return op(NULL, f);
+}
diff --git a/src/op/variable_access/tee_local.c b/src/op/variable_access/tee_local.c
new file mode 100644
index 0000000..00d7832
--- /dev/null
+++ b/src/op/variable_access/tee_local.c
@@ -0,0 +1,38 @@
+/*
+ * 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 <nw/log.h>
+#include <nw/ops.h>
+#include <nanowasm/nw.h>
+#include <nw/types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(struct nw_interp *const i, FILE *const f)
+{
+ varuint32 local_index;
+
+ if (varuint32_read(f, &local_index))
+ {
+ LOG("%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_tee_local(struct nw_interp *const i)
+{
+ return op(i, i->f);
+}
+
+int check_tee_local(FILE *const f)
+{
+ return op(NULL, f);
+}