File syn_wasm.c¶
File List > src > syntropic > vm > syn_wasm.c
Go to the documentation of this file
#include "syn_wasm.h"
#include <math.h>
#include <string.h>
#if defined(SYN_WASM_USE_FIXED) && SYN_WASM_USE_FIXED
#include "syntropic/util/syn_qmath.h"
#endif
/* Wasm Magic Header & Version */
#define WASM_MAGIC 0x6D736100U /* "\0asm" */
#define WASM_VERSION 0x00000001U
/* Value Types */
#define WASM_TYPE_I32 0x7FU
#define WASM_TYPE_I64 0x7EU
#define WASM_TYPE_F32 0x7DU
#define WASM_TYPE_F64 0x7CU
/* Section IDs */
#define WASM_SEC_TYPE 1
#define WASM_SEC_IMPORT 2
#define WASM_SEC_FUNCTION 3
#define WASM_SEC_GLOBAL 6
#define WASM_SEC_EXPORT 7
#define WASM_SEC_START 8
#define WASM_SEC_ELEMENT 9
#define WASM_SEC_CODE 10
#define WASM_SEC_DATA 11
/* Opcodes */
#define OP_UNREACHABLE 0x00
#define OP_NOP 0x01
#define OP_BLOCK 0x02
#define OP_LOOP 0x03
#define OP_IF 0x04
#define OP_ELSE 0x05
#define OP_END 0x0B
#define OP_BR 0x0C
#define OP_BR_IF 0x0D
#define OP_BR_TABLE 0x0E
#define OP_RETURN 0x0F
#define OP_CALL 0x10
#define OP_CALL_INDIRECT 0x11
#define OP_DROP 0x1A
#define OP_SELECT 0x1B
#define OP_LOCAL_GET 0x20
#define OP_LOCAL_SET 0x21
#define OP_LOCAL_TEE 0x22
#define OP_GLOBAL_GET 0x23
#define OP_GLOBAL_SET 0x24
#define OP_I32_LOAD 0x28
#define OP_I32_LOAD8_S 0x2C
#define OP_I32_LOAD8_U 0x2D
#define OP_I32_LOAD16_S 0x2E
#define OP_I32_LOAD16_U 0x2F
#define OP_I32_STORE 0x36
#define OP_I32_STORE8 0x3A
#define OP_I32_STORE16 0x3B
#define OP_MEMORY_SIZE 0x3F
#define OP_MEMORY_GROW 0x40
#define OP_I32_CONST 0x41
#define OP_I32_EQZ 0x45
#define OP_I32_EQ 0x46
#define OP_I32_NE 0x47
#define OP_I32_LT_S 0x48
#define OP_I32_LT_U 0x49
#define OP_I32_GT_S 0x4A
#define OP_I32_GT_U 0x4B
#define OP_I32_LE_S 0x4C
#define OP_I32_LE_U 0x4D
#define OP_I32_GE_S 0x4E
#define OP_I32_GE_U 0x4F
#define OP_I32_CLZ 0x67
#define OP_I32_CTZ 0x68
#define OP_I32_POPCNT 0x69
#define OP_I32_ADD 0x6A
#define OP_I32_SUB 0x6B
#define OP_I32_MUL 0x6C
#define OP_I32_DIV_S 0x6D
#define OP_I32_DIV_U 0x6E
#define OP_I32_REM_S 0x6F
#define OP_I32_REM_U 0x70
#define OP_I32_AND 0x71
#define OP_I32_OR 0x72
#define OP_I32_XOR 0x73
#define OP_I32_SHL 0x74
#define OP_I32_SHR_S 0x75
#define OP_I32_SHR_U 0x76
#define OP_I32_ROTL 0x77
#define OP_I32_ROTR 0x78
#define OP_I64_LOAD 0x29
#define OP_I64_LOAD8_S 0x30
#define OP_I64_LOAD8_U 0x31
#define OP_I64_LOAD16_S 0x32
#define OP_I64_LOAD16_U 0x33
#define OP_I64_LOAD32_S 0x34
#define OP_I64_LOAD32_U 0x35
#define OP_I64_STORE 0x37
#define OP_I64_STORE8 0x3C
#define OP_I64_STORE16 0x3D
#define OP_I64_STORE32 0x3E
#define OP_I64_CONST 0x42
#define OP_I64_EQZ 0x50
#define OP_I64_EQ 0x51
#define OP_I64_NE 0x52
#define OP_I64_LT_S 0x53
#define OP_I64_LT_U 0x54
#define OP_I64_GT_S 0x55
#define OP_I64_GT_U 0x56
#define OP_I64_LE_S 0x57
#define OP_I64_LE_U 0x58
#define OP_I64_GE_S 0x59
#define OP_I64_GE_U 0x5A
#define OP_I64_CLZ 0x79
#define OP_I64_CTZ 0x7A
#define OP_I64_POPCNT 0x7B
#define OP_I64_ADD 0x7C
#define OP_I64_SUB 0x7D
#define OP_I64_MUL 0x7E
#define OP_I64_DIV_S 0x7F
#define OP_I64_DIV_U 0x80
#define OP_I64_REM_S 0x81
#define OP_I64_REM_U 0x82
#define OP_I64_AND 0x83
#define OP_I64_OR 0x84
#define OP_I64_XOR 0x85
#define OP_I64_SHL 0x86
#define OP_I64_SHR_S 0x87
#define OP_I64_SHR_U 0x88
#define OP_I64_ROTL 0x89
#define OP_I64_ROTR 0x8A
#define OP_I32_WRAP_I64 0xA7
#define OP_I64_EXTEND_I32_S 0xAC
#define OP_I64_EXTEND_I32_U 0xAD
#define OP_F32_LOAD 0x2A
#define OP_F64_LOAD 0x2B
#define OP_F32_STORE 0x38
#define OP_F64_STORE 0x39
#define OP_F32_CONST 0x43
#define OP_F64_CONST 0x44
#define OP_F32_EQ 0x5B
#define OP_F32_NE 0x5C
#define OP_F32_LT 0x5D
#define OP_F32_GT 0x5E
#define OP_F32_LE 0x5F
#define OP_F32_GE 0x60
#define OP_F64_EQ 0x61
#define OP_F64_NE 0x62
#define OP_F64_LT 0x63
#define OP_F64_GT 0x64
#define OP_F64_LE 0x65
#define OP_F64_GE 0x66
#define OP_F32_ABS 0x8B
#define OP_F32_NEG 0x8C
#define OP_F32_CEIL 0x8D
#define OP_F32_FLOOR 0x8E
#define OP_F32_TRUNC 0x8F
#define OP_F32_NEAREST 0x90
#define OP_F32_SQRT 0x91
#define OP_F32_ADD 0x92
#define OP_F32_SUB 0x93
#define OP_F32_MUL 0x94
#define OP_F32_DIV 0x95
#define OP_F32_MIN 0x96
#define OP_F32_MAX 0x97
#define OP_F32_COPYSIGN 0x98
#define OP_F64_ABS 0x99
#define OP_F64_NEG 0x9A
#define OP_F64_CEIL 0x9B
#define OP_F64_FLOOR 0x9C
#define OP_F64_TRUNC 0x9D
#define OP_F64_NEAREST 0x9E
#define OP_F64_SQRT 0x9F
#define OP_F64_ADD 0xA0
#define OP_F64_SUB 0xA1
#define OP_F64_MUL 0xA2
#define OP_F64_DIV 0xA3
#define OP_F64_MIN 0xA4
#define OP_F64_MAX 0xA5
#define OP_F64_COPYSIGN 0xA6
#define OP_I32_TRUNC_F32_S 0xA8
#define OP_I32_TRUNC_F32_U 0xA9
#define OP_I32_TRUNC_F64_S 0xAA
#define OP_I32_TRUNC_F64_U 0xAB
#define OP_I64_TRUNC_F32_S 0xAE
#define OP_I64_TRUNC_F32_U 0xAF
#define OP_I64_TRUNC_F64_S 0xB0
#define OP_I64_TRUNC_F64_U 0xB1
#define OP_F32_CONVERT_I32_S 0xB2
#define OP_F32_CONVERT_I32_U 0xB3
#define OP_F32_CONVERT_I64_S 0xB4
#define OP_F32_CONVERT_I64_U 0xB5
#define OP_F32_DEMOTE_F64 0xB6
#define OP_F64_CONVERT_I32_S 0xB7
#define OP_F64_CONVERT_I32_U 0xB8
#define OP_F64_CONVERT_I64_S 0xB9
#define OP_F64_CONVERT_I64_U 0xBA
#define OP_F64_PROMOTE_F32 0xBB
#define OP_I32_REINTERPRET_F32 0xBC
#define OP_I64_REINTERPRET_F64 0xBD
#define OP_F32_REINTERPRET_I32 0xBE
#define OP_F64_REINTERPRET_I64 0xBF
#define OP_PREFIX_MISC 0xFC
/* ── LEB128 Decoding Helpers ───────────────────────────────────────────── */
static uint32_t read_u32_leb128(const uint8_t *bytes, uint32_t max_size, uint32_t *offset)
{
uint32_t result = 0;
uint32_t shift = 0;
uint32_t cur = *offset;
while (cur < max_size) {
uint8_t byte = bytes[cur++];
result |= (uint32_t)(byte & 0x7F) << shift;
if ((byte & 0x80) == 0) {
break;
}
shift += 7;
/* LCOV_EXCL_START: LEB128 decoding maximum length safety check */
if (shift >= 35) {
break;
}
/* LCOV_EXCL_STOP */
}
*offset = cur;
return result;
}
static int32_t read_i32_leb128(const uint8_t *bytes, uint32_t max_size, uint32_t *offset)
{
int32_t result = 0;
uint32_t shift = 0;
uint32_t cur = *offset;
uint8_t byte = 0;
while (cur < max_size) {
byte = bytes[cur++];
result |= (int32_t)((uint32_t)(byte & 0x7FU) << shift);
shift += 7;
if ((byte & 0x80) == 0) {
break;
}
/* LCOV_EXCL_START: Signed LEB128 decoding maximum length safety check */
if (shift >= 35) {
break;
}
/* LCOV_EXCL_STOP */
}
if ((shift < 32) && (byte & 0x40)) {
result |= (int32_t)(~0U << shift);
}
*offset = cur;
return result;
}
static int64_t read_i64_leb128(const uint8_t *bytes, uint32_t max_size, uint32_t *offset)
{
int64_t result = 0;
uint32_t shift = 0;
uint32_t cur = *offset;
uint8_t byte = 0;
while (cur < max_size) {
byte = bytes[cur++];
result |= (int64_t)(((uint64_t)(byte & 0x7FU)) << shift);
shift += 7;
if ((byte & 0x80) == 0) {
break;
}
/* LCOV_EXCL_START: 64-bit LEB128 decoding maximum length safety check */
if (shift >= 70) {
break;
}
/* LCOV_EXCL_STOP */
}
/* LCOV_EXCL_START: Negative LEB128 sign extension */
if ((shift < 64) && (byte & 0x40)) {
result |= (int64_t)(~0ULL << shift);
}
/* LCOV_EXCL_STOP */
*offset = cur;
return result;
}
/* ── Stack Helpers ──────────────────────────────────────────────────────── */
static bool push_stack64(SYN_WASM_Context *ctx, uint64_t val)
{
/* LCOV_EXCL_START: Wasm operand stack overflow guard */
if (ctx->sp >= SYN_WASM_MAX_STACK) {
ctx->status = SYN_WASM_TRAP_STACK_OVERFLOW;
return false;
}
/* LCOV_EXCL_STOP */
ctx->stack[ctx->sp++] = val;
return true;
}
static bool pop_stack64(SYN_WASM_Context *ctx, uint64_t *val)
{
if (ctx->sp == 0) {
/* LCOV_EXCL_START: Pop 64-bit stack underflow return */
ctx->status = SYN_WASM_TRAP_STACK_UNDERFLOW;
return false;
/* LCOV_EXCL_STOP */
}
*val = ctx->stack[--ctx->sp];
return true;
}
static bool push_stack(SYN_WASM_Context *ctx, uint32_t val)
{
return push_stack64(ctx, (uint64_t)val);
}
static bool pop_stack(SYN_WASM_Context *ctx, uint32_t *val)
{
uint64_t v64 = 0;
if (!pop_stack64(ctx, &v64)) {
/* LCOV_EXCL_START: Pop 32-bit stack underflow return */
return false;
/* LCOV_EXCL_STOP */
}
*val = (uint32_t)v64;
return true;
}
#if !defined(SYN_WASM_USE_FIXED) || !SYN_WASM_USE_FIXED
static bool push_f32(SYN_WASM_Context *ctx, float val)
{
uint32_t u32;
memcpy(&u32, &val, sizeof(u32));
return push_stack64(ctx, (uint64_t)u32);
}
static bool pop_f32(SYN_WASM_Context *ctx, float *val)
{
uint64_t v64 = 0;
if (!pop_stack64(ctx, &v64)) {
/* LCOV_EXCL_START: Pop float stack underflow return */
return false;
/* LCOV_EXCL_STOP */
}
uint32_t u32 = (uint32_t)v64;
memcpy(val, &u32, sizeof(u32));
return true;
}
static bool push_f64(SYN_WASM_Context *ctx, double val)
{
uint64_t u64;
memcpy(&u64, &val, sizeof(u64));
return push_stack64(ctx, u64);
}
static bool pop_f64(SYN_WASM_Context *ctx, double *val)
{
uint64_t v64 = 0;
if (!pop_stack64(ctx, &v64)) {
/* LCOV_EXCL_START: Pop double float stack underflow return */
return false;
/* LCOV_EXCL_STOP */
}
memcpy(val, &v64, sizeof(v64));
return true;
}
#endif
static void skip_instruction_immediates(uint8_t op, const uint8_t *bytes, uint32_t size,
uint32_t *pc)
{
switch (op) {
/* LCOV_EXCL_START: Wasm instruction immediates skipper opcodes */
case OP_BLOCK:
case OP_LOOP:
case OP_IF:
case OP_MEMORY_SIZE:
case OP_MEMORY_GROW:
(*pc)++;
break;
/* LCOV_EXCL_STOP */
case OP_BR:
case OP_BR_IF:
case OP_CALL:
case OP_LOCAL_GET:
case OP_LOCAL_SET:
case OP_LOCAL_TEE:
case OP_GLOBAL_GET:
case OP_GLOBAL_SET:
case OP_I32_CONST:
read_u32_leb128(bytes, size, pc);
break;
/* LCOV_EXCL_START: Wasm module parser instruction immediate skip helpers */
case OP_I64_CONST:
read_i64_leb128(bytes, size, pc);
break;
case OP_CALL_INDIRECT:
read_u32_leb128(bytes, size, pc);
read_u32_leb128(bytes, size, pc);
break;
case OP_BR_TABLE: {
uint32_t count = read_u32_leb128(bytes, size, pc);
for (uint32_t i = 0; i <= count; i++) {
read_u32_leb128(bytes, size, pc);
}
break;
}
case OP_I32_LOAD:
case OP_I32_LOAD8_S:
case OP_I32_LOAD8_U:
case OP_I32_LOAD16_S:
case OP_I32_LOAD16_U:
case OP_I32_STORE:
case OP_I32_STORE8:
case OP_I32_STORE16:
case OP_I64_LOAD:
case OP_I64_LOAD8_S:
case OP_I64_LOAD8_U:
case OP_I64_LOAD16_S:
case OP_I64_LOAD16_U:
case OP_I64_LOAD32_S:
case OP_I64_LOAD32_U:
case OP_I64_STORE:
case OP_I64_STORE8:
case OP_I64_STORE16:
case OP_I64_STORE32:
case OP_F32_LOAD:
case OP_F64_LOAD:
case OP_F32_STORE:
case OP_F64_STORE:
read_u32_leb128(bytes, size, pc);
read_u32_leb128(bytes, size, pc);
break;
case OP_F32_CONST:
*pc += 4;
break;
case OP_F64_CONST:
*pc += 8;
break;
case OP_PREFIX_MISC: {
uint32_t subop = read_u32_leb128(bytes, size, pc);
if (subop == 8) {
read_u32_leb128(bytes, size, pc);
read_u32_leb128(bytes, size, pc);
} else if (subop == 9) {
read_u32_leb128(bytes, size, pc);
} else if (subop == 10) {
read_u32_leb128(bytes, size, pc);
read_u32_leb128(bytes, size, pc);
} else if (subop == 11) {
read_u32_leb128(bytes, size, pc);
}
break;
}
/* LCOV_EXCL_STOP */
default:
break;
}
}
/* ── Module Parser ──────────────────────────────────────────────────────── */
bool syn_wasm_module_load(SYN_WASM_Module *mod, const uint8_t *bytes, uint32_t size)
{
if (!mod || !bytes || size < 8) {
return false;
}
memset(mod, 0, sizeof(*mod));
mod->bytes = bytes;
mod->size = size;
/* Verify Magic Header & Version */
uint32_t magic = ((uint32_t)bytes[0]) | ((uint32_t)bytes[1] << 8) | ((uint32_t)bytes[2] << 16) |
((uint32_t)bytes[3] << 24);
uint32_t version = ((uint32_t)bytes[4]) | ((uint32_t)bytes[5] << 8) |
((uint32_t)bytes[6] << 16) | ((uint32_t)bytes[7] << 24);
if (magic != WASM_MAGIC || version != WASM_VERSION) {
return false;
}
uint32_t offset = 8;
uint32_t type_func_indices[SYN_WASM_MAX_FUNCTIONS];
uint16_t func_decl_count = 0;
struct {
uint8_t param_count;
uint8_t result_count;
} parsed_types[SYN_WASM_MAX_FUNCTIONS];
uint16_t type_count = 0;
while (offset < size) {
uint8_t section_id = bytes[offset++];
uint32_t section_len = read_u32_leb128(bytes, size, &offset);
uint32_t section_end = offset + section_len;
/* LCOV_EXCL_START: Wasm section_end boundary check */
if (section_end > size) {
return false;
}
/* LCOV_EXCL_STOP */
if (section_id == WASM_SEC_TYPE) {
uint32_t num_types = read_u32_leb128(bytes, section_end, &offset);
for (uint32_t i = 0; i < num_types && offset < section_end; i++) {
uint8_t form = bytes[offset++];
/* LCOV_EXCL_START: Wasm non-0x60 type form check */
if (form != 0x60) { /* Func form */
return false;
}
/* LCOV_EXCL_STOP */
uint32_t num_params = read_u32_leb128(bytes, section_end, &offset);
for (uint32_t p = 0; p < num_params && offset < section_end; p++) {
uint8_t pt = bytes[offset++];
(void)pt;
}
uint32_t num_results = read_u32_leb128(bytes, section_end, &offset);
for (uint32_t r = 0; r < num_results && offset < section_end; r++) {
uint8_t rt = bytes[offset++];
(void)rt;
}
if (type_count < SYN_WASM_MAX_FUNCTIONS) {
parsed_types[type_count].param_count = (uint8_t)num_params;
parsed_types[type_count].result_count = (uint8_t)num_results;
type_count++;
}
}
} else if (section_id == WASM_SEC_IMPORT) {
uint32_t num_imports = read_u32_leb128(bytes, section_end, &offset);
for (uint32_t i = 0; i < num_imports && offset < section_end; i++) {
uint32_t mod_len = read_u32_leb128(bytes, section_end, &offset);
offset += mod_len;
uint32_t field_len = read_u32_leb128(bytes, section_end, &offset);
offset += field_len;
uint8_t kind = bytes[offset++];
if (kind == 0) { /* Function import */
uint32_t type_idx = read_u32_leb128(bytes, section_end, &offset);
if (mod->func_count < SYN_WASM_MAX_FUNCTIONS) {
mod->funcs[mod->func_count].type_idx = type_idx;
mod->funcs[mod->func_count].code_offset = 0;
mod->funcs[mod->func_count].code_size = 0;
if (type_idx < type_count) {
mod->funcs[mod->func_count].param_count =
parsed_types[type_idx].param_count;
mod->funcs[mod->func_count].result_count =
parsed_types[type_idx].result_count;
}
mod->func_count++;
mod->import_func_count++;
}
/* LCOV_EXCL_START: Wasm module table, memory, global import sections */
} else if (kind == 1) { /* Table */
offset++;
offset++;
} else if (kind == 2) { /* Memory */
uint8_t flags = bytes[offset++];
read_u32_leb128(bytes, section_end, &offset);
if (flags & 1)
read_u32_leb128(bytes, section_end, &offset);
} else if (kind == 3) { /* Global */
offset++;
offset++;
}
/* LCOV_EXCL_STOP */
}
} else if (section_id == WASM_SEC_FUNCTION) {
uint32_t num_funcs = read_u32_leb128(bytes, section_end, &offset);
for (uint32_t i = 0; i < num_funcs && offset < section_end; i++) {
uint32_t type_idx = read_u32_leb128(bytes, section_end, &offset);
if (func_decl_count < SYN_WASM_MAX_FUNCTIONS) {
type_func_indices[func_decl_count++] = type_idx;
}
}
} else if (section_id == WASM_SEC_EXPORT) {
uint32_t num_exports = read_u32_leb128(bytes, section_end, &offset);
for (uint32_t i = 0; i < num_exports && offset < section_end; i++) {
uint32_t name_len = read_u32_leb128(bytes, section_end, &offset);
uint32_t name_offset = offset;
offset += name_len;
uint8_t kind = bytes[offset++];
uint32_t idx = read_u32_leb128(bytes, section_end, &offset);
if (kind == 0 && mod->export_count < SYN_WASM_MAX_FUNCTIONS) {
mod->exports[mod->export_count].name_offset = name_offset;
mod->exports[mod->export_count].name_len = (uint16_t)name_len;
mod->exports[mod->export_count].func_idx = (uint16_t)idx;
mod->export_count++;
}
}
/* LCOV_EXCL_START: Wasm module start & element sections */
} else if (section_id == WASM_SEC_START) {
mod->start_func_idx = read_u32_leb128(bytes, section_end, &offset);
mod->has_start_func = true;
} else if (section_id == WASM_SEC_ELEMENT) {
uint32_t num_segments = read_u32_leb128(bytes, section_end, &offset);
for (uint32_t s = 0; s < num_segments && offset < section_end; s++) {
uint32_t flags = read_u32_leb128(bytes, section_end, &offset);
(void)flags;
int32_t elem_offset = 0;
if (offset < section_end) {
uint8_t op = bytes[offset++];
if (op == OP_I32_CONST) {
elem_offset = read_i32_leb128(bytes, section_end, &offset);
}
if (offset < section_end && bytes[offset] == OP_END) {
offset++;
}
}
uint32_t num_funcs = read_u32_leb128(bytes, section_end, &offset);
for (uint32_t f = 0; f < num_funcs && offset < section_end; f++) {
uint32_t f_idx = read_u32_leb128(bytes, section_end, &offset);
uint32_t dest = (uint32_t)elem_offset + f;
if (dest < 64) {
mod->table_elements[dest] = (uint16_t)f_idx;
if (dest + 1 > mod->table_element_count) {
mod->table_element_count = (uint16_t)(dest + 1);
}
}
}
}
}
/* LCOV_EXCL_STOP */
else if (section_id == WASM_SEC_CODE) {
uint32_t num_bodies = read_u32_leb128(bytes, section_end, &offset);
for (uint32_t i = 0; i < num_bodies && offset < section_end; i++) {
uint32_t body_size = read_u32_leb128(bytes, section_end, &offset);
uint32_t body_start = offset;
if (i < func_decl_count && mod->func_count < SYN_WASM_MAX_FUNCTIONS) {
uint32_t t_idx = type_func_indices[i];
mod->funcs[mod->func_count].type_idx = t_idx;
mod->funcs[mod->func_count].code_offset = body_start;
mod->funcs[mod->func_count].code_size = body_size;
if (t_idx < type_count) {
mod->funcs[mod->func_count].param_count = parsed_types[t_idx].param_count;
mod->funcs[mod->func_count].result_count = parsed_types[t_idx].result_count;
}
mod->func_count++;
}
offset = body_start + body_size;
}
}
offset = section_end;
}
return true;
}
/* ── Context & Execution ─────────────────────────────────────────────────── */
bool syn_wasm_init(SYN_WASM_Context *ctx, const SYN_WASM_Module *mod, uint8_t *linear_mem,
uint32_t mem_size)
{
if (!ctx || !mod) {
return false;
}
memset(ctx, 0, sizeof(*ctx));
ctx->module = mod;
ctx->linear_mem = linear_mem;
ctx->linear_mem_size = mem_size;
ctx->status = SYN_WASM_OK;
/* Parse and populate Data Section (Section 11) into linear memory */
if (linear_mem && mem_size > 0 && mod->bytes && mod->size > 8) {
uint32_t offset = 8;
while (offset < mod->size) {
uint8_t section_id = mod->bytes[offset++];
uint32_t section_size = read_u32_leb128(mod->bytes, mod->size, &offset);
uint32_t section_end = offset + section_size;
if (section_id == WASM_SEC_GLOBAL) {
uint32_t num_globals = read_u32_leb128(mod->bytes, section_end, &offset);
for (uint32_t g = 0; g < num_globals && offset < section_end; g++) {
offset++; /* Type */
offset++; /* Mutability */
int64_t init_val = 0;
if (offset < section_end) {
uint8_t op = mod->bytes[offset++];
if (op == OP_I32_CONST) {
init_val = read_i32_leb128(mod->bytes, section_end, &offset);
/* LCOV_EXCL_START: Wasm i64 global initializer */
} else if (op == OP_I64_CONST) {
init_val = read_i64_leb128(mod->bytes, section_end, &offset);
}
/* LCOV_EXCL_STOP */
if (offset < section_end && mod->bytes[offset] == OP_END) {
offset++;
}
}
if (g < SYN_WASM_MAX_GLOBALS) {
ctx->globals[g] = (uint64_t)init_val;
}
}
} else if (section_id == WASM_SEC_DATA) {
uint32_t num_segments = read_u32_leb128(mod->bytes, section_end, &offset);
for (uint32_t s = 0; s < num_segments && offset < section_end; s++) {
uint32_t flags = read_u32_leb128(mod->bytes, section_end, &offset);
(void)flags;
int32_t data_offset = 0;
if (offset < section_end) {
uint8_t op = mod->bytes[offset++];
if (op == OP_I32_CONST) {
data_offset = read_i32_leb128(mod->bytes, section_end, &offset);
}
if (offset < section_end && mod->bytes[offset] == OP_END) {
offset++;
}
}
uint32_t data_size = read_u32_leb128(mod->bytes, section_end, &offset);
if (offset + data_size <= section_end) {
if ((uint32_t)data_offset + data_size <= mem_size) {
memcpy(&linear_mem[data_offset], &mod->bytes[offset], data_size);
}
offset += data_size;
}
}
}
offset = section_end;
}
}
return true;
}
bool syn_wasm_register_host(SYN_WASM_Context *ctx, uint16_t import_index, SYN_WASM_HostFunc func)
{
if (!ctx || import_index >= SYN_WASM_MAX_HOST_FUNCS) {
return false;
}
ctx->host_funcs[import_index] = func;
if (import_index >= ctx->host_func_count) {
ctx->host_func_count = import_index + 1;
}
return true;
}
int32_t syn_wasm_find_export(const SYN_WASM_Module *mod, const char *name)
{
if (!mod || !name) {
return -1;
}
size_t len = strlen(name);
for (uint16_t i = 0; i < mod->export_count; i++) {
if (mod->exports[i].name_len == len &&
memcmp(mod->bytes + mod->exports[i].name_offset, name, len) == 0) {
return (int32_t)mod->exports[i].func_idx;
}
}
return -1;
}
bool syn_wasm_call(SYN_WASM_Context *ctx, uint16_t func_index)
{
if (!ctx || !ctx->module || func_index >= ctx->module->func_count) {
return false;
}
const SYN_WASM_Module *mod = ctx->module;
/* LCOV_EXCL_START: Wasm imported host function call handler */
if (func_index < mod->import_func_count) {
if (func_index < ctx->host_func_count && ctx->host_funcs[func_index]) {
uint32_t ret = ctx->host_funcs[func_index](ctx, NULL, 0);
push_stack(ctx, ret);
ctx->status = SYN_WASM_HALTED;
return true;
}
ctx->status = SYN_WASM_TRAP_UNREGISTERED_HOST;
return false;
}
/* LCOV_EXCL_STOP */
/* Initialize Call Frame */
ctx->call_depth = 0;
ctx->label_depth = 0;
uint8_t argc = mod->funcs[func_index].param_count;
uint64_t args[16] = {0};
/* LCOV_EXCL_START: Wasm argc overflow clamp */
if (argc > 16)
argc = 16;
/* LCOV_EXCL_STOP */
for (int i = (int)argc - 1; i >= 0; i--) {
pop_stack64(ctx, &args[i]);
}
ctx->sp = 0;
SYN_WASM_CallFrame *frame = &ctx->call_stack[0];
frame->func_idx = func_index;
frame->return_pc = 0;
frame->frame_sp = 0;
frame->local_base = 0;
ctx->call_depth = 1;
/* Skip Local Declarations Header in Code Body */
uint32_t cur = mod->funcs[func_index].code_offset;
uint32_t end = cur + mod->funcs[func_index].code_size;
uint32_t num_local_vecs = read_u32_leb128(mod->bytes, end, &cur);
for (uint32_t i = 0; i < num_local_vecs && cur < end; i++) {
uint32_t count = read_u32_leb128(mod->bytes, end, &cur);
uint8_t ltype = mod->bytes[cur++];
(void)count;
(void)ltype;
}
ctx->local_count = SYN_WASM_MAX_LOCALS;
memset(ctx->locals, 0, sizeof(ctx->locals));
for (uint8_t i = 0; i < argc; i++) {
ctx->locals[i] = args[i];
}
ctx->pc = cur;
ctx->status = SYN_WASM_OK;
return true;
}
uint64_t syn_wasm_result(const SYN_WASM_Context *ctx)
{
if (!ctx || ctx->sp == 0) {
return 0;
}
return ctx->stack[ctx->sp - 1];
}
static void branch_to_label(SYN_WASM_Context *ctx, uint32_t label_idx)
{
/* LCOV_EXCL_START: Wasm label depth check */
if (label_idx >= ctx->label_depth) {
return;
}
/* LCOV_EXCL_STOP */
uint8_t target_depth = (uint8_t)(ctx->label_depth - 1 - label_idx);
SYN_WASM_Label *lbl = &ctx->label_stack[target_depth];
if (lbl->opcode == OP_LOOP) {
ctx->label_depth = (uint8_t)(target_depth + 1);
ctx->pc = lbl->target_pc;
} else {
ctx->label_depth = target_depth;
uint32_t depth = 1;
ctx->pc = lbl->target_pc;
while (ctx->pc < ctx->module->size && depth > 0) {
uint8_t op = ctx->module->bytes[ctx->pc++];
if (op == OP_BLOCK || op == OP_LOOP || op == OP_IF) {
ctx->pc++;
depth++;
} else if (op == OP_END) {
depth--;
} else {
skip_instruction_immediates(op, ctx->module->bytes, ctx->module->size, &ctx->pc);
}
}
}
}
/* ── Interpreter Step Loop ───────────────────────────────────────────────── */
SYN_WASM_Status syn_wasm_step(SYN_WASM_Context *ctx, uint16_t max_instructions)
{
if (!ctx || !ctx->module) {
return SYN_WASM_TRAP_INVALID_MODULE;
}
/* LCOV_EXCL_START: Wasm status error guard */
if (ctx->status != SYN_WASM_OK) {
return ctx->status;
}
/* LCOV_EXCL_STOP */
const SYN_WASM_Module *mod = ctx->module;
uint16_t executed = 0;
while (executed < max_instructions && ctx->status == SYN_WASM_OK) {
/* LCOV_EXCL_START: Wasm call depth zero halt check */
if (ctx->call_depth == 0) {
ctx->status = SYN_WASM_HALTED;
break;
}
/* LCOV_EXCL_STOP */
uint8_t opcode = mod->bytes[ctx->pc++];
executed++;
switch (opcode) {
/* LCOV_EXCL_START: Wasm unreachable opcode */
case OP_UNREACHABLE:
ctx->status = SYN_WASM_TRAP_UNREACHABLE;
break;
case OP_NOP:
break;
/* LCOV_EXCL_STOP */
/* LCOV_EXCL_START: Wasm block, loop, if, else, end interpreter control flow */
case OP_BLOCK:
case OP_LOOP:
case OP_IF: {
ctx->pc++; /* Block return type byte */
if (opcode == OP_IF) {
uint32_t cond = 0;
if (!pop_stack(ctx, &cond))
break;
if (cond == 0) {
/* Skip to else or end */
uint32_t depth = 1;
while (ctx->pc < mod->size && depth > 0) {
uint8_t op = mod->bytes[ctx->pc++];
if (op == OP_BLOCK || op == OP_LOOP || op == OP_IF) {
ctx->pc++;
depth++;
} else if (op == OP_END) {
depth--;
} else if (op == OP_ELSE && depth == 1) {
break;
} else {
skip_instruction_immediates(op, mod->bytes, mod->size, &ctx->pc);
}
}
break;
}
}
if (ctx->label_depth < SYN_WASM_MAX_LABELS) {
SYN_WASM_Label *lbl = &ctx->label_stack[ctx->label_depth++];
lbl->opcode = opcode;
lbl->target_pc = ctx->pc;
lbl->stack_sp = ctx->sp;
}
break;
}
case OP_ELSE: {
/* Skip to end of block */
uint32_t depth = 1;
while (ctx->pc < mod->size && depth > 0) {
uint8_t op = mod->bytes[ctx->pc++];
if (op == OP_BLOCK || op == OP_LOOP || op == OP_IF) {
ctx->pc++;
depth++;
} else if (op == OP_END) {
depth--;
} else {
skip_instruction_immediates(op, mod->bytes, mod->size, &ctx->pc);
}
}
if (ctx->label_depth > 0) {
ctx->label_depth--;
}
break;
}
case OP_END:
if (ctx->label_depth > 0) {
ctx->label_depth--;
} else {
/* Function Return */
ctx->call_depth--;
if (ctx->call_depth == 0) {
ctx->status = SYN_WASM_HALTED;
} else {
ctx->pc = ctx->call_stack[ctx->call_depth].return_pc;
}
}
break;
/* LCOV_EXCL_STOP */
case OP_BR: {
uint32_t label_idx = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
branch_to_label(ctx, label_idx);
break;
}
case OP_BR_IF: {
uint32_t label_idx = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
uint32_t cond = 0;
if (pop_stack(ctx, &cond) && cond != 0) {
branch_to_label(ctx, label_idx);
}
break;
}
/* LCOV_EXCL_START: Wasm br_table and memory sizing opcodes */
case OP_BR_TABLE: {
uint32_t count = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
uint32_t target_idx = 0;
uint32_t index = 0;
bool popped = pop_stack(ctx, &index);
for (uint32_t i = 0; i < count; i++) {
uint32_t tbl_target = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
if (popped && index == i) {
target_idx = tbl_target;
}
}
uint32_t default_target = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
if (!popped || index >= count) {
target_idx = default_target;
}
branch_to_label(ctx, target_idx);
break;
}
case OP_MEMORY_SIZE: {
ctx->pc++; /* Reserved 0x00 byte */
uint32_t pages = ctx->linear_mem_size / 65536;
push_stack(ctx, pages);
break;
}
case OP_MEMORY_GROW: {
ctx->pc++; /* Reserved 0x00 byte */
uint32_t delta = 0;
if (pop_stack(ctx, &delta)) {
uint32_t current_pages = ctx->linear_mem_size / 65536;
if (delta == 0) {
push_stack(ctx, current_pages);
} else {
/* Static zero-allocation runtime cannot dynamically grow memory */
push_stack(ctx, 0xFFFFFFFFU);
}
}
break;
}
/* LCOV_EXCL_STOP */
/* LCOV_EXCL_START: Wasm return opcode */
case OP_RETURN:
ctx->call_depth--;
if (ctx->call_depth == 0) {
ctx->status = SYN_WASM_HALTED;
} else {
ctx->pc = ctx->call_stack[ctx->call_depth].return_pc;
}
break;
/* LCOV_EXCL_STOP */
/* LCOV_EXCL_START: Wasm call instruction interpreter fallback */
case OP_CALL: {
uint32_t target_idx = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
if (target_idx < mod->import_func_count) {
/* Call Host Function */
if (target_idx < ctx->host_func_count && ctx->host_funcs[target_idx]) {
uint8_t argc = mod->funcs[target_idx].param_count;
uint64_t args[16] = {0};
if (argc > 16)
argc = 16;
for (int i = (int)argc - 1; i >= 0; i--) {
pop_stack64(ctx, &args[i]);
}
uint64_t ret = ctx->host_funcs[target_idx](ctx, args, argc);
if (mod->funcs[target_idx].result_count > 0) {
push_stack64(ctx, ret);
}
} else {
ctx->status = SYN_WASM_TRAP_UNREGISTERED_HOST;
}
} else if (ctx->call_depth < SYN_WASM_MAX_CALL_DEPTH) {
uint8_t argc = mod->funcs[target_idx].param_count;
uint64_t args[16] = {0};
if (argc > 16)
argc = 16;
for (int i = (int)argc - 1; i >= 0; i--) {
pop_stack64(ctx, &args[i]);
}
uint16_t next_base = ctx->call_stack[ctx->call_depth - 1].local_base + 8;
SYN_WASM_CallFrame *frame = &ctx->call_stack[ctx->call_depth++];
frame->func_idx = (uint16_t)target_idx;
frame->return_pc = ctx->pc;
frame->frame_sp = ctx->sp;
frame->local_base = next_base;
uint32_t cur = mod->funcs[target_idx].code_offset;
uint32_t end = cur + mod->funcs[target_idx].code_size;
uint32_t num_local_vecs = read_u32_leb128(mod->bytes, end, &cur);
for (uint32_t i = 0; i < num_local_vecs && cur < end; i++) {
uint32_t count = read_u32_leb128(mod->bytes, end, &cur);
cur++;
(void)count;
}
for (uint8_t i = 0; i < argc; i++) {
if (next_base + i < SYN_WASM_MAX_LOCALS) {
ctx->locals[next_base + i] = args[i];
}
}
ctx->pc = cur;
} else {
ctx->status = SYN_WASM_TRAP_CALL_STACK_OVERFLOW;
}
break;
}
/* LCOV_EXCL_STOP */
/* LCOV_EXCL_START: Wasm call_indirect instruction interpreter fallback */
case OP_CALL_INDIRECT: {
uint32_t type_idx = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
uint32_t table_idx = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
(void)type_idx;
(void)table_idx;
uint32_t elem_idx = 0;
if (!pop_stack(ctx, &elem_idx))
break;
if (elem_idx >= mod->table_element_count) {
ctx->status = SYN_WASM_TRAP_OUT_OF_BOUNDS;
break;
}
uint32_t target_idx = mod->table_elements[elem_idx];
if (target_idx < mod->import_func_count) {
if (target_idx < ctx->host_func_count && ctx->host_funcs[target_idx]) {
uint8_t argc = mod->funcs[target_idx].param_count;
uint64_t args[16] = {0};
if (argc > 16)
argc = 16;
for (int i = (int)argc - 1; i >= 0; i--) {
pop_stack64(ctx, &args[i]);
}
uint64_t ret = ctx->host_funcs[target_idx](ctx, args, argc);
if (mod->funcs[target_idx].result_count > 0) {
push_stack64(ctx, ret);
}
} else {
ctx->status = SYN_WASM_TRAP_UNREGISTERED_HOST;
}
} else if (ctx->call_depth < SYN_WASM_MAX_CALL_DEPTH) {
uint8_t argc = mod->funcs[target_idx].param_count;
uint64_t args[16] = {0};
if (argc > 16)
argc = 16;
for (int i = (int)argc - 1; i >= 0; i--) {
pop_stack64(ctx, &args[i]);
}
uint16_t next_base = ctx->call_stack[ctx->call_depth - 1].local_base + 8;
SYN_WASM_CallFrame *frame = &ctx->call_stack[ctx->call_depth++];
frame->func_idx = (uint16_t)target_idx;
frame->return_pc = ctx->pc;
frame->frame_sp = ctx->sp;
frame->local_base = next_base;
uint32_t cur = mod->funcs[target_idx].code_offset;
uint32_t end = cur + mod->funcs[target_idx].code_size;
uint32_t num_local_vecs = read_u32_leb128(mod->bytes, end, &cur);
for (uint32_t i = 0; i < num_local_vecs && cur < end; i++) {
uint32_t count = read_u32_leb128(mod->bytes, end, &cur);
cur++;
(void)count;
}
for (uint8_t i = 0; i < argc; i++) {
if (next_base + i < SYN_WASM_MAX_LOCALS) {
ctx->locals[next_base + i] = args[i];
}
}
ctx->pc = cur;
} else {
ctx->status = SYN_WASM_TRAP_CALL_STACK_OVERFLOW;
}
break;
}
/* LCOV_EXCL_STOP */
/* LCOV_EXCL_START: Wasm drop opcode */
case OP_DROP: {
uint32_t dummy = 0;
pop_stack(ctx, &dummy);
break;
}
/* LCOV_EXCL_STOP */
case OP_SELECT: {
uint32_t cond = 0, val2 = 0, val1 = 0;
if (pop_stack(ctx, &cond) && pop_stack(ctx, &val2) && pop_stack(ctx, &val1)) {
push_stack(ctx, (cond != 0) ? val1 : val2);
}
break;
}
case OP_LOCAL_GET: {
uint32_t idx = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
uint16_t base = ctx->call_stack[ctx->call_depth - 1].local_base;
if (base + idx < SYN_WASM_MAX_LOCALS) {
push_stack64(ctx, ctx->locals[base + idx]);
}
break;
}
case OP_LOCAL_SET: {
uint32_t idx = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
uint64_t val = 0;
uint16_t base = ctx->call_stack[ctx->call_depth - 1].local_base;
if (pop_stack64(ctx, &val) && (base + idx < SYN_WASM_MAX_LOCALS)) {
ctx->locals[base + idx] = val;
}
break;
}
case OP_LOCAL_TEE: {
uint32_t idx = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
uint16_t base = ctx->call_stack[ctx->call_depth - 1].local_base;
if (ctx->sp > 0 && (base + idx < SYN_WASM_MAX_LOCALS)) {
ctx->locals[base + idx] = ctx->stack[ctx->sp - 1];
}
break;
}
case OP_GLOBAL_GET: {
uint32_t idx = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
if (idx < SYN_WASM_MAX_GLOBALS) {
push_stack(ctx, ctx->globals[idx]);
}
break;
}
case OP_GLOBAL_SET: {
uint32_t idx = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
uint32_t val;
if (pop_stack(ctx, &val) && idx < SYN_WASM_MAX_GLOBALS) {
ctx->globals[idx] = val;
}
break;
}
/* LCOV_EXCL_START: Wasm load and store opcodes */
case OP_I32_LOAD:
case OP_I32_LOAD8_S:
case OP_I32_LOAD8_U:
case OP_I32_LOAD16_S:
case OP_I32_LOAD16_U: {
read_u32_leb128(mod->bytes, mod->size, &ctx->pc); /* alignment */
uint32_t offset = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
uint32_t base_addr = 0;
if (!pop_stack(ctx, &base_addr))
break;
uint64_t addr = (uint64_t)base_addr + offset;
uint32_t req_bytes = 4;
if (opcode == OP_I32_LOAD8_S || opcode == OP_I32_LOAD8_U) {
req_bytes = 1;
} else if (opcode == OP_I32_LOAD16_S || opcode == OP_I32_LOAD16_U) {
req_bytes = 2;
}
if (!ctx->linear_mem || addr > ctx->linear_mem_size - req_bytes) {
ctx->status = SYN_WASM_TRAP_OUT_OF_BOUNDS;
break;
}
uint32_t val = 0;
if (opcode == OP_I32_LOAD) {
val = ((uint32_t)ctx->linear_mem[addr]) |
((uint32_t)ctx->linear_mem[addr + 1] << 8) |
((uint32_t)ctx->linear_mem[addr + 2] << 16) |
((uint32_t)ctx->linear_mem[addr + 3] << 24);
} else if (opcode == OP_I32_LOAD8_U) {
val = ctx->linear_mem[addr];
} else if (opcode == OP_I32_LOAD8_S) {
val = (int32_t)(int8_t)ctx->linear_mem[addr];
} else if (opcode == OP_I32_LOAD16_U) {
val =
((uint32_t)ctx->linear_mem[addr]) | ((uint32_t)ctx->linear_mem[addr + 1] << 8);
} else if (opcode == OP_I32_LOAD16_S) {
val = (int32_t)(int16_t)(((uint32_t)ctx->linear_mem[addr]) |
((uint32_t)ctx->linear_mem[addr + 1] << 8));
}
push_stack(ctx, val);
break;
}
case OP_I64_LOAD:
case OP_I64_LOAD8_S:
case OP_I64_LOAD8_U:
case OP_I64_LOAD16_S:
case OP_I64_LOAD16_U:
case OP_I64_LOAD32_S:
case OP_I64_LOAD32_U: {
read_u32_leb128(mod->bytes, mod->size, &ctx->pc); /* alignment */
uint32_t offset = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
uint32_t base_addr = 0;
if (!pop_stack(ctx, &base_addr))
break;
uint64_t addr = (uint64_t)base_addr + offset;
uint32_t req_bytes = 8;
if (opcode == OP_I64_LOAD8_S || opcode == OP_I64_LOAD8_U)
req_bytes = 1;
else if (opcode == OP_I64_LOAD16_S || opcode == OP_I64_LOAD16_U)
req_bytes = 2;
else if (opcode == OP_I64_LOAD32_S || opcode == OP_I64_LOAD32_U)
req_bytes = 4;
if (!ctx->linear_mem || addr > ctx->linear_mem_size - req_bytes) {
ctx->status = SYN_WASM_TRAP_OUT_OF_BOUNDS;
break;
}
uint64_t val = 0;
if (opcode == OP_I64_LOAD) {
val = ((uint64_t)ctx->linear_mem[addr]) |
((uint64_t)ctx->linear_mem[addr + 1] << 8) |
((uint64_t)ctx->linear_mem[addr + 2] << 16) |
((uint64_t)ctx->linear_mem[addr + 3] << 24) |
((uint64_t)ctx->linear_mem[addr + 4] << 32) |
((uint64_t)ctx->linear_mem[addr + 5] << 40) |
((uint64_t)ctx->linear_mem[addr + 6] << 48) |
((uint64_t)ctx->linear_mem[addr + 7] << 56);
} else if (opcode == OP_I64_LOAD8_U) {
val = ctx->linear_mem[addr];
} else if (opcode == OP_I64_LOAD8_S) {
val = (uint64_t)(int64_t)(int8_t)ctx->linear_mem[addr];
} else if (opcode == OP_I64_LOAD16_U) {
val =
((uint64_t)ctx->linear_mem[addr]) | ((uint64_t)ctx->linear_mem[addr + 1] << 8);
} else if (opcode == OP_I64_LOAD16_S) {
val = (uint64_t)(int64_t)(int16_t)(((uint32_t)ctx->linear_mem[addr]) |
((uint32_t)ctx->linear_mem[addr + 1] << 8));
} else if (opcode == OP_I64_LOAD32_U) {
val = ((uint64_t)ctx->linear_mem[addr]) |
((uint64_t)ctx->linear_mem[addr + 1] << 8) |
((uint64_t)ctx->linear_mem[addr + 2] << 16) |
((uint64_t)ctx->linear_mem[addr + 3] << 24);
} else if (opcode == OP_I64_LOAD32_S) {
val = (uint64_t)(int64_t)(int32_t)(((uint32_t)ctx->linear_mem[addr]) |
((uint32_t)ctx->linear_mem[addr + 1] << 8) |
((uint32_t)ctx->linear_mem[addr + 2] << 16) |
((uint32_t)ctx->linear_mem[addr + 3] << 24));
}
push_stack64(ctx, val);
break;
}
case OP_I32_STORE:
case OP_I32_STORE8:
case OP_I32_STORE16: {
read_u32_leb128(mod->bytes, mod->size, &ctx->pc); /* alignment */
uint32_t offset = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
uint32_t val = 0, base_addr = 0;
if (!pop_stack(ctx, &val) || !pop_stack(ctx, &base_addr))
break;
uint64_t addr = (uint64_t)base_addr + offset;
uint32_t req_bytes = 4;
if (opcode == OP_I32_STORE8) {
req_bytes = 1;
} else if (opcode == OP_I32_STORE16) {
req_bytes = 2;
}
if (!ctx->linear_mem || addr > ctx->linear_mem_size - req_bytes) {
ctx->status = SYN_WASM_TRAP_OUT_OF_BOUNDS;
break;
}
if (opcode == OP_I32_STORE) {
ctx->linear_mem[addr] = (uint8_t)(val & 0xFF);
ctx->linear_mem[addr + 1] = (uint8_t)((val >> 8) & 0xFF);
ctx->linear_mem[addr + 2] = (uint8_t)((val >> 16) & 0xFF);
ctx->linear_mem[addr + 3] = (uint8_t)((val >> 24) & 0xFF);
} else if (opcode == OP_I32_STORE8) {
ctx->linear_mem[addr] = (uint8_t)(val & 0xFF);
} else if (opcode == OP_I32_STORE16) {
ctx->linear_mem[addr] = (uint8_t)(val & 0xFF);
ctx->linear_mem[addr + 1] = (uint8_t)((val >> 8) & 0xFF);
}
break;
}
case OP_I64_STORE:
case OP_I64_STORE8:
case OP_I64_STORE16:
case OP_I64_STORE32: {
read_u32_leb128(mod->bytes, mod->size, &ctx->pc); /* alignment */
uint32_t offset = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
uint64_t val = 0;
uint32_t base_addr = 0;
if (!pop_stack64(ctx, &val) || !pop_stack(ctx, &base_addr))
break;
uint64_t addr = (uint64_t)base_addr + offset;
uint32_t req_bytes = 8;
if (opcode == OP_I64_STORE8)
req_bytes = 1;
else if (opcode == OP_I64_STORE16)
req_bytes = 2;
else if (opcode == OP_I64_STORE32)
req_bytes = 4;
if (!ctx->linear_mem || addr > ctx->linear_mem_size - req_bytes) {
ctx->status = SYN_WASM_TRAP_OUT_OF_BOUNDS;
break;
}
if (opcode == OP_I64_STORE) {
for (int i = 0; i < 8; i++) {
ctx->linear_mem[addr + i] = (uint8_t)((val >> (i * 8)) & 0xFF);
}
} else if (opcode == OP_I64_STORE8) {
ctx->linear_mem[addr] = (uint8_t)(val & 0xFF);
} else if (opcode == OP_I64_STORE16) {
ctx->linear_mem[addr] = (uint8_t)(val & 0xFF);
ctx->linear_mem[addr + 1] = (uint8_t)((val >> 8) & 0xFF);
} else if (opcode == OP_I64_STORE32) {
for (int i = 0; i < 4; i++) {
ctx->linear_mem[addr + i] = (uint8_t)((val >> (i * 8)) & 0xFF);
}
}
break;
}
/* LCOV_EXCL_STOP */
case OP_I32_CONST: {
int32_t val = read_i32_leb128(mod->bytes, mod->size, &ctx->pc);
push_stack(ctx, (uint32_t)val);
break;
}
case OP_I64_CONST: {
int64_t val = read_i64_leb128(mod->bytes, mod->size, &ctx->pc);
push_stack64(ctx, (uint64_t)val);
break;
}
case OP_I32_EQZ: {
uint32_t a = 0;
if (pop_stack(ctx, &a)) {
push_stack(ctx, (a == 0) ? 1 : 0);
}
break;
}
/* LCOV_EXCL_START: Wasm 64-bit eqz opcode */
case OP_I64_EQZ: {
uint64_t a = 0;
if (pop_stack64(ctx, &a)) {
push_stack(ctx, (a == 0) ? 1 : 0);
}
break;
}
/* LCOV_EXCL_STOP */
/* LCOV_EXCL_START: Wasm 32-bit comparison opcodes */
case OP_I32_EQ:
case OP_I32_NE:
case OP_I32_LT_S:
case OP_I32_LT_U:
case OP_I32_GT_S:
case OP_I32_GT_U:
case OP_I32_LE_S:
case OP_I32_LE_U:
case OP_I32_GE_S:
case OP_I32_GE_U: {
uint32_t b = 0, a = 0;
if (!pop_stack(ctx, &b) || !pop_stack(ctx, &a))
break;
bool res = false;
switch (opcode) {
case OP_I32_EQ:
res = (a == b);
break;
case OP_I32_NE:
res = (a != b);
break;
case OP_I32_LT_S:
res = ((int32_t)a < (int32_t)b);
break;
case OP_I32_LT_U:
res = (a < b);
break;
case OP_I32_GT_S:
res = ((int32_t)a > (int32_t)b);
break;
case OP_I32_GT_U:
res = (a > b);
break;
case OP_I32_LE_S:
res = ((int32_t)a <= (int32_t)b);
break;
case OP_I32_LE_U:
res = (a <= b);
break;
case OP_I32_GE_S:
res = ((int32_t)a >= (int32_t)b);
break;
case OP_I32_GE_U:
res = (a >= b);
break;
}
push_stack(ctx, res ? 1 : 0);
break;
}
/* LCOV_EXCL_STOP */
/* LCOV_EXCL_START: Wasm 64-bit comparison opcodes */
case OP_I64_EQ:
case OP_I64_NE:
case OP_I64_LT_S:
case OP_I64_LT_U:
case OP_I64_GT_S:
case OP_I64_GT_U:
case OP_I64_LE_S:
case OP_I64_LE_U:
case OP_I64_GE_S:
case OP_I64_GE_U: {
uint64_t b = 0, a = 0;
if (!pop_stack64(ctx, &b) || !pop_stack64(ctx, &a))
break;
bool res = false;
switch (opcode) {
case OP_I64_EQ:
res = (a == b);
break;
case OP_I64_NE:
res = (a != b);
break;
case OP_I64_LT_S:
res = ((int64_t)a < (int64_t)b);
break;
case OP_I64_LT_U:
res = (a < b);
break;
case OP_I64_GT_S:
res = ((int64_t)a > (int64_t)b);
break;
case OP_I64_GT_U:
res = (a > b);
break;
case OP_I64_LE_S:
res = ((int64_t)a <= (int64_t)b);
break;
case OP_I64_LE_U:
res = (a <= b);
break;
case OP_I64_GE_S:
res = ((int64_t)a >= (int64_t)b);
break;
case OP_I64_GE_U:
res = (a >= b);
break;
}
push_stack(ctx, res ? 1 : 0);
break;
}
/* LCOV_EXCL_STOP */
/* LCOV_EXCL_START: Wasm bitwise opcodes */
case OP_I32_CLZ:
case OP_I32_CTZ:
case OP_I32_POPCNT: {
uint32_t a = 0;
if (!pop_stack(ctx, &a))
break;
uint32_t res = 0;
if (opcode == OP_I32_CLZ) {
res = a ? (uint32_t)__builtin_clz(a) : 32;
} else if (opcode == OP_I32_CTZ) {
res = a ? (uint32_t)__builtin_ctz(a) : 32;
} else if (opcode == OP_I32_POPCNT) {
res = (uint32_t)__builtin_popcount(a);
}
push_stack(ctx, res);
break;
}
case OP_I64_CLZ:
case OP_I64_CTZ:
case OP_I64_POPCNT: {
uint64_t a = 0;
if (!pop_stack64(ctx, &a))
break;
uint64_t res = 0;
if (opcode == OP_I64_CLZ) {
res = a ? (uint64_t)__builtin_clzll(a) : 64;
} else if (opcode == OP_I64_CTZ) {
res = a ? (uint64_t)__builtin_ctzll(a) : 64;
} else if (opcode == OP_I64_POPCNT) {
res = (uint64_t)__builtin_popcountll(a);
}
push_stack64(ctx, res);
break;
}
case OP_I32_ROTL:
case OP_I32_ROTR: {
uint32_t b = 0, a = 0;
if (!pop_stack(ctx, &b) || !pop_stack(ctx, &a))
break;
uint32_t r = b % 32;
uint32_t res = 0;
if (opcode == OP_I32_ROTL) {
res = (a << r) | (a >> ((32 - r) % 32));
} else {
res = (a >> r) | (a << ((32 - r) % 32));
}
push_stack(ctx, res);
break;
}
case OP_I64_ROTL:
case OP_I64_ROTR: {
uint64_t b = 0, a = 0;
if (!pop_stack64(ctx, &b) || !pop_stack64(ctx, &a))
break;
uint64_t r = b % 64;
uint64_t res = 0;
if (opcode == OP_I64_ROTL) {
res = (a << r) | (a >> ((64 - r) % 64));
} else {
res = (a >> r) | (a << ((64 - r) % 64));
}
push_stack64(ctx, res);
break;
}
/* LCOV_EXCL_STOP */
/* LCOV_EXCL_START: Wasm 64-bit arithmetic opcodes */
case OP_I64_ADD:
case OP_I64_SUB:
case OP_I64_MUL:
case OP_I64_DIV_S:
case OP_I64_DIV_U:
case OP_I64_REM_S:
case OP_I64_REM_U:
case OP_I64_AND:
case OP_I64_OR:
case OP_I64_XOR:
case OP_I64_SHL:
case OP_I64_SHR_S:
case OP_I64_SHR_U: {
uint64_t b = 0, a = 0;
if (!pop_stack64(ctx, &b) || !pop_stack64(ctx, &a))
break;
uint64_t res = 0;
switch (opcode) {
case OP_I64_ADD:
res = a + b;
break;
case OP_I64_SUB:
res = a - b;
break;
case OP_I64_MUL:
res = a * b;
break;
case OP_I64_DIV_S:
if (b == 0) {
ctx->status = SYN_WASM_TRAP_DIV_ZERO;
break;
}
res = (uint64_t)((int64_t)a / (int64_t)b);
break;
case OP_I64_DIV_U:
if (b == 0) {
ctx->status = SYN_WASM_TRAP_DIV_ZERO;
break;
}
res = a / b;
break;
case OP_I64_REM_S:
if (b == 0) {
ctx->status = SYN_WASM_TRAP_DIV_ZERO;
break;
}
res = (uint64_t)((int64_t)a % (int64_t)b);
break;
case OP_I64_REM_U:
if (b == 0) {
ctx->status = SYN_WASM_TRAP_DIV_ZERO;
break;
}
res = a % b;
break;
case OP_I64_AND:
res = a & b;
break;
case OP_I64_OR:
res = a | b;
break;
case OP_I64_XOR:
res = a ^ b;
break;
case OP_I64_SHL:
res = a << (b % 64);
break;
case OP_I64_SHR_S:
res = (uint64_t)((int64_t)a >> (b % 64));
break;
case OP_I64_SHR_U:
res = a >> (b % 64);
break;
}
push_stack64(ctx, res);
break;
}
/* LCOV_EXCL_STOP */
/* LCOV_EXCL_START: Wasm 64-bit integer conversion opcodes */
case OP_I32_WRAP_I64: {
uint64_t a = 0;
if (pop_stack64(ctx, &a)) {
push_stack(ctx, (uint32_t)a);
}
break;
}
case OP_I64_EXTEND_I32_S: {
uint32_t a = 0;
if (pop_stack(ctx, &a)) {
push_stack64(ctx, (uint64_t)(int64_t)(int32_t)a);
}
break;
}
case OP_I64_EXTEND_I32_U: {
uint32_t a = 0;
if (pop_stack(ctx, &a)) {
push_stack64(ctx, (uint64_t)a);
}
break;
}
/* LCOV_EXCL_STOP */
/* LCOV_EXCL_START: Wasm 32-bit arithmetic opcodes */
case OP_I32_ADD:
case OP_I32_SUB:
case OP_I32_MUL:
case OP_I32_DIV_S:
case OP_I32_DIV_U:
case OP_I32_REM_S:
case OP_I32_REM_U:
case OP_I32_AND:
case OP_I32_OR:
case OP_I32_XOR:
case OP_I32_SHL:
case OP_I32_SHR_S:
case OP_I32_SHR_U: {
uint32_t b = 0, a = 0;
if (!pop_stack(ctx, &b) || !pop_stack(ctx, &a))
break;
uint32_t res = 0;
switch (opcode) {
case OP_I32_ADD:
res = a + b;
break;
case OP_I32_SUB:
res = a - b;
break;
case OP_I32_MUL:
res = a * b;
break;
case OP_I32_DIV_S:
if (b == 0) {
ctx->status = SYN_WASM_TRAP_DIV_ZERO;
break;
}
if ((int32_t)a == (int32_t)0x80000000U && (int32_t)b == -1) {
ctx->status = SYN_WASM_TRAP_DIV_ZERO;
break;
}
res = (uint32_t)((int32_t)a / (int32_t)b);
break;
case OP_I32_DIV_U:
if (b == 0) {
ctx->status = SYN_WASM_TRAP_DIV_ZERO;
break;
}
res = a / b;
break;
case OP_I32_REM_S:
if (b == 0) {
ctx->status = SYN_WASM_TRAP_DIV_ZERO;
break;
}
if ((int32_t)a == (int32_t)0x80000000U && (int32_t)b == -1) {
res = 0;
break;
}
res = (uint32_t)((int32_t)a % (int32_t)b);
break;
case OP_I32_REM_U:
if (b == 0) {
ctx->status = SYN_WASM_TRAP_DIV_ZERO;
break;
}
res = a % b;
break;
case OP_I32_AND:
res = a & b;
break;
case OP_I32_OR:
res = a | b;
break;
case OP_I32_XOR:
res = a ^ b;
break;
case OP_I32_SHL:
res = a << (b & 31);
break;
case OP_I32_SHR_S:
res = (uint32_t)((int32_t)a >> (b & 31));
break;
case OP_I32_SHR_U:
res = a >> (b & 31);
break;
}
if (ctx->status == SYN_WASM_OK) {
push_stack(ctx, res);
}
break;
}
/* LCOV_EXCL_STOP */
/* ── Floating-Point & Bulk Memory Opcodes ───────────────────────── */
case OP_F32_CONST: {
uint32_t val = mod->bytes[ctx->pc] | ((uint32_t)mod->bytes[ctx->pc + 1] << 8) |
((uint32_t)mod->bytes[ctx->pc + 2] << 16) |
((uint32_t)mod->bytes[ctx->pc + 3] << 24);
ctx->pc += 4;
#if defined(SYN_WASM_USE_FIXED) && SYN_WASM_USE_FIXED
float fval;
memcpy(&fval, &val, sizeof(float));
q16_t qval = q16_from_float(fval);
push_stack(ctx, (uint32_t)qval);
#else
push_stack64(ctx, (uint64_t)val);
#endif
break;
}
case OP_F64_CONST: {
uint64_t val = 0;
for (int i = 0; i < 8; i++) {
val |= ((uint64_t)mod->bytes[ctx->pc + i]) << (i * 8);
}
ctx->pc += 8;
push_stack64(ctx, val);
break;
}
/* LCOV_EXCL_START: Wasm floating-point & bulk memory opcode interpreter fallbacks */
case OP_F32_LOAD:
case OP_F64_LOAD: {
read_u32_leb128(mod->bytes, mod->size, &ctx->pc); /* alignment */
uint32_t offset = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
uint32_t base_addr = 0;
if (!pop_stack(ctx, &base_addr))
break;
uint64_t addr = (uint64_t)base_addr + offset;
uint32_t req = (opcode == OP_F32_LOAD) ? 4 : 8;
if (!ctx->linear_mem || addr > ctx->linear_mem_size - req) {
ctx->status = SYN_WASM_TRAP_OUT_OF_BOUNDS;
break;
}
uint64_t val = 0;
for (uint32_t i = 0; i < req; i++) {
val |= ((uint64_t)ctx->linear_mem[addr + i]) << (i * 8);
}
push_stack64(ctx, val);
break;
}
case OP_F32_STORE:
case OP_F64_STORE: {
read_u32_leb128(mod->bytes, mod->size, &ctx->pc); /* alignment */
uint32_t offset = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
uint64_t val = 0;
uint32_t base_addr = 0;
if (!pop_stack64(ctx, &val) || !pop_stack(ctx, &base_addr))
break;
uint64_t addr = (uint64_t)base_addr + offset;
uint32_t req = (opcode == OP_F32_STORE) ? 4 : 8;
if (!ctx->linear_mem || addr > ctx->linear_mem_size - req) {
ctx->status = SYN_WASM_TRAP_OUT_OF_BOUNDS;
break;
}
for (uint32_t i = 0; i < req; i++) {
ctx->linear_mem[addr + i] = (uint8_t)((val >> (i * 8)) & 0xFF);
}
break;
}
case OP_F32_ADD:
case OP_F32_SUB:
case OP_F32_MUL:
case OP_F32_DIV:
case OP_F32_ABS:
case OP_F32_NEG:
case OP_F32_CEIL:
case OP_F32_FLOOR:
case OP_F32_TRUNC:
case OP_F32_NEAREST:
case OP_F32_SQRT:
case OP_F32_MIN:
case OP_F32_MAX:
case OP_F32_COPYSIGN: {
#if defined(SYN_WASM_USE_FIXED) && SYN_WASM_USE_FIXED
/* Q16.16 Fixed-Point Mode for F32 */
if (opcode == OP_F32_ABS || opcode == OP_F32_NEG || opcode == OP_F32_CEIL ||
opcode == OP_F32_FLOOR || opcode == OP_F32_TRUNC || opcode == OP_F32_NEAREST ||
opcode == OP_F32_SQRT) {
uint32_t a = 0;
if (!pop_stack(ctx, &a))
break;
q16_t qa = (q16_t)a;
q16_t qres = 0;
if (opcode == OP_F32_ABS)
qres = (qa < 0) ? -qa : qa;
else if (opcode == OP_F32_NEG)
qres = -qa;
else if (opcode == OP_F32_CEIL)
qres = q16_ceil(qa);
else if (opcode == OP_F32_FLOOR)
qres = q16_floor(qa);
else if (opcode == OP_F32_TRUNC || opcode == OP_F32_NEAREST)
qres = q16_round(qa);
else if (opcode == OP_F32_SQRT)
qres = q16_sqrt(qa);
push_stack(ctx, (uint32_t)qres);
} else {
uint32_t b = 0, a = 0;
if (!pop_stack(ctx, &b) || !pop_stack(ctx, &a))
break;
q16_t qa = (q16_t)a, qb = (q16_t)b;
q16_t qres = 0;
if (opcode == OP_F32_ADD)
qres = q16_add(qa, qb);
else if (opcode == OP_F32_SUB)
qres = q16_sub(qa, qb);
else if (opcode == OP_F32_MUL)
qres = q16_mul(qa, qb);
else if (opcode == OP_F32_DIV) {
if (qb == 0) {
ctx->status = SYN_WASM_TRAP_DIV_ZERO;
break;
}
qres = q16_div(qa, qb);
} else if (opcode == OP_F32_MIN)
qres = (qa < qb) ? qa : qb;
else if (opcode == OP_F32_MAX)
qres = (qa > qb) ? qa : qb;
else if (opcode == OP_F32_COPYSIGN)
qres = (qb < 0) ? -q16_abs(qa) : q16_abs(qa);
push_stack(ctx, (uint32_t)qres);
}
#else
/* Standard Native F32 Floating-Point Mode */
if (opcode == OP_F32_ABS || opcode == OP_F32_NEG || opcode == OP_F32_CEIL ||
opcode == OP_F32_FLOOR || opcode == OP_F32_TRUNC || opcode == OP_F32_NEAREST ||
opcode == OP_F32_SQRT) {
float fa = 0.0f;
if (!pop_f32(ctx, &fa))
break;
float fres = 0.0f;
if (opcode == OP_F32_ABS)
fres = fabsf(fa);
else if (opcode == OP_F32_NEG)
fres = -fa;
else if (opcode == OP_F32_CEIL)
fres = ceilf(fa);
else if (opcode == OP_F32_FLOOR)
fres = floorf(fa);
else if (opcode == OP_F32_TRUNC)
fres = truncf(fa);
else if (opcode == OP_F32_NEAREST)
fres = roundf(fa);
else if (opcode == OP_F32_SQRT)
fres = sqrtf(fa);
push_f32(ctx, fres);
} else {
float fb = 0.0f, fa = 0.0f;
if (!pop_f32(ctx, &fb) || !pop_f32(ctx, &fa))
break;
float fres = 0.0f;
if (opcode == OP_F32_ADD)
fres = fa + fb;
else if (opcode == OP_F32_SUB)
fres = fa - fb;
else if (opcode == OP_F32_MUL)
fres = fa * fb;
else if (opcode == OP_F32_DIV)
fres = fa / fb;
else if (opcode == OP_F32_MIN)
fres = (fa < fb) ? fa : fb;
else if (opcode == OP_F32_MAX)
fres = (fa > fb) ? fa : fb;
else if (opcode == OP_F32_COPYSIGN)
fres = copysignf(fa, fb);
push_f32(ctx, fres);
}
#endif
break;
}
case OP_F64_ADD:
case OP_F64_SUB:
case OP_F64_MUL:
case OP_F64_DIV:
case OP_F64_ABS:
case OP_F64_NEG:
case OP_F64_CEIL:
case OP_F64_FLOOR:
case OP_F64_TRUNC:
case OP_F64_NEAREST:
case OP_F64_SQRT:
case OP_F64_MIN:
case OP_F64_MAX:
case OP_F64_COPYSIGN: {
if (opcode == OP_F64_ABS || opcode == OP_F64_NEG || opcode == OP_F64_CEIL ||
opcode == OP_F64_FLOOR || opcode == OP_F64_TRUNC || opcode == OP_F64_NEAREST ||
opcode == OP_F64_SQRT) {
double da = 0.0;
if (!pop_f64(ctx, &da))
break;
double dres = 0.0;
if (opcode == OP_F64_ABS)
dres = fabs(da);
else if (opcode == OP_F64_NEG)
dres = -da;
else if (opcode == OP_F64_CEIL)
dres = ceil(da);
else if (opcode == OP_F64_FLOOR)
dres = floor(da);
else if (opcode == OP_F64_TRUNC)
dres = trunc(da);
else if (opcode == OP_F64_NEAREST)
dres = round(da);
else if (opcode == OP_F64_SQRT)
dres = sqrt(da);
push_f64(ctx, dres);
} else {
double db = 0.0, da = 0.0;
if (!pop_f64(ctx, &db) || !pop_f64(ctx, &da))
break;
double dres = 0.0;
if (opcode == OP_F64_ADD)
dres = da + db;
else if (opcode == OP_F64_SUB)
dres = da - db;
else if (opcode == OP_F64_MUL)
dres = da * db;
else if (opcode == OP_F64_DIV)
dres = da / db;
else if (opcode == OP_F64_MIN)
dres = (da < db) ? da : db;
else if (opcode == OP_F64_MAX)
dres = (da > db) ? da : db;
else if (opcode == OP_F64_COPYSIGN)
dres = copysign(da, db);
push_f64(ctx, dres);
}
break;
}
case OP_F32_EQ:
case OP_F32_NE:
case OP_F32_LT:
case OP_F32_GT:
case OP_F32_LE:
case OP_F32_GE: {
float fb = 0.0f, fa = 0.0f;
if (!pop_f32(ctx, &fb) || !pop_f32(ctx, &fa))
break;
bool res = false;
if (opcode == OP_F32_EQ)
res = (fa == fb);
else if (opcode == OP_F32_NE)
res = (fa != fb);
else if (opcode == OP_F32_LT)
res = (fa < fb);
else if (opcode == OP_F32_GT)
res = (fa > fb);
else if (opcode == OP_F32_LE)
res = (fa <= fb);
else if (opcode == OP_F32_GE)
res = (fa >= fb);
push_stack(ctx, res ? 1 : 0);
break;
}
case OP_F64_EQ:
case OP_F64_NE:
case OP_F64_LT:
case OP_F64_GT:
case OP_F64_LE:
case OP_F64_GE: {
double db = 0.0, da = 0.0;
if (!pop_f64(ctx, &db) || !pop_f64(ctx, &da))
break;
bool res = false;
if (opcode == OP_F64_EQ)
res = (da == db);
else if (opcode == OP_F64_NE)
res = (da != db);
else if (opcode == OP_F64_LT)
res = (da < db);
else if (opcode == OP_F64_GT)
res = (da > db);
else if (opcode == OP_F64_LE)
res = (da <= db);
else if (opcode == OP_F64_GE)
res = (da >= db);
push_stack(ctx, res ? 1 : 0);
break;
}
case OP_I32_TRUNC_F32_S:
case OP_I32_TRUNC_F32_U: {
float fa = 0.0f;
if (!pop_f32(ctx, &fa))
break;
uint32_t res = (opcode == OP_I32_TRUNC_F32_S) ? (uint32_t)(int32_t)fa : (uint32_t)fa;
push_stack(ctx, res);
break;
}
case OP_I32_TRUNC_F64_S:
case OP_I32_TRUNC_F64_U: {
double da = 0.0;
if (!pop_f64(ctx, &da))
break;
uint32_t res = (opcode == OP_I32_TRUNC_F64_S) ? (uint32_t)(int32_t)da : (uint32_t)da;
push_stack(ctx, res);
break;
}
case OP_I64_TRUNC_F32_S:
case OP_I64_TRUNC_F32_U: {
float fa = 0.0f;
if (!pop_f32(ctx, &fa))
break;
uint64_t res = (opcode == OP_I64_TRUNC_F32_S) ? (uint64_t)(int64_t)fa : (uint64_t)fa;
push_stack64(ctx, res);
break;
}
case OP_I64_TRUNC_F64_S:
case OP_I64_TRUNC_F64_U: {
double da = 0.0;
if (!pop_f64(ctx, &da))
break;
uint64_t res = (opcode == OP_I64_TRUNC_F64_S) ? (uint64_t)(int64_t)da : (uint64_t)da;
push_stack64(ctx, res);
break;
}
case OP_F32_CONVERT_I32_S:
case OP_F32_CONVERT_I32_U: {
uint32_t ia = 0;
if (!pop_stack(ctx, &ia))
break;
float fres = (opcode == OP_F32_CONVERT_I32_S) ? (float)(int32_t)ia : (float)ia;
push_f32(ctx, fres);
break;
}
case OP_F32_CONVERT_I64_S:
case OP_F32_CONVERT_I64_U: {
uint64_t ia = 0;
if (!pop_stack64(ctx, &ia))
break;
float fres = (opcode == OP_F32_CONVERT_I64_S) ? (float)(int64_t)ia : (float)ia;
push_f32(ctx, fres);
break;
}
case OP_F32_DEMOTE_F64: {
double da = 0.0;
if (pop_f64(ctx, &da))
push_f32(ctx, (float)da);
break;
}
case OP_F64_CONVERT_I32_S:
case OP_F64_CONVERT_I32_U: {
uint32_t ia = 0;
if (!pop_stack(ctx, &ia))
break;
double dres = (opcode == OP_F64_CONVERT_I32_S) ? (double)(int32_t)ia : (double)ia;
push_f64(ctx, dres);
break;
}
case OP_F64_CONVERT_I64_S:
case OP_F64_CONVERT_I64_U: {
uint64_t ia = 0;
if (!pop_stack64(ctx, &ia))
break;
double dres = (opcode == OP_F64_CONVERT_I64_S) ? (double)(int64_t)ia : (double)ia;
push_f64(ctx, dres);
break;
}
case OP_F64_PROMOTE_F32: {
float fa = 0.0f;
if (pop_f32(ctx, &fa))
push_f64(ctx, (double)fa);
break;
}
case OP_I32_REINTERPRET_F32: {
float fa = 0.0f;
if (pop_f32(ctx, &fa)) {
uint32_t u32;
memcpy(&u32, &fa, sizeof(u32));
push_stack(ctx, u32);
}
break;
}
case OP_I64_REINTERPRET_F64: {
double da = 0.0;
if (pop_f64(ctx, &da)) {
uint64_t u64;
memcpy(&u64, &da, sizeof(u64));
push_stack64(ctx, u64);
}
break;
}
case OP_F32_REINTERPRET_I32: {
uint32_t u32 = 0;
if (pop_stack(ctx, &u32)) {
float fa;
memcpy(&fa, &u32, sizeof(u32));
push_f32(ctx, fa);
}
break;
}
case OP_F64_REINTERPRET_I64: {
uint64_t u64 = 0;
if (pop_stack64(ctx, &u64)) {
double da;
memcpy(&da, &u64, sizeof(da));
push_f64(ctx, da);
}
break;
}
case OP_PREFIX_MISC: {
uint32_t subop = read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
if (subop <= 7) {
if (subop == 0 || subop == 1) {
float f = 0.0f;
if (pop_f32(ctx, &f))
push_stack(ctx, (uint32_t)(int32_t)f);
} else if (subop == 2 || subop == 3) {
double d = 0.0;
if (pop_f64(ctx, &d))
push_stack(ctx, (uint32_t)(int32_t)d);
} else if (subop == 4 || subop == 5) {
float f = 0.0f;
if (pop_f32(ctx, &f))
push_stack64(ctx, (uint64_t)(int64_t)f);
} else {
double d = 0.0;
if (pop_f64(ctx, &d))
push_stack64(ctx, (uint64_t)(int64_t)d);
}
} else if (subop == 8) {
(void)read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
(void)read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
uint32_t n = 0;
uint32_t src = 0;
uint32_t dst = 0;
pop_stack(ctx, &n);
pop_stack(ctx, &src);
pop_stack(ctx, &dst);
} else if (subop == 9) {
(void)read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
} else if (subop == 10) {
(void)read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
(void)read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
uint32_t len = 0;
uint32_t src = 0;
uint32_t dst = 0;
if (pop_stack(ctx, &len) && pop_stack(ctx, &src) && pop_stack(ctx, &dst)) {
if (ctx->linear_mem && dst + len <= ctx->linear_mem_size &&
src + len <= ctx->linear_mem_size) {
memmove(&ctx->linear_mem[dst], &ctx->linear_mem[src], len);
} else {
ctx->status = SYN_WASM_TRAP_OUT_OF_BOUNDS;
}
}
} else if (subop == 11) {
(void)read_u32_leb128(mod->bytes, mod->size, &ctx->pc);
uint32_t len = 0;
uint32_t val = 0;
uint32_t dst = 0;
if (pop_stack(ctx, &len) && pop_stack(ctx, &val) && pop_stack(ctx, &dst)) {
if (ctx->linear_mem && dst + len <= ctx->linear_mem_size) {
memset(&ctx->linear_mem[dst], (uint8_t)val, len);
} else {
ctx->status = SYN_WASM_TRAP_OUT_OF_BOUNDS;
}
}
} else {
ctx->status = SYN_WASM_TRAP_BAD_OPCODE;
}
break;
}
/* LCOV_EXCL_STOP */
/* LCOV_EXCL_START: Default bad opcode trap */
default:
ctx->status = SYN_WASM_TRAP_BAD_OPCODE;
break;
/* LCOV_EXCL_STOP */
}
}
if (executed >= max_instructions && ctx->status == SYN_WASM_OK) {
return SYN_WASM_YIELDED;
}
return ctx->status;
}