diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2024-09-07 00:04:38 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi92@disroot.org> | 2025-11-06 14:38:40 +0100 |
| commit | 6d9d80362f9932bbc87e162b8ef7df06c73e27e1 (patch) | |
| tree | e3e228c63fe26f07503f226de7fb5086b3dc2286 /src/interp | |
| download | nanowasm-6d9d80362f9932bbc87e162b8ef7df06c73e27e1.tar.gz | |
First commit
Diffstat (limited to 'src/interp')
27 files changed, 1275 insertions, 0 deletions
diff --git a/src/interp/CMakeLists.txt b/src/interp/CMakeLists.txt new file mode 100644 index 0000000..c38da8c --- /dev/null +++ b/src/interp/CMakeLists.txt @@ -0,0 +1,21 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 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 + data_set.c + initexpr_set.c + ops.c + run.c + resume.c + start.c +) + +add_subdirectory(global) +add_subdirectory(linear) +add_subdirectory(mem) +add_subdirectory(routines) +add_subdirectory(stack) diff --git a/src/interp/data_set.c b/src/interp/data_set.c new file mode 100644 index 0000000..266852b --- /dev/null +++ b/src/interp/data_set.c @@ -0,0 +1,24 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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/opcodes.h> + +static const enum opcode opcodes[] = +{ + OP_END, + OP_I32_CONST, +}; + +const struct nwp_interp_set nwp_interp_data_set = +{ + opcodes, + sizeof opcodes / sizeof *opcodes +}; diff --git a/src/interp/global/CMakeLists.txt b/src/interp/global/CMakeLists.txt new file mode 100644 index 0000000..0f0e056 --- /dev/null +++ b/src/interp/global/CMakeLists.txt @@ -0,0 +1,11 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 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 + load.c + store.c +) diff --git a/src/interp/global/load.c b/src/interp/global/load.c new file mode 100644 index 0000000..26d8c8d --- /dev/null +++ b/src/interp/global/load.c @@ -0,0 +1,34 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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/global.h> +#include <nw/mem.h> +#include <nw/log.h> + +enum nw_state nwp_global_load(struct nw_interp *const i, + struct nw_sm_io *const io, const nw_varuint32 index) +{ + const struct nw_interp_cfg *const cfg = &i->cfg; + const size_t offset = index * sizeof (struct nw_global); + const enum nw_state n = nwp_mem_load(&cfg->global, io, offset, cfg->user); + + if (n == NW_FATAL) + { + static const char *const exc = "failed to load from global memory"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + } + + return n; +} diff --git a/src/interp/global/store.c b/src/interp/global/store.c new file mode 100644 index 0000000..91550d4 --- /dev/null +++ b/src/interp/global/store.c @@ -0,0 +1,34 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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/global.h> +#include <nw/interp.h> +#include <nw/mem.h> +#include <nw/log.h> + +enum nw_state nwp_global_store(struct nw_interp *const i, + struct nw_sm_io *const io, const nw_varuint32 index) +{ + const struct nw_interp_cfg *const cfg = &i->cfg; + const size_t offset = index * sizeof (struct nw_global); + const enum nw_state n = nwp_mem_store(&cfg->global, io, offset, cfg->user); + + if (n == NW_FATAL) + { + static const char *const exc = "failed to store into global memory"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + } + + return n; +} diff --git a/src/interp/initexpr_set.c b/src/interp/initexpr_set.c new file mode 100644 index 0000000..1e200f6 --- /dev/null +++ b/src/interp/initexpr_set.c @@ -0,0 +1,28 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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/opcodes.h> + +static const enum opcode opcodes[] = +{ + OP_NOP, + OP_END, + OP_I32_CONST, + OP_I64_CONST, + OP_F32_CONST, + OP_F64_CONST +}; + +const struct nwp_interp_set nwp_interp_initexpr_set = +{ + opcodes, + sizeof opcodes / sizeof *opcodes +}; diff --git a/src/interp/linear/CMakeLists.txt b/src/interp/linear/CMakeLists.txt new file mode 100644 index 0000000..0f0e056 --- /dev/null +++ b/src/interp/linear/CMakeLists.txt @@ -0,0 +1,11 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 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 + load.c + store.c +) diff --git a/src/interp/linear/load.c b/src/interp/linear/load.c new file mode 100644 index 0000000..afc6154 --- /dev/null +++ b/src/interp/linear/load.c @@ -0,0 +1,39 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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 <nanowasm/linear.h> +#include <nw/interp.h> +#include <nw/mem.h> +#include <nw/log.h> + +enum nw_state nwp_linear_load(struct nw_interp *const i, + struct nw_sm_io *const io, const unsigned long offset) +{ + const struct nw_interp_cfg *const cfg = &i->cfg; + const enum nw_state n = nwp_mem_load(&cfg->linear, io, offset, cfg->user); + + if (n == NW_FATAL) + { + static const char *const exc = "failed to load from linear memory"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + } + + return n; +} + +enum nw_state nw_linear_load(struct nw_inst *const i, + struct nw_sm_io *const io, const unsigned long offset) +{ + return nwp_linear_load(&i->interp, io, offset); +} diff --git a/src/interp/linear/store.c b/src/interp/linear/store.c new file mode 100644 index 0000000..6edffd9 --- /dev/null +++ b/src/interp/linear/store.c @@ -0,0 +1,40 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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 <nanowasm/linear.h> +#include <nw/linear.h> +#include <nw/interp.h> +#include <nw/mem.h> +#include <nw/log.h> + +enum nw_state nwp_linear_store(struct nw_interp *const i, + struct nw_sm_io *const io, const unsigned long offset) +{ + const struct nw_interp_cfg *const cfg = &i->cfg; + const enum nw_state n = nwp_mem_store(&cfg->linear, io, offset, cfg->user); + + if (n == NW_FATAL) + { + static const char *const exc = "failed to store into linear memory"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + } + + return n; +} + +enum nw_state nw_linear_store(struct nw_inst *const i, + struct nw_sm_io *const io, const unsigned long offset) +{ + return nwp_linear_store(&i->interp, io, offset); +} diff --git a/src/interp/mem/CMakeLists.txt b/src/interp/mem/CMakeLists.txt new file mode 100644 index 0000000..0f0e056 --- /dev/null +++ b/src/interp/mem/CMakeLists.txt @@ -0,0 +1,11 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 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 + load.c + store.c +) diff --git a/src/interp/mem/load.c b/src/interp/mem/load.c new file mode 100644 index 0000000..0ccf29b --- /dev/null +++ b/src/interp/mem/load.c @@ -0,0 +1,27 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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/mem.h> +#include <nw/log.h> + +enum nw_state nwp_mem_load(const struct nw_mem_cfg *const cfg, + struct nw_sm_io *const io, const nw_varuint32 offset, void *const user) +{ + void *const dst = (unsigned char *)io->buf + io->read; + const int n = cfg->load(offset + io->read, dst, io->n - io->read, user); + + if (n < 0) + return NW_FATAL; + else if ((io->read += n) >= io->n) + return NW_OK; + + return NW_AGAIN; +} diff --git a/src/interp/mem/store.c b/src/interp/mem/store.c new file mode 100644 index 0000000..cc58169 --- /dev/null +++ b/src/interp/mem/store.c @@ -0,0 +1,27 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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/mem.h> +#include <nw/log.h> + +enum nw_state nwp_mem_store(const struct nw_mem_cfg *const cfg, + struct nw_sm_io *const io, const nw_varuint32 offset, void *const user) +{ + const void *const src = (const unsigned char *)io->buf + io->read; + const int n = cfg->store(offset + io->read, src, io->n - io->read, user); + + if (n < 0) + return NW_FATAL; + else if ((io->read += n) >= io->n) + return NW_OK; + + return NW_AGAIN; +} diff --git a/src/interp/ops.c b/src/interp/ops.c new file mode 100644 index 0000000..6135f96 --- /dev/null +++ b/src/interp/ops.c @@ -0,0 +1,278 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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/opcodes.h> +#include <nw/ops.h> +#include <nw/interp.h> + +static void (*const ops[])(struct nw_interp *) = +{ + nwp_op_unreachable, /* OP_UNREACHABLE */ + nwp_op_nop, /* OP_NOP */ + nwp_op_block, /* OP_BLOCK */ + nwp_op_loop, /* OP_LOOP */ + NULL, /* OP_IF */ + NULL, /* OP_ELSE */ + NULL, + NULL, + NULL, + NULL, + NULL, + nwp_op_end, /* OP_END */ + nwp_op_br, /* OP_BR */ + nwp_op_br_if, /* OP_BR_IF */ + NULL, /* OP_BR_TABLE */ + nwp_op_return, /* OP_RETURN */ + nwp_op_call, /* OP_CALL */ + nwp_op_call_indirect, /* OP_CALL_INDIRECT */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + nwp_op_drop, /* OP_DROP */ + NULL, /* OP_SELECT */ + NULL, + NULL, + NULL, + NULL, + nwp_op_get_local, /* OP_GET_LOCAL */ + nwp_op_set_local, /* OP_SET_LOCAL */ + nwp_op_tee_local, /* OP_TEE_LOCAL */ + nwp_op_get_global, /* OP_GET_GLOBAL */ + nwp_op_set_global, /* OP_SET_GLOBAL */ + NULL, + NULL, + NULL, + nwp_op_i32_load, /* OP_I32_LOAD */ + NULL, /* OP_I64_LOAD */ + NULL, /* OP_F32_LOAD */ + NULL, /* OP_F64_LOAD */ + NULL, /* OP_I32_LOAD8_S */ + nwp_op_i32_load8_u, /* OP_I32_LOAD8_U */ + NULL, /* OP_I32_LOAD16_S */ + NULL, /* OP_I32_LOAD16_U */ + NULL, /* OP_I64_LOAD8_S */ + NULL, /* OP_I64_LOAD8_U */ + NULL, /* OP_I64_LOAD16_S */ + NULL, /* OP_I64_LOAD16_U */ + NULL, /* OP_I64_LOAD32_S */ + NULL, /* OP_I64_LOAD32_U */ + nwp_op_i32_store, /* OP_I32_STORE */ + nwp_op_i64_store, /* OP_I64_STORE */ + NULL, /* OP_F32_STORE */ + NULL, /* OP_F64_STORE */ + NULL, /* OP_I32_STORE8 */ + NULL, /* OP_I32_STORE16 */ + NULL, /* OP_I64_STORE8 */ + NULL, /* OP_I64_STORE16 */ + NULL, /* OP_I64_STORE32 */ + NULL, /* OP_CURRENT_MEMORY */ + NULL, /* OP_GROW_MEMORY */ + nwp_op_i32_const, /* OP_I32_CONST */ + nwp_op_i64_const, /* OP_I64_CONST */ + NULL, /* OP_F32_CONST */ + NULL, /* OP_F64_CONST */ + nwp_op_i32_eqz, /* OP_I32_EQZ */ + nwp_op_i32_eq, /* OP_I32_EQ */ + nwp_op_i32_ne, /* OP_I32_NE */ + nwp_op_i32_lt_s, /* OP_I32_LT_S */ + NULL, /* OP_I32_LT_U */ + NULL, /* OP_I32_GT_S */ + NULL, /* OP_I32_GT_U */ + NULL, /* OP_I32_LE_S */ + NULL, /* OP_I32_LE_U */ + nwp_op_i32_ge_s, /* OP_I32_GE_S */ + nwp_op_i32_ge_u, /* OP_I32_GE_U */ + NULL, /* OP_I64_EQZ */ + NULL, /* OP_I64_EQ */ + NULL, /* OP_I64_NE */ + NULL, /* OP_I64_LT_S */ + NULL, /* OP_I64_LT_U */ + NULL, /* OP_I64_GT_S */ + NULL, /* OP_I64_GT_U */ + NULL, /* OP_I64_LE_S */ + NULL, /* OP_I64_LE_U */ + NULL, /* OP_I64_GE_S */ + NULL, /* OP_I64_GE_U */ + NULL, /* OP_F32_EQ */ + NULL, /* OP_F32_NE */ + NULL, /* OP_F32_LT */ + NULL, /* OP_F32_GT */ + NULL, /* OP_F32_LE */ + NULL, /* OP_F32_GE */ + NULL, /* OP_F64_EQ */ + NULL, /* OP_F64_NE */ + NULL, /* OP_F64_LT */ + NULL, /* OP_F64_GT */ + NULL, /* OP_F64_LE */ + NULL, /* OP_F64_GE */ + NULL, /* OP_I32_CLZ */ + NULL, /* OP_I32_CTZ */ + NULL, /* OP_I32_POPCNT */ + nwp_op_i32_add, /* OP_I32_ADD */ + nwp_op_i32_sub, /* OP_I32_SUB */ + nwp_op_i32_mul, /* OP_I32_MUL */ + NULL, /* OP_I32_DIV_S */ + NULL, /* OP_I32_DIV_U */ + NULL, /* OP_I32_REM_S */ + NULL, /* OP_I32_REM_U */ + nwp_op_i32_and, /* OP_I32_AND */ + nwp_op_i32_or, /* OP_I32_OR */ + NULL, /* OP_I32_XOR */ + NULL, /* OP_I32_SHL */ + NULL, /* OP_I32_SHR_S */ + NULL, /* OP_I32_SHR_U */ + NULL, /* OP_I32_ROTL */ + NULL, /* OP_I32_ROTR */ + NULL, /* OP_I64_CLZ */ + NULL, /* OP_I64_CTZ */ + NULL, /* OP_I64_POPCNT */ + NULL, /* OP_I64_ADD */ + NULL, /* OP_I64_SUB */ + NULL, /* OP_I64_MUL */ + NULL, /* OP_I64_DIV_S */ + NULL, /* OP_I64_DIV_U */ + NULL, /* OP_I64_REM_S */ + NULL, /* OP_I64_REM_U */ + NULL, /* OP_I64_AND */ + NULL, /* OP_I64_OR */ + NULL, /* OP_I64_XOR */ + NULL, /* OP_I64_SHL */ + NULL, /* OP_I64_SHR_S */ + NULL, /* OP_I64_SHR_U */ + NULL, /* OP_I64_ROTL */ + NULL, /* OP_I64_ROTR */ + NULL, /* OP_F32_ABS */ + NULL, /* OP_F32_NEG */ + NULL, /* OP_F32_CEIL */ + NULL, /* OP_F32_FLOOR */ + NULL, /* OP_F32_TRUNC */ + NULL, /* OP_F32_NEAREST */ + NULL, /* OP_F32_SQRT */ + NULL, /* OP_F32_ADD */ + NULL, /* OP_F32_SUB */ + NULL, /* OP_F32_MUL */ + NULL, /* OP_F32_DIV */ + NULL, /* OP_F32_MIN */ + NULL, /* OP_F32_MAX */ + NULL, /* OP_F32_COPYSIGN */ + NULL, /* OP_F64_ABS */ + NULL, /* OP_F64_NEG */ + NULL, /* OP_F64_CEIL */ + NULL, /* OP_F64_FLOOR */ + NULL, /* OP_F64_TRUNC */ + NULL, /* OP_F64_NEAREST */ + NULL, /* OP_F64_SQRT */ + NULL, /* OP_F64_ADD */ + NULL, /* OP_F64_SUB */ + NULL, /* OP_F64_MUL */ + NULL, /* OP_F64_DIV */ + NULL, /* OP_F64_MIN */ + NULL, /* OP_F64_MAX */ + NULL, /* OP_F64_COPYSIGN */ + NULL, /* OP_I32_WRAP_I64 */ + NULL, /* OP_I32_TRUNC_S_F32 */ + NULL, /* OP_I32_TRUNC_U_F32 */ + NULL, /* OP_I32_TRUNC_S_F64 */ + NULL, /* OP_I32_TRUNC_U_F64 */ + NULL, /* OP_I64_EXTEND_S_I32 */ + NULL, /* OP_I64_EXTEND_U_I32 */ + NULL, /* OP_I64_TRUNC_S_F32 */ + NULL, /* OP_I64_TRUNC_U_F32 */ + NULL, /* OP_I64_TRUNC_S_F64 */ + NULL, /* OP_I64_TRUNC_U_F64 */ + NULL, /* OP_F32_CONVERT_S_I32 */ + NULL, /* OP_F32_CONVERT_U_I32 */ + NULL, /* OP_F32_CONVERT_S_I64 */ + NULL, /* OP_F32_CONVERT_U_I64 */ + NULL, /* OP_F32_DEMOTE_F64 */ + NULL, /* OP_F64_CONVERT_S_I32 */ + NULL, /* OP_F64_CONVERT_U_I32 */ + NULL, /* OP_F64_CONVERT_S_I64 */ + NULL, /* OP_F64_CONVERT_U_I64 */ + NULL, /* OP_F64_PROMOTE_F32 */ + NULL, /* OP_I32_REINTERPRET_F32 */ + NULL, /* OP_I64_REINTERPRET_F64 */ + NULL, /* OP_F32_REINTERPRET_I32 */ + NULL, /* OP_F64_REINTERPRET_I64 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL /* OP_MISC */ +}; + +const struct nwp_ops nwp_ops = +{ + ops, + sizeof ops / sizeof *ops +}; diff --git a/src/interp/resume.c b/src/interp/resume.c new file mode 100644 index 0000000..cf9ece2 --- /dev/null +++ b/src/interp/resume.c @@ -0,0 +1,20 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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 <nanowasm/types.h> +#include <nw/interp.h> + +void nwp_interp_resume(struct nw_interp *const i) +{ + if (i->set) + nwp_interp_limited(i); + else + nwp_interp_full(i); +} diff --git a/src/interp/routines/CMakeLists.txt b/src/interp/routines/CMakeLists.txt new file mode 100644 index 0000000..b27172b --- /dev/null +++ b/src/interp/routines/CMakeLists.txt @@ -0,0 +1,13 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 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 + execute.c + find_export.c + full.c + limited.c +) diff --git a/src/interp/routines/execute.c b/src/interp/routines/execute.c new file mode 100644 index 0000000..49711ae --- /dev/null +++ b/src/interp/routines/execute.c @@ -0,0 +1,83 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/opcodes.h> +#include <nw/ops.h> +#include <nw/routines.h> + +static enum nw_state execute(struct nw_interp *const i) +{ + struct nw_i_sm_b *const b = &i->sm.bytecode; + +#ifdef NW_LOG + nwp_log("opcode: %s, pc=%#lx\n", nwp_op_tostr(b->op), b->pc); +#endif + b->f(i); + i->fr.prev_op = b->op; + return NW_AGAIN; +} + +static enum nw_state repeat(struct nw_interp *const i) +{ + struct nw_i_sm_b *const b = &i->sm.bytecode; + struct nw_next *const next = &b->next; + const enum nw_state n = next->fn(next->user, next); + + if (n) + return n; + + return execute(i); +} + +static enum nw_state exec_pc(struct nw_interp *const i) +{ + struct nw_i_sm_b *const b = &i->sm.bytecode; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const long pc = b->pc; + + if (cfg->pc) + { + const enum nw_state n = cfg->pc(pc, &b->next, cfg->user); + + if (n == NW_FATAL) + { + static const char *const exc = "pc callback failed"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s, pc: %ld\n", exc, pc); +#endif + return NW_FATAL; + } + else if (n) + { + i->next = repeat; + return n; + } + } + + return execute(i); +} + +enum nw_state nwp_execute(struct nw_interp *const i) +{ + struct nw_i_sm_b *const b = &i->sm.bytecode; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&b->pc, cfg->user); + + if (n) + return n; + + b->pc -= sizeof b->op; + i->next = exec_pc; + return NW_AGAIN; +} diff --git a/src/interp/routines/find_export.c b/src/interp/routines/find_export.c new file mode 100644 index 0000000..effa8d1 --- /dev/null +++ b/src/interp/routines/find_export.c @@ -0,0 +1,229 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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 <nanowasm/types.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <string.h> + +static enum nw_state entry_loop(struct nw_interp *i); + +static enum nw_state get_index(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_exp *const e = &i->sm.export; + struct nw_sm_leb128 *const l = &e->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &e->index, cfg->user); + + if (n) + return n; + + i->next = e->next; + return NW_AGAIN; +} + +static enum nw_state get_kind(struct nw_interp *const i) +{ + unsigned char kind; + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_exp *const e = &i->sm.export; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &kind; + io.n = sizeof kind; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + else if (kind >= NW_KINDS) + { + static const char *const exc = "invalid export kind"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)kind); +#endif + return NW_FATAL; + } + + e->kind = kind; + i->next = get_index; + return NW_AGAIN; +} + +static enum nw_state skip_index(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_exp *const e = &i->sm.export; + struct nw_sm_leb128 *const l = &e->leb128; + nw_varuint32 index; + const enum nw_state n = nwp_varuint32(cfg, l, &index, cfg->user); + + if (n) + return n; + + e->entry_i++; + i->next = entry_loop; + return NW_AGAIN; +} + +static enum nw_state skip_kind(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + unsigned char b; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &b; + io.n = sizeof b; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + + i->next = skip_index; + return NW_AGAIN; +} + +static enum nw_state skip_field_str(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + const struct nw_i_sm_exp *const e = &i->sm.export; + const long offset = e->offset + (e->len - e->len_i); + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + + i->next = skip_kind; + return NW_AGAIN; +} + +static enum nw_state tell(struct nw_interp *const i) +{ + struct nw_i_sm_exp *const e = &i->sm.export; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&e->offset, cfg->user); + + if (n) + return n; + + i->next = skip_field_str; + return NW_AGAIN; +} + +static enum nw_state compare(struct nw_interp *const i) +{ + unsigned char b; + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_exp *const e = &i->sm.export; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &b; + io.n = sizeof b; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + else if (b != e->sym[e->len_i++]) + i->next = tell; + else if (e->len_i >= e->len) + i->next = get_kind; + + return NW_AGAIN; +} + +static enum nw_state get_len(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_exp *const e = &i->sm.export; + struct nw_sm_leb128 *const l = &e->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &e->len, cfg->user); + + if (n) + return n; + else if (e->len != strlen(e->sym)) + i->next = skip_field_str; + else + i->next = compare; + + return NW_AGAIN; +} + +static enum nw_state entry_loop(struct nw_interp *const i) +{ + struct nw_i_sm_exp *const e = &i->sm.export; + + if (e->entry_i >= e->count) + { + static const char *const exc = "failed to find symbol"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %s\n", exc, e->sym); +#endif + return NW_FATAL; + } + + e->len_i = 0; + i->next = get_len; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_exp *const e = &i->sm.export; + struct nw_sm_leb128 *const l = &e->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &e->count, cfg->user); + + if (n) + return n; + + i->next = entry_loop; + return NW_AGAIN; +} + +static enum nw_state seek(struct nw_interp *const i) +{ + const struct nw_mod *const m = i->cfg.m; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const long offset = m->sections[NW_SECTION_EXPORT]; + enum nw_state n; + + if (!offset) + { + static const char *const exc = "section not found"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %s\n", exc, "export"); +#endif + return NW_FATAL; + } + else if ((n = cfg->seek(offset, cfg->user))) + return n; + + i->next = get_count; + return NW_AGAIN; +} + +void nwp_find_export(struct nw_interp *const i, const char *const sym, + enum nw_state (*const next)(struct nw_interp *)) +{ + const struct nw_i_sm_exp e = {0}; + struct nw_i_sm_exp *const pe = &i->sm.export; + + *pe = e; + pe->sym = sym; + pe->next = next; + i->next = seek; +} diff --git a/src/interp/routines/full.c b/src/interp/routines/full.c new file mode 100644 index 0000000..c00f11f --- /dev/null +++ b/src/interp/routines/full.c @@ -0,0 +1,63 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/opcodes.h> +#include <nw/ops.h> +#include <nw/routines.h> + +static enum nw_state tell(struct nw_interp *const i) +{ + long offset; + struct nw_i_sm_b *const b = &i->sm.bytecode; + static const char *const exc = "invalid opcode"; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&offset, cfg->user); + + if (n) + return n; + + offset -= sizeof b->op; + +#ifdef NW_LOG + nwp_log("%s: %#x, offset=%#lx\n", exc, (unsigned)b->op, offset); +#endif + i->exception = exc; + return NW_FATAL; +} + +static enum nw_state get_bytecode(struct nw_interp *const i) +{ + struct nw_i_sm_b *const b = &i->sm.bytecode; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_io_read(cfg, &b->io, cfg->user); + + if (n) + return n; + else if (b->op >= nwp_ops.n || !(b->f = nwp_ops.ops[b->op])) + { + i->next = tell; + return NW_AGAIN; + } + + i->next = nwp_execute; + return NW_AGAIN; +} + +void nwp_interp_full(struct nw_interp *const i) +{ + struct nw_i_sm_b *const pb = &i->sm.bytecode, b = {0}; + + b.io.buf = &pb->op; + b.io.n = sizeof pb->op; + *pb = b; + i->next = get_bytecode; +} diff --git a/src/interp/routines/limited.c b/src/interp/routines/limited.c new file mode 100644 index 0000000..dc18ded --- /dev/null +++ b/src/interp/routines/limited.c @@ -0,0 +1,54 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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 <nanowasm/types.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <stddef.h> + +static enum nw_state get_bytecode(struct nw_interp *const in) +{ + const struct nw_io_cfg *const cfg = &in->cfg.io; + struct nw_i_sm_b *const b = &in->sm.bytecode; + const struct nwp_interp_set *const set = in->set; + static const char *const exc = "invalid opcode"; + const enum nw_state n = nwp_io_read(cfg, &b->io, cfg->user); + size_t i; + + if (n) + return n; + + for (i = 0; i < set->n; i++) + if (b->op == set->opcodes[i]) + { + b->f = nwp_ops.ops[b->op]; + in->next = nwp_execute; + return NW_AGAIN; + } + +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)b->op); +#endif + in->exception = exc; + return NW_FATAL; +} + +void nwp_interp_limited(struct nw_interp *const i) +{ + struct nw_i_sm_b *const b = &i->sm.bytecode; + struct nw_sm_io io = {0}; + + io.buf = &b->op; + io.n = sizeof b->op; + b->io = io; + i->next = get_bytecode; +} diff --git a/src/interp/run.c b/src/interp/run.c new file mode 100644 index 0000000..f5e80e6 --- /dev/null +++ b/src/interp/run.c @@ -0,0 +1,16 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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> + +enum nw_state nwp_interp_run(struct nw_interp *const i) +{ + return i->next(i); +} diff --git a/src/interp/stack/CMakeLists.txt b/src/interp/stack/CMakeLists.txt new file mode 100644 index 0000000..51c7cb5 --- /dev/null +++ b/src/interp/stack/CMakeLists.txt @@ -0,0 +1,14 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 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 + pop.c + push.c + ptr.c + read.c + write.c +) diff --git a/src/interp/stack/pop.c b/src/interp/stack/pop.c new file mode 100644 index 0000000..03ed689 --- /dev/null +++ b/src/interp/stack/pop.c @@ -0,0 +1,37 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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/log.h> +#include <nw/stack.h> + +enum nw_state nwp_stack_pop(struct nw_interp *const i, + struct nw_sm_io *const io) +{ + void *const dst = (unsigned char *)io->buf + io->read; + const struct nw_interp_cfg *const cfg = &i->cfg; + const struct nw_fifo_cfg *const fc = &cfg->stack; + const int n = fc->pop(dst, io->n - io->read, cfg->user); + + if (n < 0) + { + static const char *const exc = "failed to pop from stack"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + return NW_FATAL; + } + else if ((io->read += n) >= io->n) + return NW_OK; + + return NW_AGAIN; +} diff --git a/src/interp/stack/ptr.c b/src/interp/stack/ptr.c new file mode 100644 index 0000000..d5fa455 --- /dev/null +++ b/src/interp/stack/ptr.c @@ -0,0 +1,21 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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/stack.h> +#include <stddef.h> + +size_t nwp_stack_ptr(const struct nw_interp *const i) +{ + const struct nw_interp_cfg *const cfg = &i->cfg; + const struct nw_fifo_cfg *const fc = &cfg->stack; + + return fc->ptr(cfg->user); +} diff --git a/src/interp/stack/push.c b/src/interp/stack/push.c new file mode 100644 index 0000000..b8b48ce --- /dev/null +++ b/src/interp/stack/push.c @@ -0,0 +1,37 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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/log.h> +#include <nw/stack.h> + +enum nw_state nwp_stack_push(struct nw_interp *const i, + struct nw_sm_io *const io) +{ + const void *const src = (const unsigned char *)io->buf + io->read; + const struct nw_interp_cfg *const cfg = &i->cfg; + const struct nw_fifo_cfg *const fc = &cfg->stack; + const int n = fc->push(src, io->n - io->read, cfg->user); + + if (n < 0) + { + static const char *const exc = "failed to push to stack"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + return NW_FATAL; + } + else if ((io->read += n) >= io->n) + return NW_OK; + + return NW_AGAIN; +} diff --git a/src/interp/stack/read.c b/src/interp/stack/read.c new file mode 100644 index 0000000..8db6bcc --- /dev/null +++ b/src/interp/stack/read.c @@ -0,0 +1,37 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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/log.h> +#include <nw/stack.h> + +enum nw_state nwp_stack_read(struct nw_interp *const i, + struct nw_sm_io *const io, const size_t offset) +{ + void *const dst = (unsigned char *)io->buf + io->read; + const struct nw_interp_cfg *const cfg = &i->cfg; + const struct nw_fifo_cfg *const fc = &cfg->stack; + const int n = fc->read(offset + io->read, dst, io->n - io->read, cfg->user); + + if (n < 0) + { + static const char *const exc = "failed to read from stack"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + return NW_FATAL; + } + else if ((io->read += n) >= io->n) + return NW_OK; + + return NW_AGAIN; +} diff --git a/src/interp/stack/write.c b/src/interp/stack/write.c new file mode 100644 index 0000000..73e749e --- /dev/null +++ b/src/interp/stack/write.c @@ -0,0 +1,38 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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/log.h> +#include <nw/stack.h> + +enum nw_state nwp_stack_write(struct nw_interp *const i, + struct nw_sm_io *const io, const size_t offset) +{ + const void *const src = (const unsigned char *)io->buf + io->read; + const struct nw_interp_cfg *const cfg = &i->cfg; + const struct nw_fifo_cfg *const fc = &cfg->stack; + const int n = fc->write(offset + io->read, src, io->n - io->read, + cfg->user); + + if (n < 0) + { + static const char *const exc = "failed to write to stack"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + return NW_FATAL; + } + else if ((io->read += n) >= io->n) + return NW_OK; + + return NW_AGAIN; +} diff --git a/src/interp/start.c b/src/interp/start.c new file mode 100644 index 0000000..2bade41 --- /dev/null +++ b/src/interp/start.c @@ -0,0 +1,28 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 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 <nanowasm/types.h> +#include <nw/interp.h> + +int nwp_interp_start(struct nw_interp *const i, + const struct nw_interp_cfg *const cfg, + const struct nwp_interp_set *const set) +{ + const struct nw_interp in = {0}; + const struct nw_mod_out *const mout = &cfg->m->out; + + *i = in; + i->cfg = *cfg; + i->set = set; + i->table.n_pages = mout->table.initial; + i->linear.n_pages = mout->linear.initial; + nwp_interp_resume(i); + return 0; +} |
