use crate::clang::clang_wasm_types::{self as wasm_types};
use crate::clang::clang_wasm_types::{
ArraySubscriptExpr, AssignmentExpr, BinOp, BinaryExpr, CallExpr, CastExpr, CompoundStmt,
DoWhileStmt, Expr, ExternFunctionDecl, ForInit, ForStmt, GotoStmt, IfStmt, LabeledStmt,
MemberAccessExpr, ReturnStmt, Stmt, SwitchStmt, TernaryExpr, Type, TypeKind, UnaryExpr,
UnaryOp, WhileStmt,
};
use crate::clang::*;
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::io::{self, Cursor};
pub const WASM_PAGE_SIZE: u64 = 65536;
pub const WASM32_MAX_PAGES: u64 = 65536;
pub const WASM64_MAX_PAGES: u64 = 1 << 48;
pub const DEFAULT_MEMORY_PAGES: u64 = 256;
pub const DEFAULT_MAX_MEMORY_PAGES: u64 = 65536;
pub const WASM_STACK_SIZE: u64 = 5 * 1024 * 1024;
pub const WASM_MAX_TABLE_SIZE: u32 = 10_000_000;
pub const WASM_MAGIC: u32 = 0x6d736100;
pub const WASM_VERSION: u32 = 1;
pub const WASM_MAX_FUNCTIONS: u32 = 1_000_000;
pub const WASM_MAX_LOCALS: u32 = 50000;
pub const WASM_MAX_GLOBALS: u32 = 1_000_000;
pub const WASM_SECTION_TYPE: u8 = 1;
pub const WASM_SECTION_IMPORT: u8 = 2;
pub const WASM_SECTION_FUNCTION: u8 = 3;
pub const WASM_SECTION_TABLE: u8 = 4;
pub const WASM_SECTION_MEMORY: u8 = 5;
pub const WASM_SECTION_GLOBAL: u8 = 6;
pub const WASM_SECTION_EXPORT: u8 = 7;
pub const WASM_SECTION_START: u8 = 8;
pub const WASM_SECTION_ELEMENT: u8 = 9;
pub const WASM_SECTION_CODE: u8 = 10;
pub const WASM_SECTION_DATA: u8 = 11;
pub const WASM_SECTION_CUSTOM: u8 = 0;
pub const WASM_SECTION_DATA_COUNT: u8 = 12;
pub const WASM_SECTION_TAG: u8 = 13;
pub const WASM_TYPE_I32: i8 = -1;
pub const WASM_TYPE_I64: i8 = -2;
pub const WASM_TYPE_F32: i8 = -3;
pub const WASM_TYPE_F64: i8 = -4;
pub const WASM_TYPE_V128: i8 = -5;
pub const WASM_TYPE_FUNCREF: i8 = -16;
pub const WASM_TYPE_EXTERNREF: i8 = -17;
pub const WASM_TYPE_FUNC: i8 = -32;
pub const WASM_TYPE_EMPTY: i8 = -64;
pub const WASM_OP_UNREACHABLE: u8 = 0x00;
pub const WASM_OP_NOP: u8 = 0x01;
pub const WASM_OP_BLOCK: u8 = 0x02;
pub const WASM_OP_LOOP: u8 = 0x03;
pub const WASM_OP_IF: u8 = 0x04;
pub const WASM_OP_ELSE: u8 = 0x05;
pub const WASM_OP_END: u8 = 0x0b;
pub const WASM_OP_BR: u8 = 0x0c;
pub const WASM_OP_BR_IF: u8 = 0x0d;
pub const WASM_OP_BR_TABLE: u8 = 0x0e;
pub const WASM_OP_RETURN: u8 = 0x0f;
pub const WASM_OP_CALL: u8 = 0x10;
pub const WASM_OP_CALL_INDIRECT: u8 = 0x11;
pub const WASM_OP_RETURN_CALL: u8 = 0x12;
pub const WASM_OP_RETURN_CALL_INDIRECT: u8 = 0x13;
pub const WASM_OP_CALL_REF: u8 = 0x14;
pub const WASM_OP_DROP: u8 = 0x1a;
pub const WASM_OP_SELECT: u8 = 0x1b;
pub const WASM_OP_SELECT_T: u8 = 0x1c;
pub const WASM_OP_LOCAL_GET: u8 = 0x20;
pub const WASM_OP_LOCAL_SET: u8 = 0x21;
pub const WASM_OP_LOCAL_TEE: u8 = 0x22;
pub const WASM_OP_GLOBAL_GET: u8 = 0x23;
pub const WASM_OP_GLOBAL_SET: u8 = 0x24;
pub const WASM_OP_TABLE_GET: u8 = 0x25;
pub const WASM_OP_TABLE_SET: u8 = 0x26;
pub const WASM_OP_I32_LOAD: u8 = 0x28;
pub const WASM_OP_I64_LOAD: u8 = 0x29;
pub const WASM_OP_F32_LOAD: u8 = 0x2a;
pub const WASM_OP_F64_LOAD: u8 = 0x2b;
pub const WASM_OP_I32_LOAD8_S: u8 = 0x2c;
pub const WASM_OP_I32_LOAD8_U: u8 = 0x2d;
pub const WASM_OP_I32_LOAD16_S: u8 = 0x2e;
pub const WASM_OP_I32_LOAD16_U: u8 = 0x2f;
pub const WASM_OP_I64_LOAD8_S: u8 = 0x30;
pub const WASM_OP_I64_LOAD8_U: u8 = 0x31;
pub const WASM_OP_I64_LOAD16_S: u8 = 0x32;
pub const WASM_OP_I64_LOAD16_U: u8 = 0x33;
pub const WASM_OP_I64_LOAD32_S: u8 = 0x34;
pub const WASM_OP_I64_LOAD32_U: u8 = 0x35;
pub const WASM_OP_I32_STORE: u8 = 0x36;
pub const WASM_OP_I64_STORE: u8 = 0x37;
pub const WASM_OP_F32_STORE: u8 = 0x38;
pub const WASM_OP_F64_STORE: u8 = 0x39;
pub const WASM_OP_I32_STORE8: u8 = 0x3a;
pub const WASM_OP_I32_STORE16: u8 = 0x3b;
pub const WASM_OP_I64_STORE8: u8 = 0x3c;
pub const WASM_OP_I64_STORE16: u8 = 0x3d;
pub const WASM_OP_I64_STORE32: u8 = 0x3e;
pub const WASM_OP_MEMORY_SIZE: u8 = 0x3f;
pub const WASM_OP_MEMORY_GROW: u8 = 0x40;
pub const WASM_OP_I32_CONST: u8 = 0x41;
pub const WASM_OP_I64_CONST: u8 = 0x42;
pub const WASM_OP_F32_CONST: u8 = 0x43;
pub const WASM_OP_F64_CONST: u8 = 0x44;
pub const WASM_OP_I32_EQZ: u8 = 0x45;
pub const WASM_OP_I32_EQ: u8 = 0x46;
pub const WASM_OP_I32_NE: u8 = 0x47;
pub const WASM_OP_I32_LT_S: u8 = 0x48;
pub const WASM_OP_I32_LT_U: u8 = 0x49;
pub const WASM_OP_I32_GT_S: u8 = 0x4a;
pub const WASM_OP_I32_GT_U: u8 = 0x4b;
pub const WASM_OP_I32_LE_S: u8 = 0x4c;
pub const WASM_OP_I32_LE_U: u8 = 0x4d;
pub const WASM_OP_I32_GE_S: u8 = 0x4e;
pub const WASM_OP_I32_GE_U: u8 = 0x4f;
pub const WASM_OP_I64_EQZ: u8 = 0x50;
pub const WASM_OP_I64_EQ: u8 = 0x51;
pub const WASM_OP_I64_NE: u8 = 0x52;
pub const WASM_OP_I64_LT_S: u8 = 0x53;
pub const WASM_OP_I64_LT_U: u8 = 0x54;
pub const WASM_OP_I64_GT_S: u8 = 0x55;
pub const WASM_OP_I64_GT_U: u8 = 0x56;
pub const WASM_OP_I64_LE_S: u8 = 0x57;
pub const WASM_OP_I64_LE_U: u8 = 0x58;
pub const WASM_OP_I64_GE_S: u8 = 0x59;
pub const WASM_OP_I64_GE_U: u8 = 0x5a;
pub const WASM_OP_F32_EQ: u8 = 0x5b;
pub const WASM_OP_F32_NE: u8 = 0x5c;
pub const WASM_OP_F32_LT: u8 = 0x5d;
pub const WASM_OP_F32_GT: u8 = 0x5e;
pub const WASM_OP_F32_LE: u8 = 0x5f;
pub const WASM_OP_F32_GE: u8 = 0x60;
pub const WASM_OP_F64_EQ: u8 = 0x61;
pub const WASM_OP_F64_NE: u8 = 0x62;
pub const WASM_OP_F64_LT: u8 = 0x63;
pub const WASM_OP_F64_GT: u8 = 0x64;
pub const WASM_OP_F64_LE: u8 = 0x65;
pub const WASM_OP_F64_GE: u8 = 0x66;
pub const WASM_OP_I32_CLZ: u8 = 0x67;
pub const WASM_OP_I32_CTZ: u8 = 0x68;
pub const WASM_OP_I32_POPCNT: u8 = 0x69;
pub const WASM_OP_I32_ADD: u8 = 0x6a;
pub const WASM_OP_I32_SUB: u8 = 0x6b;
pub const WASM_OP_I32_MUL: u8 = 0x6c;
pub const WASM_OP_I32_DIV_S: u8 = 0x6d;
pub const WASM_OP_I32_DIV_U: u8 = 0x6e;
pub const WASM_OP_I32_REM_S: u8 = 0x6f;
pub const WASM_OP_I32_REM_U: u8 = 0x70;
pub const WASM_OP_I32_AND: u8 = 0x71;
pub const WASM_OP_I32_OR: u8 = 0x72;
pub const WASM_OP_I32_XOR: u8 = 0x73;
pub const WASM_OP_I32_SHL: u8 = 0x74;
pub const WASM_OP_I32_SHR_S: u8 = 0x75;
pub const WASM_OP_I32_SHR_U: u8 = 0x76;
pub const WASM_OP_I32_ROTL: u8 = 0x77;
pub const WASM_OP_I32_ROTR: u8 = 0x78;
pub const WASM_OP_I64_CLZ: u8 = 0x79;
pub const WASM_OP_I64_CTZ: u8 = 0x7a;
pub const WASM_OP_I64_POPCNT: u8 = 0x7b;
pub const WASM_OP_I64_ADD: u8 = 0x7c;
pub const WASM_OP_I64_SUB: u8 = 0x7d;
pub const WASM_OP_I64_MUL: u8 = 0x7e;
pub const WASM_OP_I64_DIV_S: u8 = 0x7f;
pub const WASM_OP_I64_DIV_U: u8 = 0x80;
pub const WASM_OP_I64_REM_S: u8 = 0x81;
pub const WASM_OP_I64_REM_U: u8 = 0x82;
pub const WASM_OP_I64_AND: u8 = 0x83;
pub const WASM_OP_I64_OR: u8 = 0x84;
pub const WASM_OP_I64_XOR: u8 = 0x85;
pub const WASM_OP_I64_SHL: u8 = 0x86;
pub const WASM_OP_I64_SHR_S: u8 = 0x87;
pub const WASM_OP_I64_SHR_U: u8 = 0x88;
pub const WASM_OP_I64_ROTL: u8 = 0x89;
pub const WASM_OP_I64_ROTR: u8 = 0x8a;
pub const WASM_OP_F32_ABS: u8 = 0x8b;
pub const WASM_OP_F32_NEG: u8 = 0x8c;
pub const WASM_OP_F32_CEIL: u8 = 0x8d;
pub const WASM_OP_F32_FLOOR: u8 = 0x8e;
pub const WASM_OP_F32_TRUNC: u8 = 0x8f;
pub const WASM_OP_F32_NEAREST: u8 = 0x90;
pub const WASM_OP_F32_SQRT: u8 = 0x91;
pub const WASM_OP_F32_ADD: u8 = 0x92;
pub const WASM_OP_F32_SUB: u8 = 0x93;
pub const WASM_OP_F32_MUL: u8 = 0x94;
pub const WASM_OP_F32_DIV: u8 = 0x95;
pub const WASM_OP_F32_MIN: u8 = 0x96;
pub const WASM_OP_F32_MAX: u8 = 0x97;
pub const WASM_OP_F32_COPYSIGN: u8 = 0x98;
pub const WASM_OP_F64_ABS: u8 = 0x99;
pub const WASM_OP_F64_NEG: u8 = 0x9a;
pub const WASM_OP_F64_CEIL: u8 = 0x9b;
pub const WASM_OP_F64_FLOOR: u8 = 0x9c;
pub const WASM_OP_F64_TRUNC: u8 = 0x9d;
pub const WASM_OP_F64_NEAREST: u8 = 0x9e;
pub const WASM_OP_F64_SQRT: u8 = 0x9f;
pub const WASM_OP_F64_ADD: u8 = 0xa0;
pub const WASM_OP_F64_SUB: u8 = 0xa1;
pub const WASM_OP_F64_MUL: u8 = 0xa2;
pub const WASM_OP_F64_DIV: u8 = 0xa3;
pub const WASM_OP_F64_MIN: u8 = 0xa4;
pub const WASM_OP_F64_MAX: u8 = 0xa5;
pub const WASM_OP_F64_COPYSIGN: u8 = 0xa6;
pub const WASM_OP_PREFIX_MISC: u8 = 0xfc;
pub const WASM_OP_PREFIX_SIMD: u8 = 0xfd;
pub const WASM_OP_PREFIX_ATOMIC: u8 = 0xfe;
pub const WASM_OP_PREFIX_GC: u8 = 0xfb;
pub const WASM_OP_I32_TRUNC_SAT_F32_S: u32 = 0;
pub const WASM_OP_I32_TRUNC_SAT_F32_U: u32 = 1;
pub const WASM_OP_I32_TRUNC_SAT_F64_S: u32 = 2;
pub const WASM_OP_I32_TRUNC_SAT_F64_U: u32 = 3;
pub const WASM_OP_I64_TRUNC_SAT_F32_S: u32 = 4;
pub const WASM_OP_I64_TRUNC_SAT_F32_U: u32 = 5;
pub const WASM_OP_I64_TRUNC_SAT_F64_S: u32 = 6;
pub const WASM_OP_I64_TRUNC_SAT_F64_U: u32 = 7;
pub const WASM_OP_MEMORY_INIT: u32 = 8;
pub const WASM_OP_DATA_DROP: u32 = 9;
pub const WASM_OP_MEMORY_COPY: u32 = 10;
pub const WASM_OP_MEMORY_FILL: u32 = 11;
pub const WASM_OP_TABLE_INIT: u32 = 12;
pub const WASM_OP_ELEM_DROP: u32 = 13;
pub const WASM_OP_TABLE_COPY: u32 = 14;
pub const WASM_OP_TABLE_GROW: u32 = 15;
pub const WASM_OP_TABLE_SIZE: u32 = 16;
pub const WASM_OP_TABLE_FILL: u32 = 17;
pub const WASM_OP_I32_EXTEND8_S: u8 = 0xc0;
pub const WASM_OP_I32_EXTEND16_S: u8 = 0xc1;
pub const WASM_OP_I64_EXTEND8_S: u8 = 0xc2;
pub const WASM_OP_I64_EXTEND16_S: u8 = 0xc3;
pub const WASM_OP_I64_EXTEND32_S: u8 = 0xc4;
pub const WASM_OP_REF_NULL: u8 = 0xd0;
pub const WASM_OP_REF_IS_NULL: u8 = 0xd1;
pub const WASM_OP_REF_FUNC: u8 = 0xd2;
pub const WASM_OP_REF_EQ: u8 = 0xd3;
pub const WASM_OP_REF_AS_NON_NULL: u8 = 0xd4;
pub const WASM_OP_BR_ON_NULL: u8 = 0xd5;
pub const WASM_OP_BR_ON_NON_NULL: u8 = 0xd6;
pub const WASM_OP_V128_LOAD: u32 = 0;
pub const WASM_OP_V128_LOAD8X8_S: u32 = 1;
pub const WASM_OP_V128_LOAD8X8_U: u32 = 2;
pub const WASM_OP_V128_LOAD16X4_S: u32 = 3;
pub const WASM_OP_V128_LOAD16X4_U: u32 = 4;
pub const WASM_OP_V128_LOAD32X2_S: u32 = 5;
pub const WASM_OP_V128_LOAD32X2_U: u32 = 6;
pub const WASM_OP_V128_LOAD8_SPLAT: u32 = 7;
pub const WASM_OP_V128_LOAD16_SPLAT: u32 = 8;
pub const WASM_OP_V128_LOAD32_SPLAT: u32 = 9;
pub const WASM_OP_V128_LOAD64_SPLAT: u32 = 10;
pub const WASM_OP_V128_STORE: u32 = 11;
pub const WASM_OP_V128_CONST: u32 = 12;
pub const WASM_OP_I8X16_SHUFFLE: u32 = 13;
pub const WASM_OP_I8X16_SWIZZLE: u32 = 14;
pub const WASM_OP_I8X16_SPLAT: u32 = 15;
pub const WASM_OP_I16X8_SPLAT: u32 = 16;
pub const WASM_OP_I32X4_SPLAT: u32 = 17;
pub const WASM_OP_I64X2_SPLAT: u32 = 18;
pub const WASM_OP_F32X4_SPLAT: u32 = 19;
pub const WASM_OP_F64X2_SPLAT: u32 = 20;
pub const WASM_OP_I8X16_EXTRACT_LANE_S: u32 = 21;
pub const WASM_OP_I8X16_EXTRACT_LANE_U: u32 = 22;
pub const WASM_OP_I8X16_REPLACE_LANE: u32 = 23;
pub const WASM_OP_I16X8_EXTRACT_LANE_S: u32 = 24;
pub const WASM_OP_I16X8_EXTRACT_LANE_U: u32 = 25;
pub const WASM_OP_I16X8_REPLACE_LANE: u32 = 26;
pub const WASM_OP_I32X4_EXTRACT_LANE: u32 = 27;
pub const WASM_OP_I32X4_REPLACE_LANE: u32 = 28;
pub const WASM_OP_I64X2_EXTRACT_LANE: u32 = 29;
pub const WASM_OP_I64X2_REPLACE_LANE: u32 = 30;
pub const WASM_OP_F32X4_EXTRACT_LANE: u32 = 31;
pub const WASM_OP_F32X4_REPLACE_LANE: u32 = 32;
pub const WASM_OP_F64X2_EXTRACT_LANE: u32 = 33;
pub const WASM_OP_F64X2_REPLACE_LANE: u32 = 34;
pub const WASM_OP_I8X16_EQ: u32 = 35;
pub const WASM_OP_I8X16_NE: u32 = 36;
pub const WASM_OP_I8X16_LT_S: u32 = 37;
pub const WASM_OP_I8X16_LT_U: u32 = 38;
pub const WASM_OP_I8X16_GT_S: u32 = 39;
pub const WASM_OP_I8X16_GT_U: u32 = 40;
pub const WASM_OP_I8X16_LE_S: u32 = 41;
pub const WASM_OP_I8X16_LE_U: u32 = 42;
pub const WASM_OP_I8X16_GE_S: u32 = 43;
pub const WASM_OP_I8X16_GE_U: u32 = 44;
pub const WASM_OP_I16X8_EQ: u32 = 45;
pub const WASM_OP_I16X8_NE: u32 = 46;
pub const WASM_OP_I16X8_LT_S: u32 = 47;
pub const WASM_OP_I16X8_LT_U: u32 = 48;
pub const WASM_OP_I16X8_GT_S: u32 = 49;
pub const WASM_OP_I16X8_GT_U: u32 = 50;
pub const WASM_OP_I16X8_LE_S: u32 = 51;
pub const WASM_OP_I16X8_LE_U: u32 = 52;
pub const WASM_OP_I16X8_GE_S: u32 = 53;
pub const WASM_OP_I16X8_GE_U: u32 = 54;
pub const WASM_OP_I32X4_EQ: u32 = 55;
pub const WASM_OP_I32X4_NE: u32 = 56;
pub const WASM_OP_I32X4_LT_S: u32 = 57;
pub const WASM_OP_I32X4_LT_U: u32 = 58;
pub const WASM_OP_I32X4_GT_S: u32 = 59;
pub const WASM_OP_I32X4_GT_U: u32 = 60;
pub const WASM_OP_I32X4_LE_S: u32 = 61;
pub const WASM_OP_I32X4_LE_U: u32 = 62;
pub const WASM_OP_I32X4_GE_S: u32 = 63;
pub const WASM_OP_I32X4_GE_U: u32 = 64;
pub const WASM_OP_F32X4_EQ: u32 = 65;
pub const WASM_OP_F32X4_NE: u32 = 66;
pub const WASM_OP_F32X4_LT: u32 = 67;
pub const WASM_OP_F32X4_GT: u32 = 68;
pub const WASM_OP_F32X4_LE: u32 = 69;
pub const WASM_OP_F32X4_GE: u32 = 70;
pub const WASM_OP_F64X2_EQ: u32 = 71;
pub const WASM_OP_F64X2_NE: u32 = 72;
pub const WASM_OP_F64X2_LT: u32 = 73;
pub const WASM_OP_F64X2_GT: u32 = 74;
pub const WASM_OP_F64X2_LE: u32 = 75;
pub const WASM_OP_F64X2_GE: u32 = 76;
pub const WASM_OP_V128_NOT: u32 = 77;
pub const WASM_OP_V128_AND: u32 = 78;
pub const WASM_OP_V128_ANDNOT: u32 = 79;
pub const WASM_OP_V128_OR: u32 = 80;
pub const WASM_OP_V128_XOR: u32 = 81;
pub const WASM_OP_V128_BITSELECT: u32 = 82;
pub const WASM_OP_V128_ANY_TRUE: u32 = 83;
pub const WASM_OP_V128_LOAD8_LANE: u32 = 84;
pub const WASM_OP_V128_LOAD16_LANE: u32 = 85;
pub const WASM_OP_V128_LOAD32_LANE: u32 = 86;
pub const WASM_OP_V128_LOAD64_LANE: u32 = 87;
pub const WASM_OP_V128_STORE8_LANE: u32 = 88;
pub const WASM_OP_V128_STORE16_LANE: u32 = 89;
pub const WASM_OP_V128_STORE32_LANE: u32 = 90;
pub const WASM_OP_V128_STORE64_LANE: u32 = 91;
pub const WASM_OP_V128_LOAD32_ZERO: u32 = 92;
pub const WASM_OP_V128_LOAD64_ZERO: u32 = 93;
pub const WASM_OP_MEMORY_ATOMIC_NOTIFY: u32 = 0;
pub const WASM_OP_MEMORY_ATOMIC_WAIT32: u32 = 1;
pub const WASM_OP_MEMORY_ATOMIC_WAIT64: u32 = 2;
pub const WASM_OP_ATOMIC_FENCE: u32 = 3;
pub const WASM_OP_I32_ATOMIC_LOAD: u32 = 16;
pub const WASM_OP_I64_ATOMIC_LOAD: u32 = 17;
pub const WASM_OP_I32_ATOMIC_LOAD8_U: u32 = 18;
pub const WASM_OP_I32_ATOMIC_LOAD16_U: u32 = 19;
pub const WASM_OP_I64_ATOMIC_LOAD8_U: u32 = 20;
pub const WASM_OP_I64_ATOMIC_LOAD16_U: u32 = 21;
pub const WASM_OP_I64_ATOMIC_LOAD32_U: u32 = 22;
pub const WASM_OP_I32_ATOMIC_STORE: u32 = 23;
pub const WASM_OP_I64_ATOMIC_STORE: u32 = 24;
pub const WASM_OP_I32_ATOMIC_STORE8: u32 = 25;
pub const WASM_OP_I32_ATOMIC_STORE16: u32 = 26;
pub const WASM_OP_I64_ATOMIC_STORE8: u32 = 27;
pub const WASM_OP_I64_ATOMIC_STORE16: u32 = 28;
pub const WASM_OP_I64_ATOMIC_STORE32: u32 = 29;
pub const WASM_OP_I32_ATOMIC_RMW_ADD: u32 = 30;
pub const WASM_OP_I64_ATOMIC_RMW_ADD: u32 = 31;
pub const WASM_OP_I32_ATOMIC_RMW8_ADD_U: u32 = 32;
pub const WASM_OP_I32_ATOMIC_RMW16_ADD_U: u32 = 33;
pub const WASM_OP_I64_ATOMIC_RMW8_ADD_U: u32 = 34;
pub const WASM_OP_I64_ATOMIC_RMW16_ADD_U: u32 = 35;
pub const WASM_OP_I64_ATOMIC_RMW32_ADD_U: u32 = 36;
pub const WASM_OP_I32_ATOMIC_RMW_SUB: u32 = 37;
pub const WASM_OP_I64_ATOMIC_RMW_SUB: u32 = 38;
pub const WASM_OP_I32_ATOMIC_RMW8_SUB_U: u32 = 39;
pub const WASM_OP_I32_ATOMIC_RMW16_SUB_U: u32 = 40;
pub const WASM_OP_I64_ATOMIC_RMW8_SUB_U: u32 = 41;
pub const WASM_OP_I64_ATOMIC_RMW16_SUB_U: u32 = 42;
pub const WASM_OP_I64_ATOMIC_RMW32_SUB_U: u32 = 43;
pub const WASM_OP_I32_ATOMIC_RMW_AND: u32 = 44;
pub const WASM_OP_I64_ATOMIC_RMW_AND: u32 = 45;
pub const WASM_OP_I32_ATOMIC_RMW8_AND_U: u32 = 46;
pub const WASM_OP_I32_ATOMIC_RMW16_AND_U: u32 = 47;
pub const WASM_OP_I64_ATOMIC_RMW8_AND_U: u32 = 48;
pub const WASM_OP_I64_ATOMIC_RMW16_AND_U: u32 = 49;
pub const WASM_OP_I64_ATOMIC_RMW32_AND_U: u32 = 50;
pub const WASM_OP_I32_ATOMIC_RMW_OR: u32 = 51;
pub const WASM_OP_I64_ATOMIC_RMW_OR: u32 = 52;
pub const WASM_OP_I32_ATOMIC_RMW8_OR_U: u32 = 53;
pub const WASM_OP_I32_ATOMIC_RMW16_OR_U: u32 = 54;
pub const WASM_OP_I64_ATOMIC_RMW8_OR_U: u32 = 55;
pub const WASM_OP_I64_ATOMIC_RMW16_OR_U: u32 = 56;
pub const WASM_OP_I64_ATOMIC_RMW32_OR_U: u32 = 57;
pub const WASM_OP_I32_ATOMIC_RMW_XOR: u32 = 58;
pub const WASM_OP_I64_ATOMIC_RMW_XOR: u32 = 59;
pub const WASM_OP_I32_ATOMIC_RMW8_XOR_U: u32 = 60;
pub const WASM_OP_I32_ATOMIC_RMW16_XOR_U: u32 = 61;
pub const WASM_OP_I64_ATOMIC_RMW8_XOR_U: u32 = 62;
pub const WASM_OP_I64_ATOMIC_RMW16_XOR_U: u32 = 63;
pub const WASM_OP_I64_ATOMIC_RMW32_XOR_U: u32 = 64;
pub const WASM_OP_I32_ATOMIC_RMW_XCHG: u32 = 65;
pub const WASM_OP_I64_ATOMIC_RMW_XCHG: u32 = 66;
pub const WASM_OP_I32_ATOMIC_RMW8_XCHG_U: u32 = 67;
pub const WASM_OP_I32_ATOMIC_RMW16_XCHG_U: u32 = 68;
pub const WASM_OP_I64_ATOMIC_RMW8_XCHG_U: u32 = 69;
pub const WASM_OP_I64_ATOMIC_RMW16_XCHG_U: u32 = 70;
pub const WASM_OP_I64_ATOMIC_RMW32_XCHG_U: u32 = 71;
pub const WASM_OP_I32_ATOMIC_RMW_CMPXCHG: u32 = 72;
pub const WASM_OP_I64_ATOMIC_RMW_CMPXCHG: u32 = 73;
pub const WASM_OP_I32_ATOMIC_RMW8_CMPXCHG_U: u32 = 74;
pub const WASM_OP_I32_ATOMIC_RMW16_CMPXCHG_U: u32 = 75;
pub const WASM_OP_I64_ATOMIC_RMW8_CMPXCHG_U: u32 = 76;
pub const WASM_OP_I64_ATOMIC_RMW16_CMPXCHG_U: u32 = 77;
pub const WASM_OP_I64_ATOMIC_RMW32_CMPXCHG_U: u32 = 78;
pub const WASM_EXTERNAL_KIND_FUNCTION: u8 = 0;
pub const WASM_EXTERNAL_KIND_TABLE: u8 = 1;
pub const WASM_EXTERNAL_KIND_MEMORY: u8 = 2;
pub const WASM_EXTERNAL_KIND_GLOBAL: u8 = 3;
pub const WASM_EXTERNAL_KIND_TAG: u8 = 4;
pub const WASM_MUT_CONST: u8 = 0;
pub const WASM_MUT_VAR: u8 = 1;
pub const WASM_LIMIT_MIN: u8 = 0;
pub const WASM_LIMIT_MIN_MAX: u8 = 1;
pub const WASI_ESUCCESS: u16 = 0;
pub const WASI_E2BIG: u16 = 1;
pub const WASI_EACCES: u16 = 2;
pub const WASI_EADDRINUSE: u16 = 3;
pub const WASI_EADDRNOTAVAIL: u16 = 4;
pub const WASI_EAFNOSUPPORT: u16 = 5;
pub const WASI_EAGAIN: u16 = 6;
pub const WASI_EALREADY: u16 = 7;
pub const WASI_EBADF: u16 = 8;
pub const WASI_EBADMSG: u16 = 9;
pub const WASI_EBUSY: u16 = 10;
pub const WASI_ECANCELED: u16 = 11;
pub const WASI_ECHILD: u16 = 12;
pub const WASI_ECONNABORTED: u16 = 13;
pub const WASI_ECONNREFUSED: u16 = 14;
pub const WASI_ECONNRESET: u16 = 15;
pub const WASI_EDEADLK: u16 = 16;
pub const WASI_EDESTADDRREQ: u16 = 17;
pub const WASI_EDOM: u16 = 18;
pub const WASI_EDQUOT: u16 = 19;
pub const WASI_EEXIST: u16 = 20;
pub const WASI_EFAULT: u16 = 21;
pub const WASI_EFBIG: u16 = 22;
pub const WASI_EHOSTUNREACH: u16 = 23;
pub const WASI_EIDRM: u16 = 24;
pub const WASI_EILSEQ: u16 = 25;
pub const WASI_EINPROGRESS: u16 = 26;
pub const WASI_EINTR: u16 = 27;
pub const WASI_EINVAL: u16 = 28;
pub const WASI_EIO: u16 = 29;
pub const WASI_EISCONN: u16 = 30;
pub const WASI_EISDIR: u16 = 31;
pub const WASI_ELOOP: u16 = 32;
pub const WASI_EMFILE: u16 = 33;
pub const WASI_EMLINK: u16 = 34;
pub const WASI_EMSGSIZE: u16 = 35;
pub const WASI_EMULTIHOP: u16 = 36;
pub const WASI_ENAMETOOLONG: u16 = 37;
pub const WASI_ENETDOWN: u16 = 38;
pub const WASI_ENETRESET: u16 = 39;
pub const WASI_ENETUNREACH: u16 = 40;
pub const WASI_ENFILE: u16 = 41;
pub const WASI_ENOBUFS: u16 = 42;
pub const WASI_ENODEV: u16 = 43;
pub const WASI_ENOENT: u16 = 44;
pub const WASI_ENOEXEC: u16 = 45;
pub const WASI_ENOLCK: u16 = 46;
pub const WASI_ENOLINK: u16 = 47;
pub const WASI_ENOMEM: u16 = 48;
pub const WASI_ENOMSG: u16 = 49;
pub const WASI_ENOPROTOOPT: u16 = 50;
pub const WASI_ENOSPC: u16 = 51;
pub const WASI_ENOSYS: u16 = 52;
pub const WASI_ENOTCONN: u16 = 53;
pub const WASI_ENOTDIR: u16 = 54;
pub const WASI_ENOTEMPTY: u16 = 55;
pub const WASI_ENOTRECOVERABLE: u16 = 56;
pub const WASI_ENOTSOCK: u16 = 57;
pub const WASI_ENOTSUP: u16 = 58;
pub const WASI_ENOTTY: u16 = 59;
pub const WASI_ENXIO: u16 = 60;
pub const WASI_EOVERFLOW: u16 = 61;
pub const WASI_EOWNERDEAD: u16 = 62;
pub const WASI_EPERM: u16 = 63;
pub const WASI_EPIPE: u16 = 64;
pub const WASI_EPROTO: u16 = 65;
pub const WASI_EPROTONOSUPPORT: u16 = 66;
pub const WASI_EPROTOTYPE: u16 = 67;
pub const WASI_ERANGE: u16 = 68;
pub const WASI_EROFS: u16 = 69;
pub const WASI_ESPIPE: u16 = 70;
pub const WASI_ESRCH: u16 = 71;
pub const WASI_ESTALE: u16 = 72;
pub const WASI_ETIMEDOUT: u16 = 73;
pub const WASI_ETXTBSY: u16 = 74;
pub const WASI_EXDEV: u16 = 75;
pub const WASI_ENOTCAPABLE: u16 = 76;
#[derive(Debug, Clone)]
pub struct X86WASM {
pub config: X86WASMConfig,
pub target: X86WASMTarget,
pub stats: X86WASMStats,
pub warnings: Vec<String>,
pub errors: Vec<String>,
pub function_index: HashMap<String, u32>,
pub global_index: HashMap<String, u32>,
pub memory_index: HashMap<String, u32>,
pub table_index: HashMap<String, u32>,
pub custom_sections: Vec<X86WASMCustomSection>,
pub wasi_imports: Vec<WasiImport>,
pub emscripten_metadata: Option<EmscriptenMetadata>,
pub debug_info: bool,
pub dwarf_sections: Vec<(String, Vec<u8>)>,
}
impl X86WASM {
pub fn new(config: X86WASMConfig) -> Self {
let target = X86WASMTarget::from_config(&config);
Self {
config,
target,
stats: X86WASMStats::default(),
warnings: Vec::new(),
errors: Vec::new(),
function_index: HashMap::new(),
global_index: HashMap::new(),
memory_index: HashMap::new(),
table_index: HashMap::new(),
custom_sections: Vec::new(),
wasi_imports: Vec::new(),
emscripten_metadata: None,
debug_info: false,
dwarf_sections: Vec::new(),
}
}
pub fn compile_c_source(&mut self, source: &str) -> Result<X86WASMModule, X86WASMError> {
self.stats.total_source_bytes = source.len() as u64;
let start = std::time::Instant::now();
let c_std = CLangStandard::C17;
let mut lexer = Lexer::new(source, c_std);
let tokens = lexer.lex_all();
let mut preprocessor = Preprocessor::new(c_std);
let processed = preprocessor.process(tokens);
if !preprocessor.errors.is_empty() {
return Err(X86WASMError::CompilationError(format!(
"Preprocessing failed: {:?}",
preprocessor.errors
)));
}
let mut parser = Parser::new(&processed, c_std);
let ast = parser
.parse()
.map_err(|e| X86WASMError::CompilationError(format!("Parsing failed: {:?}", e)))?;
let mut codegen = X86WASMCodeGen::new(&self.target, &self.config);
let module = codegen
.generate_module(&ast)
.map_err(|e| X86WASMError::CodegenError(format!("Code generation failed: {:?}", e)))?;
self.stats.parse_time_ms = start.elapsed().as_millis() as u64;
self.stats.function_count = module.functions.len() as u64;
self.stats.global_count = module.globals.len() as u64;
self.stats.import_count = module.imports.len() as u64;
self.stats.export_count = module.exports.len() as u64;
Ok(module)
}
pub fn compile_cxx_source(&mut self, source: &str) -> Result<X86WASMModule, X86WASMError> {
self.stats.total_source_bytes = source.len() as u64;
Err(X86WASMError::UnsupportedFeature(
"C++ WASM compilation not yet fully implemented".into(),
))
}
pub fn compile_wasi_c_source(&mut self, source: &str) -> Result<X86WASMModule, X86WASMError> {
let mut module = self.compile_c_source(source)?;
let wasi = X86WASMWASI::new(&self.config);
wasi.add_wasi_imports(&mut module)
.map_err(|e| X86WASMError::CompilationError(e))?;
wasi.add_wasi_start(&mut module)
.map_err(|e| X86WASMError::CompilationError(e))?;
Ok(module)
}
pub fn compile_emscripten_source(
&mut self,
source: &str,
) -> Result<X86WASMModule, X86WASMError> {
let mut module = self.compile_c_source(source)?;
let emscripten = X86WASMEmscripten::new(&self.config);
emscripten
.add_emscripten_runtime(&mut module)
.map_err(|e| X86WASMError::CompilationError(e))?;
emscripten
.add_js_glue(&mut module)
.map_err(|e| X86WASMError::CompilationError(e))?;
Ok(module)
}
pub fn link_modules(
&mut self,
modules: &[X86WASMModule],
) -> Result<X86WASMModule, X86WASMError> {
let mut linker = X86WASMLinker::new(&self.config);
let linked = linker
.link(modules)
.map_err(|e| X86WASMError::LinkerError(format!("Linking failed: {}", e)))?;
Ok(linked)
}
pub fn optimize_module(&mut self, module: &mut X86WASMModule) -> Result<(), X86WASMError> {
let optimizer = X86WASMOptimizer::new(&self.config);
optimizer
.optimize(module)
.map_err(|e| X86WASMError::OptimizerError(format!("Optimization failed: {}", e)))?;
Ok(())
}
pub fn emit_binary(&mut self, module: &X86WASMModule) -> Result<Vec<u8>, X86WASMError> {
let start = std::time::Instant::now();
let emitter = X86WASMBinaryEmitter::new(&self.target);
let binary = emitter.emit(module)?;
self.stats.codegen_time_ms = start.elapsed().as_millis() as u64;
self.stats.output_size_bytes = binary.len() as u64;
Ok(binary)
}
pub fn emit_wat(&mut self, module: &X86WASMModule) -> String {
let emitter = X86WASMTextEmitter::new();
emitter.emit(module)
}
pub fn validate_module(&self, module: &X86WASMModule) -> Result<(), X86WASMError> {
let validator = X86WASMValidator::new(&self.target);
validator
.validate(module)
.map_err(|e| X86WASMError::ValidationError(format!("Validation failed: {}", e)))?;
Ok(())
}
pub fn generate_name_section(&self, module: &X86WASMModule) -> Vec<u8> {
let mut names = Vec::new();
let encoder = X86WASMEncoder::new();
let mut name_map = Vec::new();
encoder.write_u32_leb128(&mut name_map, module.functions.len() as u32);
for (i, func) in module.functions.iter().enumerate() {
encoder.write_u32_leb128(&mut name_map, i as u32);
encoder.write_name(&mut name_map, &func.name);
}
names.push((0, name_map));
let mut name_section = Vec::new();
name_section.push(WASM_SECTION_CUSTOM);
encoder.write_name(&mut name_section, "name");
let mut name_payload = Vec::new();
for (id, payload) in &names {
name_payload.push(*id);
encoder.write_u32_leb128(&mut name_payload, payload.len() as u32);
name_payload.extend_from_slice(payload);
}
encoder.write_u32_leb128(&mut name_section, name_payload.len() as u32);
name_section.extend_from_slice(&name_payload);
name_section
}
pub fn warn(&mut self, msg: &str) {
self.warnings.push(msg.to_string());
}
pub fn error(&mut self, msg: &str) {
self.errors.push(msg.to_string());
}
pub fn has_errors(&self) -> bool {
!self.errors.is_empty()
}
pub fn summary(&self) -> String {
format!(
"WASM compilation summary:\n\
Source bytes: {}\n\
Functions: {}\n\
Imports: {}\n\
Exports: {}\n\
Globals: {}\n\
Parse time: {}ms\n\
Codegen time: {}ms\n\
Output size: {} bytes\n\
Warnings: {}\n\
Errors: {}",
self.stats.total_source_bytes,
self.stats.function_count,
self.stats.import_count,
self.stats.export_count,
self.stats.global_count,
self.stats.parse_time_ms,
self.stats.codegen_time_ms,
self.stats.output_size_bytes,
self.warnings.len(),
self.errors.len()
)
}
}
impl Default for X86WASM {
fn default() -> Self {
Self::new(X86WASMConfig::default())
}
}
#[derive(Debug, Clone)]
pub struct X86WASMConfig {
pub target_triple: String,
pub optimization_level: u8,
pub debug_info: bool,
pub enable_wasi: bool,
pub enable_emscripten: bool,
pub enable_threads: bool,
pub enable_simd128: bool,
pub enable_exceptions: bool,
pub enable_tail_calls: bool,
pub enable_reference_types: bool,
pub enable_bulk_memory: bool,
pub enable_memory64: bool,
pub enable_gc: bool,
pub enable_relaxed_simd: bool,
pub enable_extended_const: bool,
pub enable_multi_value: bool,
pub initial_memory_pages: u64,
pub maximum_memory_pages: Option<u64>,
pub stack_size: u64,
pub include_paths: Vec<String>,
pub defines: Vec<(String, Option<String>)>,
pub output_file: Option<String>,
pub custom_sections: Vec<X86WASMCustomSection>,
pub reactor_mode: bool,
pub command_mode: bool,
pub enable_lto: bool,
pub enable_binaryen: bool,
pub host_cpu: Option<String>,
}
impl Default for X86WASMConfig {
fn default() -> Self {
Self {
target_triple: "wasm32-unknown-unknown".to_string(),
optimization_level: 2,
debug_info: false,
enable_wasi: false,
enable_emscripten: false,
enable_threads: false,
enable_simd128: true,
enable_exceptions: false,
enable_tail_calls: true,
enable_reference_types: true,
enable_bulk_memory: true,
enable_memory64: false,
enable_gc: false,
enable_relaxed_simd: false,
enable_extended_const: true,
enable_multi_value: true,
initial_memory_pages: DEFAULT_MEMORY_PAGES,
maximum_memory_pages: Some(DEFAULT_MAX_MEMORY_PAGES),
stack_size: WASM_STACK_SIZE,
include_paths: Vec::new(),
defines: Vec::new(),
output_file: None,
custom_sections: Vec::new(),
reactor_mode: false,
command_mode: false,
enable_lto: false,
enable_binaryen: true,
host_cpu: None,
}
}
}
impl X86WASMConfig {
pub fn wasi() -> Self {
Self {
target_triple: "wasm32-wasi".to_string(),
enable_wasi: true,
command_mode: true,
..Default::default()
}
}
pub fn wasi_reactor() -> Self {
Self {
target_triple: "wasm32-wasi".to_string(),
enable_wasi: true,
reactor_mode: true,
..Default::default()
}
}
pub fn emscripten() -> Self {
Self {
target_triple: "wasm32-unknown-emscripten".to_string(),
enable_emscripten: true,
..Default::default()
}
}
pub fn wasm64() -> Self {
Self {
target_triple: "wasm64-unknown-unknown".to_string(),
enable_memory64: true,
initial_memory_pages: 256,
maximum_memory_pages: Some(1 << 32),
..Default::default()
}
}
pub fn set_features(&mut self, features: &str) {
for feature in features.split(',') {
let feature = feature.trim();
match feature {
"+simd128" => self.enable_simd128 = true,
"-simd128" => self.enable_simd128 = false,
"+atomics" | "+bulk-memory" => {
self.enable_threads = true;
self.enable_bulk_memory = true;
}
"-bulk-memory" => self.enable_bulk_memory = false,
"+mutable-globals" => {}
"+sign-ext" => {}
"+nontrapping-fptoint" => {}
"+multi-value" => self.enable_multi_value = true,
"-multi-value" => self.enable_multi_value = false,
"+reference-types" => self.enable_reference_types = true,
"-reference-types" => self.enable_reference_types = false,
"+tail-call" => self.enable_tail_calls = true,
"-tail-call" => self.enable_tail_calls = false,
"+exception-handling" => self.enable_exceptions = true,
"-exception-handling" => self.enable_exceptions = false,
"+extended-const" => self.enable_extended_const = true,
"-extended-const" => self.enable_extended_const = false,
"+relaxed-simd" => self.enable_relaxed_simd = true,
"-relaxed-simd" => self.enable_relaxed_simd = false,
"+memory64" => self.enable_memory64 = true,
"-memory64" => self.enable_memory64 = false,
"+gc" => self.enable_gc = true,
"-gc" => self.enable_gc = false,
_ => {}
}
}
}
pub fn data_layout(&self) -> &'static str {
if self.enable_memory64 {
"e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
} else {
"e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
}
}
}
#[derive(Debug, Clone, Default)]
pub struct X86WASMStats {
pub total_source_bytes: u64,
pub parse_time_ms: u64,
pub codegen_time_ms: u64,
pub optimization_time_ms: u64,
pub link_time_ms: u64,
pub function_count: u64,
pub import_count: u64,
pub export_count: u64,
pub global_count: u64,
pub output_size_bytes: u64,
pub type_count: u64,
pub code_section_size: u64,
pub data_segment_count: u64,
pub data_segment_size: u64,
}
impl fmt::Display for X86WASMStats {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Compilation Statistics:")?;
writeln!(f, " Source bytes: {}", self.total_source_bytes)?;
writeln!(f, " Parse time: {}ms", self.parse_time_ms)?;
writeln!(f, " Codegen time: {}ms", self.codegen_time_ms)?;
writeln!(f, " Opt time: {}ms", self.optimization_time_ms)?;
writeln!(f, " Link time: {}ms", self.link_time_ms)?;
writeln!(f, " Functions: {}", self.function_count)?;
writeln!(f, " Imports: {}", self.import_count)?;
writeln!(f, " Exports: {}", self.export_count)?;
writeln!(f, " Globals: {}", self.global_count)?;
writeln!(f, " Types: {}", self.type_count)?;
writeln!(f, " Output size: {} bytes", self.output_size_bytes)?;
writeln!(f, " Code section: {} bytes", self.code_section_size)?;
writeln!(f, " Data segments: {}", self.data_segment_count)?;
writeln!(f, " Data size: {} bytes", self.data_segment_size)?;
Ok(())
}
}
#[derive(Debug, Clone)]
pub enum X86WASMError {
CompilationError(String),
CodegenError(String),
ValidationError(String),
LinkerError(String),
OptimizerError(String),
IoError(String),
UnsupportedFeature(String),
InternalError(String),
}
impl fmt::Display for X86WASMError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86WASMError::CompilationError(s) => write!(f, "Compilation error: {}", s),
X86WASMError::CodegenError(s) => write!(f, "Codegen error: {}", s),
X86WASMError::ValidationError(s) => write!(f, "Validation error: {}", s),
X86WASMError::LinkerError(s) => write!(f, "Linker error: {}", s),
X86WASMError::OptimizerError(s) => write!(f, "Optimizer error: {}", s),
X86WASMError::IoError(s) => write!(f, "IO error: {}", s),
X86WASMError::UnsupportedFeature(s) => write!(f, "Unsupported feature: {}", s),
X86WASMError::InternalError(s) => write!(f, "Internal error: {}", s),
}
}
}
impl std::error::Error for X86WASMError {}
#[derive(Debug, Clone)]
pub struct X86WASMTarget {
pub triple: String,
pub data_layout: String,
pub pointer_width: u32,
pub features: X86WASMFeatureFlags,
pub is_wasi: bool,
pub is_emscripten: bool,
pub is_bare: bool,
pub stack_alignment: u32,
pub heap_alignment: u32,
pub max_function_params: u32,
pub max_function_results: u32,
pub min_memory_pages: u64,
pub max_memory_pages: u64,
pub abi: X86WASMABI,
}
impl X86WASMTarget {
pub fn from_triple(triple: &str) -> Self {
let t = triple.to_lowercase();
let is_wasi = t.contains("wasi");
let is_emscripten = t.contains("emscripten");
let is_wasm64 = t.starts_with("wasm64");
let is_bare = !is_wasi && !is_emscripten;
let pointer_width = if is_wasm64 { 64 } else { 32 };
let data_layout = if is_wasm64 {
"e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
} else {
"e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
};
let features = X86WASMFeatureFlags::from_triple(triple);
let abi = if is_wasi {
X86WASMABI::Wasi
} else if is_emscripten {
X86WASMABI::Emscripten
} else {
X86WASMABI::Bare
};
Self {
triple: triple.to_string(),
data_layout: data_layout.to_string(),
pointer_width,
features: features.clone(),
is_wasi,
is_emscripten,
is_bare,
stack_alignment: 16,
heap_alignment: 16,
max_function_params: 1000,
max_function_results: if features.multi_value { 128 } else { 1 },
min_memory_pages: 1,
max_memory_pages: if is_wasm64 { 1 << 48 } else { 65536 },
abi,
}
}
pub fn from_config(config: &X86WASMConfig) -> Self {
let mut target = Self::from_triple(&config.target_triple);
target.features = X86WASMFeatureFlags::from_config(config);
target.min_memory_pages = config.initial_memory_pages;
target.max_memory_pages = config
.maximum_memory_pages
.unwrap_or(target.max_memory_pages);
target
}
pub fn wasm32() -> Self {
Self::from_triple("wasm32-unknown-unknown")
}
pub fn wasm32_wasi() -> Self {
Self::from_triple("wasm32-wasi")
}
pub fn wasm32_emscripten() -> Self {
Self::from_triple("wasm32-unknown-emscripten")
}
pub fn wasm64() -> Self {
Self::from_triple("wasm64-unknown-unknown")
}
pub fn ptr_size(&self) -> u32 {
self.pointer_width / 8
}
pub fn has_feature(&self, feature: &str) -> bool {
self.features.has(feature)
}
pub fn feature_string(&self) -> String {
self.features.to_string()
}
pub fn is_wasm64(&self) -> bool {
self.pointer_width == 64
}
pub fn stack_pointer_name(&self) -> &'static str {
"__stack_pointer"
}
pub fn heap_base_name(&self) -> &'static str {
if self.is_emscripten {
"__heap_base"
} else {
"__heap_base"
}
}
pub fn data_end_name(&self) -> &'static str {
if self.is_emscripten {
"__data_end"
} else {
"__data_end"
}
}
}
impl Default for X86WASMTarget {
fn default() -> Self {
Self::from_triple("wasm32-unknown-unknown")
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct X86WASMFeatureFlags {
pub sign_ext: bool,
pub mutable_globals: bool,
pub nontrapping_fptoint: bool,
pub simd128: bool,
pub relaxed_simd: bool,
pub bulk_memory: bool,
pub atomics: bool,
pub reference_types: bool,
pub multi_value: bool,
pub tail_call: bool,
pub exception_handling: bool,
pub extended_const: bool,
pub memory64: bool,
pub gc: bool,
pub multi_memory: bool,
pub function_references: bool,
pub component_model: bool,
}
impl Default for X86WASMFeatureFlags {
fn default() -> Self {
Self {
sign_ext: true,
mutable_globals: true,
nontrapping_fptoint: true,
simd128: true,
relaxed_simd: false,
bulk_memory: true,
atomics: true,
reference_types: true,
multi_value: true,
tail_call: true,
exception_handling: false,
extended_const: true,
memory64: false,
gc: false,
multi_memory: false,
function_references: false,
component_model: false,
}
}
}
impl X86WASMFeatureFlags {
pub fn from_triple(triple: &str) -> Self {
let t = triple.to_lowercase();
let mut flags = Self::default();
if t.starts_with("wasm64") {
flags.memory64 = true;
}
if t.contains("emscripten") {
flags.sign_ext = true;
flags.mutable_globals = true;
flags.nontrapping_fptoint = true;
flags.simd128 = true;
flags.bulk_memory = true;
flags.atomics = true;
flags.reference_types = true;
flags.multi_value = true;
flags.tail_call = true;
flags.exception_handling = true;
flags.extended_const = true;
}
flags
}
pub fn from_config(config: &X86WASMConfig) -> Self {
let mut flags = Self::from_triple(&config.target_triple);
flags.simd128 = config.enable_simd128;
flags.relaxed_simd = config.enable_relaxed_simd;
flags.atomics = config.enable_threads;
flags.bulk_memory = config.enable_bulk_memory;
flags.reference_types = config.enable_reference_types;
flags.multi_value = config.enable_multi_value;
flags.tail_call = config.enable_tail_calls;
flags.exception_handling = config.enable_exceptions;
flags.extended_const = config.enable_extended_const;
flags.memory64 = config.enable_memory64;
flags.gc = config.enable_gc;
flags
}
pub fn has(&self, feature: &str) -> bool {
match feature {
"sign-ext" => self.sign_ext,
"mutable-globals" => self.mutable_globals,
"nontrapping-fptoint" => self.nontrapping_fptoint,
"simd128" => self.simd128,
"relaxed-simd" => self.relaxed_simd,
"bulk-memory" => self.bulk_memory,
"atomics" => self.atomics,
"reference-types" => self.reference_types,
"multi-value" => self.multi_value,
"tail-call" => self.tail_call,
"exception-handling" => self.exception_handling,
"extended-const" => self.extended_const,
"memory64" => self.memory64,
"gc" => self.gc,
"multi-memory" => self.multi_memory,
"function-references" => self.function_references,
"component-model" => self.component_model,
_ => false,
}
}
pub fn enabled_features(&self) -> Vec<&'static str> {
let mut features = Vec::new();
if self.sign_ext {
features.push("sign-ext");
}
if self.mutable_globals {
features.push("mutable-globals");
}
if self.nontrapping_fptoint {
features.push("nontrapping-fptoint");
}
if self.simd128 {
features.push("simd128");
}
if self.relaxed_simd {
features.push("relaxed-simd");
}
if self.bulk_memory {
features.push("bulk-memory");
}
if self.atomics {
features.push("atomics");
}
if self.reference_types {
features.push("reference-types");
}
if self.multi_value {
features.push("multi-value");
}
if self.tail_call {
features.push("tail-call");
}
if self.exception_handling {
features.push("exception-handling");
}
if self.extended_const {
features.push("extended-const");
}
if self.memory64 {
features.push("memory64");
}
if self.gc {
features.push("gc");
}
if self.multi_memory {
features.push("multi-memory");
}
if self.function_references {
features.push("function-references");
}
if self.component_model {
features.push("component-model");
}
features
}
}
impl fmt::Display for X86WASMFeatureFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.enabled_features().join(","))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86WASMABI {
Bare,
Wasi,
Emscripten,
Generic,
Custom(u32),
}
impl X86WASMABI {
pub fn name(&self) -> &'static str {
match self {
X86WASMABI::Bare => "bare",
X86WASMABI::Wasi => "wasi",
X86WASMABI::Emscripten => "emscripten",
X86WASMABI::Generic => "generic",
X86WASMABI::Custom(_) => "custom",
}
}
pub fn is_hosted(&self) -> bool {
matches!(self, X86WASMABI::Wasi | X86WASMABI::Emscripten)
}
}
impl Default for X86WASMABI {
fn default() -> Self {
X86WASMABI::Bare
}
}
pub struct X86WASMCodeGen<'a> {
target: &'a X86WASMTarget,
config: &'a X86WASMConfig,
module: X86WASMModule,
current_function: Option<WasmFunction>,
current_body: Vec<WasmInstruction>,
current_locals: Vec<WasmValueType>,
label_stack: Vec<WasmLabel>,
type_cache: HashMap<WasmFuncType, u32>,
next_local: u32,
next_global: u32,
next_function: u32,
next_data_segment: u32,
local_map: HashMap<String, u32>,
global_map: HashMap<String, u32>,
func_map: HashMap<String, u32>,
string_table: HashMap<String, u32>,
next_memory_offset: u32,
static_data: Vec<u8>,
break_depth: Vec<u32>,
continue_depth: Vec<u32>,
errors: Vec<String>,
}
impl<'a> X86WASMCodeGen<'a> {
pub fn new(target: &'a X86WASMTarget, config: &'a X86WASMConfig) -> Self {
let mut module = X86WASMModule::new();
module.target_triple = Some(target.triple.clone());
module.data_layout = Some(target.data_layout.clone());
Self {
target,
config,
module,
current_function: None,
current_body: Vec::new(),
current_locals: Vec::new(),
label_stack: Vec::new(),
type_cache: HashMap::new(),
next_local: 0,
next_global: 0,
next_function: 0,
next_data_segment: 0,
local_map: HashMap::new(),
global_map: HashMap::new(),
func_map: HashMap::new(),
string_table: HashMap::new(),
next_memory_offset: 0,
static_data: Vec::new(),
break_depth: Vec::new(),
continue_depth: Vec::new(),
errors: Vec::new(),
}
}
pub fn generate_module(&mut self, ast: &TranslationUnit) -> Result<X86WASMModule, String> {
self.collect_declarations(ast)?;
for _decl in &ast.decls {
}
self.emit_data_section()?;
self.emit_exports()?;
Ok(self.module.clone())
}
pub fn generate_cxx_module(
&mut self,
_cxx_ast: &CXXTranslationUnit,
) -> Result<X86WASMModule, String> {
self.emit_data_section()?;
self.emit_exports()?;
Ok(self.module.clone())
}
fn collect_declarations(&mut self, ast: &TranslationUnit) -> Result<(), String> {
for decl in &ast.decls {
match decl {
Decl::Function(_func) => {
let _func_idx = self.next_function;
self.next_function += 1;
}
Decl::Variable(_var) => {
}
_ => {}
}
}
Ok(())
}
fn get_or_create_type(&mut self, func_type: WasmFuncType) -> u32 {
let idx = self.module.types.len() as u32;
*self.type_cache.entry(func_type.clone()).or_insert_with(|| {
self.module.types.push(func_type);
idx
})
}
fn function_type_from_decl(&self, func: &wasm_types::FunctionDecl) -> WasmFuncType {
let params: Vec<WasmValueType> = func
.parameters
.iter()
.map(|p| self.clang_type_to_wasm_type(&p.ty))
.collect();
let results: Vec<WasmValueType> = if func.return_type.is_void() {
Vec::new()
} else {
vec![self.clang_type_to_wasm_type(&func.return_type)]
};
WasmFuncType { params, results }
}
fn extern_function_type(&self, extern_func: &ExternFunctionDecl) -> WasmFuncType {
let params: Vec<WasmValueType> = extern_func
.parameters
.iter()
.map(|p| self.clang_type_to_wasm_type(&p.ty))
.collect();
let results: Vec<WasmValueType> = if extern_func.return_type.is_void() {
Vec::new()
} else {
vec![self.clang_type_to_wasm_type(&extern_func.return_type)]
};
WasmFuncType { params, results }
}
fn clang_type_to_wasm_type(&self, ty: &Type) -> WasmValueType {
match ty.kind {
TypeKind::Int | TypeKind::Bool | TypeKind::Char | TypeKind::Short => WasmValueType::I32,
TypeKind::Long | TypeKind::LongLong | TypeKind::Int64 => WasmValueType::I64,
TypeKind::Float => WasmValueType::F32,
TypeKind::Double => WasmValueType::F64,
TypeKind::Pointer | TypeKind::Array | TypeKind::Function => {
if self.target.is_wasm64() {
WasmValueType::I64
} else {
WasmValueType::I32
}
}
TypeKind::Void => WasmValueType::Empty,
TypeKind::Struct | TypeKind::Union => {
if self.target.is_wasm64() {
WasmValueType::I64
} else {
WasmValueType::I32
}
}
}
}
fn generate_declaration(&mut self, decl: &wasm_types::Decl) -> Result<(), String> {
match decl {
wasm_types::Decl::Function(_func) => {
}
wasm_types::Decl::Variable(_) => { }
_ => {}
}
Ok(())
}
fn generate_function(
&mut self,
_func_decl: &wasm_types::FunctionDecl,
_body: &CompoundStmt,
) -> Result<(), String> {
Ok(())
}
fn generate_cxx_declaration(&mut self, decl: &wasm_types::CXXDecl) -> Result<(), String> {
match decl {
wasm_types::CXXDecl::Function(cxx_func) => {
let param_type_strs: Vec<String> = cxx_func
.parameters
.iter()
.map(|p| format!("{}", p.ty))
.collect();
let param_refs: Vec<&str> = param_type_strs.iter().map(|s| s.as_str()).collect();
let mangled = mangle_simple_function(&cxx_func.name, ¶m_refs);
let sig = self.cxx_function_type(cxx_func);
let type_idx = self.get_or_create_type(sig);
let func_idx = self.next_function;
self.next_function += 1;
let mangled_str = mangled.into_string();
self.func_map.insert(mangled_str.clone(), func_idx);
self.module.functions.push(WasmFunction {
name: mangled_str,
type_idx,
locals: Vec::new(),
body: Vec::new(),
is_export: cxx_func.is_public,
is_import: false,
import_module: None,
import_name: None,
});
}
_ => {}
}
Ok(())
}
fn cxx_function_type(&self, func: &wasm_types::CXXFunctionDecl) -> WasmFuncType {
let params: Vec<WasmValueType> = func
.parameters
.iter()
.map(|p| self.clang_type_to_wasm_type(&p.ty))
.collect();
let results: Vec<WasmValueType> = if func.return_type.is_void() {
Vec::new()
} else {
vec![self.clang_type_to_wasm_type(&func.return_type)]
};
WasmFuncType { params, results }
}
fn generate_statement(&mut self, stmt: &Stmt) -> Result<(), String> {
match stmt {
Stmt::Compound(compound) => self.generate_compound(&compound.stmts),
Stmt::Return(ret) => self.generate_return(ret),
Stmt::If(if_stmt) => self.generate_if(if_stmt),
Stmt::While(while_stmt) => self.generate_while(while_stmt),
Stmt::For(for_stmt) => self.generate_for(for_stmt),
Stmt::DoWhile(do_stmt) => self.generate_do_while(do_stmt),
Stmt::Expr(expr) => self.generate_expr(expr).map(|_| ()),
Stmt::Decl(_decl) => {
Ok(())
}
Stmt::Break => self.generate_break(),
Stmt::Continue => self.generate_continue(),
Stmt::Switch(switch_stmt) => self.generate_switch(switch_stmt),
Stmt::Labeled(label_stmt) => self.generate_labeled(label_stmt),
Stmt::Goto(goto_stmt) => self.generate_goto(goto_stmt),
Stmt::Null => Ok(()),
}
}
fn generate_compound(&mut self, stmts: &[Stmt]) -> Result<(), String> {
for stmt in stmts {
self.generate_statement(stmt)?;
}
Ok(())
}
fn generate_return(&mut self, ret: &ReturnStmt) -> Result<(), String> {
if let Some(ref expr) = ret.value {
self.generate_expr(expr)?;
}
self.current_body.push(WasmInstruction::Return);
Ok(())
}
fn generate_if(&mut self, if_stmt: &IfStmt) -> Result<(), String> {
self.generate_expr(&if_stmt.condition)?;
self.current_body.push(WasmInstruction::If {
block_type: WasmBlockType::Empty,
});
self.generate_statement(&if_stmt.then_branch)?;
if let Some(ref else_branch) = if_stmt.else_branch {
self.current_body.push(WasmInstruction::Else);
self.generate_statement(else_branch)?;
}
self.current_body.push(WasmInstruction::End);
Ok(())
}
fn generate_while(&mut self, while_stmt: &WhileStmt) -> Result<(), String> {
let break_depth = self.break_depth.len() as u32;
let continue_depth = self.continue_depth.len() as u32;
self.current_body.push(WasmInstruction::Block {
block_type: WasmBlockType::Empty,
label: Some(format!("while_break_{}", break_depth)),
});
self.break_depth.push(0);
self.current_body.push(WasmInstruction::Loop {
block_type: WasmBlockType::Empty,
label: Some(format!("while_loop_{}", continue_depth)),
});
self.continue_depth.push(0);
self.generate_expr(&while_stmt.condition)?;
self.current_body.push(WasmInstruction::I32Eqz);
self.current_body.push(WasmInstruction::BrIf {
label_idx: 1, });
self.generate_statement(&while_stmt.body)?;
self.current_body.push(WasmInstruction::Br {
label_idx: 0, });
self.current_body.push(WasmInstruction::End);
self.continue_depth.pop();
self.current_body.push(WasmInstruction::End);
self.break_depth.pop();
Ok(())
}
fn generate_for(&mut self, for_stmt: &ForStmt) -> Result<(), String> {
if let Some(ref init) = for_stmt.init {
match init {
ForInit::Expr(expr) => {
self.generate_expr(expr)?;
self.current_body.push(WasmInstruction::Drop);
}
ForInit::Decl(_decl) => {
}
}
}
let break_depth = self.break_depth.len() as u32;
let continue_depth = self.continue_depth.len() as u32;
self.current_body.push(WasmInstruction::Block {
block_type: WasmBlockType::Empty,
label: Some(format!("for_break_{}", break_depth)),
});
self.break_depth.push(0);
self.current_body.push(WasmInstruction::Loop {
block_type: WasmBlockType::Empty,
label: Some(format!("for_loop_{}", continue_depth)),
});
self.continue_depth.push(0);
if let Some(ref cond) = for_stmt.condition {
self.generate_expr(cond)?;
self.current_body.push(WasmInstruction::I32Eqz);
self.current_body
.push(WasmInstruction::BrIf { label_idx: 1 });
}
self.generate_statement(&for_stmt.body)?;
if let Some(ref incr) = for_stmt.increment {
self.generate_expr(incr)?;
self.current_body.push(WasmInstruction::Drop);
}
self.current_body.push(WasmInstruction::Br { label_idx: 0 });
self.current_body.push(WasmInstruction::End);
self.continue_depth.pop();
self.current_body.push(WasmInstruction::End);
self.break_depth.pop();
Ok(())
}
fn generate_do_while(&mut self, do_stmt: &DoWhileStmt) -> Result<(), String> {
let break_depth = self.break_depth.len() as u32;
let continue_depth = self.continue_depth.len() as u32;
self.current_body.push(WasmInstruction::Block {
block_type: WasmBlockType::Empty,
label: Some(format!("do_break_{}", break_depth)),
});
self.break_depth.push(0);
self.current_body.push(WasmInstruction::Loop {
block_type: WasmBlockType::Empty,
label: Some(format!("do_loop_{}", continue_depth)),
});
self.continue_depth.push(0);
self.generate_statement(&do_stmt.body)?;
self.generate_expr(&do_stmt.condition)?;
self.current_body
.push(WasmInstruction::BrIf { label_idx: 0 });
self.current_body.push(WasmInstruction::End);
self.continue_depth.pop();
self.current_body.push(WasmInstruction::End);
self.break_depth.pop();
Ok(())
}
fn generate_break(&mut self) -> Result<(), String> {
if self.break_depth.is_empty() {
return Err("Break outside of loop or switch".to_string());
}
let break_idx = self.break_depth.len() as u32 + 1; self.current_body.push(WasmInstruction::Br {
label_idx: break_idx,
});
Ok(())
}
fn generate_continue(&mut self) -> Result<(), String> {
if self.continue_depth.is_empty() {
return Err("Continue outside of loop".to_string());
}
let continue_idx = self.continue_depth.len() as u32; self.current_body.push(WasmInstruction::Br {
label_idx: continue_idx,
});
Ok(())
}
fn generate_switch(&mut self, switch_stmt: &SwitchStmt) -> Result<(), String> {
let break_depth = self.break_depth.len() as u32;
self.generate_expr(&switch_stmt.discriminant)?;
self.current_body.push(WasmInstruction::Block {
block_type: WasmBlockType::Empty,
label: Some(format!("switch_break_{}", break_depth)),
});
self.break_depth.push(0);
self.current_body.push(WasmInstruction::Block {
block_type: WasmBlockType::Empty,
label: Some(format!("switch_inner_{}", break_depth)),
});
let case_values: Vec<u32> = switch_stmt
.cases
.iter()
.filter_map(|c| c.value.as_ref().and_then(|v| self.eval_constant_int(v)))
.collect();
let default_idx = switch_stmt
.cases
.iter()
.position(|c| c.value.is_none())
.unwrap_or(case_values.len());
self.current_body.push(WasmInstruction::BrTable {
targets: case_values.iter().map(|_| 0u32).collect(),
default_target: default_idx as u32,
});
for case in &switch_stmt.cases {
self.generate_statement(&case.body)?;
}
self.current_body.push(WasmInstruction::End);
self.break_depth.pop();
self.current_body.push(WasmInstruction::End);
Ok(())
}
fn generate_labeled(&mut self, label_stmt: &LabeledStmt) -> Result<(), String> {
self.current_body.push(WasmInstruction::Block {
block_type: WasmBlockType::Empty,
label: Some(label_stmt.label.clone()),
});
self.generate_statement(&label_stmt.body)?;
self.current_body.push(WasmInstruction::End);
Ok(())
}
fn generate_goto(&mut self, _goto_stmt: &GotoStmt) -> Result<(), String> {
self.current_body.push(WasmInstruction::Br {
label_idx: 0, });
Ok(())
}
fn generate_decl_stmt(&mut self, _decl: &Decl) -> Result<(), String> {
Ok(())
}
fn emit_default_value(&mut self, ty: WasmValueType) {
match ty {
WasmValueType::I32 => {
self.current_body.push(WasmInstruction::I32Const(0));
}
WasmValueType::I64 => {
self.current_body.push(WasmInstruction::I64Const(0));
}
WasmValueType::F32 => {
self.current_body.push(WasmInstruction::F32Const(0.0));
}
WasmValueType::F64 => {
self.current_body.push(WasmInstruction::F64Const(0.0));
}
_ => {
self.current_body.push(WasmInstruction::I32Const(0));
}
}
}
fn generate_expr(&mut self, expr: &Expr) -> Result<WasmValueType, String> {
match expr {
Expr::IntLiteral(lit) => {
self.current_body
.push(WasmInstruction::I32Const(lit.value as i32));
Ok(WasmValueType::I32)
}
Expr::Int64Literal(lit) => {
self.current_body.push(WasmInstruction::I64Const(lit.value));
Ok(WasmValueType::I64)
}
Expr::FloatLiteral(lit) => {
self.current_body
.push(WasmInstruction::F32Const(lit.value as f32));
Ok(WasmValueType::F32)
}
Expr::DoubleLiteral(lit) => {
self.current_body.push(WasmInstruction::F64Const(lit.value));
Ok(WasmValueType::F64)
}
Expr::StringLiteral(s) => {
let offset = self.allocate_string(s);
self.current_body
.push(WasmInstruction::I32Const(offset as i32));
Ok(WasmValueType::I32)
}
Expr::Ident(name) => {
if let Some(&local_idx) = self.local_map.get(name) {
self.current_body
.push(WasmInstruction::LocalGet { local_idx });
let wasm_type = self
.current_locals
.get(local_idx as usize)
.copied()
.unwrap_or(WasmValueType::I32);
Ok(wasm_type)
} else if let Some(&global_idx) = self.global_map.get(name) {
self.current_body
.push(WasmInstruction::GlobalGet { global_idx });
Ok(WasmValueType::I32) } else if let Some(&func_idx) = self.func_map.get(name) {
self.current_body
.push(WasmInstruction::RefFunc { func_idx });
Ok(WasmValueType::Funcref)
} else {
Err(format!("Undefined variable: {}", name))
}
}
Expr::Binary(bin) => self.generate_binary_expr(bin),
Expr::Unary(unary) => self.generate_unary_expr(unary),
Expr::Call(call) => self.generate_call_expr(call),
Expr::Cast(cast) => self.generate_cast_expr(cast),
Expr::Ternary(tern) => self.generate_ternary_expr(tern),
Expr::ArraySubscript(sub) => self.generate_array_subscript(sub),
Expr::MemberAccess(member) => self.generate_member_access(member),
Expr::Assignment(assign) => self.generate_assignment(assign),
Expr::Sizeof(ty) => {
let size = self.size_of_type(ty);
self.current_body
.push(WasmInstruction::I32Const(size as i32));
Ok(WasmValueType::I32)
}
Expr::Null => {
self.current_body.push(WasmInstruction::I32Const(0));
Ok(WasmValueType::I32)
}
}
}
fn generate_binary_expr(&mut self, bin: &BinaryExpr) -> Result<WasmValueType, String> {
let lhs_type = self.generate_expr(&bin.lhs)?;
let rhs_type = self.generate_expr(&bin.rhs)?;
let op_type = if lhs_type == WasmValueType::I64 || rhs_type == WasmValueType::I64 {
WasmValueType::I64
} else if lhs_type == WasmValueType::F64 || rhs_type == WasmValueType::F64 {
WasmValueType::F64
} else if lhs_type == WasmValueType::F32 || rhs_type == WasmValueType::F32 {
WasmValueType::F32
} else {
WasmValueType::I32
};
match (&bin.op, op_type) {
(BinOp::Add, WasmValueType::I32) => self.current_body.push(WasmInstruction::I32Add),
(BinOp::Add, WasmValueType::I64) => self.current_body.push(WasmInstruction::I64Add),
(BinOp::Add, WasmValueType::F32) => self.current_body.push(WasmInstruction::F32Add),
(BinOp::Add, WasmValueType::F64) => self.current_body.push(WasmInstruction::F64Add),
(BinOp::Sub, WasmValueType::I32) => self.current_body.push(WasmInstruction::I32Sub),
(BinOp::Sub, WasmValueType::I64) => self.current_body.push(WasmInstruction::I64Sub),
(BinOp::Sub, WasmValueType::F32) => self.current_body.push(WasmInstruction::F32Sub),
(BinOp::Sub, WasmValueType::F64) => self.current_body.push(WasmInstruction::F64Sub),
(BinOp::Mul, WasmValueType::I32) => self.current_body.push(WasmInstruction::I32Mul),
(BinOp::Mul, WasmValueType::I64) => self.current_body.push(WasmInstruction::I64Mul),
(BinOp::Mul, WasmValueType::F32) => self.current_body.push(WasmInstruction::F32Mul),
(BinOp::Mul, WasmValueType::F64) => self.current_body.push(WasmInstruction::F64Mul),
(BinOp::Div, WasmValueType::I32) => self.current_body.push(WasmInstruction::I32DivS),
(BinOp::Div, WasmValueType::I64) => self.current_body.push(WasmInstruction::I64DivS),
(BinOp::Div, WasmValueType::F32) => self.current_body.push(WasmInstruction::F32Div),
(BinOp::Div, WasmValueType::F64) => self.current_body.push(WasmInstruction::F64Div),
(BinOp::Mod, WasmValueType::I32) => self.current_body.push(WasmInstruction::I32RemS),
(BinOp::Mod, WasmValueType::I64) => self.current_body.push(WasmInstruction::I64RemS),
(BinOp::BitAnd, _) => self.current_body.push(WasmInstruction::I32And),
(BinOp::BitOr, _) => self.current_body.push(WasmInstruction::I32Or),
(BinOp::BitXor, _) => self.current_body.push(WasmInstruction::I32Xor),
(BinOp::Shl, _) => self.current_body.push(WasmInstruction::I32Shl),
(BinOp::Shr, _) => self.current_body.push(WasmInstruction::I32ShrS),
(BinOp::Eq, WasmValueType::I32) => self.current_body.push(WasmInstruction::I32Eq),
(BinOp::Eq, WasmValueType::I64) => self.current_body.push(WasmInstruction::I64Eq),
(BinOp::Eq, WasmValueType::F32) => self.current_body.push(WasmInstruction::F32Eq),
(BinOp::Eq, WasmValueType::F64) => self.current_body.push(WasmInstruction::F64Eq),
(BinOp::Ne, WasmValueType::I32) => self.current_body.push(WasmInstruction::I32Ne),
(BinOp::Ne, WasmValueType::I64) => self.current_body.push(WasmInstruction::I64Ne),
(BinOp::Ne, WasmValueType::F32) => self.current_body.push(WasmInstruction::F32Ne),
(BinOp::Ne, WasmValueType::F64) => self.current_body.push(WasmInstruction::F64Ne),
(BinOp::Lt, WasmValueType::I32) => self.current_body.push(WasmInstruction::I32LtS),
(BinOp::Lt, WasmValueType::I64) => self.current_body.push(WasmInstruction::I64LtS),
(BinOp::Lt, WasmValueType::F32) => self.current_body.push(WasmInstruction::F32Lt),
(BinOp::Lt, WasmValueType::F64) => self.current_body.push(WasmInstruction::F64Lt),
(BinOp::Gt, WasmValueType::I32) => self.current_body.push(WasmInstruction::I32GtS),
(BinOp::Gt, WasmValueType::I64) => self.current_body.push(WasmInstruction::I64GtS),
(BinOp::Gt, WasmValueType::F32) => self.current_body.push(WasmInstruction::F32Gt),
(BinOp::Gt, WasmValueType::F64) => self.current_body.push(WasmInstruction::F64Gt),
(BinOp::Le, WasmValueType::I32) => self.current_body.push(WasmInstruction::I32LeS),
(BinOp::Le, WasmValueType::I64) => self.current_body.push(WasmInstruction::I64LeS),
(BinOp::Le, WasmValueType::F32) => self.current_body.push(WasmInstruction::F32Le),
(BinOp::Le, WasmValueType::F64) => self.current_body.push(WasmInstruction::F64Le),
(BinOp::Ge, WasmValueType::I32) => self.current_body.push(WasmInstruction::I32GeS),
(BinOp::Ge, WasmValueType::I64) => self.current_body.push(WasmInstruction::I64GeS),
(BinOp::Ge, WasmValueType::F32) => self.current_body.push(WasmInstruction::F32Ge),
(BinOp::Ge, WasmValueType::F64) => self.current_body.push(WasmInstruction::F64Ge),
_ => {
return Ok(WasmValueType::I32);
}
}
match bin.op {
BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge => {
Ok(WasmValueType::I32)
}
BinOp::LogicalAnd | BinOp::LogicalOr => Ok(WasmValueType::I32),
_ => Ok(op_type),
}
}
fn generate_unary_expr(&mut self, unary: &UnaryExpr) -> Result<WasmValueType, String> {
let inner_type = self.generate_expr(&unary.operand)?;
match unary.op {
UnaryOp::Neg => match inner_type {
WasmValueType::I32 => {
self.current_body.push(WasmInstruction::I32Const(0));
self.current_body.push(WasmInstruction::I32Sub);
Ok(WasmValueType::I32)
}
WasmValueType::I64 => {
self.current_body.push(WasmInstruction::I64Const(0));
self.current_body.push(WasmInstruction::I64Sub);
Ok(WasmValueType::I64)
}
WasmValueType::F32 => {
self.current_body.push(WasmInstruction::F32Neg);
Ok(WasmValueType::F32)
}
WasmValueType::F64 => {
self.current_body.push(WasmInstruction::F64Neg);
Ok(WasmValueType::F64)
}
_ => Ok(inner_type),
},
UnaryOp::Not => {
self.current_body.push(WasmInstruction::I32Eqz);
Ok(WasmValueType::I32)
}
UnaryOp::BitNot => {
self.current_body.push(WasmInstruction::I32Const(-1));
self.current_body.push(WasmInstruction::I32Xor);
Ok(WasmValueType::I32)
}
UnaryOp::AddrOf => {
Ok(inner_type)
}
UnaryOp::Deref => {
match inner_type {
WasmValueType::I32 => {
self.current_body.push(WasmInstruction::I32Load {
align: 4,
offset: 0,
});
}
WasmValueType::I64 => {
self.current_body.push(WasmInstruction::I64Load {
align: 8,
offset: 0,
});
}
WasmValueType::F32 => {
self.current_body.push(WasmInstruction::F32Load {
align: 4,
offset: 0,
});
}
WasmValueType::F64 => {
self.current_body.push(WasmInstruction::F64Load {
align: 8,
offset: 0,
});
}
_ => {}
}
Ok(inner_type)
}
UnaryOp::PreInc | UnaryOp::PostInc => {
self.current_body.push(WasmInstruction::I32Const(1));
self.current_body.push(WasmInstruction::I32Add);
Ok(WasmValueType::I32)
}
UnaryOp::PreDec | UnaryOp::PostDec => {
self.current_body.push(WasmInstruction::I32Const(1));
self.current_body.push(WasmInstruction::I32Sub);
Ok(WasmValueType::I32)
}
}
}
fn generate_call_expr(&mut self, call: &CallExpr) -> Result<WasmValueType, String> {
for arg in &call.args {
self.generate_expr(arg)?;
}
if let Some(&func_idx) = self.func_map.get(&call.name) {
self.current_body.push(WasmInstruction::Call { func_idx });
} else {
self.generate_expr(&Expr::Ident(call.name.clone()))?;
let type_idx = 0u32; self.current_body.push(WasmInstruction::CallIndirect {
type_idx,
table_idx: 0,
});
}
Ok(WasmValueType::I32)
}
fn generate_cast_expr(&mut self, cast: &CastExpr) -> Result<WasmValueType, String> {
let src_type = self.generate_expr(&cast.expr)?;
let dst_type = self.clang_type_to_wasm_type(&cast.target_type);
match (src_type, dst_type) {
(WasmValueType::I32, WasmValueType::I64) => {
self.current_body.push(WasmInstruction::I64ExtendI32S);
}
(WasmValueType::I64, WasmValueType::I32) => {
self.current_body.push(WasmInstruction::I32WrapI64);
}
(WasmValueType::I32, WasmValueType::F32) => {
self.current_body.push(WasmInstruction::F32ConvertI32S);
}
(WasmValueType::I32, WasmValueType::F64) => {
self.current_body.push(WasmInstruction::F64ConvertI32S);
}
(WasmValueType::I64, WasmValueType::F32) => {
self.current_body.push(WasmInstruction::F32ConvertI64S);
}
(WasmValueType::I64, WasmValueType::F64) => {
self.current_body.push(WasmInstruction::F64ConvertI64S);
}
(WasmValueType::F32, WasmValueType::I32) => {
self.current_body.push(WasmInstruction::I32TruncF32S);
}
(WasmValueType::F64, WasmValueType::I32) => {
self.current_body.push(WasmInstruction::I32TruncF64S);
}
(WasmValueType::F32, WasmValueType::I64) => {
self.current_body.push(WasmInstruction::I64TruncF32S);
}
(WasmValueType::F64, WasmValueType::I64) => {
self.current_body.push(WasmInstruction::I64TruncF64S);
}
(WasmValueType::F32, WasmValueType::F64) => {
self.current_body.push(WasmInstruction::F64PromoteF32);
}
(WasmValueType::F64, WasmValueType::F32) => {
self.current_body.push(WasmInstruction::F32DemoteF64);
}
_ => { }
}
Ok(dst_type)
}
fn generate_ternary_expr(&mut self, tern: &TernaryExpr) -> Result<WasmValueType, String> {
self.generate_expr(&tern.condition)?;
let result_type = self.clang_type_to_wasm_type(&tern.true_expr.get_type());
let block_type = match result_type {
WasmValueType::I32 => WasmBlockType::I32,
WasmValueType::I64 => WasmBlockType::I64,
WasmValueType::F32 => WasmBlockType::F32,
WasmValueType::F64 => WasmBlockType::F64,
_ => WasmBlockType::Empty,
};
self.current_body.push(WasmInstruction::If { block_type });
self.generate_expr(&tern.true_expr)?;
self.current_body.push(WasmInstruction::Else);
self.generate_expr(&tern.false_expr)?;
self.current_body.push(WasmInstruction::End);
Ok(result_type)
}
fn generate_array_subscript(
&mut self,
sub: &ArraySubscriptExpr,
) -> Result<WasmValueType, String> {
let _base_type = self.generate_expr(&sub.array)?;
self.generate_expr(&sub.index)?;
let elem_size = self.size_of_type(&sub.element_type);
if elem_size > 1 {
self.current_body
.push(WasmInstruction::I32Const(elem_size as i32));
self.current_body.push(WasmInstruction::I32Mul);
}
self.current_body.push(WasmInstruction::I32Add);
let elem_wasm_type = self.clang_type_to_wasm_type(&sub.element_type);
match elem_wasm_type {
WasmValueType::I32 => {
self.current_body.push(WasmInstruction::I32Load {
align: 4,
offset: 0,
});
}
WasmValueType::I64 => {
self.current_body.push(WasmInstruction::I64Load {
align: 8,
offset: 0,
});
}
WasmValueType::F32 => {
self.current_body.push(WasmInstruction::F32Load {
align: 4,
offset: 0,
});
}
WasmValueType::F64 => {
self.current_body.push(WasmInstruction::F64Load {
align: 8,
offset: 0,
});
}
_ => {}
}
Ok(elem_wasm_type)
}
fn generate_member_access(
&mut self,
member: &MemberAccessExpr,
) -> Result<WasmValueType, String> {
let _base_type = self.generate_expr(&member.object)?;
let offset = 0i32;
if offset != 0 {
self.current_body.push(WasmInstruction::I32Const(offset));
self.current_body.push(WasmInstruction::I32Add);
}
let field_wasm_type = self.clang_type_to_wasm_type(&member.field_type);
match field_wasm_type {
WasmValueType::I32 => {
self.current_body.push(WasmInstruction::I32Load {
align: 4,
offset: 0,
});
}
WasmValueType::I64 => {
self.current_body.push(WasmInstruction::I64Load {
align: 8,
offset: 0,
});
}
_ => {}
}
Ok(field_wasm_type)
}
fn generate_assignment(&mut self, assign: &AssignmentExpr) -> Result<WasmValueType, String> {
let rhs_type = self.generate_expr(&assign.rhs)?;
match &*assign.lhs {
Expr::Ident(name) => {
if let Some(&local_idx) = self.local_map.get(name) {
self.current_body
.push(WasmInstruction::LocalTee { local_idx });
} else if let Some(&global_idx) = self.global_map.get(name) {
self.current_body
.push(WasmInstruction::GlobalSet { global_idx });
}
}
_ => {
}
}
Ok(rhs_type)
}
fn size_of_type(&self, ty: &Type) -> u32 {
match ty.kind {
TypeKind::Int | TypeKind::Float => 4,
TypeKind::Char | TypeKind::Bool => 1,
TypeKind::Short => 2,
TypeKind::Long | TypeKind::LongLong | TypeKind::Double | TypeKind::Int64 => 8,
TypeKind::Pointer => {
if self.target.is_wasm64() {
8
} else {
4
}
}
TypeKind::Void => 0,
TypeKind::Array => {
if let Some(len) = ty.array_len {
match &ty.element_type {
Some(elem_ty) => self.size_of_type(elem_ty) * len as u32,
None => 0,
}
} else {
0
}
}
_ => 4,
}
}
fn eval_constant_int(&self, expr: &Expr) -> Option<u32> {
match expr {
Expr::IntLiteral(lit) => Some(lit.value as u32),
_ => None,
}
}
fn allocate_string(&mut self, s: &str) -> u32 {
if let Some(&offset) = self.string_table.get(s) {
return offset;
}
let offset = self.next_memory_offset;
let bytes = s.as_bytes();
self.static_data.extend_from_slice(bytes);
self.static_data.push(0);
self.next_memory_offset += bytes.len() as u32 + 1;
self.string_table.insert(s.to_string(), offset);
offset
}
fn allocate_global(&mut self, var: &wasm_types::VariableDecl) -> Result<(), String> {
let wasm_type = self.clang_type_to_wasm_type(&var.ty);
let global_idx = self.next_global;
self.next_global += 1;
self.global_map.insert(var.name.clone(), global_idx);
let size = self.size_of_type(&var.ty);
let offset = self.next_memory_offset;
self.next_memory_offset += size;
while self.next_memory_offset % 4 != 0 {
self.static_data.push(0);
self.next_memory_offset += 1;
}
self.module.globals.push(WasmGlobal {
name: var.name.clone(),
value_type: wasm_type,
mutable: !var.is_const,
init_expr: WasmInitExpr::I32Const(offset as i32),
});
Ok(())
}
fn emit_data_section(&mut self) -> Result<(), String> {
if self.static_data.is_empty() {
return Ok(());
}
self.module.data_segments.push(WasmDataSegment {
memory_idx: 0,
offset_expr: WasmInitExpr::I32Const(0),
data: self.static_data.clone(),
});
Ok(())
}
fn emit_exports(&mut self) -> Result<(), String> {
for func in &self.module.functions {
if func.is_export && !func.is_import {
if let Some(&func_idx) = self.func_map.get(&func.name) {
self.module.exports.push(WasmExport {
name: func.name.clone(),
kind: WasmExternalKind::Function,
index: func_idx,
});
}
}
}
Ok(())
}
}
#[derive(Debug, Clone, Default)]
pub struct X86WASMModule {
pub target_triple: Option<String>,
pub data_layout: Option<String>,
pub types: Vec<WasmFuncType>,
pub imports: Vec<WasmImport>,
pub functions: Vec<WasmFunction>,
pub tables: Vec<WasmTable>,
pub memories: Vec<WasmMemory>,
pub globals: Vec<WasmGlobal>,
pub exports: Vec<WasmExport>,
pub start_function: Option<u32>,
pub elements: Vec<WasmElement>,
pub data_segments: Vec<WasmDataSegment>,
pub custom_sections: Vec<X86WASMCustomSection>,
pub tags: Vec<WasmTag>,
pub data_count: Option<u32>,
pub features: X86WASMFeatureFlags,
}
impl X86WASMModule {
pub fn new() -> Self {
Self {
target_triple: None,
data_layout: None,
types: Vec::new(),
imports: Vec::new(),
functions: Vec::new(),
tables: Vec::new(),
memories: Vec::new(),
globals: Vec::new(),
exports: Vec::new(),
start_function: None,
elements: Vec::new(),
data_segments: Vec::new(),
custom_sections: Vec::new(),
tags: Vec::new(),
data_count: None,
features: X86WASMFeatureFlags::default(),
}
}
pub fn add_import_function(&mut self, module: &str, name: &str, type_idx: u32) -> u32 {
let idx = self.imports.len() as u32;
self.imports.push(WasmImport {
module: module.to_string(),
name: name.to_string(),
kind: WasmExternalKind::Function,
type_idx,
});
idx
}
pub fn add_import_memory(
&mut self,
module: &str,
name: &str,
min_pages: u64,
max_pages: Option<u64>,
) -> u32 {
let idx = self.imports.len() as u32;
self.imports.push(WasmImport {
module: module.to_string(),
name: name.to_string(),
kind: WasmExternalKind::Memory,
type_idx: 0,
});
self.memories.push(WasmMemory {
min_pages,
max_pages,
is_shared: false,
is_64: false,
});
idx
}
pub fn add_import_global(
&mut self,
module: &str,
name: &str,
_value_type: WasmValueType,
_mutable: bool,
) -> u32 {
let idx = self.imports.len() as u32;
self.imports.push(WasmImport {
module: module.to_string(),
name: name.to_string(),
kind: WasmExternalKind::Global,
type_idx: 0,
});
idx
}
pub fn emit_binary(&self) -> Vec<u8> {
let encoder = X86WASMEncoder::new();
encoder.encode_module(self)
}
pub fn emit_wat(&self) -> String {
let emitter = X86WASMTextEmitter::new();
emitter.emit_module(self)
}
pub fn validate(&self) -> Result<(), String> {
let validator = X86WASMValidator::default();
validator.validate_module(self)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct WasmFuncType {
pub params: Vec<WasmValueType>,
pub results: Vec<WasmValueType>,
}
impl Default for WasmFuncType {
fn default() -> Self {
Self {
params: Vec::new(),
results: Vec::new(),
}
}
}
impl fmt::Display for WasmFuncType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(")?;
for param in &self.params {
write!(f, " {}", param)?;
}
write!(f, " ) -> (")?;
for result in &self.results {
write!(f, " {}", result)?;
}
write!(f, " )")
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum WasmValueType {
I32,
I64,
F32,
F64,
V128,
Funcref,
Externref,
Empty,
Exnref,
Eqref,
Anyref,
}
impl Default for WasmValueType {
fn default() -> Self {
WasmValueType::I32
}
}
impl fmt::Display for WasmValueType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
WasmValueType::I32 => write!(f, "i32"),
WasmValueType::I64 => write!(f, "i64"),
WasmValueType::F32 => write!(f, "f32"),
WasmValueType::F64 => write!(f, "f64"),
WasmValueType::V128 => write!(f, "v128"),
WasmValueType::Funcref => write!(f, "funcref"),
WasmValueType::Externref => write!(f, "externref"),
WasmValueType::Empty => write!(f, ""),
WasmValueType::Exnref => write!(f, "exnref"),
WasmValueType::Eqref => write!(f, "eqref"),
WasmValueType::Anyref => write!(f, "anyref"),
}
}
}
impl WasmValueType {
pub fn is_numeric(&self) -> bool {
matches!(
self,
WasmValueType::I32 | WasmValueType::I64 | WasmValueType::F32 | WasmValueType::F64
)
}
pub fn is_reference(&self) -> bool {
matches!(
self,
WasmValueType::Funcref
| WasmValueType::Externref
| WasmValueType::Exnref
| WasmValueType::Eqref
| WasmValueType::Anyref
)
}
pub fn size(&self) -> u32 {
match self {
WasmValueType::I32 | WasmValueType::F32 => 4,
WasmValueType::I64 | WasmValueType::F64 => 8,
WasmValueType::V128 => 16,
_ => 4,
}
}
pub fn as_byte(&self) -> i8 {
match self {
WasmValueType::I32 => WASM_TYPE_I32,
WasmValueType::I64 => WASM_TYPE_I64,
WasmValueType::F32 => WASM_TYPE_F32,
WasmValueType::F64 => WASM_TYPE_F64,
WasmValueType::V128 => WASM_TYPE_V128,
WasmValueType::Funcref => WASM_TYPE_FUNCREF,
WasmValueType::Externref => WASM_TYPE_EXTERNREF,
WasmValueType::Empty => WASM_TYPE_EMPTY,
_ => WASM_TYPE_I32,
}
}
pub fn from_byte(b: i8) -> Option<Self> {
match b {
WASM_TYPE_I32 => Some(WasmValueType::I32),
WASM_TYPE_I64 => Some(WasmValueType::I64),
WASM_TYPE_F32 => Some(WasmValueType::F32),
WASM_TYPE_F64 => Some(WasmValueType::F64),
WASM_TYPE_V128 => Some(WasmValueType::V128),
WASM_TYPE_FUNCREF => Some(WasmValueType::Funcref),
WASM_TYPE_EXTERNREF => Some(WasmValueType::Externref),
WASM_TYPE_EMPTY => Some(WasmValueType::Empty),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WasmBlockType {
Empty,
I32,
I64,
F32,
F64,
V128,
Funcref,
Externref,
TypeIndex(u32),
}
impl Default for WasmBlockType {
fn default() -> Self {
WasmBlockType::Empty
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum WasmExternalKind {
Function,
Table,
Memory,
Global,
Tag,
}
impl WasmExternalKind {
pub fn as_byte(&self) -> u8 {
match self {
WasmExternalKind::Function => WASM_EXTERNAL_KIND_FUNCTION,
WasmExternalKind::Table => WASM_EXTERNAL_KIND_TABLE,
WasmExternalKind::Memory => WASM_EXTERNAL_KIND_MEMORY,
WasmExternalKind::Global => WASM_EXTERNAL_KIND_GLOBAL,
WasmExternalKind::Tag => WASM_EXTERNAL_KIND_TAG,
}
}
}
#[derive(Debug, Clone)]
pub struct WasmFunction {
pub name: String,
pub type_idx: u32,
pub locals: Vec<WasmValueType>,
pub body: Vec<WasmInstruction>,
pub is_export: bool,
pub is_import: bool,
pub import_module: Option<String>,
pub import_name: Option<String>,
}
#[derive(Debug, Clone)]
pub struct WasmImport {
pub module: String,
pub name: String,
pub kind: WasmExternalKind,
pub type_idx: u32,
}
#[derive(Debug, Clone)]
pub struct WasmExport {
pub name: String,
pub kind: WasmExternalKind,
pub index: u32,
}
#[derive(Debug, Clone)]
pub struct WasmTable {
pub element_type: WasmValueType,
pub min_size: u32,
pub max_size: Option<u32>,
}
#[derive(Debug, Clone)]
pub struct WasmMemory {
pub min_pages: u64,
pub max_pages: Option<u64>,
pub is_shared: bool,
pub is_64: bool,
}
#[derive(Debug, Clone)]
pub struct WasmGlobal {
pub name: String,
pub value_type: WasmValueType,
pub mutable: bool,
pub init_expr: WasmInitExpr,
}
#[derive(Debug, Clone)]
pub struct WasmElement {
pub table_idx: u32,
pub offset_expr: WasmInitExpr,
pub init: Vec<u32>,
}
#[derive(Debug, Clone)]
pub struct WasmDataSegment {
pub memory_idx: u32,
pub offset_expr: WasmInitExpr,
pub data: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct WasmTag {
pub attribute: u8,
pub type_idx: u32,
}
#[derive(Debug, Clone)]
pub enum WasmInitExpr {
I32Const(i32),
I64Const(i64),
F32Const(f32),
F64Const(f64),
GlobalGet(u32),
RefNull(WasmValueType),
RefFunc(u32),
I32Add,
I64Add,
End,
}
#[derive(Debug, Clone)]
pub enum WasmInstruction {
Unreachable,
Nop,
Block {
block_type: WasmBlockType,
label: Option<String>,
},
Loop {
block_type: WasmBlockType,
label: Option<String>,
},
If {
block_type: WasmBlockType,
},
Else,
End,
Br {
label_idx: u32,
},
BrIf {
label_idx: u32,
},
BrTable {
targets: Vec<u32>,
default_target: u32,
},
Return,
Call {
func_idx: u32,
},
CallIndirect {
type_idx: u32,
table_idx: u32,
},
ReturnCall {
func_idx: u32,
},
ReturnCallIndirect {
type_idx: u32,
},
CallRef {
type_idx: u32,
},
Drop,
Select,
SelectT {
num_results: u32,
},
LocalGet {
local_idx: u32,
},
LocalSet {
local_idx: u32,
},
LocalTee {
local_idx: u32,
},
GlobalGet {
global_idx: u32,
},
GlobalSet {
global_idx: u32,
},
TableGet {
table_idx: u32,
},
TableSet {
table_idx: u32,
},
I32Load {
align: u32,
offset: u32,
},
I64Load {
align: u32,
offset: u32,
},
F32Load {
align: u32,
offset: u32,
},
F64Load {
align: u32,
offset: u32,
},
I32Load8S {
align: u32,
offset: u32,
},
I32Load8U {
align: u32,
offset: u32,
},
I32Load16S {
align: u32,
offset: u32,
},
I32Load16U {
align: u32,
offset: u32,
},
I64Load8S {
align: u32,
offset: u32,
},
I64Load8U {
align: u32,
offset: u32,
},
I64Load16S {
align: u32,
offset: u32,
},
I64Load16U {
align: u32,
offset: u32,
},
I64Load32S {
align: u32,
offset: u32,
},
I64Load32U {
align: u32,
offset: u32,
},
I32Store {
align: u32,
offset: u32,
},
I64Store {
align: u32,
offset: u32,
},
F32Store {
align: u32,
offset: u32,
},
F64Store {
align: u32,
offset: u32,
},
I32Store8 {
align: u32,
offset: u32,
},
I32Store16 {
align: u32,
offset: u32,
},
I64Store8 {
align: u32,
offset: u32,
},
I64Store16 {
align: u32,
offset: u32,
},
I64Store32 {
align: u32,
offset: u32,
},
MemorySize {
mem_idx: u32,
},
MemoryGrow {
mem_idx: u32,
},
I32Const(i32),
I64Const(i64),
F32Const(f32),
F64Const(f64),
I32Eqz,
I32Eq,
I32Ne,
I32LtS,
I32LtU,
I32GtS,
I32GtU,
I32LeS,
I32LeU,
I32GeS,
I32GeU,
I64Eqz,
I64Eq,
I64Ne,
I64LtS,
I64LtU,
I64GtS,
I64GtU,
I64LeS,
I64LeU,
I64GeS,
I64GeU,
F32Eq,
F32Ne,
F32Lt,
F32Gt,
F32Le,
F32Ge,
F64Eq,
F64Ne,
F64Lt,
F64Gt,
F64Le,
F64Ge,
I32Clz,
I32Ctz,
I32Popcnt,
I32Add,
I32Sub,
I32Mul,
I32DivS,
I32DivU,
I32RemS,
I32RemU,
I32And,
I32Or,
I32Xor,
I32Shl,
I32ShrS,
I32ShrU,
I32Rotl,
I32Rotr,
I64Clz,
I64Ctz,
I64Popcnt,
I64Add,
I64Sub,
I64Mul,
I64DivS,
I64DivU,
I64RemS,
I64RemU,
I64And,
I64Or,
I64Xor,
I64Shl,
I64ShrS,
I64ShrU,
I64Rotl,
I64Rotr,
F32Abs,
F32Neg,
F32Ceil,
F32Floor,
F32Trunc,
F32Nearest,
F32Sqrt,
F32Add,
F32Sub,
F32Mul,
F32Div,
F32Min,
F32Max,
F32CopySign,
F64Abs,
F64Neg,
F64Ceil,
F64Floor,
F64Trunc,
F64Nearest,
F64Sqrt,
F64Add,
F64Sub,
F64Mul,
F64Div,
F64Min,
F64Max,
F64CopySign,
I32WrapI64,
I32TruncF32S,
I32TruncF32U,
I32TruncF64S,
I32TruncF64U,
I64ExtendI32S,
I64ExtendI32U,
I64TruncF32S,
I64TruncF32U,
I64TruncF64S,
I64TruncF64U,
F32ConvertI32S,
F32ConvertI32U,
F32ConvertI64S,
F32ConvertI64U,
F32DemoteF64,
F64ConvertI32S,
F64ConvertI32U,
F64ConvertI64S,
F64ConvertI64U,
F64PromoteF32,
I32ReinterpretF32,
I64ReinterpretF64,
F32ReinterpretI32,
F64ReinterpretI64,
RefNull {
ref_type: WasmValueType,
},
RefIsNull,
RefFunc {
func_idx: u32,
},
RefEq,
RefAsNonNull,
BrOnNull {
label_idx: u32,
},
BrOnNonNull {
label_idx: u32,
},
V128Load {
align: u32,
offset: u32,
},
V128Store {
align: u32,
offset: u32,
},
V128Const(Vec<u8>),
I8X16Shuffle([u8; 16]),
I8X16Swizzle,
I8X16Splat,
I16X8Splat,
I32X4Splat,
I64X2Splat,
F32X4Splat,
F64X2Splat,
I8X16ExtractLaneS(u8),
I8X16ExtractLaneU(u8),
I8X16ReplaceLane(u8),
I16X8ExtractLaneS(u8),
I16X8ExtractLaneU(u8),
I16X8ReplaceLane(u8),
I32X4ExtractLane(u8),
I32X4ReplaceLane(u8),
I64X2ExtractLane(u8),
I64X2ReplaceLane(u8),
F32X4ExtractLane(u8),
F32X4ReplaceLane(u8),
F64X2ExtractLane(u8),
F64X2ReplaceLane(u8),
I8X16Eq,
I8X16Ne,
I8X16LtS,
I8X16LtU,
I8X16GtS,
I8X16GtU,
I8X16LeS,
I8X16LeU,
I8X16GeS,
I8X16GeU,
I16X8Eq,
I16X8Ne,
I16X8LtS,
I16X8LtU,
I16X8GtS,
I16X8GtU,
I16X8LeS,
I16X8LeU,
I16X8GeS,
I16X8GeU,
I32X4Eq,
I32X4Ne,
I32X4LtS,
I32X4LtU,
I32X4GtS,
I32X4GtU,
I32X4LeS,
I32X4LeU,
I32X4GeS,
I32X4GeU,
F32X4Eq,
F32X4Ne,
F32X4Lt,
F32X4Gt,
F32X4Le,
F32X4Ge,
F64X2Eq,
F64X2Ne,
F64X2Lt,
F64X2Gt,
F64X2Le,
F64X2Ge,
V128Not,
V128And,
V128AndNot,
V128Or,
V128Xor,
V128BitSelect,
V128AnyTrue,
V128Load8Lane(u8),
V128Load16Lane(u8),
V128Load32Lane(u8),
V128Load64Lane(u8),
V128Store8Lane(u8),
V128Store16Lane(u8),
V128Store32Lane(u8),
V128Store64Lane(u8),
V128Load32Zero,
V128Load64Zero,
F32X4Add,
F32X4Sub,
F32X4Mul,
F32X4Div,
F32X4Min,
F32X4Max,
F32X4Abs,
F32X4Neg,
F32X4Sqrt,
F64X2Add,
F64X2Sub,
F64X2Mul,
F64X2Div,
F64X2Min,
F64X2Max,
F64X2Abs,
F64X2Neg,
F64X2Sqrt,
I8X16Add,
I8X16Sub,
I16X8Add,
I16X8Sub,
I32X4Add,
I32X4Sub,
I64X2Add,
I64X2Sub,
I8X16Mul,
I16X8Mul,
I32X4Mul,
I64X2Mul,
I32Extend8S,
I32Extend16S,
I64Extend8S,
I64Extend16S,
I64Extend32S,
MemoryInit {
data_idx: u32,
},
DataDrop {
data_idx: u32,
},
MemoryCopy,
MemoryFill,
TableInit {
elem_idx: u32,
table_idx: u32,
},
ElemDrop {
elem_idx: u32,
},
TableCopy {
dst_table: u32,
src_table: u32,
},
TableGrow {
table_idx: u32,
},
TableSize {
table_idx: u32,
},
TableFill {
table_idx: u32,
},
MemoryAtomicNotify {
align: u32,
offset: u32,
},
MemoryAtomicWait32 {
align: u32,
offset: u32,
},
MemoryAtomicWait64 {
align: u32,
offset: u32,
},
AtomicFence,
}
impl Default for WasmInstruction {
fn default() -> Self {
WasmInstruction::Nop
}
}
#[derive(Debug, Clone)]
pub struct WasmLabel {
pub label: String,
pub arity: u32,
pub is_loop: bool,
}
#[derive(Debug, Clone)]
pub struct X86WASMCustomSection {
pub name: String,
pub data: Vec<u8>,
}
#[derive(Debug, Clone, Default)]
pub struct EmscriptenMetadata {
pub emscripten_version: String,
pub initial_stack_pointer: u64,
pub initial_memory_base: u64,
pub table_size: u32,
pub dynamic_base: u64,
pub dynamic_top_ptr: u64,
pub temp_double_ptr: u64,
pub abort_on_cannot_grow_memory: bool,
pub standalone_wasm: bool,
pub export_name: Option<String>,
}
#[derive(Debug, Clone)]
pub struct WasiImport {
pub module: String,
pub function: String,
pub params: Vec<WasmValueType>,
pub results: Vec<WasmValueType>,
}
#[derive(Debug, Clone)]
pub struct X86WASMWASI {
config: X86WASMConfig,
wasi_imports: Vec<WasiImport>,
wasi_version: String,
is_reactor: bool,
is_command: bool,
}
impl X86WASMWASI {
pub fn new(config: &X86WASMConfig) -> Self {
Self {
config: config.clone(),
wasi_imports: Vec::new(),
wasi_version: "wasi_snapshot_preview1".to_string(),
is_reactor: config.reactor_mode,
is_command: config.command_mode,
}
}
pub fn add_wasi_imports(&self, module: &mut X86WASMModule) -> Result<(), String> {
let wasi_functions = self.wasi_preview1_functions();
for (name, params, results) in &wasi_functions {
let func_type = WasmFuncType {
params: params.clone(),
results: results.clone(),
};
let type_idx = module
.types
.iter()
.position(|t| t == &func_type)
.map(|i| i as u32)
.unwrap_or_else(|| {
let idx = module.types.len() as u32;
module.types.push(func_type);
idx
});
module.imports.push(WasmImport {
module: self.wasi_version.clone(),
name: name.clone(),
kind: WasmExternalKind::Function,
type_idx,
});
}
Ok(())
}
pub fn add_wasi_start(&self, module: &mut X86WASMModule) -> Result<(), String> {
if self.is_command {
let start_type = WasmFuncType {
params: Vec::new(),
results: Vec::new(),
};
let type_idx = module
.types
.iter()
.position(|t| t == &start_type)
.map(|i| i as u32)
.unwrap_or_else(|| {
let idx = module.types.len() as u32;
module.types.push(start_type);
idx
});
let start_idx = module
.functions
.iter()
.position(|f| f.name == "_start")
.map(|i| i as u32);
if let Some(idx) = start_idx {
module.start_function = Some(idx);
} else {
let func_idx = module.functions.len() as u32;
module.functions.push(WasmFunction {
name: "_start".to_string(),
type_idx,
locals: Vec::new(),
body: vec![
WasmInstruction::Call { func_idx: 0 }, WasmInstruction::End,
],
is_export: false,
is_import: false,
import_module: None,
import_name: None,
});
module.start_function = Some(func_idx);
}
}
Ok(())
}
fn wasi_preview1_functions(&self) -> Vec<(String, Vec<WasmValueType>, Vec<WasmValueType>)> {
let i32 = WasmValueType::I32;
let i64 = WasmValueType::I64;
vec![
("args_get".into(), vec![i32, i32], vec![i32]),
("args_sizes_get".into(), vec![i32, i32], vec![i32]),
("environ_get".into(), vec![i32, i32], vec![i32]),
("environ_sizes_get".into(), vec![i32, i32], vec![i32]),
("clock_res_get".into(), vec![i32, i32], vec![i32]),
("clock_time_get".into(), vec![i32, i64, i32], vec![i32]),
("fd_advise".into(), vec![i32, i64, i64, i32], vec![i32]),
("fd_allocate".into(), vec![i32, i64, i64], vec![i32]),
("fd_close".into(), vec![i32], vec![i32]),
("fd_datasync".into(), vec![i32], vec![i32]),
("fd_fdstat_get".into(), vec![i32, i32], vec![i32]),
("fd_fdstat_set_flags".into(), vec![i32, i32], vec![i32]),
("fd_filestat_get".into(), vec![i32, i32], vec![i32]),
("fd_filestat_set_size".into(), vec![i32, i64], vec![i32]),
(
"fd_filestat_set_times".into(),
vec![i32, i64, i64, i32],
vec![i32],
),
("fd_pread".into(), vec![i32, i32, i32, i64, i32], vec![i32]),
("fd_prestat_get".into(), vec![i32, i32], vec![i32]),
("fd_prestat_dir_name".into(), vec![i32, i32, i32], vec![i32]),
("fd_pwrite".into(), vec![i32, i32, i32, i64, i32], vec![i32]),
("fd_read".into(), vec![i32, i32, i32, i32], vec![i32]),
(
"fd_readdir".into(),
vec![i32, i32, i32, i64, i32],
vec![i32],
),
("fd_renumber".into(), vec![i32, i32], vec![i32]),
("fd_seek".into(), vec![i32, i64, i32, i32], vec![i32]),
("fd_sync".into(), vec![i32], vec![i32]),
("fd_tell".into(), vec![i32, i32], vec![i32]),
("fd_write".into(), vec![i32, i32, i32, i32], vec![i32]),
(
"path_create_directory".into(),
vec![i32, i32, i32],
vec![i32],
),
(
"path_filestat_get".into(),
vec![i32, i32, i32, i32, i32],
vec![i32],
),
(
"path_filestat_set_times".into(),
vec![i32, i32, i32, i32, i64, i64, i32],
vec![i32],
),
(
"path_link".into(),
vec![i32, i32, i32, i32, i32, i32],
vec![i32],
),
(
"path_open".into(),
vec![i32, i32, i32, i32, i32, i64, i64, i32, i32],
vec![i32],
),
(
"path_readlink".into(),
vec![i32, i32, i32, i32, i32, i32],
vec![i32],
),
(
"path_remove_directory".into(),
vec![i32, i32, i32],
vec![i32],
),
(
"path_rename".into(),
vec![i32, i32, i32, i32, i32, i32],
vec![i32],
),
(
"path_symlink".into(),
vec![i32, i32, i32, i32, i32],
vec![i32],
),
("path_unlink_file".into(), vec![i32, i32, i32], vec![i32]),
("poll_oneoff".into(), vec![i32, i32, i32, i32], vec![i32]),
("proc_exit".into(), vec![i32], vec![]),
("proc_raise".into(), vec![i32], vec![i32]),
("sched_yield".into(), vec![], vec![i32]),
("random_get".into(), vec![i32, i32], vec![i32]),
("sock_accept".into(), vec![i32, i32, i32], vec![i32]),
(
"sock_recv".into(),
vec![i32, i32, i32, i32, i32, i32],
vec![i32],
),
("sock_send".into(), vec![i32, i32, i32, i32, i32], vec![i32]),
("sock_shutdown".into(), vec![i32, i32], vec![i32]),
]
}
pub fn is_valid_wasi_function(name: &str) -> bool {
let functions = [
"args_get",
"args_sizes_get",
"environ_get",
"environ_sizes_get",
"clock_res_get",
"clock_time_get",
"fd_advise",
"fd_allocate",
"fd_close",
"fd_datasync",
"fd_fdstat_get",
"fd_fdstat_set_flags",
"fd_filestat_get",
"fd_filestat_set_size",
"fd_filestat_set_times",
"fd_pread",
"fd_prestat_get",
"fd_prestat_dir_name",
"fd_pwrite",
"fd_read",
"fd_readdir",
"fd_renumber",
"fd_seek",
"fd_sync",
"fd_tell",
"fd_write",
"path_create_directory",
"path_filestat_get",
"path_filestat_set_times",
"path_link",
"path_open",
"path_readlink",
"path_remove_directory",
"path_rename",
"path_symlink",
"path_unlink_file",
"poll_oneoff",
"proc_exit",
"proc_raise",
"sched_yield",
"random_get",
"sock_accept",
"sock_recv",
"sock_send",
"sock_shutdown",
];
functions.contains(&name)
}
pub fn wasi_version(&self) -> &str {
&self.wasi_version
}
pub fn generate_reactor_exports(&self, module: &mut X86WASMModule) {
if self.is_reactor {
if !module.exports.iter().any(|e| e.name == "memory") {
module.exports.push(WasmExport {
name: "memory".to_string(),
kind: WasmExternalKind::Memory,
index: 0,
});
}
if let Some(idx) = module
.functions
.iter()
.position(|f| f.name == "_initialize")
{
module.exports.push(WasmExport {
name: "_initialize".to_string(),
kind: WasmExternalKind::Function,
index: idx as u32,
});
}
}
}
}
#[derive(Debug, Clone)]
pub struct X86WASMEmscripten {
config: X86WASMConfig,
metadata: EmscriptenMetadata,
}
impl X86WASMEmscripten {
pub fn new(config: &X86WASMConfig) -> Self {
Self {
config: config.clone(),
metadata: EmscriptenMetadata::default(),
}
}
pub fn add_emscripten_runtime(&self, module: &mut X86WASMModule) -> Result<(), String> {
self.add_emscripten_imports(module)?;
self.add_emscripten_globals(module)?;
self.add_stack_init(module)?;
Ok(())
}
pub fn add_js_glue(&self, module: &mut X86WASMModule) -> Result<(), String> {
let run_script_type = WasmFuncType {
params: vec![WasmValueType::I32],
results: Vec::new(),
};
let type_idx = module.types.len() as u32;
module.types.push(run_script_type);
module.imports.push(WasmImport {
module: "env".to_string(),
name: "emscripten_run_script".to_string(),
kind: WasmExternalKind::Function,
type_idx,
});
let sleep_type = WasmFuncType {
params: vec![WasmValueType::I32],
results: Vec::new(),
};
let type_idx = module.types.len() as u32;
module.types.push(sleep_type);
module.imports.push(WasmImport {
module: "env".to_string(),
name: "emscripten_sleep".to_string(),
kind: WasmExternalKind::Function,
type_idx,
});
let asm_const_type = WasmFuncType {
params: vec![WasmValueType::I32],
results: vec![WasmValueType::I32],
};
let type_idx = module.types.len() as u32;
module.types.push(asm_const_type);
module.imports.push(WasmImport {
module: "env".to_string(),
name: "emscripten_asm_const_int".to_string(),
kind: WasmExternalKind::Function,
type_idx,
});
Ok(())
}
fn add_emscripten_imports(&self, module: &mut X86WASMModule) -> Result<(), String> {
let i32 = WasmValueType::I32;
let i64 = WasmValueType::I64;
let f64 = WasmValueType::F64;
let emscripten_imports = vec![
("__assert_fail", vec![i32, i32, i32, i32], vec![]),
("__cxa_throw", vec![i32, i32, i32], vec![]),
("__cxa_allocate_exception", vec![i32], vec![i32]),
("__cxa_begin_catch", vec![i32], vec![i32]),
("__cxa_end_catch", vec![], vec![]),
("__cxa_rethrow", vec![], vec![]),
("__resumeException", vec![i32], vec![]),
("setThrew", vec![i32, i32], vec![]),
("__syscall6", vec![i32, i32], vec![i32]),
("__syscall54", vec![i32, i32], vec![i32]),
("__syscall140", vec![i32, i32], vec![i32]),
("__syscall146", vec![i32, i32], vec![i32]),
("abort", vec![], vec![]),
("exit", vec![i32], vec![]),
("_Exit", vec![i32], vec![]),
("__stdio_write", vec![i32, i32, i32], vec![i32]),
("__stdio_read", vec![i32, i32, i32], vec![i32]),
("__stdio_seek", vec![i32, i64, i32], vec![i32]),
("__stdio_close", vec![i32], vec![i32]),
("__stdout_write", vec![i32, i32, i32], vec![i32]),
("__stderr_write", vec![i32, i32, i32], vec![i32]),
("emscripten_memcpy_big", vec![i32, i32, i32], vec![i32]),
];
for (name, params, results) in &emscripten_imports {
let func_type = WasmFuncType {
params: params.clone(),
results: results.clone(),
};
let type_idx = module
.types
.iter()
.position(|t| t == &func_type)
.map(|i| i as u32)
.unwrap_or_else(|| {
let idx = module.types.len() as u32;
module.types.push(func_type);
idx
});
module.imports.push(WasmImport {
module: "env".to_string(),
name: name.to_string(),
kind: WasmExternalKind::Function,
type_idx,
});
}
Ok(())
}
fn add_emscripten_globals(&self, module: &mut X86WASMModule) -> Result<(), String> {
let _stack_top_type = WasmFuncType {
params: Vec::new(),
results: vec![WasmValueType::I32],
};
module.globals.push(WasmGlobal {
name: "__stack_pointer".to_string(),
value_type: WasmValueType::I32,
mutable: true,
init_expr: WasmInitExpr::I32Const(WASM_STACK_SIZE as i32),
});
module.globals.push(WasmGlobal {
name: "__heap_base".to_string(),
value_type: WasmValueType::I32,
mutable: false,
init_expr: WasmInitExpr::I32Const(WASM_STACK_SIZE as i32 + 4096),
});
module.globals.push(WasmGlobal {
name: "__data_end".to_string(),
value_type: WasmValueType::I32,
mutable: false,
init_expr: WasmInitExpr::I32Const(WASM_STACK_SIZE as i32 + 4096),
});
module.globals.push(WasmGlobal {
name: "STACKTOP".to_string(),
value_type: WasmValueType::I32,
mutable: true,
init_expr: WasmInitExpr::I32Const(WASM_STACK_SIZE as i32),
});
module.globals.push(WasmGlobal {
name: "STACK_MAX".to_string(),
value_type: WasmValueType::I32,
mutable: false,
init_expr: WasmInitExpr::I32Const(WASM_STACK_SIZE as i32),
});
Ok(())
}
fn add_stack_init(&self, module: &mut X86WASMModule) -> Result<(), String> {
if module.memories.is_empty() {
module.memories.push(WasmMemory {
min_pages: self.config.initial_memory_pages,
max_pages: self.config.maximum_memory_pages,
is_shared: self.config.enable_threads,
is_64: self.config.enable_memory64,
});
}
Ok(())
}
pub fn map_sse_to_simd128(sse_intrinsic: &str) -> Option<Vec<WasmInstruction>> {
match sse_intrinsic {
"_mm_add_ps" => Some(vec![WasmInstruction::F32X4Add]),
"_mm_sub_ps" => Some(vec![WasmInstruction::F32X4Sub]),
"_mm_mul_ps" => Some(vec![WasmInstruction::F32X4Mul]),
"_mm_div_ps" => Some(vec![WasmInstruction::F32X4Div]),
"_mm_and_ps" => Some(vec![WasmInstruction::V128And]),
"_mm_or_ps" => Some(vec![WasmInstruction::V128Or]),
"_mm_xor_ps" => Some(vec![WasmInstruction::V128Xor]),
"_mm_add_pd" => Some(vec![WasmInstruction::F64X2Add]),
"_mm_sub_pd" => Some(vec![WasmInstruction::F64X2Sub]),
"_mm_mul_pd" => Some(vec![WasmInstruction::F64X2Mul]),
"_mm_div_pd" => Some(vec![WasmInstruction::F64X2Div]),
"_mm_sqrt_ps" => Some(vec![WasmInstruction::F32X4Sqrt]),
"_mm_sqrt_pd" => Some(vec![WasmInstruction::F64X2Sqrt]),
"_mm_min_ps" => Some(vec![WasmInstruction::F32X4Min]),
"_mm_max_ps" => Some(vec![WasmInstruction::F32X4Max]),
"_mm_min_pd" => Some(vec![WasmInstruction::F64X2Min]),
"_mm_max_pd" => Some(vec![WasmInstruction::F64X2Max]),
"_mm_set1_ps" => Some(vec![WasmInstruction::F32X4Splat]),
"_mm_set1_pd" => Some(vec![WasmInstruction::F64X2Splat]),
"_mm_load_ps" => Some(vec![WasmInstruction::V128Load {
align: 16,
offset: 0,
}]),
"_mm_store_ps" => Some(vec![WasmInstruction::V128Store {
align: 16,
offset: 0,
}]),
"_mm_load_si128" => Some(vec![WasmInstruction::V128Load {
align: 16,
offset: 0,
}]),
"_mm_store_si128" => Some(vec![WasmInstruction::V128Store {
align: 16,
offset: 0,
}]),
"_mm_loadu_ps" => Some(vec![WasmInstruction::V128Load {
align: 1,
offset: 0,
}]),
"_mm_storeu_ps" => Some(vec![WasmInstruction::V128Store {
align: 1,
offset: 0,
}]),
"_mm_cmpeq_ps" => Some(vec![WasmInstruction::F32X4Eq]),
"_mm_cmplt_ps" => Some(vec![WasmInstruction::F32X4Lt]),
"_mm_cmple_ps" => Some(vec![WasmInstruction::F32X4Le]),
"_mm_cmpgt_ps" => Some(vec![WasmInstruction::F32X4Gt]),
"_mm_cmpge_ps" => Some(vec![WasmInstruction::F32X4Ge]),
_ => None,
}
}
pub fn map_avx_to_simd128(avx_intrinsic: &str) -> Option<Vec<Vec<WasmInstruction>>> {
match avx_intrinsic {
"_mm256_add_ps" => Some(vec![
vec![WasmInstruction::F32X4Add], vec![WasmInstruction::F32X4Add], ]),
"_mm256_mul_ps" => Some(vec![
vec![WasmInstruction::F32X4Mul],
vec![WasmInstruction::F32X4Mul],
]),
"_mm256_add_pd" => Some(vec![
vec![WasmInstruction::F64X2Add],
vec![WasmInstruction::F64X2Add],
]),
_ => None,
}
}
pub fn map_gles_to_webgl(gl_function: &str) -> Option<String> {
match gl_function {
"glClear" | "glClearColor" | "glDrawArrays" | "glDrawElements" | "glEnable"
| "glDisable" | "glViewport" | "glScissor" | "glBindBuffer" | "glBufferData"
| "glBufferSubData" | "glCreateShader" | "glShaderSource" | "glCompileShader"
| "glCreateProgram" | "glAttachShader" | "glLinkProgram" | "glUseProgram"
| "glUniform1f" | "glUniform2f" | "glUniform3f" | "glUniform4f" | "glUniform1i"
| "glUniformMatrix4fv" | "glGenTextures" | "glBindTexture" | "glTexImage2D"
| "glTexParameteri" | "glActiveTexture" | "glBlendFunc" => {
Some(gl_function.to_string())
}
"glGenVertexArraysOES" => Some("glGenVertexArrays".to_string()),
"glBindVertexArrayOES" => Some("glBindVertexArray".to_string()),
"glDeleteVertexArraysOES" => Some("glDeleteVertexArrays".to_string()),
_ => None,
}
}
pub fn metadata(&self) -> &EmscriptenMetadata {
&self.metadata
}
}
#[derive(Debug, Clone)]
pub struct X86WASMLinker {
config: X86WASMConfig,
output_module: X86WASMModule,
import_map: HashMap<(String, String), u32>,
export_map: HashMap<String, u32>,
function_symbols: HashMap<String, (usize, u32)>,
global_symbols: HashMap<String, (usize, u32)>,
memory_offset: u64,
stack_pointer_init: u64,
heap_base: u64,
enable_lto: bool,
}
impl X86WASMLinker {
pub fn new(config: &X86WASMConfig) -> Self {
Self {
config: config.clone(),
output_module: X86WASMModule::new(),
import_map: HashMap::new(),
export_map: HashMap::new(),
function_symbols: HashMap::new(),
global_symbols: HashMap::new(),
memory_offset: 0,
stack_pointer_init: 0,
heap_base: 0,
enable_lto: config.enable_lto,
}
}
pub fn link(&mut self, modules: &[X86WASMModule]) -> Result<X86WASMModule, String> {
self.collect_symbols(modules)?;
self.merge_types(modules)?;
self.merge_imports(modules)?;
self.merge_functions(modules)?;
self.merge_tables(modules)?;
self.merge_memories(modules)?;
self.merge_globals(modules)?;
self.merge_exports(modules)?;
self.merge_data_segments(modules)?;
self.merge_elements(modules)?;
self.merge_custom_sections(modules)?;
self.resolve_start_function(modules)?;
Ok(self.output_module.clone())
}
fn collect_symbols(&mut self, modules: &[X86WASMModule]) -> Result<(), String> {
for (mod_idx, module) in modules.iter().enumerate() {
for export in &module.exports {
if let WasmExternalKind::Function = export.kind {
self.function_symbols
.insert(export.name.clone(), (mod_idx, export.index));
}
}
for export in &module.exports {
if let WasmExternalKind::Global = export.kind {
self.global_symbols
.insert(export.name.clone(), (mod_idx, export.index));
}
}
for (func_idx, func) in module.functions.iter().enumerate() {
self.function_symbols
.entry(func.name.clone())
.or_insert((mod_idx, func_idx as u32));
}
}
Ok(())
}
fn merge_types(&mut self, modules: &[X86WASMModule]) -> Result<(), String> {
let mut type_map: HashMap<WasmFuncType, u32> = HashMap::new();
for module in modules {
for func_type in &module.types {
if !type_map.contains_key(func_type) {
let idx = self.output_module.types.len() as u32;
type_map.insert(func_type.clone(), idx);
self.output_module.types.push(func_type.clone());
}
}
}
Ok(())
}
fn merge_imports(&mut self, modules: &[X86WASMModule]) -> Result<(), String> {
for module in modules {
for import in &module.imports {
let key = (import.module.clone(), import.name.clone());
if !self.import_map.contains_key(&key) {
let idx = self.output_module.imports.len() as u32;
let new_import = WasmImport {
module: import.module.clone(),
name: import.name.clone(),
kind: import.kind,
type_idx: import.type_idx, };
self.output_module.imports.push(new_import);
self.import_map.insert(key, idx);
}
}
}
Ok(())
}
fn merge_functions(&mut self, modules: &[X86WASMModule]) -> Result<(), String> {
for module in modules {
for func in &module.functions {
if !func.is_import {
let mut new_func = func.clone();
self.output_module.functions.push(new_func);
}
}
}
Ok(())
}
fn merge_tables(&mut self, modules: &[X86WASMModule]) -> Result<(), String> {
let mut max_min_size: u32 = 0;
let mut max_max_size: Option<u32> = None;
for module in modules {
for table in &module.tables {
if table.min_size > max_min_size {
max_min_size = table.min_size;
}
if let Some(max) = table.max_size {
match max_max_size {
None => max_max_size = Some(max),
Some(existing) if max > existing => max_max_size = Some(max),
_ => {}
}
}
}
}
if max_min_size > 0 {
self.output_module.tables.push(WasmTable {
element_type: WasmValueType::Funcref,
min_size: max_min_size,
max_size: max_max_size,
});
}
Ok(())
}
fn merge_memories(&mut self, modules: &[X86WASMModule]) -> Result<(), String> {
let mut total_pages: u64 = 0;
let mut max_pages: Option<u64> = None;
let mut is_shared = false;
let mut is_64 = false;
for module in modules {
for memory in &module.memories {
total_pages = total_pages.max(memory.min_pages);
if let Some(max) = memory.max_pages {
match max_pages {
None => max_pages = Some(max),
Some(existing) if max > existing => max_pages = Some(max),
_ => {}
}
}
is_shared = is_shared || memory.is_shared;
is_64 = is_64 || memory.is_64;
}
}
total_pages = total_pages.max(self.config.initial_memory_pages);
if let Some(config_max) = self.config.maximum_memory_pages {
match max_pages {
None => max_pages = Some(config_max),
Some(existing) if config_max > existing => max_pages = Some(config_max),
_ => {}
}
}
if total_pages > 0 {
self.output_module.memories.push(WasmMemory {
min_pages: total_pages,
max_pages,
is_shared,
is_64,
});
}
Ok(())
}
fn merge_globals(&mut self, modules: &[X86WASMModule]) -> Result<(), String> {
for module in modules {
for global in &module.globals {
let mut new_global = global.clone();
self.output_module.globals.push(new_global);
}
}
Ok(())
}
fn merge_exports(&mut self, modules: &[X86WASMModule]) -> Result<(), String> {
for module in modules {
for export in &module.exports {
let key = export.name.clone();
if !self.export_map.contains_key(&key) {
let idx = self.output_module.exports.len() as u32;
self.output_module.exports.push(WasmExport {
name: export.name.clone(),
kind: export.kind,
index: export.index,
});
self.export_map.insert(key, idx);
}
}
}
Ok(())
}
fn merge_data_segments(&mut self, modules: &[X86WASMModule]) -> Result<(), String> {
let mut running_offset: u64 = 0;
running_offset = self.config.stack_size;
self.stack_pointer_init = running_offset;
for module in modules {
for segment in &module.data_segments {
let mut new_segment = segment.clone();
new_segment.offset_expr = WasmInitExpr::I32Const(running_offset as i32);
running_offset += segment.data.len() as u64;
if running_offset % 8 != 0 {
let pad = 8 - (running_offset % 8);
running_offset += pad;
}
self.output_module.data_segments.push(new_segment);
}
}
self.heap_base = running_offset;
self.memory_offset = running_offset;
Ok(())
}
fn merge_elements(&mut self, modules: &[X86WASMModule]) -> Result<(), String> {
for module in modules {
for element in &module.elements {
self.output_module.elements.push(element.clone());
}
}
Ok(())
}
fn merge_custom_sections(&mut self, modules: &[X86WASMModule]) -> Result<(), String> {
for module in modules {
for section in &module.custom_sections {
if section.name == "name" {
if self
.output_module
.custom_sections
.iter()
.any(|s| s.name == "name")
{
continue;
}
}
self.output_module.custom_sections.push(section.clone());
}
}
Ok(())
}
fn resolve_start_function(&mut self, modules: &[X86WASMModule]) -> Result<(), String> {
for module in modules {
if let Some(start_idx) = module.start_function {
if let Some(pos) = self
.output_module
.functions
.iter()
.position(|f| f.name == "_start")
{
self.output_module.start_function = Some(pos as u32);
} else {
self.output_module.start_function = Some(start_idx);
}
break;
}
}
Ok(())
}
pub fn heap_base(&self) -> u64 {
self.heap_base
}
pub fn stack_pointer_init(&self) -> u64 {
self.stack_pointer_init
}
}
#[derive(Debug, Clone)]
pub struct X86WASMOptimizer {
opt_level: u8,
enable_dce: bool,
enable_inlining: bool,
enable_memory_packing: bool,
enable_size_opt: bool,
enable_const_prop: bool,
inline_threshold: usize,
}
impl X86WASMOptimizer {
pub fn new(config: &X86WASMConfig) -> Self {
let opt_level = config.optimization_level;
Self {
opt_level,
enable_dce: opt_level >= 1,
enable_inlining: opt_level >= 2,
enable_memory_packing: opt_level >= 2,
enable_size_opt: opt_level >= 2,
enable_const_prop: opt_level >= 3,
inline_threshold: match opt_level {
0 => 0,
1 => 20,
2 => 50,
3 => 100,
_ => 50,
},
}
}
pub fn optimize(&self, module: &mut X86WASMModule) -> Result<(), String> {
if self.opt_level == 0 {
return Ok(());
}
if self.enable_const_prop {
self.constant_propagation(module)?;
}
if self.enable_dce {
self.dead_code_elimination(module)?;
}
if self.enable_inlining {
self.function_inlining(module)?;
}
if self.enable_memory_packing {
self.memory_packing(module)?;
}
if self.enable_size_opt {
self.code_size_optimization(module)?;
}
if self.opt_level >= 2 {
self.peephole_optimizations(module)?;
}
Ok(())
}
fn dead_code_elimination(&self, module: &mut X86WASMModule) -> Result<(), String> {
let mut used_functions: HashSet<u32> = HashSet::new();
if let Some(start) = module.start_function {
used_functions.insert(start);
}
for export in &module.exports {
if let WasmExternalKind::Function = export.kind {
used_functions.insert(export.index);
}
}
loop {
let prev_count = used_functions.len();
for func in &module.functions {
let func_idx = module.functions.iter().position(|f| std::ptr::eq(f, func));
if let Some(idx) = func_idx {
if used_functions.contains(&(idx as u32)) {
for inst in &func.body {
if let WasmInstruction::Call { func_idx } = inst {
used_functions.insert(*func_idx);
}
}
}
}
}
if used_functions.len() == prev_count {
break;
}
}
let mut keep_indices: Vec<usize> = Vec::new();
for (i, func) in module.functions.iter().enumerate() {
if used_functions.contains(&(i as u32)) || func.is_import {
keep_indices.push(i);
}
}
if keep_indices.len() < module.functions.len() {
let mut old_to_new: HashMap<u32, u32> = HashMap::new();
let mut kept: Vec<WasmFunction> = keep_indices
.iter()
.enumerate()
.map(|(new_idx, &old_idx)| {
old_to_new.insert(old_idx as u32, new_idx as u32);
module.functions[old_idx].clone()
})
.collect();
for func in &mut kept {
for inst in &mut func.body {
if let WasmInstruction::Call { func_idx } = inst {
if let Some(&new_idx) = old_to_new.get(func_idx) {
*func_idx = new_idx;
}
}
}
}
module.functions = kept;
}
for func in &mut module.functions {
func.body = self.remove_unreachable_code(&func.body);
}
Ok(())
}
fn remove_unreachable_code(&self, body: &[WasmInstruction]) -> Vec<WasmInstruction> {
let mut result = Vec::new();
let mut is_unreachable = false;
for inst in body {
if is_unreachable {
if matches!(inst, WasmInstruction::End | WasmInstruction::Else) {
is_unreachable = false;
result.push(inst.clone());
}
continue;
}
match inst {
WasmInstruction::Unreachable
| WasmInstruction::Return
| WasmInstruction::Br { .. } => {
result.push(inst.clone());
is_unreachable = true;
}
WasmInstruction::BrIf { .. } | WasmInstruction::BrTable { .. } => {
result.push(inst.clone());
}
_ => {
result.push(inst.clone());
}
}
}
result
}
fn function_inlining(&self, module: &mut X86WASMModule) -> Result<(), String> {
if self.inline_threshold == 0 {
return Ok(());
}
let mut inline_candidates: HashMap<u32, Vec<WasmInstruction>> = HashMap::new();
for (i, func) in module.functions.iter().enumerate() {
if !func.is_import && func.body.len() <= self.inline_threshold {
let has_call = func.body.iter().any(|inst| {
matches!(
inst,
WasmInstruction::Call { .. } | WasmInstruction::CallIndirect { .. }
)
});
if !has_call {
inline_candidates.insert(i as u32, func.body.clone());
}
}
}
if inline_candidates.is_empty() {
return Ok(());
}
for func_idx in 0..module.functions.len() {
let func = &module.functions[func_idx];
let mut new_body = Vec::new();
for inst in &func.body {
if let WasmInstruction::Call {
func_idx: callee_idx,
} = inst
{
if let Some(inline_body) = inline_candidates.get(callee_idx) {
for inlined_inst in inline_body
.iter()
.filter(|i| !matches!(i, WasmInstruction::End))
{
new_body.push(inlined_inst.clone());
}
continue;
}
}
new_body.push(inst.clone());
}
if new_body.len() > func.body.len() {
if let Some(func) = module.functions.get_mut(func_idx) {
func.body = new_body;
}
}
}
self.dead_code_elimination(module)?;
Ok(())
}
fn memory_packing(&self, module: &mut X86WASMModule) -> Result<(), String> {
module
.data_segments
.sort_by(|a, b| b.data.len().cmp(&a.data.len()));
let mut offset: u64 = 1024; for segment in &mut module.data_segments {
segment.offset_expr = WasmInitExpr::I32Const(offset as i32);
offset += segment.data.len() as u64;
if offset % 16 != 0 {
offset += 16 - (offset % 16);
}
}
Ok(())
}
fn code_size_optimization(&self, module: &mut X86WASMModule) -> Result<(), String> {
for func in &mut module.functions {
func.body
.retain(|inst| !matches!(inst, WasmInstruction::Nop));
let mut optimized = Vec::new();
let mut i = 0;
while i < func.body.len() {
if i + 1 < func.body.len() {
if let (WasmInstruction::LocalTee { local_idx }, WasmInstruction::Drop) =
(&func.body[i], &func.body[i + 1])
{
optimized.push(WasmInstruction::LocalSet {
local_idx: *local_idx,
});
i += 2;
continue;
}
}
if i + 1 < func.body.len() {
if let (WasmInstruction::I32Const(0), WasmInstruction::I32Add) =
(&func.body[i], &func.body[i + 1])
{
i += 2;
continue;
}
}
if i + 1 < func.body.len() {
if let (WasmInstruction::I32Const(1), WasmInstruction::I32Mul) =
(&func.body[i], &func.body[i + 1])
{
i += 2;
continue;
}
}
if i + 1 < func.body.len() {
if let (WasmInstruction::I32Const(0), WasmInstruction::I32Sub) =
(&func.body[i], &func.body[i + 1])
{
i += 2;
continue;
}
}
optimized.push(func.body[i].clone());
i += 1;
}
func.body = optimized;
}
Ok(())
}
fn constant_propagation(&self, module: &mut X86WASMModule) -> Result<(), String> {
for func in &mut module.functions {
let mut new_body = Vec::new();
let mut i = 0;
while i < func.body.len() {
if i + 2 < func.body.len() {
if let (WasmInstruction::I32Const(a), WasmInstruction::I32Const(b)) =
(&func.body[i], &func.body[i + 1])
{
match &func.body[i + 2] {
WasmInstruction::I32Add => {
new_body.push(WasmInstruction::I32Const(a.wrapping_add(*b)));
i += 3;
continue;
}
WasmInstruction::I32Sub => {
new_body.push(WasmInstruction::I32Const(a.wrapping_sub(*b)));
i += 3;
continue;
}
WasmInstruction::I32Mul => {
new_body.push(WasmInstruction::I32Const(a.wrapping_mul(*b)));
i += 3;
continue;
}
WasmInstruction::I32And => {
new_body.push(WasmInstruction::I32Const(a & b));
i += 3;
continue;
}
WasmInstruction::I32Or => {
new_body.push(WasmInstruction::I32Const(a | b));
i += 3;
continue;
}
WasmInstruction::I32Xor => {
new_body.push(WasmInstruction::I32Const(a ^ b));
i += 3;
continue;
}
_ => {}
}
}
}
if i + 2 < func.body.len() {
if let (WasmInstruction::I64Const(a), WasmInstruction::I64Const(b)) =
(&func.body[i], &func.body[i + 1])
{
match &func.body[i + 2] {
WasmInstruction::I64Add => {
new_body.push(WasmInstruction::I64Const(a.wrapping_add(*b)));
i += 3;
continue;
}
WasmInstruction::I64Sub => {
new_body.push(WasmInstruction::I64Const(a.wrapping_sub(*b)));
i += 3;
continue;
}
WasmInstruction::I64Mul => {
new_body.push(WasmInstruction::I64Const(a.wrapping_mul(*b)));
i += 3;
continue;
}
_ => {}
}
}
}
new_body.push(func.body[i].clone());
i += 1;
}
func.body = new_body;
}
Ok(())
}
fn peephole_optimizations(&self, module: &mut X86WASMModule) -> Result<(), String> {
for func in &mut module.functions {
let mut optimized = Vec::new();
let mut i = 0;
while i < func.body.len() {
if matches!(func.body[i], WasmInstruction::Br { label_idx: 0 }) {
i += 1;
continue; }
optimized.push(func.body[i].clone());
i += 1;
}
func.body = optimized;
}
Ok(())
}
}
#[derive(Debug, Clone, Default)]
pub struct X86WASMEncoder {
buffer: Vec<u8>,
}
impl X86WASMEncoder {
pub fn new() -> Self {
Self { buffer: Vec::new() }
}
pub fn write_u32_leb128(&self, buf: &mut Vec<u8>, value: u32) {
let mut v = value;
loop {
let mut byte = (v & 0x7f) as u8;
v >>= 7;
if v != 0 {
byte |= 0x80;
}
buf.push(byte);
if v == 0 {
break;
}
}
}
pub fn write_i32_leb128(&self, buf: &mut Vec<u8>, value: i32) {
let mut v = value;
loop {
let mut byte = (v & 0x7f) as u8;
v >>= 7;
if (v == 0 && (byte & 0x40) == 0) || (v == -1 && (byte & 0x40) != 0) {
buf.push(byte);
break;
}
byte |= 0x80;
buf.push(byte);
}
}
pub fn write_u64_leb128(&self, buf: &mut Vec<u8>, value: u64) {
let mut v = value;
loop {
let mut byte = (v & 0x7f) as u8;
v >>= 7;
if v != 0 {
byte |= 0x80;
}
buf.push(byte);
if v == 0 {
break;
}
}
}
pub fn write_i64_leb128(&self, buf: &mut Vec<u8>, value: i64) {
let mut v = value;
loop {
let mut byte = (v & 0x7f) as u8;
v >>= 7;
if (v == 0 && (byte & 0x40) == 0) || (v == -1 && (byte & 0x40) != 0) {
buf.push(byte);
break;
}
byte |= 0x80;
buf.push(byte);
}
}
pub fn write_name(&self, buf: &mut Vec<u8>, name: &str) {
let bytes = name.as_bytes();
self.write_u32_leb128(buf, bytes.len() as u32);
buf.extend_from_slice(bytes);
}
pub fn write_f32(&self, buf: &mut Vec<u8>, value: f32) {
buf.extend_from_slice(&value.to_le_bytes());
}
pub fn write_f64(&self, buf: &mut Vec<u8>, value: f64) {
buf.extend_from_slice(&value.to_le_bytes());
}
pub fn write_value_type(&self, buf: &mut Vec<u8>, ty: &WasmValueType) {
buf.push(ty.as_byte() as u8);
}
pub fn write_block_type(&self, buf: &mut Vec<u8>, bt: &WasmBlockType) {
match bt {
WasmBlockType::Empty => buf.push(0x40),
WasmBlockType::I32 => buf.push(0x7f),
WasmBlockType::I64 => buf.push(0x7e),
WasmBlockType::F32 => buf.push(0x7d),
WasmBlockType::F64 => buf.push(0x7c),
WasmBlockType::V128 => buf.push(0x7b),
WasmBlockType::Funcref => buf.push(0x70),
WasmBlockType::Externref => buf.push(0x6f),
WasmBlockType::TypeIndex(idx) => {
self.write_i32_leb128(buf, *idx as i32);
}
}
}
pub fn read_u32_leb128(buf: &[u8], pos: &mut usize) -> Option<u32> {
let mut result: u32 = 0;
let mut shift: u32 = 0;
loop {
if *pos >= buf.len() {
return None;
}
let byte = buf[*pos];
*pos += 1;
result |= ((byte & 0x7f) as u32) << shift;
if (byte & 0x80) == 0 {
break;
}
shift += 7;
if shift >= 35 {
return None;
}
}
Some(result)
}
pub fn read_i32_leb128(buf: &[u8], pos: &mut usize) -> Option<i32> {
let mut result: i32 = 0;
let mut shift: u32 = 0;
loop {
if *pos >= buf.len() {
return None;
}
let byte = buf[*pos];
*pos += 1;
result |= ((byte & 0x7f) as i32) << shift;
shift += 7;
if (byte & 0x80) == 0 {
if shift < 32 && (byte & 0x40) != 0 {
result |= !0 << shift;
}
break;
}
if shift >= 35 {
return None;
}
}
Some(result)
}
pub fn read_name(buf: &[u8], pos: &mut usize) -> Option<String> {
let len = Self::read_u32_leb128(buf, pos)? as usize;
if *pos + len > buf.len() {
return None;
}
let bytes = &buf[*pos..*pos + len];
*pos += len;
String::from_utf8(bytes.to_vec()).ok()
}
pub fn encode_module(&self, module: &X86WASMModule) -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(&WASM_MAGIC.to_le_bytes());
buf.extend_from_slice(&WASM_VERSION.to_le_bytes());
if !module.types.is_empty() {
self.encode_type_section(&mut buf, module);
}
if !module.imports.is_empty() {
self.encode_import_section(&mut buf, module);
}
let non_import_funcs: Vec<&WasmFunction> =
module.functions.iter().filter(|f| !f.is_import).collect();
if !non_import_funcs.is_empty() {
self.encode_function_section(&mut buf, &non_import_funcs);
}
if !module.tables.is_empty() {
self.encode_table_section(&mut buf, module);
}
if !module.memories.is_empty() {
self.encode_memory_section(&mut buf, module);
}
if !module.globals.is_empty() {
self.encode_global_section(&mut buf, module);
}
if !module.exports.is_empty() {
self.encode_export_section(&mut buf, module);
}
if let Some(start) = module.start_function {
self.encode_start_section(&mut buf, start);
}
if !module.elements.is_empty() {
self.encode_element_section(&mut buf, module);
}
if !non_import_funcs.is_empty() {
self.encode_code_section(&mut buf, &non_import_funcs);
}
if !module.data_segments.is_empty() {
self.encode_data_section(&mut buf, module);
}
for section in &module.custom_sections {
self.encode_custom_section(&mut buf, section);
}
buf
}
fn encode_type_section(&self, buf: &mut Vec<u8>, module: &X86WASMModule) {
let mut payload = Vec::new();
self.write_u32_leb128(&mut payload, module.types.len() as u32);
for func_type in &module.types {
payload.push(0x60);
self.write_u32_leb128(&mut payload, func_type.params.len() as u32);
for param in &func_type.params {
self.write_value_type(&mut payload, param);
}
self.write_u32_leb128(&mut payload, func_type.results.len() as u32);
for result in &func_type.results {
self.write_value_type(&mut payload, result);
}
}
self.write_section(buf, WASM_SECTION_TYPE, &payload);
}
fn encode_import_section(&self, buf: &mut Vec<u8>, module: &X86WASMModule) {
let mut payload = Vec::new();
self.write_u32_leb128(&mut payload, module.imports.len() as u32);
for import in &module.imports {
self.write_name(&mut payload, &import.module);
self.write_name(&mut payload, &import.name);
payload.push(import.kind.as_byte());
match import.kind {
WasmExternalKind::Function => {
self.write_u32_leb128(&mut payload, import.type_idx);
}
WasmExternalKind::Memory => {
if let Some(memory) = module.memories.first() {
let flags = if memory.max_pages.is_some() {
WASM_LIMIT_MIN_MAX
} else {
WASM_LIMIT_MIN
};
payload.push(flags);
self.write_u64_leb128(&mut payload, memory.min_pages);
if let Some(max) = memory.max_pages {
self.write_u64_leb128(&mut payload, max);
}
}
}
_ => {}
}
}
self.write_section(buf, WASM_SECTION_IMPORT, &payload);
}
fn encode_function_section(&self, buf: &mut Vec<u8>, functions: &[&WasmFunction]) {
let mut payload = Vec::new();
self.write_u32_leb128(&mut payload, functions.len() as u32);
for func in functions {
self.write_u32_leb128(&mut payload, func.type_idx);
}
self.write_section(buf, WASM_SECTION_FUNCTION, &payload);
}
fn encode_table_section(&self, buf: &mut Vec<u8>, module: &X86WASMModule) {
let mut payload = Vec::new();
self.write_u32_leb128(&mut payload, module.tables.len() as u32);
for table in &module.tables {
payload.push(0x70);
let flags = if table.max_size.is_some() {
WASM_LIMIT_MIN_MAX
} else {
WASM_LIMIT_MIN
};
payload.push(flags);
self.write_u32_leb128(&mut payload, table.min_size);
if let Some(max) = table.max_size {
self.write_u32_leb128(&mut payload, max);
}
}
self.write_section(buf, WASM_SECTION_TABLE, &payload);
}
fn encode_memory_section(&self, buf: &mut Vec<u8>, module: &X86WASMModule) {
let mut payload = Vec::new();
self.write_u32_leb128(&mut payload, module.memories.len() as u32);
for memory in &module.memories {
let flags = if memory.max_pages.is_some() {
WASM_LIMIT_MIN_MAX
} else {
WASM_LIMIT_MIN
};
payload.push(flags);
self.write_u64_leb128(&mut payload, memory.min_pages);
if let Some(max) = memory.max_pages {
self.write_u64_leb128(&mut payload, max);
}
}
self.write_section(buf, WASM_SECTION_MEMORY, &payload);
}
fn encode_global_section(&self, buf: &mut Vec<u8>, module: &X86WASMModule) {
let mut payload = Vec::new();
self.write_u32_leb128(&mut payload, module.globals.len() as u32);
for global in &module.globals {
self.write_value_type(&mut payload, &global.value_type);
payload.push(if global.mutable {
WASM_MUT_VAR
} else {
WASM_MUT_CONST
});
self.encode_init_expr(&mut payload, &global.init_expr);
}
self.write_section(buf, WASM_SECTION_GLOBAL, &payload);
}
fn encode_export_section(&self, buf: &mut Vec<u8>, module: &X86WASMModule) {
let mut payload = Vec::new();
self.write_u32_leb128(&mut payload, module.exports.len() as u32);
for export in &module.exports {
self.write_name(&mut payload, &export.name);
payload.push(export.kind.as_byte());
self.write_u32_leb128(&mut payload, export.index);
}
self.write_section(buf, WASM_SECTION_EXPORT, &payload);
}
fn encode_start_section(&self, buf: &mut Vec<u8>, start_idx: u32) {
let mut payload = Vec::new();
self.write_u32_leb128(&mut payload, start_idx);
self.write_section(buf, WASM_SECTION_START, &payload);
}
fn encode_element_section(&self, buf: &mut Vec<u8>, module: &X86WASMModule) {
let mut payload = Vec::new();
self.write_u32_leb128(&mut payload, module.elements.len() as u32);
for element in &module.elements {
payload.push(0);
self.encode_init_expr(&mut payload, &element.offset_expr);
self.write_u32_leb128(&mut payload, element.init.len() as u32);
for func_idx in &element.init {
self.write_u32_leb128(&mut payload, *func_idx);
}
}
self.write_section(buf, WASM_SECTION_ELEMENT, &payload);
}
fn encode_code_section(&self, buf: &mut Vec<u8>, functions: &[&WasmFunction]) {
let mut payload = Vec::new();
self.write_u32_leb128(&mut payload, functions.len() as u32);
for func in functions {
let mut func_body = Vec::new();
let mut locals_compressed: Vec<(u32, WasmValueType)> = Vec::new();
for local in &func.locals {
if let Some((count, ty)) = locals_compressed.last_mut() {
if *ty == *local {
*count += 1;
continue;
}
}
locals_compressed.push((1, *local));
}
self.write_u32_leb128(&mut func_body, locals_compressed.len() as u32);
for (count, ty) in &locals_compressed {
self.write_u32_leb128(&mut func_body, *count);
self.write_value_type(&mut func_body, ty);
}
for inst in &func.body {
self.encode_instruction(&mut func_body, inst);
}
self.write_u32_leb128(&mut payload, func_body.len() as u32);
payload.extend_from_slice(&func_body);
}
self.write_section(buf, WASM_SECTION_CODE, &payload);
}
fn encode_data_section(&self, buf: &mut Vec<u8>, module: &X86WASMModule) {
let mut payload = Vec::new();
self.write_u32_leb128(&mut payload, module.data_segments.len() as u32);
for segment in &module.data_segments {
payload.push(0);
self.encode_init_expr(&mut payload, &segment.offset_expr);
self.write_u32_leb128(&mut payload, segment.data.len() as u32);
payload.extend_from_slice(&segment.data);
}
self.write_section(buf, WASM_SECTION_DATA, &payload);
}
fn encode_custom_section(&self, buf: &mut Vec<u8>, section: &X86WASMCustomSection) {
let mut payload = Vec::new();
self.write_name(&mut payload, §ion.name);
payload.extend_from_slice(§ion.data);
self.write_section(buf, WASM_SECTION_CUSTOM, &payload);
}
fn encode_init_expr(&self, buf: &mut Vec<u8>, expr: &WasmInitExpr) {
match expr {
WasmInitExpr::I32Const(v) => {
buf.push(WASM_OP_I32_CONST);
self.write_i32_leb128(buf, *v);
}
WasmInitExpr::I64Const(v) => {
buf.push(WASM_OP_I64_CONST);
self.write_i64_leb128(buf, *v);
}
WasmInitExpr::F32Const(v) => {
buf.push(WASM_OP_F32_CONST);
self.write_f32(buf, *v);
}
WasmInitExpr::F64Const(v) => {
buf.push(WASM_OP_F64_CONST);
self.write_f64(buf, *v);
}
WasmInitExpr::GlobalGet(idx) => {
buf.push(WASM_OP_GLOBAL_GET);
self.write_u32_leb128(buf, *idx);
}
WasmInitExpr::End => {
buf.push(WASM_OP_END);
}
_ => {
buf.push(WASM_OP_END);
}
}
}
fn encode_instruction(&self, buf: &mut Vec<u8>, inst: &WasmInstruction) {
match inst {
WasmInstruction::Unreachable => buf.push(WASM_OP_UNREACHABLE),
WasmInstruction::Nop => buf.push(WASM_OP_NOP),
WasmInstruction::Block { block_type, .. } => {
buf.push(WASM_OP_BLOCK);
self.write_block_type(buf, block_type);
}
WasmInstruction::Loop { block_type, .. } => {
buf.push(WASM_OP_LOOP);
self.write_block_type(buf, block_type);
}
WasmInstruction::If { block_type } => {
buf.push(WASM_OP_IF);
self.write_block_type(buf, block_type);
}
WasmInstruction::Else => buf.push(WASM_OP_ELSE),
WasmInstruction::End => buf.push(WASM_OP_END),
WasmInstruction::Br { label_idx } => {
buf.push(WASM_OP_BR);
self.write_u32_leb128(buf, *label_idx);
}
WasmInstruction::BrIf { label_idx } => {
buf.push(WASM_OP_BR_IF);
self.write_u32_leb128(buf, *label_idx);
}
WasmInstruction::BrTable {
targets,
default_target,
} => {
buf.push(WASM_OP_BR_TABLE);
self.write_u32_leb128(buf, targets.len() as u32);
for t in targets {
self.write_u32_leb128(buf, *t);
}
self.write_u32_leb128(buf, *default_target);
}
WasmInstruction::Return => buf.push(WASM_OP_RETURN),
WasmInstruction::Call { func_idx } => {
buf.push(WASM_OP_CALL);
self.write_u32_leb128(buf, *func_idx);
}
WasmInstruction::CallIndirect {
type_idx,
table_idx,
} => {
buf.push(WASM_OP_CALL_INDIRECT);
self.write_u32_leb128(buf, *type_idx);
self.write_u32_leb128(buf, *table_idx);
}
WasmInstruction::Drop => buf.push(WASM_OP_DROP),
WasmInstruction::LocalGet { local_idx } => {
buf.push(WASM_OP_LOCAL_GET);
self.write_u32_leb128(buf, *local_idx);
}
WasmInstruction::LocalSet { local_idx } => {
buf.push(WASM_OP_LOCAL_SET);
self.write_u32_leb128(buf, *local_idx);
}
WasmInstruction::LocalTee { local_idx } => {
buf.push(WASM_OP_LOCAL_TEE);
self.write_u32_leb128(buf, *local_idx);
}
WasmInstruction::GlobalGet { global_idx } => {
buf.push(WASM_OP_GLOBAL_GET);
self.write_u32_leb128(buf, *global_idx);
}
WasmInstruction::GlobalSet { global_idx } => {
buf.push(WASM_OP_GLOBAL_SET);
self.write_u32_leb128(buf, *global_idx);
}
WasmInstruction::I32Const(v) => {
buf.push(WASM_OP_I32_CONST);
self.write_i32_leb128(buf, *v);
}
WasmInstruction::I64Const(v) => {
buf.push(WASM_OP_I64_CONST);
self.write_i64_leb128(buf, *v);
}
WasmInstruction::F32Const(v) => {
buf.push(WASM_OP_F32_CONST);
self.write_f32(buf, *v);
}
WasmInstruction::F64Const(v) => {
buf.push(WASM_OP_F64_CONST);
self.write_f64(buf, *v);
}
WasmInstruction::I32Add => buf.push(WASM_OP_I32_ADD),
WasmInstruction::I32Sub => buf.push(WASM_OP_I32_SUB),
WasmInstruction::I32Mul => buf.push(WASM_OP_I32_MUL),
WasmInstruction::I32DivS => buf.push(WASM_OP_I32_DIV_S),
WasmInstruction::I32Eqz => buf.push(WASM_OP_I32_EQZ),
WasmInstruction::I32Eq => buf.push(WASM_OP_I32_EQ),
WasmInstruction::I32Ne => buf.push(WASM_OP_I32_NE),
WasmInstruction::I32LtS => buf.push(WASM_OP_I32_LT_S),
WasmInstruction::I32GtS => buf.push(WASM_OP_I32_GT_S),
WasmInstruction::I64Add => buf.push(WASM_OP_I64_ADD),
WasmInstruction::I64Sub => buf.push(WASM_OP_I64_SUB),
WasmInstruction::I64Mul => buf.push(WASM_OP_I64_MUL),
WasmInstruction::F32Add => buf.push(WASM_OP_F32_ADD),
WasmInstruction::F64Add => buf.push(WASM_OP_F64_ADD),
WasmInstruction::I32And => buf.push(WASM_OP_I32_AND),
WasmInstruction::I32Or => buf.push(WASM_OP_I32_OR),
WasmInstruction::I32Xor => buf.push(WASM_OP_I32_XOR),
WasmInstruction::I32Shl => buf.push(WASM_OP_I32_SHL),
WasmInstruction::I32ShrS => buf.push(WASM_OP_I32_SHR_S),
WasmInstruction::I32ShrU => buf.push(WASM_OP_I32_SHR_U),
WasmInstruction::I32Rotl => buf.push(WASM_OP_I32_ROTL),
WasmInstruction::I32Rotr => buf.push(WASM_OP_I32_ROTR),
WasmInstruction::I32Clz => buf.push(WASM_OP_I32_CLZ),
WasmInstruction::I32Ctz => buf.push(WASM_OP_I32_CTZ),
WasmInstruction::I32Popcnt => buf.push(WASM_OP_I32_POPCNT),
WasmInstruction::I64Clz => buf.push(WASM_OP_I64_CLZ),
WasmInstruction::I64And => buf.push(WASM_OP_I64_AND),
WasmInstruction::I64Or => buf.push(WASM_OP_I64_OR),
WasmInstruction::I64Xor => buf.push(WASM_OP_I64_XOR),
WasmInstruction::F32Mul => buf.push(WASM_OP_F32_MUL),
WasmInstruction::F32Div => buf.push(WASM_OP_F32_DIV),
WasmInstruction::F32Abs => buf.push(WASM_OP_F32_ABS),
WasmInstruction::F32Neg => buf.push(WASM_OP_F32_NEG),
WasmInstruction::F64Mul => buf.push(WASM_OP_F64_MUL),
WasmInstruction::F64Div => buf.push(WASM_OP_F64_DIV),
WasmInstruction::I32Extend8S => buf.push(WASM_OP_I32_EXTEND8_S),
WasmInstruction::I32Extend16S => buf.push(WASM_OP_I32_EXTEND16_S),
WasmInstruction::I64Extend8S => buf.push(WASM_OP_I64_EXTEND8_S),
WasmInstruction::I64Extend16S => buf.push(WASM_OP_I64_EXTEND16_S),
WasmInstruction::I64Extend32S => buf.push(WASM_OP_I64_EXTEND32_S),
WasmInstruction::MemorySize { .. } => {
buf.push(WASM_OP_MEMORY_SIZE);
buf.push(0x00);
}
WasmInstruction::MemoryGrow { .. } => {
buf.push(WASM_OP_MEMORY_GROW);
buf.push(0x00);
}
WasmInstruction::MemoryCopy => {
buf.push(WASM_OP_PREFIX_MISC);
self.write_u32_leb128(buf, WASM_OP_MEMORY_COPY);
buf.push(0x00);
buf.push(0x00);
}
WasmInstruction::MemoryFill => {
buf.push(WASM_OP_PREFIX_MISC);
self.write_u32_leb128(buf, WASM_OP_MEMORY_FILL);
buf.push(0x00);
}
WasmInstruction::RefNull { ref_type } => {
buf.push(WASM_OP_REF_NULL);
self.write_value_type(buf, ref_type);
}
WasmInstruction::RefIsNull => buf.push(WASM_OP_REF_IS_NULL),
WasmInstruction::RefFunc { func_idx } => {
buf.push(WASM_OP_REF_FUNC);
self.write_u32_leb128(buf, *func_idx);
}
_ => {
buf.push(WASM_OP_UNREACHABLE);
}
}
}
fn write_section(&self, buf: &mut Vec<u8>, section_id: u8, payload: &[u8]) {
buf.push(section_id);
self.write_u32_leb128(buf, payload.len() as u32);
buf.extend_from_slice(payload);
}
}
#[derive(Debug, Clone)]
pub struct X86WASMBinaryEmitter {
target: X86WASMTarget,
encoder: X86WASMEncoder,
}
impl X86WASMBinaryEmitter {
pub fn new(target: &X86WASMTarget) -> Self {
Self {
target: target.clone(),
encoder: X86WASMEncoder::new(),
}
}
pub fn emit(&self, module: &X86WASMModule) -> Result<Vec<u8>, X86WASMError> {
Ok(self.encoder.encode_module(module))
}
pub fn emit_to_file(&self, module: &X86WASMModule, path: &str) -> Result<(), X86WASMError> {
let bytes = self.emit(module)?;
std::fs::write(path, &bytes)
.map_err(|e| X86WASMError::IoError(format!("Failed to write {}: {}", path, e)))?;
Ok(())
}
}
#[derive(Debug, Clone, Default)]
pub struct X86WASMTextEmitter;
impl X86WASMTextEmitter {
pub fn new() -> Self {
Self
}
pub fn emit(&self, module: &X86WASMModule) -> String {
self.emit_module(module)
}
pub fn emit_module(&self, module: &X86WASMModule) -> String {
let mut out = String::new();
out.push_str("(module\n");
for (i, ty) in module.types.iter().enumerate() {
out.push_str(&format!(
" (type (;{};) (func (param {}) (result {})))\n",
i,
ty.params
.iter()
.map(|t| t.to_string())
.collect::<Vec<_>>()
.join(" "),
ty.results
.iter()
.map(|t| t.to_string())
.collect::<Vec<_>>()
.join(" ")
));
}
for import in &module.imports {
out.push_str(&format!(
" (import \"{}\" \"{}\" (func $import_{}))\n",
import.module, import.name, import.name
));
}
for memory in &module.memories {
if let Some(max) = memory.max_pages {
out.push_str(&format!(
" (memory (export \"memory\") {} {})\n",
memory.min_pages, max
));
} else {
out.push_str(&format!(
" (memory (export \"memory\") {})\n",
memory.min_pages
));
}
}
for func in &module.functions {
if !func.is_import {
if func.is_export {
out.push_str(&format!(" (func (export \"{}\")\n", func.name));
} else {
out.push_str(&format!(" (func ${}\n", func.name));
}
for inst in &func.body {
out.push_str(&format!(" {}\n", self.emit_instruction(inst)));
}
out.push_str(" )\n");
}
}
for global in &module.globals {
out.push_str(&format!(
" (global ${} (mut {}) (i32.const 0))\n",
global.name, global.value_type
));
}
if !module.data_segments.is_empty() {
out.push_str(&format!(
" (data (;{} segments;))\n",
module.data_segments.len()
));
}
out.push_str(")\n");
out
}
fn emit_instruction(&self, inst: &WasmInstruction) -> String {
match inst {
WasmInstruction::Unreachable => "unreachable".to_string(),
WasmInstruction::Nop => "nop".to_string(),
WasmInstruction::Block { label, .. } => {
if let Some(l) = label {
format!("block ${}", l)
} else {
"block".to_string()
}
}
WasmInstruction::Loop { label, .. } => {
if let Some(l) = label {
format!("loop ${}", l)
} else {
"loop".to_string()
}
}
WasmInstruction::If { .. } => "if".to_string(),
WasmInstruction::Else => "else".to_string(),
WasmInstruction::End => "end".to_string(),
WasmInstruction::Br { label_idx } => format!("br {}", label_idx),
WasmInstruction::BrIf { label_idx } => format!("br_if {}", label_idx),
WasmInstruction::Return => "return".to_string(),
WasmInstruction::Call { func_idx } => format!("call {}", func_idx),
WasmInstruction::Drop => "drop".to_string(),
WasmInstruction::LocalGet { local_idx } => format!("local.get {}", local_idx),
WasmInstruction::LocalSet { local_idx } => format!("local.set {}", local_idx),
WasmInstruction::LocalTee { local_idx } => format!("local.tee {}", local_idx),
WasmInstruction::GlobalGet { global_idx } => format!("global.get {}", global_idx),
WasmInstruction::GlobalSet { global_idx } => format!("global.set {}", global_idx),
WasmInstruction::I32Const(v) => format!("i32.const {}", v),
WasmInstruction::I64Const(v) => format!("i64.const {}", v),
WasmInstruction::F32Const(v) => format!("f32.const {}", v),
WasmInstruction::F64Const(v) => format!("f64.const {}", v),
WasmInstruction::I32Add => "i32.add".to_string(),
WasmInstruction::I32Sub => "i32.sub".to_string(),
WasmInstruction::I32Mul => "i32.mul".to_string(),
WasmInstruction::I32DivS => "i32.div_s".to_string(),
WasmInstruction::I32Eqz => "i32.eqz".to_string(),
WasmInstruction::I32Eq => "i32.eq".to_string(),
WasmInstruction::I32Ne => "i32.ne".to_string(),
WasmInstruction::I32LtS => "i32.lt_s".to_string(),
WasmInstruction::I32GtS => "i32.gt_s".to_string(),
WasmInstruction::I64Add => "i64.add".to_string(),
WasmInstruction::I64Sub => "i64.sub".to_string(),
WasmInstruction::I64Mul => "i64.mul".to_string(),
WasmInstruction::F32Add => "f32.add".to_string(),
WasmInstruction::F64Add => "f64.add".to_string(),
WasmInstruction::I32And => "i32.and".to_string(),
WasmInstruction::I32Or => "i32.or".to_string(),
WasmInstruction::I32Xor => "i32.xor".to_string(),
WasmInstruction::I32Shl => "i32.shl".to_string(),
WasmInstruction::I32ShrS => "i32.shr_s".to_string(),
WasmInstruction::I32ShrU => "i32.shr_u".to_string(),
_ => ";; unknown".to_string(),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct X86WASMValidator {
target: Option<X86WASMTarget>,
}
impl X86WASMValidator {
pub fn new(target: &X86WASMTarget) -> Self {
Self {
target: Some(target.clone()),
}
}
pub fn validate(&self, module: &X86WASMModule) -> Result<(), String> {
self.validate_module(module)
}
pub fn validate_module(&self, module: &X86WASMModule) -> Result<(), String> {
let mut errors: Vec<String> = Vec::new();
if module.types.len() > WASM_MAX_FUNCTIONS as usize {
errors.push(format!(
"Too many types: {} (max {})",
module.types.len(),
WASM_MAX_FUNCTIONS
));
}
for func in &module.functions {
if func.type_idx >= module.types.len() as u32 {
errors.push(format!(
"Function '{}' references invalid type index {}",
func.name, func.type_idx
));
}
}
for export in &module.exports {
match export.kind {
WasmExternalKind::Function => {
if export.index >= module.functions.len() as u32 {
errors.push(format!(
"Export '{}' references invalid function index {}",
export.name, export.index
));
}
}
WasmExternalKind::Memory => {
if export.index >= module.memories.len() as u32 {
errors.push(format!(
"Export '{}' references invalid memory index {}",
export.name, export.index
));
}
}
WasmExternalKind::Global => {
if export.index >= module.globals.len() as u32 {
errors.push(format!(
"Export '{}' references invalid global index {}",
export.name, export.index
));
}
}
_ => {}
}
}
if let Some(start) = module.start_function {
if start >= module.functions.len() as u32 {
errors.push(format!("Start function index {} is out of range", start));
}
}
for (i, segment) in module.data_segments.iter().enumerate() {
if segment.memory_idx >= module.memories.len() as u32 && !module.memories.is_empty() {
errors.push(format!(
"Data segment {} references invalid memory index {}",
i, segment.memory_idx
));
}
}
let mut export_names: HashSet<&str> = HashSet::new();
for export in &module.exports {
if !export_names.insert(&export.name) {
errors.push(format!("Duplicate export name: {}", export.name));
}
}
if !errors.is_empty() {
return Err(errors.join("\n"));
}
Ok(())
}
pub fn validate_function(
&self,
func: &WasmFunction,
func_type: &WasmFuncType,
) -> Result<(), String> {
let mut stack_depth: u32 = 0;
for inst in &func.body {
match inst {
WasmInstruction::I32Const(_)
| WasmInstruction::I64Const(_)
| WasmInstruction::F32Const(_)
| WasmInstruction::F64Const(_)
| WasmInstruction::LocalGet { .. }
| WasmInstruction::GlobalGet { .. } => {
stack_depth += 1;
}
WasmInstruction::I32Add
| WasmInstruction::I32Sub
| WasmInstruction::I32Mul
| WasmInstruction::I32DivS
| WasmInstruction::I32And
| WasmInstruction::I32Or
| WasmInstruction::I32Xor
| WasmInstruction::I32Eq
| WasmInstruction::F32Add
| WasmInstruction::F64Add => {
if stack_depth < 2 {
return Err("Stack underflow in binary operation".to_string());
}
stack_depth -= 1;
}
WasmInstruction::I32Eqz | WasmInstruction::Drop => {
if stack_depth < 1 {
return Err("Stack underflow in unary operation".to_string());
}
if matches!(inst, WasmInstruction::Drop) {
stack_depth -= 1;
}
}
WasmInstruction::LocalSet { .. } | WasmInstruction::GlobalSet { .. } => {
if stack_depth < 1 {
return Err("Stack underflow in set operation".to_string());
}
stack_depth -= 1;
}
WasmInstruction::Return => {
if stack_depth < func_type.results.len() as u32 {
return Err("Stack underflow at return".to_string());
}
stack_depth -= func_type.results.len() as u32;
}
_ => {}
}
if stack_depth > 1000 {
return Err(format!("Stack depth {} exceeds maximum 1000", stack_depth));
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_target_wasm32() {
let t = X86WASMTarget::wasm32();
assert_eq!(t.triple, "wasm32-unknown-unknown");
assert_eq!(t.pointer_width, 32);
assert!(!t.is_wasi);
}
#[test]
fn test_target_wasm32_wasi() {
let t = X86WASMTarget::wasm32_wasi();
assert!(t.is_wasi);
assert_eq!(t.pointer_width, 32);
}
#[test]
fn test_target_wasm64() {
let t = X86WASMTarget::wasm64();
assert_eq!(t.pointer_width, 64);
assert!(t.is_wasm64());
}
#[test]
fn test_target_wasm32_emscripten() {
let t = X86WASMTarget::wasm32_emscripten();
assert!(t.is_emscripten);
}
#[test]
fn test_target_data_layout() {
let t32 = X86WASMTarget::wasm32();
assert!(t32.data_layout.contains("p:32:32"));
let t64 = X86WASMTarget::wasm64();
assert!(t64.data_layout.contains("p:64:64"));
}
#[test]
fn test_target_from_triple() {
let t = X86WASMTarget::from_triple("wasm32-wasi");
assert!(t.is_wasi);
let t2 = X86WASMTarget::from_triple("wasm64-unknown-unknown");
assert!(t2.is_wasm64());
}
#[test]
fn test_feature_flags_default() {
let f = X86WASMFeatureFlags::default();
assert!(f.sign_ext);
assert!(f.simd128);
assert!(!f.memory64);
}
#[test]
fn test_feature_flags_from_triple() {
let f = X86WASMFeatureFlags::from_triple("wasm64-unknown-unknown");
assert!(f.memory64);
let f2 = X86WASMFeatureFlags::from_triple("wasm32-unknown-unknown");
assert!(!f2.memory64);
}
#[test]
fn test_feature_flags_has() {
let f = X86WASMFeatureFlags::default();
assert!(f.has("simd128"));
assert!(!f.has("nonexistent"));
}
#[test]
fn test_feature_flags_enabled() {
let f = X86WASMFeatureFlags::default();
let e = f.enabled_features();
assert!(e.contains(&"sign-ext"));
assert!(e.contains(&"simd128"));
}
#[test]
fn test_feature_flags_display() {
let f = X86WASMFeatureFlags::default();
let s = format!("{}", f);
assert!(s.contains("sign-ext"));
assert!(s.contains("simd128"));
}
#[test]
fn test_config_default() {
let c = X86WASMConfig::default();
assert_eq!(c.target_triple, "wasm32-unknown-unknown");
assert_eq!(c.optimization_level, 2);
}
#[test]
fn test_config_wasi() {
let c = X86WASMConfig::wasi();
assert_eq!(c.target_triple, "wasm32-wasi");
assert!(c.enable_wasi);
}
#[test]
fn test_config_wasi_reactor() {
let c = X86WASMConfig::wasi_reactor();
assert!(c.reactor_mode);
assert!(!c.command_mode);
}
#[test]
fn test_config_emscripten() {
let c = X86WASMConfig::emscripten();
assert!(c.enable_emscripten);
}
#[test]
fn test_config_wasm64() {
let c = X86WASMConfig::wasm64();
assert!(c.enable_memory64);
}
#[test]
fn test_config_set_features() {
let mut c = X86WASMConfig::default();
c.set_features("+simd128,-atomics,+gc");
assert!(c.enable_simd128);
assert!(c.enable_gc);
}
#[test]
fn test_config_data_layout() {
let c32 = X86WASMConfig::default();
assert!(c32.data_layout().contains("p:32:32"));
let c64 = X86WASMConfig::wasm64();
assert!(c64.data_layout().contains("p:64:64"));
}
#[test]
fn test_wasm_new() {
let w = X86WASM::new(X86WASMConfig::default());
assert!(!w.has_errors());
assert!(w.warnings.is_empty());
}
#[test]
fn test_wasm_default() {
let w = X86WASM::default();
assert_eq!(w.config.optimization_level, 2);
}
#[test]
fn test_wasm_warn_and_error() {
let mut w = X86WASM::default();
w.warn("test warning");
w.error("test error");
assert!(w.has_errors());
assert_eq!(w.warnings.len(), 1);
assert_eq!(w.errors.len(), 1);
}
#[test]
fn test_wasm_summary() {
let w = X86WASM::default();
let s = w.summary();
assert!(s.contains("WASM compilation summary"));
assert!(s.contains("Functions:"));
}
#[test]
fn test_stats_default() {
let s = X86WASMStats::default();
assert_eq!(s.total_source_bytes, 0);
}
#[test]
fn test_stats_display() {
let s = X86WASMStats::default();
assert!(format!("{}", s).contains("Compilation Statistics"));
}
#[test]
fn test_error_display() {
let e = X86WASMError::CompilationError("test".to_string());
assert!(format!("{}", e).contains("Compilation error: test"));
let e2 = X86WASMError::CodegenError("test2".to_string());
assert!(format!("{}", e2).contains("Codegen error: test2"));
}
#[test]
fn test_value_type_numeric() {
assert!(WasmValueType::I32.is_numeric());
assert!(WasmValueType::I64.is_numeric());
assert!(WasmValueType::F32.is_numeric());
assert!(WasmValueType::F64.is_numeric());
assert!(!WasmValueType::Funcref.is_numeric());
}
#[test]
fn test_value_type_reference() {
assert!(WasmValueType::Funcref.is_reference());
assert!(WasmValueType::Externref.is_reference());
assert!(!WasmValueType::I32.is_reference());
}
#[test]
fn test_value_type_size() {
assert_eq!(WasmValueType::I32.size(), 4);
assert_eq!(WasmValueType::I64.size(), 8);
assert_eq!(WasmValueType::V128.size(), 16);
}
#[test]
fn test_value_type_display() {
assert_eq!(format!("{}", WasmValueType::I32), "i32");
assert_eq!(format!("{}", WasmValueType::I64), "i64");
assert_eq!(format!("{}", WasmValueType::F32), "f32");
assert_eq!(format!("{}", WasmValueType::F64), "f64");
assert_eq!(format!("{}", WasmValueType::Funcref), "funcref");
assert_eq!(format!("{}", WasmValueType::Externref), "externref");
}
#[test]
fn test_value_type_roundtrip() {
let types = vec![
WasmValueType::I32,
WasmValueType::I64,
WasmValueType::F32,
WasmValueType::F64,
WasmValueType::V128,
WasmValueType::Funcref,
WasmValueType::Externref,
];
for ty in &types {
assert_eq!(WasmValueType::from_byte(ty.as_byte()), Some(*ty));
}
}
#[test]
fn test_external_kind_byte() {
assert_eq!(WasmExternalKind::Function.as_byte(), 0);
assert_eq!(WasmExternalKind::Table.as_byte(), 1);
assert_eq!(WasmExternalKind::Memory.as_byte(), 2);
assert_eq!(WasmExternalKind::Global.as_byte(), 3);
}
#[test]
fn test_module_new() {
let m = X86WASMModule::new();
assert!(m.types.is_empty());
assert!(m.functions.is_empty());
}
#[test]
fn test_module_add_import_function() {
let mut m = X86WASMModule::new();
let idx = m.add_import_function("wasi_snapshot_preview1", "fd_write", 0);
assert_eq!(idx, 0);
assert_eq!(m.imports.len(), 1);
assert_eq!(m.imports[0].module, "wasi_snapshot_preview1");
}
#[test]
fn test_module_add_import_memory() {
let mut m = X86WASMModule::new();
m.add_import_memory("env", "memory", 256, Some(65536));
assert_eq!(m.memories.len(), 1);
assert_eq!(m.memories[0].min_pages, 256);
}
#[test]
fn test_module_emit_binary() {
let m = X86WASMModule::new();
let bin = m.emit_binary();
assert_eq!(&bin[0..4], &[0x00, 0x61, 0x73, 0x6d]);
assert_eq!(&bin[4..8], &[0x01, 0x00, 0x00, 0x00]);
}
#[test]
fn test_module_emit_wat() {
let m = X86WASMModule::new();
let wat = m.emit_wat();
assert!(wat.starts_with("(module"));
assert!(wat.ends_with(")\n"));
}
#[test]
fn test_wasi_new() {
let c = X86WASMConfig::wasi();
let w = X86WASMWASI::new(&c);
assert_eq!(w.wasi_version(), "wasi_snapshot_preview1");
}
#[test]
fn test_wasi_add_imports() {
let c = X86WASMConfig::wasi();
let w = X86WASMWASI::new(&c);
let mut m = X86WASMModule::new();
w.add_wasi_imports(&mut m).unwrap();
assert!(m.imports.len() > 30);
}
#[test]
fn test_wasi_valid_function() {
assert!(X86WASMWASI::is_valid_wasi_function("fd_read"));
assert!(X86WASMWASI::is_valid_wasi_function("fd_write"));
assert!(X86WASMWASI::is_valid_wasi_function("proc_exit"));
assert!(!X86WASMWASI::is_valid_wasi_function("nonexistent"));
}
#[test]
fn test_wasi_preview1_functions() {
let c = X86WASMConfig::wasi();
let w = X86WASMWASI::new(&c);
let funcs = w.wasi_preview1_functions();
assert!(funcs.len() >= 44);
let names: Vec<&str> = funcs.iter().map(|(n, _, _)| n.as_str()).collect();
assert!(names.contains(&"fd_read"));
assert!(names.contains(&"fd_write"));
assert!(names.contains(&"args_get"));
assert!(names.contains(&"environ_get"));
assert!(names.contains(&"random_get"));
assert!(names.contains(&"proc_exit"));
assert!(names.contains(&"sock_accept"));
assert!(names.contains(&"path_open"));
}
#[test]
fn test_wasi_add_start() {
let c = X86WASMConfig::wasi();
let w = X86WASMWASI::new(&c);
let mut m = X86WASMModule::new();
w.add_wasi_start(&mut m).unwrap();
assert!(m.start_function.is_some());
}
#[test]
fn test_wasi_reactor_exports() {
let c = X86WASMConfig::wasi_reactor();
let w = X86WASMWASI::new(&c);
let mut m = X86WASMModule::new();
w.generate_reactor_exports(&mut m);
assert!(m.exports.iter().any(|e| e.name == "memory"));
}
#[test]
fn test_emscripten_new() {
let c = X86WASMConfig::emscripten();
let e = X86WASMEmscripten::new(&c);
assert!(e.metadata().emscripten_version.is_empty());
}
#[test]
fn test_emscripten_add_js_glue() {
let c = X86WASMConfig::emscripten();
let e = X86WASMEmscripten::new(&c);
let mut m = X86WASMModule::new();
e.add_js_glue(&mut m).unwrap();
assert!(m.imports.len() >= 3);
}
#[test]
fn test_emscripten_add_runtime() {
let c = X86WASMConfig::emscripten();
let e = X86WASMEmscripten::new(&c);
let mut m = X86WASMModule::new();
e.add_emscripten_runtime(&mut m).unwrap();
assert!(!m.memories.is_empty());
assert!(!m.globals.is_empty());
}
#[test]
fn test_sse_to_simd128() {
assert!(X86WASMEmscripten::map_sse_to_simd128("_mm_add_ps").is_some());
assert!(X86WASMEmscripten::map_sse_to_simd128("_mm_mul_ps").is_some());
assert!(X86WASMEmscripten::map_sse_to_simd128("nonexistent").is_none());
}
#[test]
fn test_avx_to_simd128() {
let r = X86WASMEmscripten::map_avx_to_simd128("_mm256_add_ps");
assert!(r.is_some());
assert_eq!(r.unwrap().len(), 2);
}
#[test]
fn test_gles_to_webgl() {
assert_eq!(
X86WASMEmscripten::map_gles_to_webgl("glClear"),
Some("glClear".to_string())
);
assert_eq!(
X86WASMEmscripten::map_gles_to_webgl("glGenVertexArraysOES"),
Some("glGenVertexArrays".to_string())
);
}
#[test]
fn test_linker_new() {
let c = X86WASMConfig::default();
let l = X86WASMLinker::new(&c);
assert_eq!(l.heap_base(), 0);
assert_eq!(l.stack_pointer_init(), 0);
}
#[test]
fn test_linker_link_empty() {
let c = X86WASMConfig::default();
let mut l = X86WASMLinker::new(&c);
let r = l.link(&[X86WASMModule::new(), X86WASMModule::new()]);
assert!(r.is_ok());
}
#[test]
fn test_linker_link_single_module() {
let c = X86WASMConfig::default();
let mut l = X86WASMLinker::new(&c);
let mut m = X86WASMModule::new();
m.types.push(WasmFuncType::default());
m.functions.push(WasmFunction {
name: "test".to_string(),
type_idx: 0,
locals: Vec::new(),
body: vec![WasmInstruction::I32Const(42), WasmInstruction::End],
is_export: true,
is_import: false,
import_module: None,
import_name: None,
});
m.memories.push(WasmMemory {
min_pages: 256,
max_pages: Some(65536),
is_shared: false,
is_64: false,
});
let r = l.link(&[m]);
assert!(r.is_ok());
assert_eq!(r.unwrap().functions.len(), 1);
}
#[test]
fn test_optimizer_new() {
let c = X86WASMConfig::default();
let o = X86WASMOptimizer::new(&c);
assert!(o.enable_dce);
assert!(o.enable_inlining);
}
#[test]
fn test_optimizer_o0() {
let mut c = X86WASMConfig::default();
c.optimization_level = 0;
let o = X86WASMOptimizer::new(&c);
assert!(!o.enable_dce);
}
#[test]
fn test_optimizer_on_empty() {
let c = X86WASMConfig::default();
let o = X86WASMOptimizer::new(&c);
let mut m = X86WASMModule::new();
assert!(o.optimize(&mut m).is_ok());
}
#[test]
fn test_optimizer_dce() {
let c = X86WASMConfig::default();
let o = X86WASMOptimizer::new(&c);
let mut m = X86WASMModule::new();
m.functions.push(WasmFunction {
name: "test".to_string(),
type_idx: 0,
locals: Vec::new(),
body: vec![
WasmInstruction::I32Const(1),
WasmInstruction::Return,
WasmInstruction::I32Const(2),
],
is_export: true,
is_import: false,
import_module: None,
import_name: None,
});
o.optimize(&mut m).unwrap();
assert!(m.functions[0].body.len() <= 3);
}
#[test]
fn test_optimizer_const_prop() {
let c = X86WASMConfig::default();
let o = X86WASMOptimizer::new(&c);
let mut m = X86WASMModule::new();
m.functions.push(WasmFunction {
name: "test".to_string(),
type_idx: 0,
locals: Vec::new(),
body: vec![
WasmInstruction::I32Const(5),
WasmInstruction::I32Const(3),
WasmInstruction::I32Add,
WasmInstruction::End,
],
is_export: false,
is_import: false,
import_module: None,
import_name: None,
});
o.optimize(&mut m).unwrap();
let has_folded = m.functions[0]
.body
.iter()
.any(|i| matches!(i, WasmInstruction::I32Const(8)));
assert!(has_folded);
}
#[test]
fn test_leb128_u32_basic() {
let e = X86WASMEncoder::new();
let mut b = Vec::new();
e.write_u32_leb128(&mut b, 0);
assert_eq!(b, vec![0x00]);
b.clear();
e.write_u32_leb128(&mut b, 127);
assert_eq!(b, vec![0x7f]);
b.clear();
e.write_u32_leb128(&mut b, 128);
assert_eq!(b, vec![0x80, 0x01]);
b.clear();
e.write_u32_leb128(&mut b, 624485);
assert_eq!(b, vec![0xe5, 0x8e, 0x26]);
}
#[test]
fn test_leb128_i32_negative() {
let e = X86WASMEncoder::new();
let mut b = Vec::new();
e.write_i32_leb128(&mut b, -1);
assert_eq!(b, vec![0x7f]);
}
#[test]
fn test_leb128_read_u32() {
let b = vec![0xe5, 0x8e, 0x26];
let mut p = 0;
assert_eq!(X86WASMEncoder::read_u32_leb128(&b, &mut p), Some(624485));
assert_eq!(p, 3);
}
#[test]
fn test_leb128_read_i32() {
let b = vec![0x7f];
let mut p = 0;
assert_eq!(X86WASMEncoder::read_i32_leb128(&b, &mut p), Some(-1));
}
#[test]
fn test_leb128_roundtrip_u32() {
let e = X86WASMEncoder::new();
for val in &[0, 1, 127, 128, 255, 16384, 624485, u32::MAX] {
let mut b = Vec::new();
e.write_u32_leb128(&mut b, *val);
let mut p = 0;
assert_eq!(
X86WASMEncoder::read_u32_leb128(&b, &mut p),
Some(*val),
"Roundtrip failed for {}",
val
);
}
}
#[test]
fn test_leb128_roundtrip_i32() {
let e = X86WASMEncoder::new();
for val in &[
0,
1,
-1,
127,
-127,
128,
-128,
16384,
-16384,
i32::MAX,
i32::MIN,
] {
let mut b = Vec::new();
e.write_i32_leb128(&mut b, *val);
let mut p = 0;
assert_eq!(
X86WASMEncoder::read_i32_leb128(&b, &mut p),
Some(*val),
"Roundtrip failed for {}",
val
);
}
}
#[test]
fn test_write_name() {
let e = X86WASMEncoder::new();
let mut b = Vec::new();
e.write_name(&mut b, "hello");
assert_eq!(b[0], 5);
assert_eq!(&b[1..], b"hello");
}
#[test]
fn test_read_name() {
let b = vec![5, b'h', b'e', b'l', b'l', b'o'];
let mut p = 0;
assert_eq!(
X86WASMEncoder::read_name(&b, &mut p),
Some("hello".to_string())
);
}
#[test]
fn test_emitter_empty_module() {
let t = X86WASMTarget::wasm32();
let e = X86WASMBinaryEmitter::new(&t);
let m = X86WASMModule::new();
let bytes = e.emit(&m).unwrap();
assert!(bytes.len() >= 8);
assert_eq!(&bytes[0..4], &[0x00, 0x61, 0x73, 0x6d]);
}
#[test]
fn test_text_emitter_empty() {
let e = X86WASMTextEmitter::new();
let m = X86WASMModule::new();
let wat = e.emit_module(&m);
assert!(wat.starts_with("(module"));
}
#[test]
fn test_text_emitter_with_types() {
let e = X86WASMTextEmitter::new();
let mut m = X86WASMModule::new();
m.types.push(WasmFuncType {
params: vec![WasmValueType::I32, WasmValueType::I32],
results: vec![WasmValueType::I32],
});
let wat = e.emit_module(&m);
assert!(wat.contains("type"));
assert!(wat.contains("i32"));
}
#[test]
fn test_text_emitter_with_imports() {
let e = X86WASMTextEmitter::new();
let mut m = X86WASMModule::new();
m.imports.push(WasmImport {
module: "env".to_string(),
name: "memory".to_string(),
kind: WasmExternalKind::Memory,
type_idx: 0,
});
let wat = e.emit_module(&m);
assert!(wat.contains("import"));
assert!(wat.contains("env"));
}
#[test]
fn test_validator_empty() {
let v = X86WASMValidator::default();
let m = X86WASMModule::new();
assert!(v.validate_module(&m).is_ok());
}
#[test]
fn test_validator_invalid_start() {
let v = X86WASMValidator::default();
let mut m = X86WASMModule::new();
m.start_function = Some(0);
assert!(v.validate_module(&m).is_err());
}
#[test]
fn test_validator_duplicate_exports() {
let v = X86WASMValidator::default();
let mut m = X86WASMModule::new();
m.functions.push(WasmFunction {
name: "f".to_string(),
type_idx: 0,
locals: Vec::new(),
body: Vec::new(),
is_export: true,
is_import: false,
import_module: None,
import_name: None,
});
m.exports.push(WasmExport {
name: "dup".to_string(),
kind: WasmExternalKind::Function,
index: 0,
});
m.exports.push(WasmExport {
name: "dup".to_string(),
kind: WasmExternalKind::Function,
index: 0,
});
assert!(v.validate_module(&m).is_err());
}
#[test]
fn test_validator_function_stack() {
let v = X86WASMValidator::default();
let ft = WasmFuncType::default();
let f = WasmFunction {
name: "t".to_string(),
type_idx: 0,
locals: Vec::new(),
body: vec![
WasmInstruction::I32Const(1),
WasmInstruction::I32Const(2),
WasmInstruction::I32Add,
WasmInstruction::Return,
],
is_export: false,
is_import: false,
import_module: None,
import_name: None,
};
assert!(v.validate_function(&f, &ft).is_ok());
}
#[test]
fn test_wasm_constants() {
assert_eq!(WASM_PAGE_SIZE, 65536);
assert_eq!(WASM_MAGIC, 0x6d736100);
assert_eq!(WASM_VERSION, 1);
assert_eq!(WASM_MAX_LOCALS, 50000);
assert_eq!(WASM_MAX_FUNCTIONS, 1_000_000);
}
#[test]
fn test_wasm_section_ids() {
assert_eq!(WASM_SECTION_CUSTOM, 0);
assert_eq!(WASM_SECTION_TYPE, 1);
assert_eq!(WASM_SECTION_IMPORT, 2);
assert_eq!(WASM_SECTION_FUNCTION, 3);
assert_eq!(WASM_SECTION_CODE, 10);
assert_eq!(WASM_SECTION_DATA, 11);
}
#[test]
fn test_wasm_opcode_constants() {
assert_eq!(WASM_OP_UNREACHABLE, 0x00);
assert_eq!(WASM_OP_BLOCK, 0x02);
assert_eq!(WASM_OP_LOOP, 0x03);
assert_eq!(WASM_OP_IF, 0x04);
assert_eq!(WASM_OP_END, 0x0b);
assert_eq!(WASM_OP_CALL, 0x10);
assert_eq!(WASM_OP_I32_CONST, 0x41);
assert_eq!(WASM_OP_I32_ADD, 0x6a);
}
#[test]
fn test_wasi_errno_values() {
assert_eq!(WASI_ESUCCESS, 0);
assert_eq!(WASI_EBADF, 8);
assert_eq!(WASI_EACCES, 2);
assert_eq!(WASI_ENOENT, 44);
assert_eq!(WASI_ENOSYS, 52);
assert_eq!(WASI_ENOTCAPABLE, 76);
}
#[test]
fn test_wasm_abi_name() {
assert_eq!(X86WASMABI::Bare.name(), "bare");
assert_eq!(X86WASMABI::Wasi.name(), "wasi");
assert_eq!(X86WASMABI::Emscripten.name(), "emscripten");
}
#[test]
fn test_wasm_abi_is_hosted() {
assert!(X86WASMABI::Wasi.is_hosted());
assert!(X86WASMABI::Emscripten.is_hosted());
assert!(!X86WASMABI::Bare.is_hosted());
}
#[test]
fn test_module_with_memory_and_data() {
let mut m = X86WASMModule::new();
m.memories.push(WasmMemory {
min_pages: 1,
max_pages: Some(256),
is_shared: false,
is_64: false,
});
m.data_segments.push(WasmDataSegment {
memory_idx: 0,
offset_expr: WasmInitExpr::I32Const(0),
data: b"Hello".to_vec(),
});
let bin = m.emit_binary();
assert!(bin.len() > 8);
}
#[test]
fn test_module_with_type_and_function() {
let mut m = X86WASMModule::new();
m.types.push(WasmFuncType {
params: vec![],
results: vec![WasmValueType::I32],
});
m.functions.push(WasmFunction {
name: "answer".to_string(),
type_idx: 0,
locals: Vec::new(),
body: vec![WasmInstruction::I32Const(42), WasmInstruction::End],
is_export: true,
is_import: false,
import_module: None,
import_name: None,
});
let bin = m.emit_binary();
assert!(bin.len() > 8);
let wat = m.emit_wat();
assert!(wat.contains("answer"));
assert!(wat.contains("i32.const 42"));
}
#[test]
fn test_custom_section() {
let s = X86WASMCustomSection {
name: "producers".to_string(),
data: vec![0, 1, 2, 3],
};
assert_eq!(s.name, "producers");
assert_eq!(s.data.len(), 4);
}
#[test]
fn test_wasi_import_descriptor() {
let i = WasiImport {
module: "wasi_snapshot_preview1".to_string(),
function: "fd_read".to_string(),
params: vec![WasmValueType::I32; 4],
results: vec![WasmValueType::I32],
};
assert_eq!(i.module, "wasi_snapshot_preview1");
assert_eq!(i.function, "fd_read");
assert_eq!(i.params.len(), 4);
}
#[test]
fn test_func_type_display() {
let ft = WasmFuncType {
params: vec![WasmValueType::I32, WasmValueType::I32],
results: vec![WasmValueType::I32],
};
assert!(format!("{}", ft).contains("i32"));
}
#[test]
fn test_full_pipeline_config() {
let c = X86WASMConfig::wasi();
let w = X86WASM::new(c);
assert!(w.config.enable_wasi);
assert!(w.target.is_wasi);
let c = X86WASMConfig::emscripten();
let w = X86WASM::new(c);
assert!(w.config.enable_emscripten);
assert!(w.target.is_emscripten);
}
#[test]
fn test_full_compile_empty() {
let mut w = X86WASM::new(X86WASMConfig::default());
let _ = w.compile_c_source("");
}
#[test]
fn test_wasm_default_instruction() {
assert!(matches!(WasmInstruction::default(), WasmInstruction::Nop));
}
#[test]
fn test_wasm_label() {
let l = WasmLabel {
label: "test".to_string(),
arity: 1,
is_loop: true,
};
assert_eq!(l.label, "test");
assert_eq!(l.arity, 1);
assert!(l.is_loop);
}
#[test]
fn test_emscripten_metadata_default() {
let m = EmscriptenMetadata::default();
assert!(m.emscripten_version.is_empty());
assert_eq!(m.initial_stack_pointer, 0);
}
}
impl X86WASMEncoder {
fn encode_simd_instruction(&self, buf: &mut Vec<u8>, op: u32) {
buf.push(WASM_OP_PREFIX_SIMD);
self.write_u32_leb128(buf, op);
}
fn encode_atomic_instruction(&self, buf: &mut Vec<u8>, op: u32) {
buf.push(WASM_OP_PREFIX_ATOMIC);
self.write_u32_leb128(buf, op);
}
pub fn encode_memarg(&self, buf: &mut Vec<u8>, align: u32, offset: u32) {
self.write_u32_leb128(buf, align);
self.write_u32_leb128(buf, offset);
}
fn encode_misc_instruction(&self, buf: &mut Vec<u8>, op: u32) {
buf.push(WASM_OP_PREFIX_MISC);
self.write_u32_leb128(buf, op);
}
fn encode_gc_instruction(&self, buf: &mut Vec<u8>, op: u32) {
buf.push(WASM_OP_PREFIX_GC);
self.write_u32_leb128(buf, op);
}
pub fn encode_simd_full_instruction(&self, buf: &mut Vec<u8>, inst: &WasmInstruction) {
match inst {
WasmInstruction::V128Load { align, offset } => {
self.encode_simd_instruction(buf, WASM_OP_V128_LOAD);
self.encode_memarg(buf, *align, *offset);
}
WasmInstruction::V128Store { align, offset } => {
self.encode_simd_instruction(buf, WASM_OP_V128_STORE);
self.encode_memarg(buf, *align, *offset);
}
WasmInstruction::V128Const(data) => {
self.encode_simd_instruction(buf, WASM_OP_V128_CONST);
buf.extend_from_slice(data);
}
WasmInstruction::F32X4Add => self.encode_simd_instruction(buf, 0x8a),
WasmInstruction::F32X4Sub => self.encode_simd_instruction(buf, 0x8b),
WasmInstruction::F32X4Mul => self.encode_simd_instruction(buf, 0x8c),
WasmInstruction::F32X4Div => self.encode_simd_instruction(buf, 0x8d),
WasmInstruction::F32X4Min => self.encode_simd_instruction(buf, 0x90),
WasmInstruction::F32X4Max => self.encode_simd_instruction(buf, 0x91),
WasmInstruction::F64X2Add => self.encode_simd_instruction(buf, 0xa0),
WasmInstruction::F64X2Sub => self.encode_simd_instruction(buf, 0xa1),
WasmInstruction::F64X2Mul => self.encode_simd_instruction(buf, 0xa2),
WasmInstruction::F64X2Div => self.encode_simd_instruction(buf, 0xa3),
WasmInstruction::F64X2Min => self.encode_simd_instruction(buf, 0xa4),
WasmInstruction::F64X2Max => self.encode_simd_instruction(buf, 0xa5),
WasmInstruction::F64X2Sqrt => self.encode_simd_instruction(buf, 0x9f),
WasmInstruction::F32X4Sqrt => self.encode_simd_instruction(buf, 0x8f),
WasmInstruction::F32X4Splat => self.encode_simd_instruction(buf, WASM_OP_F32X4_SPLAT),
WasmInstruction::F64X2Splat => self.encode_simd_instruction(buf, WASM_OP_F64X2_SPLAT),
WasmInstruction::V128And => self.encode_simd_instruction(buf, WASM_OP_V128_AND),
WasmInstruction::V128Or => self.encode_simd_instruction(buf, WASM_OP_V128_OR),
WasmInstruction::V128Xor => self.encode_simd_instruction(buf, WASM_OP_V128_XOR),
WasmInstruction::V128Not => self.encode_simd_instruction(buf, WASM_OP_V128_NOT),
WasmInstruction::F32X4Eq => self.encode_simd_instruction(buf, WASM_OP_F32X4_EQ),
WasmInstruction::F32X4Lt => self.encode_simd_instruction(buf, WASM_OP_F32X4_LT),
WasmInstruction::F32X4Gt => self.encode_simd_instruction(buf, WASM_OP_F32X4_GT),
WasmInstruction::F32X4Le => self.encode_simd_instruction(buf, WASM_OP_F32X4_LE),
WasmInstruction::F32X4Ge => self.encode_simd_instruction(buf, WASM_OP_F32X4_GE),
_ => {}
}
}
}
#[derive(Debug, Clone)]
pub struct X86WASMMemoryManager {
next_address: u64,
stack_pointer_idx: Option<u32>,
heap_base_idx: Option<u32>,
data_end_idx: Option<u32>,
memory_pages: u64,
max_memory_pages: Option<u64>,
stack_size: u64,
allocations: Vec<X86WASMMemoryRegion>,
default_alignment: u64,
}
impl X86WASMMemoryManager {
pub fn new(config: &X86WASMConfig) -> Self {
Self {
next_address: 1024, stack_pointer_idx: None,
heap_base_idx: None,
data_end_idx: None,
memory_pages: config.initial_memory_pages,
max_memory_pages: config.maximum_memory_pages,
stack_size: config.stack_size,
allocations: Vec::new(),
default_alignment: 16,
}
}
pub fn allocate(&mut self, size: u64, alignment: u64) -> u64 {
let align = alignment.max(self.default_alignment);
let addr = (self.next_address + align - 1) & !(align - 1);
self.next_address = addr + size;
let required_pages = (self.next_address + WASM_PAGE_SIZE - 1) / WASM_PAGE_SIZE;
if required_pages > self.memory_pages {
if let Some(max) = self.max_memory_pages {
if required_pages > max {
self.next_address = addr; return 0;
}
}
self.memory_pages = required_pages;
}
self.allocations.push(X86WASMMemoryRegion {
address: addr,
size,
alignment: align,
name: String::new(),
});
addr
}
pub fn allocate_static(&mut self, size: u64, alignment: u64, name: &str) -> u64 {
let addr = self.allocate(size, alignment);
if let Some(region) = self.allocations.last_mut() {
region.name = name.to_string();
}
addr
}
pub fn reserve_stack(&mut self) -> u64 {
let stack_base = self.next_address;
self.next_address += self.stack_size;
self.next_address = (self.next_address + 15) & !15; stack_base
}
pub fn memory_pages(&self) -> u64 {
self.memory_pages
}
pub fn heap_end(&self) -> u64 {
self.next_address
}
pub fn add_memory_globals(&mut self, module: &mut X86WASMModule) {
let sp_idx = module.globals.len() as u32;
module.globals.push(WasmGlobal {
name: "__stack_pointer".to_string(),
value_type: WasmValueType::I32,
mutable: true,
init_expr: WasmInitExpr::I32Const(self.stack_size as i32 + 1024),
});
self.stack_pointer_idx = Some(sp_idx);
let hb_idx = module.globals.len() as u32;
module.globals.push(WasmGlobal {
name: "__heap_base".to_string(),
value_type: WasmValueType::I32,
mutable: false,
init_expr: WasmInitExpr::I32Const((self.stack_size + 1024 + 4096) as i32),
});
self.heap_base_idx = Some(hb_idx);
let de_idx = module.globals.len() as u32;
module.globals.push(WasmGlobal {
name: "__data_end".to_string(),
value_type: WasmValueType::I32,
mutable: false,
init_expr: WasmInitExpr::I32Const(self.next_address as i32),
});
self.data_end_idx = Some(de_idx);
}
pub fn add_memory_section(&self, module: &mut X86WASMModule) {
module.memories.push(WasmMemory {
min_pages: self.memory_pages,
max_pages: self.max_memory_pages,
is_shared: false,
is_64: false,
});
}
}
#[derive(Debug, Clone)]
pub struct X86WASMMemoryRegion {
pub address: u64,
pub size: u64,
pub alignment: u64,
pub name: String,
}
pub struct X86WASMOpcodeInfo;
impl X86WASMOpcodeInfo {
pub fn opcode_name(opcode: u8) -> &'static str {
match opcode {
WASM_OP_UNREACHABLE => "unreachable",
WASM_OP_NOP => "nop",
WASM_OP_BLOCK => "block",
WASM_OP_LOOP => "loop",
WASM_OP_IF => "if",
WASM_OP_ELSE => "else",
WASM_OP_END => "end",
WASM_OP_BR => "br",
WASM_OP_BR_IF => "br_if",
WASM_OP_BR_TABLE => "br_table",
WASM_OP_RETURN => "return",
WASM_OP_CALL => "call",
WASM_OP_CALL_INDIRECT => "call_indirect",
WASM_OP_RETURN_CALL => "return_call",
WASM_OP_RETURN_CALL_INDIRECT => "return_call_indirect",
WASM_OP_CALL_REF => "call_ref",
WASM_OP_DROP => "drop",
WASM_OP_SELECT => "select",
WASM_OP_SELECT_T => "select_t",
WASM_OP_LOCAL_GET => "local.get",
WASM_OP_LOCAL_SET => "local.set",
WASM_OP_LOCAL_TEE => "local.tee",
WASM_OP_GLOBAL_GET => "global.get",
WASM_OP_GLOBAL_SET => "global.set",
WASM_OP_TABLE_GET => "table.get",
WASM_OP_TABLE_SET => "table.set",
WASM_OP_I32_LOAD => "i32.load",
WASM_OP_I64_LOAD => "i64.load",
WASM_OP_F32_LOAD => "f32.load",
WASM_OP_F64_LOAD => "f64.load",
WASM_OP_I32_LOAD8_S => "i32.load8_s",
WASM_OP_I32_LOAD8_U => "i32.load8_u",
WASM_OP_I32_LOAD16_S => "i32.load16_s",
WASM_OP_I32_LOAD16_U => "i32.load16_u",
WASM_OP_I64_LOAD8_S => "i64.load8_s",
WASM_OP_I64_LOAD8_U => "i64.load8_u",
WASM_OP_I64_LOAD16_S => "i64.load16_s",
WASM_OP_I64_LOAD16_U => "i64.load16_u",
WASM_OP_I64_LOAD32_S => "i64.load32_s",
WASM_OP_I64_LOAD32_U => "i64.load32_u",
WASM_OP_I32_STORE => "i32.store",
WASM_OP_I64_STORE => "i64.store",
WASM_OP_F32_STORE => "f32.store",
WASM_OP_F64_STORE => "f64.store",
WASM_OP_I32_STORE8 => "i32.store8",
WASM_OP_I32_STORE16 => "i32.store16",
WASM_OP_I64_STORE8 => "i64.store8",
WASM_OP_I64_STORE16 => "i64.store16",
WASM_OP_I64_STORE32 => "i64.store32",
WASM_OP_MEMORY_SIZE => "memory.size",
WASM_OP_MEMORY_GROW => "memory.grow",
WASM_OP_I32_CONST => "i32.const",
WASM_OP_I64_CONST => "i64.const",
WASM_OP_F32_CONST => "f32.const",
WASM_OP_F64_CONST => "f64.const",
WASM_OP_I32_EQZ => "i32.eqz",
WASM_OP_I32_EQ => "i32.eq",
WASM_OP_I32_NE => "i32.ne",
WASM_OP_I32_LT_S => "i32.lt_s",
WASM_OP_I32_LT_U => "i32.lt_u",
WASM_OP_I32_GT_S => "i32.gt_s",
WASM_OP_I32_GT_U => "i32.gt_u",
WASM_OP_I32_LE_S => "i32.le_s",
WASM_OP_I32_LE_U => "i32.le_u",
WASM_OP_I32_GE_S => "i32.ge_s",
WASM_OP_I32_GE_U => "i32.ge_u",
WASM_OP_I64_EQZ => "i64.eqz",
WASM_OP_I64_EQ => "i64.eq",
WASM_OP_I64_NE => "i64.ne",
WASM_OP_I64_LT_S => "i64.lt_s",
WASM_OP_I64_LT_U => "i64.lt_u",
WASM_OP_I64_GT_S => "i64.gt_s",
WASM_OP_I64_GT_U => "i64.gt_u",
WASM_OP_I64_LE_S => "i64.le_s",
WASM_OP_I64_LE_U => "i64.le_u",
WASM_OP_I64_GE_S => "i64.ge_s",
WASM_OP_I64_GE_U => "i64.ge_u",
WASM_OP_F32_EQ => "f32.eq",
WASM_OP_F32_NE => "f32.ne",
WASM_OP_F32_LT => "f32.lt",
WASM_OP_F32_GT => "f32.gt",
WASM_OP_F32_LE => "f32.le",
WASM_OP_F32_GE => "f32.ge",
WASM_OP_F64_EQ => "f64.eq",
WASM_OP_F64_NE => "f64.ne",
WASM_OP_F64_LT => "f64.lt",
WASM_OP_F64_GT => "f64.gt",
WASM_OP_F64_LE => "f64.le",
WASM_OP_F64_GE => "f64.ge",
WASM_OP_I32_CLZ => "i32.clz",
WASM_OP_I32_CTZ => "i32.ctz",
WASM_OP_I32_POPCNT => "i32.popcnt",
WASM_OP_I32_ADD => "i32.add",
WASM_OP_I32_SUB => "i32.sub",
WASM_OP_I32_MUL => "i32.mul",
WASM_OP_I32_DIV_S => "i32.div_s",
WASM_OP_I32_DIV_U => "i32.div_u",
WASM_OP_I32_REM_S => "i32.rem_s",
WASM_OP_I32_REM_U => "i32.rem_u",
WASM_OP_I32_AND => "i32.and",
WASM_OP_I32_OR => "i32.or",
WASM_OP_I32_XOR => "i32.xor",
WASM_OP_I32_SHL => "i32.shl",
WASM_OP_I32_SHR_S => "i32.shr_s",
WASM_OP_I32_SHR_U => "i32.shr_u",
WASM_OP_I32_ROTL => "i32.rotl",
WASM_OP_I32_ROTR => "i32.rotr",
WASM_OP_I64_CLZ => "i64.clz",
WASM_OP_I64_CTZ => "i64.ctz",
WASM_OP_I64_POPCNT => "i64.popcnt",
WASM_OP_I64_ADD => "i64.add",
WASM_OP_I64_SUB => "i64.sub",
WASM_OP_I64_MUL => "i64.mul",
WASM_OP_I64_DIV_S => "i64.div_s",
WASM_OP_I64_DIV_U => "i64.div_u",
WASM_OP_I64_REM_S => "i64.rem_s",
WASM_OP_I64_REM_U => "i64.rem_u",
WASM_OP_I64_AND => "i64.and",
WASM_OP_I64_OR => "i64.or",
WASM_OP_I64_XOR => "i64.xor",
WASM_OP_I64_SHL => "i64.shl",
WASM_OP_I64_SHR_S => "i64.shr_s",
WASM_OP_I64_SHR_U => "i64.shr_u",
WASM_OP_I64_ROTL => "i64.rotl",
WASM_OP_I64_ROTR => "i64.rotr",
WASM_OP_F32_ABS => "f32.abs",
WASM_OP_F32_NEG => "f32.neg",
WASM_OP_F32_CEIL => "f32.ceil",
WASM_OP_F32_FLOOR => "f32.floor",
WASM_OP_F32_TRUNC => "f32.trunc",
WASM_OP_F32_NEAREST => "f32.nearest",
WASM_OP_F32_SQRT => "f32.sqrt",
WASM_OP_F32_ADD => "f32.add",
WASM_OP_F32_SUB => "f32.sub",
WASM_OP_F32_MUL => "f32.mul",
WASM_OP_F32_DIV => "f32.div",
WASM_OP_F32_MIN => "f32.min",
WASM_OP_F32_MAX => "f32.max",
WASM_OP_F32_COPYSIGN => "f32.copysign",
WASM_OP_F64_ABS => "f64.abs",
WASM_OP_F64_NEG => "f64.neg",
WASM_OP_F64_CEIL => "f64.ceil",
WASM_OP_F64_FLOOR => "f64.floor",
WASM_OP_F64_TRUNC => "f64.trunc",
WASM_OP_F64_NEAREST => "f64.nearest",
WASM_OP_F64_SQRT => "f64.sqrt",
WASM_OP_F64_ADD => "f64.add",
WASM_OP_F64_SUB => "f64.sub",
WASM_OP_F64_MUL => "f64.mul",
WASM_OP_F64_DIV => "f64.div",
WASM_OP_F64_MIN => "f64.min",
WASM_OP_F64_MAX => "f64.max",
WASM_OP_F64_COPYSIGN => "f64.copysign",
WASM_OP_I32_EXTEND8_S => "i32.extend8_s",
WASM_OP_I32_EXTEND16_S => "i32.extend16_s",
WASM_OP_I64_EXTEND8_S => "i64.extend8_s",
WASM_OP_I64_EXTEND16_S => "i64.extend16_s",
WASM_OP_I64_EXTEND32_S => "i64.extend32_s",
WASM_OP_REF_NULL => "ref.null",
WASM_OP_REF_IS_NULL => "ref.is_null",
WASM_OP_REF_FUNC => "ref.func",
WASM_OP_REF_EQ => "ref.eq",
WASM_OP_REF_AS_NON_NULL => "ref.as_non_null",
WASM_OP_BR_ON_NULL => "br_on_null",
WASM_OP_BR_ON_NON_NULL => "br_on_non_null",
_ => "<unknown>",
}
}
pub fn is_control_flow(opcode: u8) -> bool {
matches!(
opcode,
WASM_OP_UNREACHABLE
| WASM_OP_NOP
| WASM_OP_BLOCK
| WASM_OP_LOOP
| WASM_OP_IF
| WASM_OP_ELSE
| WASM_OP_END
| WASM_OP_BR
| WASM_OP_BR_IF
| WASM_OP_BR_TABLE
| WASM_OP_RETURN
| WASM_OP_CALL
| WASM_OP_CALL_INDIRECT
)
}
pub fn is_constant(opcode: u8) -> bool {
matches!(
opcode,
WASM_OP_I32_CONST | WASM_OP_I64_CONST | WASM_OP_F32_CONST | WASM_OP_F64_CONST
)
}
pub fn is_memory(opcode: u8) -> bool {
(WASM_OP_I32_LOAD..=WASM_OP_I64_STORE32).contains(&opcode)
|| matches!(opcode, WASM_OP_MEMORY_SIZE | WASM_OP_MEMORY_GROW)
}
}
#[derive(Debug, Clone)]
pub struct X86WASMTypeEncoder;
impl X86WASMTypeEncoder {
pub fn encode_reftype(heap_type: i8) -> i8 {
match heap_type {
-0x10 => WASM_TYPE_FUNCREF,
-0x11 => WASM_TYPE_EXTERNREF,
-0x14 => WASM_TYPE_FUNCREF,
-0x15 => WASM_TYPE_EXTERNREF,
_ => WASM_TYPE_EXTERNREF,
}
}
pub fn value_type_byte_size(ty: &WasmValueType, pointer_width: u32) -> u32 {
match ty {
WasmValueType::I32 | WasmValueType::F32 => 4,
WasmValueType::I64 | WasmValueType::F64 => 8,
WasmValueType::V128 => 16,
WasmValueType::Funcref | WasmValueType::Externref => pointer_width / 8,
WasmValueType::Empty => 0,
_ => 4,
}
}
pub fn value_type_alignment(ty: &WasmValueType) -> u32 {
match ty {
WasmValueType::I32 | WasmValueType::F32 => 4,
WasmValueType::I64 | WasmValueType::F64 => 8,
WasmValueType::V128 => 16,
WasmValueType::Funcref | WasmValueType::Externref => 4,
WasmValueType::Empty => 1,
_ => 4,
}
}
pub fn types_compatible(a: &WasmFuncType, b: &WasmFuncType) -> bool {
if a.params.len() != b.params.len() || a.results.len() != b.results.len() {
return false;
}
a.params.iter().zip(&b.params).all(|(p1, p2)| p1 == p2)
&& a.results.iter().zip(&b.results).all(|(r1, r2)| r1 == r2)
}
}
#[derive(Debug, Clone)]
pub struct X86WASMBinaryParser;
impl X86WASMBinaryParser {
pub fn parse(data: &[u8]) -> Result<X86WASMModule, String> {
if data.len() < 8 {
return Err("WASM binary too short".to_string());
}
let magic = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
if magic != WASM_MAGIC {
return Err(format!("Invalid WASM magic: 0x{:08x}", magic));
}
let version = u32::from_le_bytes([data[4], data[5], data[6], data[7]]);
if version != WASM_VERSION {
return Err(format!("Unsupported WASM version: {}", version));
}
let mut module = X86WASMModule::new();
let mut pos = 8;
while pos < data.len() {
let section_id = data[pos];
pos += 1;
let section_size = X86WASMEncoder::read_u32_leb128(data, &mut pos)
.ok_or("Failed to read section size")? as usize;
let section_end = pos + section_size;
match section_id {
WASM_SECTION_CUSTOM => {
let name = X86WASMEncoder::read_name(data, &mut pos).unwrap_or_default();
let payload = data[pos..section_end].to_vec();
module.custom_sections.push(X86WASMCustomSection {
name,
data: payload,
});
}
WASM_SECTION_TYPE => {
let count = X86WASMEncoder::read_u32_leb128(data, &mut pos)
.ok_or("Failed to read type count")?;
for _ in 0..count {
if pos < data.len() && data[pos] == 0x60 {
pos += 1;
let param_count = X86WASMEncoder::read_u32_leb128(data, &mut pos)
.ok_or("Failed to read param count")?;
let mut params = Vec::new();
for _ in 0..param_count {
if pos < data.len() {
let ty = WasmValueType::from_byte(data[pos] as i8)
.unwrap_or(WasmValueType::I32);
params.push(ty);
pos += 1;
}
}
let result_count = X86WASMEncoder::read_u32_leb128(data, &mut pos)
.ok_or("Failed to read result count")?;
let mut results = Vec::new();
for _ in 0..result_count {
if pos < data.len() {
let ty = WasmValueType::from_byte(data[pos] as i8)
.unwrap_or(WasmValueType::I32);
results.push(ty);
pos += 1;
}
}
module.types.push(WasmFuncType { params, results });
}
}
}
WASM_SECTION_IMPORT => {
let count = X86WASMEncoder::read_u32_leb128(data, &mut pos)
.ok_or("Failed to read import count")?;
for _ in 0..count {
let module_name =
X86WASMEncoder::read_name(data, &mut pos).unwrap_or_default();
let field_name =
X86WASMEncoder::read_name(data, &mut pos).unwrap_or_default();
let kind_byte = data[pos];
pos += 1;
let kind = match kind_byte {
WASM_EXTERNAL_KIND_FUNCTION => WasmExternalKind::Function,
WASM_EXTERNAL_KIND_TABLE => WasmExternalKind::Table,
WASM_EXTERNAL_KIND_MEMORY => WasmExternalKind::Memory,
WASM_EXTERNAL_KIND_GLOBAL => WasmExternalKind::Global,
_ => continue,
};
let type_idx = if kind_byte == WASM_EXTERNAL_KIND_FUNCTION {
X86WASMEncoder::read_u32_leb128(data, &mut pos).unwrap_or(0)
} else {
0
};
module.imports.push(WasmImport {
module: module_name,
name: field_name,
kind,
type_idx,
});
}
}
WASM_SECTION_FUNCTION => {
let count = X86WASMEncoder::read_u32_leb128(data, &mut pos)
.ok_or("Failed to read function count")?;
for _ in 0..count {
let type_idx = X86WASMEncoder::read_u32_leb128(data, &mut pos).unwrap_or(0);
module.functions.push(WasmFunction {
name: format!("func_{}", module.functions.len()),
type_idx,
locals: Vec::new(),
body: Vec::new(),
is_export: false,
is_import: false,
import_module: None,
import_name: None,
});
}
}
WASM_SECTION_MEMORY => {
let count = X86WASMEncoder::read_u32_leb128(data, &mut pos)
.ok_or("Failed to read memory count")?;
for _ in 0..count {
let flags = data[pos];
pos += 1;
let min =
X86WASMEncoder::read_u32_leb128(data, &mut pos).unwrap_or(1) as u64;
let max = if flags & 0x01 != 0 {
Some(
X86WASMEncoder::read_u32_leb128(data, &mut pos).unwrap_or(65536)
as u64,
)
} else {
None
};
module.memories.push(WasmMemory {
min_pages: min,
max_pages: max,
is_shared: false,
is_64: false,
});
}
}
WASM_SECTION_EXPORT => {
let count = X86WASMEncoder::read_u32_leb128(data, &mut pos)
.ok_or("Failed to read export count")?;
for _ in 0..count {
let name = X86WASMEncoder::read_name(data, &mut pos).unwrap_or_default();
let kind_byte = data[pos];
pos += 1;
let kind = match kind_byte {
WASM_EXTERNAL_KIND_FUNCTION => WasmExternalKind::Function,
WASM_EXTERNAL_KIND_TABLE => WasmExternalKind::Table,
WASM_EXTERNAL_KIND_MEMORY => WasmExternalKind::Memory,
WASM_EXTERNAL_KIND_GLOBAL => WasmExternalKind::Global,
_ => continue,
};
let index = X86WASMEncoder::read_u32_leb128(data, &mut pos).unwrap_or(0);
module.exports.push(WasmExport { name, kind, index });
}
}
WASM_SECTION_START => {
let start = X86WASMEncoder::read_u32_leb128(data, &mut pos).unwrap_or(0);
module.start_function = Some(start);
}
WASM_SECTION_CODE => {
let count = X86WASMEncoder::read_u32_leb128(data, &mut pos)
.ok_or("Failed to read code count")?;
for i in 0..count as usize {
let body_size = X86WASMEncoder::read_u32_leb128(data, &mut pos)
.ok_or("Failed to read body size")?
as usize;
let _body_end = pos + body_size;
let local_count =
X86WASMEncoder::read_u32_leb128(data, &mut pos).unwrap_or(0);
let mut locals = Vec::new();
for _ in 0..local_count {
let n = X86WASMEncoder::read_u32_leb128(data, &mut pos).unwrap_or(1);
let ty_byte = if pos < data.len() { data[pos] } else { 0x7f };
pos += 1;
let ty = WasmValueType::from_byte(ty_byte as i8)
.unwrap_or(WasmValueType::I32);
for _ in 0..n {
locals.push(ty);
}
}
if let Some(func) = module.functions.get_mut(i) {
func.locals = locals;
}
pos = pos + body_size - (pos - (pos)); }
}
_ => {
}
}
pos = section_end;
}
Ok(module)
}
}
#[derive(Debug, Clone)]
pub struct X86WASMComponent {
pub name: String,
pub imports: Vec<X86WASMComponentImport>,
pub exports: Vec<X86WASMComponentExport>,
pub modules: Vec<X86WASMModule>,
pub sub_components: Vec<X86WASMComponent>,
pub instances: Vec<X86WASMComponentInstance>,
}
impl X86WASMComponent {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
imports: Vec::new(),
exports: Vec::new(),
modules: Vec::new(),
sub_components: Vec::new(),
instances: Vec::new(),
}
}
pub fn add_module(&mut self, module: X86WASMModule) {
self.modules.push(module);
}
pub fn add_import(&mut self, import: X86WASMComponentImport) {
self.imports.push(import);
}
pub fn add_export(&mut self, export: X86WASMComponentExport) {
self.exports.push(export);
}
}
#[derive(Debug, Clone)]
pub struct X86WASMComponentImport {
pub name: String,
pub kind: X86WASMComponentImportKind,
}
#[derive(Debug, Clone)]
pub enum X86WASMComponentImportKind {
Func(WasmFuncType),
Module(X86WASMModule),
Component(X86WASMComponent),
Instance(Vec<X86WASMComponentExport>),
Value(WasmValueType),
Type(WasmFuncType),
}
#[derive(Debug, Clone)]
pub struct X86WASMComponentExport {
pub name: String,
pub kind: X86WASMComponentExportKind,
}
#[derive(Debug, Clone)]
pub enum X86WASMComponentExportKind {
Func(u32),
Module(u32),
Component(u32),
Instance(u32),
Value(WasmValueType),
}
#[derive(Debug, Clone)]
pub struct X86WASMComponentInstance {
pub name: String,
pub component_idx: u32,
pub args: Vec<(String, X86WASMComponentImport)>,
}
#[cfg(test)]
mod extended_tests {
use super::*;
#[test]
fn test_memory_manager_new() {
let config = X86WASMConfig::default();
let mm = X86WASMMemoryManager::new(&config);
assert_eq!(mm.memory_pages(), config.initial_memory_pages);
assert_eq!(mm.heap_end(), 1024); }
#[test]
fn test_memory_manager_allocate() {
let config = X86WASMConfig::default();
let mut mm = X86WASMMemoryManager::new(&config);
let addr = mm.allocate(256, 16);
assert!(addr >= 1024);
assert_eq!(addr % 16, 0);
assert!(mm.heap_end() >= addr + 256);
}
#[test]
fn test_memory_manager_allocate_static() {
let config = X86WASMConfig::default();
let mut mm = X86WASMMemoryManager::new(&config);
let addr = mm.allocate_static(128, 8, "my_data");
assert!(addr >= 1024);
}
#[test]
fn test_memory_manager_reserve_stack() {
let config = X86WASMConfig::default();
let mut mm = X86WASMMemoryManager::new(&config);
let stack_base = mm.reserve_stack();
assert!(stack_base >= 1024);
}
#[test]
fn test_memory_manager_add_memory_globals() {
let config = X86WASMConfig::default();
let mut mm = X86WASMMemoryManager::new(&config);
let mut module = X86WASMModule::new();
mm.add_memory_globals(&mut module);
assert_eq!(module.globals.len(), 3);
assert!(module.globals.iter().any(|g| g.name == "__stack_pointer"));
assert!(module.globals.iter().any(|g| g.name == "__heap_base"));
assert!(module.globals.iter().any(|g| g.name == "__data_end"));
}
#[test]
fn test_memory_manager_add_memory_section() {
let config = X86WASMConfig::default();
let mm = X86WASMMemoryManager::new(&config);
let mut module = X86WASMModule::new();
mm.add_memory_section(&mut module);
assert!(!module.memories.is_empty());
assert_eq!(module.memories[0].min_pages, config.initial_memory_pages);
}
#[test]
fn test_opcode_name() {
assert_eq!(
X86WASMOpcodeInfo::opcode_name(WASM_OP_UNREACHABLE),
"unreachable"
);
assert_eq!(X86WASMOpcodeInfo::opcode_name(WASM_OP_NOP), "nop");
assert_eq!(X86WASMOpcodeInfo::opcode_name(WASM_OP_BLOCK), "block");
assert_eq!(X86WASMOpcodeInfo::opcode_name(WASM_OP_LOOP), "loop");
assert_eq!(X86WASMOpcodeInfo::opcode_name(WASM_OP_IF), "if");
assert_eq!(X86WASMOpcodeInfo::opcode_name(WASM_OP_ELSE), "else");
assert_eq!(X86WASMOpcodeInfo::opcode_name(WASM_OP_END), "end");
assert_eq!(X86WASMOpcodeInfo::opcode_name(WASM_OP_BR), "br");
assert_eq!(X86WASMOpcodeInfo::opcode_name(WASM_OP_BR_IF), "br_if");
assert_eq!(X86WASMOpcodeInfo::opcode_name(WASM_OP_RETURN), "return");
assert_eq!(X86WASMOpcodeInfo::opcode_name(WASM_OP_CALL), "call");
assert_eq!(
X86WASMOpcodeInfo::opcode_name(WASM_OP_I32_CONST),
"i32.const"
);
assert_eq!(X86WASMOpcodeInfo::opcode_name(WASM_OP_I32_ADD), "i32.add");
assert_eq!(X86WASMOpcodeInfo::opcode_name(WASM_OP_I32_MUL), "i32.mul");
assert_eq!(X86WASMOpcodeInfo::opcode_name(WASM_OP_I32_LOAD), "i32.load");
assert_eq!(
X86WASMOpcodeInfo::opcode_name(WASM_OP_I32_STORE),
"i32.store"
);
assert_eq!(
X86WASMOpcodeInfo::opcode_name(WASM_OP_MEMORY_SIZE),
"memory.size"
);
assert_eq!(
X86WASMOpcodeInfo::opcode_name(WASM_OP_MEMORY_GROW),
"memory.grow"
);
assert_eq!(
X86WASMOpcodeInfo::opcode_name(WASM_OP_I32_EXTEND8_S),
"i32.extend8_s"
);
assert_eq!(X86WASMOpcodeInfo::opcode_name(WASM_OP_REF_NULL), "ref.null");
assert_eq!(X86WASMOpcodeInfo::opcode_name(WASM_OP_REF_FUNC), "ref.func");
assert_eq!(X86WASMOpcodeInfo::opcode_name(0xFF), "<unknown>");
}
#[test]
fn test_opcode_is_control_flow() {
assert!(X86WASMOpcodeInfo::is_control_flow(WASM_OP_BLOCK));
assert!(X86WASMOpcodeInfo::is_control_flow(WASM_OP_LOOP));
assert!(X86WASMOpcodeInfo::is_control_flow(WASM_OP_IF));
assert!(X86WASMOpcodeInfo::is_control_flow(WASM_OP_BR));
assert!(X86WASMOpcodeInfo::is_control_flow(WASM_OP_BR_IF));
assert!(X86WASMOpcodeInfo::is_control_flow(WASM_OP_RETURN));
assert!(X86WASMOpcodeInfo::is_control_flow(WASM_OP_CALL));
assert!(!X86WASMOpcodeInfo::is_control_flow(WASM_OP_I32_ADD));
assert!(!X86WASMOpcodeInfo::is_control_flow(WASM_OP_I32_CONST));
}
#[test]
fn test_opcode_is_constant() {
assert!(X86WASMOpcodeInfo::is_constant(WASM_OP_I32_CONST));
assert!(X86WASMOpcodeInfo::is_constant(WASM_OP_I64_CONST));
assert!(X86WASMOpcodeInfo::is_constant(WASM_OP_F32_CONST));
assert!(X86WASMOpcodeInfo::is_constant(WASM_OP_F64_CONST));
assert!(!X86WASMOpcodeInfo::is_constant(WASM_OP_I32_ADD));
}
#[test]
fn test_opcode_is_memory() {
assert!(X86WASMOpcodeInfo::is_memory(WASM_OP_I32_LOAD));
assert!(X86WASMOpcodeInfo::is_memory(WASM_OP_I64_LOAD));
assert!(X86WASMOpcodeInfo::is_memory(WASM_OP_I32_STORE));
assert!(X86WASMOpcodeInfo::is_memory(WASM_OP_MEMORY_SIZE));
assert!(!X86WASMOpcodeInfo::is_memory(WASM_OP_I32_ADD));
}
#[test]
fn test_reftype_encoding() {
assert_eq!(X86WASMTypeEncoder::encode_reftype(-0x10), WASM_TYPE_FUNCREF);
assert_eq!(
X86WASMTypeEncoder::encode_reftype(-0x11),
WASM_TYPE_EXTERNREF
);
}
#[test]
fn test_value_type_byte_size() {
assert_eq!(
X86WASMTypeEncoder::value_type_byte_size(&WasmValueType::I32, 32),
4
);
assert_eq!(
X86WASMTypeEncoder::value_type_byte_size(&WasmValueType::I64, 32),
8
);
assert_eq!(
X86WASMTypeEncoder::value_type_byte_size(&WasmValueType::V128, 32),
16
);
assert_eq!(
X86WASMTypeEncoder::value_type_byte_size(&WasmValueType::Empty, 32),
0
);
}
#[test]
fn test_value_type_alignment() {
assert_eq!(
X86WASMTypeEncoder::value_type_alignment(&WasmValueType::I32),
4
);
assert_eq!(
X86WASMTypeEncoder::value_type_alignment(&WasmValueType::I64),
8
);
assert_eq!(
X86WASMTypeEncoder::value_type_alignment(&WasmValueType::V128),
16
);
}
#[test]
fn test_types_compatible() {
let a = WasmFuncType {
params: vec![WasmValueType::I32],
results: vec![WasmValueType::I32],
};
let b = WasmFuncType {
params: vec![WasmValueType::I32],
results: vec![WasmValueType::I32],
};
assert!(X86WASMTypeEncoder::types_compatible(&a, &b));
let c = WasmFuncType {
params: vec![WasmValueType::I64],
results: vec![WasmValueType::I32],
};
assert!(!X86WASMTypeEncoder::types_compatible(&a, &c));
let d = WasmFuncType {
params: vec![WasmValueType::I32],
results: vec![WasmValueType::I64],
};
assert!(!X86WASMTypeEncoder::types_compatible(&a, &d));
}
#[test]
fn test_parser_valid_module() {
let module = X86WASMModule::new();
let binary = module.emit_binary();
let parsed = X86WASMBinaryParser::parse(&binary);
assert!(parsed.is_ok());
}
#[test]
fn test_parser_invalid_magic() {
let result = X86WASMBinaryParser::parse(&[0; 100]);
assert!(result.is_err());
}
#[test]
fn test_parser_too_short() {
let result = X86WASMBinaryParser::parse(&[0; 4]);
assert!(result.is_err());
}
#[test]
fn test_parser_with_types() {
let mut module = X86WASMModule::new();
module.types.push(WasmFuncType {
params: vec![WasmValueType::I32, WasmValueType::I32],
results: vec![WasmValueType::I32],
});
let binary = module.emit_binary();
let parsed = X86WASMBinaryParser::parse(&binary);
assert!(parsed.is_ok());
assert_eq!(parsed.unwrap().types.len(), 1);
}
#[test]
fn test_parser_with_imports() {
let mut module = X86WASMModule::new();
module.types.push(WasmFuncType {
params: vec![WasmValueType::I32; 4],
results: vec![WasmValueType::I32],
});
module.imports.push(WasmImport {
module: "wasi_snapshot_preview1".to_string(),
name: "fd_write".to_string(),
kind: WasmExternalKind::Function,
type_idx: 0,
});
let binary = module.emit_binary();
let parsed = X86WASMBinaryParser::parse(&binary);
assert!(parsed.is_ok());
assert!(parsed.unwrap().imports.len() >= 1);
}
#[test]
fn test_parser_roundtrip_with_memory() {
let mut module = X86WASMModule::new();
module.memories.push(WasmMemory {
min_pages: 256,
max_pages: Some(65536),
is_shared: false,
is_64: false,
});
let binary = module.emit_binary();
let parsed = X86WASMBinaryParser::parse(&binary);
assert!(parsed.is_ok());
assert!(parsed.unwrap().memories.len() >= 1);
}
#[test]
fn test_parser_roundtrip_with_exports_and_start() {
let mut module = X86WASMModule::new();
module.types.push(WasmFuncType {
params: vec![],
results: vec![],
});
module.functions.push(WasmFunction {
name: "_start".to_string(),
type_idx: 0,
locals: Vec::new(),
body: vec![WasmInstruction::End],
is_export: false,
is_import: false,
import_module: None,
import_name: None,
});
module.exports.push(WasmExport {
name: "memory".to_string(),
kind: WasmExternalKind::Memory,
index: 0,
});
module.start_function = Some(0);
let binary = module.emit_binary();
let parsed = X86WASMBinaryParser::parse(&binary);
assert!(parsed.is_ok());
let p = parsed.unwrap();
assert!(p.exports.len() >= 1);
assert_eq!(p.start_function, Some(0));
}
#[test]
fn test_component_new() {
let comp = X86WASMComponent::new("my-component");
assert_eq!(comp.name, "my-component");
assert!(comp.modules.is_empty());
}
#[test]
fn test_component_add_module() {
let mut comp = X86WASMComponent::new("test");
comp.add_module(X86WASMModule::new());
assert_eq!(comp.modules.len(), 1);
}
#[test]
fn test_component_add_import_export() {
let mut comp = X86WASMComponent::new("test");
comp.add_import(X86WASMComponentImport {
name: "my-import".to_string(),
kind: X86WASMComponentImportKind::Func(WasmFuncType::default()),
});
comp.add_export(X86WASMComponentExport {
name: "my-export".to_string(),
kind: X86WASMComponentExportKind::Func(0),
});
assert_eq!(comp.imports.len(), 1);
assert_eq!(comp.exports.len(), 1);
}
#[test]
fn test_feature_flags_from_config() {
let mut config = X86WASMConfig::default();
config.enable_simd128 = true;
config.enable_threads = true;
let flags = X86WASMFeatureFlags::from_config(&config);
assert!(flags.simd128);
assert!(flags.atomics);
}
#[test]
fn test_feature_flags_display_all() {
let mut flags = X86WASMFeatureFlags::default();
flags.gc = true;
flags.memory64 = true;
let s = format!("{}", flags);
assert!(s.contains("gc"));
assert!(s.contains("memory64"));
}
#[test]
fn test_leb128_u64_large() {
let encoder = X86WASMEncoder::new();
let mut buf = Vec::new();
encoder.write_u64_leb128(&mut buf, u64::MAX);
let mut pos = 0;
let decoded = X86WASMEncoder::read_u32_leb128(&buf, &mut pos);
assert!(decoded.is_some());
}
#[test]
fn test_leb128_i64_large() {
let encoder = X86WASMEncoder::new();
let mut buf = Vec::new();
encoder.write_i64_leb128(&mut buf, i64::MIN);
assert!(!buf.is_empty());
}
#[test]
fn test_leb128_read_invalid() {
let buf = vec![0x80, 0x80, 0x80, 0x80, 0x80, 0x80]; let mut pos = 0;
let decoded = X86WASMEncoder::read_u32_leb128(&buf, &mut pos);
assert!(decoded.is_none());
}
#[test]
fn test_config_all_features_enabled() {
let mut config = X86WASMConfig::default();
config.set_features("+simd128,+atomics,+bulk-memory,+reference-types,+multi-value,+tail-call,+exception-handling,+extended-const,+relaxed-simd,+memory64,+gc");
assert!(config.enable_simd128);
assert!(config.enable_threads);
assert!(config.enable_tail_calls);
assert!(config.enable_exceptions);
assert!(config.enable_memory64);
assert!(config.enable_gc);
}
#[test]
fn test_config_all_features_disabled() {
let mut config = X86WASMConfig::default();
config.set_features("-simd128,-atomics,-bulk-memory,-reference-types,-multi-value,-tail-call,-exception-handling,-extended-const,-relaxed-simd,-memory64,-gc");
assert!(!config.enable_simd128);
assert!(!config.enable_threads);
assert!(!config.enable_tail_calls);
assert!(!config.enable_exceptions);
assert!(!config.enable_memory64);
assert!(!config.enable_gc);
}
#[test]
fn test_module_clone_identity() {
let mut m = X86WASMModule::new();
m.types.push(WasmFuncType::default());
m.functions.push(WasmFunction {
name: "f".to_string(),
type_idx: 0,
locals: Vec::new(),
body: Vec::new(),
is_export: true,
is_import: false,
import_module: None,
import_name: None,
});
let clone = m.clone();
assert_eq!(clone.types.len(), m.types.len());
assert_eq!(clone.functions.len(), m.functions.len());
assert_eq!(clone.functions[0].name, "f");
}
#[test]
fn test_module_validate_roundtrip() {
let mut m = X86WASMModule::new();
m.types.push(WasmFuncType {
params: vec![],
results: vec![WasmValueType::I32],
});
m.functions.push(WasmFunction {
name: "main".to_string(),
type_idx: 0,
locals: Vec::new(),
body: vec![WasmInstruction::I32Const(0), WasmInstruction::End],
is_export: true,
is_import: false,
import_module: None,
import_name: None,
});
m.exports.push(WasmExport {
name: "main".to_string(),
kind: WasmExternalKind::Function,
index: 0,
});
let v = X86WASMValidator::default();
assert!(v.validate_module(&m).is_ok());
let bin = m.emit_binary();
let parsed = X86WASMBinaryParser::parse(&bin).unwrap();
assert!(v.validate_module(&parsed).is_ok());
}
#[test]
fn test_wasi_all_44_functions_valid() {
let functions = [
"args_get",
"args_sizes_get",
"environ_get",
"environ_sizes_get",
"clock_res_get",
"clock_time_get",
"fd_advise",
"fd_allocate",
"fd_close",
"fd_datasync",
"fd_fdstat_get",
"fd_fdstat_set_flags",
"fd_filestat_get",
"fd_filestat_set_size",
"fd_filestat_set_times",
"fd_pread",
"fd_prestat_get",
"fd_prestat_dir_name",
"fd_pwrite",
"fd_read",
"fd_readdir",
"fd_renumber",
"fd_seek",
"fd_sync",
"fd_tell",
"fd_write",
"path_create_directory",
"path_filestat_get",
"path_filestat_set_times",
"path_link",
"path_open",
"path_readlink",
"path_remove_directory",
"path_rename",
"path_symlink",
"path_unlink_file",
"poll_oneoff",
"proc_exit",
"proc_raise",
"sched_yield",
"random_get",
"sock_accept",
"sock_recv",
"sock_send",
"sock_shutdown",
];
for f in &functions {
assert!(
X86WASMWASI::is_valid_wasi_function(f),
"Expected {} to be valid",
f
);
}
assert_eq!(functions.len(), 44);
}
#[test]
fn test_sse_intrinsics_coverage() {
assert!(X86WASMEmscripten::map_sse_to_simd128("_mm_add_ps").is_some());
assert!(X86WASMEmscripten::map_sse_to_simd128("_mm_sub_ps").is_some());
assert!(X86WASMEmscripten::map_sse_to_simd128("_mm_mul_ps").is_some());
assert!(X86WASMEmscripten::map_sse_to_simd128("_mm_div_ps").is_some());
assert!(X86WASMEmscripten::map_sse_to_simd128("_mm_and_ps").is_some());
assert!(X86WASMEmscripten::map_sse_to_simd128("_mm_or_ps").is_some());
assert!(X86WASMEmscripten::map_sse_to_simd128("_mm_xor_ps").is_some());
assert!(X86WASMEmscripten::map_sse_to_simd128("_mm_cmpeq_ps").is_some());
assert!(X86WASMEmscripten::map_sse_to_simd128("_mm_cmplt_ps").is_some());
assert!(X86WASMEmscripten::map_sse_to_simd128("_mm_cmple_ps").is_some());
assert!(X86WASMEmscripten::map_sse_to_simd128("_mm_load_ps").is_some());
assert!(X86WASMEmscripten::map_sse_to_simd128("_mm_store_ps").is_some());
assert!(X86WASMEmscripten::map_sse_to_simd128("_mm_set1_ps").is_some());
}
#[test]
fn test_block_type_default() {
assert_eq!(WasmBlockType::default(), WasmBlockType::Empty);
}
#[test]
fn test_init_expr_variants() {
let e1 = WasmInitExpr::I32Const(42);
let e2 = WasmInitExpr::I64Const(42);
let e3 = WasmInitExpr::F32Const(3.14);
let e4 = WasmInitExpr::F64Const(3.14);
let e5 = WasmInitExpr::GlobalGet(0);
let e6 = WasmInitExpr::End;
assert!(true);
}
#[test]
fn test_validator_stack_underflow_binary() {
let v = X86WASMValidator::default();
let ft = WasmFuncType::default();
let f = WasmFunction {
name: "bad".to_string(),
type_idx: 0,
locals: Vec::new(),
body: vec![WasmInstruction::I32Add], is_export: false,
is_import: false,
import_module: None,
import_name: None,
};
assert!(v.validate_function(&f, &ft).is_err());
}
#[test]
fn test_validator_stack_underflow_set() {
let v = X86WASMValidator::default();
let ft = WasmFuncType::default();
let f = WasmFunction {
name: "bad2".to_string(),
type_idx: 0,
locals: Vec::new(),
body: vec![WasmInstruction::LocalSet { local_idx: 0 }], is_export: false,
is_import: false,
import_module: None,
import_name: None,
};
assert!(v.validate_function(&f, &ft).is_err());
}
#[test]
fn test_validator_valid_function() {
let v = X86WASMValidator::default();
let ft = WasmFuncType::default();
let f = WasmFunction {
name: "good".to_string(),
type_idx: 0,
locals: Vec::new(),
body: vec![
WasmInstruction::I32Const(1),
WasmInstruction::I32Const(2),
WasmInstruction::I32Add,
WasmInstruction::Drop,
WasmInstruction::Return,
],
is_export: false,
is_import: false,
import_module: None,
import_name: None,
};
assert!(v.validate_function(&f, &ft).is_ok());
}
#[test]
fn test_linker_resolves_imports() {
let config = X86WASMConfig::default();
let mut linker = X86WASMLinker::new(&config);
let mut m1 = X86WASMModule::new();
m1.types.push(WasmFuncType {
params: vec![],
results: vec![WasmValueType::I32],
});
m1.functions.push(WasmFunction {
name: "get_value".to_string(),
type_idx: 0,
locals: Vec::new(),
body: vec![WasmInstruction::I32Const(42), WasmInstruction::End],
is_export: true,
is_import: false,
import_module: None,
import_name: None,
});
m1.exports.push(WasmExport {
name: "get_value".to_string(),
kind: WasmExternalKind::Function,
index: 0,
});
let result = linker.link(&[m1]);
assert!(result.is_ok());
let linked = result.unwrap();
assert_eq!(linked.functions.len(), 1);
}
}