diff --git a/CMakeLists.txt b/CMakeLists.txt index 5365a291b2..4bc2ae9dd4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -179,8 +179,8 @@ set(ZIG_STD_DEST "${ZIG_LIB_DIR}/std") set(ZIG_CONFIG_H_OUT "${CMAKE_BINARY_DIR}/config.h") set(ZIG_CONFIG_ZIG_OUT "${CMAKE_BINARY_DIR}/config.zig") -set(STAGE1_SOURCES - "${CMAKE_SOURCE_DIR}/stage1/zig1.c" +set(ZIG_WASM2C_SOURCES + "${CMAKE_SOURCE_DIR}/stage1/wasm2c.c" "${CMAKE_SOURCE_DIR}/stage1/zstd/lib/decompress/huf_decompress.c" "${CMAKE_SOURCE_DIR}/stage1/zstd/lib/decompress/zstd_ddict.c" "${CMAKE_SOURCE_DIR}/stage1/zstd/lib/decompress/zstd_decompress.c" @@ -709,30 +709,53 @@ target_link_libraries(zigcpp LINK_PUBLIC ) if(MSVC) - set(ZIG1_COMPILE_FLAGS "/std:c99") - set(ZIG2_COMPILE_FLAGS "/std:c99") + set(ZIG_WASM2C_COMPILE_FLAGS "/std:c99 /O2") + set(ZIG1_COMPILE_FLAGS "/std:c99 /Os") + set(ZIG2_COMPILE_FLAGS "/std:c99 /O0") set(ZIG2_LINK_FLAGS "/STACK:16777216") else() + set(ZIG_WASM2C_COMPILE_FLAGS "-std=c99 -O2") set(ZIG1_COMPILE_FLAGS "-std=c99") set(ZIG2_COMPILE_FLAGS "-std=c99") set(ZIG2_LINK_FLAGS "-Wl,-z,stack-size=0x10000000") endif() -add_executable(zig1 ${STAGE1_SOURCES}) -set_target_properties(zig1 PROPERTIES COMPILE_FLAGS ${ZIG1_COMPILE_FLAGS}) +string(TOLOWER "${CMAKE_HOST_SYSTEM_PROCESSOR}-${CMAKE_HOST_SYSTEM_NAME}" HOST_TARGET_TRIPLE) +set(ZIG1_WASM_SOURCE "${CMAKE_SOURCE_DIR}/stage1/zig1.wasm.zst") +set(ZIG1_C_SOURCE "${CMAKE_BINARY_DIR}/zig1.c") +set(ZIG2_C_SOURCE "${CMAKE_BINARY_DIR}/zig2.c") +set(ZIG_COMPILER_RT_C_SOURCE "${CMAKE_BINARY_DIR}/compiler_rt.c") + +add_executable(zig-wasm2c ${ZIG_WASM2C_SOURCES}) +set_target_properties(zig-wasm2c PROPERTIES COMPILE_FLAGS ${ZIG_WASM2C_COMPILE_FLAGS}) +target_include_directories(zig-wasm2c PUBLIC "${CMAKE_SOURCE_DIR}/stage1/zstd/lib") +target_compile_definitions(zig-wasm2c PRIVATE ZSTD_DISABLE_ASM) + +add_custom_command( + OUTPUT "${ZIG1_C_SOURCE}" + COMMAND zig-wasm2c "${ZIG1_WASM_SOURCE}" "${ZIG1_C_SOURCE}" + DEPENDS zig-wasm2c "${ZIG1_WASM_SOURCE}" + COMMENT STATUS "Converting ${ZIG1_WASM_SOURCE} to ${ZIG1_C_SOURCE}" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" +) + +add_executable(zig1 ${ZIG1_C_SOURCE} "${CMAKE_SOURCE_DIR}/stage1/wasi.c") +set_target_properties(zig1 PROPERTIES + COMPILE_FLAGS ${ZIG1_COMPILE_FLAGS} + LINK_FLAGS ${ZIG2_LINK_FLAGS}) target_link_libraries(zig1 LINK_PUBLIC m) target_include_directories(zig1 PUBLIC "${CMAKE_SOURCE_DIR}/stage1/zstd/lib") target_compile_definitions(zig1 PRIVATE ZSTD_DISABLE_ASM) -set(ZIG2_C_SOURCE "${CMAKE_BINARY_DIR}/zig2.c") set(ZIG1_WASM_ZST_SOURCE "${CMAKE_SOURCE_DIR}/stage1/zig1.wasm.zst") set(BUILD_ZIG2_ARGS "${CMAKE_SOURCE_DIR}/lib" - "${CMAKE_BINARY_DIR}" - zig2 - "${ZIG1_WASM_ZST_SOURCE}" build-exe src/main.zig -ofmt=c -lc -OReleaseSmall + --name zig2 -femit-bin="${ZIG2_C_SOURCE}" + --pkg-begin build_options "${CMAKE_BINARY_DIR}/config.zig" --pkg-end + -target "${HOST_TARGET_TRIPLE}" + --color on ) add_custom_command( @@ -743,14 +766,14 @@ add_custom_command( WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" ) -set(ZIG_COMPILER_RT_C_SOURCE "${CMAKE_BINARY_DIR}/compiler_rt.c") set(BUILD_COMPILER_RT_ARGS "${CMAKE_SOURCE_DIR}/lib" - "${CMAKE_BINARY_DIR}" - compiler_rt - "${CMAKE_SOURCE_DIR}/stage1/zig1.wasm.zst" build-obj lib/compiler_rt.zig -ofmt=c -OReleaseSmall + --name compiler_rt -femit-bin="${ZIG_COMPILER_RT_C_SOURCE}" + --pkg-begin build_options "${CMAKE_BINARY_DIR}/config.zig" --pkg-end + -target "${HOST_TARGET_TRIPLE}" + --color on ) add_custom_command( diff --git a/stage1/FuncGen.h b/stage1/FuncGen.h new file mode 100644 index 0000000000..11bd4198f0 --- /dev/null +++ b/stage1/FuncGen.h @@ -0,0 +1,164 @@ +#ifndef FUNC_GEN_H +#define FUNC_GEN_H + +#include "panic.h" +#include "wasm.h" + +#include +#include +#include +#include +#include + +struct Block { + uint32_t type; + uint32_t label; + uint32_t stack_i; +}; + +struct FuncGen { + int8_t *type; + uint32_t *stack; + struct Block *block; + uint32_t type_i; + uint32_t stack_i; + uint32_t block_i; + uint32_t type_len; + uint32_t stack_len; + uint32_t block_len; +}; + +static void FuncGen_init(struct FuncGen *self) { + memset(self, 0, sizeof(struct FuncGen)); +} + +static void FuncGen_reset(struct FuncGen *self) { + self->type_i = 0; + self->stack_i = 0; + self->block_i = 0; +} + +static void FuncGen_free(struct FuncGen *self) { + free(self->block); + free(self->stack); + free(self->type); +} + +static void FuncGen_outdent(struct FuncGen *self, FILE *out) { + for (uint32_t i = 0; i < self->block_i; i += 1) fputs(" ", out); +} + +static void FuncGen_indent(struct FuncGen *self, FILE *out) { + FuncGen_outdent(self, out); + fputs(" ", out); +} + +static void FuncGen_cont(struct FuncGen *self, FILE *out) { + FuncGen_indent(self, out); + fputs(" ", out); +} + +static uint32_t FuncGen_localAlloc(struct FuncGen *self, int8_t type) { + if (self->type_i == self->type_len) { + self->type_len += 10; + self->type_len *= 2; + self->type = realloc(self->type, sizeof(int8_t) * self->type_len); + if (self->type == NULL) panic("out of memory"); + } + uint32_t local_i = self->type_i; + self->type[local_i] = type; + self->type_i += 1; + return local_i; +} + +static uint32_t FuncGen_localDeclare(struct FuncGen *self, FILE *out, enum WasmValType val_type) { + uint32_t local_i = FuncGen_localAlloc(self, (int8_t)val_type); + fprintf(out, "%s l%" PRIu32, WasmValType_toC(val_type), local_i); + return local_i; +} + +static enum WasmValType FuncGen_localType(const struct FuncGen *self, uint32_t local_idx) { + return self->type[local_idx]; +} + +static void FuncGen_stackPush(struct FuncGen *self, FILE *out, enum WasmValType val_type) { + if (self->stack_i == self->stack_len) { + self->stack_len += 10; + self->stack_len *= 2; + self->stack = realloc(self->stack, sizeof(uint32_t) * self->stack_len); + if (self->stack == NULL) panic("out of memory"); + } + FuncGen_indent(self, out); + fputs("const ", out); + self->stack[self->stack_i] = FuncGen_localDeclare(self, out, val_type); + self->stack_i += 1; + fputs(" = ", out); +} + +static uint32_t FuncGen_stackAt(const struct FuncGen *self, uint32_t stack_idx) { + return self->stack[self->stack_i - 1 - stack_idx]; +} + +static uint32_t FuncGen_stackPop(struct FuncGen *self) { + self->stack_i -= 1; + return self->stack[self->stack_i]; +} + +static void FuncGen_label(struct FuncGen *self, FILE *out, uint32_t label) { + FuncGen_indent(self, out); + fprintf(out, "goto l%" PRIu32 ";\n", label); + FuncGen_outdent(self, out); + fprintf(out, "l%" PRIu32 ":;\n", label); +} + +static void FuncGen_blockBegin(struct FuncGen *self, FILE *out, enum WasmOpcode kind, int64_t type) { + if (self->block_i == self->block_len) { + self->block_len += 10; + self->block_len *= 2; + self->block = realloc(self->block, sizeof(struct Block) * self->block_len); + if (self->block == NULL) panic("out of memory"); + } + uint32_t label = FuncGen_localAlloc(self, type < 0 ? ~(int8_t)kind : (int8_t)kind); + FuncGen_indent(self, out); + if (kind == WasmOpcode_if) fprintf(out, "if (l%" PRIu32 ") ", FuncGen_stackPop(self)); + fputs("{\n", out); + self->block[self->block_i].type = type < 0 ? ~type : type; + self->block[self->block_i].label = label; + self->block[self->block_i].stack_i = self->stack_i; + self->block_i += 1; + if (kind == WasmOpcode_loop) FuncGen_label(self, out, label); +} + +static enum WasmOpcode FuncGen_blockKind(const struct FuncGen *self, uint32_t label_idx) { + int8_t kind = self->type[self->block[self->block_i - 1 - label_idx].label]; + return (enum WasmOpcode)(kind < 0 ? ~kind : kind); +} + +static int64_t FuncGen_blockType(const struct FuncGen *self, uint32_t label_idx) { + struct Block *block = &self->block[self->block_i - 1 - label_idx]; + return self->type[block->label] < 0 ? ~(int64_t)block->type : (int64_t)block->type; +} + +static uint32_t FuncGen_blockLabel(const struct FuncGen *self, uint32_t label_idx) { + return self->block[self->block_i - 1 - label_idx].label; +} + +static void FuncGen_blockEnd(struct FuncGen *self, FILE *out) { + enum WasmOpcode kind = FuncGen_blockKind(self, 0); + uint32_t label = FuncGen_blockLabel(self, 0); + if (kind != WasmOpcode_loop) FuncGen_label(self, out, label); + self->block_i -= 1; + FuncGen_indent(self, out); + fputs("}\n", out); + if (self->stack_i != self->block[self->block_i].stack_i) { + FuncGen_indent(self, out); + fprintf(out, "// stack mismatch %u != %u\n", self->stack_i, self->block[self->block_i].stack_i); + } + self->stack_i = self->block[self->block_i].stack_i; +} + +static bool FuncGen_done(const struct FuncGen *self) { + return self->block_i == 0; +} + +#endif /* FUNC_GEN_H */ diff --git a/stage1/InputStream.h b/stage1/InputStream.h new file mode 100644 index 0000000000..36de3d6c7b --- /dev/null +++ b/stage1/InputStream.h @@ -0,0 +1,241 @@ +#ifndef INPUT_STREAM_H +#define INPUT_STREAM_H + +#include "panic.h" +#include "wasm.h" + +#include + +#include +#include +#include +#include +#include +#include + +struct InputStream { + FILE *stream; + ZSTD_DStream *ds; + ZSTD_outBuffer out; + ZSTD_inBuffer in; + size_t pos; +}; + +static void InputStream_open(struct InputStream *self, const char *path) { + self->stream = fopen(path, "rb"); + if (self->stream == NULL) panic("unable to open input file"); + self->ds = ZSTD_createDStream(); + if (self->ds == NULL) panic("unable to create zstd context"); + size_t in_size = ZSTD_initDStream(self->ds); + if (ZSTD_isError(in_size)) panic(ZSTD_getErrorName(in_size)); + self->out.size = ZSTD_DStreamOutSize(); + self->out.dst = malloc(self->out.size + ZSTD_DStreamInSize()); + if (self->out.dst == NULL) panic("unable to allocate input buffers"); + self->out.pos = 0; + self->in.src = (const char *)self->out.dst + self->out.size; + self->in.size = fread((void *)self->in.src, 1, in_size, self->stream); + self->in.pos = 0; + self->pos = 0; +} + +static void InputStream_close(struct InputStream *self) { + free(self->out.dst); + ZSTD_freeDStream(self->ds); + fclose(self->stream); +} + +static bool InputStream_atEnd(struct InputStream *self) { + while (self->pos >= self->out.pos) { + self->out.pos = 0; + self->pos = 0; + size_t in_size = ZSTD_decompressStream(self->ds, &self->out, &self->in); + if (ZSTD_isError(in_size)) panic(ZSTD_getErrorName(in_size)); + if (self->in.pos >= self->in.size) { + size_t max_in_size = ZSTD_DStreamInSize(); + if (in_size > max_in_size) in_size = max_in_size; + self->in.size = fread((void *)self->in.src, 1, in_size, self->stream); + self->in.pos = 0; + if (self->in.pos >= self->in.size) return true; + } + } + return false; +} + +static uint8_t InputStream_readByte(struct InputStream *self) { + if (InputStream_atEnd(self)) panic("unexpected end of input stream"); + uint8_t value = ((uint8_t *)self->out.dst)[self->pos]; + self->pos += 1; + return value; +} + +static uint32_t InputStream_readLittle_u32(struct InputStream *self) { + uint32_t value = 0; + value |= (uint32_t)InputStream_readByte(self) << 0; + value |= (uint32_t)InputStream_readByte(self) << 8; + value |= (uint32_t)InputStream_readByte(self) << 16; + value |= (uint32_t)InputStream_readByte(self) << 24; + return value; +} + +static uint64_t InputStream_readLittle_u64(struct InputStream *self) { + uint64_t value = 0; + value |= (uint64_t)InputStream_readByte(self) << 0; + value |= (uint64_t)InputStream_readByte(self) << 8; + value |= (uint64_t)InputStream_readByte(self) << 16; + value |= (uint64_t)InputStream_readByte(self) << 24; + value |= (uint64_t)InputStream_readByte(self) << 32; + value |= (uint64_t)InputStream_readByte(self) << 40; + value |= (uint64_t)InputStream_readByte(self) << 48; + value |= (uint64_t)InputStream_readByte(self) << 56; + return value; +} + +static float InputStream_readLittle_f32(struct InputStream *self) { + uint32_t value = InputStream_readLittle_u32(self); + float result; + memcpy(&result, &value, sizeof(result)); + return result; +} + +static double InputStream_readLittle_f64(struct InputStream *self) { + uint64_t value = InputStream_readLittle_u64(self); + double result; + memcpy(&result, &value, sizeof(result)); + return result; +} + +static uint32_t InputStream_readLeb128_u32(struct InputStream *self) { + uint32_t value = 0; + uint8_t shift = 0; + uint8_t byte; + do { + byte = InputStream_readByte(self); + assert(shift < 32); + value |= (uint32_t)(byte & 0x7F) << shift; + shift += 7; + } while (byte & 0x80); + return value; +} + +static int32_t InputStream_readLeb128_i32(struct InputStream *self) { + uint32_t value = 0; + uint8_t shift = 0; + uint8_t byte; + do { + byte = InputStream_readByte(self); + assert(shift < 64); + value |= (uint32_t)(byte & 0x7F) << shift; + shift += 7; + } while (byte & 0x80); + if (shift < 32) { + uint32_t mask = -((uint32_t)1 << shift); + if (byte & 0x40) value |= mask; else value &= ~mask; + } + return (int32_t)value; +} + +static int64_t InputStream_readLeb128_u64(struct InputStream *self) { + uint64_t value = 0; + uint8_t shift = 0; + uint8_t byte; + do { + byte = InputStream_readByte(self); + assert(shift < 64); + value |= (uint64_t)(byte & 0x7F) << shift; + shift += 7; + } while (byte & 0x80); + return value; +} + +static int64_t InputStream_readLeb128_i64(struct InputStream *self) { + uint64_t value = 0; + uint8_t shift = 0; + uint8_t byte; + do { + byte = InputStream_readByte(self); + assert(shift < 64); + value |= (uint64_t)(byte & 0x7F) << shift; + shift += 7; + } while (byte & 0x80); + if (shift < 64) { + uint64_t mask = -((uint64_t)1 << shift); + if (byte & 0x40) value |= mask; else value &= ~mask; + } + return (int64_t)value; +} + +static char *InputStream_readName(struct InputStream *self) { + uint32_t len = InputStream_readLeb128_u32(self); + char *name = malloc(len + 1); + if (name == NULL) panic("out of memory"); + for (uint32_t i = 0; i < len; ) { + if (InputStream_atEnd(self)) panic("unexpected end of input stream"); + size_t remaining = self->out.pos - self->pos; + if (remaining > len - i) remaining = len - i; + memcpy(&name[i], &((char *)self->out.dst)[self->pos], remaining); + i += remaining; + self->pos += remaining; + } + name[len] = '\0'; + return name; +} + +static void InputStream_skipBytes(struct InputStream *self, size_t len) { + for (size_t i = 0; i < len; ) { + if (InputStream_atEnd(self)) panic("unexpected end of input stream"); + size_t remaining = self->out.pos - self->pos; + if (remaining > len - i) remaining = len - i; + i += remaining; + self->pos += remaining; + } +} + +static uint32_t InputStream_skipToSection(struct InputStream *self, uint8_t expected_id) { + while (true) { + uint8_t id = InputStream_readByte(self); + uint32_t size = InputStream_readLeb128_u32(self); + if (id == expected_id) return size; + InputStream_skipBytes(self, size); + } +} + +struct ResultType { + uint32_t len; + int8_t types[1]; +}; +static struct ResultType *InputStream_readResultType(struct InputStream *self) { + uint32_t len = InputStream_readLeb128_u32(self); + struct ResultType *result_type = malloc(offsetof(struct ResultType, types) + sizeof(int8_t) * len); + if (result_type == NULL) panic("out of memory"); + result_type->len = len; + for (uint32_t i = 0; i < len; i += 1) { + int64_t val_type = InputStream_readLeb128_i64(self); + switch (val_type) { + case WasmValType_i32: case WasmValType_i64: + case WasmValType_f32: case WasmValType_f64: + break; + + default: panic("unsupported valtype"); + } + result_type->types[i] = val_type; + } + return result_type; +} + +struct Limits { + uint32_t min; + uint32_t max; +}; +static struct Limits InputStream_readLimits(struct InputStream *self) { + struct Limits limits; + uint8_t kind = InputStream_readByte(self); + limits.min = InputStream_readLeb128_u32(self); + switch (kind) { + case 0x00: limits.max = UINT32_MAX; break; + case 0x01: limits.max = InputStream_readLeb128_u32(self); break; + default: panic("unsupported limit kind"); + } + return limits; +} + +#endif /* INPUT_STREAM_H */ diff --git a/stage1/panic.h b/stage1/panic.h new file mode 100644 index 0000000000..7a8fc5f450 --- /dev/null +++ b/stage1/panic.h @@ -0,0 +1,12 @@ +#ifndef PANIC_H +#define PANIC_H + +#include +#include + +static void panic(const char *reason) { + fprintf(stderr, "%s\n", reason); + abort(); +} + +#endif /* PANIC_H */ diff --git a/stage1/wasi.c b/stage1/wasi.c new file mode 100644 index 0000000000..8ff04d5ef4 --- /dev/null +++ b/stage1/wasi.c @@ -0,0 +1,948 @@ +#include +#include +#include +#include +#include +#include + +#include "panic.h" + +#define LOG_TRACE 0 + +enum wasi_errno { + wasi_errno_success = 0, + wasi_errno_2big = 1, + wasi_errno_acces = 2, + wasi_errno_addrinuse = 3, + wasi_errno_addrnotavail = 4, + wasi_errno_afnosupport = 5, + wasi_errno_again = 6, + wasi_errno_already = 7, + wasi_errno_badf = 8, + wasi_errno_badmsg = 9, + wasi_errno_busy = 10, + wasi_errno_canceled = 11, + wasi_errno_child = 12, + wasi_errno_connaborted = 13, + wasi_errno_connrefused = 14, + wasi_errno_connreset = 15, + wasi_errno_deadlk = 16, + wasi_errno_destaddrreq = 17, + wasi_errno_dom = 18, + wasi_errno_dquot = 19, + wasi_errno_exist = 20, + wasi_errno_fault = 21, + wasi_errno_fbig = 22, + wasi_errno_hostunreach = 23, + wasi_errno_idrm = 24, + wasi_errno_ilseq = 25, + wasi_errno_inprogress = 26, + wasi_errno_intr = 27, + wasi_errno_inval = 28, + wasi_errno_io = 29, + wasi_errno_isconn = 30, + wasi_errno_isdir = 31, + wasi_errno_loop = 32, + wasi_errno_mfile = 33, + wasi_errno_mlink = 34, + wasi_errno_msgsize = 35, + wasi_errno_multihop = 36, + wasi_errno_nametoolong = 37, + wasi_errno_netdown = 38, + wasi_errno_netreset = 39, + wasi_errno_netunreach = 40, + wasi_errno_nfile = 41, + wasi_errno_nobufs = 42, + wasi_errno_nodev = 43, + wasi_errno_noent = 44, + wasi_errno_noexec = 45, + wasi_errno_nolck = 46, + wasi_errno_nolink = 47, + wasi_errno_nomem = 48, + wasi_errno_nomsg = 49, + wasi_errno_noprotoopt = 50, + wasi_errno_nospc = 51, + wasi_errno_nosys = 52, + wasi_errno_notconn = 53, + wasi_errno_notdir = 54, + wasi_errno_notempty = 55, + wasi_errno_notrecoverable = 56, + wasi_errno_notsock = 57, + wasi_errno_opnotsupp = 58, + wasi_errno_notty = 59, + wasi_errno_nxio = 60, + wasi_errno_overflow = 61, + wasi_errno_ownerdead = 62, + wasi_errno_perm = 63, + wasi_errno_pipe = 64, + wasi_errno_proto = 65, + wasi_errno_protonosupport = 66, + wasi_errno_prototype = 67, + wasi_errno_range = 68, + wasi_errno_rofs = 69, + wasi_errno_spipe = 70, + wasi_errno_srch = 71, + wasi_errno_stale = 72, + wasi_errno_timedout = 73, + wasi_errno_txtbsy = 74, + wasi_errno_xdev = 75, + wasi_errno_notcapable = 76, +}; + +enum wasi_oflags { + wasi_oflags_creat = 1 << 0, + wasi_oflags_directory = 1 << 1, + wasi_oflags_excl = 1 << 2, + wasi_oflags_trunc = 1 << 3, +}; + +enum wasi_rights { + wasi_rights_fd_datasync = 1ull << 0, + wasi_rights_fd_read = 1ull << 1, + wasi_rights_fd_seek = 1ull << 2, + wasi_rights_fd_fdstat_set_flags = 1ull << 3, + wasi_rights_fd_sync = 1ull << 4, + wasi_rights_fd_tell = 1ull << 5, + wasi_rights_fd_write = 1ull << 6, + wasi_rights_fd_advise = 1ull << 7, + wasi_rights_fd_allocate = 1ull << 8, + wasi_rights_path_create_directory = 1ull << 9, + wasi_rights_path_create_file = 1ull << 10, + wasi_rights_path_link_source = 1ull << 11, + wasi_rights_path_link_target = 1ull << 12, + wasi_rights_path_open = 1ull << 13, + wasi_rights_fd_readdir = 1ull << 14, + wasi_rights_path_readlink = 1ull << 15, + wasi_rights_path_rename_source = 1ull << 16, + wasi_rights_path_rename_target = 1ull << 17, + wasi_rights_path_filestat_get = 1ull << 18, + wasi_rights_path_filestat_set_size = 1ull << 19, + wasi_rights_path_filestat_set_times = 1ull << 20, + wasi_rights_fd_filestat_get = 1ull << 21, + wasi_rights_fd_filestat_set_size = 1ull << 22, + wasi_rights_fd_filestat_set_times = 1ull << 23, + wasi_rights_path_symlink = 1ull << 24, + wasi_rights_path_remove_directory = 1ull << 25, + wasi_rights_path_unlink_file = 1ull << 26, + wasi_rights_poll_fd_readwrite = 1ull << 27, + wasi_rights_sock_shutdown = 1ull << 28, + wasi_rights_sock_accept = 1ull << 29, +}; + +enum wasi_clockid { + wasi_clockid_realtime = 0, + wasi_clockid_monotonic = 1, + wasi_clockid_process_cputime_id = 2, + wasi_clockid_thread_cputime_id = 3, +}; + +enum wasi_filetype { + wasi_filetype_unknown = 0, + wasi_filetype_block_device = 1, + wasi_filetype_character_device = 2, + wasi_filetype_directory = 3, + wasi_filetype_regular_file = 4, + wasi_filetype_socket_dgram = 5, + wasi_filetype_socket_stream = 6, + wasi_filetype_symbolic_link = 7, +}; + +enum wasi_fdflags { + wasi_fdflags_append = 1 << 0, + wasi_fdflags_dsync = 1 << 1, + wasi_fdflags_nonblock = 1 << 2, + wasi_fdflags_rsync = 1 << 3, + wasi_fdflags_sync = 1 << 4, +}; + +struct wasi_filestat { + uint64_t dev; + uint64_t ino; + uint64_t filetype; + uint64_t nlink; + uint64_t size; + uint64_t atim; + uint64_t mtim; + uint64_t ctim; +}; + +struct wasi_fdstat { + uint16_t fs_filetype; + uint16_t fs_flags; + uint32_t padding; + uint64_t fs_rights_inheriting; +}; + +struct wasi_ciovec { + uint32_t ptr; + uint32_t len; +}; + +extern uint8_t **const wasm_memory; +extern void wasm__start(void); + +static int global_argc; +static char **global_argv; + +static uint32_t de_len; +struct DirEntry { + enum wasi_filetype filetype; + time_t atim; + time_t mtim; + time_t ctim; + char *guest_path; + char *host_path; +} *des; + +static uint32_t fd_len; +static struct FileDescriptor { + uint32_t de; + FILE *stream; +} *fds; + +static void *dupe(const void *data, size_t len) { + void *copy = malloc(len); + if (copy == NULL) panic("out of memory"); + memcpy(copy, data, len); + return copy; +} + +int main(int argc, char **argv) { + if (argc < 2) { + fprintf(stderr, "usage: %s \n", argv[0]); + return 1; + } + + global_argc = argc; + global_argv = argv; + + time_t now = time(NULL); + srand((unsigned)now); + + de_len = 4; + des = calloc(de_len, sizeof(struct DirEntry)); + if (des == NULL) panic("out of memory"); + + des[0].filetype = wasi_filetype_character_device; + + des[1].filetype = wasi_filetype_directory; + des[1].guest_path = dupe(".", sizeof(".")); + des[1].host_path = dupe(".", sizeof(".")); + + des[2].filetype = wasi_filetype_directory; + des[2].guest_path = dupe("/cache", sizeof("/cache")); + des[2].atim = now; + des[2].mtim = now; + des[2].ctim = now; + + des[3].filetype = wasi_filetype_directory; + des[3].guest_path = dupe("/lib", sizeof("/lib")); + des[3].host_path = dupe(argv[1], strlen(argv[1])); + + fd_len = 6; + fds = calloc(sizeof(struct FileDescriptor), fd_len); + if (fds == NULL) panic("out of memory"); + fds[0].stream = stdin; + fds[1].stream = stdout; + fds[2].stream = stderr; + fds[3].de = 1; + fds[4].de = 2; + fds[5].de = 3; + + wasm__start(); +} + +static enum wasi_errno DirEntry_create(uint32_t dir_fd, const char *path, uint32_t path_len, enum wasi_filetype filetype, time_t tim, uint32_t *res_de) { + if (path[0] != '/') { + if (dir_fd >= fd_len || fds[dir_fd].de >= de_len) return wasi_errno_badf; + if (des[fds[dir_fd].de].filetype != wasi_filetype_directory) return wasi_errno_notdir; + } + + struct DirEntry *new_des = realloc(des, (de_len + 1) * sizeof(struct DirEntry)); + if (new_des == NULL) return wasi_errno_nomem; + des = new_des; + + struct DirEntry *de = &des[de_len]; + de->filetype = filetype; + de->atim = tim; + de->mtim = tim; + de->ctim = tim; + if (path[0] == '/') { + de->guest_path = malloc(path_len + 1); + if (de->guest_path == NULL) return wasi_errno_nomem; + memcpy(&de->guest_path[0], path, path_len); + de->guest_path[path_len] = '\0'; + + de->host_path = malloc(path_len + 1); + if (de->host_path == NULL) return wasi_errno_nomem; + memcpy(&de->host_path[0], path, path_len); + de->host_path[path_len] = '\0'; + } else { + const struct DirEntry *dir_de = &des[fds[dir_fd].de]; + if (dir_de->guest_path != NULL) { + size_t dir_guest_path_len = strlen(dir_de->guest_path); + de->guest_path = malloc(dir_guest_path_len + 1 + path_len + 1); + if (de->guest_path == NULL) return wasi_errno_nomem; + memcpy(&de->guest_path[0], dir_de->guest_path, dir_guest_path_len); + de->guest_path[dir_guest_path_len] = '/'; + memcpy(&de->guest_path[dir_guest_path_len + 1], path, path_len); + de->guest_path[dir_guest_path_len + 1 + path_len] = '\0'; + } else de->guest_path = NULL; + + if (dir_de->host_path != NULL) { + size_t dir_host_path_len = strlen(dir_de->host_path); + de->host_path = malloc(dir_host_path_len + 1 + path_len + 1); + if (de->host_path == NULL) { free(de->guest_path); return wasi_errno_nomem; } + memcpy(&de->host_path[0], dir_de->host_path, dir_host_path_len); + de->host_path[dir_host_path_len] = '/'; + memcpy(&de->host_path[dir_host_path_len + 1], path, path_len); + de->host_path[dir_host_path_len + 1 + path_len] = '\0'; + } else de->host_path = NULL; + } + + if (res_de != NULL) *res_de = de_len; + de_len += 1; + return wasi_errno_success; +} + +static enum wasi_errno DirEntry_lookup(uint32_t dir_fd, uint32_t flags, const char *path, uint32_t path_len, uint32_t *res_de) { + (void)flags; + if (path[0] == '/') { + for (uint32_t de = 0; de < de_len; de += 1) { + if (des[de].guest_path == NULL) continue; + if (memcmp(&des[de].guest_path[0], path, path_len) != 0) continue; + if (des[de].guest_path[path_len] != '\0') continue; + if (res_de != NULL) *res_de = de; + return wasi_errno_success; + } + } else { + if (dir_fd >= fd_len || fds[dir_fd].de >= de_len) return wasi_errno_badf; + const struct DirEntry *dir_de = &des[fds[dir_fd].de]; + if (dir_de->filetype != wasi_filetype_directory) return wasi_errno_notdir; + + size_t dir_guest_path_len = strlen(dir_de->guest_path); + for (uint32_t de = 0; de < de_len; de += 1) { + if (des[de].guest_path == NULL) continue; + if (memcmp(&des[de].guest_path[0], dir_de->guest_path, dir_guest_path_len) != 0) continue; + if (des[de].guest_path[dir_guest_path_len] != '/') continue; + if (memcmp(&des[de].guest_path[dir_guest_path_len + 1], path, path_len) != 0) continue; + if (des[de].guest_path[dir_guest_path_len + 1 + path_len] != '\0') continue; + if (res_de != NULL) *res_de = de; + return wasi_errno_success; + } + } + return wasi_errno_noent; +} + +static void DirEntry_filestat(uint32_t de, struct wasi_filestat *res_filestat) { + res_filestat->dev = 0; + res_filestat->ino = de; + res_filestat->filetype = des[de].filetype; + res_filestat->nlink = 1; + res_filestat->size = 0; + res_filestat->atim = des[de].atim * UINT64_C(1000000000); + res_filestat->mtim = des[de].mtim * UINT64_C(1000000000); + res_filestat->ctim = des[de].ctim * UINT64_C(1000000000); +} + +static void DirEntry_unlink(uint32_t de) { + free(des[de].guest_path); + des[de].guest_path = NULL; + free(des[de].host_path); + des[de].host_path = NULL; +} + +uint32_t wasi_snapshot_preview1_args_sizes_get(uint32_t argv_size, uint32_t argv_buf_size) { + uint8_t *const m = *wasm_memory; + uint32_t *argv_size_ptr = (uint32_t *)&m[argv_size]; + uint32_t *argv_buf_size_ptr = (uint32_t *)&m[argv_buf_size]; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_args_sizes_get()\n"); +#endif + + int c_argc = global_argc; + char **c_argv = global_argv; + uint32_t size = 0; + for (int i = 0; i < c_argc; i += 1) { + if (i == 1) continue; + size += strlen(c_argv[i]) + 1; + } + *argv_size_ptr = c_argc - 1; + *argv_buf_size_ptr = size; + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_args_get(uint32_t argv, uint32_t argv_buf) { + uint8_t *const m = *wasm_memory; + uint32_t *argv_ptr = (uint32_t *)&m[argv]; + char *argv_buf_ptr = (char *)&m[argv_buf]; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_args_get()\n"); +#endif + + int c_argc = global_argc; + char **c_argv = global_argv; + uint32_t dst_i = 0; + uint32_t argv_buf_i = 0; + for (int src_i = 0; src_i < c_argc; src_i += 1) { + if (src_i == 1) continue; + argv_ptr[dst_i] = argv_buf + argv_buf_i; + dst_i += 1; + strcpy(&argv_buf_ptr[argv_buf_i], c_argv[src_i]); + argv_buf_i += strlen(c_argv[src_i]) + 1; + } + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_fd_prestat_get(uint32_t fd, uint32_t res_prestat) { + uint8_t *const m = *wasm_memory; + uint32_t *res_prestat_ptr = (uint32_t *)&m[res_prestat]; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_fd_prestat_get(%u)\n", fd); +#endif + + if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf; + + res_prestat_ptr[0] = 0; + res_prestat_ptr[1] = strlen(des[fds[fd].de].guest_path); + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_fd_prestat_dir_name(uint32_t fd, uint32_t path, uint32_t path_len) { + uint8_t *const m = *wasm_memory; + char *path_ptr = (char *)&m[path]; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_fd_prestat_dir_name(%u, \"%.*s\")\n", fd, (int)path_len, path_ptr); +#endif + + if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf; + strncpy(path_ptr, des[fds[fd].de].guest_path, path_len); + return wasi_errno_success; +} + +void wasi_snapshot_preview1_proc_exit(uint32_t rval) { +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_proc_exit(%u)\n", rval); +#endif + + exit(rval); +} + +uint32_t wasi_snapshot_preview1_fd_close(uint32_t fd) { +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_fd_close(%u)\n", fd); +#endif + + if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf; + if (fds[fd].stream != NULL) fclose(fds[fd].stream); + + fds[fd].de = ~0; + fds[fd].stream = NULL; + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_path_create_directory(uint32_t fd, uint32_t path, uint32_t path_len) { + uint8_t *const m = *wasm_memory; + const char *path_ptr = (const char *)&m[path]; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_path_create_directory(%u, \"%.*s\")\n", fd, (int)path_len, path_ptr); +#endif + + enum wasi_errno lookup_errno = DirEntry_lookup(fd, 0, path_ptr, path_len, NULL); + switch (lookup_errno) { + case wasi_errno_success: return wasi_errno_exist; + case wasi_errno_noent: break; + default: return lookup_errno; + } + return DirEntry_create(fd, path_ptr, path_len, wasi_filetype_directory, time(NULL), NULL); +} + +uint32_t wasi_snapshot_preview1_fd_read(uint32_t fd, uint32_t iovs, uint32_t iovs_len, uint32_t res_size) { + uint8_t *const m = *wasm_memory; + struct wasi_ciovec *iovs_ptr = (struct wasi_ciovec *)&m[iovs]; + uint32_t *res_size_ptr = (uint32_t *)&m[res_size]; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_fd_read(%u, 0x%X, %u)\n", fd, iovs, iovs_len); +#endif + + if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf; + switch (des[fds[fd].de].filetype) { + case wasi_filetype_character_device: break; + case wasi_filetype_regular_file: break; + case wasi_filetype_directory: return wasi_errno_inval; + default: panic("unimplemented"); + } + + size_t size = 0; + for (uint32_t i = 0; i < iovs_len; i += 1) { + size_t read_size = 0; + if (fds[fd].stream != NULL) + read_size = fread(&m[iovs_ptr[i].ptr], 1, iovs_ptr[i].len, fds[fd].stream); + else + panic("unimplemented"); + size += read_size; + if (read_size < iovs_ptr[i].len) break; + } + + if (size > 0) des[fds[fd].de].atim = time(NULL); + *res_size_ptr = size; + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_fd_filestat_get(uint32_t fd, uint32_t res_filestat) { + uint8_t *const m = *wasm_memory; + struct wasi_filestat *res_filestat_ptr = (struct wasi_filestat *)&m[res_filestat]; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_fd_filestat_get(%u)\n", fd); +#endif + + if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf; + DirEntry_filestat(fds[fd].de, res_filestat_ptr); + if (des[fds[fd].de].filetype != wasi_filetype_regular_file) return wasi_errno_success; + if (fds[fd].stream == NULL) return wasi_errno_success; + fpos_t pos; + if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io; + if (fseek(fds[fd].stream, 0, SEEK_END) < 0) return wasi_errno_io; + long size = ftell(fds[fd].stream); + if (size < 0) return wasi_errno_io; + res_filestat_ptr->size = size; + if (fsetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io; + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_path_rename(uint32_t fd, uint32_t old_path, uint32_t old_path_len, uint32_t new_fd, uint32_t new_path, uint32_t new_path_len) { + uint8_t *const m = *wasm_memory; + const char *old_path_ptr = (const char *)&m[old_path]; + const char *new_path_ptr = (const char *)&m[new_path]; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_path_rename(%u, \"%.*s\", %u, \"%.*s\")\n", fd, (int)old_path_len, old_path_ptr, new_fd, (int)new_path_len, new_path_ptr); +#endif + + uint32_t old_de; + enum wasi_errno old_lookup_errno = DirEntry_lookup(fd, 0, old_path_ptr, old_path_len, &old_de); + if (old_lookup_errno != wasi_errno_success) return old_lookup_errno; + DirEntry_unlink(old_de); + + uint32_t de; + enum wasi_errno new_lookup_errno = DirEntry_lookup(new_fd, 0, new_path_ptr, new_path_len, &de); + switch (new_lookup_errno) { + case wasi_errno_success: DirEntry_unlink(de); break; + case wasi_errno_noent: break; + default: return new_lookup_errno; + } + + uint32_t new_de; + enum wasi_errno create_errno = + DirEntry_create(new_fd, new_path_ptr, new_path_len, des[old_de].filetype, 0, &new_de); + if (create_errno != wasi_errno_success) return create_errno; + des[new_de].atim = des[old_de].atim; + des[new_de].mtim = des[old_de].mtim; + des[new_de].ctim = time(NULL); + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_fd_filestat_set_size(uint32_t fd, uint64_t size) { +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_fd_filestat_set_size(%u, %llu)\n", fd, (unsigned long long)size); +#endif + + if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf; + if (des[fds[fd].de].filetype != wasi_filetype_regular_file) return wasi_errno_inval; + + if (fds[fd].stream == NULL) return wasi_errno_success; + fpos_t pos; + if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io; + if (fseek(fds[fd].stream, 0, SEEK_END) < 0) return wasi_errno_io; + long old_size = ftell(fds[fd].stream); + if (old_size < 0) return wasi_errno_io; + if (size > (unsigned long)old_size) { + if (fseek(fds[fd].stream, size - 1, SEEK_SET) < 0) return wasi_errno_io; + fputc(0, fds[fd].stream); + } else if (size < (unsigned long)old_size) panic("unimplemented"); + if (fsetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io; + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_fd_pwrite(uint32_t fd, uint32_t iovs, uint32_t iovs_len, uint64_t offset, uint32_t res_size) { + uint8_t *const m = *wasm_memory; + struct wasi_ciovec *iovs_ptr = (struct wasi_ciovec *)&m[iovs]; + uint32_t *res_size_ptr = (uint32_t *)&m[res_size]; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_fd_pwrite(%u, 0x%X, %u)\n", fd, iovs, iovs_len); +#endif + + if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf; + switch (des[fds[fd].de].filetype) { + case wasi_filetype_character_device: break; + case wasi_filetype_regular_file: break; + case wasi_filetype_directory: return wasi_errno_inval; + default: panic("unimplemented"); + } + + fpos_t pos; + if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io; + if (fseek(fds[fd].stream, offset, SEEK_SET) < 0) return wasi_errno_io; + + size_t size = 0; + for (uint32_t i = 0; i < iovs_len; i += 1) { + size_t written_size = 0; + if (fds[fd].stream != NULL) + written_size = fwrite(&m[iovs_ptr[i].ptr], 1, iovs_ptr[i].len, fds[fd].stream); + else + written_size = iovs_ptr[i].len; + size += written_size; + if (written_size < iovs_ptr[i].len) break; + } + + if (fsetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io; + + if (size > 0) { + time_t now = time(NULL); + des[fds[fd].de].atim = now; + des[fds[fd].de].mtim = now; + } + *res_size_ptr = size; + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_random_get(uint32_t buf, uint32_t buf_len) { + uint8_t *const m = *wasm_memory; + uint8_t *buf_ptr = (uint8_t *)&m[buf]; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_random_get(%u)\n", buf_len); +#endif + + for (uint32_t i = 0; i < buf_len; i += 1) buf_ptr[i] = (uint8_t)rand(); + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_fd_filestat_set_times(uint32_t fd, uint64_t atim, uint64_t mtim, uint32_t fst_flags) { + (void)fd; + (void)atim; + (void)mtim; + (void)fst_flags; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_fd_filestat_set_times(%u, %llu, %llu, 0x%X)\n", fd, (unsigned long long)atim, (unsigned long long)mtim, fst_flags); +#endif + + panic("unimplemented"); + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_environ_sizes_get(uint32_t environ_size, uint32_t environ_buf_size) { + (void)environ_size; + (void)environ_buf_size; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_environ_sizes_get()\n"); +#endif + + panic("unimplemented"); + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_environ_get(uint32_t environ, uint32_t environ_buf) { + (void)environ; + (void)environ_buf; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_environ_get()\n"); +#endif + + panic("unimplemented"); + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_path_filestat_get(uint32_t fd, uint32_t flags, uint32_t path, uint32_t path_len, uint32_t res_filestat) { + uint8_t *const m = *wasm_memory; + const char *path_ptr = (const char *)&m[path]; + struct wasi_filestat *res_filestat_ptr = (struct wasi_filestat *)&m[res_filestat]; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_path_filestat_get(%u, 0x%X, \"%.*s\")\n", fd, flags, (int)path_len, path_ptr); +#endif + + uint32_t de; + enum wasi_errno lookup_errno = DirEntry_lookup(fd, flags, path_ptr, path_len, &de); + if (lookup_errno != wasi_errno_success) return lookup_errno; + DirEntry_filestat(de, res_filestat_ptr); + if (des[de].filetype == wasi_filetype_regular_file && des[de].host_path != NULL) { + FILE *stream = fopen(des[de].host_path, "rb"); + if (stream != NULL) { + if (fseek(stream, 0, SEEK_END) >= 0) { + long size = ftell(stream); + if (size >= 0) res_filestat_ptr->size = size; + } + fclose(stream); + } + } + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_fd_fdstat_get(uint32_t fd, uint32_t res_fdstat) { + (void)fd; + (void)res_fdstat; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_fd_fdstat_get(%u)\n", fd); +#endif + + panic("unimplemented"); + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_fd_readdir(uint32_t fd, uint32_t buf, uint32_t buf_len, uint64_t cookie, uint32_t res_size) { + (void)fd; + (void)buf; + (void)buf_len; + (void)cookie; + (void)res_size; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_fd_readdir(%u, 0x%X, %u, %llu)\n", fd, buf, buf_len, (unsigned long long)cookie); +#endif + + panic("unimplemented"); + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_fd_write(uint32_t fd, uint32_t iovs, uint32_t iovs_len, uint32_t res_size) { + uint8_t *const m = *wasm_memory; + struct wasi_ciovec *iovs_ptr = (struct wasi_ciovec *)&m[iovs]; + uint32_t *res_size_ptr = (uint32_t *)&m[res_size]; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_fd_write(%u, 0x%X, %u)\n", fd, iovs, iovs_len); +#endif + + if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf; + switch (des[fds[fd].de].filetype) { + case wasi_filetype_character_device: break; + case wasi_filetype_regular_file: break; + case wasi_filetype_directory: return wasi_errno_inval; + default: panic("unimplemented"); + } + + size_t size = 0; + for (uint32_t i = 0; i < iovs_len; i += 1) { + size_t written_size = 0; + if (fds[fd].stream != NULL) + written_size = fwrite(&m[iovs_ptr[i].ptr], 1, iovs_ptr[i].len, fds[fd].stream); + else + written_size = iovs_ptr[i].len; + size += written_size; + if (written_size < iovs_ptr[i].len) break; + } + + if (size > 0) { + time_t now = time(NULL); + des[fds[fd].de].atim = now; + des[fds[fd].de].mtim = now; + } + *res_size_ptr = size; + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_path_open(uint32_t fd, uint32_t dirflags, uint32_t path, uint32_t path_len, uint32_t oflags, uint64_t fs_rights_base, uint64_t fs_rights_inheriting, uint32_t fdflags, uint32_t res_fd) { + uint8_t *const m = *wasm_memory; + const char *path_ptr = (const char *)&m[path]; + (void)fs_rights_inheriting; + uint32_t *res_fd_ptr = (uint32_t *)&m[res_fd]; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_path_open(%u, 0x%X, \"%.*s\", 0x%X, 0x%llX, 0x%llX, 0x%X)\n", fd, dirflags, (int)path_len, path_ptr, oflags, (unsigned long long)fs_rights_base, (unsigned long long)fs_rights_inheriting, fdflags); +#endif + + bool creat = (oflags & wasi_oflags_creat) != 0; + bool directory = (oflags & wasi_oflags_directory) != 0; + bool excl = (oflags & wasi_oflags_excl) != 0; + bool trunc = (oflags & wasi_oflags_trunc) != 0; + bool append = (fdflags & wasi_fdflags_append) != 0; + + uint32_t de; + enum wasi_errno lookup_errno = DirEntry_lookup(fd, dirflags, path_ptr, path_len, &de); + if (lookup_errno == wasi_errno_success) { + if (directory && des[de].filetype != wasi_filetype_directory) return wasi_errno_notdir; + + struct FileDescriptor *new_fds = realloc(fds, (fd_len + 1) * sizeof(struct FileDescriptor)); + if (new_fds == NULL) return wasi_errno_nomem; + fds = new_fds; + + fds[fd_len].de = de; + switch (des[de].filetype) { + case wasi_filetype_directory: fds[fd_len].stream = NULL; break; + default: panic("unimplemented"); + } + +#if LOG_TRACE + fprintf(stderr, "fd = %u\n", fd_len); +#endif + *res_fd_ptr = fd_len; + fd_len += 1; + } + if (lookup_errno != wasi_errno_noent) return lookup_errno; + + struct FileDescriptor *new_fds = realloc(fds, (fd_len + 1) * sizeof(struct FileDescriptor)); + if (new_fds == NULL) return wasi_errno_nomem; + fds = new_fds; + + enum wasi_filetype filetype = directory ? wasi_filetype_directory : wasi_filetype_regular_file; + enum wasi_errno create_errno = DirEntry_create(fd, path_ptr, path_len, filetype, 0, &de); + if (create_errno != wasi_errno_success) return create_errno; + FILE *stream; + if (!directory) { + if (des[de].host_path == NULL) { + if (!creat) { DirEntry_unlink(de); de_len -= 1; return wasi_errno_noent; } + time_t now = time(NULL); + des[de].atim = now; + des[de].mtim = now; + des[de].ctim = now; + stream = NULL; + } else { + if (oflags != (append ? wasi_oflags_creat : wasi_oflags_creat | wasi_oflags_trunc)) { + char mode[] = "rb+"; + if ((fs_rights_base & wasi_rights_fd_write) == 0) mode[2] = '\0'; + stream = fopen(des[de].host_path, mode); + if (stream != NULL) { + if (append || excl || trunc) fclose(stream); + if (excl) { + DirEntry_unlink(de); + de_len -= 1; + return wasi_errno_exist; + } + } else if (!creat) { DirEntry_unlink(de); de_len -= 1; return wasi_errno_noent; } + } + if (append || trunc || stream == NULL) { + char mode[] = "wb+"; + if ((fs_rights_base & wasi_rights_fd_read) == 0) mode[2] = '\0'; + if (trunc || !append) { + stream = fopen(des[de].host_path, mode); + if (append && stream != NULL) fclose(stream); + } + if (append) { + mode[0] = 'a'; + stream = fopen(des[de].host_path, mode); + } + } + if (stream == NULL) { DirEntry_unlink(de); de_len -= 1; return wasi_errno_isdir; } + } + } else stream = NULL; + +#if LOG_TRACE + fprintf(stderr, "fd = %u\n", fd_len); +#endif + fds[fd_len].de = de; + fds[fd_len].stream = stream; + *res_fd_ptr = fd_len; + fd_len += 1; + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_clock_time_get(uint32_t id, uint64_t precision, uint32_t res_timestamp) { + uint8_t *const m = *wasm_memory; + (void)precision; + uint64_t *res_timestamp_ptr = (uint64_t *)&m[res_timestamp]; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_clock_time_get(%u, %llu)\n", id, (unsigned long long)precision); +#endif + + switch (id) { + case wasi_clockid_realtime: + *res_timestamp_ptr = time(NULL) * UINT64_C(1000000000); + break; + case wasi_clockid_monotonic: + case wasi_clockid_process_cputime_id: + case wasi_clockid_thread_cputime_id: + *res_timestamp_ptr = clock() * (UINT64_C(1000000000) / CLOCKS_PER_SEC); + break; + default: return wasi_errno_inval; + } + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_path_remove_directory(uint32_t fd, uint32_t path, uint32_t path_len) { + uint8_t *const m = *wasm_memory; + const char *path_ptr = (const char *)&m[path]; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_path_remove_directory(%u, \"%.*s\")\n", fd, (int)path_len, path_ptr); +#endif + + uint32_t de; + enum wasi_errno lookup_errno = DirEntry_lookup(fd, 0, path_ptr, path_len, &de); + if (lookup_errno != wasi_errno_success) return lookup_errno; + if (des[de].filetype != wasi_filetype_directory) return wasi_errno_notdir; + DirEntry_unlink(de); + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_path_unlink_file(uint32_t fd, uint32_t path, uint32_t path_len) { + uint8_t *const m = *wasm_memory; + const char *path_ptr = (const char *)&m[path]; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_path_unlink_file(%u, \"%.*s\")\n", fd, (int)path_len, path_ptr); +#endif + + uint32_t de; + enum wasi_errno lookup_errno = DirEntry_lookup(fd, 0, path_ptr, path_len, &de); + if (lookup_errno != wasi_errno_success) return lookup_errno; + if (des[de].filetype == wasi_filetype_directory) return wasi_errno_isdir; + if (des[de].filetype != wasi_filetype_regular_file) panic("unimplemented"); + DirEntry_unlink(de); + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_fd_pread(uint32_t fd, uint32_t iovs, uint32_t iovs_len, uint64_t offset, uint32_t res_size) { + uint8_t *const m = *wasm_memory; + struct wasi_ciovec *iovs_ptr = (struct wasi_ciovec *)&m[iovs]; + uint32_t *res_size_ptr = (uint32_t *)&m[res_size]; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_fd_pread(%u, 0x%X, %u)\n", fd, iovs, iovs_len); +#endif + + if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf; + switch (des[fds[fd].de].filetype) { + case wasi_filetype_character_device: break; + case wasi_filetype_regular_file: break; + case wasi_filetype_directory: return wasi_errno_inval; + default: panic("unimplemented"); + } + + fpos_t pos; + if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io; + if (fseek(fds[fd].stream, offset, SEEK_SET) < 0) return wasi_errno_io; + + size_t size = 0; + for (uint32_t i = 0; i < iovs_len; i += 1) { + size_t read_size = 0; + if (fds[fd].stream != NULL) + read_size = fread(&m[iovs_ptr[i].ptr], 1, iovs_ptr[i].len, fds[fd].stream); + else + panic("unimplemented"); + size += read_size; + if (read_size < iovs_ptr[i].len) break; + } + + if (fsetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io; + + if (size > 0) des[fds[fd].de].atim = time(NULL); + *res_size_ptr = size; + return wasi_errno_success; +} + +uint32_t wasi_snapshot_preview1_poll_oneoff(uint32_t in, uint32_t out, uint32_t nsubscriptions, uint32_t res_nevents) { + (void)in; + (void)out; + (void)nsubscriptions; + (void)res_nevents; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_poll_oneoff(%u)\n", nsubscriptions); +#endif + + panic("unimplemented"); + return wasi_errno_success; +} + + +void wasi_snapshot_preview1_debug(uint32_t string, uint64_t x) { + uint8_t *const m = *wasm_memory; + const char *string_ptr = (const char *)&m[string]; +#if LOG_TRACE + fprintf(stderr, "wasi_snapshot_preview1_debug(\"%s\", %llu, 0x%llX)\n", string_ptr, (unsigned long long)x, (unsigned long long)x); +#endif + + (void)string_ptr; + (void)x; +} diff --git a/stage1/wasm.h b/stage1/wasm.h new file mode 100644 index 0000000000..276d340bbb --- /dev/null +++ b/stage1/wasm.h @@ -0,0 +1,280 @@ +#ifndef WASM_H +#define WASM_H + +#include "panic.h" + +enum WasmSectionId { + WasmSectionId_type = 1, + WasmSectionId_import = 2, + WasmSectionId_func = 3, + WasmSectionId_table = 4, + WasmSectionId_mem = 5, + WasmSectionId_global = 6, + WasmSectionId_export = 7, + WasmSectionId_start = 8, + WasmSectionId_elem = 9, + WasmSectionId_code = 10, + WasmSectionId_data = 11, + WasmSectionId_datacount = 12, +}; + +enum WasmValType { + WasmValType_i32 = -0x01, + WasmValType_i64 = -0x02, + WasmValType_f32 = -0x03, + WasmValType_f64 = -0x04, + WasmValType_v128 = -0x05, + WasmValType_funcref = -0x10, + WasmValType_externref = -0x11, + WasmValType_empty = -0x40, +}; +static const char *WasmValType_toC(enum WasmValType val_type) { + switch (val_type) { + case WasmValType_i32: return "uint32_t"; + case WasmValType_i64: return "uint64_t"; + case WasmValType_f32: return "float"; + case WasmValType_f64: return "double"; + case WasmValType_v128: panic("vector types are unsupported"); + case WasmValType_funcref: return "void (*)(void)"; + case WasmValType_externref: return "void *"; + default: panic("unsupported value type"); + } + return NULL; +} + +enum WasmMut { + WasmMut_const = 0x00, + WasmMut_var = 0x01, +}; +static const char *WasmMut_toC(enum WasmMut val_type) { + switch (val_type) { + case WasmMut_const: return "const "; + case WasmMut_var: return ""; + default: panic("unsupported mut"); + } +} + +enum WasmOpcode { + WasmOpcode_unreachable = 0x00, + WasmOpcode_nop = 0x01, + WasmOpcode_block = 0x02, + WasmOpcode_loop = 0x03, + WasmOpcode_if = 0x04, + WasmOpcode_else = 0x05, + WasmOpcode_end = 0x0B, + WasmOpcode_br = 0x0C, + WasmOpcode_br_if = 0x0D, + WasmOpcode_br_table = 0x0E, + WasmOpcode_return = 0x0F, + WasmOpcode_call = 0x10, + WasmOpcode_call_indirect = 0x11, + + WasmOpcode_drop = 0x1A, + WasmOpcode_select = 0x1B, + WasmOpcode_select_t = 0x1C, + + WasmOpcode_local_get = 0x20, + WasmOpcode_local_set = 0x21, + WasmOpcode_local_tee = 0x22, + WasmOpcode_global_get = 0x23, + WasmOpcode_global_set = 0x24, + + WasmOpcode_table_get = 0x25, + WasmOpcode_table_set = 0x26, + + WasmOpcode_i32_load = 0x28, + WasmOpcode_i64_load = 0x29, + WasmOpcode_f32_load = 0x2A, + WasmOpcode_f64_load = 0x2B, + WasmOpcode_i32_load8_s = 0x2C, + WasmOpcode_i32_load8_u = 0x2D, + WasmOpcode_i32_load16_s = 0x2E, + WasmOpcode_i32_load16_u = 0x2F, + WasmOpcode_i64_load8_s = 0x30, + WasmOpcode_i64_load8_u = 0x31, + WasmOpcode_i64_load16_s = 0x32, + WasmOpcode_i64_load16_u = 0x33, + WasmOpcode_i64_load32_s = 0x34, + WasmOpcode_i64_load32_u = 0x35, + WasmOpcode_i32_store = 0x36, + WasmOpcode_i64_store = 0x37, + WasmOpcode_f32_store = 0x38, + WasmOpcode_f64_store = 0x39, + WasmOpcode_i32_store8 = 0x3A, + WasmOpcode_i32_store16 = 0x3B, + WasmOpcode_i64_store8 = 0x3C, + WasmOpcode_i64_store16 = 0x3D, + WasmOpcode_i64_store32 = 0x3E, + WasmOpcode_memory_size = 0x3F, + WasmOpcode_memory_grow = 0x40, + + WasmOpcode_i32_const = 0x41, + WasmOpcode_i64_const = 0x42, + WasmOpcode_f32_const = 0x43, + WasmOpcode_f64_const = 0x44, + + WasmOpcode_i32_eqz = 0x45, + WasmOpcode_i32_eq = 0x46, + WasmOpcode_i32_ne = 0x47, + WasmOpcode_i32_lt_s = 0x48, + WasmOpcode_i32_lt_u = 0x49, + WasmOpcode_i32_gt_s = 0x4A, + WasmOpcode_i32_gt_u = 0x4B, + WasmOpcode_i32_le_s = 0x4C, + WasmOpcode_i32_le_u = 0x4D, + WasmOpcode_i32_ge_s = 0x4E, + WasmOpcode_i32_ge_u = 0x4F, + + WasmOpcode_i64_eqz = 0x50, + WasmOpcode_i64_eq = 0x51, + WasmOpcode_i64_ne = 0x52, + WasmOpcode_i64_lt_s = 0x53, + WasmOpcode_i64_lt_u = 0x54, + WasmOpcode_i64_gt_s = 0x55, + WasmOpcode_i64_gt_u = 0x56, + WasmOpcode_i64_le_s = 0x57, + WasmOpcode_i64_le_u = 0x58, + WasmOpcode_i64_ge_s = 0x59, + WasmOpcode_i64_ge_u = 0x5A, + + WasmOpcode_f32_eq = 0x5B, + WasmOpcode_f32_ne = 0x5C, + WasmOpcode_f32_lt = 0x5D, + WasmOpcode_f32_gt = 0x5E, + WasmOpcode_f32_le = 0x5F, + WasmOpcode_f32_ge = 0x60, + + WasmOpcode_f64_eq = 0x61, + WasmOpcode_f64_ne = 0x62, + WasmOpcode_f64_lt = 0x63, + WasmOpcode_f64_gt = 0x64, + WasmOpcode_f64_le = 0x65, + WasmOpcode_f64_ge = 0x66, + + WasmOpcode_i32_clz = 0x67, + WasmOpcode_i32_ctz = 0x68, + WasmOpcode_i32_popcnt = 0x69, + WasmOpcode_i32_add = 0x6A, + WasmOpcode_i32_sub = 0x6B, + WasmOpcode_i32_mul = 0x6C, + WasmOpcode_i32_div_s = 0x6D, + WasmOpcode_i32_div_u = 0x6E, + WasmOpcode_i32_rem_s = 0x6F, + WasmOpcode_i32_rem_u = 0x70, + WasmOpcode_i32_and = 0x71, + WasmOpcode_i32_or = 0x72, + WasmOpcode_i32_xor = 0x73, + WasmOpcode_i32_shl = 0x74, + WasmOpcode_i32_shr_s = 0x75, + WasmOpcode_i32_shr_u = 0x76, + WasmOpcode_i32_rotl = 0x77, + WasmOpcode_i32_rotr = 0x78, + + WasmOpcode_i64_clz = 0x79, + WasmOpcode_i64_ctz = 0x7A, + WasmOpcode_i64_popcnt = 0x7B, + WasmOpcode_i64_add = 0x7C, + WasmOpcode_i64_sub = 0x7D, + WasmOpcode_i64_mul = 0x7E, + WasmOpcode_i64_div_s = 0x7F, + WasmOpcode_i64_div_u = 0x80, + WasmOpcode_i64_rem_s = 0x81, + WasmOpcode_i64_rem_u = 0x82, + WasmOpcode_i64_and = 0x83, + WasmOpcode_i64_or = 0x84, + WasmOpcode_i64_xor = 0x85, + WasmOpcode_i64_shl = 0x86, + WasmOpcode_i64_shr_s = 0x87, + WasmOpcode_i64_shr_u = 0x88, + WasmOpcode_i64_rotl = 0x89, + WasmOpcode_i64_rotr = 0x8A, + + WasmOpcode_f32_abs = 0x8B, + WasmOpcode_f32_neg = 0x8C, + WasmOpcode_f32_ceil = 0x8D, + WasmOpcode_f32_floor = 0x8E, + WasmOpcode_f32_trunc = 0x8F, + WasmOpcode_f32_nearest = 0x90, + WasmOpcode_f32_sqrt = 0x91, + WasmOpcode_f32_add = 0x92, + WasmOpcode_f32_sub = 0x93, + WasmOpcode_f32_mul = 0x94, + WasmOpcode_f32_div = 0x95, + WasmOpcode_f32_min = 0x96, + WasmOpcode_f32_max = 0x97, + WasmOpcode_f32_copysign = 0x98, + + WasmOpcode_f64_abs = 0x99, + WasmOpcode_f64_neg = 0x9A, + WasmOpcode_f64_ceil = 0x9B, + WasmOpcode_f64_floor = 0x9C, + WasmOpcode_f64_trunc = 0x9D, + WasmOpcode_f64_nearest = 0x9E, + WasmOpcode_f64_sqrt = 0x9F, + WasmOpcode_f64_add = 0xA0, + WasmOpcode_f64_sub = 0xA1, + WasmOpcode_f64_mul = 0xA2, + WasmOpcode_f64_div = 0xA3, + WasmOpcode_f64_min = 0xA4, + WasmOpcode_f64_max = 0xA5, + WasmOpcode_f64_copysign = 0xA6, + + WasmOpcode_i32_wrap_i64 = 0xA7, + WasmOpcode_i32_trunc_f32_s = 0xA8, + WasmOpcode_i32_trunc_f32_u = 0xA9, + WasmOpcode_i32_trunc_f64_s = 0xAA, + WasmOpcode_i32_trunc_f64_u = 0xAB, + WasmOpcode_i64_extend_i32_s = 0xAC, + WasmOpcode_i64_extend_i32_u = 0xAD, + WasmOpcode_i64_trunc_f32_s = 0xAE, + WasmOpcode_i64_trunc_f32_u = 0xAF, + WasmOpcode_i64_trunc_f64_s = 0xB0, + WasmOpcode_i64_trunc_f64_u = 0xB1, + WasmOpcode_f32_convert_i32_s = 0xB2, + WasmOpcode_f32_convert_i32_u = 0xB3, + WasmOpcode_f32_convert_i64_s = 0xB4, + WasmOpcode_f32_convert_i64_u = 0xB5, + WasmOpcode_f32_demote_f64 = 0xB6, + WasmOpcode_f64_convert_i32_s = 0xB7, + WasmOpcode_f64_convert_i32_u = 0xB8, + WasmOpcode_f64_convert_i64_s = 0xB9, + WasmOpcode_f64_convert_i64_u = 0xBA, + WasmOpcode_f64_promote_f32 = 0xBB, + WasmOpcode_i32_reinterpret_f32 = 0xBC, + WasmOpcode_i64_reinterpret_f64 = 0xBD, + WasmOpcode_f32_reinterpret_i32 = 0xBE, + WasmOpcode_f64_reinterpret_i64 = 0xBF, + + WasmOpcode_i32_extend8_s = 0xC0, + WasmOpcode_i32_extend16_s = 0xC1, + WasmOpcode_i64_extend8_s = 0xC2, + WasmOpcode_i64_extend16_s = 0xC3, + WasmOpcode_i64_extend32_s = 0xC4, + + WasmOpcode_prefixed = 0xFC, +}; + +enum WasmPrefixedOpcode { + WasmPrefixedOpcode_i32_trunc_sat_f32_s = 0, + WasmPrefixedOpcode_i32_trunc_sat_f32_u = 1, + WasmPrefixedOpcode_i32_trunc_sat_f64_s = 2, + WasmPrefixedOpcode_i32_trunc_sat_f64_u = 3, + WasmPrefixedOpcode_i64_trunc_sat_f32_s = 4, + WasmPrefixedOpcode_i64_trunc_sat_f32_u = 5, + WasmPrefixedOpcode_i64_trunc_sat_f64_s = 6, + WasmPrefixedOpcode_i64_trunc_sat_f64_u = 7, + + WasmPrefixedOpcode_memory_init = 8, + WasmPrefixedOpcode_data_drop = 9, + WasmPrefixedOpcode_memory_copy = 10, + WasmPrefixedOpcode_memory_fill = 11, + + WasmPrefixedOpcode_table_init = 12, + WasmPrefixedOpcode_elem_drop = 13, + WasmPrefixedOpcode_table_copy = 14, + WasmPrefixedOpcode_table_grow = 15, + WasmPrefixedOpcode_table_size = 16, + WasmPrefixedOpcode_table_fill = 17, +}; + +#endif /* WASM_H */ diff --git a/stage1/wasm2c.c b/stage1/wasm2c.c new file mode 100644 index 0000000000..1e0fb46bff --- /dev/null +++ b/stage1/wasm2c.c @@ -0,0 +1,2520 @@ +#include "FuncGen.h" +#include "InputStream.h" +#include "panic.h" +#include "wasm.h" + +#include +#include +#include +#include +#include +#include + +struct FuncType { + const struct ResultType *param; + const struct ResultType *result; +}; +static const struct FuncType *FuncType_blockType(const struct FuncType *types, int64_t block_type) { + if (block_type >= 0) return &types[block_type]; + + static const struct ResultType none = { 0, { 0 }}; + static const struct ResultType i32 = { 1, { WasmValType_i32 } }; + static const struct ResultType i64 = { 1, { WasmValType_i64 } }; + static const struct ResultType f32 = { 1, { WasmValType_f32 } }; + static const struct ResultType f64 = { 1, { WasmValType_f64 } }; + + static const struct FuncType none_i32 = { &none, &i32 }; + static const struct FuncType none_i64 = { &none, &i64 }; + static const struct FuncType none_f32 = { &none, &f32 }; + static const struct FuncType none_f64 = { &none, &f64 }; + static const struct FuncType none_none = { &none, &none }; + + switch (block_type) { + case WasmValType_i32: return &none_i32; + case WasmValType_i64: return &none_i64; + case WasmValType_f32: return &none_f32; + case WasmValType_f64: return &none_f64; + case WasmValType_empty: return &none_none; + default: panic("unsupported block type"); + } + return NULL; +} + +static uint32_t evalExpr(struct InputStream *in) { + uint32_t value; + while (true) { + switch (InputStream_readByte(in)) { + case WasmOpcode_end: return value; + + case WasmOpcode_i32_const: + value = (uint32_t)InputStream_readLeb128_i32(in); + break; + + default: panic("unsupported expr opcode"); + } + } +} + +static void renderExpr(FILE *out, struct InputStream *in) { + while (true) { + switch (InputStream_readByte(in)) { + case WasmOpcode_end: return; + + case WasmOpcode_i32_const: { + uint32_t value = (uint32_t)InputStream_readLeb128_i32(in); + fprintf(out, "UINT32_C(0x%" PRIX32 ")", value); + break; + } + + default: panic("unsupported expr opcode"); + } + } +} + +int main(int argc, char **argv) { + if (argc != 3) { + fprintf(stderr, "usage: %s in.wasm.zst out.c\n", argv[0]); + return 1; + } + + const char *mod = "wasm"; + bool isBigEndian = false; // TODO + + struct InputStream in; + InputStream_open(&in, argv[1]); + + if (InputStream_readByte(&in) != '\0' || + InputStream_readByte(&in) != 'a' || + InputStream_readByte(&in) != 's' || + InputStream_readByte(&in) != 'm') panic("input is not a zstd-compressed wasm file"); + if (InputStream_readLittle_u32(&in) != 1) panic("unsupported wasm version"); + + FILE *out = fopen(argv[2], "w"); + if (out == NULL) panic("unable to open output file"); + fputs("#include \n" + "#include \n" + "#include \n" + "#include \n" + "\n" + "static uint32_t i32_reinterpret_f32(const float src) {\n" + " uint32_t dst;\n" + " memcpy(&dst, &src, sizeof(dst));\n" + " return dst;\n" + "}\n" + "static uint64_t i64_reinterpret_f64(const double src) {\n" + " uint64_t dst;\n" + " memcpy(&dst, &src, sizeof(dst));\n" + " return dst;\n" + "}\n" + "static float f32_reinterpret_i32(const uint32_t src) {\n" + " float dst;\n" + " memcpy(&dst, &src, sizeof(dst));\n" + " return dst;\n" + "}\n" + "static double f64_reinterpret_i64(const uint64_t src) {\n" + " double dst;\n" + " memcpy(&dst, &src, sizeof(dst));\n" + " return dst;\n" + "}\n" + "\n" + "static uint32_t memory_grow(uint8_t **m, uint32_t *p, uint32_t n) {\n" + " uint32_t r = *p;\n" + " uint32_t new_p = r + n;\n" + " uint8_t *new_m = realloc(*m, new_p << 16);\n" + " if (new_m == NULL) return UINT32_C(0xFFFFFFF);\n" + " memset(&new_m[r << 16], 0, n << 16);\n" + " *m = new_m;\n" + " *p = new_p;\n" + " return r;\n" + "}\n" + "\n" + "static int inited;\n" + "static void init_elem(void);\n" + "static void init_data(void);\n" + "static void init(void) {\n" + " if (inited != 0) return;\n" + " init_elem();\n" + " init_data();\n" + " inited = 1;\n" + "}\n" + "\n", out); + + struct FuncType *types; + uint32_t max_param_len = 0; + (void)InputStream_skipToSection(&in, WasmSectionId_type); + { + uint32_t len = InputStream_readLeb128_u32(&in); + types = malloc(sizeof(struct FuncType) * len); + if (types == NULL) panic("out of memory"); + for (uint32_t i = 0; i < len; i += 1) { + if (InputStream_readByte(&in) != 0x60) panic("expected functype"); + types[i].param = InputStream_readResultType(&in); + if (types[i].param->len > max_param_len) max_param_len = types[i].param->len; + types[i].result = InputStream_readResultType(&in); + } + } + + struct Import { + const char *mod; + const char *name; + uint32_t type_idx; + } *imports; + (void)InputStream_skipToSection(&in, WasmSectionId_import); + uint32_t imports_len = InputStream_readLeb128_u32(&in); + { + imports = malloc(sizeof(struct Import) * imports_len); + if (imports == NULL) panic("out of memory"); + for (uint32_t i = 0; i < imports_len; i += 1) { + imports[i].mod = InputStream_readName(&in); + imports[i].name = InputStream_readName(&in); + switch (InputStream_readByte(&in)) { + case 0x00: { // func + imports[i].type_idx = InputStream_readLeb128_u32(&in); + const struct FuncType *func_type = &types[imports[i].type_idx]; + switch (func_type->result->len) { + case 0: fputs("void", out); break; + case 1: fputs(WasmValType_toC(func_type->result->types[0]), out); break; + default: panic("multiple function returns not supported"); + } + fprintf(out, " %s_%s(", imports[i].mod, imports[i].name); + if (func_type->param->len == 0) fputs("void", out); + for (uint32_t param_i = 0; param_i < func_type->param->len; param_i += 1) { + if (param_i > 0) fputs(", ", out); + fputs(WasmValType_toC(func_type->param->types[param_i]), out); + } + fputs(");\n", out); + break; + } + + case 0x01: // table + case 0x02: // mem + case 0x03: // global + default: + panic("unsupported import type"); + } + } + fputc('\n', out); + } + + struct Func { + uint32_t type_idx; + } *funcs; + (void)InputStream_skipToSection(&in, WasmSectionId_func); + { + uint32_t len = InputStream_readLeb128_u32(&in); + funcs = malloc(sizeof(struct Func) * len); + if (funcs == NULL) panic("out of memory"); + for (uint32_t i = 0; i < len; i += 1) { + funcs[i].type_idx = InputStream_readLeb128_u32(&in); + const struct FuncType *func_type = &types[funcs[i].type_idx]; + fputs("static ", out); + switch (func_type->result->len) { + case 0: fputs("void", out); break; + case 1: fputs(WasmValType_toC(func_type->result->types[0]), out); break; + default: panic("multiple function returns not supported"); + } + fprintf(out, " f%" PRIu32 "(", i); + if (func_type->param->len == 0) fputs("void", out); + for (uint32_t param_i = 0; param_i < func_type->param->len; param_i += 1) { + if (param_i > 0) fputs(", ", out); + fprintf(out, "%s", WasmValType_toC(func_type->param->types[param_i])); + } + fputs(");\n", out); + } + fputc('\n', out); + } + + struct Table { + int8_t type; + struct Limits limits; + } *tables; + (void)InputStream_skipToSection(&in, WasmSectionId_table); + { + uint32_t len = InputStream_readLeb128_u32(&in); + tables = malloc(sizeof(struct Table) * len); + if (tables == NULL) panic("out of memory"); + for (uint32_t i = 0; i < len; i += 1) { + int64_t ref_type = InputStream_readLeb128_i64(&in); + switch (ref_type) { + case WasmValType_funcref: + break; + + default: panic("unsupported reftype"); + } + tables[i].type = ref_type; + tables[i].limits = InputStream_readLimits(&in); + if (tables[i].limits.min != tables[i].limits.max) panic("growable table not supported"); + fprintf(out, "static void (*t%" PRIu32 "[UINT32_C(%" PRIu32 ")])(void);\n", + i, tables[i].limits.min); + } + fputc('\n', out); + } + + struct Mem { + struct Limits limits; + } *mems; + (void)InputStream_skipToSection(&in, WasmSectionId_mem); + uint32_t mems_len = InputStream_readLeb128_u32(&in); + { + mems = malloc(sizeof(struct Mem) * mems_len); + if (mems == NULL) panic("out of memory"); + for (uint32_t i = 0; i < mems_len; i += 1) { + mems[i].limits = InputStream_readLimits(&in); + fprintf(out, "static uint8_t *m%" PRIu32 ";\n" + "static uint32_t p%" PRIu32 ";\n", i, i); + } + fputc('\n', out); + } + + struct Global { + bool mut; + int8_t val_type; + } *globals; + (void)InputStream_skipToSection(&in, WasmSectionId_global); + { + uint32_t len = InputStream_readLeb128_u32(&in); + globals = malloc(sizeof(struct Global) * len); + if (globals == NULL) panic("out of memory"); + for (uint32_t i = 0; i < len; i += 1) { + int64_t val_type = InputStream_readLeb128_i64(&in); + enum WasmMut mut = InputStream_readByte(&in); + fprintf(out, "%s%s g%" PRIu32 " = ", WasmMut_toC(mut), WasmValType_toC(val_type), i); + renderExpr(out, &in); + fputs(";\n", out); + globals[i].mut = mut; + globals[i].val_type = val_type; + } + fputc('\n', out); + } + + (void)InputStream_skipToSection(&in, WasmSectionId_export); + { + uint32_t len = InputStream_readLeb128_u32(&in); + for (uint32_t i = 0; i < len; i += 1) { + char *name = InputStream_readName(&in); + uint8_t kind = InputStream_readByte(&in); + uint32_t idx = InputStream_readLeb128_u32(&in); + switch (kind) { + case 0x00: { + if (idx < imports_len) panic("can't export an import"); + const struct FuncType *func_type = &types[funcs[idx - imports_len].type_idx]; + switch (func_type->result->len) { + case 0: fputs("void", out); break; + case 1: fputs(WasmValType_toC(func_type->result->types[0]), out); break; + default: panic("multiple function returns not supported"); + } + fprintf(out, " %s_%s(", mod, name); + if (func_type->param->len == 0) fputs("void", out); + for (uint32_t param_i = 0; param_i < func_type->param->len; param_i += 1) { + if (param_i > 0) fputs(", ", out); + fprintf(out, "%s l%" PRIu32, WasmValType_toC(func_type->param->types[param_i]), param_i); + } + fprintf(out, + ") {\n" + " init();\n" + " %sf%" PRIu32 "(", + func_type->result->len > 0 ? "return " : "", idx - imports_len); + for (uint32_t param_i = 0; param_i < func_type->param->len; param_i += 1) { + if (param_i > 0) fputs(", ", out); + fprintf(out, "l%" PRIu32, param_i); + } + fputs(");\n}\n", out); + break; + } + + case 0x02: + fprintf(out, "uint8_t **const %s_%s = &m%" PRIu32 ";\n", mod, name, idx); + break; + + default: panic("unsupported export kind"); + } + free(name); + } + fputc('\n', out); + } + + (void)InputStream_skipToSection(&in, WasmSectionId_elem); + { + uint32_t table_i = 0; + uint32_t len = InputStream_readLeb128_u32(&in); + fputs("static void init_elem(void) {\n", out); + for (uint32_t segment_i = 0; segment_i < len; segment_i += 1) { + uint32_t table_idx = 0; + uint32_t elem_type = InputStream_readLeb128_u32(&in); + if (elem_type != 0x00) panic("unsupported elem type"); + uint32_t offset = evalExpr(&in); + uint32_t segment_len = InputStream_readLeb128_u32(&in); + for (uint32_t i = 0; i < segment_len; i += 1) { + uint32_t func_id = InputStream_readLeb128_u32(&in); + fprintf(out, " t%" PRIu32 "[UINT32_C(%" PRIu32 ")] = (void (*)(void))&", + table_idx, offset + i); + if (func_id < imports_len) + fprintf(out, "%s_%s", imports[func_id].mod, imports[func_id].name); + else + fprintf(out, "f%" PRIu32, func_id - imports_len); + fputs(";\n", out); + } + } + fputs("}\n\n", out); + } + + (void)InputStream_skipToSection(&in, WasmSectionId_code); + { + struct FuncGen fg; + FuncGen_init(&fg); + bool *param_used = malloc(sizeof(bool) * max_param_len); + uint32_t *param_stash = malloc(sizeof(uint32_t) * max_param_len); + + uint32_t len = InputStream_readLeb128_u32(&in); + for (uint32_t func_i = 0; func_i < len; func_i += 1) { + FuncGen_reset(&fg); + + uint32_t code_len = InputStream_readLeb128_u32(&in); + const struct FuncType *func_type = &types[funcs[func_i].type_idx]; + fputs("static ", out); + switch (func_type->result->len) { + case 0: fputs("void", out); break; + case 1: fputs(WasmValType_toC(func_type->result->types[0]), out); break; + default: panic("multiple function returns not supported"); + } + fprintf(out, " f%" PRIu32 "(", func_i); + if (func_type->param->len == 0) fputs("void", out); + for (uint32_t param_i = 0; param_i < func_type->param->len; param_i += 1) { + param_used[param_i] = false; + int8_t param_type = func_type->param->types[param_i]; + if (param_i > 0) fputs(", ", out); + FuncGen_localDeclare(&fg, out, param_type); + } + fputs(") {\n", out); + + for (uint32_t local_sets_remaining = InputStream_readLeb128_u32(&in); + local_sets_remaining > 0; local_sets_remaining -= 1) { + uint32_t local_set_len = InputStream_readLeb128_u32(&in); + int64_t val_type = InputStream_readLeb128_i64(&in); + for (; local_set_len > 0; local_set_len -= 1) { + FuncGen_indent(&fg, out); + FuncGen_localDeclare(&fg, out, val_type); + fputs(" = 0;\n", out); + } + } + + uint32_t unreachable_depth = 0; + for (uint32_t result_i = func_type->result->len; result_i > 0; ) { + result_i -= 1; + FuncGen_indent(&fg, out); + (void)FuncGen_localDeclare(&fg, out, + func_type->result->types[result_i]); + fputs(";\n", out); + } + FuncGen_blockBegin(&fg, out, WasmOpcode_block, funcs[func_i].type_idx); + while (!FuncGen_done(&fg)) { + //static const char *mnemonics[] = { + // [WasmOpcode_unreachable] = "unreachable", + // [WasmOpcode_nop] = "nop", + // [WasmOpcode_block] = "block", + // [WasmOpcode_loop] = "loop", + // [WasmOpcode_if] = "if", + // [WasmOpcode_else] = "else", + // [WasmOpcode_end] = "end", + // [WasmOpcode_br] = "br", + // [WasmOpcode_br_if] = "br_if", + // [WasmOpcode_br_table] = "br_table", + // [WasmOpcode_return] = "return", + // [WasmOpcode_call] = "call", + // [WasmOpcode_call_indirect] = "call_indirect", + // + // [WasmOpcode_drop] = "drop", + // [WasmOpcode_select] = "select", + // [WasmOpcode_select_t] = "select t", + // + // [WasmOpcode_local_get] = "local.get", + // [WasmOpcode_local_set] = "local.set", + // [WasmOpcode_local_tee] = "local.tee", + // [WasmOpcode_global_get] = "global.get", + // [WasmOpcode_global_set] = "global.set", + // [WasmOpcode_table_get] = "table.get", + // [WasmOpcode_table_set] = "table.set", + // + // [WasmOpcode_i32_load] = "i32.load", + // [WasmOpcode_i64_load] = "i64.load", + // [WasmOpcode_f32_load] = "f32.load", + // [WasmOpcode_f64_load] = "f64.load", + // [WasmOpcode_i32_load8_s] = "i32.load8_s", + // [WasmOpcode_i32_load8_u] = "i32.load8_u", + // [WasmOpcode_i32_load16_s] = "i32.load16_s", + // [WasmOpcode_i32_load16_u] = "i32.load16_u", + // [WasmOpcode_i64_load8_s] = "i64.load8_s", + // [WasmOpcode_i64_load8_u] = "i64.load8_u", + // [WasmOpcode_i64_load16_s] = "i64.load16_s", + // [WasmOpcode_i64_load16_u] = "i64.load16_u", + // [WasmOpcode_i64_load32_s] = "i64.load32_s", + // [WasmOpcode_i64_load32_u] = "i64.load32_u", + // [WasmOpcode_i32_store] = "i32.store", + // [WasmOpcode_i64_store] = "i64.store", + // [WasmOpcode_f32_store] = "f32.store", + // [WasmOpcode_f64_store] = "f64.store", + // [WasmOpcode_i32_store8] = "i32.store8", + // [WasmOpcode_i32_store16] = "i32.store16", + // [WasmOpcode_i64_store8] = "i64.store8", + // [WasmOpcode_i64_store16] = "i64.store16", + // [WasmOpcode_i64_store32] = "i64.store32", + // [WasmOpcode_memory_size] = "memory.size", + // [WasmOpcode_memory_grow] = "memory.grow", + // + // [WasmOpcode_i32_const] = "i32.const", + // [WasmOpcode_i64_const] = "i64.const", + // [WasmOpcode_f32_const] = "f32.const", + // [WasmOpcode_f64_const] = "f64.const", + // + // [WasmOpcode_i32_eqz] = "i32.eqz", + // [WasmOpcode_i32_eq] = "i32.eq", + // [WasmOpcode_i32_ne] = "i32.ne", + // [WasmOpcode_i32_lt_s] = "i32.lt_s", + // [WasmOpcode_i32_lt_u] = "i32.lt_u", + // [WasmOpcode_i32_gt_s] = "i32.gt_s", + // [WasmOpcode_i32_gt_u] = "i32.gt_u", + // [WasmOpcode_i32_le_s] = "i32.le_s", + // [WasmOpcode_i32_le_u] = "i32.le_u", + // [WasmOpcode_i32_ge_s] = "i32.ge_s", + // [WasmOpcode_i32_ge_u] = "i32.ge_u", + // + // [WasmOpcode_i64_eqz] = "i64.eqz", + // [WasmOpcode_i64_eq] = "i64.eq", + // [WasmOpcode_i64_ne] = "i64.ne", + // [WasmOpcode_i64_lt_s] = "i64.lt_s", + // [WasmOpcode_i64_lt_u] = "i64.lt_u", + // [WasmOpcode_i64_gt_s] = "i64.gt_s", + // [WasmOpcode_i64_gt_u] = "i64.gt_u", + // [WasmOpcode_i64_le_s] = "i64.le_s", + // [WasmOpcode_i64_le_u] = "i64.le_u", + // [WasmOpcode_i64_ge_s] = "i64.ge_s", + // [WasmOpcode_i64_ge_u] = "i64.ge_u", + // + // [WasmOpcode_f32_eq] = "f32.eq", + // [WasmOpcode_f32_ne] = "f32.ne", + // [WasmOpcode_f32_lt] = "f32.lt", + // [WasmOpcode_f32_gt] = "f32.gt", + // [WasmOpcode_f32_le] = "f32.le", + // [WasmOpcode_f32_ge] = "f32.ge", + // + // [WasmOpcode_f64_eq] = "f64.eq", + // [WasmOpcode_f64_ne] = "f64.ne", + // [WasmOpcode_f64_lt] = "f64.lt", + // [WasmOpcode_f64_gt] = "f64.gt", + // [WasmOpcode_f64_le] = "f64.le", + // [WasmOpcode_f64_ge] = "f64.ge", + // + // [WasmOpcode_i32_clz] = "i32.clz", + // [WasmOpcode_i32_ctz] = "i32.ctz", + // [WasmOpcode_i32_popcnt] = "i32.popcnt", + // [WasmOpcode_i32_add] = "i32.add", + // [WasmOpcode_i32_sub] = "i32.sub", + // [WasmOpcode_i32_mul] = "i32.mul", + // [WasmOpcode_i32_div_s] = "i32.div_s", + // [WasmOpcode_i32_div_u] = "i32.div_u", + // [WasmOpcode_i32_rem_s] = "i32.rem_s", + // [WasmOpcode_i32_rem_u] = "i32.rem_u", + // [WasmOpcode_i32_and] = "i32.and", + // [WasmOpcode_i32_or] = "i32.or", + // [WasmOpcode_i32_xor] = "i32.xor", + // [WasmOpcode_i32_shl] = "i32.shl", + // [WasmOpcode_i32_shr_s] = "i32.shr_s", + // [WasmOpcode_i32_shr_u] = "i32.shr_u", + // [WasmOpcode_i32_rotl] = "i32.rotl", + // [WasmOpcode_i32_rotr] = "i32.rotr", + // + // [WasmOpcode_i64_clz] = "i64.clz", + // [WasmOpcode_i64_ctz] = "i64.ctz", + // [WasmOpcode_i64_popcnt] = "i64.popcnt", + // [WasmOpcode_i64_add] = "i64.add", + // [WasmOpcode_i64_sub] = "i64.sub", + // [WasmOpcode_i64_mul] = "i64.mul", + // [WasmOpcode_i64_div_s] = "i64.div_s", + // [WasmOpcode_i64_div_u] = "i64.div_u", + // [WasmOpcode_i64_rem_s] = "i64.rem_s", + // [WasmOpcode_i64_rem_u] = "i64.rem_u", + // [WasmOpcode_i64_and] = "i64.and", + // [WasmOpcode_i64_or] = "i64.or", + // [WasmOpcode_i64_xor] = "i64.xor", + // [WasmOpcode_i64_shl] = "i64.shl", + // [WasmOpcode_i64_shr_s] = "i64.shr_s", + // [WasmOpcode_i64_shr_u] = "i64.shr_u", + // [WasmOpcode_i64_rotl] = "i64.rotl", + // [WasmOpcode_i64_rotr] = "i64.rotr", + // + // [WasmOpcode_f32_abs] = "f32.abs", + // [WasmOpcode_f32_neg] = "f32.neg", + // [WasmOpcode_f32_ceil] = "f32.ceil", + // [WasmOpcode_f32_floor] = "f32.floor", + // [WasmOpcode_f32_trunc] = "f32.trunc", + // [WasmOpcode_f32_nearest] = "f32.nearest", + // [WasmOpcode_f32_sqrt] = "f32.sqrt", + // [WasmOpcode_f32_add] = "f32.add", + // [WasmOpcode_f32_sub] = "f32.sub", + // [WasmOpcode_f32_mul] = "f32.mul", + // [WasmOpcode_f32_div] = "f32.div", + // [WasmOpcode_f32_min] = "f32.min", + // [WasmOpcode_f32_max] = "f32.max", + // [WasmOpcode_f32_copysign] = "f32.copysign", + // + // [WasmOpcode_f64_abs] = "f64.abs", + // [WasmOpcode_f64_neg] = "f64.neg", + // [WasmOpcode_f64_ceil] = "f64.ceil", + // [WasmOpcode_f64_floor] = "f64.floor", + // [WasmOpcode_f64_trunc] = "f64.trunc", + // [WasmOpcode_f64_nearest] = "f64.nearest", + // [WasmOpcode_f64_sqrt] = "f64.sqrt", + // [WasmOpcode_f64_add] = "f64.add", + // [WasmOpcode_f64_sub] = "f64.sub", + // [WasmOpcode_f64_mul] = "f64.mul", + // [WasmOpcode_f64_div] = "f64.div", + // [WasmOpcode_f64_min] = "f64.min", + // [WasmOpcode_f64_max] = "f64.max", + // [WasmOpcode_f64_copysign] = "f64.copysign", + // + // [WasmOpcode_i32_wrap_i64] = "i32.wrap_i64", + // [WasmOpcode_i32_trunc_f32_s] = "i32.trunc_f32_s", + // [WasmOpcode_i32_trunc_f32_u] = "i32.trunc_f32_u", + // [WasmOpcode_i32_trunc_f64_s] = "i32.trunc_f64_s", + // [WasmOpcode_i32_trunc_f64_u] = "i32.trunc_f64_u", + // [WasmOpcode_i64_extend_i32_s] = "i64.extend_i32_s", + // [WasmOpcode_i64_extend_i32_u] = "i64.extend_i32_u", + // [WasmOpcode_i64_trunc_f32_s] = "i64.trunc_f32_s", + // [WasmOpcode_i64_trunc_f32_u] = "i64.trunc_f32_u", + // [WasmOpcode_i64_trunc_f64_s] = "i64.trunc_f64_s", + // [WasmOpcode_i64_trunc_f64_u] = "i64.trunc_f64_u", + // [WasmOpcode_f32_convert_i32_s] = "f32.convert_i32_s", + // [WasmOpcode_f32_convert_i32_u] = "f32.convert_i32_u", + // [WasmOpcode_f32_convert_i64_s] = "f32.convert_i64_s", + // [WasmOpcode_f32_convert_i64_u] = "f32.convert_i64_u", + // [WasmOpcode_f32_demote_f64] = "f32.demote_f64", + // [WasmOpcode_f64_convert_i32_s] = "f64.convert_i32_s", + // [WasmOpcode_f64_convert_i32_u] = "f64.convert_i32_u", + // [WasmOpcode_f64_convert_i64_s] = "f64.convert_i64_s", + // [WasmOpcode_f64_convert_i64_u] = "f64.convert_i64_u", + // [WasmOpcode_f64_promote_f32] = "f64.promote_f32", + // [WasmOpcode_i32_reinterpret_f32] = "i32.reinterpret_f32", + // [WasmOpcode_i64_reinterpret_f64] = "i64.reinterpret_f64", + // [WasmOpcode_f32_reinterpret_i32] = "f32.reinterpret_i32", + // [WasmOpcode_f64_reinterpret_i64] = "f64.reinterpret_i64", + // + // [WasmOpcode_i32_extend8_s] = "i32.extend8_s", + // [WasmOpcode_i32_extend16_s] = "i32.extend16_s", + // [WasmOpcode_i64_extend8_s] = "i64.extend8_s", + // [WasmOpcode_i64_extend16_s] = "i64.extend16_s", + // [WasmOpcode_i64_extend32_s] = "i64.extend32_s", + // + // [WasmOpcode_prefixed] = "prefixed", + //}; + uint8_t opcode = InputStream_readByte(&in); + //FuncGen_indent(&fg, out); + //fprintf(out, "// %2u: ", fg.stack_i); + //if (mnemonics[opcode]) + // fprintf(out, "%s\n", mnemonics[opcode]); + //else + // fprintf(out, "%02hhX\n", opcode); + //fflush(out); // DEBUG + switch (opcode) { + case WasmOpcode_unreachable: + if (unreachable_depth == 0) { + FuncGen_indent(&fg, out); + fprintf(out, "abort();\n"); + unreachable_depth += 1; + } + break; + case WasmOpcode_nop: + break; + case WasmOpcode_block: + case WasmOpcode_loop: + case WasmOpcode_if: { + int64_t block_type = InputStream_readLeb128_i64(&in); + if (unreachable_depth == 0) { + const struct FuncType *func_type = FuncType_blockType(types, block_type); + for (uint32_t param_i = func_type->param->len; param_i > 0; ) { + param_i -= 1; + FuncGen_indent(&fg, out); + param_stash[param_i] = + FuncGen_localDeclare(&fg, out, func_type->param->types[param_i]); + fprintf(out, " = l%" PRIu32 ";\n", FuncGen_stackPop(&fg)); + } + for (uint32_t result_i = func_type->result->len; result_i > 0; ) { + result_i -= 1; + FuncGen_indent(&fg, out); + (void)FuncGen_localDeclare(&fg, out, + func_type->result->types[result_i]); + fputs(";\n", out); + } + FuncGen_blockBegin(&fg, out, opcode, block_type); + for (uint32_t param_i = 0; param_i < func_type->param->len; param_i += 1) { + FuncGen_stackPush(&fg, out, func_type->param->types[param_i]); + fprintf(out, " = l%" PRIu32 ";\n", param_stash[param_i]); + } + } else unreachable_depth += 1; + break; + } + case WasmOpcode_else: + case WasmOpcode_end: + if (unreachable_depth <= 1) { + const struct ResultType *result_type = + FuncType_blockType(types, FuncGen_blockType(&fg, 0))->result; + uint32_t label = FuncGen_blockLabel(&fg, 0); + if (unreachable_depth == 0) { + const struct ResultType *result_type = + FuncType_blockType(types, FuncGen_blockType(&fg, 0))->result; + for (uint32_t result_i = result_type->len; result_i > 0; ) { + result_i -= 1; + FuncGen_indent(&fg, out); + fprintf(out, "l%" PRIu32 " = l%" PRIu32 ";\n", + label - result_type->len + result_i, FuncGen_stackPop(&fg)); + } + } else unreachable_depth -= 1; + switch (opcode) { + case WasmOpcode_else: + FuncGen_outdent(&fg, out); + fputs("} else {\n", out); + break; + case WasmOpcode_end: + FuncGen_blockEnd(&fg, out); + for (uint32_t result_i = 0; result_i < result_type->len; + result_i += 1) { + FuncGen_stackPush(&fg, out, result_type->types[result_i]); + fprintf(out, "l%" PRIu32 ";\n", + label - result_type->len + result_i); + } + break; + } + } else if (opcode == WasmOpcode_end) unreachable_depth -= 1; + break; + case WasmOpcode_br: + case WasmOpcode_br_if: { + uint32_t label_idx = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + enum WasmOpcode kind = FuncGen_blockKind(&fg, label_idx); + const struct FuncType *func_type = + FuncType_blockType(types, FuncGen_blockType(&fg, label_idx)); + uint32_t label = FuncGen_blockLabel(&fg, label_idx); + + FuncGen_indent(&fg, out); + if (opcode == WasmOpcode_br_if) + fprintf(out, "if (l%" PRIu32 ") ", FuncGen_stackPop(&fg)); + fputs("{\n", out); + const struct ResultType *label_type; + uint32_t lhs; + switch (kind) { + case WasmOpcode_loop: + label_type = func_type->param; + lhs = label - func_type->result->len - func_type->param->len; + break; + default: + label_type = func_type->result; + lhs = label - func_type->result->len; + break; + } + for (uint32_t stack_i = 0; stack_i < label_type->len; stack_i += 1) { + uint32_t rhs; + switch (opcode) { + case WasmOpcode_br: + rhs = FuncGen_stackPop(&fg); + break; + case WasmOpcode_br_if: + rhs = FuncGen_stackAt(&fg, stack_i); + break; + default: panic("unexpected opcode"); + } + FuncGen_cont(&fg, out); + fprintf(out, "l%" PRIu32 " = l%" PRIu32 ";\n", lhs, rhs); + lhs += 1; + } + FuncGen_cont(&fg, out); + fprintf(out, "goto l%" PRIu32 ";\n", label); + FuncGen_indent(&fg, out); + fprintf(out, "}\n"); + if (opcode == WasmOpcode_br) unreachable_depth += 1; + } + break; + } + case WasmOpcode_br_table: { + if (unreachable_depth == 0) { + FuncGen_indent(&fg, out); + fprintf(out, "switch (l%" PRIu32 ") {\n", FuncGen_stackPop(&fg)); + } + uint32_t label_len = InputStream_readLeb128_u32(&in); + for (uint32_t i = 0; i < label_len; i += 1) { + uint32_t label = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + FuncGen_indent(&fg, out); + fprintf(out, "case %u: goto l%" PRIu32 ";\n", + i, FuncGen_blockLabel(&fg, label)); + } + } + uint32_t label = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + FuncGen_indent(&fg, out); + fprintf(out, "default: goto l%" PRIu32 ";\n", + FuncGen_blockLabel(&fg, label)); + FuncGen_indent(&fg, out); + fputs("}\n", out); + unreachable_depth += 1; + } + break; + } + case WasmOpcode_return: + if (unreachable_depth == 0) { + FuncGen_indent(&fg, out); + fputs("return", out); + switch (func_type->result->len) { + case 0: break; + case 1: fprintf(out, " l%" PRIu32, FuncGen_stackPop(&fg)); break; + default: panic("multiple function returns not supported"); + } + fputs(";\n", out); + unreachable_depth += 1; + } + break; + case WasmOpcode_call: + case WasmOpcode_call_indirect: { + uint32_t func_id; + uint32_t type_idx; + uint32_t table_idx; + switch (opcode) { + case WasmOpcode_call: + func_id = InputStream_readLeb128_u32(&in); + if (func_id < imports_len) + type_idx = imports[func_id].type_idx; + else + type_idx = funcs[func_id - imports_len].type_idx; + break; + case WasmOpcode_call_indirect: + type_idx = InputStream_readLeb128_u32(&in); + table_idx = InputStream_readLeb128_u32(&in); + func_id = FuncGen_stackPop(&fg); + break; + } + if (unreachable_depth == 0) { + const struct FuncType *callee_func_type = &types[type_idx]; + for (uint32_t param_i = callee_func_type->param->len; param_i > 0; ) { + param_i -= 1; + param_stash[param_i] = FuncGen_stackPop(&fg); + } + switch (callee_func_type->result->len) { + case 0: FuncGen_indent(&fg, out); break; + case 1: FuncGen_stackPush(&fg, out, callee_func_type->result->types[0]); break; + default: panic("multiple function returns not supported"); + } + switch (opcode) { + case WasmOpcode_call: + if (func_id < imports_len) + fprintf(out, "%s_%s", imports[func_id].mod, imports[func_id].name); + else + fprintf(out, "f%" PRIu32, func_id - imports_len); + break; + case WasmOpcode_call_indirect: + fputs("(*(", out); + switch (callee_func_type->result->len) { + case 0: fputs("void", out); break; + case 1: fputs(WasmValType_toC(callee_func_type->result->types[0]), out); break; + default: panic("multiple function returns not supported"); + } + fputs(" (*)(", out); + if (callee_func_type->param->len == 0) fputs("void", out); + for (uint32_t param_i = 0; param_i < callee_func_type->param->len; param_i += 1) { + if (param_i > 0) fputs(", ", out); + fputs(WasmValType_toC(callee_func_type->param->types[param_i]), out); + } + fprintf(out, "))t%" PRIu32 "[l%" PRIu32 "])", table_idx, func_id); + break; + } + fputc('(', out); + for (uint32_t param_i = 0; param_i < callee_func_type->param->len; + param_i += 1) { + if (param_i > 0) fputs(", ", out); + fprintf(out, "l%" PRIu32, param_stash[param_i]); + } + fputs(");\n", out); + } + break; + } + + case WasmOpcode_drop: + if (unreachable_depth == 0) { + FuncGen_indent(&fg, out); + fprintf(out, "(void)l%" PRIu32 ";\n", FuncGen_stackPop(&fg)); + } + break; + case WasmOpcode_select: + if (unreachable_depth == 0) { + uint32_t cond = FuncGen_stackPop(&fg); + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, FuncGen_localType(&fg, lhs)); + fprintf(out, "l%" PRIu32 " ? l%" PRIu32 " : l%" PRIu32 ";\n", + cond, lhs, rhs); + } + break; + + case WasmOpcode_local_get: { + uint32_t local_idx = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + if (local_idx < func_type->param->len) param_used[local_idx] = true; + FuncGen_stackPush(&fg, out, FuncGen_localType(&fg, local_idx)); + fprintf(out, "l%" PRIu32 ";\n", local_idx); + } + break; + } + case WasmOpcode_local_set: { + uint32_t local_idx = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + if (local_idx < func_type->param->len) param_used[local_idx] = true; + FuncGen_indent(&fg, out); + fprintf(out, "l%" PRIu32 " = l%" PRIu32 ";\n", + local_idx, FuncGen_stackPop(&fg)); + } + break; + } + case WasmOpcode_local_tee: { + uint32_t local_idx = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + if (local_idx < func_type->param->len) param_used[local_idx] = true; + FuncGen_indent(&fg, out); + fprintf(out, "l%" PRIu32 " = l%" PRIu32 ";\n", + local_idx, FuncGen_stackAt(&fg, 0)); + } + break; + } + + case WasmOpcode_global_get: { + uint32_t global_idx = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + FuncGen_stackPush(&fg, out, globals[global_idx].val_type); + fprintf(out, "g%" PRIu32 ";\n", global_idx); + } + break; + } + case WasmOpcode_global_set: { + uint32_t global_idx = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + FuncGen_indent(&fg, out); + fprintf(out, "g%" PRIu32 " = l%" PRIu32 ";\n", + global_idx, FuncGen_stackPop(&fg)); + } + break; + } + + case WasmOpcode_table_get: + case WasmOpcode_table_set: + (void)InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) panic("unimplemented opcode"); + break; + + case WasmOpcode_i32_load: { + uint32_t align = InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t base = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + if (align < 2 || isBigEndian) { + fseek(out, -1, SEEK_CUR); + fputc('\n', out); + for (uint8_t byte_i = 0; byte_i < 4; byte_i += 1) { + if (byte_i > 0) fputs(" |\n", out); + FuncGen_cont(&fg, out); + fprintf(out, "(uint32_t)m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" + PRIu32 ")] << %2u", 0, base, offset + byte_i, byte_i << 3); + } + } else fprintf(out, "*(const uint32_t *)&m%" PRIu32 "[l%" PRIu32 + " + UINT32_C(%" PRIu32 ")]", 0, base, offset); + fputs(";\n", out); + } + break; + } + case WasmOpcode_i64_load: { + uint32_t align = InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t base = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + if (align < 3 || isBigEndian) { + fseek(out, -1, SEEK_CUR); + fputc('\n', out); + for (uint8_t byte_i = 0; byte_i < 8; byte_i += 1) { + if (byte_i > 0) fputs(" |\n", out); + FuncGen_cont(&fg, out); + fprintf(out, "(uint64_t)m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" + PRIu32 ")] << %2u", 0, base, offset + byte_i, byte_i << 3); + } + } else fprintf(out, "*(const uint64_t *)&m%" PRIu32 "[l%" PRIu32 + " + UINT32_C(%" PRIu32 ")]", 0, base, offset); + fputs(";\n", out); + } + break; + } + case WasmOpcode_f32_load: { + uint32_t align = InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t base = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + if (align < 2 || isBigEndian) { + fputs("f32_reinterpret_i32(\n", out); + for (uint8_t byte_i = 0; byte_i < 4; byte_i += 1) { + if (byte_i > 0) fputs(" |\n", out); + FuncGen_cont(&fg, out); + fprintf(out, "(uint32_t)m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" + PRIu32 ")] << %2u", 0, base, offset + byte_i, byte_i << 3); + } + fputc(')', out); + } else fprintf(out, "*(const float *)&m%" PRIu32 "[l%" PRIu32 + " + UINT32_C(%" PRIu32 ")]", 0, base, offset); + fputs(";\n", out); + } + break; + } + case WasmOpcode_f64_load: { + uint32_t align = InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t base = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + if (align < 3 || isBigEndian) { + fputs("f64_reinterpret_i64(\n", out); + for (uint8_t byte_i = 0; byte_i < 8; byte_i += 1) { + if (byte_i > 0) fputs(" |\n", out); + FuncGen_cont(&fg, out); + fprintf(out, "(uint64_t)m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" + PRIu32 ")] << %2u", 0, base, offset + byte_i, byte_i << 3); + } + fputc(')', out); + } else fprintf(out, "*(const double *)&m%" PRIu32 "[l%" PRIu32 + " + UINT32_C(%" PRIu32 ")]", 0, base, offset); + fputs(";\n", out); + } + break; + } + case WasmOpcode_i32_load8_s: { + (void)InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t base = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(int8_t)m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" PRIu32 ")];\n", + 0, base, offset); + } + break; + } + case WasmOpcode_i32_load8_u: { + (void)InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t base = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" PRIu32 ")];\n", + 0, base, offset); + } + break; + } + case WasmOpcode_i32_load16_s: { + uint32_t align = InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t base = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + if (align < 1 || isBigEndian) { + fputs("(int16_t)(\n", out); + for (uint8_t byte_i = 0; byte_i < 2; byte_i += 1) { + if (byte_i > 0) fputs(" |\n", out); + FuncGen_cont(&fg, out); + fprintf(out, "(uint16_t)m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" + PRIu32 ")] << %2u", 0, base, offset + byte_i, byte_i << 3); + } + fputc(')', out); + } else fprintf(out, "*(const int16_t *)&m%" PRIu32 "[l%" PRIu32 + " + UINT32_C(%" PRIu32 ")]", 0, base, offset); + fputs(";\n", out); + } + break; + } + case WasmOpcode_i32_load16_u: { + uint32_t align = InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t base = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + if (align < 1 || isBigEndian) { + fseek(out, -1, SEEK_CUR); + fputc('\n', out); + for (uint8_t byte_i = 0; byte_i < 2; byte_i += 1) { + if (byte_i > 0) fputs(" |\n", out); + FuncGen_cont(&fg, out); + fprintf(out, "(uint16_t)m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" + PRIu32 ")] << %2u", 0, base, offset + byte_i, byte_i << 3); + } + } else fprintf(out, "*(const uint16_t *)&m%" PRIu32 "[l%" PRIu32 + " + UINT32_C(%" PRIu32 ")]", 0, base, offset); + fputs(";\n", out); + } + break; + } + case WasmOpcode_i64_load8_s: { + (void)InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t base = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "(int8_t)m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" PRIu32 ")];\n", + 0, base, offset); + } + break; + } + case WasmOpcode_i64_load8_u: { + (void)InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t base = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" PRIu32 ")];\n", + 0, base, offset); + } + break; + } + case WasmOpcode_i64_load16_s: { + uint32_t align = InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t base = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + if (align < 1 || isBigEndian) { + fputs("(int16_t)(\n", out); + for (uint8_t byte_i = 0; byte_i < 2; byte_i += 1) { + if (byte_i > 0) fputs(" |\n", out); + FuncGen_cont(&fg, out); + fprintf(out, "(uint16_t)m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" + PRIu32 ")] << %2u", 0, base, offset + byte_i, byte_i << 3); + } + fputc(')', out); + } else fprintf(out, "*(const int16_t *)&m%" PRIu32 "[l%" PRIu32 + " + UINT32_C(%" PRIu32 ")]", 0, base, offset); + fputs(";\n", out); + } + break; + } + case WasmOpcode_i64_load16_u: { + uint32_t align = InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t base = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + if (align < 1 || isBigEndian) { + fseek(out, -1, SEEK_CUR); + fputc('\n', out); + for (uint8_t byte_i = 0; byte_i < 2; byte_i += 1) { + if (byte_i > 0) fputs(" |\n", out); + FuncGen_cont(&fg, out); + fprintf(out, "(uint16_t)m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" + PRIu32 ")] << %2u", 0, base, offset + byte_i, byte_i << 3); + } + } else fprintf(out, "*(const uint16_t *)&m%" PRIu32 "[l%" PRIu32 + " + UINT32_C(%" PRIu32 ")]", 0, base, offset); + fputs(";\n", out); + } + break; + } + case WasmOpcode_i64_load32_s: { + uint32_t align = InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t base = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + if (align < 2 || isBigEndian) { + fputs("(int32_t)(\n", out); + for (uint8_t byte_i = 0; byte_i < 4; byte_i += 1) { + if (byte_i > 0) fputs(" |\n", out); + FuncGen_cont(&fg, out); + fprintf(out, "(uint32_t)m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" + PRIu32 ")] << %2u", 0, base, offset + byte_i, byte_i << 3); + } + fputc(')', out); + } else fprintf(out, "*(const int32_t *)&m%" PRIu32 "[l%" PRIu32 + " + UINT32_C(%" PRIu32 ")]", 0, base, offset); + fputs(";\n", out); + } + break; + } + case WasmOpcode_i64_load32_u: { + uint32_t align = InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t base = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + if (align < 2 || isBigEndian) { + fseek(out, -1, SEEK_CUR); + fputc('\n', out); + for (uint8_t byte_i = 0; byte_i < 4; byte_i += 1) { + if (byte_i > 0) fputs(" |\n", out); + FuncGen_cont(&fg, out); + fprintf(out, "(uint32_t)m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" + PRIu32 ")] << %2u", 0, base, offset + byte_i, byte_i << 3); + } + } else fprintf(out, "*(const uint32_t *)&m%" PRIu32 "[l%" PRIu32 + " + UINT32_C(%" PRIu32 ")]", 0, base, offset); + fputs(";\n", out); + } + break; + } + + case WasmOpcode_i32_store: { + uint32_t align = InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t value = FuncGen_stackPop(&fg); + uint32_t base = FuncGen_stackPop(&fg); + if (align < 2 || isBigEndian) { + for (uint8_t byte_i = 0; byte_i < 4; byte_i += 1) { + FuncGen_indent(&fg, out); + fprintf(out, "m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" PRIu32 ")] = " + "(uint8_t)(l%" PRIu32 " >> %2u);\n", + 0, base, offset + byte_i, value, byte_i << 3); + } + } else { + FuncGen_indent(&fg, out); + fprintf(out, "*(uint32_t *)&m%" PRIu32 "[l%" PRIu32 + " + UINT32_C(%" PRIu32 ")] = l%" PRIu32 ";\n", + 0, base, offset, value); + } + } + break; + } + case WasmOpcode_i64_store: { + uint32_t align = InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t value = FuncGen_stackPop(&fg); + uint32_t base = FuncGen_stackPop(&fg); + if (align < 3 || isBigEndian) { + for (uint8_t byte_i = 0; byte_i < 8; byte_i += 1) { + FuncGen_indent(&fg, out); + fprintf(out, "m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" PRIu32 ")] = " + "(uint8_t)(l%" PRIu32 " >> %2u);\n", + 0, base, offset + byte_i, value, byte_i << 3); + } + } else { + FuncGen_indent(&fg, out); + fprintf(out, "*(uint64_t *)&m%" PRIu32 "[l%" PRIu32 + " + UINT32_C(%" PRIu32 ")] = l%" PRIu32 ";\n", + 0, base, offset, value); + } + } + break; + } + case WasmOpcode_f32_store: { + uint32_t align = InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t value = FuncGen_stackPop(&fg); + uint32_t base = FuncGen_stackPop(&fg); + if (align < 2 || isBigEndian) { + for (uint8_t byte_i = 0; byte_i < 4; byte_i += 1) { + FuncGen_indent(&fg, out); + fprintf(out, "m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" PRIu32 ")] = " + "(uint8_t)(i32_reinterpret_f32(l%" PRIu32 ") >> %2u);\n", + 0, base, offset + byte_i, value, byte_i << 3); + } + } else { + FuncGen_indent(&fg, out); + fprintf(out, "*(float *)&m%" PRIu32 "[l%" PRIu32 + " + UINT32_C(%" PRIu32 ")] = l%" PRIu32 ";\n", + 0, base, offset, value); + } + } + break; + } + case WasmOpcode_f64_store: { + uint32_t align = InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t value = FuncGen_stackPop(&fg); + uint32_t base = FuncGen_stackPop(&fg); + if (align < 3 || isBigEndian) { + for (uint8_t byte_i = 0; byte_i < 8; byte_i += 1) { + FuncGen_indent(&fg, out); + fprintf(out, "m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" PRIu32 ")] = " + "(uint8_t)(i64_reinterpret_f64(l%" PRIu32 ") >> %2u);\n", + 0, base, offset + byte_i, value, byte_i << 3); + } + } else { + FuncGen_indent(&fg, out); + fprintf(out, "*(double *)&m%" PRIu32 "[l%" PRIu32 + " + UINT32_C(%" PRIu32 ")] = l%" PRIu32 ";\n", + 0, base, offset, value); + } + } + break; + } + case WasmOpcode_i32_store8: { + (void)InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t value = FuncGen_stackPop(&fg); + uint32_t base = FuncGen_stackPop(&fg); + FuncGen_indent(&fg, out); + fprintf(out, "m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" PRIu32 + ")] = (uint8_t)l%" PRIu32 ";\n", 0, base, offset, value); + } + break; + } + case WasmOpcode_i32_store16: { + uint32_t align = InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t value = FuncGen_stackPop(&fg); + uint32_t base = FuncGen_stackPop(&fg); + if (align < 1 || isBigEndian) { + for (uint8_t byte_i = 0; byte_i < 2; byte_i += 1) { + FuncGen_indent(&fg, out); + fprintf(out, "m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" PRIu32 ")] = " + "(uint8_t)(l%" PRIu32 " >> %2u);\n", + 0, base, offset + byte_i, value, byte_i << 3); + } + } else { + FuncGen_indent(&fg, out); + fprintf(out, "*(uint16_t *)&m%" PRIu32 "[l%" PRIu32 + " + UINT32_C(%" PRIu32 ")] = (uint16_t)l%" PRIu32 ";\n", + 0, base, offset, value); + } + } + break; + } + case WasmOpcode_i64_store8: { + (void)InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t value = FuncGen_stackPop(&fg); + uint32_t base = FuncGen_stackPop(&fg); + FuncGen_indent(&fg, out); + fprintf(out, "m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" PRIu32 + ")] = (uint8_t)l%" PRIu32 ";\n", 0, base, offset, value); + } + break; + } + case WasmOpcode_i64_store16: { + uint32_t align = InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t value = FuncGen_stackPop(&fg); + uint32_t base = FuncGen_stackPop(&fg); + if (align < 1 || isBigEndian) { + for (uint8_t byte_i = 0; byte_i < 2; byte_i += 1) { + FuncGen_indent(&fg, out); + fprintf(out, "m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" PRIu32 ")] = " + "(uint8_t)(l%" PRIu32 " >> %2u);\n", + 0, base, offset + byte_i, value, byte_i << 3); + } + } else { + FuncGen_indent(&fg, out); + fprintf(out, "*(uint16_t *)&m%" PRIu32 "[l%" PRIu32 + " + UINT32_C(%" PRIu32 ")] = (uint16_t)l%" PRIu32 ";\n", + 0, base, offset, value); + } + } + break; + } + case WasmOpcode_i64_store32: { + uint32_t align = InputStream_readLeb128_u32(&in); + uint32_t offset = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t value = FuncGen_stackPop(&fg); + uint32_t base = FuncGen_stackPop(&fg); + if (align < 2 || isBigEndian) { + for (uint8_t byte_i = 0; byte_i < 4; byte_i += 1) { + FuncGen_indent(&fg, out); + fprintf(out, "m%" PRIu32 "[l%" PRIu32 " + UINT32_C(%" PRIu32 ")] = " + "(uint8_t)(l%" PRIu32 " >> %2u);\n", + 0, base, offset + byte_i, value, byte_i << 3); + } + } else { + FuncGen_indent(&fg, out); + fprintf(out, "*(uint32_t *)&m%" PRIu32 "[l%" PRIu32 + " + UINT32_C(%" PRIu32 ")] = (uint32_t)l%" PRIu32 ";\n", + 0, base, offset, value); + } + } + break; + } + + case WasmOpcode_memory_size: { + uint32_t mem_idx = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "p%" PRIu32 ";\n", mem_idx); + } + break; + } + case WasmOpcode_memory_grow: { + uint32_t mem_idx = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t pages = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "memory_grow(&m%" PRIu32 ", &p%" PRIu32 ", l%" PRIu32 ");\n", + mem_idx, mem_idx, pages); + } + break; + } + + case WasmOpcode_i32_const: { + uint32_t value = (uint32_t)InputStream_readLeb128_i32(&in); + if (unreachable_depth == 0) { + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "UINT32_C(0x%" PRIX32 ");\n", value); + } + break; + } + case WasmOpcode_i64_const: { + uint64_t value = (uint64_t)InputStream_readLeb128_i64(&in); + if (unreachable_depth == 0) { + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "UINT64_C(0x%" PRIX64 ");\n", value); + } + break; + } + case WasmOpcode_f32_const: { + uint32_t value = InputStream_readLittle_u32(&in); + if (unreachable_depth == 0) { + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "f32_reinterpret_i32(UINT32_C(0x%" PRIX32 "));\n", value); + } + break; + } + case WasmOpcode_f64_const: { + uint64_t value = InputStream_readLittle_u64(&in); + if (unreachable_depth == 0) { + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "f64_reinterpret_i64(UINT64_C(0x%" PRIX64 "));\n", value); + } + break; + } + + case WasmOpcode_i32_eqz: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "!l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_i32_eq: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " == l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_ne: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " != l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_lt_s: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(int32_t)l%" PRIu32 " < (int32_t)l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_lt_u: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " < l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_gt_s: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(int32_t)l%" PRIu32 " > (int32_t)l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_gt_u: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " > l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_le_s: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(int32_t)l%" PRIu32 " <= (int32_t)l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_le_u: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " <= l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_ge_s: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(int32_t)l%" PRIu32 " >= (int32_t)l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_ge_u: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " >= l%" PRIu32 ";\n", lhs, rhs); + } + break; + + case WasmOpcode_i64_eqz: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "!l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_i64_eq: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " == l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_ne: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " != l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_lt_s: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(int64_t)l%" PRIu32 " < (int64_t)l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_lt_u: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " < l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_gt_s: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(int64_t)l%" PRIu32 " > (int64_t)l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_gt_u: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " > l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_le_s: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(int64_t)l%" PRIu32 " <= (int64_t)l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_le_u: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " <= l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_ge_s: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(int64_t)l%" PRIu32 " >= (int64_t)l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_ge_u: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " >= l%" PRIu32 ";\n", lhs, rhs); + } + break; + + case WasmOpcode_f32_eq: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " == l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_f32_ne: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " != l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_f32_lt: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " < l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_f32_gt: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " > l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_f32_le: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " <= l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_f32_ge: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " >= l%" PRIu32 ";\n", lhs, rhs); + } + break; + + case WasmOpcode_f64_eq: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " == l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_f64_ne: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " != l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_f64_lt: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " < l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_f64_gt: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " > l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_f64_le: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " <= l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_f64_ge: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " >= l%" PRIu32 ";\n", lhs, rhs); + } + break; + + case WasmOpcode_i32_clz: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " != 0 ? __builtin_clz(l%" PRIu32 ") : 32;\n", + lhs, lhs); + } + break; + case WasmOpcode_i32_ctz: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " != 0 ? __builtin_ctz(l%" PRIu32 ") : 32;\n", + lhs, lhs); + } + break; + case WasmOpcode_i32_popcnt: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "__builtin_popcount(l%" PRIu32 ");\n", lhs); + } + break; + case WasmOpcode_i32_add: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " + l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_sub: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " - l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_mul: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " * l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_div_s: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(int32_t)l%" PRIu32 " / (int32_t)l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_div_u: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " / l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_rem_s: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(int32_t)l%" PRIu32 " %% (int32_t)l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_rem_u: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " %% l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_and: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " & l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_or: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " | l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_xor: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " ^ l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i32_shl: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " << (l%" PRIu32 " & 0x1F);\n", lhs, rhs); + } + break; + case WasmOpcode_i32_shr_s: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(int32_t)l%" PRIu32 " >> (l%" PRIu32 " & 0x1F);\n", lhs, rhs); + } + break; + case WasmOpcode_i32_shr_u: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " >> (l%" PRIu32 " & 0x1F);\n", lhs, rhs); + } + break; + case WasmOpcode_i32_rotl: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " << (l%" PRIu32 " & 0x1F) | " + "l%" PRIu32 " >> (-l%" PRIu32" & 0x1F);\n", lhs, rhs, lhs, rhs); + } + break; + case WasmOpcode_i32_rotr: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "l%" PRIu32 " >> (l%" PRIu32 " & 0x1F) | " + "l%" PRIu32 " << (-l%" PRIu32" & 0x1F);\n", lhs, rhs, lhs, rhs); + } + break; + + case WasmOpcode_i64_clz: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "l%" PRIu32 " != 0 ? __builtin_clzll(l%" PRIu32 ") : 64;\n", + lhs, lhs); + } + break; + case WasmOpcode_i64_ctz: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "l%" PRIu32 " != 0 ? __builtin_ctzll(l%" PRIu32 ") : 64;\n", + lhs, lhs); + } + break; + case WasmOpcode_i64_popcnt: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "__builtin_popcountll(l%" PRIu32 ");\n", lhs); + } + break; + case WasmOpcode_i64_add: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "l%" PRIu32 " + l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_sub: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "l%" PRIu32 " - l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_mul: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "l%" PRIu32 " * l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_div_s: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "(int64_t)l%" PRIu32 " / (int64_t)l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_div_u: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "l%" PRIu32 " / l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_rem_s: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "(int64_t)l%" PRIu32 " %% (int64_t)l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_rem_u: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "l%" PRIu32 " %% l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_and: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "l%" PRIu32 " & l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_or: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "l%" PRIu32 " | l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_xor: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "l%" PRIu32 " ^ l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_i64_shl: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "l%" PRIu32 " << (l%" PRIu32 " & 0x3F);\n", lhs, rhs); + } + break; + case WasmOpcode_i64_shr_s: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "(int64_t)l%" PRIu32 " >> (l%" PRIu32 " & 0x3F);\n", lhs, rhs); + } + break; + case WasmOpcode_i64_shr_u: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "l%" PRIu32 " >> (l%" PRIu32 " & 0x3F);\n", lhs, rhs); + } + break; + case WasmOpcode_i64_rotl: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "l%" PRIu32 " << (l%" PRIu32 " & 0x3F) | " + "l%" PRIu32 " >> (-l%" PRIu32" & 0x3F);\n", lhs, rhs, lhs, rhs); + } + break; + case WasmOpcode_i64_rotr: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "l%" PRIu32 " >> (l%" PRIu32 " & 0x3F) | " + "l%" PRIu32 " << (-l%" PRIu32" & 0x3F);\n", lhs, rhs, lhs, rhs); + } + break; + + case WasmOpcode_f32_abs: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "fabsf(l%" PRIu32 ");\n", lhs); + } + break; + case WasmOpcode_f32_neg: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "-l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_f32_ceil: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "ceilf(l%" PRIu32 ");\n", lhs); + } + break; + case WasmOpcode_f32_floor: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "floorf(l%" PRIu32 ");\n", lhs); + } + break; + case WasmOpcode_f32_trunc: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "truncf(l%" PRIu32 ");\n", lhs); + } + break; + case WasmOpcode_f32_nearest: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "roundf(l%" PRIu32 ");\n", lhs); + } + break; + case WasmOpcode_f32_sqrt: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "sqrtf(l%" PRIu32 ");\n", lhs); + } + break; + case WasmOpcode_f32_add: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "l%" PRIu32 " + l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_f32_sub: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "l%" PRIu32 " - l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_f32_mul: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "l%" PRIu32 " * l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_f32_div: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "l%" PRIu32 " / l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_f32_min: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "fminf(l%" PRIu32 ", l%" PRIu32 ");\n", lhs, rhs); + } + break; + case WasmOpcode_f32_max: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "fmaxf(l%" PRIu32 ", l%" PRIu32 ");\n", lhs, rhs); + } + break; + case WasmOpcode_f32_copysign: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "copysignf(l%" PRIu32 ", l%" PRIu32 ");\n", lhs, rhs); + } + break; + + case WasmOpcode_f64_abs: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "fabs(l%" PRIu32 ");\n", lhs); + } + break; + case WasmOpcode_f64_neg: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "-l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_f64_ceil: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "ceil(l%" PRIu32 ");\n", lhs); + } + break; + case WasmOpcode_f64_floor: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "floor(l%" PRIu32 ");\n", lhs); + } + break; + case WasmOpcode_f64_trunc: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "trunc(l%" PRIu32 ");\n", lhs); + } + break; + case WasmOpcode_f64_nearest: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "round(l%" PRIu32 ");\n", lhs); + } + break; + case WasmOpcode_f64_sqrt: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "sqrt(l%" PRIu32 ");\n", lhs); + } + break; + case WasmOpcode_f64_add: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "l%" PRIu32 " + l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_f64_sub: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "l%" PRIu32 " - l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_f64_mul: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "l%" PRIu32 " * l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_f64_div: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "l%" PRIu32 " / l%" PRIu32 ";\n", lhs, rhs); + } + break; + case WasmOpcode_f64_min: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "fmin(l%" PRIu32 ", l%" PRIu32 ");\n", lhs, rhs); + } + break; + case WasmOpcode_f64_max: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "fmax(l%" PRIu32 ", l%" PRIu32 ");\n", lhs, rhs); + } + break; + case WasmOpcode_f64_copysign: + if (unreachable_depth == 0) { + uint32_t rhs = FuncGen_stackPop(&fg); + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "copysign(l%" PRIu32 ", l%" PRIu32 ");\n", lhs, rhs); + } + break; + + case WasmOpcode_i32_wrap_i64: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(uint32_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_i32_trunc_f32_s: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(int32_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_i32_trunc_f32_u: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(uint32_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_i32_trunc_f64_s: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(int32_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_i32_trunc_f64_u: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(uint32_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_i64_extend_i32_s: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "(int32_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_i64_extend_i32_u: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "(uint32_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_i64_trunc_f32_s: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "(int64_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_i64_trunc_f32_u: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "(uint64_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_i64_trunc_f64_s: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "(int64_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_i64_trunc_f64_u: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "(uint64_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_f32_convert_i32_s: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "(int32_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_f32_convert_i32_u: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "(uint32_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_f32_convert_i64_s: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "(int64_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_f32_convert_i64_u: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "(uint64_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_f32_demote_f64: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "(float)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_f64_convert_i32_s: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "(int32_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_f64_convert_i32_u: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "(uint32_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_f64_convert_i64_s: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "(int64_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_f64_convert_i64_u: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "(uint64_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_f64_promote_f32: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "(double)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_i32_reinterpret_f32: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "i32_reinterpret_f32(l%" PRIu32 ");\n", lhs); + } + break; + case WasmOpcode_i64_reinterpret_f64: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "i64_reinterpret_f64(l%" PRIu32 ");\n", lhs); + } + break; + case WasmOpcode_f32_reinterpret_i32: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f32); + fprintf(out, "f32_reinterpret_i32(l%" PRIu32 ");\n", lhs); + } + break; + case WasmOpcode_f64_reinterpret_i64: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_f64); + fprintf(out, "f64_reinterpret_i64(l%" PRIu32 ");\n", lhs); + } + break; + + case WasmOpcode_i32_extend8_s: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(int8_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_i32_extend16_s: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i32); + fprintf(out, "(int16_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_i64_extend8_s: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "(int8_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_i64_extend16_s: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "(int16_t)l%" PRIu32 ";\n", lhs); + } + break; + case WasmOpcode_i64_extend32_s: + if (unreachable_depth == 0) { + uint32_t lhs = FuncGen_stackPop(&fg); + FuncGen_stackPush(&fg, out, WasmValType_i64); + fprintf(out, "(int32_t)l%" PRIu32 ";\n", lhs); + } + break; + + case WasmOpcode_prefixed: + switch (InputStream_readLeb128_u32(&in)) { + case WasmPrefixedOpcode_i32_trunc_sat_f32_s: + case WasmPrefixedOpcode_i32_trunc_sat_f32_u: + case WasmPrefixedOpcode_i32_trunc_sat_f64_s: + case WasmPrefixedOpcode_i32_trunc_sat_f64_u: + case WasmPrefixedOpcode_i64_trunc_sat_f32_s: + case WasmPrefixedOpcode_i64_trunc_sat_f32_u: + case WasmPrefixedOpcode_i64_trunc_sat_f64_s: + case WasmPrefixedOpcode_i64_trunc_sat_f64_u: + if (unreachable_depth == 0) panic("unimplemented opcode"); + + case WasmPrefixedOpcode_memory_init: + (void)InputStream_readLeb128_u32(&in); + (void)InputStream_readByte(&in); + if (unreachable_depth == 0) panic("unimplemented opcode"); + + case WasmPrefixedOpcode_data_drop: + (void)InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) panic("unimplemented opcode"); + + case WasmPrefixedOpcode_memory_copy: { + uint32_t dst_mem_idx = InputStream_readLeb128_u32(&in); + uint32_t src_mem_idx = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t n = FuncGen_stackPop(&fg); + uint32_t src = FuncGen_stackPop(&fg); + uint32_t dst = FuncGen_stackPop(&fg); + FuncGen_indent(&fg, out); + fprintf(out, "memcpy(&m%" PRIu32 "[l%" PRIu32 "], " + "&m%" PRIu32 "[l%" PRIu32 "], l%" PRIu32 ");\n", + dst_mem_idx, dst, src_mem_idx, src, n); + } + break; + } + + case WasmPrefixedOpcode_memory_fill: { + uint32_t mem_idx = InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) { + uint32_t n = FuncGen_stackPop(&fg); + uint32_t c = FuncGen_stackPop(&fg); + uint32_t s = FuncGen_stackPop(&fg); + FuncGen_indent(&fg, out); + fprintf(out, "memset(&m%" PRIu32 "[l%" PRIu32 "], " + "l%" PRIu32 ", l%" PRIu32 ");\n", mem_idx, s, c, n); + } + break; + } + + case WasmPrefixedOpcode_table_init: + (void)InputStream_readLeb128_u32(&in); + (void)InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) panic("unimplemented opcode"); + + case WasmPrefixedOpcode_elem_drop: + (void)InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) panic("unimplemented opcode"); + + case WasmPrefixedOpcode_table_copy: + (void)InputStream_readLeb128_u32(&in); + (void)InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) panic("unimplemented opcode"); + + case WasmPrefixedOpcode_table_grow: + (void)InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) panic("unimplemented opcode"); + + case WasmPrefixedOpcode_table_size: + (void)InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) panic("unimplemented opcode"); + + case WasmPrefixedOpcode_table_fill: + (void)InputStream_readLeb128_u32(&in); + if (unreachable_depth == 0) panic("unimplemented opcode"); + } + break; + } + } + for (uint32_t param_i = 0; param_i < func_type->param->len; param_i += 1) { + if (param_used[param_i]) continue; + FuncGen_indent(&fg, out); + fprintf(out, "(void)l%" PRIu32 ";\n", param_i); + } + switch (func_type->result->len) { + case 0: break; + case 1: + FuncGen_indent(&fg, out); + fprintf(out, "return l%" PRIu32 ";\n", FuncGen_stackPop(&fg)); + break; + default: panic("multiple function returns not supported"); + } + fputs("}\n\n", out); + } + } + + (void)InputStream_skipToSection(&in, WasmSectionId_data); + { + uint32_t len = InputStream_readLeb128_u32(&in); + fputs("static void init_data(void) {\n", out); + for (uint32_t i = 0; i < mems_len; i += 1) + fprintf(out, " p%" PRIu32 " = UINT32_C(%" PRIu32 ");\n" + " m%" PRIu32 " = calloc(p%" PRIu32 ", UINT32_C(1) << 16);\n", + i, mems[i].limits.min, i, i); + for (uint32_t segment_i = 0; segment_i < len; segment_i += 1) { + uint32_t mem_idx; + switch (InputStream_readLeb128_u32(&in)) { + case 0: + mem_idx = 0; + break; + + case 2: + mem_idx = InputStream_readLeb128_u32(&in); + break; + + default: panic("unsupported data kind"); + } + uint32_t offset = evalExpr(&in); + uint32_t segment_len = InputStream_readLeb128_u32(&in); + fputc('\n', out); + fprintf(out, " static const uint8_t s%" PRIu32 "[UINT32_C(%" PRIu32 ")] = {", + segment_i, segment_len); + for (uint32_t i = 0; i < segment_len; i += 1) { + if (i % 32 == 0) fputs("\n ", out); + fprintf(out, " 0x%02hhX,", InputStream_readByte(&in)); + } + fprintf(out, "\n" + " };\n" + " memcpy(&m%" PRIu32 "[UINT32_C(0x%" PRIX32 ")], s%" PRIu32 ", UINT32_C(%" PRIu32 "));\n", + mem_idx, offset, segment_i, segment_len); + } + fputs("}\n", out); + } + + InputStream_close(&in); + fclose(out); +} diff --git a/stage1/zig1.c b/stage1/zig1.c deleted file mode 100644 index bc65179992..0000000000 --- a/stage1/zig1.c +++ /dev/null @@ -1,4567 +0,0 @@ -// TODO get rid of _GNU_SOURCE -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#ifdef __linux__ -#include -#endif - -#include - -#if defined(__APPLE__) -#define ZIG_TRIPLE_OS "macos" -#elif defined(_WIN32) -#define ZIG_TRIPLE_OS "windows" -#elif defined(__linux__) -#define ZIG_TRIPLE_OS "linux" -#elif defined(__FreeBSD__) -#define ZIG_TRIPLE_OS "freebsd" -#elif defined(__NetBSD__) -#define ZIG_TRIPLE_OS "netbsd" -#elif defined(__DragonFly__) -#define ZIG_TRIPLE_OS "dragonfly" -#elif defined(__OpenBSD__) -#define ZIG_TRIPLE_OS "openbsd" -#elif defined(__HAIKU__) -#define ZIG_TRIPLE_OS "haiku" -#elif defined(__sun) -#define ZIG_TRIPLE_OS "solaris" -#else -#error please add more os definitions above this line -#endif - -#if defined(__x86_64__) -#define ZIG_TRIPLE_ARCH "x86_64" -#elif defined(__aarch64__) -#define ZIG_TRIPLE_ARCH "aarch64" -#elif defined(__ARM_EABI__) -#define ZIG_TRIPLE_ARCH "arm" -#else -#error please add more arch definitions above this line -#endif - -enum wasi_errno_t { - WASI_ESUCCESS = 0, - WASI_E2BIG = 1, - WASI_EACCES = 2, - WASI_EADDRINUSE = 3, - WASI_EADDRNOTAVAIL = 4, - WASI_EAFNOSUPPORT = 5, - WASI_EAGAIN = 6, - WASI_EALREADY = 7, - WASI_EBADF = 8, - WASI_EBADMSG = 9, - WASI_EBUSY = 10, - WASI_ECANCELED = 11, - WASI_ECHILD = 12, - WASI_ECONNABORTED = 13, - WASI_ECONNREFUSED = 14, - WASI_ECONNRESET = 15, - WASI_EDEADLK = 16, - WASI_EDESTADDRREQ = 17, - WASI_EDOM = 18, - WASI_EDQUOT = 19, - WASI_EEXIST = 20, - WASI_EFAULT = 21, - WASI_EFBIG = 22, - WASI_EHOSTUNREACH = 23, - WASI_EIDRM = 24, - WASI_EILSEQ = 25, - WASI_EINPROGRESS = 26, - WASI_EINTR = 27, - WASI_EINVAL = 28, - WASI_EIO = 29, - WASI_EISCONN = 30, - WASI_EISDIR = 31, - WASI_ELOOP = 32, - WASI_EMFILE = 33, - WASI_EMLINK = 34, - WASI_EMSGSIZE = 35, - WASI_EMULTIHOP = 36, - WASI_ENAMETOOLONG = 37, - WASI_ENETDOWN = 38, - WASI_ENETRESET = 39, - WASI_ENETUNREACH = 40, - WASI_ENFILE = 41, - WASI_ENOBUFS = 42, - WASI_ENODEV = 43, - WASI_ENOENT = 44, - WASI_ENOEXEC = 45, - WASI_ENOLCK = 46, - WASI_ENOLINK = 47, - WASI_ENOMEM = 48, - WASI_ENOMSG = 49, - WASI_ENOPROTOOPT = 50, - WASI_ENOSPC = 51, - WASI_ENOSYS = 52, - WASI_ENOTCONN = 53, - WASI_ENOTDIR = 54, - WASI_ENOTEMPTY = 55, - WASI_ENOTRECOVERABLE = 56, - WASI_ENOTSOCK = 57, - WASI_EOPNOTSUPP = 58, - WASI_ENOTTY = 59, - WASI_ENXIO = 60, - WASI_EOVERFLOW = 61, - WASI_EOWNERDEAD = 62, - WASI_EPERM = 63, - WASI_EPIPE = 64, - WASI_EPROTO = 65, - WASI_EPROTONOSUPPORT = 66, - WASI_EPROTOTYPE = 67, - WASI_ERANGE = 68, - WASI_EROFS = 69, - WASI_ESPIPE = 70, - WASI_ESRCH = 71, - WASI_ESTALE = 72, - WASI_ETIMEDOUT = 73, - WASI_ETXTBSY = 74, - WASI_EXDEV = 75, - WASI_ENOTCAPABLE = 76, -}; - -static void panic(const char *msg) { - fprintf(stderr, "%s\n", msg); - abort(); -} - -static uint32_t min_u32(uint32_t a, uint32_t b) { - return (a < b) ? a : b; -} - -static uint32_t rotl32(uint32_t n, unsigned c) { - const unsigned mask = CHAR_BIT * sizeof(n) - 1; - c &= mask & 31; - return (n << c) | (n >> ((-c) & mask)); -} - -static uint32_t rotr32(uint32_t n, unsigned c) { - const unsigned mask = CHAR_BIT * sizeof(n) - 1; - c &= mask & 31; - return (n >> c) | (n << ((-c) & mask)); -} - -static uint64_t rotl64(uint64_t n, unsigned c) { - const unsigned mask = CHAR_BIT * sizeof(n) - 1; - c &= mask & 63; - return (n << c) | (n >> ((-c) & mask)); -} - -static uint64_t rotr64(uint64_t n, unsigned c) { - const unsigned mask = CHAR_BIT * sizeof(n) - 1; - c &= mask & 63; - return (n >> c) | (n << ((-c) & mask)); -} - -static void *arena_alloc(size_t n) { - void *ptr = malloc(n); - if (!ptr) panic("out of memory"); -#ifndef NDEBUG - memset(ptr, 0xaa, n); // to match the zig version -#endif - return ptr; -} - -static int err_wrap(const char *prefix, int rc) { - if (rc == -1) { - perror(prefix); - abort(); - } - return rc; -} - -static bool bs_isSet(const uint32_t *bitset, uint32_t index) { - return (bitset[index >> 5] >> (index & 0x1f)) & 1; -} -static void bs_set(uint32_t *bitset, uint32_t index) { - bitset[index >> 5] |= ((uint32_t)1 << (index & 0x1f)); -} -static void bs_unset(uint32_t *bitset, uint32_t index) { - bitset[index >> 5] &= ~((uint32_t)1 << (index & 0x1f)); -} -static void bs_setValue(uint32_t *bitset, uint32_t index, bool value) { - if (value) bs_set(bitset, index); else bs_unset(bitset, index); -} - -struct ByteSlice { - char *ptr; - size_t len; -}; - -static struct ByteSlice read_file_alloc(const char *file_path) { - FILE *f = fopen(file_path, "rb"); - if (!f) { - fprintf(stderr, "failed to read %s: ", file_path); - perror(""); - abort(); - } - if (fseek(f, 0L, SEEK_END) == -1) panic("failed to seek"); - struct ByteSlice res; - res.len = ftell(f); - res.ptr = malloc(res.len); - rewind(f); - size_t amt_read = fread(res.ptr, 1, res.len, f); - if (amt_read != res.len) panic("short read"); - fclose(f); - return res; -} - - -struct Preopen { - int wasi_fd; - int host_fd; - const char *name; - size_t name_len; -}; - -static struct Preopen preopens_buffer[10]; -static size_t preopens_len = 0; - -static void add_preopen(int wasi_fd, const char *name, int host_fd) { - preopens_buffer[preopens_len].wasi_fd = wasi_fd; - preopens_buffer[preopens_len].host_fd = host_fd; - preopens_buffer[preopens_len].name = name; - preopens_buffer[preopens_len].name_len = strlen(name); - preopens_len += 1; -} - -static const struct Preopen *find_preopen(int32_t wasi_fd) { - for (size_t i = 0; i < preopens_len; i += 1) { - const struct Preopen *preopen = &preopens_buffer[i]; - if (preopen->wasi_fd == wasi_fd) { - return preopen; - } - } - return NULL; -} - -static const uint32_t max_memory = 2ul * 1024ul * 1024ul * 1024ul; // 2 GiB - -static uint16_t read_u16_le(const char *ptr) { - const uint8_t *u8_ptr = (const uint8_t *)ptr; - return - (((uint64_t)u8_ptr[0]) << 0x00) | - (((uint64_t)u8_ptr[1]) << 0x08); -} - -static uint32_t read_u32_le(const char *ptr) { - const uint8_t *u8_ptr = (const uint8_t *)ptr; - return - (((uint64_t)u8_ptr[0]) << 0x00) | - (((uint64_t)u8_ptr[1]) << 0x08) | - (((uint64_t)u8_ptr[2]) << 0x10) | - (((uint64_t)u8_ptr[3]) << 0x18); -} - -static uint64_t read_u64_le(const char *ptr) { - const uint8_t *u8_ptr = (const uint8_t *)ptr; - return - (((uint64_t)u8_ptr[0]) << 0x00) | - (((uint64_t)u8_ptr[1]) << 0x08) | - (((uint64_t)u8_ptr[2]) << 0x10) | - (((uint64_t)u8_ptr[3]) << 0x18) | - (((uint64_t)u8_ptr[4]) << 0x20) | - (((uint64_t)u8_ptr[5]) << 0x28) | - (((uint64_t)u8_ptr[6]) << 0x30) | - (((uint64_t)u8_ptr[7]) << 0x38); -} - -static void write_u16_le(char *ptr, uint16_t x) { - uint8_t *u8_ptr = (uint8_t*)ptr; - u8_ptr[0] = (x >> 0x00); - u8_ptr[1] = (x >> 0x08); -} - -static void write_u32_le(char *ptr, uint32_t x) { - uint8_t *u8_ptr = (uint8_t*)ptr; - u8_ptr[0] = (x >> 0x00); - u8_ptr[1] = (x >> 0x08); - u8_ptr[2] = (x >> 0x10); - u8_ptr[3] = (x >> 0x18); -} - -static void write_u64_le(char *ptr, uint64_t x) { - uint8_t *u8_ptr = (uint8_t*)ptr; - u8_ptr[0] = (x >> 0x00); - u8_ptr[1] = (x >> 0x08); - u8_ptr[2] = (x >> 0x10); - u8_ptr[3] = (x >> 0x18); - u8_ptr[4] = (x >> 0x20); - u8_ptr[5] = (x >> 0x28); - u8_ptr[6] = (x >> 0x30); - u8_ptr[7] = (x >> 0x38); -} - -static uint32_t read32_uleb128(const char *ptr, uint32_t *i) { - uint32_t result = 0; - uint32_t shift = 0; - - for (;;) { - uint32_t byte = ptr[*i]; - *i += 1; - result |= ((byte & 0x7f) << shift); - shift += 7; - if ((byte & 0x80) == 0) return result; - if (shift >= 32) panic("read32_uleb128 failed"); - } -} - -static int64_t read64_ileb128(const char *ptr, uint32_t *i) { - int64_t result = 0; - uint32_t shift = 0; - - for (;;) { - uint64_t byte = ptr[*i]; - *i += 1; - result |= ((byte & 0x7f) << shift); - shift += 7; - if ((byte & 0x80) == 0) { - if ((byte & 0x40) && (shift < 64)) { - uint64_t extend = 0; - result |= (~extend << shift); - } - return result; - } - if (shift >= 64) panic("read64_ileb128 failed"); - } -} - -static int32_t read32_ileb128(const char *ptr, uint32_t *i) { - return read64_ileb128(ptr, i); -} - -static struct ByteSlice read_name(char *ptr, uint32_t *i) { - uint32_t len = read32_uleb128(ptr, i); - struct ByteSlice res; - res.ptr = ptr + *i; - res.len = len; - *i += len; - return res; -} - -enum Section { - Section_custom, - Section_type, - Section_import, - Section_function, - Section_table, - Section_memory, - Section_global, - Section_export, - Section_start, - Section_element, - Section_code, - Section_data, - Section_data_count, -}; - -enum Op { - Op_unreachable, - Op_br_void, - Op_br_32, - Op_br_64, - Op_br_nez_void, - Op_br_nez_32, - Op_br_nez_64, - Op_br_eqz_void, - Op_br_eqz_32, - Op_br_eqz_64, - Op_br_table_void, - Op_br_table_32, - Op_br_table_64, - Op_return_void, - Op_return_32, - Op_return_64, - Op_call_import, - Op_call_func, - Op_call_indirect, - Op_drop_32, - Op_drop_64, - Op_select_32, - Op_select_64, - Op_local_get_32, - Op_local_get_64, - Op_local_set_32, - Op_local_set_64, - Op_local_tee_32, - Op_local_tee_64, - Op_global_get_0_32, - Op_global_get_32, - Op_global_set_0_32, - Op_global_set_32, - Op_load_0_8, - Op_load_8, - Op_load_0_16, - Op_load_16, - Op_load_0_32, - Op_load_32, - Op_load_0_64, - Op_load_64, - Op_store_0_8, - Op_store_8, - Op_store_0_16, - Op_store_16, - Op_store_0_32, - Op_store_32, - Op_store_0_64, - Op_store_64, - Op_mem_size, - Op_mem_grow, - Op_const_0_32, - Op_const_0_64, - Op_const_1_32, - Op_const_1_64, - Op_const_32, - Op_const_64, - Op_const_umax_32, - Op_const_umax_64, - Op_eqz_32, - Op_eq_32, - Op_ne_32, - Op_slt_32, - Op_ult_32, - Op_sgt_32, - Op_ugt_32, - Op_sle_32, - Op_ule_32, - Op_sge_32, - Op_uge_32, - Op_eqz_64, - Op_eq_64, - Op_ne_64, - Op_slt_64, - Op_ult_64, - Op_sgt_64, - Op_ugt_64, - Op_sle_64, - Op_ule_64, - Op_sge_64, - Op_uge_64, - Op_feq_32, - Op_fne_32, - Op_flt_32, - Op_fgt_32, - Op_fle_32, - Op_fge_32, - Op_feq_64, - Op_fne_64, - Op_flt_64, - Op_fgt_64, - Op_fle_64, - Op_fge_64, - Op_clz_32, - Op_ctz_32, - Op_popcnt_32, - Op_add_32, - Op_sub_32, - Op_mul_32, - Op_sdiv_32, - Op_udiv_32, - Op_srem_32, - Op_urem_32, - Op_and_32, - Op_or_32, - Op_xor_32, - Op_shl_32, - Op_ashr_32, - Op_lshr_32, - Op_rol_32, - Op_ror_32, - Op_clz_64, - Op_ctz_64, - Op_popcnt_64, - Op_add_64, - Op_sub_64, - Op_mul_64, - Op_sdiv_64, - Op_udiv_64, - Op_srem_64, - Op_urem_64, - Op_and_64, - Op_or_64, - Op_xor_64, - Op_shl_64, - Op_ashr_64, - Op_lshr_64, - Op_rol_64, - Op_ror_64, - Op_fabs_32, - Op_fneg_32, - Op_ceil_32, - Op_floor_32, - Op_trunc_32, - Op_nearest_32, - Op_sqrt_32, - Op_fadd_32, - Op_fsub_32, - Op_fmul_32, - Op_fdiv_32, - Op_fmin_32, - Op_fmax_32, - Op_copysign_32, - Op_fabs_64, - Op_fneg_64, - Op_ceil_64, - Op_floor_64, - Op_trunc_64, - Op_nearest_64, - Op_sqrt_64, - Op_fadd_64, - Op_fsub_64, - Op_fmul_64, - Op_fdiv_64, - Op_fmin_64, - Op_fmax_64, - Op_copysign_64, - Op_ftos_32_32, - Op_ftou_32_32, - Op_ftos_32_64, - Op_ftou_32_64, - Op_sext_64_32, - Op_ftos_64_32, - Op_ftou_64_32, - Op_ftos_64_64, - Op_ftou_64_64, - Op_stof_32_32, - Op_utof_32_32, - Op_stof_32_64, - Op_utof_32_64, - Op_ftof_32_64, - Op_stof_64_32, - Op_utof_64_32, - Op_stof_64_64, - Op_utof_64_64, - Op_ftof_64_32, - Op_sext8_32, - Op_sext16_32, - Op_sext8_64, - Op_sext16_64, - Op_sext32_64, - Op_memcpy, - Op_memset, - - Op_wrap_32_64 = Op_drop_32, - Op_zext_64_32 = Op_const_0_32, - Op_last = Op_memset, -}; - -enum WasmOp { - WasmOp_unreachable = 0x00, - WasmOp_nop = 0x01, - WasmOp_block = 0x02, - WasmOp_loop = 0x03, - WasmOp_if = 0x04, - WasmOp_else = 0x05, - WasmOp_end = 0x0B, - WasmOp_br = 0x0C, - WasmOp_br_if = 0x0D, - WasmOp_br_table = 0x0E, - WasmOp_return = 0x0F, - WasmOp_call = 0x10, - WasmOp_call_indirect = 0x11, - WasmOp_drop = 0x1A, - WasmOp_select = 0x1B, - WasmOp_local_get = 0x20, - WasmOp_local_set = 0x21, - WasmOp_local_tee = 0x22, - WasmOp_global_get = 0x23, - WasmOp_global_set = 0x24, - WasmOp_i32_load = 0x28, - WasmOp_i64_load = 0x29, - WasmOp_f32_load = 0x2A, - WasmOp_f64_load = 0x2B, - WasmOp_i32_load8_s = 0x2C, - WasmOp_i32_load8_u = 0x2D, - WasmOp_i32_load16_s = 0x2E, - WasmOp_i32_load16_u = 0x2F, - WasmOp_i64_load8_s = 0x30, - WasmOp_i64_load8_u = 0x31, - WasmOp_i64_load16_s = 0x32, - WasmOp_i64_load16_u = 0x33, - WasmOp_i64_load32_s = 0x34, - WasmOp_i64_load32_u = 0x35, - WasmOp_i32_store = 0x36, - WasmOp_i64_store = 0x37, - WasmOp_f32_store = 0x38, - WasmOp_f64_store = 0x39, - WasmOp_i32_store8 = 0x3A, - WasmOp_i32_store16 = 0x3B, - WasmOp_i64_store8 = 0x3C, - WasmOp_i64_store16 = 0x3D, - WasmOp_i64_store32 = 0x3E, - WasmOp_memory_size = 0x3F, - WasmOp_memory_grow = 0x40, - WasmOp_i32_const = 0x41, - WasmOp_i64_const = 0x42, - WasmOp_f32_const = 0x43, - WasmOp_f64_const = 0x44, - WasmOp_i32_eqz = 0x45, - WasmOp_i32_eq = 0x46, - WasmOp_i32_ne = 0x47, - WasmOp_i32_lt_s = 0x48, - WasmOp_i32_lt_u = 0x49, - WasmOp_i32_gt_s = 0x4A, - WasmOp_i32_gt_u = 0x4B, - WasmOp_i32_le_s = 0x4C, - WasmOp_i32_le_u = 0x4D, - WasmOp_i32_ge_s = 0x4E, - WasmOp_i32_ge_u = 0x4F, - WasmOp_i64_eqz = 0x50, - WasmOp_i64_eq = 0x51, - WasmOp_i64_ne = 0x52, - WasmOp_i64_lt_s = 0x53, - WasmOp_i64_lt_u = 0x54, - WasmOp_i64_gt_s = 0x55, - WasmOp_i64_gt_u = 0x56, - WasmOp_i64_le_s = 0x57, - WasmOp_i64_le_u = 0x58, - WasmOp_i64_ge_s = 0x59, - WasmOp_i64_ge_u = 0x5A, - WasmOp_f32_eq = 0x5B, - WasmOp_f32_ne = 0x5C, - WasmOp_f32_lt = 0x5D, - WasmOp_f32_gt = 0x5E, - WasmOp_f32_le = 0x5F, - WasmOp_f32_ge = 0x60, - WasmOp_f64_eq = 0x61, - WasmOp_f64_ne = 0x62, - WasmOp_f64_lt = 0x63, - WasmOp_f64_gt = 0x64, - WasmOp_f64_le = 0x65, - WasmOp_f64_ge = 0x66, - WasmOp_i32_clz = 0x67, - WasmOp_i32_ctz = 0x68, - WasmOp_i32_popcnt = 0x69, - WasmOp_i32_add = 0x6A, - WasmOp_i32_sub = 0x6B, - WasmOp_i32_mul = 0x6C, - WasmOp_i32_div_s = 0x6D, - WasmOp_i32_div_u = 0x6E, - WasmOp_i32_rem_s = 0x6F, - WasmOp_i32_rem_u = 0x70, - WasmOp_i32_and = 0x71, - WasmOp_i32_or = 0x72, - WasmOp_i32_xor = 0x73, - WasmOp_i32_shl = 0x74, - WasmOp_i32_shr_s = 0x75, - WasmOp_i32_shr_u = 0x76, - WasmOp_i32_rotl = 0x77, - WasmOp_i32_rotr = 0x78, - WasmOp_i64_clz = 0x79, - WasmOp_i64_ctz = 0x7A, - WasmOp_i64_popcnt = 0x7B, - WasmOp_i64_add = 0x7C, - WasmOp_i64_sub = 0x7D, - WasmOp_i64_mul = 0x7E, - WasmOp_i64_div_s = 0x7F, - WasmOp_i64_div_u = 0x80, - WasmOp_i64_rem_s = 0x81, - WasmOp_i64_rem_u = 0x82, - WasmOp_i64_and = 0x83, - WasmOp_i64_or = 0x84, - WasmOp_i64_xor = 0x85, - WasmOp_i64_shl = 0x86, - WasmOp_i64_shr_s = 0x87, - WasmOp_i64_shr_u = 0x88, - WasmOp_i64_rotl = 0x89, - WasmOp_i64_rotr = 0x8A, - WasmOp_f32_abs = 0x8B, - WasmOp_f32_neg = 0x8C, - WasmOp_f32_ceil = 0x8D, - WasmOp_f32_floor = 0x8E, - WasmOp_f32_trunc = 0x8F, - WasmOp_f32_nearest = 0x90, - WasmOp_f32_sqrt = 0x91, - WasmOp_f32_add = 0x92, - WasmOp_f32_sub = 0x93, - WasmOp_f32_mul = 0x94, - WasmOp_f32_div = 0x95, - WasmOp_f32_min = 0x96, - WasmOp_f32_max = 0x97, - WasmOp_f32_copysign = 0x98, - WasmOp_f64_abs = 0x99, - WasmOp_f64_neg = 0x9A, - WasmOp_f64_ceil = 0x9B, - WasmOp_f64_floor = 0x9C, - WasmOp_f64_trunc = 0x9D, - WasmOp_f64_nearest = 0x9E, - WasmOp_f64_sqrt = 0x9F, - WasmOp_f64_add = 0xA0, - WasmOp_f64_sub = 0xA1, - WasmOp_f64_mul = 0xA2, - WasmOp_f64_div = 0xA3, - WasmOp_f64_min = 0xA4, - WasmOp_f64_max = 0xA5, - WasmOp_f64_copysign = 0xA6, - WasmOp_i32_wrap_i64 = 0xA7, - WasmOp_i32_trunc_f32_s = 0xA8, - WasmOp_i32_trunc_f32_u = 0xA9, - WasmOp_i32_trunc_f64_s = 0xAA, - WasmOp_i32_trunc_f64_u = 0xAB, - WasmOp_i64_extend_i32_s = 0xAC, - WasmOp_i64_extend_i32_u = 0xAD, - WasmOp_i64_trunc_f32_s = 0xAE, - WasmOp_i64_trunc_f32_u = 0xAF, - WasmOp_i64_trunc_f64_s = 0xB0, - WasmOp_i64_trunc_f64_u = 0xB1, - WasmOp_f32_convert_i32_s = 0xB2, - WasmOp_f32_convert_i32_u = 0xB3, - WasmOp_f32_convert_i64_s = 0xB4, - WasmOp_f32_convert_i64_u = 0xB5, - WasmOp_f32_demote_f64 = 0xB6, - WasmOp_f64_convert_i32_s = 0xB7, - WasmOp_f64_convert_i32_u = 0xB8, - WasmOp_f64_convert_i64_s = 0xB9, - WasmOp_f64_convert_i64_u = 0xBA, - WasmOp_f64_promote_f32 = 0xBB, - WasmOp_i32_reinterpret_f32 = 0xBC, - WasmOp_i64_reinterpret_f64 = 0xBD, - WasmOp_f32_reinterpret_i32 = 0xBE, - WasmOp_f64_reinterpret_i64 = 0xBF, - WasmOp_i32_extend8_s = 0xC0, - WasmOp_i32_extend16_s = 0xC1, - WasmOp_i64_extend8_s = 0xC2, - WasmOp_i64_extend16_s = 0xC3, - WasmOp_i64_extend32_s = 0xC4, - - WasmOp_prefixed = 0xFC, -}; - -enum WasmPrefixedOp { - WasmPrefixedOp_i32_trunc_sat_f32_s = 0x00, - WasmPrefixedOp_i32_trunc_sat_f32_u = 0x01, - WasmPrefixedOp_i32_trunc_sat_f64_s = 0x02, - WasmPrefixedOp_i32_trunc_sat_f64_u = 0x03, - WasmPrefixedOp_i64_trunc_sat_f32_s = 0x04, - WasmPrefixedOp_i64_trunc_sat_f32_u = 0x05, - WasmPrefixedOp_i64_trunc_sat_f64_s = 0x06, - WasmPrefixedOp_i64_trunc_sat_f64_u = 0x07, - WasmPrefixedOp_memory_init = 0x08, - WasmPrefixedOp_data_drop = 0x09, - WasmPrefixedOp_memory_copy = 0x0A, - WasmPrefixedOp_memory_fill = 0x0B, - WasmPrefixedOp_table_init = 0x0C, - WasmPrefixedOp_elem_drop = 0x0D, - WasmPrefixedOp_table_copy = 0x0E, - WasmPrefixedOp_table_grow = 0x0F, - WasmPrefixedOp_table_size = 0x10, - WasmPrefixedOp_table_fill = 0x11, -}; - -static const uint32_t wasm_page_size = 64 * 1024; - -struct ProgramCounter { - uint32_t opcode; - uint32_t operand; -}; - -struct TypeInfo { - uint32_t param_count; - // bitset with param_count bits, indexed from lsb, 0 -> 32-bit, 1 -> 64-bit - uint32_t param_types; - uint32_t result_count; - // bitset with result_count bits, indexed from lsb, 0 -> 32-bit, 1 -> 64-bit - uint32_t result_types; -}; - -struct Function { - uint32_t id; - // Index to start of code in opcodes/operands. - struct ProgramCounter entry_pc; - uint32_t type_idx; - uint32_t locals_size; -}; - -enum ImpMod { - ImpMod_wasi_snapshot_preview1, -}; - -enum ImpName { - ImpName_args_get, - ImpName_args_sizes_get, - ImpName_clock_time_get, - ImpName_debug, - ImpName_debug_slice, - ImpName_environ_get, - ImpName_environ_sizes_get, - ImpName_fd_close, - ImpName_fd_fdstat_get, - ImpName_fd_filestat_get, - ImpName_fd_filestat_set_size, - ImpName_fd_filestat_set_times, - ImpName_fd_pread, - ImpName_fd_prestat_dir_name, - ImpName_fd_prestat_get, - ImpName_fd_pwrite, - ImpName_fd_read, - ImpName_fd_readdir, - ImpName_fd_write, - ImpName_path_create_directory, - ImpName_path_filestat_get, - ImpName_path_open, - ImpName_path_remove_directory, - ImpName_path_rename, - ImpName_path_unlink_file, - ImpName_proc_exit, - ImpName_random_get, -}; - -struct Import { - enum ImpMod mod; - enum ImpName name; - uint32_t type_idx; -}; - -struct VirtualMachine { - uint32_t *stack; - /// Points to one after the last stack item. - uint32_t stack_top; - struct ProgramCounter pc; - /// Actual memory usage of the WASI code. The capacity is max_memory. - uint32_t memory_len; - const char *mod_ptr; - uint8_t *opcodes; - uint32_t *operands; - struct Function *functions; - /// Type index to start of type in module_bytes. - struct TypeInfo *types; - uint64_t *globals; - char *memory; - struct Import *imports; - uint32_t imports_len; - const char **args; - uint32_t *table; -}; - -static int to_host_fd(int32_t wasi_fd) { - const struct Preopen *preopen = find_preopen(wasi_fd); - if (!preopen) return wasi_fd; - return preopen->host_fd; -} - -static enum wasi_errno_t to_wasi_err(int err) { - switch (err) { - case E2BIG: return WASI_E2BIG; - case EACCES: return WASI_EACCES; - case EADDRINUSE: return WASI_EADDRINUSE; - case EADDRNOTAVAIL: return WASI_EADDRNOTAVAIL; - case EAFNOSUPPORT: return WASI_EAFNOSUPPORT; - case EAGAIN: return WASI_EAGAIN; - case EALREADY: return WASI_EALREADY; - case EBADF: return WASI_EBADF; - case EBADMSG: return WASI_EBADMSG; - case EBUSY: return WASI_EBUSY; - case ECANCELED: return WASI_ECANCELED; - case ECHILD: return WASI_ECHILD; - case ECONNABORTED: return WASI_ECONNABORTED; - case ECONNREFUSED: return WASI_ECONNREFUSED; - case ECONNRESET: return WASI_ECONNRESET; - case EDEADLK: return WASI_EDEADLK; - case EDESTADDRREQ: return WASI_EDESTADDRREQ; - case EDOM: return WASI_EDOM; - case EDQUOT: return WASI_EDQUOT; - case EEXIST: return WASI_EEXIST; - case EFAULT: return WASI_EFAULT; - case EFBIG: return WASI_EFBIG; - case EHOSTUNREACH: return WASI_EHOSTUNREACH; - case EIDRM: return WASI_EIDRM; - case EILSEQ: return WASI_EILSEQ; - case EINPROGRESS: return WASI_EINPROGRESS; - case EINTR: return WASI_EINTR; - case EINVAL: return WASI_EINVAL; - case EIO: return WASI_EIO; - case EISCONN: return WASI_EISCONN; - case EISDIR: return WASI_EISDIR; - case ELOOP: return WASI_ELOOP; - case EMFILE: return WASI_EMFILE; - case EMLINK: return WASI_EMLINK; - case EMSGSIZE: return WASI_EMSGSIZE; - case EMULTIHOP: return WASI_EMULTIHOP; - case ENAMETOOLONG: return WASI_ENAMETOOLONG; - case ENETDOWN: return WASI_ENETDOWN; - case ENETRESET: return WASI_ENETRESET; - case ENETUNREACH: return WASI_ENETUNREACH; - case ENFILE: return WASI_ENFILE; - case ENOBUFS: return WASI_ENOBUFS; - case ENODEV: return WASI_ENODEV; - case ENOENT: return WASI_ENOENT; - case ENOEXEC: return WASI_ENOEXEC; - case ENOLCK: return WASI_ENOLCK; - case ENOLINK: return WASI_ENOLINK; - case ENOMEM: return WASI_ENOMEM; - case ENOMSG: return WASI_ENOMSG; - case ENOPROTOOPT: return WASI_ENOPROTOOPT; - case ENOSPC: return WASI_ENOSPC; - case ENOSYS: return WASI_ENOSYS; - case ENOTCONN: return WASI_ENOTCONN; - case ENOTDIR: return WASI_ENOTDIR; - case ENOTEMPTY: return WASI_ENOTEMPTY; - case ENOTRECOVERABLE: return WASI_ENOTRECOVERABLE; - case ENOTSOCK: return WASI_ENOTSOCK; - case EOPNOTSUPP: return WASI_EOPNOTSUPP; - case ENOTTY: return WASI_ENOTTY; - case ENXIO: return WASI_ENXIO; - case EOVERFLOW: return WASI_EOVERFLOW; - case EOWNERDEAD: return WASI_EOWNERDEAD; - case EPERM: return WASI_EPERM; - case EPIPE: return WASI_EPIPE; - case EPROTO: return WASI_EPROTO; - case EPROTONOSUPPORT: return WASI_EPROTONOSUPPORT; - case EPROTOTYPE: return WASI_EPROTOTYPE; - case ERANGE: return WASI_ERANGE; - case EROFS: return WASI_EROFS; - case ESPIPE: return WASI_ESPIPE; - case ESRCH: return WASI_ESRCH; - case ESTALE: return WASI_ESTALE; - case ETIMEDOUT: return WASI_ETIMEDOUT; - case ETXTBSY: return WASI_ETXTBSY; - case EXDEV: return WASI_EXDEV; - default: - fprintf(stderr, "unexpected errno: %s\n", strerror(err)); - abort(); - }; -} - -enum wasi_filetype_t { - wasi_filetype_t_UNKNOWN, - wasi_filetype_t_BLOCK_DEVICE, - wasi_filetype_t_CHARACTER_DEVICE, - wasi_filetype_t_DIRECTORY, - wasi_filetype_t_REGULAR_FILE, - wasi_filetype_t_SOCKET_DGRAM, - wasi_filetype_t_SOCKET_STREAM, - wasi_filetype_t_SYMBOLIC_LINK, -}; - -static const uint16_t WASI_O_CREAT = 0x0001; -static const uint16_t WASI_O_DIRECTORY = 0x0002; -static const uint16_t WASI_O_EXCL = 0x0004; -static const uint16_t WASI_O_TRUNC = 0x0008; - -static const uint16_t WASI_FDFLAG_APPEND = 0x0001; -static const uint16_t WASI_FDFLAG_DSYNC = 0x0002; -static const uint16_t WASI_FDFLAG_NONBLOCK = 0x0004; -static const uint16_t WASI_FDFLAG_SYNC = 0x0010; - -static const uint64_t WASI_RIGHT_FD_READ = 0x0000000000000002ull; -static const uint64_t WASI_RIGHT_FD_WRITE = 0x0000000000000040ull; - -static enum wasi_filetype_t to_wasi_filetype(mode_t st_mode) { - switch (st_mode & S_IFMT) { - case S_IFBLK: - return wasi_filetype_t_BLOCK_DEVICE; - case S_IFCHR: - return wasi_filetype_t_CHARACTER_DEVICE; - case S_IFDIR: - return wasi_filetype_t_DIRECTORY; - case S_IFLNK: - return wasi_filetype_t_SYMBOLIC_LINK; - case S_IFREG: - return wasi_filetype_t_REGULAR_FILE; - default: - return wasi_filetype_t_UNKNOWN; - } -} - -static uint64_t to_wasi_timestamp(struct timespec ts) { - return ts.tv_sec * 1000000000ull + ts.tv_nsec; -} - -/// const filestat_t = extern struct { -/// dev: device_t, u64 -/// ino: inode_t, u64 -/// filetype: filetype_t, u8 -/// nlink: linkcount_t, u64 -/// size: filesize_t, u64 -/// atim: timestamp_t, u64 -/// mtim: timestamp_t, u64 -/// ctim: timestamp_t, u64 -/// }; -static enum wasi_errno_t finish_wasi_stat(struct VirtualMachine *vm, - uint32_t buf, struct stat st) -{ - write_u64_le(vm->memory + buf + 0x00, 0); // device - write_u64_le(vm->memory + buf + 0x08, st.st_ino); - write_u64_le(vm->memory + buf + 0x10, to_wasi_filetype(st.st_mode)); - write_u64_le(vm->memory + buf + 0x18, 1); // nlink - write_u64_le(vm->memory + buf + 0x20, st.st_size); -#if defined(__APPLE__) - write_u64_le(vm->memory + buf + 0x28, to_wasi_timestamp(st.st_atimespec)); - write_u64_le(vm->memory + buf + 0x30, to_wasi_timestamp(st.st_mtimespec)); - write_u64_le(vm->memory + buf + 0x38, to_wasi_timestamp(st.st_ctimespec)); -#else - write_u64_le(vm->memory + buf + 0x28, to_wasi_timestamp(st.st_atim)); - write_u64_le(vm->memory + buf + 0x30, to_wasi_timestamp(st.st_mtim)); - write_u64_le(vm->memory + buf + 0x38, to_wasi_timestamp(st.st_ctim)); -#endif - return WASI_ESUCCESS; -} - -/// fn args_sizes_get(argc: *usize, argv_buf_size: *usize) errno_t; -static enum wasi_errno_t wasi_args_sizes_get(struct VirtualMachine *vm, - uint32_t argc, uint32_t argv_buf_size) -{ - uint32_t args_len = 0; - size_t buf_size = 0; - while (vm->args[args_len]) { - buf_size += strlen(vm->args[args_len]) + 1; - args_len += 1; - } - write_u32_le(vm->memory + argc, args_len); - write_u32_le(vm->memory + argv_buf_size, buf_size); - return WASI_ESUCCESS; -} - -/// extern fn args_get(argv: [*][*:0]u8, argv_buf: [*]u8) errno_t; -static enum wasi_errno_t wasi_args_get(struct VirtualMachine *vm, - uint32_t argv, uint32_t argv_buf) -{ - uint32_t argv_buf_i = 0; - uint32_t arg_i = 0; - for (;; arg_i += 1) { - const char *arg = vm->args[arg_i]; - if (!arg) break; - // Write the arg to the buffer. - uint32_t argv_ptr = argv_buf + argv_buf_i; - uint32_t arg_len = strlen(arg) + 1; - memcpy(vm->memory + argv_buf + argv_buf_i, arg, arg_len); - argv_buf_i += arg_len; - - write_u32_le(vm->memory + argv + 4 * arg_i , argv_ptr); - } - return WASI_ESUCCESS; -} - -/// extern fn random_get(buf: [*]u8, buf_len: usize) errno_t; -static enum wasi_errno_t wasi_random_get(struct VirtualMachine *vm, - uint32_t buf, uint32_t buf_len) -{ -#ifdef __linux__ - if (getrandom(vm->memory + buf, buf_len, 0) != buf_len) { - panic("getrandom failed"); - } -#else - for (uint32_t i = 0; i < buf_len; i += 1) { - vm->memory[buf + i] = rand(); - } -#endif - return WASI_ESUCCESS; -} - -/// fn fd_prestat_get(fd: fd_t, buf: *prestat_t) errno_t; -/// const prestat_t = extern struct { -/// pr_type: u8, -/// u: usize, -/// }; -static enum wasi_errno_t wasi_fd_prestat_get(struct VirtualMachine *vm, - int32_t fd, uint32_t buf) -{ - const struct Preopen *preopen = find_preopen(fd); - if (!preopen) return WASI_EBADF; - write_u32_le(vm->memory + buf + 0, 0); - write_u32_le(vm->memory + buf + 4, preopen->name_len); - return WASI_ESUCCESS; -} - -/// fn fd_prestat_dir_name(fd: fd_t, path: [*]u8, path_len: usize) errno_t; -static enum wasi_errno_t wasi_fd_prestat_dir_name(struct VirtualMachine *vm, - int32_t fd, uint32_t path, uint32_t path_len) -{ - const struct Preopen *preopen = find_preopen(fd); - if (!preopen) return WASI_EBADF; - if (path_len != preopen->name_len) - panic("wasi_fd_prestat_dir_name expects correct name_len"); - memcpy(vm->memory + path, preopen->name, path_len); - return WASI_ESUCCESS; -} - -/// extern fn fd_close(fd: fd_t) errno_t; -static enum wasi_errno_t wasi_fd_close(struct VirtualMachine *vm, int32_t fd) { - int host_fd = to_host_fd(fd); - close(host_fd); - return WASI_ESUCCESS; -} - -static enum wasi_errno_t wasi_fd_read( - struct VirtualMachine *vm, - int32_t fd, - uint32_t iovs, // [*]const iovec_t - uint32_t iovs_len, // usize - uint32_t nread // *usize -) { - int host_fd = to_host_fd(fd); - uint32_t i = 0; - size_t total_read = 0; - for (; i < iovs_len; i += 1) { - uint32_t ptr = read_u32_le(vm->memory + iovs + i * 8 + 0); - uint32_t len = read_u32_le(vm->memory + iovs + i * 8 + 4); - ssize_t amt_read = read(host_fd, vm->memory + ptr, len); - if (amt_read < 0) return to_wasi_err(errno); - total_read += amt_read; - if (amt_read != len) break; - } - write_u32_le(vm->memory + nread, total_read); - return WASI_ESUCCESS; -} - -/// extern fn fd_write(fd: fd_t, iovs: [*]const ciovec_t, iovs_len: usize, nwritten: *usize) errno_t; -/// const ciovec_t = extern struct { -/// base: [*]const u8, -/// len: usize, -/// }; -static enum wasi_errno_t wasi_fd_write(struct VirtualMachine *vm, - int32_t fd, uint32_t iovs, uint32_t iovs_len, uint32_t nwritten) -{ - int host_fd = to_host_fd(fd); - size_t total_written = 0; - for (uint32_t i = 0; i < iovs_len; i += 1) { - uint32_t ptr = read_u32_le(vm->memory + iovs + i * 8 + 0); - uint32_t len = read_u32_le(vm->memory + iovs + i * 8 + 4); - ssize_t written = write(host_fd, vm->memory + ptr, len); - if (written < 0) return to_wasi_err(errno); - total_written += written; - if (written != len) break; - } - write_u32_le(vm->memory + nwritten, total_written); - return WASI_ESUCCESS; -} - -static enum wasi_errno_t wasi_fd_pwrite( - struct VirtualMachine *vm, - int32_t fd, - uint32_t iovs, // [*]const ciovec_t - uint32_t iovs_len, // usize - uint64_t offset, // wasi.filesize_t, - uint32_t written_ptr // *usize -) { - int host_fd = to_host_fd(fd); - uint32_t i = 0; - size_t written = 0; - for (; i < iovs_len; i += 1) { - uint32_t ptr = read_u32_le(vm->memory + iovs + i * 8 + 0); - uint32_t len = read_u32_le(vm->memory + iovs + i * 8 + 4); - ssize_t w = pwrite(host_fd, vm->memory + ptr, len, offset + written); - if (w < 0) return to_wasi_err(errno); - written += w; - if (w != len) break; - } - write_u32_le(vm->memory + written_ptr, written); - return WASI_ESUCCESS; -} - -///extern fn path_open( -/// dirfd: fd_t, -/// dirflags: lookupflags_t, -/// path: [*]const u8, -/// path_len: usize, -/// oflags: oflags_t, -/// fs_rights_base: rights_t, -/// fs_rights_inheriting: rights_t, -/// fs_flags: fdflags_t, -/// fd: *fd_t, -///) errno_t; -static enum wasi_errno_t wasi_path_open( - struct VirtualMachine *vm, - int32_t dirfd, - uint32_t dirflags, // wasi.lookupflags_t, - uint32_t path, - uint32_t path_len, - uint16_t oflags, // wasi.oflags_t, - uint64_t fs_rights_base, // wasi.rights_t, - uint64_t fs_rights_inheriting, // wasi.rights_t, - uint16_t fs_flags, // wasi.fdflags_t, - uint32_t fd -) { - char sub_path[PATH_MAX]; - memcpy(sub_path, vm->memory + path, path_len); - sub_path[path_len] = 0; - - int host_fd = to_host_fd(dirfd); - uint32_t flags = - (((oflags & WASI_O_CREAT) != 0) ? O_CREAT : 0) | - (((oflags & WASI_O_DIRECTORY) != 0) ? O_DIRECTORY : 0) | - (((oflags & WASI_O_EXCL) != 0) ? O_EXCL : 0) | - (((oflags & WASI_O_TRUNC) != 0) ? O_TRUNC : 0) | - (((fs_flags & WASI_FDFLAG_APPEND) != 0) ? O_APPEND : 0) | - (((fs_flags & WASI_FDFLAG_DSYNC) != 0) ? O_DSYNC : 0) | - (((fs_flags & WASI_FDFLAG_NONBLOCK) != 0) ? O_NONBLOCK : 0) | - (((fs_flags & WASI_FDFLAG_SYNC) != 0) ? O_SYNC : 0); - - if (((fs_rights_base & WASI_RIGHT_FD_READ) != 0) && - ((fs_rights_base & WASI_RIGHT_FD_WRITE) != 0)) - { - flags |= O_RDWR; - } else if ((fs_rights_base & WASI_RIGHT_FD_WRITE) != 0) { - flags |= O_WRONLY; - } else if ((fs_rights_base & WASI_RIGHT_FD_READ) != 0) { - flags |= O_RDONLY; // no-op because O_RDONLY is 0 - } - mode_t mode = 0644; - int res_fd = openat(host_fd, sub_path, flags, mode); - if (res_fd == -1) return to_wasi_err(errno); - write_u32_le(vm->memory + fd, res_fd); - return WASI_ESUCCESS; -} - -static enum wasi_errno_t wasi_path_filestat_get( - struct VirtualMachine *vm, - int32_t fd, - uint32_t flags, // wasi.lookupflags_t, - uint32_t path, // [*]const u8 - uint32_t path_len, // usize - uint32_t buf // *filestat_t -) { - char sub_path[PATH_MAX]; - memcpy(sub_path, vm->memory + path, path_len); - sub_path[path_len] = 0; - - int host_fd = to_host_fd(fd); - struct stat st; - if (fstatat(host_fd, sub_path, &st, 0) == -1) return to_wasi_err(errno); - return finish_wasi_stat(vm, buf, st); -} - -/// extern fn path_create_directory(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; -static enum wasi_errno_t wasi_path_create_directory(struct VirtualMachine *vm, - int32_t wasi_fd, uint32_t path, uint32_t path_len) -{ - char sub_path[PATH_MAX]; - memcpy(sub_path, vm->memory + path, path_len); - sub_path[path_len] = 0; - - int host_fd = to_host_fd(wasi_fd); - if (mkdirat(host_fd, sub_path, 0777) == -1) return to_wasi_err(errno); - return WASI_ESUCCESS; -} - -static enum wasi_errno_t wasi_path_rename( - struct VirtualMachine *vm, - int32_t old_fd, - uint32_t old_path_ptr, // [*]const u8 - uint32_t old_path_len, // usize - int32_t new_fd, - uint32_t new_path_ptr, // [*]const u8 - uint32_t new_path_len // usize -) { - char old_path[PATH_MAX]; - memcpy(old_path, vm->memory + old_path_ptr, old_path_len); - old_path[old_path_len] = 0; - - char new_path[PATH_MAX]; - memcpy(new_path, vm->memory + new_path_ptr, new_path_len); - new_path[new_path_len] = 0; - - int old_host_fd = to_host_fd(old_fd); - int new_host_fd = to_host_fd(new_fd); - if (renameat(old_host_fd, old_path, new_host_fd, new_path) == -1) return to_wasi_err(errno); - return WASI_ESUCCESS; -} - -/// extern fn fd_filestat_get(fd: fd_t, buf: *filestat_t) errno_t; -static enum wasi_errno_t wasi_fd_filestat_get(struct VirtualMachine *vm, int32_t fd, uint32_t buf) { - int host_fd = to_host_fd(fd); - struct stat st; - if (fstat(host_fd, &st) == -1) return to_wasi_err(errno); - return finish_wasi_stat(vm, buf, st); -} - -static enum wasi_errno_t wasi_fd_filestat_set_size( struct VirtualMachine *vm, - int32_t fd, uint64_t size) -{ - int host_fd = to_host_fd(fd); - if (ftruncate(host_fd, size) == -1) return to_wasi_err(errno); - return WASI_ESUCCESS; -} - -/// pub extern "wasi_snapshot_preview1" fn fd_fdstat_get(fd: fd_t, buf: *fdstat_t) errno_t; -/// pub const fdstat_t = extern struct { -/// fs_filetype: filetype_t, u8 -/// fs_flags: fdflags_t, u16 -/// fs_rights_base: rights_t, u64 -/// fs_rights_inheriting: rights_t, u64 -/// }; -static enum wasi_errno_t wasi_fd_fdstat_get(struct VirtualMachine *vm, int32_t fd, uint32_t buf) { - int host_fd = to_host_fd(fd); - struct stat st; - if (fstat(host_fd, &st) == -1) return to_wasi_err(errno); - write_u16_le(vm->memory + buf + 0x00, to_wasi_filetype(st.st_mode)); - write_u16_le(vm->memory + buf + 0x02, 0); // flags - write_u64_le(vm->memory + buf + 0x08, UINT64_MAX); // rights_base - write_u64_le(vm->memory + buf + 0x10, UINT64_MAX); // rights_inheriting - return WASI_ESUCCESS; -} - -/// extern fn clock_time_get(clock_id: clockid_t, precision: timestamp_t, timestamp: *timestamp_t) errno_t; -static enum wasi_errno_t wasi_clock_time_get(struct VirtualMachine *vm, - uint32_t clock_id, uint64_t precision, uint32_t timestamp) -{ - if (clock_id != 1) panic("expected wasi_clock_time_get to use CLOCK_MONOTONIC"); - struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) return to_wasi_err(errno); - uint64_t wasi_ts = to_wasi_timestamp(ts); - write_u64_le(vm->memory + timestamp, wasi_ts); - return WASI_ESUCCESS; -} - -///pub extern "wasi_snapshot_preview1" fn debug(string: [*:0]const u8, x: u64) void; -void wasi_debug(struct VirtualMachine *vm, uint32_t text, uint64_t n) { - fprintf(stderr, "wasi_debug: '%s' number=%" PRIu64 " %" PRIx64 "\n", vm->memory + text, n, n); -} - -/// pub extern "wasi_snapshot_preview1" fn debug_slice(ptr: [*]const u8, len: usize) void; -void wasi_debug_slice(struct VirtualMachine *vm, uint32_t ptr, uint32_t len) { - fprintf(stderr, "wasi_debug_slice: '%.*s'\n", len, vm->memory + ptr); -} - -enum StackType { - ST_32, - ST_64, -}; - -struct Label { - enum WasmOp opcode; - uint32_t stack_index; - uint32_t stack_offset; - struct TypeInfo type_info; - // this is a UINT32_MAX terminated linked list that is stored in the operands array - uint32_t ref_list; - union { - struct ProgramCounter loop_pc; - uint32_t else_ref; - } extra; -}; - -static uint32_t Label_operandCount(const struct Label *label) { - if (label->opcode == WasmOp_loop) { - return label->type_info.param_count; - } else { - return label->type_info.result_count; - } -} - -static enum StackType Label_operandType(const struct Label *label, uint32_t index) { - if (label->opcode == WasmOp_loop) { - return bs_isSet(&label->type_info.param_types, index); - } else { - return bs_isSet(&label->type_info.result_types, index); - } -} - -#define max_stack_depth (1 << 12) - -struct StackInfo { - uint32_t top_index; - uint32_t top_offset; - uint32_t types[max_stack_depth >> 5]; - uint32_t offsets[max_stack_depth]; -}; - -static enum StackType si_top(const struct StackInfo *si) { - return bs_isSet(si->types, si->top_index - 1); -} - -static enum StackType si_local(const struct StackInfo *si, uint32_t local_idx) { - return bs_isSet(si->types, local_idx); -} - -static void si_push(struct StackInfo *si, enum StackType entry_type) { - bs_setValue(si->types, si->top_index, entry_type); - si->offsets[si->top_index] = si->top_offset; - si->top_index += 1; - si->top_offset += 1 + entry_type; -} - -static void si_pop(struct StackInfo *si, enum StackType entry_type) { - assert(si_top(si) == entry_type); - si->top_index -= 1; - si->top_offset -= 1 + entry_type; - assert(si->top_offset == si->offsets[si->top_index]); -} - -static void vm_decodeCode(struct VirtualMachine *vm, struct TypeInfo *func_type_info, - uint32_t *code_i, struct ProgramCounter *pc, struct StackInfo *stack) -{ - const char *mod_ptr = vm->mod_ptr; - uint8_t *opcodes = vm->opcodes; - uint32_t *operands = vm->operands; - - // push return address - uint32_t frame_size = stack->top_offset; - si_push(stack, ST_32); - si_push(stack, ST_32); - - uint32_t unreachable_depth = 0; - uint32_t label_i = 0; - static struct Label labels[1 << 9]; -#ifndef NDEBUG - memset(labels, 0xaa, sizeof(struct Label) * (1 << 9)); // to match the zig version -#endif - labels[label_i].opcode = WasmOp_block; - labels[label_i].stack_index = stack->top_index; - labels[label_i].stack_offset = stack->top_offset; - labels[label_i].type_info = *func_type_info; - labels[label_i].ref_list = UINT32_MAX; - - enum { - State_default, - State_bool_not, - } state = State_default; - - for (;;) { - assert(stack->top_index >= labels[0].stack_index); - assert(stack->top_offset >= labels[0].stack_offset); - enum WasmOp opcode = (uint8_t)mod_ptr[*code_i]; - *code_i += 1; - enum WasmPrefixedOp prefixed_opcode; - if (opcode == WasmOp_prefixed) prefixed_opcode = read32_uleb128(mod_ptr, code_i); - - //fprintf(stderr, "decodeCode opcode=0x%x pc=%u:%u\n", opcode, pc->opcode, pc->operand); - //struct ProgramCounter old_pc = *pc; - - if (unreachable_depth == 0) - switch (opcode) { - case WasmOp_unreachable: - case WasmOp_nop: - case WasmOp_block: - case WasmOp_loop: - case WasmOp_else: - case WasmOp_end: - case WasmOp_br: - case WasmOp_return: - case WasmOp_call: - case WasmOp_local_get: - case WasmOp_local_set: - case WasmOp_local_tee: - case WasmOp_global_get: - case WasmOp_global_set: - case WasmOp_drop: - case WasmOp_select: - break; // handled manually below - - case WasmOp_if: - case WasmOp_br_if: - case WasmOp_br_table: - case WasmOp_call_indirect: - si_pop(stack, ST_32); - break; - - case WasmOp_memory_size: - case WasmOp_i32_const: - case WasmOp_f32_const: - si_push(stack, ST_32); - break; - - case WasmOp_i64_const: - case WasmOp_f64_const: - si_push(stack, ST_64); - break; - - case WasmOp_i32_load: - case WasmOp_f32_load: - case WasmOp_i32_load8_s: - case WasmOp_i32_load8_u: - case WasmOp_i32_load16_s: - case WasmOp_i32_load16_u: - si_pop(stack, ST_32); - si_push(stack, ST_32); - break; - - case WasmOp_i64_load: - case WasmOp_f64_load: - case WasmOp_i64_load8_s: - case WasmOp_i64_load8_u: - case WasmOp_i64_load16_s: - case WasmOp_i64_load16_u: - case WasmOp_i64_load32_s: - case WasmOp_i64_load32_u: - si_pop(stack, ST_32); - si_push(stack, ST_64); - break; - - case WasmOp_memory_grow: - case WasmOp_i32_eqz: - case WasmOp_i32_clz: - case WasmOp_i32_ctz: - case WasmOp_i32_popcnt: - case WasmOp_f32_abs: - case WasmOp_f32_neg: - case WasmOp_f32_ceil: - case WasmOp_f32_floor: - case WasmOp_f32_trunc: - case WasmOp_f32_nearest: - case WasmOp_f32_sqrt: - case WasmOp_i32_trunc_f32_s: - case WasmOp_i32_trunc_f32_u: - case WasmOp_f32_convert_i32_s: - case WasmOp_f32_convert_i32_u: - case WasmOp_i32_reinterpret_f32: - case WasmOp_f32_reinterpret_i32: - case WasmOp_i32_extend8_s: - case WasmOp_i32_extend16_s: - si_pop(stack, ST_32); - si_push(stack, ST_32); - break; - - case WasmOp_i64_eqz: - case WasmOp_i32_wrap_i64: - case WasmOp_i32_trunc_f64_s: - case WasmOp_i32_trunc_f64_u: - case WasmOp_f32_convert_i64_s: - case WasmOp_f32_convert_i64_u: - case WasmOp_f32_demote_f64: - si_pop(stack, ST_64); - si_push(stack, ST_32); - break; - - case WasmOp_i64_clz: - case WasmOp_i64_ctz: - case WasmOp_i64_popcnt: - case WasmOp_f64_abs: - case WasmOp_f64_neg: - case WasmOp_f64_ceil: - case WasmOp_f64_floor: - case WasmOp_f64_trunc: - case WasmOp_f64_nearest: - case WasmOp_f64_sqrt: - case WasmOp_i64_trunc_f64_s: - case WasmOp_i64_trunc_f64_u: - case WasmOp_f64_convert_i64_s: - case WasmOp_f64_convert_i64_u: - case WasmOp_i64_reinterpret_f64: - case WasmOp_f64_reinterpret_i64: - case WasmOp_i64_extend8_s: - case WasmOp_i64_extend16_s: - case WasmOp_i64_extend32_s: - si_pop(stack, ST_64); - si_push(stack, ST_64); - break; - - case WasmOp_i64_extend_i32_s: - case WasmOp_i64_extend_i32_u: - case WasmOp_i64_trunc_f32_s: - case WasmOp_i64_trunc_f32_u: - case WasmOp_f64_convert_i32_s: - case WasmOp_f64_convert_i32_u: - case WasmOp_f64_promote_f32: - si_pop(stack, ST_32); - si_push(stack, ST_64); - break; - - case WasmOp_i32_store: - case WasmOp_f32_store: - case WasmOp_i32_store8: - case WasmOp_i32_store16: - si_pop(stack, ST_32); - si_pop(stack, ST_32); - break; - - case WasmOp_i64_store: - case WasmOp_f64_store: - case WasmOp_i64_store8: - case WasmOp_i64_store16: - case WasmOp_i64_store32: - si_pop(stack, ST_64); - si_pop(stack, ST_32); - break; - - case WasmOp_i32_eq: - case WasmOp_i32_ne: - case WasmOp_i32_lt_s: - case WasmOp_i32_lt_u: - case WasmOp_i32_gt_s: - case WasmOp_i32_gt_u: - case WasmOp_i32_le_s: - case WasmOp_i32_le_u: - case WasmOp_i32_ge_s: - case WasmOp_i32_ge_u: - case WasmOp_f32_eq: - case WasmOp_f32_ne: - case WasmOp_f32_lt: - case WasmOp_f32_gt: - case WasmOp_f32_le: - case WasmOp_f32_ge: - si_pop(stack, ST_32); - si_pop(stack, ST_32); - si_push(stack, ST_32); - break; - - case WasmOp_i64_eq: - case WasmOp_i64_ne: - case WasmOp_i64_lt_s: - case WasmOp_i64_lt_u: - case WasmOp_i64_gt_s: - case WasmOp_i64_gt_u: - case WasmOp_i64_le_s: - case WasmOp_i64_le_u: - case WasmOp_i64_ge_s: - case WasmOp_i64_ge_u: - case WasmOp_f64_eq: - case WasmOp_f64_ne: - case WasmOp_f64_lt: - case WasmOp_f64_gt: - case WasmOp_f64_le: - case WasmOp_f64_ge: - si_pop(stack, ST_64); - si_pop(stack, ST_64); - si_push(stack, ST_32); - break; - - case WasmOp_i32_add: - case WasmOp_i32_sub: - case WasmOp_i32_mul: - case WasmOp_i32_div_s: - case WasmOp_i32_div_u: - case WasmOp_i32_rem_s: - case WasmOp_i32_rem_u: - case WasmOp_i32_and: - case WasmOp_i32_or: - case WasmOp_i32_xor: - case WasmOp_i32_shl: - case WasmOp_i32_shr_s: - case WasmOp_i32_shr_u: - case WasmOp_i32_rotl: - case WasmOp_i32_rotr: - case WasmOp_f32_add: - case WasmOp_f32_sub: - case WasmOp_f32_mul: - case WasmOp_f32_div: - case WasmOp_f32_min: - case WasmOp_f32_max: - case WasmOp_f32_copysign: - si_pop(stack, ST_32); - si_pop(stack, ST_32); - si_push(stack, ST_32); - break; - - case WasmOp_i64_add: - case WasmOp_i64_sub: - case WasmOp_i64_mul: - case WasmOp_i64_div_s: - case WasmOp_i64_div_u: - case WasmOp_i64_rem_s: - case WasmOp_i64_rem_u: - case WasmOp_i64_and: - case WasmOp_i64_or: - case WasmOp_i64_xor: - case WasmOp_i64_shl: - case WasmOp_i64_shr_s: - case WasmOp_i64_shr_u: - case WasmOp_i64_rotl: - case WasmOp_i64_rotr: - case WasmOp_f64_add: - case WasmOp_f64_sub: - case WasmOp_f64_mul: - case WasmOp_f64_div: - case WasmOp_f64_min: - case WasmOp_f64_max: - case WasmOp_f64_copysign: - si_pop(stack, ST_64); - si_pop(stack, ST_64); - si_push(stack, ST_64); - break; - - case WasmOp_prefixed: - switch (prefixed_opcode) { - case WasmPrefixedOp_i32_trunc_sat_f32_s: - case WasmPrefixedOp_i32_trunc_sat_f32_u: - si_pop(stack, ST_32); - si_push(stack, ST_32); - break; - - case WasmPrefixedOp_i32_trunc_sat_f64_s: - case WasmPrefixedOp_i32_trunc_sat_f64_u: - si_pop(stack, ST_64); - si_push(stack, ST_32); - break; - - case WasmPrefixedOp_i64_trunc_sat_f32_s: - case WasmPrefixedOp_i64_trunc_sat_f32_u: - si_pop(stack, ST_32); - si_push(stack, ST_64); - break; - - case WasmPrefixedOp_i64_trunc_sat_f64_s: - case WasmPrefixedOp_i64_trunc_sat_f64_u: - si_pop(stack, ST_64); - si_push(stack, ST_64); - break; - - case WasmPrefixedOp_memory_init: - case WasmPrefixedOp_memory_copy: - case WasmPrefixedOp_memory_fill: - case WasmPrefixedOp_table_init: - case WasmPrefixedOp_table_copy: - si_pop(stack, ST_32); - si_pop(stack, ST_32); - si_pop(stack, ST_32); - break; - - case WasmPrefixedOp_table_fill: - si_pop(stack, ST_32); - panic("si_pop(stack, unreachable);"); - si_pop(stack, ST_32); - break; - - case WasmPrefixedOp_data_drop: - case WasmPrefixedOp_elem_drop: - break; - - case WasmPrefixedOp_table_grow: - si_pop(stack, ST_32); - panic("si_pop(stack, unreachable);"); - si_push(stack, ST_32); - break; - - case WasmPrefixedOp_table_size: - si_push(stack, ST_32); - break; - - default: panic("unexpected prefixed opcode"); - } - break; - - default: panic("unexpected opcode"); - } - switch (opcode) { - case WasmOp_unreachable: - if (unreachable_depth == 0) { - opcodes[pc->opcode] = Op_unreachable; - pc->opcode += 1; - unreachable_depth += 1; - } - break; - - case WasmOp_nop: - case WasmOp_i32_reinterpret_f32: - case WasmOp_i64_reinterpret_f64: - case WasmOp_f32_reinterpret_i32: - case WasmOp_f64_reinterpret_i64: - break; - - case WasmOp_block: - case WasmOp_loop: - case WasmOp_if: - { - int64_t block_type = read64_ileb128(mod_ptr, code_i); - if (unreachable_depth == 0) { - label_i += 1; - struct Label *label = &labels[label_i]; - label->opcode = opcode; - if (block_type < 0) { - label->type_info.param_count = 0; - label->type_info.param_types = 0; - label->type_info.result_count = block_type != -0x40; - switch (block_type) { - case -0x40: - case -1: - case -3: - label->type_info.result_types = 0; - break; - case -2: - case -4: - label->type_info.result_types = UINT32_MAX; - break; - default: panic("unexpected param type"); - } - } else label->type_info = vm->types[block_type]; - - uint32_t param_i = label->type_info.param_count; - while (param_i > 0) { - param_i -= 1; - si_pop(stack, bs_isSet(&label->type_info.param_types, param_i)); - } - label->stack_index = stack->top_index; - label->stack_offset = stack->top_offset; - label->ref_list = UINT32_MAX; - for (; param_i < label->type_info.param_count; param_i += 1) - si_push(stack, bs_isSet(&label->type_info.param_types, param_i)); - - switch (opcode) { - case WasmOp_block: - break; - - case WasmOp_loop: - label->extra.loop_pc = *pc; - break; - - case WasmOp_if: - if (state == State_bool_not) { - pc->opcode -= 1; - opcodes[pc->opcode] = Op_br_nez_void; - } else opcodes[pc->opcode] = Op_br_eqz_void; - pc->opcode += 1; - operands[pc->operand] = 0; - label->extra.else_ref = pc->operand + 1; - pc->operand += 3; - break; - - default: panic("unexpected label opcode"); - } - } else unreachable_depth += 1; - } - break; - - case WasmOp_else: - if (unreachable_depth <= 1) { - struct Label *label = &labels[label_i]; - assert(label->opcode == WasmOp_if); - label->opcode = WasmOp_else; - - if (unreachable_depth == 0) { - uint32_t operand_count = Label_operandCount(label); - for (uint32_t operand_i = operand_count; operand_i > 0; ) { - operand_i -= 1; - si_pop(stack, Label_operandType(label, operand_i)); - } - assert(stack->top_index == label->stack_index); - assert(stack->top_offset == label->stack_offset); - - switch (operand_count) { - case 0: - opcodes[pc->opcode] = Op_br_void; - break; - - case 1: - //fprintf(stderr, "label_i=%u operand_type=%d\n", - // label_i, Label_operandType(label, 0)); - switch (Label_operandType(label, 0)) { - case ST_32: opcodes[pc->opcode] = Op_br_32; break; - case ST_64: opcodes[pc->opcode] = Op_br_64; break; - } - break; - - default: panic("unexpected operand count"); - } - pc->opcode += 1; - operands[pc->operand + 0] = stack->top_offset - label->stack_offset; - operands[pc->operand + 1] = label->ref_list; - label->ref_list = pc->operand + 1; - pc->operand += 3; - } else unreachable_depth = 0; - - operands[label->extra.else_ref + 0] = pc->opcode; - operands[label->extra.else_ref + 1] = pc->operand; - for (uint32_t param_i = 0; param_i < label->type_info.param_count; param_i += 1) - si_push(stack, bs_isSet(&label->type_info.param_types, param_i)); - } - break; - - case WasmOp_end: - if (unreachable_depth <= 1) { - struct Label *label = &labels[label_i]; - struct ProgramCounter *target_pc = (label->opcode == WasmOp_loop) ? &label->extra.loop_pc : pc; - if (label->opcode == WasmOp_if) { - operands[label->extra.else_ref + 0] = target_pc->opcode; - operands[label->extra.else_ref + 1] = target_pc->operand; - } - uint32_t ref = label->ref_list; - while (ref != UINT32_MAX) { - uint32_t next_ref = operands[ref]; - operands[ref + 0] = target_pc->opcode; - operands[ref + 1] = target_pc->operand; - ref = next_ref; - } - - if (unreachable_depth == 0) { - for (uint32_t result_i = label->type_info.result_count; result_i > 0; ) { - result_i -= 1; - si_pop(stack, bs_isSet(&label->type_info.result_types, result_i)); - } - } else unreachable_depth = 0; - - if (label_i == 0) { - assert(stack->top_index == label->stack_index); - assert(stack->top_offset == label->stack_offset); - - switch (labels[0].type_info.result_count) { - case 0: - opcodes[pc->opcode] = Op_return_void; - break; - - case 1: - switch ((enum StackType)bs_isSet(&labels[0].type_info.result_types, 0)) { - case ST_32: opcodes[pc->opcode] = Op_return_32; break; - case ST_64: opcodes[pc->opcode] = Op_return_64; break; - } - break; - - default: panic("unexpected operand count"); - } - pc->opcode += 1; - operands[pc->operand + 0] = stack->top_offset - labels[0].stack_offset; - operands[pc->operand + 1] = frame_size; - pc->operand += 2; - return; - } - label_i -= 1; - - stack->top_index = label->stack_index; - stack->top_offset = label->stack_offset; - for (uint32_t result_i = 0; result_i < label->type_info.result_count; result_i += 1) - si_push(stack, bs_isSet(&label->type_info.result_types, result_i)); - } else unreachable_depth -= 1; - break; - - case WasmOp_br: - case WasmOp_br_if: - { - uint32_t label_idx = read32_uleb128(mod_ptr, code_i); - if (unreachable_depth == 0) { - struct Label *label = &labels[label_i - label_idx]; - uint32_t operand_count = Label_operandCount(label); - uint32_t operand_i = operand_count; - while (operand_i > 0) { - operand_i -= 1; - si_pop(stack, Label_operandType(label, operand_i)); - } - - switch (opcode) { - case WasmOp_br: - switch (operand_count) { - case 0: - opcodes[pc->opcode] = Op_br_void; - break; - - case 1: - switch (Label_operandType(label, 0)) { - case ST_32: opcodes[pc->opcode] = Op_br_32; break; - case ST_64: opcodes[pc->opcode] = Op_br_64; break; - } - break; - - default: panic("unexpected operand count"); - } - break; - - case WasmOp_br_if: - switch (operand_count) { - case 0: - if (state == State_bool_not) { - pc->opcode -= 1; - opcodes[pc->opcode] = Op_br_eqz_void; - } else opcodes[pc->opcode] = Op_br_nez_void; - break; - - case 1: - switch (Label_operandType(label, 0)) { - case ST_32: - if (state == State_bool_not) { - pc->opcode -= 1; - opcodes[pc->opcode] = Op_br_eqz_32; - } else opcodes[pc->opcode] = Op_br_nez_32; - break; - - case ST_64: - if (state == State_bool_not) { - pc->opcode -= 1; - opcodes[pc->opcode] = Op_br_eqz_64; - } else opcodes[pc->opcode] = Op_br_nez_64; - break; - } - break; - - default: panic("unexpected operand count"); - } - break; - - default: panic("unexpected opcode"); - } - pc->opcode += 1; - operands[pc->operand + 0] = stack->top_offset - label->stack_offset; - operands[pc->operand + 1] = label->ref_list; - label->ref_list = pc->operand + 1; - pc->operand += 3; - - switch (opcode) { - case WasmOp_br: - unreachable_depth += 1; - break; - - case WasmOp_br_if: - for (; operand_i < operand_count; operand_i += 1) - si_push(stack, Label_operandType(label, operand_i)); - break; - - default: panic("unexpected opcode"); - } - } - } - break; - - case WasmOp_br_table: - { - uint32_t labels_len = read32_uleb128(mod_ptr, code_i); - for (uint32_t i = 0; i <= labels_len; i += 1) { - uint32_t label_idx = read32_uleb128(mod_ptr, code_i); - if (unreachable_depth != 0) continue; - struct Label *label = &labels[label_i - label_idx]; - if (i == 0) { - uint32_t operand_count = Label_operandCount(label); - for (uint32_t operand_i = operand_count; operand_i > 0; ) { - operand_i -= 1; - si_pop(stack, Label_operandType(label, operand_i)); - } - - switch (operand_count) { - case 0: - opcodes[pc->opcode] = Op_br_table_void; - break; - - case 1: - switch (Label_operandType(label, 0)) { - case ST_32: opcodes[pc->opcode] = Op_br_table_32; break; - case ST_64: opcodes[pc->opcode] = Op_br_table_64; break; - } - break; - - default: panic("unexpected operand count"); - } - pc->opcode += 1; - operands[pc->operand] = labels_len; - pc->operand += 1; - } - operands[pc->operand + 0] = stack->top_offset - label->stack_offset; - operands[pc->operand + 1] = label->ref_list; - label->ref_list = pc->operand + 1; - pc->operand += 3; - } - if (unreachable_depth == 0) unreachable_depth += 1; - } - break; - - case WasmOp_return: - if (unreachable_depth == 0) { - for (uint32_t result_i = labels[0].type_info.result_count; result_i > 0; ) { - result_i -= 1; - si_pop(stack, bs_isSet(&labels[0].type_info.result_types, result_i)); - } - - switch (labels[0].type_info.result_count) { - case 0: - opcodes[pc->opcode] = Op_return_void; - break; - - case 1: - switch ((enum StackType)bs_isSet(&labels[0].type_info.result_types, 0)) { - case ST_32: opcodes[pc->opcode] = Op_return_32; break; - case ST_64: opcodes[pc->opcode] = Op_return_64; break; - } - break; - - default: panic("unexpected operand count"); - } - pc->opcode += 1; - operands[pc->operand + 0] = stack->top_offset - labels[0].stack_offset; - operands[pc->operand + 1] = frame_size; - pc->operand += 2; - unreachable_depth += 1; - } - break; - - case WasmOp_call: - { - uint32_t fn_id = read32_uleb128(mod_ptr, code_i); - if (unreachable_depth == 0) { - uint32_t type_idx; - if (fn_id < vm->imports_len) { - opcodes[pc->opcode + 0] = Op_call_import; - opcodes[pc->opcode + 1] = fn_id; - pc->opcode += 2; - type_idx = vm->imports[fn_id].type_idx; - } else { - uint32_t fn_idx = fn_id - vm->imports_len; - opcodes[pc->opcode] = Op_call_func; - pc->opcode += 1; - operands[pc->operand] = fn_idx; - pc->operand += 1; - type_idx = vm->functions[fn_idx].type_idx; - } - struct TypeInfo *type_info = &vm->types[type_idx]; - - for (uint32_t param_i = type_info->param_count; param_i > 0; ) { - param_i -= 1; - si_pop(stack, bs_isSet(&type_info->param_types, param_i)); - } - for (uint32_t result_i = 0; result_i < type_info->result_count; result_i += 1) - si_push(stack, bs_isSet(&type_info->result_types, result_i)); - } - } - break; - - case WasmOp_call_indirect: - { - uint32_t type_idx = read32_uleb128(mod_ptr, code_i); - if (read32_uleb128(mod_ptr, code_i) != 0) panic("unexpected table index"); - if (unreachable_depth == 0) { - opcodes[pc->opcode] = Op_call_indirect; - pc->opcode += 1; - - struct TypeInfo *type_info = &vm->types[type_idx]; - for (uint32_t param_i = type_info->param_count; param_i > 0; ) { - param_i -= 1; - si_pop(stack, bs_isSet(&type_info->param_types, param_i)); - } - for (uint32_t result_i = 0; result_i < type_info->result_count; result_i += 1) - si_push(stack, bs_isSet(&type_info->result_types, result_i)); - } - } - break; - - case WasmOp_select: - case WasmOp_drop: - if (unreachable_depth == 0) { - if (opcode == WasmOp_select) si_pop(stack, ST_32); - enum StackType operand_type = si_top(stack); - si_pop(stack, operand_type); - if (opcode == WasmOp_select) { - si_pop(stack, operand_type); - si_push(stack, operand_type); - } - switch (opcode) { - case WasmOp_select: - switch (operand_type) { - case ST_32: opcodes[pc->opcode] = Op_select_32; break; - case ST_64: opcodes[pc->opcode] = Op_select_64; break; - } - break; - - case WasmOp_drop: - switch (operand_type) { - case ST_32: opcodes[pc->opcode] = Op_drop_32; break; - case ST_64: opcodes[pc->opcode] = Op_drop_64; break; - } - break; - - default: panic("unexpected opcode"); - } - pc->opcode += 1; - } - break; - - case WasmOp_local_get: - case WasmOp_local_set: - case WasmOp_local_tee: - { - uint32_t local_idx = read32_uleb128(mod_ptr, code_i); - if (unreachable_depth == 0) { - enum StackType local_type = si_local(stack, local_idx); - switch (opcode) { - case WasmOp_local_get: - switch (local_type) { - case ST_32: opcodes[pc->opcode] = Op_local_get_32; break; - case ST_64: opcodes[pc->opcode] = Op_local_get_64; break; - } - break; - - case WasmOp_local_set: - switch (local_type) { - case ST_32: opcodes[pc->opcode] = Op_local_set_32; break; - case ST_64: opcodes[pc->opcode] = Op_local_set_64; break; - } - break; - - case WasmOp_local_tee: - switch (local_type) { - case ST_32: opcodes[pc->opcode] = Op_local_tee_32; break; - case ST_64: opcodes[pc->opcode] = Op_local_tee_64; break; - } - break; - - default: panic("unexpected opcode"); - } - pc->opcode += 1; - operands[pc->operand] = stack->top_offset - stack->offsets[local_idx]; - pc->operand += 1; - switch (opcode) { - case WasmOp_local_get: - si_push(stack, local_type); - break; - - case WasmOp_local_set: - si_pop(stack, local_type); - break; - - case WasmOp_local_tee: - si_pop(stack, local_type); - si_push(stack, local_type); - break; - - default: panic("unexpected opcode"); - } - } - } - break; - - case WasmOp_global_get: - case WasmOp_global_set: - { - uint32_t global_idx = read32_uleb128(mod_ptr, code_i); - if (unreachable_depth == 0) { - enum StackType global_type = ST_32; // all globals assumed to be 32-bit - switch (opcode) { - case WasmOp_global_get: - switch (global_idx) { - case 0: opcodes[pc->opcode] = Op_global_get_0_32; break; - default: opcodes[pc->opcode] = Op_global_get_32; break; - } - break; - - case WasmOp_global_set: - switch (global_idx) { - case 0: opcodes[pc->opcode] = Op_global_set_0_32; break; - default: opcodes[pc->opcode] = Op_global_set_32; break; - } - break; - - default: panic("unexpected opcode"); - } - pc->opcode += 1; - if (global_idx != 0) { - operands[pc->operand] = global_idx; - pc->operand += 1; - } - switch (opcode) { - case WasmOp_global_get: - si_push(stack, global_type); - break; - - case WasmOp_global_set: - si_pop(stack, global_type); - break; - - default: panic("unexpected opcode"); - } - } - } - break; - - case WasmOp_i32_load: - case WasmOp_i64_load: - case WasmOp_f32_load: - case WasmOp_f64_load: - case WasmOp_i32_load8_s: - case WasmOp_i32_load8_u: - case WasmOp_i32_load16_s: - case WasmOp_i32_load16_u: - case WasmOp_i64_load8_s: - case WasmOp_i64_load8_u: - case WasmOp_i64_load16_s: - case WasmOp_i64_load16_u: - case WasmOp_i64_load32_s: - case WasmOp_i64_load32_u: - case WasmOp_i32_store: - case WasmOp_i64_store: - case WasmOp_f32_store: - case WasmOp_f64_store: - case WasmOp_i32_store8: - case WasmOp_i32_store16: - case WasmOp_i64_store8: - case WasmOp_i64_store16: - case WasmOp_i64_store32: - { - uint32_t alignment = read32_uleb128(mod_ptr, code_i); - uint32_t offset = read32_uleb128(mod_ptr, code_i); - (void)alignment; - if (unreachable_depth == 0) { - switch (opcode) { - default: break; - - case WasmOp_i64_store8: case WasmOp_i64_store16: case WasmOp_i64_store32: - opcodes[pc->opcode] = Op_drop_32; - pc->opcode += 1; - break; - } - switch (opcode) { - case WasmOp_i32_load8_s: case WasmOp_i32_load8_u: - case WasmOp_i64_load8_s: case WasmOp_i64_load8_u: - switch (offset) { - case 0: opcodes[pc->opcode] = Op_load_0_8; break; - default: opcodes[pc->opcode] = Op_load_8; break; - } - break; - - case WasmOp_i32_load16_s: case WasmOp_i32_load16_u: - case WasmOp_i64_load16_s: case WasmOp_i64_load16_u: - switch (offset) { - case 0: opcodes[pc->opcode] = Op_load_0_16; break; - default: opcodes[pc->opcode] = Op_load_16; break; - } - break; - - case WasmOp_i32_load: case WasmOp_f32_load: - case WasmOp_i64_load32_s: case WasmOp_i64_load32_u: - switch (offset) { - case 0: opcodes[pc->opcode] = Op_load_0_32; break; - default: opcodes[pc->opcode] = Op_load_32; break; - } - break; - - case WasmOp_i64_load: case WasmOp_f64_load: - switch (offset) { - case 0: opcodes[pc->opcode] = Op_load_0_64; break; - default: opcodes[pc->opcode] = Op_load_64; break; - } - break; - - case WasmOp_i32_store8: case WasmOp_i64_store8: - switch (offset) { - case 0: opcodes[pc->opcode] = Op_store_0_8; break; - default: opcodes[pc->opcode] = Op_store_8; break; - } - break; - - case WasmOp_i32_store16: case WasmOp_i64_store16: - switch (offset) { - case 0: opcodes[pc->opcode] = Op_store_0_16; break; - default: opcodes[pc->opcode] = Op_store_16; break; - } - break; - - case WasmOp_i32_store: case WasmOp_f32_store: case WasmOp_i64_store32: - switch (offset) { - case 0: opcodes[pc->opcode] = Op_store_0_32; break; - default: opcodes[pc->opcode] = Op_store_32; break; - } - break; - - case WasmOp_i64_store: case WasmOp_f64_store: - switch (offset) { - case 0: opcodes[pc->opcode] = Op_store_0_64; break; - default: opcodes[pc->opcode] = Op_store_64; break; - } - break; - - default: panic("unexpected opcode"); - } - pc->opcode += 1; - switch (offset) { - case 0: break; - - default: - operands[pc->operand] = offset; - pc->operand += 1; - break; - } - switch (opcode) { - default: break; - - case WasmOp_i32_load8_s: case WasmOp_i64_load8_s: - opcodes[pc->opcode] = Op_sext8_32; - pc->opcode += 1; - break; - - case WasmOp_i32_load16_s: case WasmOp_i64_load16_s: - opcodes[pc->opcode] = Op_sext16_32; - pc->opcode += 1; - break; - } - switch (opcode) { - default: break; - - case WasmOp_i64_load8_s: case WasmOp_i64_load16_s: case WasmOp_i64_load32_s: - opcodes[pc->opcode] = Op_sext_64_32; - pc->opcode += 1; - break; - - case WasmOp_i64_load8_u: case WasmOp_i64_load16_u: case WasmOp_i64_load32_u: - opcodes[pc->opcode] = Op_zext_64_32; - pc->opcode += 1; - break; - } - } - } - break; - - case WasmOp_memory_size: - case WasmOp_memory_grow: - { - if (mod_ptr[*code_i] != 0) panic("unexpected memory index"); - *code_i += 1; - if (unreachable_depth == 0) { - switch (opcode) { - case WasmOp_memory_size: opcodes[pc->opcode] = Op_mem_size; break; - case WasmOp_memory_grow: opcodes[pc->opcode] = Op_mem_grow; break; - default: panic("unexpected opcode"); - } - pc->opcode += 1; - } - } - break; - - case WasmOp_i32_const: - case WasmOp_f32_const: - { - uint32_t value; - switch (opcode) { - case WasmOp_i32_const: value = read32_ileb128(mod_ptr, code_i); break; - - case WasmOp_f32_const: - value = read_u32_le(&mod_ptr[*code_i]); - *code_i += sizeof(value); - break; - - default: panic("unexpected opcode"); - } - if (unreachable_depth == 0) { - switch (value) { - case 0: opcodes[pc->opcode] = Op_const_0_32; break; - case 1: opcodes[pc->opcode] = Op_const_1_32; break; - - default: - opcodes[pc->opcode] = Op_const_32; - operands[pc->operand] = value; - pc->operand += 1; - break; - - case UINT32_MAX: opcodes[pc->opcode] = Op_const_umax_32; break; - } - pc->opcode += 1; - } - } - break; - - case WasmOp_i64_const: - case WasmOp_f64_const: - { - uint64_t value; - switch (opcode) { - case WasmOp_i64_const: value = read64_ileb128(mod_ptr, code_i); break; - - case WasmOp_f64_const: - value = read_u64_le(&mod_ptr[*code_i]); - *code_i += sizeof(value); - break; - - default: panic("unexpected opcode"); - } - - if (unreachable_depth == 0) { - switch (value) { - case 0: opcodes[pc->opcode] = Op_const_0_64; break; - case 1: opcodes[pc->opcode] = Op_const_1_64; break; - - default: - opcodes[pc->opcode] = Op_const_64; - operands[pc->operand + 0] = (uint32_t)(value >> 0); - operands[pc->operand + 1] = (uint32_t)(value >> 32); - pc->operand += 2; - break; - - case UINT64_MAX: opcodes[pc->opcode] = Op_const_umax_64; break; - } - pc->opcode += 1; - } - } - break; - - default: - if (unreachable_depth == 0) { - switch (opcode) { - case WasmOp_i32_eqz: opcodes[pc->opcode] = Op_eqz_32; break; - case WasmOp_i32_eq: opcodes[pc->opcode] = Op_eq_32; break; - case WasmOp_i32_ne: opcodes[pc->opcode] = Op_ne_32; break; - case WasmOp_i32_lt_s: opcodes[pc->opcode] = Op_slt_32; break; - case WasmOp_i32_lt_u: opcodes[pc->opcode] = Op_ult_32; break; - case WasmOp_i32_gt_s: opcodes[pc->opcode] = Op_sgt_32; break; - case WasmOp_i32_gt_u: opcodes[pc->opcode] = Op_ugt_32; break; - case WasmOp_i32_le_s: opcodes[pc->opcode] = Op_sle_32; break; - case WasmOp_i32_le_u: opcodes[pc->opcode] = Op_ule_32; break; - case WasmOp_i32_ge_s: opcodes[pc->opcode] = Op_sge_32; break; - case WasmOp_i32_ge_u: opcodes[pc->opcode] = Op_uge_32; break; - case WasmOp_i64_eqz: opcodes[pc->opcode] = Op_eqz_64; break; - case WasmOp_i64_eq: opcodes[pc->opcode] = Op_eq_64; break; - case WasmOp_i64_ne: opcodes[pc->opcode] = Op_ne_64; break; - case WasmOp_i64_lt_s: opcodes[pc->opcode] = Op_slt_64; break; - case WasmOp_i64_lt_u: opcodes[pc->opcode] = Op_ult_64; break; - case WasmOp_i64_gt_s: opcodes[pc->opcode] = Op_sgt_64; break; - case WasmOp_i64_gt_u: opcodes[pc->opcode] = Op_ugt_64; break; - case WasmOp_i64_le_s: opcodes[pc->opcode] = Op_sle_64; break; - case WasmOp_i64_le_u: opcodes[pc->opcode] = Op_ule_64; break; - case WasmOp_i64_ge_s: opcodes[pc->opcode] = Op_sge_64; break; - case WasmOp_i64_ge_u: opcodes[pc->opcode] = Op_uge_64; break; - case WasmOp_f32_eq: opcodes[pc->opcode] = Op_feq_32; break; - case WasmOp_f32_ne: opcodes[pc->opcode] = Op_fne_32; break; - case WasmOp_f32_lt: opcodes[pc->opcode] = Op_flt_32; break; - case WasmOp_f32_gt: opcodes[pc->opcode] = Op_fgt_32; break; - case WasmOp_f32_le: opcodes[pc->opcode] = Op_fle_32; break; - case WasmOp_f32_ge: opcodes[pc->opcode] = Op_fge_32; break; - case WasmOp_f64_eq: opcodes[pc->opcode] = Op_feq_64; break; - case WasmOp_f64_ne: opcodes[pc->opcode] = Op_fne_64; break; - case WasmOp_f64_lt: opcodes[pc->opcode] = Op_flt_64; break; - case WasmOp_f64_gt: opcodes[pc->opcode] = Op_fgt_64; break; - case WasmOp_f64_le: opcodes[pc->opcode] = Op_fle_64; break; - case WasmOp_f64_ge: opcodes[pc->opcode] = Op_fge_64; break; - case WasmOp_i32_clz: opcodes[pc->opcode] = Op_clz_32; break; - case WasmOp_i32_ctz: opcodes[pc->opcode] = Op_ctz_32; break; - case WasmOp_i32_popcnt: opcodes[pc->opcode] = Op_popcnt_32; break; - case WasmOp_i32_add: opcodes[pc->opcode] = Op_add_32; break; - case WasmOp_i32_sub: opcodes[pc->opcode] = Op_sub_32; break; - case WasmOp_i32_mul: opcodes[pc->opcode] = Op_mul_32; break; - case WasmOp_i32_div_s: opcodes[pc->opcode] = Op_sdiv_32; break; - case WasmOp_i32_div_u: opcodes[pc->opcode] = Op_udiv_32; break; - case WasmOp_i32_rem_s: opcodes[pc->opcode] = Op_srem_32; break; - case WasmOp_i32_rem_u: opcodes[pc->opcode] = Op_urem_32; break; - case WasmOp_i32_and: opcodes[pc->opcode] = Op_and_32; break; - case WasmOp_i32_or: opcodes[pc->opcode] = Op_or_32; break; - case WasmOp_i32_xor: opcodes[pc->opcode] = Op_xor_32; break; - case WasmOp_i32_shl: opcodes[pc->opcode] = Op_shl_32; break; - case WasmOp_i32_shr_s: opcodes[pc->opcode] = Op_ashr_32; break; - case WasmOp_i32_shr_u: opcodes[pc->opcode] = Op_lshr_32; break; - case WasmOp_i32_rotl: opcodes[pc->opcode] = Op_rol_32; break; - case WasmOp_i32_rotr: opcodes[pc->opcode] = Op_ror_32; break; - case WasmOp_i64_clz: opcodes[pc->opcode] = Op_clz_64; break; - case WasmOp_i64_ctz: opcodes[pc->opcode] = Op_ctz_64; break; - case WasmOp_i64_popcnt: opcodes[pc->opcode] = Op_popcnt_64; break; - case WasmOp_i64_add: opcodes[pc->opcode] = Op_add_64; break; - case WasmOp_i64_sub: opcodes[pc->opcode] = Op_sub_64; break; - case WasmOp_i64_mul: opcodes[pc->opcode] = Op_mul_64; break; - case WasmOp_i64_div_s: opcodes[pc->opcode] = Op_sdiv_64; break; - case WasmOp_i64_div_u: opcodes[pc->opcode] = Op_udiv_64; break; - case WasmOp_i64_rem_s: opcodes[pc->opcode] = Op_srem_64; break; - case WasmOp_i64_rem_u: opcodes[pc->opcode] = Op_urem_64; break; - case WasmOp_i64_and: opcodes[pc->opcode] = Op_and_64; break; - case WasmOp_i64_or: opcodes[pc->opcode] = Op_or_64; break; - case WasmOp_i64_xor: opcodes[pc->opcode] = Op_xor_64; break; - case WasmOp_i64_shl: opcodes[pc->opcode] = Op_shl_64; break; - case WasmOp_i64_shr_s: opcodes[pc->opcode] = Op_ashr_64; break; - case WasmOp_i64_shr_u: opcodes[pc->opcode] = Op_lshr_64; break; - case WasmOp_i64_rotl: opcodes[pc->opcode] = Op_rol_64; break; - case WasmOp_i64_rotr: opcodes[pc->opcode] = Op_ror_64; break; - case WasmOp_f32_abs: opcodes[pc->opcode] = Op_fabs_32; break; - case WasmOp_f32_neg: opcodes[pc->opcode] = Op_fneg_32; break; - case WasmOp_f32_ceil: opcodes[pc->opcode] = Op_ceil_32; break; - case WasmOp_f32_floor: opcodes[pc->opcode] = Op_floor_32; break; - case WasmOp_f32_trunc: opcodes[pc->opcode] = Op_trunc_32; break; - case WasmOp_f32_nearest: opcodes[pc->opcode] = Op_nearest_32; break; - case WasmOp_f32_sqrt: opcodes[pc->opcode] = Op_sqrt_32; break; - case WasmOp_f32_add: opcodes[pc->opcode] = Op_fadd_32; break; - case WasmOp_f32_sub: opcodes[pc->opcode] = Op_fsub_32; break; - case WasmOp_f32_mul: opcodes[pc->opcode] = Op_fmul_32; break; - case WasmOp_f32_div: opcodes[pc->opcode] = Op_fdiv_32; break; - case WasmOp_f32_min: opcodes[pc->opcode] = Op_fmin_32; break; - case WasmOp_f32_max: opcodes[pc->opcode] = Op_fmax_32; break; - case WasmOp_f32_copysign: opcodes[pc->opcode] = Op_copysign_32; break; - case WasmOp_f64_abs: opcodes[pc->opcode] = Op_fabs_64; break; - case WasmOp_f64_neg: opcodes[pc->opcode] = Op_fneg_64; break; - case WasmOp_f64_ceil: opcodes[pc->opcode] = Op_ceil_64; break; - case WasmOp_f64_floor: opcodes[pc->opcode] = Op_floor_64; break; - case WasmOp_f64_trunc: opcodes[pc->opcode] = Op_trunc_64; break; - case WasmOp_f64_nearest: opcodes[pc->opcode] = Op_nearest_64; break; - case WasmOp_f64_sqrt: opcodes[pc->opcode] = Op_sqrt_64; break; - case WasmOp_f64_add: opcodes[pc->opcode] = Op_fadd_64; break; - case WasmOp_f64_sub: opcodes[pc->opcode] = Op_fsub_64; break; - case WasmOp_f64_mul: opcodes[pc->opcode] = Op_fmul_64; break; - case WasmOp_f64_div: opcodes[pc->opcode] = Op_fdiv_64; break; - case WasmOp_f64_min: opcodes[pc->opcode] = Op_fmin_64; break; - case WasmOp_f64_max: opcodes[pc->opcode] = Op_fmax_64; break; - case WasmOp_f64_copysign: opcodes[pc->opcode] = Op_copysign_64; break; - case WasmOp_i32_wrap_i64: opcodes[pc->opcode] = Op_wrap_32_64; break; - case WasmOp_i32_trunc_f32_s: opcodes[pc->opcode] = Op_ftos_32_32; break; - case WasmOp_i32_trunc_f32_u: opcodes[pc->opcode] = Op_ftou_32_32; break; - case WasmOp_i32_trunc_f64_s: opcodes[pc->opcode] = Op_ftos_32_64; break; - case WasmOp_i32_trunc_f64_u: opcodes[pc->opcode] = Op_ftou_32_64; break; - case WasmOp_i64_extend_i32_s: opcodes[pc->opcode] = Op_sext_64_32; break; - case WasmOp_i64_extend_i32_u: opcodes[pc->opcode] = Op_zext_64_32; break; - case WasmOp_i64_trunc_f32_s: opcodes[pc->opcode] = Op_ftos_64_32; break; - case WasmOp_i64_trunc_f32_u: opcodes[pc->opcode] = Op_ftou_64_32; break; - case WasmOp_i64_trunc_f64_s: opcodes[pc->opcode] = Op_ftos_64_64; break; - case WasmOp_i64_trunc_f64_u: opcodes[pc->opcode] = Op_ftou_64_64; break; - case WasmOp_f32_convert_i32_s: opcodes[pc->opcode] = Op_stof_32_32; break; - case WasmOp_f32_convert_i32_u: opcodes[pc->opcode] = Op_utof_32_32; break; - case WasmOp_f32_convert_i64_s: opcodes[pc->opcode] = Op_stof_32_64; break; - case WasmOp_f32_convert_i64_u: opcodes[pc->opcode] = Op_utof_32_64; break; - case WasmOp_f32_demote_f64: opcodes[pc->opcode] = Op_ftof_32_64; break; - case WasmOp_f64_convert_i32_s: opcodes[pc->opcode] = Op_stof_64_32; break; - case WasmOp_f64_convert_i32_u: opcodes[pc->opcode] = Op_utof_64_32; break; - case WasmOp_f64_convert_i64_s: opcodes[pc->opcode] = Op_stof_64_64; break; - case WasmOp_f64_convert_i64_u: opcodes[pc->opcode] = Op_utof_64_64; break; - case WasmOp_f64_promote_f32: opcodes[pc->opcode] = Op_ftof_64_32; break; - case WasmOp_i32_extend8_s: opcodes[pc->opcode] = Op_sext8_32; break; - case WasmOp_i32_extend16_s: opcodes[pc->opcode] = Op_sext16_32; break; - case WasmOp_i64_extend8_s: opcodes[pc->opcode] = Op_sext8_64; break; - case WasmOp_i64_extend16_s: opcodes[pc->opcode] = Op_sext16_64; break; - case WasmOp_i64_extend32_s: opcodes[pc->opcode] = Op_sext32_64; break; - default: panic("unexpected opcode"); - } - pc->opcode += 1; - } - break; - - case WasmOp_prefixed: - switch (prefixed_opcode) { - case WasmPrefixedOp_memory_copy: - if (mod_ptr[*code_i + 0] != 0 || mod_ptr[*code_i + 1] != 0) - panic("unexpected memory index"); - *code_i += 2; - if (unreachable_depth == 0) { - opcodes[pc->opcode] = Op_memcpy; - pc->opcode += 1; - } - break; - - case WasmPrefixedOp_memory_fill: - if (mod_ptr[*code_i] != 0) panic("unexpected memory index"); - *code_i += 1; - if (unreachable_depth == 0) { - opcodes[pc->opcode] = Op_memset; - pc->opcode += 1; - } - break; - - default: panic("unexpected opcode"); - } - break; - } - switch (opcode) { - default: state = State_default; break; - case WasmOp_i32_eqz: state = State_bool_not; break; - } - - //for (uint32_t i = old_pc.opcode; i < pc->opcode; i += 1) { - // fprintf(stderr, "decoded opcode[%u] = %u\n", i, opcodes[i]); - //} - //for (uint32_t i = old_pc.operand; i < pc->operand; i += 1) { - // fprintf(stderr, "decoded operand[%u] = %u\n", i, operands[i]); - //} - } -} - -static void vm_push_u32(struct VirtualMachine *vm, uint32_t value) { - vm->stack[vm->stack_top + 0] = value; - vm->stack_top += 1; -} - -static void vm_push_i32(struct VirtualMachine *vm, int32_t value) { - vm_push_u32(vm, (uint32_t)value); -} - -static void vm_push_u64(struct VirtualMachine *vm, uint64_t value) { - vm->stack[vm->stack_top + 0] = (uint32_t)(value >> 0); - vm->stack[vm->stack_top + 1] = (uint32_t)(value >> 32); - vm->stack_top += 2; -} - -static void vm_push_i64(struct VirtualMachine *vm, int64_t value) { - vm_push_u64(vm, (uint64_t)value); -} - -static void vm_push_f32(struct VirtualMachine *vm, float value) { - uint32_t integer; - memcpy(&integer, &value, sizeof(integer)); - vm_push_u32(vm, integer); -} - -static void vm_push_f64(struct VirtualMachine *vm, double value) { - uint64_t integer; - memcpy(&integer, &value, sizeof(integer)); - vm_push_u64(vm, integer); -} - -static uint32_t vm_pop_u32(struct VirtualMachine *vm) { - vm->stack_top -= 1; - return vm->stack[vm->stack_top + 0]; -} - -static int32_t vm_pop_i32(struct VirtualMachine *vm) { - return (int32_t)vm_pop_u32(vm); -} - -static uint64_t vm_pop_u64(struct VirtualMachine *vm) { - vm->stack_top -= 2; - return vm->stack[vm->stack_top + 0] | (uint64_t)vm->stack[vm->stack_top + 1] << 32; -} - -static int64_t vm_pop_i64(struct VirtualMachine *vm) { - return (int64_t)vm_pop_u64(vm); -} - -static float vm_pop_f32(struct VirtualMachine *vm) { - uint32_t integer = vm_pop_u32(vm); - float result; - memcpy(&result, &integer, sizeof(result)); - return result; -} - -static double vm_pop_f64(struct VirtualMachine *vm) { - uint64_t integer = vm_pop_u64(vm); - double result; - memcpy(&result, &integer, sizeof(result)); - return result; -} - -static void vm_callImport(struct VirtualMachine *vm, const struct Import *import) { - switch (import->mod) { - case ImpMod_wasi_snapshot_preview1: switch (import->name) { - case ImpName_fd_prestat_get: - { - uint32_t buf = vm_pop_u32(vm); - int32_t fd = vm_pop_i32(vm); - vm_push_u32(vm, wasi_fd_prestat_get(vm, fd, buf)); - } - break; - case ImpName_fd_prestat_dir_name: - { - uint32_t path_len = vm_pop_u32(vm); - uint32_t path = vm_pop_u32(vm); - int32_t fd = vm_pop_i32(vm); - vm_push_u32(vm, wasi_fd_prestat_dir_name(vm, fd, path, path_len)); - } - break; - case ImpName_fd_close: - { - int32_t fd = vm_pop_i32(vm); - vm_push_u32(vm, wasi_fd_close(vm, fd)); - } - break; - case ImpName_fd_read: - { - uint32_t nread = vm_pop_u32(vm); - uint32_t iovs_len = vm_pop_u32(vm); - uint32_t iovs = vm_pop_u32(vm); - int32_t fd = vm_pop_i32(vm); - vm_push_u32(vm, wasi_fd_read(vm, fd, iovs, iovs_len, nread)); - } - break; - case ImpName_fd_filestat_get: - { - uint32_t buf = vm_pop_u32(vm); - int32_t fd = vm_pop_i32(vm); - vm_push_u32(vm, wasi_fd_filestat_get(vm, fd, buf)); - } - break; - case ImpName_fd_filestat_set_size: - { - uint64_t size = vm_pop_u64(vm); - int32_t fd = vm_pop_i32(vm); - vm_push_u32(vm, wasi_fd_filestat_set_size(vm, fd, size)); - } - break; - case ImpName_fd_filestat_set_times: - { - panic("unexpected call to fd_filestat_set_times"); - } - break; - case ImpName_fd_fdstat_get: - { - uint32_t buf = vm_pop_u32(vm); - int32_t fd = vm_pop_i32(vm); - vm_push_u32(vm, wasi_fd_fdstat_get(vm, fd, buf)); - } - break; - case ImpName_fd_readdir: - { - panic("unexpected call to fd_readdir"); - } - break; - case ImpName_fd_write: - { - uint32_t nwritten = vm_pop_u32(vm); - uint32_t iovs_len = vm_pop_u32(vm); - uint32_t iovs = vm_pop_u32(vm); - int32_t fd = vm_pop_i32(vm); - vm_push_u32(vm, wasi_fd_write(vm, fd, iovs, iovs_len, nwritten)); - } - break; - case ImpName_fd_pwrite: - { - uint32_t nwritten = vm_pop_u32(vm); - uint64_t offset = vm_pop_u64(vm); - uint32_t iovs_len = vm_pop_u32(vm); - uint32_t iovs = vm_pop_u32(vm); - int32_t fd = vm_pop_i32(vm); - vm_push_u32(vm, wasi_fd_pwrite(vm, fd, iovs, iovs_len, offset, nwritten)); - } - break; - case ImpName_proc_exit: - { - uint32_t code = vm_pop_u32(vm); - exit(code); - } - break; - case ImpName_args_sizes_get: - { - uint32_t argv_buf_size = vm_pop_u32(vm); - uint32_t argc = vm_pop_u32(vm); - vm_push_u32(vm, wasi_args_sizes_get(vm, argc, argv_buf_size)); - } - break; - case ImpName_args_get: - { - uint32_t argv_buf = vm_pop_u32(vm); - uint32_t argv = vm_pop_u32(vm); - vm_push_u32(vm, wasi_args_get(vm, argv, argv_buf)); - } - break; - case ImpName_random_get: - { - uint32_t buf_len = vm_pop_u32(vm); - uint32_t buf = vm_pop_u32(vm); - vm_push_u32(vm, wasi_random_get(vm, buf, buf_len)); - } - break; - case ImpName_environ_sizes_get: - { - panic("unexpected call to environ_sizes_get"); - } - break; - case ImpName_environ_get: - { - panic("unexpected call to environ_get"); - } - break; - case ImpName_path_filestat_get: - { - uint32_t buf = vm_pop_u32(vm); - uint32_t path_len = vm_pop_u32(vm); - uint32_t path = vm_pop_u32(vm); - uint32_t flags = vm_pop_u32(vm); - int32_t fd = vm_pop_i32(vm); - vm_push_u32(vm, wasi_path_filestat_get(vm, fd, flags, path, path_len, buf)); - } - break; - case ImpName_path_create_directory: - { - uint32_t path_len = vm_pop_u32(vm); - uint32_t path = vm_pop_u32(vm); - int32_t fd = vm_pop_i32(vm); - vm_push_u32(vm, wasi_path_create_directory(vm, fd, path, path_len)); - } - break; - case ImpName_path_rename: - { - uint32_t new_path_len = vm_pop_u32(vm); - uint32_t new_path = vm_pop_u32(vm); - int32_t new_fd = vm_pop_i32(vm); - uint32_t old_path_len = vm_pop_u32(vm); - uint32_t old_path = vm_pop_u32(vm); - int32_t old_fd = vm_pop_i32(vm); - vm_push_u32(vm, wasi_path_rename( - vm, - old_fd, - old_path, - old_path_len, - new_fd, - new_path, - new_path_len - )); - } - break; - case ImpName_path_open: - { - uint32_t fd = vm_pop_u32(vm); - uint32_t fs_flags = vm_pop_u32(vm); - uint64_t fs_rights_inheriting = vm_pop_u64(vm); - uint64_t fs_rights_base = vm_pop_u64(vm); - uint32_t oflags = vm_pop_u32(vm); - uint32_t path_len = vm_pop_u32(vm); - uint32_t path = vm_pop_u32(vm); - uint32_t dirflags = vm_pop_u32(vm); - int32_t dirfd = vm_pop_i32(vm); - vm_push_u32(vm, wasi_path_open( - vm, - dirfd, - dirflags, - path, - path_len, - oflags, - fs_rights_base, - fs_rights_inheriting, - fs_flags, - fd - )); - } - break; - case ImpName_path_remove_directory: - { - panic("unexpected call to path_remove_directory"); - } - break; - case ImpName_path_unlink_file: - { - panic("unexpected call to path_unlink_file"); - } - break; - case ImpName_clock_time_get: - { - uint32_t timestamp = vm_pop_u32(vm); - uint64_t precision = vm_pop_u64(vm); - uint32_t clock_id = vm_pop_u32(vm); - vm_push_u32(vm, wasi_clock_time_get(vm, clock_id, precision, timestamp)); - } - break; - case ImpName_fd_pread: - { - panic("unexpected call to fd_pread"); - } - break; - case ImpName_debug: - { - uint64_t number = vm_pop_u64(vm); - uint32_t text = vm_pop_u32(vm); - wasi_debug(vm, text, number); - } - break; - case ImpName_debug_slice: - { - uint32_t len = vm_pop_u32(vm); - uint32_t ptr = vm_pop_u32(vm); - wasi_debug_slice(vm, ptr, len); - } - break; - } - break; - } -} - -static void vm_call(struct VirtualMachine *vm, const struct Function *func) { - //struct TypeInfo *type_info = &vm->types[func->type_idx]; - //fprintf(stderr, "enter fn_id: %u, param_count: %u, result_count: %u, locals_size: %u\n", - // func->id, type_info->param_count, type_info->result_count, func->locals_size); - - // Push zeroed locals to stack - memset(&vm->stack[vm->stack_top], 0, func->locals_size * sizeof(uint32_t)); - vm->stack_top += func->locals_size; - - vm_push_u32(vm, vm->pc.opcode); - vm_push_u32(vm, vm->pc.operand); - - vm->pc = func->entry_pc; -} - -static void vm_br_void(struct VirtualMachine *vm) { - uint32_t stack_adjust = vm->operands[vm->pc.operand]; - - vm->stack_top -= stack_adjust; - - vm->pc.opcode = vm->operands[vm->pc.operand + 1]; - vm->pc.operand = vm->operands[vm->pc.operand + 2]; -} - -static void vm_br_u32(struct VirtualMachine *vm) { - uint32_t stack_adjust = vm->operands[vm->pc.operand]; - - uint32_t result = vm_pop_u32(vm); - vm->stack_top -= stack_adjust; - vm_push_u32(vm, result); - - vm->pc.opcode = vm->operands[vm->pc.operand + 1]; - vm->pc.operand = vm->operands[vm->pc.operand + 2]; -} - -static void vm_br_u64(struct VirtualMachine *vm) { - uint32_t stack_adjust = vm->operands[vm->pc.operand]; - - uint64_t result = vm_pop_u64(vm); - vm->stack_top -= stack_adjust; - vm_push_u64(vm, result); - - vm->pc.opcode = vm->operands[vm->pc.operand + 1]; - vm->pc.operand = vm->operands[vm->pc.operand + 2]; -} - -static void vm_return_void(struct VirtualMachine *vm) { - uint32_t stack_adjust = vm->operands[vm->pc.operand + 0]; - uint32_t frame_size = vm->operands[vm->pc.operand + 1]; - - vm->stack_top -= stack_adjust; - vm->pc.operand = vm_pop_u32(vm); - vm->pc.opcode = vm_pop_u32(vm); - - vm->stack_top -= frame_size; -} - -static void vm_return_u32(struct VirtualMachine *vm) { - uint32_t stack_adjust = vm->operands[vm->pc.operand + 0]; - uint32_t frame_size = vm->operands[vm->pc.operand + 1]; - - uint32_t result = vm_pop_u32(vm); - - vm->stack_top -= stack_adjust; - vm->pc.operand = vm_pop_u32(vm); - vm->pc.opcode = vm_pop_u32(vm); - - vm->stack_top -= frame_size; - vm_push_u32(vm, result); -} - -static void vm_return_u64(struct VirtualMachine *vm) { - uint32_t stack_adjust = vm->operands[vm->pc.operand + 0]; - uint32_t frame_size = vm->operands[vm->pc.operand + 1]; - - uint64_t result = vm_pop_u64(vm); - - vm->stack_top -= stack_adjust; - vm->pc.operand = vm_pop_u32(vm); - vm->pc.opcode = vm_pop_u32(vm); - - vm->stack_top -= frame_size; - vm_push_u64(vm, result); -} - -static void vm_run(struct VirtualMachine *vm) { - uint8_t *opcodes = vm->opcodes; - uint32_t *operands = vm->operands; - struct ProgramCounter *pc = &vm->pc; - uint32_t global_0 = vm->globals[0]; - for (;;) { - enum Op op = opcodes[pc->opcode]; - //fprintf(stderr, "stack[%u:%u]=%x:%x pc=%x:%x op=%u\n", - // vm->stack_top - 2, vm->stack_top - 1, - // vm->stack[vm->stack_top - 2], vm->stack[vm->stack_top - 1], - // pc->opcode, pc->operand, op); - pc->opcode += 1; - switch (op) { - case Op_unreachable: - panic("unreachable reached"); - case Op_br_void: - vm_br_void(vm); - break; - case Op_br_32: - vm_br_u32(vm); - break; - case Op_br_64: - vm_br_u64(vm); - break; - case Op_br_nez_void: - if (vm_pop_u32(vm) != 0) { - vm_br_void(vm); - } else { - pc->operand += 3; - } - break; - case Op_br_nez_32: - if (vm_pop_u32(vm) != 0) { - vm_br_u32(vm); - } else { - pc->operand += 3; - } - break; - case Op_br_nez_64: - if (vm_pop_u32(vm) != 0) { - vm_br_u64(vm); - } else { - pc->operand += 3; - } - break; - case Op_br_eqz_void: - if (vm_pop_u32(vm) == 0) { - vm_br_void(vm); - } else { - pc->operand += 3; - } - break; - case Op_br_eqz_32: - if (vm_pop_u32(vm) == 0) { - vm_br_u32(vm); - } else { - pc->operand += 3; - } - break; - case Op_br_eqz_64: - if (vm_pop_u32(vm) == 0) { - vm_br_u64(vm); - } else { - pc->operand += 3; - } - break; - case Op_br_table_void: - { - uint32_t index = min_u32(vm_pop_u32(vm), operands[pc->operand]); - pc->operand += 1 + index * 3; - vm_br_void(vm); - } - break; - case Op_br_table_32: - { - uint32_t index = min_u32(vm_pop_u32(vm), operands[pc->operand]); - pc->operand += 1 + index * 3; - vm_br_u32(vm); - } - break; - case Op_br_table_64: - { - uint32_t index = min_u32(vm_pop_u32(vm), operands[pc->operand]); - pc->operand += 1 + index * 3; - vm_br_u64(vm); - } - break; - case Op_return_void: - vm_return_void(vm); - break; - case Op_return_32: - vm_return_u32(vm); - break; - case Op_return_64: - vm_return_u64(vm); - break; - case Op_call_import: - { - uint8_t import_idx = opcodes[pc->opcode]; - pc->opcode += 1; - vm_callImport(vm, &vm->imports[import_idx]); - } - break; - case Op_call_func: - { - uint32_t func_idx = operands[pc->operand]; - pc->operand += 1; - vm_call(vm, &vm->functions[func_idx]); - } - break; - case Op_call_indirect: - { - uint32_t fn_id = vm->table[vm_pop_u32(vm)]; - if (fn_id < vm->imports_len) - vm_callImport(vm, &vm->imports[fn_id]); - else - vm_call(vm, &vm->functions[fn_id - vm->imports_len]); - } - break; - - case Op_drop_32: - vm->stack_top -= 1; - break; - case Op_drop_64: - vm->stack_top -= 2; - break; - case Op_select_32: - { - uint32_t c = vm_pop_u32(vm); - uint32_t b = vm_pop_u32(vm); - uint32_t a = vm_pop_u32(vm); - uint32_t result = (c != 0) ? a : b; - vm_push_u32(vm, result); - } - break; - case Op_select_64: - { - uint32_t c = vm_pop_u32(vm); - uint64_t b = vm_pop_u64(vm); - uint64_t a = vm_pop_u64(vm); - uint64_t result = (c != 0) ? a : b; - vm_push_u64(vm, result); - } - break; - - case Op_local_get_32: - { - uint32_t *local = &vm->stack[vm->stack_top - operands[pc->operand]]; - pc->operand += 1; - vm_push_u32(vm, *local); - } - break; - case Op_local_get_64: - { - uint32_t *local = &vm->stack[vm->stack_top - operands[pc->operand]]; - pc->operand += 1; - vm_push_u64(vm, local[0] | (uint64_t)local[1] << 32); - } - break; - case Op_local_set_32: - { - uint32_t *local = &vm->stack[vm->stack_top - operands[pc->operand]]; - pc->operand += 1; - *local = vm_pop_u32(vm); - } - break; - case Op_local_set_64: - { - uint32_t *local = &vm->stack[vm->stack_top - operands[pc->operand]]; - pc->operand += 1; - uint64_t value = vm_pop_u64(vm); - local[0] = (uint32_t)(value >> 0); - local[1] = (uint32_t)(value >> 32); - } - break; - case Op_local_tee_32: - { - uint32_t *local = &vm->stack[vm->stack_top - operands[pc->operand]]; - pc->operand += 1; - *local = vm->stack[vm->stack_top - 1]; - } - break; - case Op_local_tee_64: - { - uint32_t *local = &vm->stack[vm->stack_top - operands[pc->operand]]; - pc->operand += 1; - local[0] = vm->stack[vm->stack_top - 2]; - local[1] = vm->stack[vm->stack_top - 1]; - } - break; - - case Op_global_get_0_32: - vm_push_u32(vm, global_0); - break; - case Op_global_get_32: - { - uint32_t idx = operands[pc->operand]; - pc->operand += 1; - vm_push_u32(vm, vm->globals[idx]); - } - break; - case Op_global_set_0_32: - global_0 = vm_pop_u32(vm); - break; - case Op_global_set_32: - { - uint32_t idx = operands[pc->operand]; - pc->operand += 1; - vm->globals[idx] = vm_pop_u32(vm); - } - break; - - case Op_load_0_8: - { - uint32_t address = vm_pop_u32(vm); - vm_push_u32(vm, (uint8_t)vm->memory[address]); - } - break; - case Op_load_8: - { - uint32_t address = vm_pop_u32(vm) + operands[pc->operand]; - pc->operand += 1; - vm_push_u32(vm, (uint8_t)vm->memory[address]); - } - break; - case Op_load_0_16: - { - uint32_t address = vm_pop_u32(vm); - vm_push_u32(vm, read_u16_le(&vm->memory[address])); - } - break; - case Op_load_16: - { - uint32_t address = vm_pop_u32(vm) + operands[pc->operand]; - pc->operand += 1; - vm_push_u32(vm, read_u16_le(&vm->memory[address])); - } - break; - case Op_load_0_32: - { - uint32_t address = vm_pop_u32(vm); - vm_push_u32(vm, read_u32_le(&vm->memory[address])); - } - break; - case Op_load_32: - { - uint32_t address = vm_pop_u32(vm) + operands[pc->operand]; - pc->operand += 1; - vm_push_u32(vm, read_u32_le(&vm->memory[address])); - } - break; - case Op_load_0_64: - { - uint32_t address = vm_pop_u32(vm); - vm_push_u64(vm, read_u64_le(&vm->memory[address])); - } - break; - case Op_load_64: - { - uint32_t address = vm_pop_u32(vm) + operands[pc->operand]; - pc->operand += 1; - vm_push_u64(vm, read_u64_le(&vm->memory[address])); - } - break; - case Op_store_0_8: - { - uint8_t value = (uint8_t)vm_pop_u32(vm); - uint32_t address = vm_pop_u32(vm); - vm->memory[address] = value; - } - break; - case Op_store_8: - { - uint8_t value = (uint8_t)vm_pop_u32(vm); - uint32_t address = vm_pop_u32(vm) + operands[pc->operand]; - pc->operand += 1; - vm->memory[address] = value; - } - break; - case Op_store_0_16: - { - uint16_t value = (uint16_t)vm_pop_u32(vm); - uint32_t address = vm_pop_u32(vm); - write_u16_le(&vm->memory[address], value); - } - break; - case Op_store_16: - { - uint16_t value = (uint16_t)vm_pop_u32(vm); - uint32_t address = vm_pop_u32(vm) + operands[pc->operand]; - pc->operand += 1; - write_u16_le(&vm->memory[address], value); - } - break; - case Op_store_0_32: - { - uint32_t value = vm_pop_u32(vm); - uint32_t address = vm_pop_u32(vm); - write_u32_le(&vm->memory[address], value); - } - break; - case Op_store_32: - { - uint32_t value = vm_pop_u32(vm); - uint32_t address = vm_pop_u32(vm) + operands[pc->operand]; - pc->operand += 1; - write_u32_le(&vm->memory[address], value); - } - break; - case Op_store_0_64: - { - uint64_t value = vm_pop_u64(vm); - uint32_t address = vm_pop_u32(vm); - write_u64_le(&vm->memory[address], value); - } - break; - case Op_store_64: - { - uint64_t value = vm_pop_u64(vm); - uint32_t address = vm_pop_u32(vm) + operands[pc->operand]; - pc->operand += 1; - write_u64_le(&vm->memory[address], value); - } - break; - case Op_mem_size: - vm_push_u32(vm, vm->memory_len / wasm_page_size); - break; - case Op_mem_grow: - { - uint32_t page_count = vm_pop_u32(vm); - uint32_t old_page_count = vm->memory_len / wasm_page_size; - uint32_t new_len = vm->memory_len + page_count * wasm_page_size; - if (new_len > max_memory) { - vm_push_i32(vm, -1); - } else { - vm->memory_len = new_len; - vm_push_u32(vm, old_page_count); - } - } - break; - - case Op_const_0_32: - vm_push_i32(vm, 0); - break; - case Op_const_0_64: - vm_push_i64(vm, 0); - break; - case Op_const_1_32: - vm_push_i32(vm, 1); - break; - case Op_const_1_64: - vm_push_i64(vm, 1); - break; - case Op_const_32: - { - uint32_t value = operands[pc->operand]; - pc->operand += 1; - vm_push_i32(vm, value); - } - break; - case Op_const_64: - { - uint64_t value = ((uint64_t)operands[pc->operand]) | - (((uint64_t)operands[pc->operand + 1]) << 32); - pc->operand += 2; - vm_push_i64(vm, value); - } - break; - case Op_const_umax_32: - vm_push_i32(vm, -1); - break; - case Op_const_umax_64: - vm_push_i64(vm, -1); - break; - - case Op_eqz_32: - { - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, lhs == 0); - } - break; - case Op_eq_32: - { - uint32_t rhs = vm_pop_u32(vm); - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, lhs == rhs); - } - break; - case Op_ne_32: - { - uint32_t rhs = vm_pop_u32(vm); - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, lhs != rhs); - } - break; - case Op_slt_32: - { - int32_t rhs = vm_pop_i32(vm); - int32_t lhs = vm_pop_i32(vm); - vm_push_u32(vm, lhs < rhs); - } - break; - case Op_ult_32: - { - uint32_t rhs = vm_pop_u32(vm); - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, lhs < rhs); - } - break; - case Op_sgt_32: - { - int32_t rhs = vm_pop_i32(vm); - int32_t lhs = vm_pop_i32(vm); - vm_push_u32(vm, lhs > rhs); - } - break; - case Op_ugt_32: - { - uint32_t rhs = vm_pop_u32(vm); - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, lhs > rhs); - } - break; - case Op_sle_32: - { - int32_t rhs = vm_pop_i32(vm); - int32_t lhs = vm_pop_i32(vm); - vm_push_u32(vm, lhs <= rhs); - } - break; - case Op_ule_32: - { - uint32_t rhs = vm_pop_u32(vm); - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, lhs <= rhs); - } - break; - case Op_sge_32: - { - int32_t rhs = vm_pop_i32(vm); - int32_t lhs = vm_pop_i32(vm); - vm_push_u32(vm, lhs >= rhs); - } - break; - case Op_uge_32: - { - uint32_t rhs = vm_pop_u32(vm); - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, lhs >= rhs); - } - break; - - case Op_eqz_64: - { - uint64_t lhs = vm_pop_u64(vm); - vm_push_u32(vm, lhs == 0); - } - break; - case Op_eq_64: - { - uint64_t rhs = vm_pop_u64(vm); - uint64_t lhs = vm_pop_u64(vm); - vm_push_u32(vm, lhs == rhs); - } - break; - case Op_ne_64: - { - uint64_t rhs = vm_pop_u64(vm); - uint64_t lhs = vm_pop_u64(vm); - vm_push_u32(vm, lhs != rhs); - } - break; - case Op_slt_64: - { - int64_t rhs = vm_pop_i64(vm); - int64_t lhs = vm_pop_i64(vm); - vm_push_u32(vm, lhs < rhs); - } - break; - case Op_ult_64: - { - uint64_t rhs = vm_pop_u64(vm); - uint64_t lhs = vm_pop_u64(vm); - vm_push_u32(vm, lhs < rhs); - } - break; - case Op_sgt_64: - { - int64_t rhs = vm_pop_i64(vm); - int64_t lhs = vm_pop_i64(vm); - vm_push_u32(vm, lhs > rhs); - } - break; - case Op_ugt_64: - { - uint64_t rhs = vm_pop_u64(vm); - uint64_t lhs = vm_pop_u64(vm); - vm_push_u32(vm, lhs > rhs); - } - break; - case Op_sle_64: - { - int64_t rhs = vm_pop_i64(vm); - int64_t lhs = vm_pop_i64(vm); - vm_push_u32(vm, lhs <= rhs); - } - break; - case Op_ule_64: - { - uint64_t rhs = vm_pop_u64(vm); - uint64_t lhs = vm_pop_u64(vm); - vm_push_u32(vm, lhs <= rhs); - } - break; - case Op_sge_64: - { - int64_t rhs = vm_pop_i64(vm); - int64_t lhs = vm_pop_i64(vm); - vm_push_u32(vm, lhs >= rhs); - } - break; - case Op_uge_64: - { - uint64_t rhs = vm_pop_u64(vm); - uint64_t lhs = vm_pop_u64(vm); - vm_push_u32(vm, lhs >= rhs); - } - break; - - case Op_feq_32: - { - float rhs = vm_pop_f32(vm); - float lhs = vm_pop_f32(vm); - vm_push_u32(vm, lhs == rhs); - } - break; - case Op_fne_32: - { - float rhs = vm_pop_f32(vm); - float lhs = vm_pop_f32(vm); - vm_push_u32(vm, lhs != rhs); - } - break; - case Op_flt_32: - { - float rhs = vm_pop_f32(vm); - float lhs = vm_pop_f32(vm); - vm_push_u32(vm, lhs < rhs); - } - break; - case Op_fgt_32: - { - float rhs = vm_pop_f32(vm); - float lhs = vm_pop_f32(vm); - vm_push_u32(vm, lhs > rhs); - } - break; - case Op_fle_32: - { - float rhs = vm_pop_f32(vm); - float lhs = vm_pop_f32(vm); - vm_push_u32(vm, lhs <= rhs); - } - break; - case Op_fge_32: - { - float rhs = vm_pop_f32(vm); - float lhs = vm_pop_f32(vm); - vm_push_u32(vm, lhs >= rhs); - } - break; - - case Op_feq_64: - { - double rhs = vm_pop_f64(vm); - double lhs = vm_pop_f64(vm); - vm_push_u32(vm, lhs == rhs); - } - break; - case Op_fne_64: - { - double rhs = vm_pop_f64(vm); - double lhs = vm_pop_f64(vm); - vm_push_u32(vm, lhs != rhs); - } - break; - case Op_flt_64: - { - double rhs = vm_pop_f64(vm); - double lhs = vm_pop_f64(vm); - vm_push_u32(vm, lhs <= rhs); - } - break; - case Op_fgt_64: - { - double rhs = vm_pop_f64(vm); - double lhs = vm_pop_f64(vm); - vm_push_u32(vm, lhs > rhs); - } - break; - case Op_fle_64: - { - double rhs = vm_pop_f64(vm); - double lhs = vm_pop_f64(vm); - vm_push_u32(vm, lhs <= rhs); - } - break; - case Op_fge_64: - { - double rhs = vm_pop_f64(vm); - double lhs = vm_pop_f64(vm); - vm_push_u32(vm, lhs >= rhs); - } - break; - - case Op_clz_32: - { - uint32_t operand = vm_pop_u32(vm); - uint32_t result = (operand == 0) ? 32 : __builtin_clz(operand); - vm_push_u32(vm, result); - } - break; - case Op_ctz_32: - { - uint32_t operand = vm_pop_u32(vm); - uint32_t result = (operand == 0) ? 32 : __builtin_ctz(operand); - vm_push_u32(vm, result); - } - break; - case Op_popcnt_32: - { - uint32_t operand = vm_pop_u32(vm); - uint32_t result = __builtin_popcount(operand); - vm_push_u32(vm, result); - } - break; - case Op_add_32: - { - uint32_t rhs = vm_pop_u32(vm); - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, lhs + rhs); - } - break; - case Op_sub_32: - { - uint32_t rhs = vm_pop_u32(vm); - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, lhs - rhs); - } - break; - case Op_mul_32: - { - uint32_t rhs = vm_pop_u32(vm); - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, lhs * rhs); - } - break; - case Op_sdiv_32: - { - int32_t rhs = vm_pop_i32(vm); - int32_t lhs = vm_pop_i32(vm); - vm_push_i32(vm, lhs / rhs); - } - break; - case Op_udiv_32: - { - uint32_t rhs = vm_pop_u32(vm); - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, lhs / rhs); - } - break; - case Op_srem_32: - { - int32_t rhs = vm_pop_i32(vm); - int32_t lhs = vm_pop_i32(vm); - vm_push_i32(vm, lhs % rhs); - } - break; - case Op_urem_32: - { - uint32_t rhs = vm_pop_u32(vm); - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, lhs % rhs); - } - break; - case Op_and_32: - { - uint32_t rhs = vm_pop_u32(vm); - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, lhs & rhs); - } - break; - case Op_or_32: - { - uint32_t rhs = vm_pop_u32(vm); - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, lhs | rhs); - } - break; - case Op_xor_32: - { - uint32_t rhs = vm_pop_u32(vm); - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, lhs ^ rhs); - } - break; - case Op_shl_32: - { - uint32_t rhs = vm_pop_u32(vm); - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, lhs << (rhs & 0x1f)); - } - break; - case Op_ashr_32: - { - uint32_t rhs = vm_pop_u32(vm); - int32_t lhs = vm_pop_i32(vm); - vm_push_i32(vm, lhs >> (rhs & 0x1f)); - } - break; - case Op_lshr_32: - { - uint32_t rhs = vm_pop_u32(vm); - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, lhs >> (rhs & 0x1f)); - } - break; - case Op_rol_32: - { - uint32_t rhs = vm_pop_u32(vm); - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, rotl32(lhs, rhs)); - } - break; - case Op_ror_32: - { - uint32_t rhs = vm_pop_u32(vm); - uint32_t lhs = vm_pop_u32(vm); - vm_push_u32(vm, rotr32(lhs, rhs)); - } - break; - - case Op_clz_64: - { - uint64_t operand = vm_pop_u64(vm); - uint64_t result = (operand == 0) ? 64 : __builtin_clzll(operand); - vm_push_u64(vm, result); - } - break; - case Op_ctz_64: - { - uint64_t operand = vm_pop_u64(vm); - uint64_t result = (operand == 0) ? 64 : __builtin_ctzll(operand); - vm_push_u64(vm, result); - } - break; - case Op_popcnt_64: - { - uint64_t operand = vm_pop_u64(vm); - uint64_t result = __builtin_popcountll(operand); - vm_push_u64(vm, result); - } - break; - case Op_add_64: - { - uint64_t rhs = vm_pop_u64(vm); - uint64_t lhs = vm_pop_u64(vm); - vm_push_u64(vm, lhs + rhs); - } - break; - case Op_sub_64: - { - uint64_t rhs = vm_pop_u64(vm); - uint64_t lhs = vm_pop_u64(vm); - vm_push_u64(vm, lhs - rhs); - } - break; - case Op_mul_64: - { - uint64_t rhs = vm_pop_u64(vm); - uint64_t lhs = vm_pop_u64(vm); - vm_push_u64(vm, lhs * rhs); - } - break; - case Op_sdiv_64: - { - int64_t rhs = vm_pop_i64(vm); - int64_t lhs = vm_pop_i64(vm); - vm_push_i64(vm, lhs / rhs); - } - break; - case Op_udiv_64: - { - uint64_t rhs = vm_pop_u64(vm); - uint64_t lhs = vm_pop_u64(vm); - vm_push_u64(vm, lhs / rhs); - } - break; - case Op_srem_64: - { - int64_t rhs = vm_pop_i64(vm); - int64_t lhs = vm_pop_i64(vm); - vm_push_i64(vm, lhs % rhs); - } - break; - case Op_urem_64: - { - uint64_t rhs = vm_pop_u64(vm); - uint64_t lhs = vm_pop_u64(vm); - vm_push_u64(vm, lhs % rhs); - } - break; - case Op_and_64: - { - uint64_t rhs = vm_pop_u64(vm); - uint64_t lhs = vm_pop_u64(vm); - vm_push_u64(vm, lhs & rhs); - } - break; - case Op_or_64: - { - uint64_t rhs = vm_pop_u64(vm); - uint64_t lhs = vm_pop_u64(vm); - vm_push_u64(vm, lhs | rhs); - } - break; - case Op_xor_64: - { - uint64_t rhs = vm_pop_u64(vm); - uint64_t lhs = vm_pop_u64(vm); - vm_push_u64(vm, lhs ^ rhs); - } - break; - case Op_shl_64: - { - uint64_t rhs = vm_pop_u64(vm); - uint64_t lhs = vm_pop_u64(vm); - vm_push_u64(vm, lhs << (rhs & 0x3f)); - } - break; - case Op_ashr_64: - { - uint64_t rhs = vm_pop_u64(vm); - int64_t lhs = vm_pop_i64(vm); - vm_push_i64(vm, lhs >> (rhs & 0x3f)); - } - break; - case Op_lshr_64: - { - uint64_t rhs = vm_pop_u64(vm); - uint64_t lhs = vm_pop_u64(vm); - vm_push_u64(vm, lhs >> (rhs & 0x3f)); - } - break; - case Op_rol_64: - { - uint64_t rhs = vm_pop_u64(vm); - uint64_t lhs = vm_pop_u64(vm); - vm_push_u64(vm, rotl64(lhs, rhs)); - } - break; - case Op_ror_64: - { - uint64_t rhs = vm_pop_u64(vm); - uint64_t lhs = vm_pop_u64(vm); - vm_push_u64(vm, rotr64(lhs, rhs)); - } - break; - - case Op_fabs_32: - vm_push_f32(vm, fabsf(vm_pop_f32(vm))); - break; - case Op_fneg_32: - vm_push_f32(vm, -vm_pop_f32(vm)); - break; - case Op_ceil_32: - vm_push_f32(vm, ceilf(vm_pop_f32(vm))); - break; - case Op_floor_32: - vm_push_f32(vm, floorf(vm_pop_f32(vm))); - break; - case Op_trunc_32: - vm_push_f32(vm, truncf(vm_pop_f32(vm))); - break; - case Op_nearest_32: - vm_push_f32(vm, roundf(vm_pop_f32(vm))); - break; - case Op_sqrt_32: - vm_push_f32(vm, sqrtf(vm_pop_f32(vm))); - break; - case Op_fadd_32: - { - float rhs = vm_pop_f32(vm); - float lhs = vm_pop_f32(vm); - vm_push_f32(vm, lhs + rhs); - } - break; - case Op_fsub_32: - { - float rhs = vm_pop_f32(vm); - float lhs = vm_pop_f32(vm); - vm_push_f32(vm, lhs - rhs); - } - break; - case Op_fmul_32: - { - float rhs = vm_pop_f32(vm); - float lhs = vm_pop_f32(vm); - vm_push_f32(vm, lhs * rhs); - } - break; - case Op_fdiv_32: - { - float rhs = vm_pop_f32(vm); - float lhs = vm_pop_f32(vm); - vm_push_f32(vm, lhs / rhs); - } - break; - case Op_fmin_32: - { - float rhs = vm_pop_f32(vm); - float lhs = vm_pop_f32(vm); - vm_push_f32(vm, fminf(lhs, rhs)); - } - break; - case Op_fmax_32: - { - float rhs = vm_pop_f32(vm); - float lhs = vm_pop_f32(vm); - vm_push_f32(vm, fmaxf(lhs, rhs)); - } - break; - case Op_copysign_32: - { - float rhs = vm_pop_f32(vm); - float lhs = vm_pop_f32(vm); - vm_push_f32(vm, copysignf(lhs, rhs)); - } - break; - - case Op_fabs_64: - vm_push_f64(vm, fabs(vm_pop_f64(vm))); - break; - case Op_fneg_64: - vm_push_f64(vm, -vm_pop_f64(vm)); - break; - case Op_ceil_64: - vm_push_f64(vm, ceil(vm_pop_f64(vm))); - break; - case Op_floor_64: - vm_push_f64(vm, floor(vm_pop_f64(vm))); - break; - case Op_trunc_64: - vm_push_f64(vm, trunc(vm_pop_f64(vm))); - break; - case Op_nearest_64: - vm_push_f64(vm, round(vm_pop_f64(vm))); - break; - case Op_sqrt_64: - vm_push_f64(vm, sqrt(vm_pop_f64(vm))); - break; - case Op_fadd_64: - { - double rhs = vm_pop_f64(vm); - double lhs = vm_pop_f64(vm); - vm_push_f64(vm, lhs + rhs); - } - break; - case Op_fsub_64: - { - double rhs = vm_pop_f64(vm); - double lhs = vm_pop_f64(vm); - vm_push_f64(vm, lhs - rhs); - } - break; - case Op_fmul_64: - { - double rhs = vm_pop_f64(vm); - double lhs = vm_pop_f64(vm); - vm_push_f64(vm, lhs * rhs); - } - break; - case Op_fdiv_64: - { - double rhs = vm_pop_f64(vm); - double lhs = vm_pop_f64(vm); - vm_push_f64(vm, lhs / rhs); - } - break; - case Op_fmin_64: - { - double rhs = vm_pop_f64(vm); - double lhs = vm_pop_f64(vm); - vm_push_f64(vm, fmin(lhs, rhs)); - } - break; - case Op_fmax_64: - { - double rhs = vm_pop_f64(vm); - double lhs = vm_pop_f64(vm); - vm_push_f64(vm, fmax(lhs, rhs)); - } - break; - case Op_copysign_64: - { - double rhs = vm_pop_f64(vm); - double lhs = vm_pop_f64(vm); - vm_push_f64(vm, copysign(lhs, rhs)); - } - break; - - case Op_ftos_32_32: vm_push_f32(vm, (float)vm_pop_i32(vm)); break; - case Op_ftou_32_32: vm_push_f32(vm, (float)vm_pop_u32(vm)); break; - case Op_ftos_32_64: vm_push_f32(vm, (float)vm_pop_i64(vm)); break; - case Op_ftou_32_64: vm_push_f32(vm, (float)vm_pop_u64(vm)); break; - case Op_sext_64_32: vm_push_i64(vm, vm_pop_i32(vm)); break; - case Op_ftos_64_32: vm_push_i64(vm, (int64_t)vm_pop_f32(vm)); break; - case Op_ftou_64_32: vm_push_u64(vm, (uint64_t)vm_pop_f32(vm)); break; - case Op_ftos_64_64: vm_push_i64(vm, (int64_t)vm_pop_f64(vm)); break; - case Op_ftou_64_64: vm_push_u64(vm, (uint64_t)vm_pop_f64(vm)); break; - case Op_stof_32_32: vm_push_f32(vm, (float)vm_pop_i32(vm)); break; - case Op_utof_32_32: vm_push_f32(vm, (float)vm_pop_u32(vm)); break; - case Op_stof_32_64: vm_push_f32(vm, (float)vm_pop_i64(vm)); break; - case Op_utof_32_64: vm_push_f32(vm, (float)vm_pop_u64(vm)); break; - case Op_ftof_32_64: vm_push_f32(vm, (float)vm_pop_f64(vm)); break; - case Op_stof_64_32: vm_push_f64(vm, (double)vm_pop_i32(vm)); break; - case Op_utof_64_32: vm_push_f64(vm, (double)vm_pop_u32(vm)); break; - case Op_stof_64_64: vm_push_f64(vm, (double)vm_pop_i64(vm)); break; - case Op_utof_64_64: vm_push_f64(vm, (double)vm_pop_u64(vm)); break; - case Op_ftof_64_32: vm_push_f64(vm, (double)vm_pop_f32(vm)); break; - case Op_sext8_32: vm_push_i32(vm, (int8_t)vm_pop_i32(vm)); break; - case Op_sext16_32: vm_push_i32(vm, (int16_t)vm_pop_i32(vm)); break; - case Op_sext8_64: vm_push_i64(vm, (int8_t)vm_pop_i64(vm)); break; - case Op_sext16_64: vm_push_i64(vm, (int16_t)vm_pop_i64(vm)); break; - case Op_sext32_64: vm_push_i64(vm, (int32_t)vm_pop_i64(vm)); break; - - case Op_memcpy: - { - uint32_t n = vm_pop_u32(vm); - uint32_t src = vm_pop_u32(vm); - uint32_t dest = vm_pop_u32(vm); - assert(dest + n <= vm->memory_len); - assert(src + n <= vm->memory_len); - assert(src + n <= dest || dest + n <= src); // overlapping - memcpy(vm->memory + dest, vm->memory + src, n); - } - break; - case Op_memset: - { - uint32_t n = vm_pop_u32(vm); - uint8_t value = (uint8_t)vm_pop_u32(vm); - uint32_t dest = vm_pop_u32(vm); - assert(dest + n <= vm->memory_len); - memset(vm->memory + dest, value, n); - } - break; - } - } -} - -static size_t common_prefix(const char *a, const char *b) { - size_t i = 0; - for (; a[i] == b[i]; i += 1) {} - return i; -} - -int main(int argc, char **argv) { - char *memory = mmap( NULL, max_memory, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); - - const char *zig_lib_dir_path = argv[1]; - const char *cmake_binary_dir_path = argv[2]; - const char *root_name = argv[3]; - size_t argv_i = 4; - const char *wasm_file = argv[argv_i]; - - size_t cwd_path_len = common_prefix(zig_lib_dir_path, cmake_binary_dir_path); - const char *rel_cmake_bin_path = cmake_binary_dir_path + cwd_path_len; - - size_t rel_cmake_bin_path_len = strlen(rel_cmake_bin_path); - - const char *new_argv[30]; - char new_argv_buf[PATH_MAX + 1024]; - uint32_t new_argv_i = 0; - uint32_t new_argv_buf_i = 0; - - int cache_dir = -1; - { - char cache_dir_buf[PATH_MAX * 2]; - size_t i = 0; - size_t cmake_binary_dir_path_len = strlen(cmake_binary_dir_path); - - memcpy(cache_dir_buf + i, cmake_binary_dir_path, cmake_binary_dir_path_len); - i += cmake_binary_dir_path_len; - - cache_dir_buf[i] = '/'; - i += 1; - - memcpy(cache_dir_buf + i, "zig1-cache", strlen("zig1-cache")); - i += strlen("zig1-cache"); - - cache_dir_buf[i] = 0; - - mkdir(cache_dir_buf, 0777); - cache_dir = err_wrap("opening cache dir", - open(cache_dir_buf, O_DIRECTORY|O_RDONLY|O_CLOEXEC)); - } - - // Construct a new argv for the WASI code which has absolute paths - // converted to relative paths, and has the target and terminal status - // autodetected. - - // wasm file path - new_argv[new_argv_i] = argv[argv_i]; - new_argv_i += 1; - argv_i += 1; - - for (; argv[argv_i]; argv_i += 1) { - new_argv[new_argv_i] = argv[argv_i]; - new_argv_i += 1; - } - - { - new_argv[new_argv_i] = "--name"; - new_argv_i += 1; - - new_argv[new_argv_i] = root_name; - new_argv_i += 1; - - char *emit_bin_arg = new_argv_buf + new_argv_buf_i; - memcpy(new_argv_buf + new_argv_buf_i, "-femit-bin=", strlen("-femit-bin=")); - new_argv_buf_i += strlen("-femit-bin="); - memcpy(new_argv_buf + new_argv_buf_i, rel_cmake_bin_path, rel_cmake_bin_path_len); - new_argv_buf_i += rel_cmake_bin_path_len; - new_argv_buf[new_argv_buf_i] = '/'; - new_argv_buf_i += 1; - memcpy(new_argv_buf + new_argv_buf_i, root_name, strlen(root_name)); - new_argv_buf_i += strlen(root_name); - memcpy(new_argv_buf + new_argv_buf_i, ".c", 3); - new_argv_buf_i += 3; - - new_argv[new_argv_i] = emit_bin_arg; - new_argv_i += 1; - } - - { - new_argv[new_argv_i] = "--pkg-begin"; - new_argv_i += 1; - - new_argv[new_argv_i] = "build_options"; - new_argv_i += 1; - - char *build_options_path = new_argv_buf + new_argv_buf_i; - memcpy(new_argv_buf + new_argv_buf_i, rel_cmake_bin_path, rel_cmake_bin_path_len); - new_argv_buf_i += rel_cmake_bin_path_len; - new_argv_buf[new_argv_buf_i] = '/'; - new_argv_buf_i += 1; - memcpy(new_argv_buf + new_argv_buf_i, "config.zig", strlen("config.zig")); - new_argv_buf_i += strlen("config.zig"); - new_argv_buf[new_argv_buf_i] = 0; - new_argv_buf_i += 1; - - new_argv[new_argv_i] = build_options_path; - new_argv_i += 1; - - new_argv[new_argv_i] = "--pkg-end"; - new_argv_i += 1; - } - - { - new_argv[new_argv_i] = "-target"; - new_argv_i += 1; - - new_argv[new_argv_i] = ZIG_TRIPLE_ARCH "-" ZIG_TRIPLE_OS; - new_argv_i += 1; - } - - if (isatty(STDERR_FILENO) != 0) { - new_argv[new_argv_i] = "--color"; - new_argv_i += 1; - - new_argv[new_argv_i] = "on"; - new_argv_i += 1; - } - - new_argv[new_argv_i] = NULL; - - const struct ByteSlice compressed_bytes = read_file_alloc(wasm_file); - - const size_t max_uncompressed_size = 2500000; - char *mod_ptr = arena_alloc(max_uncompressed_size); - size_t mod_len = ZSTD_decompress(mod_ptr, max_uncompressed_size, - compressed_bytes.ptr, compressed_bytes.len); - - int cwd = err_wrap("opening cwd", open(".", O_DIRECTORY|O_RDONLY|O_CLOEXEC)); - int zig_lib_dir = err_wrap("opening zig lib dir", open(zig_lib_dir_path, O_DIRECTORY|O_RDONLY|O_CLOEXEC)); - - add_preopen(0, "stdin", STDIN_FILENO); - add_preopen(1, "stdout", STDOUT_FILENO); - add_preopen(2, "stderr", STDERR_FILENO); - add_preopen(3, ".", cwd); - add_preopen(4, "/cache", cache_dir); - add_preopen(5, "/lib", zig_lib_dir); - - uint32_t i = 0; - - if (mod_ptr[0] != 0 || mod_ptr[1] != 'a' || mod_ptr[2] != 's' || mod_ptr[3] != 'm') { - panic("bad magic"); - } - i += 4; - - uint32_t version = read_u32_le(mod_ptr + i); - i += 4; - if (version != 1) panic("bad wasm version"); - - uint32_t section_starts[13]; - memset(§ion_starts, 0, sizeof(uint32_t) * 13); - - while (i < mod_len) { - uint8_t section_id = mod_ptr[i]; - i += 1; - uint32_t section_len = read32_uleb128(mod_ptr, &i); - section_starts[section_id] = i; - i += section_len; - } - - // Map type indexes to offsets into the module. - struct TypeInfo *types; - { - i = section_starts[Section_type]; - uint32_t types_len = read32_uleb128(mod_ptr, &i); - types = arena_alloc(sizeof(struct TypeInfo) * types_len); - for (size_t type_i = 0; type_i < types_len; type_i += 1) { - struct TypeInfo *info = &types[type_i]; - if (mod_ptr[i] != 0x60) panic("bad type byte"); - i += 1; - - info->param_count = read32_uleb128(mod_ptr, &i); - if (info->param_count > 32) panic("found a type with over 32 parameters"); - info->param_types = 0; - for (uint32_t param_i = 0; param_i < info->param_count; param_i += 1) { - int64_t param_type = read64_ileb128(mod_ptr, &i); - switch (param_type) { - case -1: case -3: bs_unset(&info->param_types, param_i); break; - case -2: case -4: bs_set(&info->param_types, param_i); break; - default: panic("unexpected param type"); - } - } - - info->result_count = read32_uleb128(mod_ptr, &i); - info->result_types = 0; - for (uint32_t result_i = 0; result_i < info->result_count; result_i += 1) { - int64_t result_type = read64_ileb128(mod_ptr, &i); - switch (result_type) { - case -1: case -3: bs_unset(&info->result_types, result_i); break; - case -2: case -4: bs_set(&info->result_types, result_i); break; - default: panic("unexpected result type"); - } - } - } - } - - // Count the imported functions so we can correct function references. - struct Import *imports; - uint32_t imports_len; - { - i = section_starts[Section_import]; - imports_len = read32_uleb128(mod_ptr, &i); - imports = arena_alloc(sizeof(struct Import) * imports_len); - for (size_t imp_i = 0; imp_i < imports_len; imp_i += 1) { - struct Import *imp = &imports[imp_i]; - - struct ByteSlice mod_name = read_name(mod_ptr, &i); - if (mod_name.len == strlen("wasi_snapshot_preview1") && - memcmp(mod_name.ptr, "wasi_snapshot_preview1", mod_name.len) == 0) { - imp->mod = ImpMod_wasi_snapshot_preview1; - } else panic("unknown import module"); - - struct ByteSlice sym_name = read_name(mod_ptr, &i); - if (sym_name.len == strlen("args_get") && - memcmp(sym_name.ptr, "args_get", sym_name.len) == 0) { - imp->name = ImpName_args_get; - } else if (sym_name.len == strlen("args_sizes_get") && - memcmp(sym_name.ptr, "args_sizes_get", sym_name.len) == 0) { - imp->name = ImpName_args_sizes_get; - } else if (sym_name.len == strlen("clock_time_get") && - memcmp(sym_name.ptr, "clock_time_get", sym_name.len) == 0) { - imp->name = ImpName_clock_time_get; - } else if (sym_name.len == strlen("debug") && - memcmp(sym_name.ptr, "debug", sym_name.len) == 0) { - imp->name = ImpName_debug; - } else if (sym_name.len == strlen("debug_slice") && - memcmp(sym_name.ptr, "debug_slice", sym_name.len) == 0) { - imp->name = ImpName_debug_slice; - } else if (sym_name.len == strlen("environ_get") && - memcmp(sym_name.ptr, "environ_get", sym_name.len) == 0) { - imp->name = ImpName_environ_get; - } else if (sym_name.len == strlen("environ_sizes_get") && - memcmp(sym_name.ptr, "environ_sizes_get", sym_name.len) == 0) { - imp->name = ImpName_environ_sizes_get; - } else if (sym_name.len == strlen("fd_close") && - memcmp(sym_name.ptr, "fd_close", sym_name.len) == 0) { - imp->name = ImpName_fd_close; - } else if (sym_name.len == strlen("fd_fdstat_get") && - memcmp(sym_name.ptr, "fd_fdstat_get", sym_name.len) == 0) { - imp->name = ImpName_fd_fdstat_get; - } else if (sym_name.len == strlen("fd_filestat_get") && - memcmp(sym_name.ptr, "fd_filestat_get", sym_name.len) == 0) { - imp->name = ImpName_fd_filestat_get; - } else if (sym_name.len == strlen("fd_filestat_set_size") && - memcmp(sym_name.ptr, "fd_filestat_set_size", sym_name.len) == 0) { - imp->name = ImpName_fd_filestat_set_size; - } else if (sym_name.len == strlen("fd_filestat_set_times") && - memcmp(sym_name.ptr, "fd_filestat_set_times", sym_name.len) == 0) { - imp->name = ImpName_fd_filestat_set_times; - } else if (sym_name.len == strlen("fd_pread") && - memcmp(sym_name.ptr, "fd_pread", sym_name.len) == 0) { - imp->name = ImpName_fd_pread; - } else if (sym_name.len == strlen("fd_prestat_dir_name") && - memcmp(sym_name.ptr, "fd_prestat_dir_name", sym_name.len) == 0) { - imp->name = ImpName_fd_prestat_dir_name; - } else if (sym_name.len == strlen("fd_prestat_get") && - memcmp(sym_name.ptr, "fd_prestat_get", sym_name.len) == 0) { - imp->name = ImpName_fd_prestat_get; - } else if (sym_name.len == strlen("fd_pwrite") && - memcmp(sym_name.ptr, "fd_pwrite", sym_name.len) == 0) { - imp->name = ImpName_fd_pwrite; - } else if (sym_name.len == strlen("fd_read") && - memcmp(sym_name.ptr, "fd_read", sym_name.len) == 0) { - imp->name = ImpName_fd_read; - } else if (sym_name.len == strlen("fd_readdir") && - memcmp(sym_name.ptr, "fd_readdir", sym_name.len) == 0) { - imp->name = ImpName_fd_readdir; - } else if (sym_name.len == strlen("fd_write") && - memcmp(sym_name.ptr, "fd_write", sym_name.len) == 0) { - imp->name = ImpName_fd_write; - } else if (sym_name.len == strlen("path_create_directory") && - memcmp(sym_name.ptr, "path_create_directory", sym_name.len) == 0) { - imp->name = ImpName_path_create_directory; - } else if (sym_name.len == strlen("path_filestat_get") && - memcmp(sym_name.ptr, "path_filestat_get", sym_name.len) == 0) { - imp->name = ImpName_path_filestat_get; - } else if (sym_name.len == strlen("path_open") && - memcmp(sym_name.ptr, "path_open", sym_name.len) == 0) { - imp->name = ImpName_path_open; - } else if (sym_name.len == strlen("path_remove_directory") && - memcmp(sym_name.ptr, "path_remove_directory", sym_name.len) == 0) { - imp->name = ImpName_path_remove_directory; - } else if (sym_name.len == strlen("path_rename") && - memcmp(sym_name.ptr, "path_rename", sym_name.len) == 0) { - imp->name = ImpName_path_rename; - } else if (sym_name.len == strlen("path_unlink_file") && - memcmp(sym_name.ptr, "path_unlink_file", sym_name.len) == 0) { - imp->name = ImpName_path_unlink_file; - } else if (sym_name.len == strlen("proc_exit") && - memcmp(sym_name.ptr, "proc_exit", sym_name.len) == 0) { - imp->name = ImpName_proc_exit; - } else if (sym_name.len == strlen("random_get") && - memcmp(sym_name.ptr, "random_get", sym_name.len) == 0) { - imp->name = ImpName_random_get; - } else panic("unknown import name"); - - uint32_t desc = read32_uleb128(mod_ptr, &i); - if (desc != 0) panic("external kind not function"); - imp->type_idx = read32_uleb128(mod_ptr, &i); - } - } - - // Find _start in the exports - uint32_t start_fn_idx; - { - i = section_starts[Section_export]; - uint32_t count = read32_uleb128(mod_ptr, &i); - for (; count > 0; count -= 1) { - struct ByteSlice name = read_name(mod_ptr, &i); - uint32_t desc = read32_uleb128(mod_ptr, &i); - start_fn_idx = read32_uleb128(mod_ptr, &i); - if (desc == 0 && name.len == strlen("_start") && - memcmp(name.ptr, "_start", name.len) == 0) - { - break; - } - } - if (count == 0) panic("_start symbol not found"); - } - - // Map function indexes to offsets into the module and type index. - struct Function *functions; - uint32_t functions_len; - { - i = section_starts[Section_function]; - functions_len = read32_uleb128(mod_ptr, &i); - functions = arena_alloc(sizeof(struct Function) * functions_len); - for (size_t func_i = 0; func_i < functions_len; func_i += 1) { - struct Function *func = &functions[func_i]; - func->id = imports_len + func_i; - func->type_idx = read32_uleb128(mod_ptr, &i); - } - } - - // Allocate and initialize globals. - uint64_t *globals; - { - i = section_starts[Section_global]; - uint32_t globals_len = read32_uleb128(mod_ptr, &i); - globals = arena_alloc(sizeof(uint64_t) * globals_len); - for (size_t glob_i = 0; glob_i < globals_len; glob_i += 1) { - uint64_t *global = &globals[glob_i]; - uint32_t content_type = read32_uleb128(mod_ptr, &i); - uint32_t mutability = read32_uleb128(mod_ptr, &i); - if (mutability != 1) panic("expected mutable global"); - if (content_type != 0x7f) panic("unexpected content type"); - uint8_t opcode = mod_ptr[i]; - i += 1; - if (opcode != WasmOp_i32_const) panic("expected i32_const op"); - uint32_t init = read32_ileb128(mod_ptr, &i); - *global = (uint32_t)init; - } - } - - // Allocate and initialize memory. - uint32_t memory_len; - { - i = section_starts[Section_memory]; - uint32_t memories_len = read32_uleb128(mod_ptr, &i); - if (memories_len != 1) panic("unexpected memory count"); - uint32_t flags = read32_uleb128(mod_ptr, &i); - (void)flags; - memory_len = read32_uleb128(mod_ptr, &i) * wasm_page_size; - - i = section_starts[Section_data]; - uint32_t datas_count = read32_uleb128(mod_ptr, &i); - for (; datas_count > 0; datas_count -= 1) { - uint32_t mode = read32_uleb128(mod_ptr, &i); - if (mode != 0) panic("expected mode 0"); - enum WasmOp opcode = mod_ptr[i]; - i += 1; - if (opcode != WasmOp_i32_const) panic("expected opcode i32_const"); - uint32_t offset = read32_uleb128(mod_ptr, &i); - enum WasmOp end = mod_ptr[i]; - if (end != WasmOp_end) panic("expected end opcode"); - i += 1; - uint32_t bytes_len = read32_uleb128(mod_ptr, &i); - memcpy(memory + offset, mod_ptr + i, bytes_len); - i += bytes_len; - } - } - - uint32_t *table = NULL; - { - i = section_starts[Section_table]; - uint32_t table_count = read32_uleb128(mod_ptr, &i); - if (table_count > 1) { - panic("expected only one table section"); - } else if (table_count == 1) { - uint32_t element_type = read32_uleb128(mod_ptr, &i); - (void)element_type; - uint32_t has_max = read32_uleb128(mod_ptr, &i); - if (has_max != 1) panic("expected has_max==1"); - uint32_t initial = read32_uleb128(mod_ptr, &i); - (void)initial; - uint32_t maximum = read32_uleb128(mod_ptr, &i); - - i = section_starts[Section_element]; - uint32_t element_section_count = read32_uleb128(mod_ptr, &i); - if (element_section_count != 1) panic("expected one element section"); - uint32_t flags = read32_uleb128(mod_ptr, &i); - (void)flags; - enum WasmOp opcode = mod_ptr[i]; - i += 1; - if (opcode != WasmOp_i32_const) panic("expected op i32_const"); - uint32_t offset = read32_uleb128(mod_ptr, &i); - enum WasmOp end = mod_ptr[i]; - if (end != WasmOp_end) panic("expected op end"); - i += 1; - uint32_t elem_count = read32_uleb128(mod_ptr, &i); - - table = arena_alloc(sizeof(uint32_t) * maximum); - memset(table, 0, sizeof(uint32_t) * maximum); - - for (uint32_t elem_i = 0; elem_i < elem_count; elem_i += 1) { - table[elem_i + offset] = read32_uleb128(mod_ptr, &i); - } - } - } - - struct VirtualMachine vm; -#ifndef NDEBUG - memset(&vm, 0xaa, sizeof(struct VirtualMachine)); // to match the zig version -#endif - vm.stack = arena_alloc(sizeof(uint32_t) * 10000000), - vm.mod_ptr = mod_ptr; - vm.opcodes = arena_alloc(2000000); - vm.operands = arena_alloc(sizeof(uint32_t) * 2000000); - vm.stack_top = 0; - vm.functions = functions; - vm.types = types; - vm.globals = globals; - vm.memory = memory; - vm.memory_len = memory_len; - vm.imports = imports; - vm.imports_len = imports_len; - vm.args = new_argv; - vm.table = table; - - { - uint32_t code_i = section_starts[Section_code]; - uint32_t codes_len = read32_uleb128(mod_ptr, &code_i); - if (codes_len != functions_len) panic("code/function length mismatch"); - struct ProgramCounter pc; - pc.opcode = 0; - pc.operand = 0; - struct StackInfo stack; - for (uint32_t func_i = 0; func_i < functions_len; func_i += 1) { - struct Function *func = &functions[func_i]; - uint32_t size = read32_uleb128(mod_ptr, &code_i); - uint32_t code_begin = code_i; - - stack.top_index = 0; - stack.top_offset = 0; - struct TypeInfo *type_info = &vm.types[func->type_idx]; - for (uint32_t param_i = 0; param_i < type_info->param_count; param_i += 1) - si_push(&stack, bs_isSet(&type_info->param_types, param_i)); - uint32_t params_size = stack.top_offset; - - for (uint32_t local_sets_count = read32_uleb128(mod_ptr, &code_i); - local_sets_count > 0; local_sets_count -= 1) - { - uint32_t local_set_count = read32_uleb128(mod_ptr, &code_i); - enum StackType local_type; - switch (read64_ileb128(mod_ptr, &code_i)) { - case -1: case -3: local_type = ST_32; break; - case -2: case -4: local_type = ST_64; break; - default: panic("unexpected local type"); - } - for (; local_set_count > 0; local_set_count -= 1) - si_push(&stack, local_type); - } - func->locals_size = stack.top_offset - params_size; - - func->entry_pc = pc; - //fprintf(stderr, "decoding func id %u with pc %u:%u\n", func->id, pc.opcode, pc.operand); - vm_decodeCode(&vm, type_info, &code_i, &pc, &stack); - if (code_i != code_begin + size) panic("bad code size"); - } - //fprintf(stderr, "%u opcodes\n%u operands\n", pc.opcode, pc.operand); - } - - vm_call(&vm, &vm.functions[start_fn_idx - imports_len]); - vm_run(&vm); - - return 0; -}