From fcc6b65ed42084702df5855a6bf209d54c77b212 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 Jul 2026 14:17:06 +0100 Subject: [PATCH] Remove old `wasm` and `wasm/module` code --- core/rexcode/wasm/decoder.odin | 259 ------- core/rexcode/wasm/decoding_tables.odin | 560 -------------- core/rexcode/wasm/encoder.odin | 212 ------ core/rexcode/wasm/encoding_table.odin | 505 ------------- core/rexcode/wasm/encoding_types.odin | 239 ------ core/rexcode/wasm/instructions.odin | 151 ---- core/rexcode/wasm/mnemonics.odin | 471 ------------ core/rexcode/wasm/module/module.odin | 281 ------- core/rexcode/wasm/module/parse.odin | 522 ------------- core/rexcode/wasm/module/print.odin | 267 ------- core/rexcode/wasm/module/print_wat.odin | 708 ------------------ core/rexcode/wasm/module/relocs.odin | 103 --- core/rexcode/wasm/operands.odin | 191 ----- core/rexcode/wasm/printer.odin | 351 --------- core/rexcode/wasm/registers.odin | 76 -- core/rexcode/wasm/reloc.odin | 44 -- core/rexcode/wasm/tests/pipeline_smoke.odin | 142 ---- core/rexcode/wasm/tests/smoke.odin | 86 --- .../rexcode/wasm/tools/dump_verify_input.odin | 99 --- 19 files changed, 5267 deletions(-) delete mode 100644 core/rexcode/wasm/decoder.odin delete mode 100644 core/rexcode/wasm/decoding_tables.odin delete mode 100644 core/rexcode/wasm/encoder.odin delete mode 100644 core/rexcode/wasm/encoding_table.odin delete mode 100644 core/rexcode/wasm/encoding_types.odin delete mode 100644 core/rexcode/wasm/instructions.odin delete mode 100644 core/rexcode/wasm/mnemonics.odin delete mode 100644 core/rexcode/wasm/module/module.odin delete mode 100644 core/rexcode/wasm/module/parse.odin delete mode 100644 core/rexcode/wasm/module/print.odin delete mode 100644 core/rexcode/wasm/module/print_wat.odin delete mode 100644 core/rexcode/wasm/module/relocs.odin delete mode 100644 core/rexcode/wasm/operands.odin delete mode 100644 core/rexcode/wasm/printer.odin delete mode 100644 core/rexcode/wasm/registers.odin delete mode 100644 core/rexcode/wasm/reloc.odin delete mode 100644 core/rexcode/wasm/tests/pipeline_smoke.odin delete mode 100644 core/rexcode/wasm/tests/smoke.odin delete mode 100644 core/rexcode/wasm/tools/dump_verify_input.odin diff --git a/core/rexcode/wasm/decoder.odin b/core/rexcode/wasm/decoder.odin deleted file mode 100644 index cd22997a0..000000000 --- a/core/rexcode/wasm/decoder.odin +++ /dev/null @@ -1,259 +0,0 @@ -// rexcode · Brendan Punsky (dotbmp@github), original author -// Ginger Bill (gingerBill@github) - -package rexcode_wasm - -import "base:runtime" - -// ============================================================================= -// WebAssembly DECODER -// ============================================================================= -// -// Single forward pass, mirroring the encoder. Each step: -// -// 1. Read the opcode. A leading 0xFC switches to the misc group, whose -// sub-opcode is an unsigned LEB128 read next; otherwise the single byte -// is the opcode. The byte (or sub-opcode) indexes the DECODE_MAIN / -// DECODE_MISC tables built from ENCODING_TABLE at package init. -// 2. Look the resulting Mnemonic's form back up in ENCODING_TABLE and read -// its immediates in declaration order, reconstructing Operands. -// -// WASM control flow is structured (branches carry relative label depths, not -// byte offsets), so there is no PC-relative label inference. Object-file index -// relocations *are* re-attached: when an input relocation lands on a decoded -// index field, that operand is marked `symbolic` and carries the label id. -// -// `br_table`'s case-label vector is materialised into a freshly allocated -// `[]u32` (caller owns it, like the rest of the decoded output). - -Instruction_Info :: struct { - offset: u32, - decode_entry: u16, - _: u16, -} -#assert(size_of(Instruction_Info) == 8) - -decode :: proc( - data: []u8, - relocs: []Relocation, - instructions: ^[dynamic]Instruction, - inst_info: ^[dynamic]Instruction_Info, - errors: ^[dynamic]Error, - targets_allocator := context.allocator, -) -> (byte_count: u32, ok: bool) { - errors_start := u32(len(errors)) - n := u32(len(data)) - - for byte_count < n { - inst, info, next, dok := decode_one(data, relocs, byte_count, targets_allocator) - if !dok { - append(errors, Error{inst_idx = byte_count, code = .INVALID_OPCODE}) - inst = Instruction{mnemonic = .INVALID, length = 1} - info = Instruction_Info{offset = byte_count} - append(instructions, inst) - append(inst_info, info) - byte_count += 1 - continue - } - inst.length = u8(min(next - byte_count, 255)) - append(instructions, inst) - append(inst_info, info) - byte_count = next - } - - ok = u32(len(errors)) == errors_start - return -} - - -@(require_results) -decode_one :: proc( - data: []u8, - relocs: []Relocation, - pc: u32, - targets_allocator: runtime.Allocator, -) -> (inst: Instruction, info: Instruction_Info, next: u32, ok: bool) { - off := pc - if off >= u32(len(data)) { - next = pc - return - } - - b0 := data[off] - off += 1 - - m: Mnemonic = .INVALID - switch b0 { - case PREFIX_MISC: - sub := read_uleb(data, &off) or_return - if sub < u64(DECODE_MISC_COUNT) { - m = DECODE_MISC[sub] - } - case PREFIX_SIMD: - sub := read_uleb(data, &off) or_return - if sub < u64(DECODE_SIMD_COUNT) { - m = DECODE_SIMD[sub] - } - case PREFIX_ATOM: - sub := read_uleb(data, &off) or_return - if sub < u64(DECODE_ATOMIC_COUNT) { - m = DECODE_ATOMIC[sub] - } - case: - m = DECODE_MAIN[b0] - } - if m == .INVALID { - next = pc - return - } - - form := encoding_form(m) - inst.mnemonic = m - inst.flags = {} - - slot := 0 - for k, ki in form.imm { - switch k { - case .NONE: - // nothing - - case .BLOCKTYPE: - v := read_sleb(data, &off) or_return - inst.ops[slot] = Operand{kind = .BLOCK_TYPE, immediate = v} - slot += 1 - - case .I32: - v := read_sleb(data, &off) or_return - inst.ops[slot] = Operand{kind = .IMMEDIATE, immediate = v, size = 4} - slot += 1 - - case .I64: - v := read_sleb(data, &off) or_return - inst.ops[slot] = Operand{kind = .IMMEDIATE, immediate = v, size = 8} - slot += 1 - - case .F32: - bits := read_u32_block(data, &off) or_return - inst.ops[slot] = Operand{ - kind = .IMMEDIATE, - immediate = i64(bits), size = 4, flags = {is_float = true}, - } - slot += 1 - - case .F64: - bits := read_u64_block(data, &off) or_return - inst.ops[slot] = Operand{ - kind = .IMMEDIATE, - immediate = i64(bits), size = 8, flags = {is_float = true}, - } - slot += 1 - - case .IDX: - field := off - raw := read_uleb(data, &off) or_return - op := Operand{kind = .INDEX, index = u32(raw), idx_kind = idx_kind_for(m, ki)} - if lid, found := reloc_label_at(relocs, field); found { - op.index = lid - op.flags.symbolic = true - op.size = 5 - } else if m == .CALL { - op.flags.symbolic = true - } - inst.ops[slot] = op - slot += 1 - - case .MEMARG: - // TODO(bill): Is this fully correct? - // See: https://webassembly.github.io/spec/core/binary/instructions.html#memory-instructions - align := read_uleb(data, &off) or_return - offset := read_uleb(data, &off) or_return - // NOTE(bill) this appears to be stored as log2 even though the docs say otherwise - align = 1<= u32(len(data)) { - next = pc - return - } - t := data[off] - off += 1 - inst.ops[slot] = Operand{kind = .IMMEDIATE, immediate = i64(t), size = 1} - slot += 1 - - case .BR_TABLE: - count := read_uleb(data, &off) or_return - targets := make([]u32, int(count), targets_allocator) - for &target in targets { - t := read_uleb(data, &off) or_return - target = u32(t) - } - def := read_uleb(data, &off) or_return - inst.targets = targets - inst.ops[slot] = Operand{kind = .INDEX, index = u32(def), idx_kind = .LABEL} - slot += 1 - - case .ZERO_BYTE: - if off >= u32(len(data)) { - next = pc - return - } - off += 1 // reserved 0x00, consumes no operand - - case .LANE: - if off >= u32(len(data)) { - next = pc - return - } - l := data[off] - off += 1 - inst.ops[slot] = Operand{kind = .IMMEDIATE, immediate = i64(l), size = 1} - slot += 1 - - case .LANES16: - if off + 16 > u32(len(data)) { - next = pc - return - } - copy(inst.bytes[:], data[off:][:16]) - off += 16 // value lives in inst.bytes, no operand - } - } - - inst.operand_count = u8(slot) - info.offset = pc - info.decode_entry = u16(m) - next = off - ok = true - return -} - -// Which index space the IDX immediate in operand slot `which` addresses, by -// mnemonic. Mirrors how the builders in instructions.odin tag each operand. -@(private="file", require_results) -idx_kind_for :: #force_inline proc "contextless" (m: Mnemonic, which: int) -> Index_Kind { - #partial switch m { - case .BR, .BR_IF: return .LABEL - case .CALL, .REF_FUNC: return .FUNC - case .CALL_INDIRECT: return .TYPE if which == 0 else .TABLE - case .LOCAL_GET, .LOCAL_SET, .LOCAL_TEE: return .LOCAL - case .GLOBAL_GET, .GLOBAL_SET: return .GLOBAL - case .MEMORY_INIT, .DATA_DROP: return .DATA - case .TABLE_INIT: return .ELEM if which == 0 else .TABLE - case .ELEM_DROP: return .ELEM - case .TABLE_COPY: return .TABLE - case .TABLE_GROW, .TABLE_SIZE, .TABLE_FILL: return .TABLE - } - return .NONE -} - -@(private="file", require_results) -reloc_label_at :: #force_inline proc "contextless" (relocs: []Relocation, offset: u32) -> (label_id: u32, found: bool) { - for r in relocs { - if r.offset == offset { - return r.label_id, true - } - } - return -} diff --git a/core/rexcode/wasm/decoding_tables.odin b/core/rexcode/wasm/decoding_tables.odin deleted file mode 100644 index 751676186..000000000 --- a/core/rexcode/wasm/decoding_tables.odin +++ /dev/null @@ -1,560 +0,0 @@ -// rexcode · Brendan Punsky (dotbmp@github), original author -// Ginger Bill (gingerBill@github) - -package rexcode_wasm - -// ============================================================================= -// WebAssembly DECODE DISPATCH TABLES -// ============================================================================= -// -// Reverse maps from wire opcode to Mnemonic. Dispatch is two-level: -// -// * core opcodes (prefix 0x00): DECODE_MAIN[opcode_byte] -// * 0xFC misc group: DECODE_MISC[sub_opcode] -// -// These mirror ENCODING_TABLE (the single source of truth) entry-for-entry; -// unlisted slots default to .INVALID. Four dispatch arrays cover the four -// opcode spaces: core (DECODE_MAIN), 0xFC misc (DECODE_MISC), 0xFD SIMD -// (DECODE_SIMD), and 0xFE threads/atomics (DECODE_ATOMIC). - -DECODE_MAIN_COUNT :: 256 // (0..=0xD2) -DECODE_MISC_COUNT :: 32 // 0xFC sub-opcodes (0..=17) -DECODE_SIMD_COUNT :: 0x114 // 0xFD sub-opcodes (0..=0x113) -DECODE_ATOMIC_COUNT :: 0x4F // 0xFE sub-opcodes (0..=0x4E) - -@(rodata) -DECODE_MAIN := [DECODE_MAIN_COUNT]Mnemonic{ - 0x00 = .UNREACHABLE, - 0x01 = .NOP, - 0x02 = .BLOCK, - 0x03 = .LOOP, - 0x04 = .IF, - 0x05 = .ELSE, - 0x0B = .END, - 0x0C = .BR, - 0x0D = .BR_IF, - 0x0E = .BR_TABLE, - 0x0F = .RETURN, - 0x10 = .CALL, - 0x11 = .CALL_INDIRECT, - 0x1A = .DROP, - 0x1B = .SELECT, - 0x20 = .LOCAL_GET, - 0x21 = .LOCAL_SET, - 0x22 = .LOCAL_TEE, - 0x23 = .GLOBAL_GET, - 0x24 = .GLOBAL_SET, - 0x28 = .I32_LOAD, - 0x29 = .I64_LOAD, - 0x2A = .F32_LOAD, - 0x2B = .F64_LOAD, - 0x2C = .I32_LOAD8_S, - 0x2D = .I32_LOAD8_U, - 0x2E = .I32_LOAD16_S, - 0x2F = .I32_LOAD16_U, - 0x30 = .I64_LOAD8_S, - 0x31 = .I64_LOAD8_U, - 0x32 = .I64_LOAD16_S, - 0x33 = .I64_LOAD16_U, - 0x34 = .I64_LOAD32_S, - 0x35 = .I64_LOAD32_U, - 0x36 = .I32_STORE, - 0x37 = .I64_STORE, - 0x38 = .F32_STORE, - 0x39 = .F64_STORE, - 0x3A = .I32_STORE8, - 0x3B = .I32_STORE16, - 0x3C = .I64_STORE8, - 0x3D = .I64_STORE16, - 0x3E = .I64_STORE32, - 0x3F = .MEMORY_SIZE, - 0x40 = .MEMORY_GROW, - 0x41 = .I32_CONST, - 0x42 = .I64_CONST, - 0x43 = .F32_CONST, - 0x44 = .F64_CONST, - 0x45 = .I32_EQZ, - 0x46 = .I32_EQ, - 0x47 = .I32_NE, - 0x48 = .I32_LT_S, - 0x49 = .I32_LT_U, - 0x4A = .I32_GT_S, - 0x4B = .I32_GT_U, - 0x4C = .I32_LE_S, - 0x4D = .I32_LE_U, - 0x4E = .I32_GE_S, - 0x4F = .I32_GE_U, - 0x50 = .I64_EQZ, - 0x51 = .I64_EQ, - 0x52 = .I64_NE, - 0x53 = .I64_LT_S, - 0x54 = .I64_LT_U, - 0x55 = .I64_GT_S, - 0x56 = .I64_GT_U, - 0x57 = .I64_LE_S, - 0x58 = .I64_LE_U, - 0x59 = .I64_GE_S, - 0x5A = .I64_GE_U, - 0x5B = .F32_EQ, - 0x5C = .F32_NE, - 0x5D = .F32_LT, - 0x5E = .F32_GT, - 0x5F = .F32_LE, - 0x60 = .F32_GE, - 0x61 = .F64_EQ, - 0x62 = .F64_NE, - 0x63 = .F64_LT, - 0x64 = .F64_GT, - 0x65 = .F64_LE, - 0x66 = .F64_GE, - 0x67 = .I32_CLZ, - 0x68 = .I32_CTZ, - 0x69 = .I32_POPCNT, - 0x6A = .I32_ADD, - 0x6B = .I32_SUB, - 0x6C = .I32_MUL, - 0x6D = .I32_DIV_S, - 0x6E = .I32_DIV_U, - 0x6F = .I32_REM_S, - 0x70 = .I32_REM_U, - 0x71 = .I32_AND, - 0x72 = .I32_OR, - 0x73 = .I32_XOR, - 0x74 = .I32_SHL, - 0x75 = .I32_SHR_S, - 0x76 = .I32_SHR_U, - 0x77 = .I32_ROTL, - 0x78 = .I32_ROTR, - 0x79 = .I64_CLZ, - 0x7A = .I64_CTZ, - 0x7B = .I64_POPCNT, - 0x7C = .I64_ADD, - 0x7D = .I64_SUB, - 0x7E = .I64_MUL, - 0x7F = .I64_DIV_S, - 0x80 = .I64_DIV_U, - 0x81 = .I64_REM_S, - 0x82 = .I64_REM_U, - 0x83 = .I64_AND, - 0x84 = .I64_OR, - 0x85 = .I64_XOR, - 0x86 = .I64_SHL, - 0x87 = .I64_SHR_S, - 0x88 = .I64_SHR_U, - 0x89 = .I64_ROTL, - 0x8A = .I64_ROTR, - 0x8B = .F32_ABS, - 0x8C = .F32_NEG, - 0x8D = .F32_CEIL, - 0x8E = .F32_FLOOR, - 0x8F = .F32_TRUNC, - 0x90 = .F32_NEAREST, - 0x91 = .F32_SQRT, - 0x92 = .F32_ADD, - 0x93 = .F32_SUB, - 0x94 = .F32_MUL, - 0x95 = .F32_DIV, - 0x96 = .F32_MIN, - 0x97 = .F32_MAX, - 0x98 = .F32_COPYSIGN, - 0x99 = .F64_ABS, - 0x9A = .F64_NEG, - 0x9B = .F64_CEIL, - 0x9C = .F64_FLOOR, - 0x9D = .F64_TRUNC, - 0x9E = .F64_NEAREST, - 0x9F = .F64_SQRT, - 0xA0 = .F64_ADD, - 0xA1 = .F64_SUB, - 0xA2 = .F64_MUL, - 0xA3 = .F64_DIV, - 0xA4 = .F64_MIN, - 0xA5 = .F64_MAX, - 0xA6 = .F64_COPYSIGN, - 0xA7 = .I32_WRAP_I64, - 0xA8 = .I32_TRUNC_F32_S, - 0xA9 = .I32_TRUNC_F32_U, - 0xAA = .I32_TRUNC_F64_S, - 0xAB = .I32_TRUNC_F64_U, - 0xAC = .I64_EXTEND_I32_S, - 0xAD = .I64_EXTEND_I32_U, - 0xAE = .I64_TRUNC_F32_S, - 0xAF = .I64_TRUNC_F32_U, - 0xB0 = .I64_TRUNC_F64_S, - 0xB1 = .I64_TRUNC_F64_U, - 0xB2 = .F32_CONVERT_I32_S, - 0xB3 = .F32_CONVERT_I32_U, - 0xB4 = .F32_CONVERT_I64_S, - 0xB5 = .F32_CONVERT_I64_U, - 0xB6 = .F32_DEMOTE_F64, - 0xB7 = .F64_CONVERT_I32_S, - 0xB8 = .F64_CONVERT_I32_U, - 0xB9 = .F64_CONVERT_I64_S, - 0xBA = .F64_CONVERT_I64_U, - 0xBB = .F64_PROMOTE_F32, - 0xBC = .I32_REINTERPRET_F32, - 0xBD = .I64_REINTERPRET_F64, - 0xBE = .F32_REINTERPRET_I32, - 0xBF = .F64_REINTERPRET_I64, - 0xC0 = .I32_EXTEND8_S, - 0xC1 = .I32_EXTEND16_S, - 0xC2 = .I64_EXTEND8_S, - 0xC3 = .I64_EXTEND16_S, - 0xC4 = .I64_EXTEND32_S, - 0xD0 = .REF_NULL, - 0xD1 = .REF_IS_NULL, - 0xD2 = .REF_FUNC, -} - -@(rodata) -DECODE_MISC := [DECODE_MISC_COUNT]Mnemonic{ - 0 = .I32_TRUNC_SAT_F32_S, - 1 = .I32_TRUNC_SAT_F32_U, - 2 = .I32_TRUNC_SAT_F64_S, - 3 = .I32_TRUNC_SAT_F64_U, - 4 = .I64_TRUNC_SAT_F32_S, - 5 = .I64_TRUNC_SAT_F32_U, - 6 = .I64_TRUNC_SAT_F64_S, - 7 = .I64_TRUNC_SAT_F64_U, - 8 = .MEMORY_INIT, - 9 = .DATA_DROP, - 10 = .MEMORY_COPY, - 11 = .MEMORY_FILL, - 12 = .TABLE_INIT, - 13 = .ELEM_DROP, - 14 = .TABLE_COPY, - 15 = .TABLE_GROW, - 16 = .TABLE_SIZE, - 17 = .TABLE_FILL, -} - -@(rodata) -DECODE_SIMD := [DECODE_SIMD_COUNT]Mnemonic{ - 0x00 = .V128_LOAD, - 0x01 = .V128_LOAD8X8_S, - 0x02 = .V128_LOAD8X8_U, - 0x03 = .V128_LOAD16X4_S, - 0x04 = .V128_LOAD16X4_U, - 0x05 = .V128_LOAD32X2_S, - 0x06 = .V128_LOAD32X2_U, - 0x07 = .V128_LOAD8_SPLAT, - 0x08 = .V128_LOAD16_SPLAT, - 0x09 = .V128_LOAD32_SPLAT, - 0x0A = .V128_LOAD64_SPLAT, - 0x0B = .V128_STORE, - 0x0C = .V128_CONST, - 0x0D = .I8X16_SHUFFLE, - 0x0E = .I8X16_SWIZZLE, - 0x0F = .I8X16_SPLAT, - 0x10 = .I16X8_SPLAT, - 0x11 = .I32X4_SPLAT, - 0x12 = .I64X2_SPLAT, - 0x13 = .F32X4_SPLAT, - 0x14 = .F64X2_SPLAT, - 0x15 = .I8X16_EXTRACT_LANE_S, - 0x16 = .I8X16_EXTRACT_LANE_U, - 0x17 = .I8X16_REPLACE_LANE, - 0x18 = .I16X8_EXTRACT_LANE_S, - 0x19 = .I16X8_EXTRACT_LANE_U, - 0x1A = .I16X8_REPLACE_LANE, - 0x1B = .I32X4_EXTRACT_LANE, - 0x1C = .I32X4_REPLACE_LANE, - 0x1D = .I64X2_EXTRACT_LANE, - 0x1E = .I64X2_REPLACE_LANE, - 0x1F = .F32X4_EXTRACT_LANE, - 0x20 = .F32X4_REPLACE_LANE, - 0x21 = .F64X2_EXTRACT_LANE, - 0x22 = .F64X2_REPLACE_LANE, - 0x23 = .I8X16_EQ, - 0x24 = .I8X16_NE, - 0x25 = .I8X16_LT_S, - 0x26 = .I8X16_LT_U, - 0x27 = .I8X16_GT_S, - 0x28 = .I8X16_GT_U, - 0x29 = .I8X16_LE_S, - 0x2A = .I8X16_LE_U, - 0x2B = .I8X16_GE_S, - 0x2C = .I8X16_GE_U, - 0x2D = .I16X8_EQ, - 0x2E = .I16X8_NE, - 0x2F = .I16X8_LT_S, - 0x30 = .I16X8_LT_U, - 0x31 = .I16X8_GT_S, - 0x32 = .I16X8_GT_U, - 0x33 = .I16X8_LE_S, - 0x34 = .I16X8_LE_U, - 0x35 = .I16X8_GE_S, - 0x36 = .I16X8_GE_U, - 0x37 = .I32X4_EQ, - 0x38 = .I32X4_NE, - 0x39 = .I32X4_LT_S, - 0x3A = .I32X4_LT_U, - 0x3B = .I32X4_GT_S, - 0x3C = .I32X4_GT_U, - 0x3D = .I32X4_LE_S, - 0x3E = .I32X4_LE_U, - 0x3F = .I32X4_GE_S, - 0x40 = .I32X4_GE_U, - 0x41 = .F32X4_EQ, - 0x42 = .F32X4_NE, - 0x43 = .F32X4_LT, - 0x44 = .F32X4_GT, - 0x45 = .F32X4_LE, - 0x46 = .F32X4_GE, - 0x47 = .F64X2_EQ, - 0x48 = .F64X2_NE, - 0x49 = .F64X2_LT, - 0x4A = .F64X2_GT, - 0x4B = .F64X2_LE, - 0x4C = .F64X2_GE, - 0x4D = .V128_NOT, - 0x4E = .V128_AND, - 0x4F = .V128_ANDNOT, - 0x50 = .V128_OR, - 0x51 = .V128_XOR, - 0x52 = .V128_BITSELECT, - 0x53 = .V128_ANY_TRUE, - 0x54 = .V128_LOAD8_LANE, - 0x55 = .V128_LOAD16_LANE, - 0x56 = .V128_LOAD32_LANE, - 0x57 = .V128_LOAD64_LANE, - 0x58 = .V128_STORE8_LANE, - 0x59 = .V128_STORE16_LANE, - 0x5A = .V128_STORE32_LANE, - 0x5B = .V128_STORE64_LANE, - 0x5C = .V128_LOAD32_ZERO, - 0x5D = .V128_LOAD64_ZERO, - 0x5E = .F32X4_DEMOTE_F64X2_ZERO, - 0x5F = .F64X2_PROMOTE_LOW_F32X4, - 0x60 = .I8X16_ABS, - 0x61 = .I8X16_NEG, - 0x62 = .I8X16_POPCNT, - 0x63 = .I8X16_ALL_TRUE, - 0x64 = .I8X16_BITMASK, - 0x65 = .I8X16_NARROW_I16X8_S, - 0x66 = .I8X16_NARROW_I16X8_U, - 0x67 = .F32X4_CEIL, - 0x68 = .F32X4_FLOOR, - 0x69 = .F32X4_TRUNC, - 0x6A = .F32X4_NEAREST, - 0x6B = .I8X16_SHL, - 0x6C = .I8X16_SHR_S, - 0x6D = .I8X16_SHR_U, - 0x6E = .I8X16_ADD, - 0x6F = .I8X16_ADD_SAT_S, - 0x70 = .I8X16_ADD_SAT_U, - 0x71 = .I8X16_SUB, - 0x72 = .I8X16_SUB_SAT_S, - 0x73 = .I8X16_SUB_SAT_U, - 0x74 = .F64X2_CEIL, - 0x75 = .F64X2_FLOOR, - 0x76 = .I8X16_MIN_S, - 0x77 = .I8X16_MIN_U, - 0x78 = .I8X16_MAX_S, - 0x79 = .I8X16_MAX_U, - 0x7A = .F64X2_TRUNC, - 0x7B = .I8X16_AVGR_U, - 0x7C = .I16X8_EXTADD_PAIRWISE_I8X16_S, - 0x7D = .I16X8_EXTADD_PAIRWISE_I8X16_U, - 0x7E = .I32X4_EXTADD_PAIRWISE_I16X8_S, - 0x7F = .I32X4_EXTADD_PAIRWISE_I16X8_U, - 0x80 = .I16X8_ABS, - 0x81 = .I16X8_NEG, - 0x82 = .I16X8_Q15MULR_SAT_S, - 0x83 = .I16X8_ALL_TRUE, - 0x84 = .I16X8_BITMASK, - 0x85 = .I16X8_NARROW_I32X4_S, - 0x86 = .I16X8_NARROW_I32X4_U, - 0x87 = .I16X8_EXTEND_LOW_I8X16_S, - 0x88 = .I16X8_EXTEND_HIGH_I8X16_S, - 0x89 = .I16X8_EXTEND_LOW_I8X16_U, - 0x8A = .I16X8_EXTEND_HIGH_I8X16_U, - 0x8B = .I16X8_SHL, - 0x8C = .I16X8_SHR_S, - 0x8D = .I16X8_SHR_U, - 0x8E = .I16X8_ADD, - 0x8F = .I16X8_ADD_SAT_S, - 0x90 = .I16X8_ADD_SAT_U, - 0x91 = .I16X8_SUB, - 0x92 = .I16X8_SUB_SAT_S, - 0x93 = .I16X8_SUB_SAT_U, - 0x94 = .F64X2_NEAREST, - 0x95 = .I16X8_MUL, - 0x96 = .I16X8_MIN_S, - 0x97 = .I16X8_MIN_U, - 0x98 = .I16X8_MAX_S, - 0x99 = .I16X8_MAX_U, - 0x9B = .I16X8_AVGR_U, - 0x9C = .I16X8_EXTMUL_LOW_I8X16_S, - 0x9D = .I16X8_EXTMUL_HIGH_I8X16_S, - 0x9E = .I16X8_EXTMUL_LOW_I8X16_U, - 0x9F = .I16X8_EXTMUL_HIGH_I8X16_U, - 0xA0 = .I32X4_ABS, - 0xA1 = .I32X4_NEG, - 0xA3 = .I32X4_ALL_TRUE, - 0xA4 = .I32X4_BITMASK, - 0xA7 = .I32X4_EXTEND_LOW_I16X8_S, - 0xA8 = .I32X4_EXTEND_HIGH_I16X8_S, - 0xA9 = .I32X4_EXTEND_LOW_I16X8_U, - 0xAA = .I32X4_EXTEND_HIGH_I16X8_U, - 0xAB = .I32X4_SHL, - 0xAC = .I32X4_SHR_S, - 0xAD = .I32X4_SHR_U, - 0xAE = .I32X4_ADD, - 0xB1 = .I32X4_SUB, - 0xB5 = .I32X4_MUL, - 0xB6 = .I32X4_MIN_S, - 0xB7 = .I32X4_MIN_U, - 0xB8 = .I32X4_MAX_S, - 0xB9 = .I32X4_MAX_U, - 0xBA = .I32X4_DOT_I16X8_S, - 0xBC = .I32X4_EXTMUL_LOW_I16X8_S, - 0xBD = .I32X4_EXTMUL_HIGH_I16X8_S, - 0xBE = .I32X4_EXTMUL_LOW_I16X8_U, - 0xBF = .I32X4_EXTMUL_HIGH_I16X8_U, - 0xC0 = .I64X2_ABS, - 0xC1 = .I64X2_NEG, - 0xC3 = .I64X2_ALL_TRUE, - 0xC4 = .I64X2_BITMASK, - 0xC7 = .I64X2_EXTEND_LOW_I32X4_S, - 0xC8 = .I64X2_EXTEND_HIGH_I32X4_S, - 0xC9 = .I64X2_EXTEND_LOW_I32X4_U, - 0xCA = .I64X2_EXTEND_HIGH_I32X4_U, - 0xCB = .I64X2_SHL, - 0xCC = .I64X2_SHR_S, - 0xCD = .I64X2_SHR_U, - 0xCE = .I64X2_ADD, - 0xD1 = .I64X2_SUB, - 0xD5 = .I64X2_MUL, - 0xD6 = .I64X2_EQ, - 0xD7 = .I64X2_NE, - 0xD8 = .I64X2_LT_S, - 0xD9 = .I64X2_GT_S, - 0xDA = .I64X2_LE_S, - 0xDB = .I64X2_GE_S, - 0xDC = .I64X2_EXTMUL_LOW_I32X4_S, - 0xDD = .I64X2_EXTMUL_HIGH_I32X4_S, - 0xDE = .I64X2_EXTMUL_LOW_I32X4_U, - 0xDF = .I64X2_EXTMUL_HIGH_I32X4_U, - 0xE0 = .F32X4_ABS, - 0xE1 = .F32X4_NEG, - 0xE3 = .F32X4_SQRT, - 0xE4 = .F32X4_ADD, - 0xE5 = .F32X4_SUB, - 0xE6 = .F32X4_MUL, - 0xE7 = .F32X4_DIV, - 0xE8 = .F32X4_MIN, - 0xE9 = .F32X4_MAX, - 0xEA = .F32X4_PMIN, - 0xEB = .F32X4_PMAX, - 0xEC = .F64X2_ABS, - 0xED = .F64X2_NEG, - 0xEF = .F64X2_SQRT, - 0xF0 = .F64X2_ADD, - 0xF1 = .F64X2_SUB, - 0xF2 = .F64X2_MUL, - 0xF3 = .F64X2_DIV, - 0xF4 = .F64X2_MIN, - 0xF5 = .F64X2_MAX, - 0xF6 = .F64X2_PMIN, - 0xF7 = .F64X2_PMAX, - 0xF8 = .I32X4_TRUNC_SAT_F32X4_S, - 0xF9 = .I32X4_TRUNC_SAT_F32X4_U, - 0xFA = .F32X4_CONVERT_I32X4_S, - 0xFB = .F32X4_CONVERT_I32X4_U, - 0xFC = .I32X4_TRUNC_SAT_F64X2_S_ZERO, - 0xFD = .I32X4_TRUNC_SAT_F64X2_U_ZERO, - 0xFE = .F64X2_CONVERT_LOW_I32X4_S, - 0xFF = .F64X2_CONVERT_LOW_I32X4_U, - 0x100 = .I8X16_RELAXED_SWIZZLE, - 0x101 = .I32X4_RELAXED_TRUNC_F32X4_S, - 0x102 = .I32X4_RELAXED_TRUNC_F32X4_U, - 0x103 = .I32X4_RELAXED_TRUNC_F64X2_S_ZERO, - 0x104 = .I32X4_RELAXED_TRUNC_F64X2_U_ZERO, - 0x105 = .F32X4_RELAXED_MADD, - 0x106 = .F32X4_RELAXED_NMADD, - 0x107 = .F64X2_RELAXED_MADD, - 0x108 = .F64X2_RELAXED_NMADD, - 0x109 = .I8X16_RELAXED_LANESELECT, - 0x10A = .I16X8_RELAXED_LANESELECT, - 0x10B = .I32X4_RELAXED_LANESELECT, - 0x10C = .I64X2_RELAXED_LANESELECT, - 0x10D = .F32X4_RELAXED_MIN, - 0x10E = .F32X4_RELAXED_MAX, - 0x10F = .F64X2_RELAXED_MIN, - 0x110 = .F64X2_RELAXED_MAX, - 0x111 = .I16X8_RELAXED_Q15MULR_S, - 0x112 = .I16X8_RELAXED_DOT_I8X16_I7X16_S, - 0x113 = .I32X4_RELAXED_DOT_I8X16_I7X16_ADD_S, -} - -@(rodata) -DECODE_ATOMIC := [DECODE_ATOMIC_COUNT]Mnemonic{ - 0x00 = .MEMORY_ATOMIC_NOTIFY, - 0x01 = .MEMORY_ATOMIC_WAIT32, - 0x02 = .MEMORY_ATOMIC_WAIT64, - 0x03 = .ATOMIC_FENCE, - 0x10 = .I32_ATOMIC_LOAD, - 0x11 = .I64_ATOMIC_LOAD, - 0x12 = .I32_ATOMIC_LOAD8_U, - 0x13 = .I32_ATOMIC_LOAD16_U, - 0x14 = .I64_ATOMIC_LOAD8_U, - 0x15 = .I64_ATOMIC_LOAD16_U, - 0x16 = .I64_ATOMIC_LOAD32_U, - 0x17 = .I32_ATOMIC_STORE, - 0x18 = .I64_ATOMIC_STORE, - 0x19 = .I32_ATOMIC_STORE8, - 0x1A = .I32_ATOMIC_STORE16, - 0x1B = .I64_ATOMIC_STORE8, - 0x1C = .I64_ATOMIC_STORE16, - 0x1D = .I64_ATOMIC_STORE32, - 0x1E = .I32_ATOMIC_RMW_ADD, - 0x1F = .I64_ATOMIC_RMW_ADD, - 0x20 = .I32_ATOMIC_RMW8_ADD_U, - 0x21 = .I32_ATOMIC_RMW16_ADD_U, - 0x22 = .I64_ATOMIC_RMW8_ADD_U, - 0x23 = .I64_ATOMIC_RMW16_ADD_U, - 0x24 = .I64_ATOMIC_RMW32_ADD_U, - 0x25 = .I32_ATOMIC_RMW_SUB, - 0x26 = .I64_ATOMIC_RMW_SUB, - 0x27 = .I32_ATOMIC_RMW8_SUB_U, - 0x28 = .I32_ATOMIC_RMW16_SUB_U, - 0x29 = .I64_ATOMIC_RMW8_SUB_U, - 0x2A = .I64_ATOMIC_RMW16_SUB_U, - 0x2B = .I64_ATOMIC_RMW32_SUB_U, - 0x2C = .I32_ATOMIC_RMW_AND, - 0x2D = .I64_ATOMIC_RMW_AND, - 0x2E = .I32_ATOMIC_RMW8_AND_U, - 0x2F = .I32_ATOMIC_RMW16_AND_U, - 0x30 = .I64_ATOMIC_RMW8_AND_U, - 0x31 = .I64_ATOMIC_RMW16_AND_U, - 0x32 = .I64_ATOMIC_RMW32_AND_U, - 0x33 = .I32_ATOMIC_RMW_OR, - 0x34 = .I64_ATOMIC_RMW_OR, - 0x35 = .I32_ATOMIC_RMW8_OR_U, - 0x36 = .I32_ATOMIC_RMW16_OR_U, - 0x37 = .I64_ATOMIC_RMW8_OR_U, - 0x38 = .I64_ATOMIC_RMW16_OR_U, - 0x39 = .I64_ATOMIC_RMW32_OR_U, - 0x3A = .I32_ATOMIC_RMW_XOR, - 0x3B = .I64_ATOMIC_RMW_XOR, - 0x3C = .I32_ATOMIC_RMW8_XOR_U, - 0x3D = .I32_ATOMIC_RMW16_XOR_U, - 0x3E = .I64_ATOMIC_RMW8_XOR_U, - 0x3F = .I64_ATOMIC_RMW16_XOR_U, - 0x40 = .I64_ATOMIC_RMW32_XOR_U, - 0x41 = .I32_ATOMIC_RMW_XCHG, - 0x42 = .I64_ATOMIC_RMW_XCHG, - 0x43 = .I32_ATOMIC_RMW8_XCHG_U, - 0x44 = .I32_ATOMIC_RMW16_XCHG_U, - 0x45 = .I64_ATOMIC_RMW8_XCHG_U, - 0x46 = .I64_ATOMIC_RMW16_XCHG_U, - 0x47 = .I64_ATOMIC_RMW32_XCHG_U, - 0x48 = .I32_ATOMIC_RMW_CMPXCHG, - 0x49 = .I64_ATOMIC_RMW_CMPXCHG, - 0x4A = .I32_ATOMIC_RMW8_CMPXCHG_U, - 0x4B = .I32_ATOMIC_RMW16_CMPXCHG_U, - 0x4C = .I64_ATOMIC_RMW8_CMPXCHG_U, - 0x4D = .I64_ATOMIC_RMW16_CMPXCHG_U, - 0x4E = .I64_ATOMIC_RMW32_CMPXCHG_U, -} diff --git a/core/rexcode/wasm/encoder.odin b/core/rexcode/wasm/encoder.odin deleted file mode 100644 index 37749ae25..000000000 --- a/core/rexcode/wasm/encoder.odin +++ /dev/null @@ -1,212 +0,0 @@ -// rexcode · Brendan Punsky (dotbmp@github), original author -// Ginger Bill (gingerBill@github) - -package rexcode_wasm - -import "core:math/bits" - -// ============================================================================= -// WebAssembly ENCODER -// ============================================================================= -// -// Variable-length, byte-oriented, LEB128-heavy. Because LEB fields are not a -// fixed width, encoding is sequential: a single forward pass writes each -// instruction's opcode (a byte, or a prefix byte plus an unsigned-LEB -// sub-opcode) followed by its immediates, advancing a byte cursor. -// -// WASM has no PC-relative branches (control flow uses structured label -// depths), so there is no second resolution pass and no rewrite of -// `label_defs`: those parameters are part of the universal signature but are -// inert here. Relocations *are* produced -- for symbolic index references -// (see op_label) -- and returned for a linker to patch; symbolic indices are -// laid down as fixed-width 5-byte LEB placeholders so the patched value fits. - -MAX_OPCODE_SIZE :: 3 // prefix byte + two-byte unsigned-LEB sub-opcode (SIMD reaches 0x113) - -@(require_results) -encode_max_code_size :: #force_inline proc "contextless" (n: int) -> int { - // Worst case per instruction without a br_table: a 3-byte opcode plus the - // largest single immediate, which is v128.const's 16 raw bytes (a memarg+ - // lane pair is smaller). br_table is unbounded in its target count; - // callers encoding tables should size from the target totals. - return n * 24 -} -@(require_results) -encode_max_relocation_count :: #force_inline proc "contextless" (n: int) -> int { - return n -} - -encode :: proc( - instructions: []Instruction, - code: []u8, - relocs: ^[dynamic]Relocation, - errors: ^[dynamic]Error, -) -> (byte_count: u32, ok: bool) { - errors_start := u32(len(errors)) - - for &inst, i in instructions { - n := encode_one(&inst, byte_count, u16(i), code, relocs, errors) or_return - inst.length = u8(min(n, 255)) - byte_count += n - } - - ok = u32(len(errors)) == errors_start - return -} - - -encode_one :: proc( - inst: ^Instruction, - pc: u32, - inst_idx: u16, - code: []u8, - relocs: ^[dynamic]Relocation, - errors: ^[dynamic]Error, -) -> (size: u32, ok: bool) { - if inst.mnemonic == .INVALID { - append(errors, Error{inst_idx = u32(inst_idx), code = .INVALID_MNEMONIC}) - return - } - form := encoding_form(inst.mnemonic) - - need := encoded_size(inst, form) - if pc + need > u32(len(code)) { - append(errors, Error{inst_idx = u32(inst_idx), code = .BUFFER_OVERFLOW}) - return - } - - off := pc - - // Opcode (and prefix sub-opcode). - if form.prefix == PREFIX_NONE { - code[off] = u8(form.opcode) - off += 1 - } else { - code[off] = form.prefix - off += 1 - write_uleb(code, &off, u64(form.opcode)) - } - - // Immediates, walked in declaration order with an operand cursor. - opi := 0 - for k in form.imm { - switch k { - case .NONE: - // nothing - case .BLOCKTYPE, .I32, .I64: - write_sleb(code, &off, inst.ops[opi].immediate) - opi += 1 - case .F32: - write_u32_block(code, &off, u32(inst.ops[opi].immediate)) - opi += 1 - case .F64: - write_u64_block(code, &off, u64(inst.ops[opi].immediate)) - opi += 1 - case .IDX: - op := &inst.ops[opi] - if op.flags.symbolic { - append(relocs, Relocation{ - offset = off, label_id = op.index, addend = 0, - type = reloc_type_for(op.idx_kind), size = 5, inst_idx = inst_idx, - }) - write_uleb_padded5(code, &off, u64(op.index)) - } else { - write_uleb(code, &off, u64(op.index)) - } - opi += 1 - case .MEMARG: - ma := inst.ops[opi].memarg - // TODO(bill): is this correct because the spec says otherwise but the binary formats look like it's log2 - align := bits.log2(u64(ma.align)) - - write_uleb(code, &off, align) - write_uleb(code, &off, u64(ma.offset)) - opi += 1 - case .REFTYPE: - code[off] = u8(inst.ops[opi].immediate) - off += 1 - opi += 1 - case .BR_TABLE: - write_uleb(code, &off, u64(len(inst.targets))) - for t in inst.targets { - write_uleb(code, &off, u64(t)) - } - write_uleb(code, &off, u64(inst.ops[opi].index)) // default depth - opi += 1 - case .ZERO_BYTE: - code[off] = 0x00 - off += 1 - case .LANE: - code[off] = u8(inst.ops[opi].immediate) - off += 1 - opi += 1 - case .LANES16: - for bb in inst.bytes { - code[off] = bb - off += 1 - } - } - } - - return off - pc, true -} - -@(private="file") -encoded_size :: proc(inst: ^Instruction, form: ^Encoding) -> u32 { - size: u32 = 1 - if form.prefix != PREFIX_NONE { - size += uleb_size(u64(form.opcode)) - } - opi := 0 - for k in form.imm { - switch k { - case .NONE: - case .BLOCKTYPE, .I32, .I64: - size += sleb_size(inst.ops[opi].immediate) - opi += 1 - case .F32: - size += 4 - opi += 1 - case .F64: - size += 8 - opi += 1 - case .IDX: - op := &inst.ops[opi] - size += op.flags.symbolic ? 5 : uleb_size(u64(op.index)) - opi += 1 - case .MEMARG: - ma := inst.ops[opi].memarg - size += uleb_size(u64(ma.align)) + uleb_size(u64(ma.offset)) - opi += 1 - case .REFTYPE: - size += 1 - opi += 1 - case .BR_TABLE: - size += uleb_size(u64(len(inst.targets))) - for t in inst.targets { - size += uleb_size(u64(t)) - } - size += uleb_size(u64(inst.ops[opi].index)) - opi += 1 - case .ZERO_BYTE: - size += 1 - case .LANE: - size += 1 - opi += 1 - case .LANES16: - size += 16 - } - } - return size -} - -@(private="file") -reloc_type_for :: #force_inline proc "contextless" (k: Index_Kind) -> Relocation_Type { - #partial switch k { - case .FUNC: return .FUNCTION_INDEX_LEB - case .TYPE: return .TYPE_INDEX_LEB - case .GLOBAL: return .GLOBAL_INDEX_LEB - case .TABLE: return .TABLE_NUMBER_LEB - } - return .FUNCTION_INDEX_LEB -} diff --git a/core/rexcode/wasm/encoding_table.odin b/core/rexcode/wasm/encoding_table.odin deleted file mode 100644 index 690079210..000000000 --- a/core/rexcode/wasm/encoding_table.odin +++ /dev/null @@ -1,505 +0,0 @@ -// rexcode · Brendan Punsky (dotbmp@github), original author -// Ginger Bill (gingerBill@github) - -package rexcode_wasm - -// ============================================================================= -// WebAssembly ENCODING TABLE (single source of truth) -// ============================================================================= -// -// One form per mnemonic, indexed directly by the Mnemonic enum. Each entry -// records the prefix byte, the (sub-)opcode, and the immediate layout. The -// decode dispatch in decoding_tables.odin is derived from this table at -// package init, so opcode bytes are written down exactly once. -// -// The `mnemonic` field of each Encoding is left at INVALID here: the table -// index already identifies the mnemonic and the encoder never reads it back. - -@(private="file") CTRL :: Encoding_Flags{control = true} -@(private="file") MEM :: Encoding_Flags{memory = true} - -@(rodata) -ENCODING_TABLE := [Mnemonic]Encoding{ - .INVALID = {inputs = 0, outputs = 0}, - - // ------------------------------------------------------------------ control - .UNREACHABLE = {prefix = PREFIX_NONE, opcode = 0x00, flags = CTRL, inputs = 0, outputs = 0}, - .NOP = {prefix = PREFIX_NONE, opcode = 0x01, inputs = 0, outputs = 0}, - .BLOCK = {prefix = PREFIX_NONE, opcode = 0x02, imm = {.BLOCKTYPE, .NONE}, flags = CTRL, inputs = -1, outputs = -1}, - .LOOP = {prefix = PREFIX_NONE, opcode = 0x03, imm = {.BLOCKTYPE, .NONE}, flags = CTRL, inputs = -1, outputs = -1}, - .IF = {prefix = PREFIX_NONE, opcode = 0x04, imm = {.BLOCKTYPE, .NONE}, flags = CTRL, inputs = -1, outputs = -1}, - .ELSE = {prefix = PREFIX_NONE, opcode = 0x05, flags = CTRL, inputs = -1, outputs = -1}, - .END = {prefix = PREFIX_NONE, opcode = 0x0B, flags = CTRL, inputs = -1, outputs = -1}, - .BR = {prefix = PREFIX_NONE, opcode = 0x0C, imm = {.IDX, .NONE}, flags = CTRL, inputs = 0, outputs = 0}, - .BR_IF = {prefix = PREFIX_NONE, opcode = 0x0D, imm = {.IDX, .NONE}, flags = CTRL, inputs = 1, outputs = 0}, - .BR_TABLE = {prefix = PREFIX_NONE, opcode = 0x0E, imm = {.BR_TABLE, .NONE}, flags = CTRL, inputs = 1, outputs = 0}, - .RETURN = {prefix = PREFIX_NONE, opcode = 0x0F, flags = CTRL, inputs = -1, outputs = -1}, - .CALL = {prefix = PREFIX_NONE, opcode = 0x10, imm = {.IDX, .NONE}, flags = CTRL, inputs = -1, outputs = -1}, - .CALL_INDIRECT = {prefix = PREFIX_NONE, opcode = 0x11, imm = {.IDX, .NONE}, flags = CTRL, inputs = -1, outputs = -1}, - .RETURN_CALL = {prefix = PREFIX_NONE, opcode = 0x12, imm = {.IDX, .NONE}, flags = CTRL, inputs = -1, outputs = -1}, - .RETURN_CALL_INDIRECT = {prefix = PREFIX_NONE, opcode = 0x13, imm = {.IDX, .NONE}, flags = CTRL, inputs = -1, outputs = -1}, - - - // -------------------------------------------------------------- parametric - .DROP = {prefix = PREFIX_NONE, opcode = 0x1A, inputs = 1, outputs = 0}, - .SELECT = {prefix = PREFIX_NONE, opcode = 0x1B, inputs = 3, outputs = 1}, - - // ---------------------------------------------------------------- variable - .LOCAL_GET = {prefix = PREFIX_NONE, opcode = 0x20, imm = {.IDX, .NONE}, inputs = 0, outputs = 1}, - .LOCAL_SET = {prefix = PREFIX_NONE, opcode = 0x21, imm = {.IDX, .NONE}, inputs = 1, outputs = 0}, - .LOCAL_TEE = {prefix = PREFIX_NONE, opcode = 0x22, imm = {.IDX, .NONE}, inputs = 1, outputs = 1}, - .GLOBAL_GET = {prefix = PREFIX_NONE, opcode = 0x23, imm = {.IDX, .NONE}, inputs = 0, outputs = 1}, - .GLOBAL_SET = {prefix = PREFIX_NONE, opcode = 0x24, imm = {.IDX, .NONE}, inputs = 1, outputs = 0}, - - // ------------------------------------------------------------------- memory - .I32_LOAD = {prefix = PREFIX_NONE, opcode = 0x28, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I64_LOAD = {prefix = PREFIX_NONE, opcode = 0x29, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .F32_LOAD = {prefix = PREFIX_NONE, opcode = 0x2A, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .F64_LOAD = {prefix = PREFIX_NONE, opcode = 0x2B, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I32_LOAD8_S = {prefix = PREFIX_NONE, opcode = 0x2C, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I32_LOAD8_U = {prefix = PREFIX_NONE, opcode = 0x2D, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I32_LOAD16_S = {prefix = PREFIX_NONE, opcode = 0x2E, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I32_LOAD16_U = {prefix = PREFIX_NONE, opcode = 0x2F, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I64_LOAD8_S = {prefix = PREFIX_NONE, opcode = 0x30, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I64_LOAD8_U = {prefix = PREFIX_NONE, opcode = 0x31, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I64_LOAD16_S = {prefix = PREFIX_NONE, opcode = 0x32, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I64_LOAD16_U = {prefix = PREFIX_NONE, opcode = 0x33, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I64_LOAD32_S = {prefix = PREFIX_NONE, opcode = 0x34, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I64_LOAD32_U = {prefix = PREFIX_NONE, opcode = 0x35, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I32_STORE = {prefix = PREFIX_NONE, opcode = 0x36, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 0}, - .I64_STORE = {prefix = PREFIX_NONE, opcode = 0x37, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 0}, - .F32_STORE = {prefix = PREFIX_NONE, opcode = 0x38, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 0}, - .F64_STORE = {prefix = PREFIX_NONE, opcode = 0x39, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 0}, - .I32_STORE8 = {prefix = PREFIX_NONE, opcode = 0x3A, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 0}, - .I32_STORE16 = {prefix = PREFIX_NONE, opcode = 0x3B, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 0}, - .I64_STORE8 = {prefix = PREFIX_NONE, opcode = 0x3C, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 0}, - .I64_STORE16 = {prefix = PREFIX_NONE, opcode = 0x3D, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 0}, - .I64_STORE32 = {prefix = PREFIX_NONE, opcode = 0x3E, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 0}, - .MEMORY_SIZE = {prefix = PREFIX_NONE, opcode = 0x3F, imm = {.ZERO_BYTE, .NONE}, flags = MEM, inputs = 0, outputs = 1}, - .MEMORY_GROW = {prefix = PREFIX_NONE, opcode = 0x40, imm = {.ZERO_BYTE, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - - // ----------------------------------------------------------------- numeric - .I32_CONST = {prefix = PREFIX_NONE, opcode = 0x41, imm = {.I32, .NONE}, inputs = 0, outputs = 1}, - .I64_CONST = {prefix = PREFIX_NONE, opcode = 0x42, imm = {.I64, .NONE}, inputs = 0, outputs = 1}, - .F32_CONST = {prefix = PREFIX_NONE, opcode = 0x43, imm = {.F32, .NONE}, inputs = 0, outputs = 1}, - .F64_CONST = {prefix = PREFIX_NONE, opcode = 0x44, imm = {.F64, .NONE}, inputs = 0, outputs = 1}, - - .I32_EQZ = {prefix = PREFIX_NONE, opcode = 0x45, inputs = 1, outputs = 1}, .I32_EQ = {prefix = PREFIX_NONE, opcode = 0x46, inputs = 2, outputs = 1}, .I32_NE = {prefix = PREFIX_NONE, opcode = 0x47, inputs = 2, outputs = 1}, - .I32_LT_S = {prefix = PREFIX_NONE, opcode = 0x48, inputs = 2, outputs = 1}, .I32_LT_U = {prefix = PREFIX_NONE, opcode = 0x49, inputs = 2, outputs = 1}, - .I32_GT_S = {prefix = PREFIX_NONE, opcode = 0x4A, inputs = 2, outputs = 1}, .I32_GT_U = {prefix = PREFIX_NONE, opcode = 0x4B, inputs = 2, outputs = 1}, - .I32_LE_S = {prefix = PREFIX_NONE, opcode = 0x4C, inputs = 2, outputs = 1}, .I32_LE_U = {prefix = PREFIX_NONE, opcode = 0x4D, inputs = 2, outputs = 1}, - .I32_GE_S = {prefix = PREFIX_NONE, opcode = 0x4E, inputs = 2, outputs = 1}, .I32_GE_U = {prefix = PREFIX_NONE, opcode = 0x4F, inputs = 2, outputs = 1}, - - .I64_EQZ = {prefix = PREFIX_NONE, opcode = 0x50, inputs = 1, outputs = 1}, .I64_EQ = {prefix = PREFIX_NONE, opcode = 0x51, inputs = 2, outputs = 1}, .I64_NE = {prefix = PREFIX_NONE, opcode = 0x52, inputs = 2, outputs = 1}, - .I64_LT_S = {prefix = PREFIX_NONE, opcode = 0x53, inputs = 2, outputs = 1}, .I64_LT_U = {prefix = PREFIX_NONE, opcode = 0x54, inputs = 2, outputs = 1}, - .I64_GT_S = {prefix = PREFIX_NONE, opcode = 0x55, inputs = 2, outputs = 1}, .I64_GT_U = {prefix = PREFIX_NONE, opcode = 0x56, inputs = 2, outputs = 1}, - .I64_LE_S = {prefix = PREFIX_NONE, opcode = 0x57, inputs = 2, outputs = 1}, .I64_LE_U = {prefix = PREFIX_NONE, opcode = 0x58, inputs = 2, outputs = 1}, - .I64_GE_S = {prefix = PREFIX_NONE, opcode = 0x59, inputs = 2, outputs = 1}, .I64_GE_U = {prefix = PREFIX_NONE, opcode = 0x5A, inputs = 2, outputs = 1}, - - .F32_EQ = {prefix = PREFIX_NONE, opcode = 0x5B, inputs = 2, outputs = 1}, .F32_NE = {prefix = PREFIX_NONE, opcode = 0x5C, inputs = 2, outputs = 1}, - .F32_LT = {prefix = PREFIX_NONE, opcode = 0x5D, inputs = 2, outputs = 1}, .F32_GT = {prefix = PREFIX_NONE, opcode = 0x5E, inputs = 2, outputs = 1}, - .F32_LE = {prefix = PREFIX_NONE, opcode = 0x5F, inputs = 2, outputs = 1}, .F32_GE = {prefix = PREFIX_NONE, opcode = 0x60, inputs = 2, outputs = 1}, - - .F64_EQ = {prefix = PREFIX_NONE, opcode = 0x61, inputs = 2, outputs = 1}, .F64_NE = {prefix = PREFIX_NONE, opcode = 0x62, inputs = 2, outputs = 1}, - .F64_LT = {prefix = PREFIX_NONE, opcode = 0x63, inputs = 2, outputs = 1}, .F64_GT = {prefix = PREFIX_NONE, opcode = 0x64, inputs = 2, outputs = 1}, - .F64_LE = {prefix = PREFIX_NONE, opcode = 0x65, inputs = 2, outputs = 1}, .F64_GE = {prefix = PREFIX_NONE, opcode = 0x66, inputs = 2, outputs = 1}, - - .I32_CLZ = {prefix = PREFIX_NONE, opcode = 0x67, inputs = 1, outputs = 1}, .I32_CTZ = {prefix = PREFIX_NONE, opcode = 0x68, inputs = 1, outputs = 1}, .I32_POPCNT = {prefix = PREFIX_NONE, opcode = 0x69, inputs = 1, outputs = 1}, - .I32_ADD = {prefix = PREFIX_NONE, opcode = 0x6A, inputs = 2, outputs = 1}, .I32_SUB = {prefix = PREFIX_NONE, opcode = 0x6B, inputs = 2, outputs = 1}, .I32_MUL = {prefix = PREFIX_NONE, opcode = 0x6C, inputs = 2, outputs = 1}, - .I32_DIV_S = {prefix = PREFIX_NONE, opcode = 0x6D, inputs = 2, outputs = 1}, .I32_DIV_U = {prefix = PREFIX_NONE, opcode = 0x6E, inputs = 2, outputs = 1}, - .I32_REM_S = {prefix = PREFIX_NONE, opcode = 0x6F, inputs = 2, outputs = 1}, .I32_REM_U = {prefix = PREFIX_NONE, opcode = 0x70, inputs = 2, outputs = 1}, - .I32_AND = {prefix = PREFIX_NONE, opcode = 0x71, inputs = 2, outputs = 1}, .I32_OR = {prefix = PREFIX_NONE, opcode = 0x72, inputs = 2, outputs = 1}, .I32_XOR = {prefix = PREFIX_NONE, opcode = 0x73, inputs = 2, outputs = 1}, - .I32_SHL = {prefix = PREFIX_NONE, opcode = 0x74, inputs = 2, outputs = 1}, .I32_SHR_S = {prefix = PREFIX_NONE, opcode = 0x75, inputs = 2, outputs = 1}, .I32_SHR_U = {prefix = PREFIX_NONE, opcode = 0x76, inputs = 2, outputs = 1}, - .I32_ROTL = {prefix = PREFIX_NONE, opcode = 0x77, inputs = 2, outputs = 1}, .I32_ROTR = {prefix = PREFIX_NONE, opcode = 0x78, inputs = 2, outputs = 1}, - - .I64_CLZ = {prefix = PREFIX_NONE, opcode = 0x79, inputs = 1, outputs = 1}, .I64_CTZ = {prefix = PREFIX_NONE, opcode = 0x7A, inputs = 1, outputs = 1}, .I64_POPCNT = {prefix = PREFIX_NONE, opcode = 0x7B, inputs = 1, outputs = 1}, - .I64_ADD = {prefix = PREFIX_NONE, opcode = 0x7C, inputs = 2, outputs = 1}, .I64_SUB = {prefix = PREFIX_NONE, opcode = 0x7D, inputs = 2, outputs = 1}, .I64_MUL = {prefix = PREFIX_NONE, opcode = 0x7E, inputs = 2, outputs = 1}, - .I64_DIV_S = {prefix = PREFIX_NONE, opcode = 0x7F, inputs = 2, outputs = 1}, .I64_DIV_U = {prefix = PREFIX_NONE, opcode = 0x80, inputs = 2, outputs = 1}, - .I64_REM_S = {prefix = PREFIX_NONE, opcode = 0x81, inputs = 2, outputs = 1}, .I64_REM_U = {prefix = PREFIX_NONE, opcode = 0x82, inputs = 2, outputs = 1}, - .I64_AND = {prefix = PREFIX_NONE, opcode = 0x83, inputs = 2, outputs = 1}, .I64_OR = {prefix = PREFIX_NONE, opcode = 0x84, inputs = 2, outputs = 1}, .I64_XOR = {prefix = PREFIX_NONE, opcode = 0x85, inputs = 2, outputs = 1}, - .I64_SHL = {prefix = PREFIX_NONE, opcode = 0x86, inputs = 2, outputs = 1}, .I64_SHR_S = {prefix = PREFIX_NONE, opcode = 0x87, inputs = 2, outputs = 1}, .I64_SHR_U = {prefix = PREFIX_NONE, opcode = 0x88, inputs = 2, outputs = 1}, - .I64_ROTL = {prefix = PREFIX_NONE, opcode = 0x89, inputs = 2, outputs = 1}, .I64_ROTR = {prefix = PREFIX_NONE, opcode = 0x8A, inputs = 2, outputs = 1}, - - .F32_ABS = {prefix = PREFIX_NONE, opcode = 0x8B, inputs = 1, outputs = 1}, .F32_NEG = {prefix = PREFIX_NONE, opcode = 0x8C, inputs = 1, outputs = 1}, .F32_CEIL = {prefix = PREFIX_NONE, opcode = 0x8D, inputs = 1, outputs = 1}, - .F32_FLOOR = {prefix = PREFIX_NONE, opcode = 0x8E, inputs = 1, outputs = 1}, .F32_TRUNC = {prefix = PREFIX_NONE, opcode = 0x8F, inputs = 1, outputs = 1}, .F32_NEAREST = {prefix = PREFIX_NONE, opcode = 0x90, inputs = 1, outputs = 1}, - .F32_SQRT = {prefix = PREFIX_NONE, opcode = 0x91, inputs = 1, outputs = 1}, .F32_ADD = {prefix = PREFIX_NONE, opcode = 0x92, inputs = 2, outputs = 1}, .F32_SUB = {prefix = PREFIX_NONE, opcode = 0x93, inputs = 2, outputs = 1}, - .F32_MUL = {prefix = PREFIX_NONE, opcode = 0x94, inputs = 2, outputs = 1}, .F32_DIV = {prefix = PREFIX_NONE, opcode = 0x95, inputs = 2, outputs = 1}, .F32_MIN = {prefix = PREFIX_NONE, opcode = 0x96, inputs = 2, outputs = 1}, - .F32_MAX = {prefix = PREFIX_NONE, opcode = 0x97, inputs = 2, outputs = 1}, .F32_COPYSIGN = {prefix = PREFIX_NONE, opcode = 0x98, inputs = 2, outputs = 1}, - - .F64_ABS = {prefix = PREFIX_NONE, opcode = 0x99, inputs = 1, outputs = 1}, .F64_NEG = {prefix = PREFIX_NONE, opcode = 0x9A, inputs = 1, outputs = 1}, .F64_CEIL = {prefix = PREFIX_NONE, opcode = 0x9B, inputs = 1, outputs = 1}, - .F64_FLOOR = {prefix = PREFIX_NONE, opcode = 0x9C, inputs = 1, outputs = 1}, .F64_TRUNC = {prefix = PREFIX_NONE, opcode = 0x9D, inputs = 1, outputs = 1}, .F64_NEAREST = {prefix = PREFIX_NONE, opcode = 0x9E, inputs = 1, outputs = 1}, - .F64_SQRT = {prefix = PREFIX_NONE, opcode = 0x9F, inputs = 1, outputs = 1}, .F64_ADD = {prefix = PREFIX_NONE, opcode = 0xA0, inputs = 2, outputs = 1}, .F64_SUB = {prefix = PREFIX_NONE, opcode = 0xA1, inputs = 2, outputs = 1}, - .F64_MUL = {prefix = PREFIX_NONE, opcode = 0xA2, inputs = 2, outputs = 1}, .F64_DIV = {prefix = PREFIX_NONE, opcode = 0xA3, inputs = 2, outputs = 1}, .F64_MIN = {prefix = PREFIX_NONE, opcode = 0xA4, inputs = 2, outputs = 1}, - .F64_MAX = {prefix = PREFIX_NONE, opcode = 0xA5, inputs = 2, outputs = 1}, .F64_COPYSIGN = {prefix = PREFIX_NONE, opcode = 0xA6, inputs = 2, outputs = 1}, - - .I32_WRAP_I64 = {prefix = PREFIX_NONE, opcode = 0xA7, inputs = 1, outputs = 1}, - .I32_TRUNC_F32_S = {prefix = PREFIX_NONE, opcode = 0xA8, inputs = 1, outputs = 1}, .I32_TRUNC_F32_U = {prefix = PREFIX_NONE, opcode = 0xA9, inputs = 1, outputs = 1}, - .I32_TRUNC_F64_S = {prefix = PREFIX_NONE, opcode = 0xAA, inputs = 1, outputs = 1}, .I32_TRUNC_F64_U = {prefix = PREFIX_NONE, opcode = 0xAB, inputs = 1, outputs = 1}, - .I64_EXTEND_I32_S = {prefix = PREFIX_NONE, opcode = 0xAC, inputs = 1, outputs = 1}, .I64_EXTEND_I32_U = {prefix = PREFIX_NONE, opcode = 0xAD, inputs = 1, outputs = 1}, - .I64_TRUNC_F32_S = {prefix = PREFIX_NONE, opcode = 0xAE, inputs = 1, outputs = 1}, .I64_TRUNC_F32_U = {prefix = PREFIX_NONE, opcode = 0xAF, inputs = 1, outputs = 1}, - .I64_TRUNC_F64_S = {prefix = PREFIX_NONE, opcode = 0xB0, inputs = 1, outputs = 1}, .I64_TRUNC_F64_U = {prefix = PREFIX_NONE, opcode = 0xB1, inputs = 1, outputs = 1}, - .F32_CONVERT_I32_S = {prefix = PREFIX_NONE, opcode = 0xB2, inputs = 1, outputs = 1}, .F32_CONVERT_I32_U = {prefix = PREFIX_NONE, opcode = 0xB3, inputs = 1, outputs = 1}, - .F32_CONVERT_I64_S = {prefix = PREFIX_NONE, opcode = 0xB4, inputs = 1, outputs = 1}, .F32_CONVERT_I64_U = {prefix = PREFIX_NONE, opcode = 0xB5, inputs = 1, outputs = 1}, - .F32_DEMOTE_F64 = {prefix = PREFIX_NONE, opcode = 0xB6, inputs = 1, outputs = 1}, - .F64_CONVERT_I32_S = {prefix = PREFIX_NONE, opcode = 0xB7, inputs = 1, outputs = 1}, .F64_CONVERT_I32_U = {prefix = PREFIX_NONE, opcode = 0xB8, inputs = 1, outputs = 1}, - .F64_CONVERT_I64_S = {prefix = PREFIX_NONE, opcode = 0xB9, inputs = 1, outputs = 1}, .F64_CONVERT_I64_U = {prefix = PREFIX_NONE, opcode = 0xBA, inputs = 1, outputs = 1}, - .F64_PROMOTE_F32 = {prefix = PREFIX_NONE, opcode = 0xBB, inputs = 1, outputs = 1}, - .I32_REINTERPRET_F32 = {prefix = PREFIX_NONE, opcode = 0xBC, inputs = 1, outputs = 1}, .I64_REINTERPRET_F64 = {prefix = PREFIX_NONE, opcode = 0xBD, inputs = 1, outputs = 1}, - .F32_REINTERPRET_I32 = {prefix = PREFIX_NONE, opcode = 0xBE, inputs = 1, outputs = 1}, .F64_REINTERPRET_I64 = {prefix = PREFIX_NONE, opcode = 0xBF, inputs = 1, outputs = 1}, - - .I32_EXTEND8_S = {prefix = PREFIX_NONE, opcode = 0xC0, inputs = 1, outputs = 1}, .I32_EXTEND16_S = {prefix = PREFIX_NONE, opcode = 0xC1, inputs = 1, outputs = 1}, - .I64_EXTEND8_S = {prefix = PREFIX_NONE, opcode = 0xC2, inputs = 1, outputs = 1}, .I64_EXTEND16_S = {prefix = PREFIX_NONE, opcode = 0xC3, inputs = 1, outputs = 1}, .I64_EXTEND32_S = {prefix = PREFIX_NONE, opcode = 0xC4, inputs = 1, outputs = 1}, - - .REF_NULL = {prefix = PREFIX_NONE, opcode = 0xD0, imm = {.REFTYPE, .NONE}, inputs = 0, outputs = 1}, - .REF_IS_NULL = {prefix = PREFIX_NONE, opcode = 0xD1, inputs = 1, outputs = 1}, - .REF_FUNC = {prefix = PREFIX_NONE, opcode = 0xD2, imm = {.IDX, .NONE}, inputs = 0, outputs = 1}, - - // ------------------------------------------------------- 0xFC misc prefix - .I32_TRUNC_SAT_F32_S = {prefix = PREFIX_MISC, opcode = 0, inputs = 1, outputs = 1}, .I32_TRUNC_SAT_F32_U = {prefix = PREFIX_MISC, opcode = 1, inputs = 1, outputs = 1}, - .I32_TRUNC_SAT_F64_S = {prefix = PREFIX_MISC, opcode = 2, inputs = 1, outputs = 1}, .I32_TRUNC_SAT_F64_U = {prefix = PREFIX_MISC, opcode = 3, inputs = 1, outputs = 1}, - .I64_TRUNC_SAT_F32_S = {prefix = PREFIX_MISC, opcode = 4, inputs = 1, outputs = 1}, .I64_TRUNC_SAT_F32_U = {prefix = PREFIX_MISC, opcode = 5, inputs = 1, outputs = 1}, - .I64_TRUNC_SAT_F64_S = {prefix = PREFIX_MISC, opcode = 6, inputs = 1, outputs = 1}, .I64_TRUNC_SAT_F64_U = {prefix = PREFIX_MISC, opcode = 7, inputs = 1, outputs = 1}, - .MEMORY_INIT = {prefix = PREFIX_MISC, opcode = 8, imm = {.IDX, .ZERO_BYTE}, flags = MEM, inputs = 3, outputs = 0}, - .DATA_DROP = {prefix = PREFIX_MISC, opcode = 9, imm = {.IDX, .NONE}, inputs = 0, outputs = 0}, - .MEMORY_COPY = {prefix = PREFIX_MISC, opcode = 10, imm = {.ZERO_BYTE, .ZERO_BYTE}, flags = MEM, inputs = 3, outputs = 0}, - .MEMORY_FILL = {prefix = PREFIX_MISC, opcode = 11, imm = {.ZERO_BYTE, .NONE}, flags = MEM, inputs = 3, outputs = 0}, - .TABLE_INIT = {prefix = PREFIX_MISC, opcode = 12, imm = {.IDX, .IDX}, inputs = 3, outputs = 0}, - .ELEM_DROP = {prefix = PREFIX_MISC, opcode = 13, imm = {.IDX, .NONE}, inputs = 0, outputs = 0}, - .TABLE_COPY = {prefix = PREFIX_MISC, opcode = 14, imm = {.IDX, .IDX}, inputs = 3, outputs = 0}, - .TABLE_GROW = {prefix = PREFIX_MISC, opcode = 15, imm = {.IDX, .NONE}, inputs = 2, outputs = 1}, - .TABLE_SIZE = {prefix = PREFIX_MISC, opcode = 16, imm = {.IDX, .NONE}, inputs = 0, outputs = 1}, - .TABLE_FILL = {prefix = PREFIX_MISC, opcode = 17, imm = {.IDX, .NONE}, inputs = 3, outputs = 0}, - - // ----------------------------------------------- 0xFD SIMD (v128) prefix - .V128_LOAD = {prefix = PREFIX_SIMD, opcode = 0x00, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .V128_LOAD8X8_S = {prefix = PREFIX_SIMD, opcode = 0x01, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .V128_LOAD8X8_U = {prefix = PREFIX_SIMD, opcode = 0x02, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .V128_LOAD16X4_S = {prefix = PREFIX_SIMD, opcode = 0x03, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .V128_LOAD16X4_U = {prefix = PREFIX_SIMD, opcode = 0x04, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .V128_LOAD32X2_S = {prefix = PREFIX_SIMD, opcode = 0x05, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .V128_LOAD32X2_U = {prefix = PREFIX_SIMD, opcode = 0x06, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .V128_LOAD8_SPLAT = {prefix = PREFIX_SIMD, opcode = 0x07, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .V128_LOAD16_SPLAT = {prefix = PREFIX_SIMD, opcode = 0x08, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .V128_LOAD32_SPLAT = {prefix = PREFIX_SIMD, opcode = 0x09, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .V128_LOAD64_SPLAT = {prefix = PREFIX_SIMD, opcode = 0x0A, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .V128_STORE = {prefix = PREFIX_SIMD, opcode = 0x0B, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 0}, - .V128_CONST = {prefix = PREFIX_SIMD, opcode = 0x0C, imm = {.LANES16, .NONE}, inputs = 0, outputs = 1}, - .I8X16_SHUFFLE = {prefix = PREFIX_SIMD, opcode = 0x0D, imm = {.LANES16, .NONE}, inputs = 2, outputs = 1}, - .I8X16_SWIZZLE = {prefix = PREFIX_SIMD, opcode = 0x0E, inputs = 2, outputs = 1}, - .I8X16_SPLAT = {prefix = PREFIX_SIMD, opcode = 0x0F, inputs = 1, outputs = 1}, - .I16X8_SPLAT = {prefix = PREFIX_SIMD, opcode = 0x10, inputs = 1, outputs = 1}, - .I32X4_SPLAT = {prefix = PREFIX_SIMD, opcode = 0x11, inputs = 1, outputs = 1}, - .I64X2_SPLAT = {prefix = PREFIX_SIMD, opcode = 0x12, inputs = 1, outputs = 1}, - .F32X4_SPLAT = {prefix = PREFIX_SIMD, opcode = 0x13, inputs = 1, outputs = 1}, - .F64X2_SPLAT = {prefix = PREFIX_SIMD, opcode = 0x14, inputs = 1, outputs = 1}, - .I8X16_EXTRACT_LANE_S = {prefix = PREFIX_SIMD, opcode = 0x15, imm = {.LANE, .NONE}, inputs = 1, outputs = 1}, - .I8X16_EXTRACT_LANE_U = {prefix = PREFIX_SIMD, opcode = 0x16, imm = {.LANE, .NONE}, inputs = 1, outputs = 1}, - .I8X16_REPLACE_LANE = {prefix = PREFIX_SIMD, opcode = 0x17, imm = {.LANE, .NONE}, inputs = 2, outputs = 1}, - .I16X8_EXTRACT_LANE_S = {prefix = PREFIX_SIMD, opcode = 0x18, imm = {.LANE, .NONE}, inputs = 1, outputs = 1}, - .I16X8_EXTRACT_LANE_U = {prefix = PREFIX_SIMD, opcode = 0x19, imm = {.LANE, .NONE}, inputs = 1, outputs = 1}, - .I16X8_REPLACE_LANE = {prefix = PREFIX_SIMD, opcode = 0x1A, imm = {.LANE, .NONE}, inputs = 2, outputs = 1}, - .I32X4_EXTRACT_LANE = {prefix = PREFIX_SIMD, opcode = 0x1B, imm = {.LANE, .NONE}, inputs = 1, outputs = 1}, - .I32X4_REPLACE_LANE = {prefix = PREFIX_SIMD, opcode = 0x1C, imm = {.LANE, .NONE}, inputs = 2, outputs = 1}, - .I64X2_EXTRACT_LANE = {prefix = PREFIX_SIMD, opcode = 0x1D, imm = {.LANE, .NONE}, inputs = 1, outputs = 1}, - .I64X2_REPLACE_LANE = {prefix = PREFIX_SIMD, opcode = 0x1E, imm = {.LANE, .NONE}, inputs = 2, outputs = 1}, - .F32X4_EXTRACT_LANE = {prefix = PREFIX_SIMD, opcode = 0x1F, imm = {.LANE, .NONE}, inputs = 1, outputs = 1}, - .F32X4_REPLACE_LANE = {prefix = PREFIX_SIMD, opcode = 0x20, imm = {.LANE, .NONE}, inputs = 2, outputs = 1}, - .F64X2_EXTRACT_LANE = {prefix = PREFIX_SIMD, opcode = 0x21, imm = {.LANE, .NONE}, inputs = 1, outputs = 1}, - .F64X2_REPLACE_LANE = {prefix = PREFIX_SIMD, opcode = 0x22, imm = {.LANE, .NONE}, inputs = 2, outputs = 1}, - .I8X16_EQ = {prefix = PREFIX_SIMD, opcode = 0x23, inputs = 2, outputs = 1}, - .I8X16_NE = {prefix = PREFIX_SIMD, opcode = 0x24, inputs = 2, outputs = 1}, - .I8X16_LT_S = {prefix = PREFIX_SIMD, opcode = 0x25, inputs = 2, outputs = 1}, - .I8X16_LT_U = {prefix = PREFIX_SIMD, opcode = 0x26, inputs = 2, outputs = 1}, - .I8X16_GT_S = {prefix = PREFIX_SIMD, opcode = 0x27, inputs = 2, outputs = 1}, - .I8X16_GT_U = {prefix = PREFIX_SIMD, opcode = 0x28, inputs = 2, outputs = 1}, - .I8X16_LE_S = {prefix = PREFIX_SIMD, opcode = 0x29, inputs = 2, outputs = 1}, - .I8X16_LE_U = {prefix = PREFIX_SIMD, opcode = 0x2A, inputs = 2, outputs = 1}, - .I8X16_GE_S = {prefix = PREFIX_SIMD, opcode = 0x2B, inputs = 2, outputs = 1}, - .I8X16_GE_U = {prefix = PREFIX_SIMD, opcode = 0x2C, inputs = 2, outputs = 1}, - .I16X8_EQ = {prefix = PREFIX_SIMD, opcode = 0x2D, inputs = 2, outputs = 1}, - .I16X8_NE = {prefix = PREFIX_SIMD, opcode = 0x2E, inputs = 2, outputs = 1}, - .I16X8_LT_S = {prefix = PREFIX_SIMD, opcode = 0x2F, inputs = 2, outputs = 1}, - .I16X8_LT_U = {prefix = PREFIX_SIMD, opcode = 0x30, inputs = 2, outputs = 1}, - .I16X8_GT_S = {prefix = PREFIX_SIMD, opcode = 0x31, inputs = 2, outputs = 1}, - .I16X8_GT_U = {prefix = PREFIX_SIMD, opcode = 0x32, inputs = 2, outputs = 1}, - .I16X8_LE_S = {prefix = PREFIX_SIMD, opcode = 0x33, inputs = 2, outputs = 1}, - .I16X8_LE_U = {prefix = PREFIX_SIMD, opcode = 0x34, inputs = 2, outputs = 1}, - .I16X8_GE_S = {prefix = PREFIX_SIMD, opcode = 0x35, inputs = 2, outputs = 1}, - .I16X8_GE_U = {prefix = PREFIX_SIMD, opcode = 0x36, inputs = 2, outputs = 1}, - .I32X4_EQ = {prefix = PREFIX_SIMD, opcode = 0x37, inputs = 2, outputs = 1}, - .I32X4_NE = {prefix = PREFIX_SIMD, opcode = 0x38, inputs = 2, outputs = 1}, - .I32X4_LT_S = {prefix = PREFIX_SIMD, opcode = 0x39, inputs = 2, outputs = 1}, - .I32X4_LT_U = {prefix = PREFIX_SIMD, opcode = 0x3A, inputs = 2, outputs = 1}, - .I32X4_GT_S = {prefix = PREFIX_SIMD, opcode = 0x3B, inputs = 2, outputs = 1}, - .I32X4_GT_U = {prefix = PREFIX_SIMD, opcode = 0x3C, inputs = 2, outputs = 1}, - .I32X4_LE_S = {prefix = PREFIX_SIMD, opcode = 0x3D, inputs = 2, outputs = 1}, - .I32X4_LE_U = {prefix = PREFIX_SIMD, opcode = 0x3E, inputs = 2, outputs = 1}, - .I32X4_GE_S = {prefix = PREFIX_SIMD, opcode = 0x3F, inputs = 2, outputs = 1}, - .I32X4_GE_U = {prefix = PREFIX_SIMD, opcode = 0x40, inputs = 2, outputs = 1}, - .F32X4_EQ = {prefix = PREFIX_SIMD, opcode = 0x41, inputs = 2, outputs = 1}, - .F32X4_NE = {prefix = PREFIX_SIMD, opcode = 0x42, inputs = 2, outputs = 1}, - .F32X4_LT = {prefix = PREFIX_SIMD, opcode = 0x43, inputs = 2, outputs = 1}, - .F32X4_GT = {prefix = PREFIX_SIMD, opcode = 0x44, inputs = 2, outputs = 1}, - .F32X4_LE = {prefix = PREFIX_SIMD, opcode = 0x45, inputs = 2, outputs = 1}, - .F32X4_GE = {prefix = PREFIX_SIMD, opcode = 0x46, inputs = 2, outputs = 1}, - .F64X2_EQ = {prefix = PREFIX_SIMD, opcode = 0x47, inputs = 2, outputs = 1}, - .F64X2_NE = {prefix = PREFIX_SIMD, opcode = 0x48, inputs = 2, outputs = 1}, - .F64X2_LT = {prefix = PREFIX_SIMD, opcode = 0x49, inputs = 2, outputs = 1}, - .F64X2_GT = {prefix = PREFIX_SIMD, opcode = 0x4A, inputs = 2, outputs = 1}, - .F64X2_LE = {prefix = PREFIX_SIMD, opcode = 0x4B, inputs = 2, outputs = 1}, - .F64X2_GE = {prefix = PREFIX_SIMD, opcode = 0x4C, inputs = 2, outputs = 1}, - .V128_NOT = {prefix = PREFIX_SIMD, opcode = 0x4D, inputs = 1, outputs = 1}, - .V128_AND = {prefix = PREFIX_SIMD, opcode = 0x4E, inputs = 2, outputs = 1}, - .V128_ANDNOT = {prefix = PREFIX_SIMD, opcode = 0x4F, inputs = 2, outputs = 1}, - .V128_OR = {prefix = PREFIX_SIMD, opcode = 0x50, inputs = 2, outputs = 1}, - .V128_XOR = {prefix = PREFIX_SIMD, opcode = 0x51, inputs = 2, outputs = 1}, - .V128_BITSELECT = {prefix = PREFIX_SIMD, opcode = 0x52, inputs = 3, outputs = 1}, - .V128_ANY_TRUE = {prefix = PREFIX_SIMD, opcode = 0x53, inputs = 1, outputs = 1}, - .V128_LOAD8_LANE = {prefix = PREFIX_SIMD, opcode = 0x54, imm = {.MEMARG, .LANE}, flags = MEM, inputs = 2, outputs = 1}, - .V128_LOAD16_LANE = {prefix = PREFIX_SIMD, opcode = 0x55, imm = {.MEMARG, .LANE}, flags = MEM, inputs = 2, outputs = 1}, - .V128_LOAD32_LANE = {prefix = PREFIX_SIMD, opcode = 0x56, imm = {.MEMARG, .LANE}, flags = MEM, inputs = 2, outputs = 1}, - .V128_LOAD64_LANE = {prefix = PREFIX_SIMD, opcode = 0x57, imm = {.MEMARG, .LANE}, flags = MEM, inputs = 2, outputs = 1}, - .V128_STORE8_LANE = {prefix = PREFIX_SIMD, opcode = 0x58, imm = {.MEMARG, .LANE}, flags = MEM, inputs = 2, outputs = 0}, - .V128_STORE16_LANE = {prefix = PREFIX_SIMD, opcode = 0x59, imm = {.MEMARG, .LANE}, flags = MEM, inputs = 2, outputs = 0}, - .V128_STORE32_LANE = {prefix = PREFIX_SIMD, opcode = 0x5A, imm = {.MEMARG, .LANE}, flags = MEM, inputs = 2, outputs = 0}, - .V128_STORE64_LANE = {prefix = PREFIX_SIMD, opcode = 0x5B, imm = {.MEMARG, .LANE}, flags = MEM, inputs = 2, outputs = 0}, - .V128_LOAD32_ZERO = {prefix = PREFIX_SIMD, opcode = 0x5C, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .V128_LOAD64_ZERO = {prefix = PREFIX_SIMD, opcode = 0x5D, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .F32X4_DEMOTE_F64X2_ZERO = {prefix = PREFIX_SIMD, opcode = 0x5E, inputs = 1, outputs = 1}, - .F64X2_PROMOTE_LOW_F32X4 = {prefix = PREFIX_SIMD, opcode = 0x5F, inputs = 1, outputs = 1}, - .I8X16_ABS = {prefix = PREFIX_SIMD, opcode = 0x60, inputs = 1, outputs = 1}, - .I8X16_NEG = {prefix = PREFIX_SIMD, opcode = 0x61, inputs = 1, outputs = 1}, - .I8X16_POPCNT = {prefix = PREFIX_SIMD, opcode = 0x62, inputs = 1, outputs = 1}, - .I8X16_ALL_TRUE = {prefix = PREFIX_SIMD, opcode = 0x63, inputs = 1, outputs = 1}, - .I8X16_BITMASK = {prefix = PREFIX_SIMD, opcode = 0x64, inputs = 1, outputs = 1}, - .I8X16_NARROW_I16X8_S = {prefix = PREFIX_SIMD, opcode = 0x65, inputs = 2, outputs = 1}, - .I8X16_NARROW_I16X8_U = {prefix = PREFIX_SIMD, opcode = 0x66, inputs = 2, outputs = 1}, - .F32X4_CEIL = {prefix = PREFIX_SIMD, opcode = 0x67, inputs = 1, outputs = 1}, - .F32X4_FLOOR = {prefix = PREFIX_SIMD, opcode = 0x68, inputs = 1, outputs = 1}, - .F32X4_TRUNC = {prefix = PREFIX_SIMD, opcode = 0x69, inputs = 1, outputs = 1}, - .F32X4_NEAREST = {prefix = PREFIX_SIMD, opcode = 0x6A, inputs = 1, outputs = 1}, - .I8X16_SHL = {prefix = PREFIX_SIMD, opcode = 0x6B, inputs = 2, outputs = 1}, - .I8X16_SHR_S = {prefix = PREFIX_SIMD, opcode = 0x6C, inputs = 2, outputs = 1}, - .I8X16_SHR_U = {prefix = PREFIX_SIMD, opcode = 0x6D, inputs = 2, outputs = 1}, - .I8X16_ADD = {prefix = PREFIX_SIMD, opcode = 0x6E, inputs = 2, outputs = 1}, - .I8X16_ADD_SAT_S = {prefix = PREFIX_SIMD, opcode = 0x6F, inputs = 2, outputs = 1}, - .I8X16_ADD_SAT_U = {prefix = PREFIX_SIMD, opcode = 0x70, inputs = 2, outputs = 1}, - .I8X16_SUB = {prefix = PREFIX_SIMD, opcode = 0x71, inputs = 2, outputs = 1}, - .I8X16_SUB_SAT_S = {prefix = PREFIX_SIMD, opcode = 0x72, inputs = 2, outputs = 1}, - .I8X16_SUB_SAT_U = {prefix = PREFIX_SIMD, opcode = 0x73, inputs = 2, outputs = 1}, - .F64X2_CEIL = {prefix = PREFIX_SIMD, opcode = 0x74, inputs = 1, outputs = 1}, - .F64X2_FLOOR = {prefix = PREFIX_SIMD, opcode = 0x75, inputs = 1, outputs = 1}, - .I8X16_MIN_S = {prefix = PREFIX_SIMD, opcode = 0x76, inputs = 2, outputs = 1}, - .I8X16_MIN_U = {prefix = PREFIX_SIMD, opcode = 0x77, inputs = 2, outputs = 1}, - .I8X16_MAX_S = {prefix = PREFIX_SIMD, opcode = 0x78, inputs = 2, outputs = 1}, - .I8X16_MAX_U = {prefix = PREFIX_SIMD, opcode = 0x79, inputs = 2, outputs = 1}, - .F64X2_TRUNC = {prefix = PREFIX_SIMD, opcode = 0x7A, inputs = 1, outputs = 1}, - .I8X16_AVGR_U = {prefix = PREFIX_SIMD, opcode = 0x7B, inputs = 2, outputs = 1}, - .I16X8_EXTADD_PAIRWISE_I8X16_S = {prefix = PREFIX_SIMD, opcode = 0x7C, inputs = 1, outputs = 1}, - .I16X8_EXTADD_PAIRWISE_I8X16_U = {prefix = PREFIX_SIMD, opcode = 0x7D, inputs = 1, outputs = 1}, - .I32X4_EXTADD_PAIRWISE_I16X8_S = {prefix = PREFIX_SIMD, opcode = 0x7E, inputs = 1, outputs = 1}, - .I32X4_EXTADD_PAIRWISE_I16X8_U = {prefix = PREFIX_SIMD, opcode = 0x7F, inputs = 1, outputs = 1}, - .I16X8_ABS = {prefix = PREFIX_SIMD, opcode = 0x80, inputs = 1, outputs = 1}, - .I16X8_NEG = {prefix = PREFIX_SIMD, opcode = 0x81, inputs = 1, outputs = 1}, - .I16X8_Q15MULR_SAT_S = {prefix = PREFIX_SIMD, opcode = 0x82, inputs = 2, outputs = 1}, - .I16X8_ALL_TRUE = {prefix = PREFIX_SIMD, opcode = 0x83, inputs = 1, outputs = 1}, - .I16X8_BITMASK = {prefix = PREFIX_SIMD, opcode = 0x84, inputs = 1, outputs = 1}, - .I16X8_NARROW_I32X4_S = {prefix = PREFIX_SIMD, opcode = 0x85, inputs = 2, outputs = 1}, - .I16X8_NARROW_I32X4_U = {prefix = PREFIX_SIMD, opcode = 0x86, inputs = 2, outputs = 1}, - .I16X8_EXTEND_LOW_I8X16_S = {prefix = PREFIX_SIMD, opcode = 0x87, inputs = 1, outputs = 1}, - .I16X8_EXTEND_HIGH_I8X16_S = {prefix = PREFIX_SIMD, opcode = 0x88, inputs = 1, outputs = 1}, - .I16X8_EXTEND_LOW_I8X16_U = {prefix = PREFIX_SIMD, opcode = 0x89, inputs = 1, outputs = 1}, - .I16X8_EXTEND_HIGH_I8X16_U = {prefix = PREFIX_SIMD, opcode = 0x8A, inputs = 1, outputs = 1}, - .I16X8_SHL = {prefix = PREFIX_SIMD, opcode = 0x8B, inputs = 2, outputs = 1}, - .I16X8_SHR_S = {prefix = PREFIX_SIMD, opcode = 0x8C, inputs = 2, outputs = 1}, - .I16X8_SHR_U = {prefix = PREFIX_SIMD, opcode = 0x8D, inputs = 2, outputs = 1}, - .I16X8_ADD = {prefix = PREFIX_SIMD, opcode = 0x8E, inputs = 2, outputs = 1}, - .I16X8_ADD_SAT_S = {prefix = PREFIX_SIMD, opcode = 0x8F, inputs = 2, outputs = 1}, - .I16X8_ADD_SAT_U = {prefix = PREFIX_SIMD, opcode = 0x90, inputs = 2, outputs = 1}, - .I16X8_SUB = {prefix = PREFIX_SIMD, opcode = 0x91, inputs = 2, outputs = 1}, - .I16X8_SUB_SAT_S = {prefix = PREFIX_SIMD, opcode = 0x92, inputs = 2, outputs = 1}, - .I16X8_SUB_SAT_U = {prefix = PREFIX_SIMD, opcode = 0x93, inputs = 2, outputs = 1}, - .F64X2_NEAREST = {prefix = PREFIX_SIMD, opcode = 0x94, inputs = 1, outputs = 1}, - .I16X8_MUL = {prefix = PREFIX_SIMD, opcode = 0x95, inputs = 2, outputs = 1}, - .I16X8_MIN_S = {prefix = PREFIX_SIMD, opcode = 0x96, inputs = 2, outputs = 1}, - .I16X8_MIN_U = {prefix = PREFIX_SIMD, opcode = 0x97, inputs = 2, outputs = 1}, - .I16X8_MAX_S = {prefix = PREFIX_SIMD, opcode = 0x98, inputs = 2, outputs = 1}, - .I16X8_MAX_U = {prefix = PREFIX_SIMD, opcode = 0x99, inputs = 2, outputs = 1}, - .I16X8_AVGR_U = {prefix = PREFIX_SIMD, opcode = 0x9B, inputs = 2, outputs = 1}, - .I16X8_EXTMUL_LOW_I8X16_S = {prefix = PREFIX_SIMD, opcode = 0x9C, inputs = 2, outputs = 1}, - .I16X8_EXTMUL_HIGH_I8X16_S = {prefix = PREFIX_SIMD, opcode = 0x9D, inputs = 2, outputs = 1}, - .I16X8_EXTMUL_LOW_I8X16_U = {prefix = PREFIX_SIMD, opcode = 0x9E, inputs = 2, outputs = 1}, - .I16X8_EXTMUL_HIGH_I8X16_U = {prefix = PREFIX_SIMD, opcode = 0x9F, inputs = 2, outputs = 1}, - .I32X4_ABS = {prefix = PREFIX_SIMD, opcode = 0xA0, inputs = 1, outputs = 1}, - .I32X4_NEG = {prefix = PREFIX_SIMD, opcode = 0xA1, inputs = 1, outputs = 1}, - .I32X4_ALL_TRUE = {prefix = PREFIX_SIMD, opcode = 0xA3, inputs = 1, outputs = 1}, - .I32X4_BITMASK = {prefix = PREFIX_SIMD, opcode = 0xA4, inputs = 1, outputs = 1}, - .I32X4_EXTEND_LOW_I16X8_S = {prefix = PREFIX_SIMD, opcode = 0xA7, inputs = 1, outputs = 1}, - .I32X4_EXTEND_HIGH_I16X8_S = {prefix = PREFIX_SIMD, opcode = 0xA8, inputs = 1, outputs = 1}, - .I32X4_EXTEND_LOW_I16X8_U = {prefix = PREFIX_SIMD, opcode = 0xA9, inputs = 1, outputs = 1}, - .I32X4_EXTEND_HIGH_I16X8_U = {prefix = PREFIX_SIMD, opcode = 0xAA, inputs = 1, outputs = 1}, - .I32X4_SHL = {prefix = PREFIX_SIMD, opcode = 0xAB, inputs = 2, outputs = 1}, - .I32X4_SHR_S = {prefix = PREFIX_SIMD, opcode = 0xAC, inputs = 2, outputs = 1}, - .I32X4_SHR_U = {prefix = PREFIX_SIMD, opcode = 0xAD, inputs = 2, outputs = 1}, - .I32X4_ADD = {prefix = PREFIX_SIMD, opcode = 0xAE, inputs = 2, outputs = 1}, - .I32X4_SUB = {prefix = PREFIX_SIMD, opcode = 0xB1, inputs = 2, outputs = 1}, - .I32X4_MUL = {prefix = PREFIX_SIMD, opcode = 0xB5, inputs = 2, outputs = 1}, - .I32X4_MIN_S = {prefix = PREFIX_SIMD, opcode = 0xB6, inputs = 2, outputs = 1}, - .I32X4_MIN_U = {prefix = PREFIX_SIMD, opcode = 0xB7, inputs = 2, outputs = 1}, - .I32X4_MAX_S = {prefix = PREFIX_SIMD, opcode = 0xB8, inputs = 2, outputs = 1}, - .I32X4_MAX_U = {prefix = PREFIX_SIMD, opcode = 0xB9, inputs = 2, outputs = 1}, - .I32X4_DOT_I16X8_S = {prefix = PREFIX_SIMD, opcode = 0xBA, inputs = 2, outputs = 1}, - .I32X4_EXTMUL_LOW_I16X8_S = {prefix = PREFIX_SIMD, opcode = 0xBC, inputs = 2, outputs = 1}, - .I32X4_EXTMUL_HIGH_I16X8_S = {prefix = PREFIX_SIMD, opcode = 0xBD, inputs = 2, outputs = 1}, - .I32X4_EXTMUL_LOW_I16X8_U = {prefix = PREFIX_SIMD, opcode = 0xBE, inputs = 2, outputs = 1}, - .I32X4_EXTMUL_HIGH_I16X8_U = {prefix = PREFIX_SIMD, opcode = 0xBF, inputs = 2, outputs = 1}, - .I64X2_ABS = {prefix = PREFIX_SIMD, opcode = 0xC0, inputs = 1, outputs = 1}, - .I64X2_NEG = {prefix = PREFIX_SIMD, opcode = 0xC1, inputs = 1, outputs = 1}, - .I64X2_ALL_TRUE = {prefix = PREFIX_SIMD, opcode = 0xC3, inputs = 1, outputs = 1}, - .I64X2_BITMASK = {prefix = PREFIX_SIMD, opcode = 0xC4, inputs = 1, outputs = 1}, - .I64X2_EXTEND_LOW_I32X4_S = {prefix = PREFIX_SIMD, opcode = 0xC7, inputs = 1, outputs = 1}, - .I64X2_EXTEND_HIGH_I32X4_S = {prefix = PREFIX_SIMD, opcode = 0xC8, inputs = 1, outputs = 1}, - .I64X2_EXTEND_LOW_I32X4_U = {prefix = PREFIX_SIMD, opcode = 0xC9, inputs = 1, outputs = 1}, - .I64X2_EXTEND_HIGH_I32X4_U = {prefix = PREFIX_SIMD, opcode = 0xCA, inputs = 1, outputs = 1}, - .I64X2_SHL = {prefix = PREFIX_SIMD, opcode = 0xCB, inputs = 2, outputs = 1}, - .I64X2_SHR_S = {prefix = PREFIX_SIMD, opcode = 0xCC, inputs = 2, outputs = 1}, - .I64X2_SHR_U = {prefix = PREFIX_SIMD, opcode = 0xCD, inputs = 2, outputs = 1}, - .I64X2_ADD = {prefix = PREFIX_SIMD, opcode = 0xCE, inputs = 2, outputs = 1}, - .I64X2_SUB = {prefix = PREFIX_SIMD, opcode = 0xD1, inputs = 2, outputs = 1}, - .I64X2_MUL = {prefix = PREFIX_SIMD, opcode = 0xD5, inputs = 2, outputs = 1}, - .I64X2_EQ = {prefix = PREFIX_SIMD, opcode = 0xD6, inputs = 2, outputs = 1}, - .I64X2_NE = {prefix = PREFIX_SIMD, opcode = 0xD7, inputs = 2, outputs = 1}, - .I64X2_LT_S = {prefix = PREFIX_SIMD, opcode = 0xD8, inputs = 2, outputs = 1}, - .I64X2_GT_S = {prefix = PREFIX_SIMD, opcode = 0xD9, inputs = 2, outputs = 1}, - .I64X2_LE_S = {prefix = PREFIX_SIMD, opcode = 0xDA, inputs = 2, outputs = 1}, - .I64X2_GE_S = {prefix = PREFIX_SIMD, opcode = 0xDB, inputs = 2, outputs = 1}, - .I64X2_EXTMUL_LOW_I32X4_S = {prefix = PREFIX_SIMD, opcode = 0xDC, inputs = 2, outputs = 1}, - .I64X2_EXTMUL_HIGH_I32X4_S = {prefix = PREFIX_SIMD, opcode = 0xDD, inputs = 2, outputs = 1}, - .I64X2_EXTMUL_LOW_I32X4_U = {prefix = PREFIX_SIMD, opcode = 0xDE, inputs = 2, outputs = 1}, - .I64X2_EXTMUL_HIGH_I32X4_U = {prefix = PREFIX_SIMD, opcode = 0xDF, inputs = 2, outputs = 1}, - .F32X4_ABS = {prefix = PREFIX_SIMD, opcode = 0xE0, inputs = 1, outputs = 1}, - .F32X4_NEG = {prefix = PREFIX_SIMD, opcode = 0xE1, inputs = 1, outputs = 1}, - .F32X4_SQRT = {prefix = PREFIX_SIMD, opcode = 0xE3, inputs = 1, outputs = 1}, - .F32X4_ADD = {prefix = PREFIX_SIMD, opcode = 0xE4, inputs = 2, outputs = 1}, - .F32X4_SUB = {prefix = PREFIX_SIMD, opcode = 0xE5, inputs = 2, outputs = 1}, - .F32X4_MUL = {prefix = PREFIX_SIMD, opcode = 0xE6, inputs = 2, outputs = 1}, - .F32X4_DIV = {prefix = PREFIX_SIMD, opcode = 0xE7, inputs = 2, outputs = 1}, - .F32X4_MIN = {prefix = PREFIX_SIMD, opcode = 0xE8, inputs = 2, outputs = 1}, - .F32X4_MAX = {prefix = PREFIX_SIMD, opcode = 0xE9, inputs = 2, outputs = 1}, - .F32X4_PMIN = {prefix = PREFIX_SIMD, opcode = 0xEA, inputs = 2, outputs = 1}, - .F32X4_PMAX = {prefix = PREFIX_SIMD, opcode = 0xEB, inputs = 2, outputs = 1}, - .F64X2_ABS = {prefix = PREFIX_SIMD, opcode = 0xEC, inputs = 1, outputs = 1}, - .F64X2_NEG = {prefix = PREFIX_SIMD, opcode = 0xED, inputs = 1, outputs = 1}, - .F64X2_SQRT = {prefix = PREFIX_SIMD, opcode = 0xEF, inputs = 1, outputs = 1}, - .F64X2_ADD = {prefix = PREFIX_SIMD, opcode = 0xF0, inputs = 2, outputs = 1}, - .F64X2_SUB = {prefix = PREFIX_SIMD, opcode = 0xF1, inputs = 2, outputs = 1}, - .F64X2_MUL = {prefix = PREFIX_SIMD, opcode = 0xF2, inputs = 2, outputs = 1}, - .F64X2_DIV = {prefix = PREFIX_SIMD, opcode = 0xF3, inputs = 2, outputs = 1}, - .F64X2_MIN = {prefix = PREFIX_SIMD, opcode = 0xF4, inputs = 2, outputs = 1}, - .F64X2_MAX = {prefix = PREFIX_SIMD, opcode = 0xF5, inputs = 2, outputs = 1}, - .F64X2_PMIN = {prefix = PREFIX_SIMD, opcode = 0xF6, inputs = 2, outputs = 1}, - .F64X2_PMAX = {prefix = PREFIX_SIMD, opcode = 0xF7, inputs = 2, outputs = 1}, - .I32X4_TRUNC_SAT_F32X4_S = {prefix = PREFIX_SIMD, opcode = 0xF8, inputs = 1, outputs = 1}, - .I32X4_TRUNC_SAT_F32X4_U = {prefix = PREFIX_SIMD, opcode = 0xF9, inputs = 1, outputs = 1}, - .F32X4_CONVERT_I32X4_S = {prefix = PREFIX_SIMD, opcode = 0xFA, inputs = 1, outputs = 1}, - .F32X4_CONVERT_I32X4_U = {prefix = PREFIX_SIMD, opcode = 0xFB, inputs = 1, outputs = 1}, - .I32X4_TRUNC_SAT_F64X2_S_ZERO = {prefix = PREFIX_SIMD, opcode = 0xFC, inputs = 1, outputs = 1}, - .I32X4_TRUNC_SAT_F64X2_U_ZERO = {prefix = PREFIX_SIMD, opcode = 0xFD, inputs = 1, outputs = 1}, - .F64X2_CONVERT_LOW_I32X4_S = {prefix = PREFIX_SIMD, opcode = 0xFE, inputs = 1, outputs = 1}, - .F64X2_CONVERT_LOW_I32X4_U = {prefix = PREFIX_SIMD, opcode = 0xFF, inputs = 1, outputs = 1}, - .I8X16_RELAXED_SWIZZLE = {prefix = PREFIX_SIMD, opcode = 0x100, inputs = 2, outputs = 1}, - .I32X4_RELAXED_TRUNC_F32X4_S = {prefix = PREFIX_SIMD, opcode = 0x101, inputs = 1, outputs = 1}, - .I32X4_RELAXED_TRUNC_F32X4_U = {prefix = PREFIX_SIMD, opcode = 0x102, inputs = 1, outputs = 1}, - .I32X4_RELAXED_TRUNC_F64X2_S_ZERO = {prefix = PREFIX_SIMD, opcode = 0x103, inputs = 1, outputs = 1}, - .I32X4_RELAXED_TRUNC_F64X2_U_ZERO = {prefix = PREFIX_SIMD, opcode = 0x104, inputs = 1, outputs = 1}, - .F32X4_RELAXED_MADD = {prefix = PREFIX_SIMD, opcode = 0x105, inputs = 3, outputs = 1}, - .F32X4_RELAXED_NMADD = {prefix = PREFIX_SIMD, opcode = 0x106, inputs = 3, outputs = 1}, - .F64X2_RELAXED_MADD = {prefix = PREFIX_SIMD, opcode = 0x107, inputs = 3, outputs = 1}, - .F64X2_RELAXED_NMADD = {prefix = PREFIX_SIMD, opcode = 0x108, inputs = 3, outputs = 1}, - .I8X16_RELAXED_LANESELECT = {prefix = PREFIX_SIMD, opcode = 0x109, inputs = 3, outputs = 1}, - .I16X8_RELAXED_LANESELECT = {prefix = PREFIX_SIMD, opcode = 0x10A, inputs = 3, outputs = 1}, - .I32X4_RELAXED_LANESELECT = {prefix = PREFIX_SIMD, opcode = 0x10B, inputs = 3, outputs = 1}, - .I64X2_RELAXED_LANESELECT = {prefix = PREFIX_SIMD, opcode = 0x10C, inputs = 3, outputs = 1}, - .F32X4_RELAXED_MIN = {prefix = PREFIX_SIMD, opcode = 0x10D, inputs = 2, outputs = 1}, - .F32X4_RELAXED_MAX = {prefix = PREFIX_SIMD, opcode = 0x10E, inputs = 2, outputs = 1}, - .F64X2_RELAXED_MIN = {prefix = PREFIX_SIMD, opcode = 0x10F, inputs = 2, outputs = 1}, - .F64X2_RELAXED_MAX = {prefix = PREFIX_SIMD, opcode = 0x110, inputs = 2, outputs = 1}, - .I16X8_RELAXED_Q15MULR_S = {prefix = PREFIX_SIMD, opcode = 0x111, inputs = 2, outputs = 1}, - .I16X8_RELAXED_DOT_I8X16_I7X16_S = {prefix = PREFIX_SIMD, opcode = 0x112, inputs = 2, outputs = 1}, - .I32X4_RELAXED_DOT_I8X16_I7X16_ADD_S = {prefix = PREFIX_SIMD, opcode = 0x113, inputs = 3, outputs = 1}, - - // ------------------------------------------ 0xFE threads / atomics prefix - .MEMORY_ATOMIC_NOTIFY = {prefix = PREFIX_ATOM, opcode = 0x00, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .MEMORY_ATOMIC_WAIT32 = {prefix = PREFIX_ATOM, opcode = 0x01, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 3, outputs = 1}, - .MEMORY_ATOMIC_WAIT64 = {prefix = PREFIX_ATOM, opcode = 0x02, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 3, outputs = 1}, - .ATOMIC_FENCE = {prefix = PREFIX_ATOM, opcode = 0x03, imm = {.ZERO_BYTE, .NONE}, inputs = 0, outputs = 0}, - .I32_ATOMIC_LOAD = {prefix = PREFIX_ATOM, opcode = 0x10, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I64_ATOMIC_LOAD = {prefix = PREFIX_ATOM, opcode = 0x11, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I32_ATOMIC_LOAD8_U = {prefix = PREFIX_ATOM, opcode = 0x12, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I32_ATOMIC_LOAD16_U = {prefix = PREFIX_ATOM, opcode = 0x13, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I64_ATOMIC_LOAD8_U = {prefix = PREFIX_ATOM, opcode = 0x14, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I64_ATOMIC_LOAD16_U = {prefix = PREFIX_ATOM, opcode = 0x15, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I64_ATOMIC_LOAD32_U = {prefix = PREFIX_ATOM, opcode = 0x16, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 1, outputs = 1}, - .I32_ATOMIC_STORE = {prefix = PREFIX_ATOM, opcode = 0x17, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 0}, - .I64_ATOMIC_STORE = {prefix = PREFIX_ATOM, opcode = 0x18, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 0}, - .I32_ATOMIC_STORE8 = {prefix = PREFIX_ATOM, opcode = 0x19, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 0}, - .I32_ATOMIC_STORE16 = {prefix = PREFIX_ATOM, opcode = 0x1A, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 0}, - .I64_ATOMIC_STORE8 = {prefix = PREFIX_ATOM, opcode = 0x1B, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 0}, - .I64_ATOMIC_STORE16 = {prefix = PREFIX_ATOM, opcode = 0x1C, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 0}, - .I64_ATOMIC_STORE32 = {prefix = PREFIX_ATOM, opcode = 0x1D, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 0}, - .I32_ATOMIC_RMW_ADD = {prefix = PREFIX_ATOM, opcode = 0x1E, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW_ADD = {prefix = PREFIX_ATOM, opcode = 0x1F, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I32_ATOMIC_RMW8_ADD_U = {prefix = PREFIX_ATOM, opcode = 0x20, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I32_ATOMIC_RMW16_ADD_U = {prefix = PREFIX_ATOM, opcode = 0x21, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW8_ADD_U = {prefix = PREFIX_ATOM, opcode = 0x22, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW16_ADD_U = {prefix = PREFIX_ATOM, opcode = 0x23, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW32_ADD_U = {prefix = PREFIX_ATOM, opcode = 0x24, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I32_ATOMIC_RMW_SUB = {prefix = PREFIX_ATOM, opcode = 0x25, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW_SUB = {prefix = PREFIX_ATOM, opcode = 0x26, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I32_ATOMIC_RMW8_SUB_U = {prefix = PREFIX_ATOM, opcode = 0x27, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I32_ATOMIC_RMW16_SUB_U = {prefix = PREFIX_ATOM, opcode = 0x28, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW8_SUB_U = {prefix = PREFIX_ATOM, opcode = 0x29, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW16_SUB_U = {prefix = PREFIX_ATOM, opcode = 0x2A, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW32_SUB_U = {prefix = PREFIX_ATOM, opcode = 0x2B, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I32_ATOMIC_RMW_AND = {prefix = PREFIX_ATOM, opcode = 0x2C, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW_AND = {prefix = PREFIX_ATOM, opcode = 0x2D, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I32_ATOMIC_RMW8_AND_U = {prefix = PREFIX_ATOM, opcode = 0x2E, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I32_ATOMIC_RMW16_AND_U = {prefix = PREFIX_ATOM, opcode = 0x2F, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW8_AND_U = {prefix = PREFIX_ATOM, opcode = 0x30, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW16_AND_U = {prefix = PREFIX_ATOM, opcode = 0x31, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW32_AND_U = {prefix = PREFIX_ATOM, opcode = 0x32, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I32_ATOMIC_RMW_OR = {prefix = PREFIX_ATOM, opcode = 0x33, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW_OR = {prefix = PREFIX_ATOM, opcode = 0x34, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I32_ATOMIC_RMW8_OR_U = {prefix = PREFIX_ATOM, opcode = 0x35, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I32_ATOMIC_RMW16_OR_U = {prefix = PREFIX_ATOM, opcode = 0x36, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW8_OR_U = {prefix = PREFIX_ATOM, opcode = 0x37, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW16_OR_U = {prefix = PREFIX_ATOM, opcode = 0x38, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW32_OR_U = {prefix = PREFIX_ATOM, opcode = 0x39, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I32_ATOMIC_RMW_XOR = {prefix = PREFIX_ATOM, opcode = 0x3A, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW_XOR = {prefix = PREFIX_ATOM, opcode = 0x3B, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I32_ATOMIC_RMW8_XOR_U = {prefix = PREFIX_ATOM, opcode = 0x3C, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I32_ATOMIC_RMW16_XOR_U = {prefix = PREFIX_ATOM, opcode = 0x3D, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW8_XOR_U = {prefix = PREFIX_ATOM, opcode = 0x3E, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW16_XOR_U = {prefix = PREFIX_ATOM, opcode = 0x3F, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW32_XOR_U = {prefix = PREFIX_ATOM, opcode = 0x40, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I32_ATOMIC_RMW_XCHG = {prefix = PREFIX_ATOM, opcode = 0x41, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW_XCHG = {prefix = PREFIX_ATOM, opcode = 0x42, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I32_ATOMIC_RMW8_XCHG_U = {prefix = PREFIX_ATOM, opcode = 0x43, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I32_ATOMIC_RMW16_XCHG_U = {prefix = PREFIX_ATOM, opcode = 0x44, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW8_XCHG_U = {prefix = PREFIX_ATOM, opcode = 0x45, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW16_XCHG_U = {prefix = PREFIX_ATOM, opcode = 0x46, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I64_ATOMIC_RMW32_XCHG_U = {prefix = PREFIX_ATOM, opcode = 0x47, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 2, outputs = 1}, - .I32_ATOMIC_RMW_CMPXCHG = {prefix = PREFIX_ATOM, opcode = 0x48, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 3, outputs = 1}, - .I64_ATOMIC_RMW_CMPXCHG = {prefix = PREFIX_ATOM, opcode = 0x49, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 3, outputs = 1}, - .I32_ATOMIC_RMW8_CMPXCHG_U = {prefix = PREFIX_ATOM, opcode = 0x4A, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 3, outputs = 1}, - .I32_ATOMIC_RMW16_CMPXCHG_U = {prefix = PREFIX_ATOM, opcode = 0x4B, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 3, outputs = 1}, - .I64_ATOMIC_RMW8_CMPXCHG_U = {prefix = PREFIX_ATOM, opcode = 0x4C, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 3, outputs = 1}, - .I64_ATOMIC_RMW16_CMPXCHG_U = {prefix = PREFIX_ATOM, opcode = 0x4D, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 3, outputs = 1}, - .I64_ATOMIC_RMW32_CMPXCHG_U = {prefix = PREFIX_ATOM, opcode = 0x4E, imm = {.MEMARG, .NONE}, flags = MEM, inputs = 3, outputs = 1}, -} - -// Per-mnemonic encode form. Returns a pointer into the rodata table. -@(private, require_results) -encoding_form :: #force_inline proc "contextless" (m: Mnemonic) -> ^Encoding { - return &ENCODING_TABLE[m] -} diff --git a/core/rexcode/wasm/encoding_types.odin b/core/rexcode/wasm/encoding_types.odin deleted file mode 100644 index 2a2c6fdce..000000000 --- a/core/rexcode/wasm/encoding_types.odin +++ /dev/null @@ -1,239 +0,0 @@ -// rexcode · Brendan Punsky (dotbmp@github), original author -// Ginger Bill (gingerBill@github) - -package rexcode_wasm - -import "core:rexcode/isa" - -// ============================================================================= -// WebAssembly ENCODING FUNDAMENTALS -// ============================================================================= -// -// An instruction is: [prefix?] opcode immediate* -// -// * `prefix` is 0 for the single-byte core opcodes, or one of 0xFC (misc), -// 0xFD (SIMD), 0xFE (threads). When present, the *sub*-opcode that -// follows is an unsigned LEB128 (so SIMD's 0..275 fit). -// * Integer immediates use LEB128 (unsigned for indices/alignment, signed -// for i32.const/i64.const and the s33 blocktype). -// * Float constants are raw little-endian IEEE-754 (4 or 8 bytes). -// -// There is at most one encoding form per mnemonic, so dispatch is a direct -// `ENCODING_TABLE[mnemonic]` lookup (O(1)) rather than the operand-shape -// scan the variable-form arches (x86) need. The immediate layout is described -// declaratively by `imm: [2]Imm_Kind`, walked in order by the encoder and -// decoder. - -Error :: isa.Error -Label_Definition :: isa.Label_Definition -LABEL_UNDEFINED :: isa.LABEL_UNDEFINED - -// Relocation / Relocation_Type live in reloc.odin (per-arch by design). - -// Opcode-space prefix bytes. -PREFIX_NONE :: u8(0x00) -PREFIX_MISC :: u8(0xFC) // saturating truncation, bulk memory/table -PREFIX_SIMD :: u8(0xFD) // vector (v128) -PREFIX_ATOM :: u8(0xFE) // threads / atomics - -Encoding_Flags :: bit_field u8 { - control: bool | 1, // structured control flow (block/loop/if/else/end/br*) - memory: bool | 1, // touches linear memory - _: u8 | 6, -} - -// How one immediate field is laid down after the opcode. -Imm_Kind :: enum u8 { - NONE, - BLOCKTYPE, // signed LEB128 s33 (negative valtype byte, or type index) - I32, // signed LEB128 (i32.const) - I64, // signed LEB128 (i64.const) - F32, // 4 little-endian bytes - F64, // 8 little-endian bytes - IDX, // unsigned LEB128 index (space comes from the operand) - MEMARG, // unsigned LEB128 align, then unsigned LEB128 offset - REFTYPE, // single value-type byte (ref.null) - BR_TABLE, // unsigned LEB128 count, that many label depths, default depth - ZERO_BYTE, // a single reserved 0x00 byte (memidx placeholders) - LANE, // single byte lane index (SIMD extract/replace/load/store lane) - LANES16, // sixteen raw bytes (v128.const value / i8x16.shuffle mask), from Instruction.bytes -} - -Encoding :: struct #packed { - mnemonic: Mnemonic, // 2 -- redundant w/ table index, kept for parity - prefix: u8, // 1 -- PREFIX_NONE / PREFIX_MISC / PREFIX_SIMD / PREFIX_ATOM - opcode: u16, // 2 -- primary opcode, or sub-opcode within a prefix group (SIMD reaches 0x113) - imm: [2]Imm_Kind, // 2 -- immediate layout, walked in order - flags: Encoding_Flags, // 1 - inputs: i8, // 1 -- stack operands consumed (the arity in); -1 when it varies - outputs: i8, // 1 -- stack results produced (the arity out); -1 when it varies -} -#assert(size_of(Encoding) == 10) - -// `inputs`/`outputs` give the fixed stack arity of an instruction: how many -// values it pops and how many it pushes. They are -1 when the count is not a -// constant of the opcode -- i.e. it depends on a type/blocktype immediate or on -// the surrounding control frame. That covers `call`/`call_indirect` (the callee -// or referenced signature), the structured control ops `block`/`loop`/`if` -// (their blocktype) plus `else`/`end`, the branches `br`/`br_if`/`br_table` and -// `return` (the target/result types, and stack-polymorphism), and the -// stack-polymorphic `unreachable`. Everything else is a fixed (inputs, outputs). - -// ============================================================================= -// LEB128 + little-endian primitives (shared by encoder and decoder) -// ============================================================================= - -// Unsigned LEB128. Advances `*offset`. Caller guarantees buffer space. -write_uleb :: #force_inline proc "contextless" (code: []u8, offset: ^u32, value: u64) { - v := value - for { - b := u8(v & 0x7F) - v >>= 7 - if v != 0 { - b |= 0x80 - } - code[offset^] = b - offset^ += 1 - if v == 0 { - break - } - } -} - -// Signed LEB128. Advances `*offset`. -write_sleb :: #force_inline proc "contextless" (code: []u8, offset: ^u32, value: i64) { - v := value - for { - b := u8(v & 0x7F) - v >>= 7 // arithmetic shift on signed value sign-extends - done := (v == 0 && (b & 0x40) == 0) || (v == -1 && (b & 0x40) != 0) - if !done { - b |= 0x80 - } - code[offset^] = b - offset^ += 1 - if done { - break - } - } -} - -// Fixed 5-byte unsigned LEB128 (relocatable placeholder for 32-bit indices). -write_uleb_padded5 :: #force_inline proc "contextless" (code: []u8, offset: ^u32, value: u64) { - v := value - for i := 0; i < 5 && offset^ < u32(len(code)); i += 1 { - b := u8(v & 0x7F) - v >>= 7 - if i != 4 { - b |= 0x80 - } - code[offset^] = b - offset^ += 1 - } -} - -@(require_results) -uleb_size :: #force_inline proc "contextless" (value: u64) -> u32 { - v := value - n: u32 = 1 - for /**/; v >= 0x80; n += 1 { - v >>= 7 - } - return n -} - -@(require_results) -sleb_size :: #force_inline proc "contextless" (value: i64) -> u32 { - v := value - n: u32 = 0 - for { - b := u8(v & 0x7F) - v >>= 7 - n += 1 - if (v == 0 && (b & 0x40) == 0) || (v == -1 && (b & 0x40) != 0) { - break - } - } - return n -} - -// Read unsigned LEB128 starting at `*offset`; advances it. `ok` is false on -// truncation. Reads at most `max` bytes (10 covers u64). -@(require_results) -read_uleb :: #force_inline proc "contextless" (data: []u8, offset: ^u32) -> (value: u64, ok: bool) { - shift: uint = 0 - for i := 0; i < 10 && offset^ < u32(len(data)); i += 1 { - b := data[offset^] - offset^ += 1 - value |= u64(b & 0x7F) << shift - if b & 0x80 == 0 { - return value, true - } - shift += 7 - } - return 0, false -} - -// Read signed LEB128 starting at `*offset`; advances it. -@(require_results) -read_sleb :: #force_inline proc "contextless" (data: []u8, offset: ^u32) -> (value: i64, ok: bool) { - shift: uint = 0 - b: u8 = 0 - for i := 0; i < 10 && offset^ < u32(len(data)); i += 1 { - b = data[offset^] - offset^ += 1 - value |= i64(b & 0x7F) << shift - shift += 7 - if b & 0x80 == 0 { - break - } - } - if shift < 64 && (b & 0x40) != 0 { - value |= -(i64(1) << shift) - } - ok = true - return -} - -write_u32_block :: #force_inline proc(code: []u8, offset: ^u32, v: u32) { - assert(offset^+ 4 <= u32(len(code))) - code[offset^+0] = u8(v) - code[offset^+1] = u8(v >> 8) - code[offset^+2] = u8(v >> 16) - code[offset^+3] = u8(v >> 24) - offset^ += 4 -} - -write_u64_block :: #force_inline proc(code: []u8, offset: ^u32, v: u64) { - assert(offset^+ 8 <= u32(len(code))) - for i in u32(0)..<8 { - code[offset^+i] = u8(v >> (8 * i)) - } - offset^ += 8 -} - -@(require_results) -read_u32_block :: #force_inline proc "contextless" (data: []u8, offset: ^u32) -> (u32, bool) { - if offset^ + 4 > u32(len(data)) { - return 0, false - } - v := u32(data[offset^+0]) | - u32(data[offset^+1])<<8 | - u32(data[offset^+2])<<16 | - u32(data[offset^+3])<<24 - offset^ += 4 - return v, true -} - -@(require_results) -read_u64_block :: #force_inline proc "contextless" (data: []u8, offset: ^u32) -> (u64, bool) { - if offset^ + 8 > u32(len(data)) { - return 0, false - } - v: u64 = 0 - for i in u32(0)..<8 { - v |= u64(data[offset^+i]) << (8 * i) - } - offset^ += 8 - return v, true -} diff --git a/core/rexcode/wasm/instructions.odin b/core/rexcode/wasm/instructions.odin deleted file mode 100644 index d2bd516b0..000000000 --- a/core/rexcode/wasm/instructions.odin +++ /dev/null @@ -1,151 +0,0 @@ -// rexcode · Brendan Punsky (dotbmp@github), original author -// Ginger Bill (gingerBill@github) - -package rexcode_wasm - -// ============================================================================= -// INSTRUCTION -// ============================================================================= -// -// WASM instructions are variable length: a single opcode byte (or a prefix -// byte 0xFC/0xFD/0xFE plus an unsigned-LEB sub-opcode) followed by zero or -// more immediate fields. Two immediate slots cover every modelled form -// (e.g. call_indirect's typeidx + tableidx, table.copy's two tableidx). -// -// `br_table` is the one operator whose immediate is a *vector* of label -// depths; its default label lives in ops[0] and the case targets in the -// `targets` slice (caller-owned, like the rest of the input). `length` is -// filled by the encoder (and by the decoder) since it is not fixed. - -Instruction_Flags :: bit_field u8 { - _: u8 | 8, -} - -Instruction :: struct { - ops: [2]Operand `fmt:"v,operand_count"`, - targets: []u32, // br_table case labels (default in ops[0]) - bytes: [16]u8, // v128.const value / i8x16.shuffle lane mask (LANES16) - mnemonic: Mnemonic, - operand_count: u8, - flags: Instruction_Flags, - length: u8, // filled by encoder/decoder (1..N) - _: [3]u8, -} -#assert(size_of(Instruction) == 48 + 2*size_of(int)) - -// ============================================================================= -// Builders (shape spelled out, comma-separated -- contract surface) -// ============================================================================= - -@(require_results) -inst_none :: #force_inline proc "contextless" (m: Mnemonic) -> Instruction { - return Instruction{mnemonic = m, operand_count = 0} -} - -// Single immediate constant (i32/i64/f32/f64.const, ref.null). -@(require_results) -inst_i :: #force_inline proc "contextless" (m: Mnemonic, o: Operand) -> Instruction { - return Instruction{mnemonic = m, operand_count = 1, ops = {o, {}}} -} - -// Single index immediate (local/global/func/.../label). -@(require_results) -inst_idx :: #force_inline proc "contextless" (m: Mnemonic, o: Operand) -> Instruction { - return Instruction{mnemonic = m, operand_count = 1, ops = {o, {}}} -} - -// Memory access: a single memarg. -@(require_results) -inst_memarg :: #force_inline proc "contextless" (m: Mnemonic, ma: Memarg) -> Instruction { - return Instruction{mnemonic = m, operand_count = 1, ops = {op_mem(ma), {}}} -} - -// Block / loop / if with a signature. -@(require_results) -inst_block :: #force_inline proc "contextless" (m: Mnemonic, bt: Block_Type = .EMPTY) -> Instruction { - return Instruction{mnemonic = m, operand_count = 1, ops = {op_blocktype(bt), {}}} -} - -// Branch with a relative label depth (br / br_if). -@(require_results) -inst_br :: #force_inline proc "contextless" (m: Mnemonic, depth: u32) -> Instruction { - return Instruction{mnemonic = m, operand_count = 1, ops = {op_labelidx(depth), {}}} -} - -// br_table: a vector of case depths plus a default depth. -@(require_results) -inst_br_table :: #force_inline proc "contextless" (targets: []u32, default_depth: u32) -> Instruction { - return Instruction{ - mnemonic = .BR_TABLE, operand_count = 1, - ops = {op_labelidx(default_depth), {}}, targets = targets, - } -} - -// call_indirect typeidx, tableidx. -@(require_results) -inst_call_indirect :: #force_inline proc "contextless" (type_index: u32, table_index: u32 = 0) -> Instruction { - return Instruction{ - mnemonic = .CALL_INDIRECT, operand_count = 2, - ops = {op_type(type_index), op_table(table_index)}, - } -} - -// Two-index operators (table.init elemidx tableidx; table.copy dst src). -@(require_results) -inst_idx_idx :: #force_inline proc "contextless" (m: Mnemonic, a, b: Operand) -> Instruction { - return Instruction{mnemonic = m, operand_count = 2, ops = {a, b}} -} - -// ----------------------------------------------------------------------------- -// SIMD (0xFD) builders -// ----------------------------------------------------------------------------- - -// v128.const: a 16-byte literal carried in `bytes` (no stack operand). -@(require_results) -inst_v128_const :: #force_inline proc "contextless" (value: [16]u8) -> Instruction { - return Instruction{mnemonic = .V128_CONST, operand_count = 0, bytes = value} -} - -// i8x16.shuffle: a 16-lane index mask carried in `bytes`. -@(require_results) -inst_shuffle :: #force_inline proc "contextless" (lanes: [16]u8) -> Instruction { - return Instruction{mnemonic = .I8X16_SHUFFLE, operand_count = 0, bytes = lanes} -} - -// extract_lane / replace_lane: a single lane index immediate. -@(require_results) -inst_lane :: #force_inline proc "contextless" (m: Mnemonic, lane: u8) -> Instruction { - return Instruction{mnemonic = m, operand_count = 1, ops = {op_lane(lane), {}}} -} - -// v128 load/store *_lane: a memarg plus a lane index. -@(require_results) -inst_mem_lane :: #force_inline proc "contextless" (m: Mnemonic, ma: Memarg, lane: u8) -> Instruction { - return Instruction{mnemonic = m, operand_count = 2, ops = {op_mem(ma), op_lane(lane)}} -} - -// ============================================================================= -// Emitters (append to a [dynamic]Instruction) -// ============================================================================= - -emit_none :: #force_inline proc(buf: ^[dynamic]Instruction, m: Mnemonic) { - append(buf, inst_none(m)) -} -emit_i :: #force_inline proc(buf: ^[dynamic]Instruction, m: Mnemonic, o: Operand) { - append(buf, inst_i(m, o)) -} -emit_idx :: #force_inline proc(buf: ^[dynamic]Instruction, m: Mnemonic, o: Operand) { - append(buf, inst_idx(m, o)) -} -emit_memarg :: #force_inline proc(buf: ^[dynamic]Instruction, m: Mnemonic, ma: Memarg) { - append(buf, inst_memarg(m, ma)) -} -emit_block :: #force_inline proc(buf: ^[dynamic]Instruction, m: Mnemonic, bt: Block_Type = .EMPTY) { - append(buf, inst_block(m, bt)) -} -emit_br :: #force_inline proc(buf: ^[dynamic]Instruction, m: Mnemonic, depth: u32) { - append(buf, inst_br(m, depth)) -} -emit_call_indirect :: #force_inline proc(buf: ^[dynamic]Instruction, type_index: u32, table_index: u32 = 0) { - append(buf, inst_call_indirect(type_index, table_index)) -} diff --git a/core/rexcode/wasm/mnemonics.odin b/core/rexcode/wasm/mnemonics.odin deleted file mode 100644 index be035288c..000000000 --- a/core/rexcode/wasm/mnemonics.odin +++ /dev/null @@ -1,471 +0,0 @@ -// rexcode · Brendan Punsky (dotbmp@github), original author -// Ginger Bill (gingerBill@github) - -package rexcode_wasm - -// ============================================================================= -// WebAssembly MNEMONICS -// ============================================================================= -// -// Coverage: -// - WebAssembly 1.0 (MVP) core: control flow, parametric, variable, -// memory, numeric (i32/i64/f32/f64) and conversion instructions. -// - The sign-extension operators (0xC0..0xC4). -// - Reference types ref.null / ref.is_null / ref.func (0xD0..0xD2). -// - The 0xFC misc prefix group: saturating float->int truncation plus the -// bulk memory / table operators (memory.init/copy/fill, table.*, ...). -// - The 0xFD SIMD (fixed-width + relaxed) vector group (v128.*, i8x16.*, ...). -// - The 0xFE threads / atomics group (atomic.fence, *.atomic.load/store/rmw*, -// memory.atomic.notify/wait*). -// -// Per the cross-arch contract: `enum u16`, `INVALID = 0`. - -Mnemonic :: enum u16 { - INVALID = 0, - - // ------------------------------------------------------------------ control - UNREACHABLE, NOP, - BLOCK, LOOP, IF, ELSE, END, - BR, BR_IF, BR_TABLE, - RETURN, CALL, CALL_INDIRECT, - RETURN_CALL, RETURN_CALL_INDIRECT, - - // -------------------------------------------------------------- parametric - DROP, SELECT, - - // ---------------------------------------------------------------- variable - LOCAL_GET, LOCAL_SET, LOCAL_TEE, - GLOBAL_GET, GLOBAL_SET, - - // ------------------------------------------------------------------- memory - I32_LOAD, I64_LOAD, F32_LOAD, F64_LOAD, - I32_LOAD8_S, I32_LOAD8_U, I32_LOAD16_S, I32_LOAD16_U, - I64_LOAD8_S, I64_LOAD8_U, I64_LOAD16_S, I64_LOAD16_U, I64_LOAD32_S, I64_LOAD32_U, - I32_STORE, I64_STORE, F32_STORE, F64_STORE, - I32_STORE8, I32_STORE16, - I64_STORE8, I64_STORE16, I64_STORE32, - MEMORY_SIZE, MEMORY_GROW, - - // ----------------------------------------------------------------- numeric - I32_CONST, I64_CONST, F32_CONST, F64_CONST, - - // i32 comparison - I32_EQZ, I32_EQ, I32_NE, I32_LT_S, I32_LT_U, I32_GT_S, I32_GT_U, - I32_LE_S, I32_LE_U, I32_GE_S, I32_GE_U, - // i64 comparison - I64_EQZ, I64_EQ, I64_NE, I64_LT_S, I64_LT_U, I64_GT_S, I64_GT_U, - I64_LE_S, I64_LE_U, I64_GE_S, I64_GE_U, - // f32 comparison - F32_EQ, F32_NE, F32_LT, F32_GT, F32_LE, F32_GE, - // f64 comparison - F64_EQ, F64_NE, F64_LT, F64_GT, F64_LE, F64_GE, - - // i32 arithmetic - I32_CLZ, I32_CTZ, I32_POPCNT, - I32_ADD, I32_SUB, I32_MUL, I32_DIV_S, I32_DIV_U, I32_REM_S, I32_REM_U, - I32_AND, I32_OR, I32_XOR, I32_SHL, I32_SHR_S, I32_SHR_U, I32_ROTL, I32_ROTR, - // i64 arithmetic - I64_CLZ, I64_CTZ, I64_POPCNT, - I64_ADD, I64_SUB, I64_MUL, I64_DIV_S, I64_DIV_U, I64_REM_S, I64_REM_U, - I64_AND, I64_OR, I64_XOR, I64_SHL, I64_SHR_S, I64_SHR_U, I64_ROTL, I64_ROTR, - // f32 arithmetic - F32_ABS, F32_NEG, F32_CEIL, F32_FLOOR, F32_TRUNC, F32_NEAREST, F32_SQRT, - F32_ADD, F32_SUB, F32_MUL, F32_DIV, F32_MIN, F32_MAX, F32_COPYSIGN, - // f64 arithmetic - F64_ABS, F64_NEG, F64_CEIL, F64_FLOOR, F64_TRUNC, F64_NEAREST, F64_SQRT, - F64_ADD, F64_SUB, F64_MUL, F64_DIV, F64_MIN, F64_MAX, F64_COPYSIGN, - - // conversions - I32_WRAP_I64, - I32_TRUNC_F32_S, I32_TRUNC_F32_U, I32_TRUNC_F64_S, I32_TRUNC_F64_U, - I64_EXTEND_I32_S, I64_EXTEND_I32_U, - I64_TRUNC_F32_S, I64_TRUNC_F32_U, I64_TRUNC_F64_S, I64_TRUNC_F64_U, - F32_CONVERT_I32_S, F32_CONVERT_I32_U, F32_CONVERT_I64_S, F32_CONVERT_I64_U, F32_DEMOTE_F64, - F64_CONVERT_I32_S, F64_CONVERT_I32_U, F64_CONVERT_I64_S, F64_CONVERT_I64_U, F64_PROMOTE_F32, - I32_REINTERPRET_F32, I64_REINTERPRET_F64, F32_REINTERPRET_I32, F64_REINTERPRET_I64, - - // sign-extension operators (0xC0..0xC4) - I32_EXTEND8_S, I32_EXTEND16_S, - I64_EXTEND8_S, I64_EXTEND16_S, I64_EXTEND32_S, - - // reference types - REF_NULL, REF_IS_NULL, REF_FUNC, - - // ------------------------------------------------------- 0xFC misc prefix - // saturating truncation - I32_TRUNC_SAT_F32_S, I32_TRUNC_SAT_F32_U, I32_TRUNC_SAT_F64_S, I32_TRUNC_SAT_F64_U, - I64_TRUNC_SAT_F32_S, I64_TRUNC_SAT_F32_U, I64_TRUNC_SAT_F64_S, I64_TRUNC_SAT_F64_U, - // bulk memory & table - MEMORY_INIT, DATA_DROP, MEMORY_COPY, MEMORY_FILL, - TABLE_INIT, ELEM_DROP, TABLE_COPY, TABLE_GROW, TABLE_SIZE, TABLE_FILL, - - // ----------------------------------------------- 0xFD SIMD (v128) prefix - V128_LOAD, V128_LOAD8X8_S, V128_LOAD8X8_U, - V128_LOAD16X4_S, V128_LOAD16X4_U, V128_LOAD32X2_S, - V128_LOAD32X2_U, V128_LOAD8_SPLAT, V128_LOAD16_SPLAT, - V128_LOAD32_SPLAT, V128_LOAD64_SPLAT, V128_STORE, - V128_CONST, I8X16_SHUFFLE, I8X16_SWIZZLE, - I8X16_SPLAT, I16X8_SPLAT, I32X4_SPLAT, - I64X2_SPLAT, F32X4_SPLAT, F64X2_SPLAT, - I8X16_EXTRACT_LANE_S, I8X16_EXTRACT_LANE_U, I8X16_REPLACE_LANE, - I16X8_EXTRACT_LANE_S, I16X8_EXTRACT_LANE_U, I16X8_REPLACE_LANE, - I32X4_EXTRACT_LANE, I32X4_REPLACE_LANE, I64X2_EXTRACT_LANE, - I64X2_REPLACE_LANE, F32X4_EXTRACT_LANE, F32X4_REPLACE_LANE, - F64X2_EXTRACT_LANE, F64X2_REPLACE_LANE, I8X16_EQ, - I8X16_NE, I8X16_LT_S, I8X16_LT_U, - I8X16_GT_S, I8X16_GT_U, I8X16_LE_S, - I8X16_LE_U, I8X16_GE_S, I8X16_GE_U, - I16X8_EQ, I16X8_NE, I16X8_LT_S, - I16X8_LT_U, I16X8_GT_S, I16X8_GT_U, - I16X8_LE_S, I16X8_LE_U, I16X8_GE_S, - I16X8_GE_U, I32X4_EQ, I32X4_NE, - I32X4_LT_S, I32X4_LT_U, I32X4_GT_S, - I32X4_GT_U, I32X4_LE_S, I32X4_LE_U, - I32X4_GE_S, I32X4_GE_U, F32X4_EQ, - F32X4_NE, F32X4_LT, F32X4_GT, - F32X4_LE, F32X4_GE, F64X2_EQ, - F64X2_NE, F64X2_LT, F64X2_GT, - F64X2_LE, F64X2_GE, V128_NOT, - V128_AND, V128_ANDNOT, V128_OR, - V128_XOR, V128_BITSELECT, V128_ANY_TRUE, - V128_LOAD8_LANE, V128_LOAD16_LANE, V128_LOAD32_LANE, - V128_LOAD64_LANE, V128_STORE8_LANE, V128_STORE16_LANE, - V128_STORE32_LANE, V128_STORE64_LANE, V128_LOAD32_ZERO, - V128_LOAD64_ZERO, F32X4_DEMOTE_F64X2_ZERO, F64X2_PROMOTE_LOW_F32X4, - I8X16_ABS, I8X16_NEG, I8X16_POPCNT, - I8X16_ALL_TRUE, I8X16_BITMASK, I8X16_NARROW_I16X8_S, - I8X16_NARROW_I16X8_U, F32X4_CEIL, F32X4_FLOOR, - F32X4_TRUNC, F32X4_NEAREST, I8X16_SHL, - I8X16_SHR_S, I8X16_SHR_U, I8X16_ADD, - I8X16_ADD_SAT_S, I8X16_ADD_SAT_U, I8X16_SUB, - I8X16_SUB_SAT_S, I8X16_SUB_SAT_U, F64X2_CEIL, - F64X2_FLOOR, I8X16_MIN_S, I8X16_MIN_U, - I8X16_MAX_S, I8X16_MAX_U, F64X2_TRUNC, - I8X16_AVGR_U, I16X8_EXTADD_PAIRWISE_I8X16_S, I16X8_EXTADD_PAIRWISE_I8X16_U, - I32X4_EXTADD_PAIRWISE_I16X8_S, I32X4_EXTADD_PAIRWISE_I16X8_U, I16X8_ABS, - I16X8_NEG, I16X8_Q15MULR_SAT_S, I16X8_ALL_TRUE, - I16X8_BITMASK, I16X8_NARROW_I32X4_S, I16X8_NARROW_I32X4_U, - I16X8_EXTEND_LOW_I8X16_S, I16X8_EXTEND_HIGH_I8X16_S, I16X8_EXTEND_LOW_I8X16_U, - I16X8_EXTEND_HIGH_I8X16_U, I16X8_SHL, I16X8_SHR_S, - I16X8_SHR_U, I16X8_ADD, I16X8_ADD_SAT_S, - I16X8_ADD_SAT_U, I16X8_SUB, I16X8_SUB_SAT_S, - I16X8_SUB_SAT_U, F64X2_NEAREST, I16X8_MUL, - I16X8_MIN_S, I16X8_MIN_U, I16X8_MAX_S, - I16X8_MAX_U, I16X8_AVGR_U, I16X8_EXTMUL_LOW_I8X16_S, - I16X8_EXTMUL_HIGH_I8X16_S, I16X8_EXTMUL_LOW_I8X16_U, I16X8_EXTMUL_HIGH_I8X16_U, - I32X4_ABS, I32X4_NEG, I32X4_ALL_TRUE, - I32X4_BITMASK, I32X4_EXTEND_LOW_I16X8_S, I32X4_EXTEND_HIGH_I16X8_S, - I32X4_EXTEND_LOW_I16X8_U, I32X4_EXTEND_HIGH_I16X8_U, I32X4_SHL, - I32X4_SHR_S, I32X4_SHR_U, I32X4_ADD, - I32X4_SUB, I32X4_MUL, I32X4_MIN_S, - I32X4_MIN_U, I32X4_MAX_S, I32X4_MAX_U, - I32X4_DOT_I16X8_S, I32X4_EXTMUL_LOW_I16X8_S, I32X4_EXTMUL_HIGH_I16X8_S, - I32X4_EXTMUL_LOW_I16X8_U, I32X4_EXTMUL_HIGH_I16X8_U, I64X2_ABS, - I64X2_NEG, I64X2_ALL_TRUE, I64X2_BITMASK, - I64X2_EXTEND_LOW_I32X4_S, I64X2_EXTEND_HIGH_I32X4_S, I64X2_EXTEND_LOW_I32X4_U, - I64X2_EXTEND_HIGH_I32X4_U, I64X2_SHL, I64X2_SHR_S, - I64X2_SHR_U, I64X2_ADD, I64X2_SUB, - I64X2_MUL, I64X2_EQ, I64X2_NE, - I64X2_LT_S, I64X2_GT_S, I64X2_LE_S, - I64X2_GE_S, I64X2_EXTMUL_LOW_I32X4_S, I64X2_EXTMUL_HIGH_I32X4_S, - I64X2_EXTMUL_LOW_I32X4_U, I64X2_EXTMUL_HIGH_I32X4_U, F32X4_ABS, - F32X4_NEG, F32X4_SQRT, F32X4_ADD, - F32X4_SUB, F32X4_MUL, F32X4_DIV, - F32X4_MIN, F32X4_MAX, F32X4_PMIN, - F32X4_PMAX, F64X2_ABS, F64X2_NEG, - F64X2_SQRT, F64X2_ADD, F64X2_SUB, - F64X2_MUL, F64X2_DIV, F64X2_MIN, - F64X2_MAX, F64X2_PMIN, F64X2_PMAX, - I32X4_TRUNC_SAT_F32X4_S, I32X4_TRUNC_SAT_F32X4_U, F32X4_CONVERT_I32X4_S, - F32X4_CONVERT_I32X4_U, I32X4_TRUNC_SAT_F64X2_S_ZERO, I32X4_TRUNC_SAT_F64X2_U_ZERO, - F64X2_CONVERT_LOW_I32X4_S, F64X2_CONVERT_LOW_I32X4_U, I8X16_RELAXED_SWIZZLE, - I32X4_RELAXED_TRUNC_F32X4_S, I32X4_RELAXED_TRUNC_F32X4_U, I32X4_RELAXED_TRUNC_F64X2_S_ZERO, - I32X4_RELAXED_TRUNC_F64X2_U_ZERO, F32X4_RELAXED_MADD, F32X4_RELAXED_NMADD, - F64X2_RELAXED_MADD, F64X2_RELAXED_NMADD, I8X16_RELAXED_LANESELECT, - I16X8_RELAXED_LANESELECT, I32X4_RELAXED_LANESELECT, I64X2_RELAXED_LANESELECT, - F32X4_RELAXED_MIN, F32X4_RELAXED_MAX, F64X2_RELAXED_MIN, - F64X2_RELAXED_MAX, I16X8_RELAXED_Q15MULR_S, I16X8_RELAXED_DOT_I8X16_I7X16_S, - I32X4_RELAXED_DOT_I8X16_I7X16_ADD_S, - - // ------------------------------------------ 0xFE threads / atomics prefix - MEMORY_ATOMIC_NOTIFY, MEMORY_ATOMIC_WAIT32, MEMORY_ATOMIC_WAIT64, - ATOMIC_FENCE, I32_ATOMIC_LOAD, I64_ATOMIC_LOAD, - I32_ATOMIC_LOAD8_U, I32_ATOMIC_LOAD16_U, I64_ATOMIC_LOAD8_U, - I64_ATOMIC_LOAD16_U, I64_ATOMIC_LOAD32_U, I32_ATOMIC_STORE, - I64_ATOMIC_STORE, I32_ATOMIC_STORE8, I32_ATOMIC_STORE16, - I64_ATOMIC_STORE8, I64_ATOMIC_STORE16, I64_ATOMIC_STORE32, - I32_ATOMIC_RMW_ADD, I64_ATOMIC_RMW_ADD, I32_ATOMIC_RMW8_ADD_U, - I32_ATOMIC_RMW16_ADD_U, I64_ATOMIC_RMW8_ADD_U, I64_ATOMIC_RMW16_ADD_U, - I64_ATOMIC_RMW32_ADD_U, I32_ATOMIC_RMW_SUB, I64_ATOMIC_RMW_SUB, - I32_ATOMIC_RMW8_SUB_U, I32_ATOMIC_RMW16_SUB_U, I64_ATOMIC_RMW8_SUB_U, - I64_ATOMIC_RMW16_SUB_U, I64_ATOMIC_RMW32_SUB_U, I32_ATOMIC_RMW_AND, - I64_ATOMIC_RMW_AND, I32_ATOMIC_RMW8_AND_U, I32_ATOMIC_RMW16_AND_U, - I64_ATOMIC_RMW8_AND_U, I64_ATOMIC_RMW16_AND_U, I64_ATOMIC_RMW32_AND_U, - I32_ATOMIC_RMW_OR, I64_ATOMIC_RMW_OR, I32_ATOMIC_RMW8_OR_U, - I32_ATOMIC_RMW16_OR_U, I64_ATOMIC_RMW8_OR_U, I64_ATOMIC_RMW16_OR_U, - I64_ATOMIC_RMW32_OR_U, I32_ATOMIC_RMW_XOR, I64_ATOMIC_RMW_XOR, - I32_ATOMIC_RMW8_XOR_U, I32_ATOMIC_RMW16_XOR_U, I64_ATOMIC_RMW8_XOR_U, - I64_ATOMIC_RMW16_XOR_U, I64_ATOMIC_RMW32_XOR_U, I32_ATOMIC_RMW_XCHG, - I64_ATOMIC_RMW_XCHG, I32_ATOMIC_RMW8_XCHG_U, I32_ATOMIC_RMW16_XCHG_U, - I64_ATOMIC_RMW8_XCHG_U, I64_ATOMIC_RMW16_XCHG_U, I64_ATOMIC_RMW32_XCHG_U, - I32_ATOMIC_RMW_CMPXCHG, I64_ATOMIC_RMW_CMPXCHG, I32_ATOMIC_RMW8_CMPXCHG_U, - I32_ATOMIC_RMW16_CMPXCHG_U, I64_ATOMIC_RMW8_CMPXCHG_U, I64_ATOMIC_RMW16_CMPXCHG_U, - I64_ATOMIC_RMW32_CMPXCHG_U, -} - -// ----------------------------------------------------------------------------- -// Canonical WAT text names (per-arch formatting -- WASM mixes '.' and '_' in -// ways no single transform of the enum name captures, so the names are -// explicit). Indexed by Mnemonic; INVALID maps to "". -// ----------------------------------------------------------------------------- - -@(rodata) -MNEMONIC_NAMES := [Mnemonic]string{ - .INVALID = "", - - .UNREACHABLE = "unreachable", .NOP = "nop", - .BLOCK = "block", .LOOP = "loop", .IF = "if", .ELSE = "else", .END = "end", - .BR = "br", .BR_IF = "br_if", .BR_TABLE = "br_table", - .RETURN = "return", .CALL = "call", .CALL_INDIRECT = "call_indirect", - .RETURN_CALL = "return_call", .RETURN_CALL_INDIRECT = "return_call_indirect", - - .DROP = "drop", .SELECT = "select", - - .LOCAL_GET = "local.get", .LOCAL_SET = "local.set", .LOCAL_TEE = "local.tee", - .GLOBAL_GET = "global.get", .GLOBAL_SET = "global.set", - - .I32_LOAD = "i32.load", .I64_LOAD = "i64.load", .F32_LOAD = "f32.load", .F64_LOAD = "f64.load", - .I32_LOAD8_S = "i32.load8_s", .I32_LOAD8_U = "i32.load8_u", - .I32_LOAD16_S = "i32.load16_s", .I32_LOAD16_U = "i32.load16_u", - .I64_LOAD8_S = "i64.load8_s", .I64_LOAD8_U = "i64.load8_u", - .I64_LOAD16_S = "i64.load16_s", .I64_LOAD16_U = "i64.load16_u", - .I64_LOAD32_S = "i64.load32_s", .I64_LOAD32_U = "i64.load32_u", - .I32_STORE = "i32.store", .I64_STORE = "i64.store", .F32_STORE = "f32.store", .F64_STORE = "f64.store", - .I32_STORE8 = "i32.store8", .I32_STORE16 = "i32.store16", - .I64_STORE8 = "i64.store8", .I64_STORE16 = "i64.store16", .I64_STORE32 = "i64.store32", - .MEMORY_SIZE = "memory.size", .MEMORY_GROW = "memory.grow", - - .I32_CONST = "i32.const", .I64_CONST = "i64.const", .F32_CONST = "f32.const", .F64_CONST = "f64.const", - - .I32_EQZ = "i32.eqz", .I32_EQ = "i32.eq", .I32_NE = "i32.ne", - .I32_LT_S = "i32.lt_s", .I32_LT_U = "i32.lt_u", .I32_GT_S = "i32.gt_s", .I32_GT_U = "i32.gt_u", - .I32_LE_S = "i32.le_s", .I32_LE_U = "i32.le_u", .I32_GE_S = "i32.ge_s", .I32_GE_U = "i32.ge_u", - .I64_EQZ = "i64.eqz", .I64_EQ = "i64.eq", .I64_NE = "i64.ne", - .I64_LT_S = "i64.lt_s", .I64_LT_U = "i64.lt_u", .I64_GT_S = "i64.gt_s", .I64_GT_U = "i64.gt_u", - .I64_LE_S = "i64.le_s", .I64_LE_U = "i64.le_u", .I64_GE_S = "i64.ge_s", .I64_GE_U = "i64.ge_u", - .F32_EQ = "f32.eq", .F32_NE = "f32.ne", .F32_LT = "f32.lt", .F32_GT = "f32.gt", .F32_LE = "f32.le", .F32_GE = "f32.ge", - .F64_EQ = "f64.eq", .F64_NE = "f64.ne", .F64_LT = "f64.lt", .F64_GT = "f64.gt", .F64_LE = "f64.le", .F64_GE = "f64.ge", - - .I32_CLZ = "i32.clz", .I32_CTZ = "i32.ctz", .I32_POPCNT = "i32.popcnt", - .I32_ADD = "i32.add", .I32_SUB = "i32.sub", .I32_MUL = "i32.mul", - .I32_DIV_S = "i32.div_s", .I32_DIV_U = "i32.div_u", .I32_REM_S = "i32.rem_s", .I32_REM_U = "i32.rem_u", - .I32_AND = "i32.and", .I32_OR = "i32.or", .I32_XOR = "i32.xor", - .I32_SHL = "i32.shl", .I32_SHR_S = "i32.shr_s", .I32_SHR_U = "i32.shr_u", .I32_ROTL = "i32.rotl", .I32_ROTR = "i32.rotr", - .I64_CLZ = "i64.clz", .I64_CTZ = "i64.ctz", .I64_POPCNT = "i64.popcnt", - .I64_ADD = "i64.add", .I64_SUB = "i64.sub", .I64_MUL = "i64.mul", - .I64_DIV_S = "i64.div_s", .I64_DIV_U = "i64.div_u", .I64_REM_S = "i64.rem_s", .I64_REM_U = "i64.rem_u", - .I64_AND = "i64.and", .I64_OR = "i64.or", .I64_XOR = "i64.xor", - .I64_SHL = "i64.shl", .I64_SHR_S = "i64.shr_s", .I64_SHR_U = "i64.shr_u", .I64_ROTL = "i64.rotl", .I64_ROTR = "i64.rotr", - .F32_ABS = "f32.abs", .F32_NEG = "f32.neg", .F32_CEIL = "f32.ceil", .F32_FLOOR = "f32.floor", - .F32_TRUNC = "f32.trunc", .F32_NEAREST = "f32.nearest", .F32_SQRT = "f32.sqrt", - .F32_ADD = "f32.add", .F32_SUB = "f32.sub", .F32_MUL = "f32.mul", .F32_DIV = "f32.div", - .F32_MIN = "f32.min", .F32_MAX = "f32.max", .F32_COPYSIGN = "f32.copysign", - .F64_ABS = "f64.abs", .F64_NEG = "f64.neg", .F64_CEIL = "f64.ceil", .F64_FLOOR = "f64.floor", - .F64_TRUNC = "f64.trunc", .F64_NEAREST = "f64.nearest", .F64_SQRT = "f64.sqrt", - .F64_ADD = "f64.add", .F64_SUB = "f64.sub", .F64_MUL = "f64.mul", .F64_DIV = "f64.div", - .F64_MIN = "f64.min", .F64_MAX = "f64.max", .F64_COPYSIGN = "f64.copysign", - - .I32_WRAP_I64 = "i32.wrap_i64", - .I32_TRUNC_F32_S = "i32.trunc_f32_s", .I32_TRUNC_F32_U = "i32.trunc_f32_u", - .I32_TRUNC_F64_S = "i32.trunc_f64_s", .I32_TRUNC_F64_U = "i32.trunc_f64_u", - .I64_EXTEND_I32_S = "i64.extend_i32_s", .I64_EXTEND_I32_U = "i64.extend_i32_u", - .I64_TRUNC_F32_S = "i64.trunc_f32_s", .I64_TRUNC_F32_U = "i64.trunc_f32_u", - .I64_TRUNC_F64_S = "i64.trunc_f64_s", .I64_TRUNC_F64_U = "i64.trunc_f64_u", - .F32_CONVERT_I32_S = "f32.convert_i32_s", .F32_CONVERT_I32_U = "f32.convert_i32_u", - .F32_CONVERT_I64_S = "f32.convert_i64_s", .F32_CONVERT_I64_U = "f32.convert_i64_u", - .F32_DEMOTE_F64 = "f32.demote_f64", - .F64_CONVERT_I32_S = "f64.convert_i32_s", .F64_CONVERT_I32_U = "f64.convert_i32_u", - .F64_CONVERT_I64_S = "f64.convert_i64_s", .F64_CONVERT_I64_U = "f64.convert_i64_u", - .F64_PROMOTE_F32 = "f64.promote_f32", - .I32_REINTERPRET_F32 = "i32.reinterpret_f32", .I64_REINTERPRET_F64 = "i64.reinterpret_f64", - .F32_REINTERPRET_I32 = "f32.reinterpret_i32", .F64_REINTERPRET_I64 = "f64.reinterpret_i64", - - .I32_EXTEND8_S = "i32.extend8_s", .I32_EXTEND16_S = "i32.extend16_s", - .I64_EXTEND8_S = "i64.extend8_s", .I64_EXTEND16_S = "i64.extend16_s", .I64_EXTEND32_S = "i64.extend32_s", - - .REF_NULL = "ref.null", .REF_IS_NULL = "ref.is_null", .REF_FUNC = "ref.func", - - .I32_TRUNC_SAT_F32_S = "i32.trunc_sat_f32_s", .I32_TRUNC_SAT_F32_U = "i32.trunc_sat_f32_u", - .I32_TRUNC_SAT_F64_S = "i32.trunc_sat_f64_s", .I32_TRUNC_SAT_F64_U = "i32.trunc_sat_f64_u", - .I64_TRUNC_SAT_F32_S = "i64.trunc_sat_f32_s", .I64_TRUNC_SAT_F32_U = "i64.trunc_sat_f32_u", - .I64_TRUNC_SAT_F64_S = "i64.trunc_sat_f64_s", .I64_TRUNC_SAT_F64_U = "i64.trunc_sat_f64_u", - .MEMORY_INIT = "memory.init", .DATA_DROP = "data.drop", .MEMORY_COPY = "memory.copy", .MEMORY_FILL = "memory.fill", - .TABLE_INIT = "table.init", .ELEM_DROP = "elem.drop", .TABLE_COPY = "table.copy", - .TABLE_GROW = "table.grow", .TABLE_SIZE = "table.size", .TABLE_FILL = "table.fill", - - // SIMD (0xFD) - .V128_LOAD = "v128.load", .V128_LOAD8X8_S = "v128.load8x8_s", - .V128_LOAD8X8_U = "v128.load8x8_u", .V128_LOAD16X4_S = "v128.load16x4_s", - .V128_LOAD16X4_U = "v128.load16x4_u", .V128_LOAD32X2_S = "v128.load32x2_s", - .V128_LOAD32X2_U = "v128.load32x2_u", .V128_LOAD8_SPLAT = "v128.load8_splat", - .V128_LOAD16_SPLAT = "v128.load16_splat", .V128_LOAD32_SPLAT = "v128.load32_splat", - .V128_LOAD64_SPLAT = "v128.load64_splat", .V128_STORE = "v128.store", - .V128_CONST = "v128.const", .I8X16_SHUFFLE = "i8x16.shuffle", - .I8X16_SWIZZLE = "i8x16.swizzle", .I8X16_SPLAT = "i8x16.splat", - .I16X8_SPLAT = "i16x8.splat", .I32X4_SPLAT = "i32x4.splat", - .I64X2_SPLAT = "i64x2.splat", .F32X4_SPLAT = "f32x4.splat", - .F64X2_SPLAT = "f64x2.splat", .I8X16_EXTRACT_LANE_S = "i8x16.extract_lane_s", - .I8X16_EXTRACT_LANE_U = "i8x16.extract_lane_u", .I8X16_REPLACE_LANE = "i8x16.replace_lane", - .I16X8_EXTRACT_LANE_S = "i16x8.extract_lane_s", .I16X8_EXTRACT_LANE_U = "i16x8.extract_lane_u", - .I16X8_REPLACE_LANE = "i16x8.replace_lane", .I32X4_EXTRACT_LANE = "i32x4.extract_lane", - .I32X4_REPLACE_LANE = "i32x4.replace_lane", .I64X2_EXTRACT_LANE = "i64x2.extract_lane", - .I64X2_REPLACE_LANE = "i64x2.replace_lane", .F32X4_EXTRACT_LANE = "f32x4.extract_lane", - .F32X4_REPLACE_LANE = "f32x4.replace_lane", .F64X2_EXTRACT_LANE = "f64x2.extract_lane", - .F64X2_REPLACE_LANE = "f64x2.replace_lane", .I8X16_EQ = "i8x16.eq", - .I8X16_NE = "i8x16.ne", .I8X16_LT_S = "i8x16.lt_s", - .I8X16_LT_U = "i8x16.lt_u", .I8X16_GT_S = "i8x16.gt_s", - .I8X16_GT_U = "i8x16.gt_u", .I8X16_LE_S = "i8x16.le_s", - .I8X16_LE_U = "i8x16.le_u", .I8X16_GE_S = "i8x16.ge_s", - .I8X16_GE_U = "i8x16.ge_u", .I16X8_EQ = "i16x8.eq", - .I16X8_NE = "i16x8.ne", .I16X8_LT_S = "i16x8.lt_s", - .I16X8_LT_U = "i16x8.lt_u", .I16X8_GT_S = "i16x8.gt_s", - .I16X8_GT_U = "i16x8.gt_u", .I16X8_LE_S = "i16x8.le_s", - .I16X8_LE_U = "i16x8.le_u", .I16X8_GE_S = "i16x8.ge_s", - .I16X8_GE_U = "i16x8.ge_u", .I32X4_EQ = "i32x4.eq", - .I32X4_NE = "i32x4.ne", .I32X4_LT_S = "i32x4.lt_s", - .I32X4_LT_U = "i32x4.lt_u", .I32X4_GT_S = "i32x4.gt_s", - .I32X4_GT_U = "i32x4.gt_u", .I32X4_LE_S = "i32x4.le_s", - .I32X4_LE_U = "i32x4.le_u", .I32X4_GE_S = "i32x4.ge_s", - .I32X4_GE_U = "i32x4.ge_u", .F32X4_EQ = "f32x4.eq", - .F32X4_NE = "f32x4.ne", .F32X4_LT = "f32x4.lt", - .F32X4_GT = "f32x4.gt", .F32X4_LE = "f32x4.le", - .F32X4_GE = "f32x4.ge", .F64X2_EQ = "f64x2.eq", - .F64X2_NE = "f64x2.ne", .F64X2_LT = "f64x2.lt", - .F64X2_GT = "f64x2.gt", .F64X2_LE = "f64x2.le", - .F64X2_GE = "f64x2.ge", .V128_NOT = "v128.not", - .V128_AND = "v128.and", .V128_ANDNOT = "v128.andnot", - .V128_OR = "v128.or", .V128_XOR = "v128.xor", - .V128_BITSELECT = "v128.bitselect", .V128_ANY_TRUE = "v128.any_true", - .V128_LOAD8_LANE = "v128.load8_lane", .V128_LOAD16_LANE = "v128.load16_lane", - .V128_LOAD32_LANE = "v128.load32_lane", .V128_LOAD64_LANE = "v128.load64_lane", - .V128_STORE8_LANE = "v128.store8_lane", .V128_STORE16_LANE = "v128.store16_lane", - .V128_STORE32_LANE = "v128.store32_lane", .V128_STORE64_LANE = "v128.store64_lane", - .V128_LOAD32_ZERO = "v128.load32_zero", .V128_LOAD64_ZERO = "v128.load64_zero", - .F32X4_DEMOTE_F64X2_ZERO = "f32x4.demote_f64x2_zero", .F64X2_PROMOTE_LOW_F32X4 = "f64x2.promote_low_f32x4", - .I8X16_ABS = "i8x16.abs", .I8X16_NEG = "i8x16.neg", - .I8X16_POPCNT = "i8x16.popcnt", .I8X16_ALL_TRUE = "i8x16.all_true", - .I8X16_BITMASK = "i8x16.bitmask", .I8X16_NARROW_I16X8_S = "i8x16.narrow_i16x8_s", - .I8X16_NARROW_I16X8_U = "i8x16.narrow_i16x8_u", .F32X4_CEIL = "f32x4.ceil", - .F32X4_FLOOR = "f32x4.floor", .F32X4_TRUNC = "f32x4.trunc", - .F32X4_NEAREST = "f32x4.nearest", .I8X16_SHL = "i8x16.shl", - .I8X16_SHR_S = "i8x16.shr_s", .I8X16_SHR_U = "i8x16.shr_u", - .I8X16_ADD = "i8x16.add", .I8X16_ADD_SAT_S = "i8x16.add_sat_s", - .I8X16_ADD_SAT_U = "i8x16.add_sat_u", .I8X16_SUB = "i8x16.sub", - .I8X16_SUB_SAT_S = "i8x16.sub_sat_s", .I8X16_SUB_SAT_U = "i8x16.sub_sat_u", - .F64X2_CEIL = "f64x2.ceil", .F64X2_FLOOR = "f64x2.floor", - .I8X16_MIN_S = "i8x16.min_s", .I8X16_MIN_U = "i8x16.min_u", - .I8X16_MAX_S = "i8x16.max_s", .I8X16_MAX_U = "i8x16.max_u", - .F64X2_TRUNC = "f64x2.trunc", .I8X16_AVGR_U = "i8x16.avgr_u", - .I16X8_EXTADD_PAIRWISE_I8X16_S = "i16x8.extadd_pairwise_i8x16_s", .I16X8_EXTADD_PAIRWISE_I8X16_U = "i16x8.extadd_pairwise_i8x16_u", - .I32X4_EXTADD_PAIRWISE_I16X8_S = "i32x4.extadd_pairwise_i16x8_s", .I32X4_EXTADD_PAIRWISE_I16X8_U = "i32x4.extadd_pairwise_i16x8_u", - .I16X8_ABS = "i16x8.abs", .I16X8_NEG = "i16x8.neg", - .I16X8_Q15MULR_SAT_S = "i16x8.q15mulr_sat_s", .I16X8_ALL_TRUE = "i16x8.all_true", - .I16X8_BITMASK = "i16x8.bitmask", .I16X8_NARROW_I32X4_S = "i16x8.narrow_i32x4_s", - .I16X8_NARROW_I32X4_U = "i16x8.narrow_i32x4_u", .I16X8_EXTEND_LOW_I8X16_S = "i16x8.extend_low_i8x16_s", - .I16X8_EXTEND_HIGH_I8X16_S = "i16x8.extend_high_i8x16_s", .I16X8_EXTEND_LOW_I8X16_U = "i16x8.extend_low_i8x16_u", - .I16X8_EXTEND_HIGH_I8X16_U = "i16x8.extend_high_i8x16_u", .I16X8_SHL = "i16x8.shl", - .I16X8_SHR_S = "i16x8.shr_s", .I16X8_SHR_U = "i16x8.shr_u", - .I16X8_ADD = "i16x8.add", .I16X8_ADD_SAT_S = "i16x8.add_sat_s", - .I16X8_ADD_SAT_U = "i16x8.add_sat_u", .I16X8_SUB = "i16x8.sub", - .I16X8_SUB_SAT_S = "i16x8.sub_sat_s", .I16X8_SUB_SAT_U = "i16x8.sub_sat_u", - .F64X2_NEAREST = "f64x2.nearest", .I16X8_MUL = "i16x8.mul", - .I16X8_MIN_S = "i16x8.min_s", .I16X8_MIN_U = "i16x8.min_u", - .I16X8_MAX_S = "i16x8.max_s", .I16X8_MAX_U = "i16x8.max_u", - .I16X8_AVGR_U = "i16x8.avgr_u", .I16X8_EXTMUL_LOW_I8X16_S = "i16x8.extmul_low_i8x16_s", - .I16X8_EXTMUL_HIGH_I8X16_S = "i16x8.extmul_high_i8x16_s", .I16X8_EXTMUL_LOW_I8X16_U = "i16x8.extmul_low_i8x16_u", - .I16X8_EXTMUL_HIGH_I8X16_U = "i16x8.extmul_high_i8x16_u", .I32X4_ABS = "i32x4.abs", - .I32X4_NEG = "i32x4.neg", .I32X4_ALL_TRUE = "i32x4.all_true", - .I32X4_BITMASK = "i32x4.bitmask", .I32X4_EXTEND_LOW_I16X8_S = "i32x4.extend_low_i16x8_s", - .I32X4_EXTEND_HIGH_I16X8_S = "i32x4.extend_high_i16x8_s", .I32X4_EXTEND_LOW_I16X8_U = "i32x4.extend_low_i16x8_u", - .I32X4_EXTEND_HIGH_I16X8_U = "i32x4.extend_high_i16x8_u", .I32X4_SHL = "i32x4.shl", - .I32X4_SHR_S = "i32x4.shr_s", .I32X4_SHR_U = "i32x4.shr_u", - .I32X4_ADD = "i32x4.add", .I32X4_SUB = "i32x4.sub", - .I32X4_MUL = "i32x4.mul", .I32X4_MIN_S = "i32x4.min_s", - .I32X4_MIN_U = "i32x4.min_u", .I32X4_MAX_S = "i32x4.max_s", - .I32X4_MAX_U = "i32x4.max_u", .I32X4_DOT_I16X8_S = "i32x4.dot_i16x8_s", - .I32X4_EXTMUL_LOW_I16X8_S = "i32x4.extmul_low_i16x8_s", .I32X4_EXTMUL_HIGH_I16X8_S = "i32x4.extmul_high_i16x8_s", - .I32X4_EXTMUL_LOW_I16X8_U = "i32x4.extmul_low_i16x8_u", .I32X4_EXTMUL_HIGH_I16X8_U = "i32x4.extmul_high_i16x8_u", - .I64X2_ABS = "i64x2.abs", .I64X2_NEG = "i64x2.neg", - .I64X2_ALL_TRUE = "i64x2.all_true", .I64X2_BITMASK = "i64x2.bitmask", - .I64X2_EXTEND_LOW_I32X4_S = "i64x2.extend_low_i32x4_s", .I64X2_EXTEND_HIGH_I32X4_S = "i64x2.extend_high_i32x4_s", - .I64X2_EXTEND_LOW_I32X4_U = "i64x2.extend_low_i32x4_u", .I64X2_EXTEND_HIGH_I32X4_U = "i64x2.extend_high_i32x4_u", - .I64X2_SHL = "i64x2.shl", .I64X2_SHR_S = "i64x2.shr_s", - .I64X2_SHR_U = "i64x2.shr_u", .I64X2_ADD = "i64x2.add", - .I64X2_SUB = "i64x2.sub", .I64X2_MUL = "i64x2.mul", - .I64X2_EQ = "i64x2.eq", .I64X2_NE = "i64x2.ne", - .I64X2_LT_S = "i64x2.lt_s", .I64X2_GT_S = "i64x2.gt_s", - .I64X2_LE_S = "i64x2.le_s", .I64X2_GE_S = "i64x2.ge_s", - .I64X2_EXTMUL_LOW_I32X4_S = "i64x2.extmul_low_i32x4_s", .I64X2_EXTMUL_HIGH_I32X4_S = "i64x2.extmul_high_i32x4_s", - .I64X2_EXTMUL_LOW_I32X4_U = "i64x2.extmul_low_i32x4_u", .I64X2_EXTMUL_HIGH_I32X4_U = "i64x2.extmul_high_i32x4_u", - .F32X4_ABS = "f32x4.abs", .F32X4_NEG = "f32x4.neg", - .F32X4_SQRT = "f32x4.sqrt", .F32X4_ADD = "f32x4.add", - .F32X4_SUB = "f32x4.sub", .F32X4_MUL = "f32x4.mul", - .F32X4_DIV = "f32x4.div", .F32X4_MIN = "f32x4.min", - .F32X4_MAX = "f32x4.max", .F32X4_PMIN = "f32x4.pmin", - .F32X4_PMAX = "f32x4.pmax", .F64X2_ABS = "f64x2.abs", - .F64X2_NEG = "f64x2.neg", .F64X2_SQRT = "f64x2.sqrt", - .F64X2_ADD = "f64x2.add", .F64X2_SUB = "f64x2.sub", - .F64X2_MUL = "f64x2.mul", .F64X2_DIV = "f64x2.div", - .F64X2_MIN = "f64x2.min", .F64X2_MAX = "f64x2.max", - .F64X2_PMIN = "f64x2.pmin", .F64X2_PMAX = "f64x2.pmax", - .I32X4_TRUNC_SAT_F32X4_S = "i32x4.trunc_sat_f32x4_s", .I32X4_TRUNC_SAT_F32X4_U = "i32x4.trunc_sat_f32x4_u", - .F32X4_CONVERT_I32X4_S = "f32x4.convert_i32x4_s", .F32X4_CONVERT_I32X4_U = "f32x4.convert_i32x4_u", - .I32X4_TRUNC_SAT_F64X2_S_ZERO = "i32x4.trunc_sat_f64x2_s_zero", .I32X4_TRUNC_SAT_F64X2_U_ZERO = "i32x4.trunc_sat_f64x2_u_zero", - .F64X2_CONVERT_LOW_I32X4_S = "f64x2.convert_low_i32x4_s", .F64X2_CONVERT_LOW_I32X4_U = "f64x2.convert_low_i32x4_u", - .I8X16_RELAXED_SWIZZLE = "i8x16.relaxed_swizzle", .I32X4_RELAXED_TRUNC_F32X4_S = "i32x4.relaxed_trunc_f32x4_s", - .I32X4_RELAXED_TRUNC_F32X4_U = "i32x4.relaxed_trunc_f32x4_u", .I32X4_RELAXED_TRUNC_F64X2_S_ZERO = "i32x4.relaxed_trunc_f64x2_s_zero", - .I32X4_RELAXED_TRUNC_F64X2_U_ZERO = "i32x4.relaxed_trunc_f64x2_u_zero", .F32X4_RELAXED_MADD = "f32x4.relaxed_madd", - .F32X4_RELAXED_NMADD = "f32x4.relaxed_nmadd", .F64X2_RELAXED_MADD = "f64x2.relaxed_madd", - .F64X2_RELAXED_NMADD = "f64x2.relaxed_nmadd", .I8X16_RELAXED_LANESELECT = "i8x16.relaxed_laneselect", - .I16X8_RELAXED_LANESELECT = "i16x8.relaxed_laneselect", .I32X4_RELAXED_LANESELECT = "i32x4.relaxed_laneselect", - .I64X2_RELAXED_LANESELECT = "i64x2.relaxed_laneselect", .F32X4_RELAXED_MIN = "f32x4.relaxed_min", - .F32X4_RELAXED_MAX = "f32x4.relaxed_max", .F64X2_RELAXED_MIN = "f64x2.relaxed_min", - .F64X2_RELAXED_MAX = "f64x2.relaxed_max", .I16X8_RELAXED_Q15MULR_S = "i16x8.relaxed_q15mulr_s", - .I16X8_RELAXED_DOT_I8X16_I7X16_S = "i16x8.relaxed_dot_i8x16_i7x16_s", .I32X4_RELAXED_DOT_I8X16_I7X16_ADD_S = "i32x4.relaxed_dot_i8x16_i7x16_add_s", - - // threads / atomics (0xFE) - .MEMORY_ATOMIC_NOTIFY = "memory.atomic.notify", .MEMORY_ATOMIC_WAIT32 = "memory.atomic.wait32", - .MEMORY_ATOMIC_WAIT64 = "memory.atomic.wait64", .ATOMIC_FENCE = "atomic.fence", - .I32_ATOMIC_LOAD = "i32.atomic.load", .I64_ATOMIC_LOAD = "i64.atomic.load", - .I32_ATOMIC_LOAD8_U = "i32.atomic.load8_u", .I32_ATOMIC_LOAD16_U = "i32.atomic.load16_u", - .I64_ATOMIC_LOAD8_U = "i64.atomic.load8_u", .I64_ATOMIC_LOAD16_U = "i64.atomic.load16_u", - .I64_ATOMIC_LOAD32_U = "i64.atomic.load32_u", .I32_ATOMIC_STORE = "i32.atomic.store", - .I64_ATOMIC_STORE = "i64.atomic.store", .I32_ATOMIC_STORE8 = "i32.atomic.store8", - .I32_ATOMIC_STORE16 = "i32.atomic.store16", .I64_ATOMIC_STORE8 = "i64.atomic.store8", - .I64_ATOMIC_STORE16 = "i64.atomic.store16", .I64_ATOMIC_STORE32 = "i64.atomic.store32", - .I32_ATOMIC_RMW_ADD = "i32.atomic.rmw.add", .I64_ATOMIC_RMW_ADD = "i64.atomic.rmw.add", - .I32_ATOMIC_RMW8_ADD_U = "i32.atomic.rmw8.add_u", .I32_ATOMIC_RMW16_ADD_U = "i32.atomic.rmw16.add_u", - .I64_ATOMIC_RMW8_ADD_U = "i64.atomic.rmw8.add_u", .I64_ATOMIC_RMW16_ADD_U = "i64.atomic.rmw16.add_u", - .I64_ATOMIC_RMW32_ADD_U = "i64.atomic.rmw32.add_u", .I32_ATOMIC_RMW_SUB = "i32.atomic.rmw.sub", - .I64_ATOMIC_RMW_SUB = "i64.atomic.rmw.sub", .I32_ATOMIC_RMW8_SUB_U = "i32.atomic.rmw8.sub_u", - .I32_ATOMIC_RMW16_SUB_U = "i32.atomic.rmw16.sub_u", .I64_ATOMIC_RMW8_SUB_U = "i64.atomic.rmw8.sub_u", - .I64_ATOMIC_RMW16_SUB_U = "i64.atomic.rmw16.sub_u", .I64_ATOMIC_RMW32_SUB_U = "i64.atomic.rmw32.sub_u", - .I32_ATOMIC_RMW_AND = "i32.atomic.rmw.and", .I64_ATOMIC_RMW_AND = "i64.atomic.rmw.and", - .I32_ATOMIC_RMW8_AND_U = "i32.atomic.rmw8.and_u", .I32_ATOMIC_RMW16_AND_U = "i32.atomic.rmw16.and_u", - .I64_ATOMIC_RMW8_AND_U = "i64.atomic.rmw8.and_u", .I64_ATOMIC_RMW16_AND_U = "i64.atomic.rmw16.and_u", - .I64_ATOMIC_RMW32_AND_U = "i64.atomic.rmw32.and_u", .I32_ATOMIC_RMW_OR = "i32.atomic.rmw.or", - .I64_ATOMIC_RMW_OR = "i64.atomic.rmw.or", .I32_ATOMIC_RMW8_OR_U = "i32.atomic.rmw8.or_u", - .I32_ATOMIC_RMW16_OR_U = "i32.atomic.rmw16.or_u", .I64_ATOMIC_RMW8_OR_U = "i64.atomic.rmw8.or_u", - .I64_ATOMIC_RMW16_OR_U = "i64.atomic.rmw16.or_u", .I64_ATOMIC_RMW32_OR_U = "i64.atomic.rmw32.or_u", - .I32_ATOMIC_RMW_XOR = "i32.atomic.rmw.xor", .I64_ATOMIC_RMW_XOR = "i64.atomic.rmw.xor", - .I32_ATOMIC_RMW8_XOR_U = "i32.atomic.rmw8.xor_u", .I32_ATOMIC_RMW16_XOR_U = "i32.atomic.rmw16.xor_u", - .I64_ATOMIC_RMW8_XOR_U = "i64.atomic.rmw8.xor_u", .I64_ATOMIC_RMW16_XOR_U = "i64.atomic.rmw16.xor_u", - .I64_ATOMIC_RMW32_XOR_U = "i64.atomic.rmw32.xor_u", .I32_ATOMIC_RMW_XCHG = "i32.atomic.rmw.xchg", - .I64_ATOMIC_RMW_XCHG = "i64.atomic.rmw.xchg", .I32_ATOMIC_RMW8_XCHG_U = "i32.atomic.rmw8.xchg_u", - .I32_ATOMIC_RMW16_XCHG_U = "i32.atomic.rmw16.xchg_u", .I64_ATOMIC_RMW8_XCHG_U = "i64.atomic.rmw8.xchg_u", - .I64_ATOMIC_RMW16_XCHG_U = "i64.atomic.rmw16.xchg_u", .I64_ATOMIC_RMW32_XCHG_U = "i64.atomic.rmw32.xchg_u", - .I32_ATOMIC_RMW_CMPXCHG = "i32.atomic.rmw.cmpxchg", .I64_ATOMIC_RMW_CMPXCHG = "i64.atomic.rmw.cmpxchg", - .I32_ATOMIC_RMW8_CMPXCHG_U = "i32.atomic.rmw8.cmpxchg_u", .I32_ATOMIC_RMW16_CMPXCHG_U = "i32.atomic.rmw16.cmpxchg_u", - .I64_ATOMIC_RMW8_CMPXCHG_U = "i64.atomic.rmw8.cmpxchg_u", .I64_ATOMIC_RMW16_CMPXCHG_U = "i64.atomic.rmw16.cmpxchg_u", - .I64_ATOMIC_RMW32_CMPXCHG_U = "i64.atomic.rmw32.cmpxchg_u", -} diff --git a/core/rexcode/wasm/module/module.odin b/core/rexcode/wasm/module/module.odin deleted file mode 100644 index f27e819d3..000000000 --- a/core/rexcode/wasm/module/module.odin +++ /dev/null @@ -1,281 +0,0 @@ -package rexcode_wasm_module - -import "base:runtime" -import "core:fmt" -import "core:rexcode/wasm" - -WASM_MAGIC :: u32(0x6d736100) // "\0asm" as a little-endian u32 -WASM_VERSION :: u32(1) - -Section_Id :: enum u8 { // Binary section ids (WebAssembly core spec §5.5.2). - CUSTOM = 0, - TYPE = 1, - IMPORT = 2, - FUNCTION = 3, - TABLE = 4, - MEMORY = 5, - GLOBAL = 6, - EXPORT = 7, - START = 8, - ELEMENT = 9, - CODE = 10, - DATA = 11, - DATA_COUNT = 12, -} - -Section :: struct { - id: Section_Id, - offset: u32, // file offset of the section *contents* - size: u32, // contents length in bytes - count: u32, // element count - name: string, // custom-section name (borrowed) -} - -External_Kind :: enum u8 { - FUNC = 0, - TABLE = 1, - MEMORY = 2, - GLOBAL = 3, -} - -@(rodata) -external_kind_string := [External_Kind]string{ - .FUNC = "func", - .TABLE = "table", - .MEMORY = "memory", - .GLOBAL = "global", -} - -Func_Type :: struct { - params: []wasm.Value_Type, - results: []wasm.Value_Type, -} - -Import :: struct { - kind: External_Kind, - module_name: string, // borrowed - field_name: string, // borrowed - index: u32, // typeidx for FUNC, 0 for other kinds -} - -Export :: struct { - kind: External_Kind, - name: string, // borrowed - index: u32, -} - -// A compressed run of declared locals (e.g. `3 x i32`) -Local_Group :: struct { - count: u32, - type: wasm.Value_Type, -} - -// A function in the module's function index space. -// Imported functions occupy the low indices, followed by the module-defined functions. -Function :: struct { - func_index: u32, - type_index: u32, - type: Func_Type, // resolved signature ({} if the type id was out of range) - imported: bool, - exported: bool, - name: string, // export / name-section / import field (borrowed) - import_module: string, // borrowed, "" for defined functions - import_field: string, // borrowed, "" for defined functions - - // defined functions only: - locals: []Local_Group, - body_offset: u32, // file offset of the instruction stream - body_size: u32, // instruction-stream length in bytes -} - -Module :: struct { - version: u32, - sections: []Section, - customs: []Custom_Section, - types: []Func_Type, - imports: []Import, - functions: []Function, // whole function index space (imports + defined) - exports: []Export, - start: i64, // -1 if absent, else the start funcidx - - reloc_groups: []Reloc_Group, - - data: []u8, // borrowed reference to the whole file (body decode reads from it) - - allocator: runtime.Allocator, -} - - - -// ----------------------------------------------------------------------------- -// Custom Section Layout -// ----------------------------------------------------------------------------- - -Custom_Section :: struct { - section: Section, - payload: []byte, // borrowed, m.data[section.offset:][:section.size] - variant: union { - Custom_Section_Name, - Custom_Section_Target_Features, - }, -} - -Custom_Section_Name_Function :: struct { - id: u32, - name: string, // borrowed -} - -Custom_Section_Name_Local :: struct { - idx: u32, - name: string, // borrowed -} - -Custom_Section_Name_Function_Locals :: struct { - func_idx: u32, - locals: []Custom_Section_Name_Local, -} - -Custom_Section_Name :: struct { - module_name: string, - functions: []Custom_Section_Name_Function, - locals: []Custom_Section_Name_Function_Locals, -} - -Custom_Section_Target_Feature_Prefix :: enum u8 { - Used = '+', - Disallowed = '-', - Required = '=', -} - -Custom_Section_Target_Feature :: struct { - prefix: Custom_Section_Target_Feature_Prefix, - feature: string, // borrowed -} - - -Custom_Section_Target_Features :: struct { - features: []Custom_Section_Target_Feature, -} - - -@(require_results) -section_name :: #force_inline proc "contextless" (id: Section_Id) -> string { - switch id { - case .CUSTOM: return "custom" - case .TYPE: return "type" - case .IMPORT: return "import" - case .FUNCTION: return "function" - case .TABLE: return "table" - case .MEMORY: return "memory" - case .GLOBAL: return "global" - case .EXPORT: return "export" - case .START: return "start" - case .ELEMENT: return "element" - case .CODE: return "code" - case .DATA: return "data" - case .DATA_COUNT: return "data.count" - } - return "unknown" -} - -@(require_results) -valtype_name :: #force_inline proc "contextless" (t: wasm.Value_Type) -> string { - switch t { - case .I32: return "i32" - case .I64: return "i64" - case .F32: return "f32" - case .F64: return "f64" - case .V128: return "v128" - case .FUNCREF: return "funcref" - case .EXTERNREF: return "externref" - } - return "?" -} - -@(require_results) -external_kind_name :: #force_inline proc "contextless" (k: External_Kind) -> string { - switch k { - case .FUNC: return "func" - case .TABLE: return "table" - case .MEMORY: return "memory" - case .GLOBAL: return "global" - } - return "?" -} - - - -@(require_results) -module_name :: proc "contextless" (m: Module) -> (string, bool) { - for c in m.customs { - #partial switch v in c.variant { - case Custom_Section_Name: - if v.module_name != "" { - return v.module_name, true - } - } - } - return "", false -} - -@(require_results) -count_import_kind :: proc "contextless" (m: Module, k: External_Kind) -> u32 { - n: u32 = 0 - for imp in m.imports { - if imp.kind == k { - n += 1 - } - } - return n -} - -@(require_results) -block_sig :: proc "contextless" (m: Module, imm: i64) -> (params: int, results: int) { - if imm < 0 { - if imm == i64(wasm.Block_Type.EMPTY) { - return 0, 0 - } - return 0, 1 // single value-type result - } - if int(imm) < len(m.types) { - t := m.types[imm] - return len(t.params), len(t.results) - } - return 0, 0 -} - - - -// Stack arity (operands consumed, results produced) for non-control operators, -// plus the control operators that flow through the plain path. Block/loop/if -// are handled directly by `wat_fold_region`. -@(require_results) -instruction_arity :: proc(m: Module, inst: wasm.Instruction) -> (inputs: int, outputs: int) { - e := &wasm.ENCODING_TABLE[inst.mnemonic] - inputs, outputs = int(e.inputs), int(e.outputs) - if inputs < 0 || outputs < 0 { - #partial switch inst.mnemonic { - case .CALL, .RETURN_CALL: - if int(inst.ops[0].index) < len(m.functions) { - t := m.functions[inst.ops[0].index].type - return len(t.params), len(t.results) - } - return 0, 0 - case .CALL_INDIRECT, .RETURN_CALL_INDIRECT: - if int(inst.ops[0].index) < len(m.types) { - t := m.types[inst.ops[0].index] - return len(t.params) + 1, len(t.results) - } - return 1, 0 - - case .RETURN: - if int(inst.ops[0].index) < len(m.types) { - t := m.types[inst.ops[0].index] - return len(t.results), 0 - } - return 0, 0 - } - fmt.panicf("Unknown optional arity handling %v", inst.mnemonic) - } - return -} diff --git a/core/rexcode/wasm/module/parse.odin b/core/rexcode/wasm/module/parse.odin deleted file mode 100644 index a77a07449..000000000 --- a/core/rexcode/wasm/module/parse.odin +++ /dev/null @@ -1,522 +0,0 @@ -package rexcode_wasm_module - -import "base:runtime" -import "core:strings" -import "core:rexcode/wasm" - -Parse_Error :: enum { - NONE = 0, - TRUNCATED, - BAD_MAGIC, - BAD_TYPE_FORM, // a functype did not start with 0x60 - BAD_SECTION, // section contents extend past the section size - BAD_ULEB, // ULEB number didn't stop after 10 bytes -} - -Reader_Error :: union #shared_nil { - Parse_Error, - runtime.Allocator_Error, -} - -Reader :: struct { - data: []u8, - off: u32, -} - -@(require_results) -reader :: proc(data: []u8, off: u32) -> Reader { - return Reader{data = data, off = off} -} - -@(require_results) -rd_byte :: proc(r: ^Reader) -> (u8, Parse_Error) { - if r.off >= u32(len(r.data)) { - return 0, .TRUNCATED - } - b := r.data[r.off] - r.off += 1 - return b, .NONE -} - -@(require_results) -rd_u32le_block :: proc(r: ^Reader) -> (u32, Parse_Error) { - if r.off + 4 > u32(len(r.data)) { - return 0, .TRUNCATED - } - v := u32(r.data[r.off]) | - u32(r.data[r.off+1])<<8 | - u32(r.data[r.off+2])<<16 | - u32(r.data[r.off+3])<<24 - r.off += 4 - return v, .NONE -} - -@(require_results) -rd_uleb :: proc(r: ^Reader) -> (u64, Parse_Error) { - shift: uint = 0 - value: u64 = 0 - for _ in 0..<10 { - if r.off >= u32(len(r.data)) { - return 0, .TRUNCATED - } - b := r.data[r.off] - r.off += 1 - value |= u64(b & 0x7F) << shift - if b & 0x80 == 0 { - return value, .NONE - } - shift += 7 - } - return 0, .BAD_ULEB -} - -// Signed-LEB128 reader. -@(require_results) -rd_sleb :: proc(r: ^Reader) -> (i64, Parse_Error) { - shift: uint = 0 - value: i64 = 0 - b: u8 = 0 - for _ in 0..<10 { - if r.off >= u32(len(r.data)) { - return 0, .TRUNCATED - } - b = r.data[r.off] - r.off += 1 - value |= i64(b & 0x7F) << shift - shift += 7 - if b & 0x80 == 0 { - break - } - } - if shift < 64 && (b & 0x40) != 0 { - value |= -(i64(1) << shift) - } - return value, .NONE -} - - -@(require_results) -rd_u32 :: proc(r: ^Reader) -> (u32, Parse_Error) { - v, err := rd_uleb(r) - return u32(v), err -} - -@(require_results) -rd_name :: proc(r: ^Reader) -> (val: string, err: Parse_Error) { - n := rd_u32(r) or_return - if r.off + n > u32(len(r.data)) { - err = .TRUNCATED - return - } - val = string(r.data[r.off:][:n]) - r.off += n - return -} - -@(require_results) -rd_valtype_vec :: proc(r: ^Reader, allocator: runtime.Allocator) -> (out: []wasm.Value_Type, err: Reader_Error) { - n := rd_u32(r) or_return - - // now actually parse it - out = make([]wasm.Value_Type, int(n), allocator) or_return - for &v in out { - v = wasm.Value_Type(rd_byte(r) or_return) - } - return -} - -@(require_results) -rd_limits :: proc(r: ^Reader) -> (min: u64, max: Maybe(u64), err: Parse_Error) { - flags := rd_byte(r) or_return - min = rd_uleb(r) or_return - if flags & 0x01 != 0 { - max = rd_uleb(r) or_return - } - return -} - - -@(require_results) -parse :: proc(data: []u8, allocator := context.allocator) -> (m: Module, err: Reader_Error) { - context.allocator = allocator - - m.data = data - m.start = -1 - m.allocator = allocator - - r := reader(data, 0) - if (rd_u32le_block(&r) or_else 0) != WASM_MAGIC { - return m, .BAD_MAGIC - } - m.version = rd_u32le_block(&r) or_return - - secs: [dynamic]Section - for r.off < u32(len(data)) { - id := Section_Id(rd_byte(&r) or_return) - size := rd_u32(&r) or_return - content := r.off - if content + size > u32(len(data)) { - return m, .BAD_SECTION - } - - sec := Section{id = id, offset = content, size = size} - switch id { - case .CUSTOM: - sub := reader(data, content) - sec.name = rd_name(&sub) or_return - case .START: - // funcidx, no vector count - case .TYPE, .IMPORT, .FUNCTION, .TABLE, .MEMORY, .GLOBAL, - .EXPORT, .ELEMENT, .CODE, .DATA, .DATA_COUNT: - sub := reader(data, content) - sec.count = rd_u32(&sub) or_return - } - append(&secs, sec) or_return - r.off = content + size - } - m.sections = secs[:] - - - func_typeidx: []u32 - codes: []Code_Body - - for &sec in m.sections { - s := reader(data, sec.offset) - #partial switch sec.id { - case .TYPE: m.types = parse_types (&s, allocator) or_return - case .IMPORT: m.imports = parse_imports (&s, allocator) or_return - case .FUNCTION: func_typeidx = parse_function_section(&s, allocator) or_return - case .EXPORT: m.exports = parse_exports (&s, allocator) or_return - case .CODE: codes = parse_code (&s, allocator) or_return - case .START: m.start = i64(rd_u32(&s) or_return) - } - } - - parse_custom_sections(&m, allocator) or_return - - build_functions(&m, func_typeidx, codes, allocator) or_return - apply_name_section(&m) - - m.reloc_groups = parse_relocations(m, allocator) or_return - - return -} - -@(require_results) -parse_types :: proc(r: ^Reader, allocator: runtime.Allocator) -> (out: []Func_Type, err: Reader_Error) { - n := rd_u32(r) or_return - out = make([]Func_Type, int(n), allocator) or_return - for &func in out { - form := rd_byte(r) or_return - if form != 0x60 { - return out, .BAD_TYPE_FORM - } - func.params = rd_valtype_vec(r, allocator) or_return - func.results = rd_valtype_vec(r, allocator) or_return - } - return -} - -@(require_results) -parse_imports :: proc(r: ^Reader, allocator: runtime.Allocator) -> (out: []Import, err: Reader_Error) { - n := rd_u32(r) or_return - out = make([]Import, int(n), allocator) or_return - for &imp in out { - imp.module_name = rd_name(r) or_return - imp.field_name = rd_name(r) or_return - imp.kind = External_Kind(rd_byte(r) or_return) - switch imp.kind { - case .FUNC: - imp.index = rd_u32(r) or_return - case .TABLE: - _ = rd_byte(r) or_return // reftype - _, _ = rd_limits(r) or_return - case .MEMORY: - _, _ = rd_limits(r) or_return - case .GLOBAL: - _ = rd_byte(r) or_return // valtype - _ = rd_byte(r) or_return // mutability - } - } - return -} - -@(require_results) -parse_function_section :: proc(r: ^Reader, allocator: runtime.Allocator) -> (out: []u32, err: Reader_Error) { - n := rd_u32(r) or_return - out = make([]u32, int(n), allocator) or_return - for &idx in out { - idx = rd_u32(r) or_return - } - return -} - -@(require_results) -parse_exports :: proc(r: ^Reader, allocator: runtime.Allocator) -> (out: []Export, err: Reader_Error) { - n := rd_u32(r) or_return - out = make([]Export, int(n), allocator) or_return - for &e in out { - e.name = rd_name(r) or_return - e.kind = External_Kind(rd_byte(r) or_return) - e.index = rd_u32(r) or_return - } - return -} - -Code_Body :: struct { - locals: []Local_Group, - body_offset: u32, - body_size: u32, -} - -@(require_results) -parse_code :: proc(r: ^Reader, allocator: runtime.Allocator) -> (out: []Code_Body, err: Reader_Error) { - n := rd_u32(r) or_return - out = make([]Code_Body, int(n), allocator) or_return - for &code_body in out { - total := rd_u32(r) or_return - body_start := r.off - body_end := body_start + total - - nl := rd_u32(r) or_return - locals := make([]Local_Group, int(nl), allocator) or_return - for &local in locals { - cnt := rd_u32(r) or_return - t := wasm.Value_Type(rd_byte(r) or_return) - local = Local_Group{count = cnt, type = t} - } - code_body = Code_Body{ - locals = locals, - body_offset = r.off, - body_size = body_end > r.off ? body_end - r.off : 0, - } - r.off = body_end // jump past the expression to the next entry - } - return -} - -@(require_results) -parse_custom_sections :: proc(m: ^Module, allocator: runtime.Allocator) -> Reader_Error { - custom_count := 0 - for &sec in m.sections { - if sec.id == .CUSTOM { - custom_count += 1 - } - } - m.customs = make([]Custom_Section, custom_count, allocator) or_return - custom_index := 0 - for &sec in m.sections { - if sec.id != .CUSTOM { - continue - } - - custom := &m.customs[custom_index] - custom_index += 1 - - custom.section = sec - - custom.payload = m.data[sec.offset:][:sec.size] - - r := reader(custom.payload, 0) - sec_name := rd_name(&r) or_continue - assert(sec_name == sec.name) - - if strings.has_prefix(sec.name, ".debug_") { - // DWARF debug stuff - } else if strings.has_prefix(sec.name, "reloc.") { - // other relocation stuff - } - - custom_block: switch sec.name { - case "linking": - // not yet handled - case "producers": - // not yet handled - case "dynlink.0": - // not yet handled - case "external_debug_info", "sourceMappingURL": - // not yet handled, but debugging related - case "metadata.code.branch_hint": - // not yet handled - case "name": - cname: Custom_Section_Name - defer custom.variant = cname - - for r.off < u32(len(r.data)) { - id := rd_byte(&r) or_return - size := rd_u32(&r) or_return - end_off := r.off+size - defer r.off = end_off - - switch id { - case 0: // module - cname.module_name = rd_name(&r) or_return - case 1: // functions - count := rd_u32(&r) or_return - cname.functions = make([]Custom_Section_Name_Function, count, allocator) or_return - for &func in cname.functions { - func.id = rd_u32(&r) or_return - func.name = rd_name(&r) or_return - } - case 2: // locals - count := rd_u32(&r) or_return - - cname.locals = make([]Custom_Section_Name_Function_Locals, count, allocator) or_return - - for &local_func in cname.locals { - local_func.func_idx = rd_u32(&r) or_return - local_count := rd_u32(&r) or_return - local_func.locals = make([]Custom_Section_Name_Local, local_count, allocator) or_return - for &local in local_func.locals { - local.idx = rd_u32(&r) or_return - local.name = rd_name(&r) or_return - } - } - } - } - - case "target_features": - target_features: Custom_Section_Target_Features - defer custom.variant = target_features - - count := rd_u32(&r) or_return - target_features.features = make([]Custom_Section_Target_Feature, count, allocator) or_return - - for &feature in target_features.features { - feature.prefix = Custom_Section_Target_Feature_Prefix(rd_byte(&r) or_return) - feature.feature = rd_name(&r) or_return - } - } - } - - return nil -} - -@(require_results) -build_functions :: proc(m: ^Module, func_typeidx: []u32, codes: []Code_Body, allocator: runtime.Allocator) -> runtime.Allocator_Error { - num_imports := 0 - for imp in m.imports { - if imp.kind == .FUNC { - num_imports += 1 - } - } - total := num_imports + len(func_typeidx) - if total == 0 { - return nil - } - - funcs := make([]Function, total, allocator) or_return - - idx := 0 - for imp in m.imports { - (imp.kind == .FUNC) or_continue - f := Function{ - func_index = u32(idx), - type_index = imp.index, - imported = true, - name = imp.field_name, - import_module = imp.module_name, - import_field = imp.field_name, - } - if int(imp.index) < len(m.types) { - f.type = m.types[imp.index] - } - funcs[idx] = f - idx += 1 - } - - for tidx, i in func_typeidx { - fi := num_imports + i - f := Function{ - func_index = u32(fi), - type_index = tidx, - imported = false, - } - if int(tidx) < len(m.types) { - f.type = m.types[tidx] - } - if i < len(codes) { - c := &codes[i] - f.locals = c.locals - f.body_offset = c.body_offset - f.body_size = c.body_size - } - funcs[fi] = f - } - - for e in m.exports { - if e.kind == .FUNC && int(e.index) < total && funcs[e.index].name == "" { - f := &funcs[e.index] - f.name = e.name - f.exported = true - } - } - - m.functions = funcs - return nil -} - -// Override function names with debug names from the "name" custom section's function-names subsection (id 1), if it exists. -apply_name_section :: proc(m: ^Module) { - for sec in m.sections { - if sec.id != .CUSTOM || sec.name != "name" { - continue - } - - r := reader(m.data, sec.offset) - _ = rd_name(&r) or_break // re-read the section name to position at the subsections - end := sec.offset + sec.size - - for r.off < end { - sub_id := rd_byte(&r) or_break - sub_size := rd_u32(&r) or_break - payload_end := r.off + sub_size - if sub_id == 1 { - count := rd_u32(&r) or_break - for _ in 0.. 0 { strings.write_string(sb, ", ") } - strings.write_string(sb, valtype_name(p)) - } - strings.write_string(sb, ") -> ") - strings.write_byte(sb, '(') - for rt, i in t.results { - if i > 0 { strings.write_string(sb, ", ") } - strings.write_string(sb, valtype_name(rt)) - } - strings.write_byte(sb, ')') - } - - - strings.write_string(sb, "module\n") - strings.write_string(sb, " version: ") - strings.write_u64(sb, u64(m.version)) - strings.write_byte(sb, '\n') - for c in m.customs { - #partial switch v in c.variant { - case Custom_Section_Name: - if v.module_name != "" { - strings.write_string(sb, " file: ") - strings.write_quoted_string(sb, v.module_name) - strings.write_byte(sb, '\n') - } - } - } - - - label_names: map[u32]string - defer delete(label_names) - for f in m.functions { - if f.name != "" { - label_names[f.func_index] = f.name - } - } - - // sections - for sec in m.sections { - write_padded :: proc(sb: ^strings.Builder, s: string, width: int) { - strings.write_string(sb, s) - for _ in len(s).. 0 { - // fmt.sbprintf(sb, " functions:\n") - // for f in v.functions { - // fmt.sbprintf(sb, " [%d] %q\n", f.id, f.name) - // } - // } - // if len(v.locals) > 0 { - // fmt.sbprintf(sb, " locals:\n") - // for fl in v.locals { - // fmt.sbprintf(sb, " [%d] function\n", fl.func_idx) - // for local in fl.locals { - // fmt.sbprintf(sb, " [%d] %q\n", local.idx, local.name) - // } - // } - // } - case Custom_Section_Target_Features: - for f in v.features { - fmt.sbprintf(sb, " \"%c%s\"\n", u8(f.prefix), f.feature) - } - } - break - } - - case .DATA: - r := reader(section_data, 0) - count := rd_u32(&r) or_break section_printing - assert(count == sec.count) - for i in 0.. 0 { - strings.write_string(sb, ".import\n") - for imp in m.imports { - f := &m.functions[imp.index] - fmt.sbprintf(sb, " %s %q %q", external_kind_string[imp.kind], imp.module_name, imp.field_name) - write_func_type(sb, f.type) - strings.write_string(sb, "\n") - } - strings.write_string(sb, "\n") - } - - if len(m.exports) > 0 { - strings.write_string(sb, ".export\n") - for e in m.exports { - f := &m.functions[e.index] - fmt.sbprintf(sb, " %s %q", external_kind_string[e.kind], e.name) - write_func_type(sb, f.type) - strings.write_string(sb, "\n") - } - strings.write_string(sb, "\n") - } - - // if len(m.types) > 0 { - // strings.write_string(sb, "\n.") - // strings.write_string(sb, section_name(.TYPE)) - // strings.write_string(sb, "\n") - // for t, i in m.types { - // strings.write_string(sb, " [") - // strings.write_u64(sb, u64(i)) - // strings.write_string(sb, "] ") - // write_func_type(sb, t) - // strings.write_byte(sb, '\n') - // } - // } - - - func_relocs := relocations_from_section_id(m.reloc_groups, .FUNCTION) - - for f in m.functions { - if f.exported { - strings.write_string(sb, "export ") - } - if f.name != "" { - strings.write_string(sb, external_kind_string[.FUNC]) - strings.write_byte(sb, ' ') - strings.write_quoted_string(sb, f.name) - } - write_func_type(sb, f.type) - - - if f.imported { - strings.write_string(sb, "\n import ") - strings.write_quoted_string(sb, f.import_module) - strings.write_string(sb, " ") - strings.write_quoted_string(sb, f.import_field) - strings.write_string(sb, "\n") - continue - } - - strings.write_byte(sb, '\n') - - if len(f.locals) != 0 { - strings.write_string(sb, " locals:") - for g in f.locals { - strings.write_byte(sb, ' ') - if g.count > 1 { - strings.write_u64(sb, u64(g.count)) - strings.write_string(sb, "x") - } - strings.write_string(sb, valtype_name(g.type)) - } - strings.write_byte(sb, '\n') - } - - if f.body_size != 0 { - sbprint_function(sb, m, f, func_relocs, &label_names) - } - } -} - -// Disassemble and print one function body. Returns the empty string for -// imported functions (which have no body). -sbprint_function :: proc(sb: ^strings.Builder, m: Module, f: Function, relocs: []wasm.Relocation, label_names: ^map[u32]string) -> string { - if f.imported || f.body_size == 0 { - return "" - } - body := m.data[f.body_offset:][:f.body_size] - - insts: [dynamic]wasm.Instruction - info: [dynamic]wasm.Instruction_Info - errs: [dynamic]wasm.Error - defer delete(insts) - defer delete(info) - defer delete(errs) - - wasm.decode(body, relocs, &insts, &info, &errs) - wasm.sbprint(sb, insts[:], info[:], label_names=label_names) - return strings.to_string(sb^) -} diff --git a/core/rexcode/wasm/module/print_wat.odin b/core/rexcode/wasm/module/print_wat.odin deleted file mode 100644 index 1f9e719aa..000000000 --- a/core/rexcode/wasm/module/print_wat.odin +++ /dev/null @@ -1,708 +0,0 @@ -package rexcode_wasm_module - -import "base:runtime" -import "core:strings" -import "core:os" -import "core:io" -import "core:fmt" -import wasm "core:rexcode/wasm" - -// ============================================================================= -// WebAssembly TEXT (WAT) PRINTER -// ============================================================================= -// -// `print.odin` emits a compact, custom *disassembly* of a parsed `Module`. -// This file emits the standard WebAssembly text format (`.wat`): the nested -// s-expression module that `wat2wasm` consumes and that `wasm2wat` produces. -// -// (module -// (type (;0;) (func (param i32 i32) (result i32))) -// (func $add (type 0) (param i32 i32) (result i32) -// (i32.add -// (local.get 0) -// (local.get 1))) -// (export "add" (func 0))) -// -// Folding -// ------- -// A WASM code section is a *linear* stack-machine stream; WAT bodies are -// *folded* operator trees. Reconstructing the trees needs each instruction's -// stack arity (operands consumed / results produced), which the linear stream -// does not carry, so it is recovered here: -// * fixed for the numeric / memory / variable / parametric / reference / -// bulk-memory operators (`instruction_arity`), -// * computed from the module's own type table for `call` (callee signature), -// `call_indirect` (the referenced type), and `block`/`loop`/`if` results. -// -// The folder is the algorithm `wat2wasm`/`wasm2wat` use: keep a stack of -// partially built operand trees; an operator with N operands claims the top N -// trees as children; an operator that yields no value (a "statement", e.g. a -// store / drop / void call) flushes the stack so nothing is reordered. Where -// folding would be unsound (not enough operands contiguously on top, e.g. -// across a side-effecting statement) it degrades to flat folded exprs such as -// `(i32.add)` whose operands are the preceding sibling exprs -- still valid, -// still correct, just less nested. SIMD / atomic operators are conservatively -// treated as statements (rendered flat) rather than risk an incorrect tree. -// -// Index operands print numerically (`call 3`, `local.get 0`): the decoder -// marks `call` targets `symbolic` (which would print `$3`); that flag is -// cleared per-instruction so references always resolve. Names from the "name" -// custom section are attached to *definitions* as `$name` (or kept as an index -// comment when the text would not be a legal WAT identifier). -// -// Coverage: TYPE, IMPORT (all four kinds), FUNCTION/CODE, TABLE, MEMORY, -// GLOBAL, EXPORT, START, ELEMENT (active table-0 funcref form), DATA. - -WAT_Options :: struct { - indent_unit: string, // one indentation step; default " " -} - -DEFAULT_WAT_OPTIONS :: WAT_Options{ - indent_unit = " ", -} - -print_wat :: proc(m: Module, file: ^os.File, opts := DEFAULT_WAT_OPTIONS) { - sb := strings.builder_make(context.allocator) - defer strings.builder_destroy(&sb) - sbprint_wat(&sb, m, opts) - os.write_string(file, strings.to_string(sb)) -} - -fprint_wat :: proc(w: io.Writer, m: Module, opts := DEFAULT_WAT_OPTIONS) { - sb := strings.builder_make(context.temp_allocator) - sbprint_wat(&sb, m, opts) - io.write_string(w, strings.to_string(sb)) -} - -// Allocates the result with `allocator` (caller owns it). -aprint_wat :: proc(m: Module, opts := DEFAULT_WAT_OPTIONS, allocator := context.allocator) -> string { - sb := strings.builder_make(allocator) - sbprint_wat(&sb, m, opts) - return strings.to_string(sb) -} - -// Result lives in the temp allocator. -tprint_wat :: proc(m: Module, opts := DEFAULT_WAT_OPTIONS) -> string { - sb := strings.builder_make(context.temp_allocator) - sbprint_wat(&sb, m, opts) - return strings.to_string(sb) -} - -sbprint_wat :: proc(sb: ^strings.Builder, m: Module, opts := DEFAULT_WAT_OPTIONS) { - unit := opts.indent_unit - if unit == "" { - unit = " " - } - - strings.write_string(sb, "(module") - defer strings.write_string(sb, ")\n") - - if name, ok := module_name(m); ok { - if wat_ident_ok(name) { - strings.write_string(sb, " $") - strings.write_string(sb, name) - } else { - fmt.sbprintf(sb, " (;%q;)", name) - } - } - strings.write_byte(sb, '\n') - - for t, i in m.types { - fmt.sbprintf(sb, "%s(type (;%d;) ", unit, i) - wat_write_functype(sb, t) - strings.write_string(sb, ")\n") - } - - wat_write_imports(sb, m, unit) - - func_relocs := relocations_from_section_id(m.reloc_groups, .FUNCTION) - - for f in m.functions { - if f.imported { - continue - } - wat_write_function(sb, m, func_relocs, f, unit) - } - - wat_write_tables (sb, m, unit) - wat_write_memories(sb, m, unit) - wat_write_globals (sb, m, unit) - - for e in m.exports { - fmt.sbprintf(sb, "%s(export %q (%s %d))\n", unit, e.name, external_kind_name(e.kind), e.index) - } - - if m.start >= 0 { - fmt.sbprintf(sb, "%s(start %d)\n", unit, m.start) - } - - wat_write_elements(sb, m, unit) - wat_write_data (sb, m, unit) -} - -wat_write_functype :: proc(sb: ^strings.Builder, t: Func_Type) { - strings.write_string(sb, "(func") - defer strings.write_byte(sb, ')') - if len(t.params) > 0 { - strings.write_string(sb, " (param") - defer strings.write_byte(sb, ')') - for p in t.params { - strings.write_byte(sb, ' ') - strings.write_string(sb, valtype_name(p)) - } - } - if len(t.results) > 0 { - strings.write_string(sb, " (result") - defer strings.write_byte(sb, ')') - for rt in t.results { - strings.write_byte(sb, ' ') - strings.write_string(sb, valtype_name(rt)) - } - } -} - -wat_write_imports :: proc(sb: ^strings.Builder, m: Module, unit: string) -> Parse_Error { - sec: Section - { - found := false - for s in m.sections { - if s.id == .IMPORT { - sec = s - found = true - break - } - } - if !found { - return nil - } - } - - r := reader(m.data, sec.offset) - count := rd_u32(&r) or_return - - fi: u32 - ti: u32 - mi: u32 - gi: u32 - - for _ in 0.. 0 { - strings.write_string(sb, " (param") - defer strings.write_byte(sb, ')') - for p in f.type.params { - strings.write_byte(sb, ' ') - strings.write_string(sb, valtype_name(p)) - } - } - if len(f.type.results) > 0 { - strings.write_string(sb, " (result") - defer strings.write_byte(sb, ')') - for rt in f.type.results { - strings.write_byte(sb, ' ') - strings.write_string(sb, valtype_name(rt)) - } - } - if len(f.locals) > 0 { - strings.write_string(sb, " (local") - defer strings.write_byte(sb, ')') - for g in f.locals { - for _ in 0.. (out: [dynamic]WAT_Node, term: wasm.Mnemonic) { - stack: [dynamic]WAT_Node - defer delete(stack) - - for i^ < len(insts) { - inst := insts[i^] - #partial switch inst.mnemonic { - case .END, .ELSE: - i^ += 1 - append(&out, ..stack[:]) - return out, inst.mnemonic - - case .BLOCK, .LOOP: - _, results := block_sig(m, inst.ops[0].immediate) - head := render_head(inst) - i^ += 1 - body, _ := wat_fold_region(insts, m, i) - node := &WAT_Node{ - kind = .BLOCK if inst.mnemonic == .BLOCK else .LOOP, - head = head, - body = body, - results = results, - } - push_expr(&stack, &out, node, 0, results) - - case .IF: - _, results := block_sig(m, inst.ops[0].immediate) - head := render_head(inst) - i^ += 1 - then_body, t := wat_fold_region(insts, m, i) - else_body: [dynamic]WAT_Node - if t == .ELSE { - else_body, _ = wat_fold_region(insts, m, i) - } - node := &WAT_Node{ - kind = .IF, - head = head, - body = then_body, - els = else_body, - results = results, - } - push_expr(&stack, &out, node, 1, results) // 1 = the condition - - case: - op, res := instruction_arity(m, inst) - node := &WAT_Node{ - kind = .PLAIN, - head = render_head(inst), - results = res, - } - i^ += 1 - push_expr(&stack, &out, node, op, res) - } - } - - append(&out, ..stack[:]) - - return out, .END -} - -push_expr :: proc(stack, out: ^[dynamic]WAT_Node, node: ^WAT_Node, op, res: int) { - if op <= len(stack) { - start := len(stack) - op - append(&node.children, ..stack[start:]) - resize(stack, start) - append(stack, node^) - } else { - append(out, ..stack[:]) - clear(stack) - append(stack, node^) - } - if res == 0 { - append(out, ..stack[:]) - clear(stack) - } -} - -render_head :: proc(inst: wasm.Instruction) -> string { - for j in 0.. bool { - (n.kind == .PLAIN) or_return - for &c in n.children { - wat_node_is_simple(&c) or_return - } - return true -} - -wat_render :: proc(sb: ^strings.Builder, n: ^WAT_Node, level: int, unit: string) { - strings.write_byte(sb, '(') - defer strings.write_byte(sb, ')') - strings.write_string(sb, n.head) - - switch n.kind { - case .PLAIN: - if wat_node_is_simple(n) { - for &c in n.children { - strings.write_byte(sb, ' ') - wat_render(sb, &c, level, unit) - } - } else { - for &c in n.children { - strings.write_byte(sb, '\n') - indent_str(sb, unit, level + 1) - wat_render(sb, &c, level + 1, unit) - } - } - - case .BLOCK, .LOOP: - for &s in n.body { - strings.write_byte(sb, '\n') - indent_str(sb, unit, level + 1) - wat_render(sb, &s, level + 1, unit) - } - - case .IF: - for &c in n.children { - if wat_node_is_simple(&c) { - strings.write_byte(sb, ' ') - wat_render(sb, &c, level, unit) - } else { - strings.write_byte(sb, '\n') - indent_str(sb, unit, level + 1) - wat_render(sb, &c, level + 1, unit) - } - } - strings.write_byte(sb, '\n') - indent_str(sb, unit, level + 1) - { - strings.write_string(sb, "(then") - defer strings.write_byte(sb, ')') - for &s in n.body { - strings.write_byte(sb, '\n') - indent_str(sb, unit, level + 2) - wat_render(sb, &s, level + 2, unit) - } - } - if len(n.els) > 0 { - strings.write_byte(sb, '\n') - indent_str(sb, unit, level + 1) - strings.write_string(sb, "(else") - defer strings.write_byte(sb, ')') - for &s in n.els { - strings.write_byte(sb, '\n') - indent_str(sb, unit, level + 2) - wat_render(sb, &s, level + 2, unit) - } - } - } -} - -wat_write_tables :: proc(sb: ^strings.Builder, m: Module, unit: string) { - idx := count_import_kind(m, .TABLE) - for sec in m.sections { - if sec.id != .TABLE { - continue - } - r := reader(m.data, sec.offset) - count := rd_u32(&r) or_break - for _ in 0.. Parse_Error { - elem_idx := 0 - for sec in m.sections { - if sec.id != .ELEMENT { - continue - } - - relocs := relocations_from_section_id(m.reloc_groups, sec.id) - - r := reader(m.data, sec.offset) - count := rd_u32(&r) or_break - for _ in 0.. Parse_Error { - data_idx := 0 - for sec in m.sections { - if sec.id != .DATA { - continue - } - r := reader(m.data, sec.offset) - count := rd_u32(&r) or_break - for _ in 0.. 0 { strings.write_byte(sb, ' ') } - wat_render(sb, &s, 0, unit) - } -} - -wat_write_id_or_comment :: proc(sb: ^strings.Builder, name: string, index: u32) { - if name != "" && wat_ident_ok(name) { - strings.write_string(sb, " $") - strings.write_string(sb, name) - } else { - fmt.sbprintf(sb, " (;%d;)", index) - } -} - -@(private) -indent_str :: proc(sb: ^strings.Builder, unit: string, level: int) { - for _ in 0.. bool { - if len(s) == 0 { - return false - } - #no_bounds_check for i in 0..', '?', '@', '\\', '^', '_', '`', '|', - '~': - // okay - case: - return false - } - } - return true -} - -wat_write_quoted_string :: proc(sb: ^strings.Builder, bytes: []u8) { - @(rodata, static) - HEX := "0123456789abcdef" - strings.write_byte(sb, '"') - for b in bytes { - switch b { - case '"': strings.write_string(sb, "\\\"") - case '\\': strings.write_string(sb, "\\\\") - case 0x20..<0x7F: strings.write_byte(sb, b) - case: - strings.write_byte(sb, '\\') - strings.write_byte(sb, HEX[b >> 4]) - strings.write_byte(sb, HEX[b & 0xF]) - } - } - strings.write_byte(sb, '"') -} diff --git a/core/rexcode/wasm/module/relocs.odin b/core/rexcode/wasm/module/relocs.odin deleted file mode 100644 index 127d59908..000000000 --- a/core/rexcode/wasm/module/relocs.odin +++ /dev/null @@ -1,103 +0,0 @@ -package rexcode_wasm_module - -import "base:runtime" -import "core:strings" -import wasm "core:rexcode/wasm" - -Reloc_Group :: struct { - target_section: Section_Id, // index of the section these apply to - relocs: []wasm.Relocation, -} - -@(require_results) -parse_relocations :: proc(m: Module, allocator: runtime.Allocator) -> (reloc_groups: []Reloc_Group, err: Reader_Error) { - groups: [dynamic]Reloc_Group - groups.allocator = m.allocator - for sec in m.sections { - if !(sec.id == .CUSTOM && strings.has_prefix(sec.name, "reloc.")) { - continue - } - - r := reader(m.data[sec.offset:][:sec.size], 0) - _ = rd_name(&r) or_return // step past the custom-section name - target := Section_Id(rd_u32(&r) or_return) - count := rd_u32(&r) or_return - - out := make([]wasm.Relocation, int(count), m.allocator) - w := 0 - for _ in 0.. []wasm.Relocation { - for rg in reloc_groups { - if rg.target_section == sec_id { - return rg.relocs - } - } - return nil -} - -@(require_results) -reloc_type_from_wire :: proc(code: u8) -> (wasm.Relocation_Type, bool) { - switch code { - case 0: return .FUNCTION_INDEX_LEB, true // R_WASM_FUNCTION_INDEX_LEB - case 1: return .TABLE_INDEX_SLEB, true // R_WASM_TABLE_INDEX_SLEB - case 2: return .TABLE_INDEX_I32, true // R_WASM_TABLE_INDEX_I32 - case 3: return .MEMORY_ADDR_LEB, true // R_WASM_MEMORY_ADDR_LEB - case 4: return .MEMORY_ADDR_SLEB, true // R_WASM_MEMORY_ADDR_SLEB - case 5: return .MEMORY_ADDR_I32, true // R_WASM_MEMORY_ADDR_I32 - case 6: return .TYPE_INDEX_LEB, true // R_WASM_TYPE_INDEX_LEB - case 7: return .GLOBAL_INDEX_LEB, true // R_WASM_GLOBAL_INDEX_LEB - case 20: return .TABLE_NUMBER_LEB, true // R_WASM_TABLE_NUMBER_LEB - } - return .NONE, false -} - -// MEMORY_ADDR_* (3,4,5) and the *_OFFSET_I32 (8,9) forms carry a trailing -// signed-LEB addend; the index-type relocations do not. -@(require_results) -reloc_has_addend :: proc(code: u8) -> bool { - switch code { - case 3, 4, 5, 8, 9: return true - } - return false -} - -@(require_results) -reloc_field_size :: proc(t: wasm.Relocation_Type) -> u8 { - #partial switch t { - case .TABLE_INDEX_I32, .MEMORY_ADDR_I32: - return 4 // 4-byte LE field - } - return 5 // 5-byte padded (S)LEB field -} diff --git a/core/rexcode/wasm/operands.odin b/core/rexcode/wasm/operands.odin deleted file mode 100644 index a1ea49db1..000000000 --- a/core/rexcode/wasm/operands.odin +++ /dev/null @@ -1,191 +0,0 @@ -// rexcode · Brendan Punsky (dotbmp@github), original author -// Ginger Bill (gingerBill@github) - -package rexcode_wasm - -// ============================================================================= -// WebAssembly OPERANDS -// ============================================================================= -// -// WASM operands are not registers or addressing modes; they are *immediates* -// that follow the opcode in the byte stream: -// -// i32.const 42 IMMEDIATE immediate = 42 (signed LEB128) -// f64.const 3.14 IMMEDIATE immediate = bits(3.14) (8 LE bytes) -// local.get 0 INDEX index = 0, idx_kind = LOCAL (unsigned LEB128) -// call $f INDEX index = funcidx, idx_kind = FUNC -// br 1 INDEX index = 1, idx_kind = LABEL (branch depth) -// i32.load align=2 off=8 MEMARG memarg = {align = 2, offset = 8} -// block (result i32) BLOCK_TYPE block_type = .I32 -// -// Branching in WASM is *structured*: `br`/`br_if`/`br_table` take a relative -// label depth (an unsigned immediate), not a PC-relative byte offset. There -// are therefore no PC-relative relocations and the isa label-inference path is -// not used; the array-index `Label_Definition` machinery is re-exported for -// contract parity but WASM control flow does not consume it. -// -// Relocations *are* real, but for the object-file index spaces (function / -// global / table / type / data / elem indices that a linker fixes up). An -// INDEX operand flagged `symbolic` carries a label id and is emitted as a -// fixed-width 5-byte LEB placeholder plus a Relocation entry. `op_label` -// (required by the contract) produces exactly such a symbolic function index. - -Operand_Kind :: enum u8 { - NONE, - IMMEDIATE, // i32/i64/f32/f64 constant (floats stored as raw bits) - INDEX, // LEB128 unsigned index into one of the index spaces - MEMARG, // load/store alignment + offset pair - BLOCK_TYPE, // block / loop / if signature -} - -// Which index space an INDEX operand addresses. Drives matching, relocation -// type selection, and printer annotation. -Index_Kind :: enum u8 { - NONE, - LOCAL, - GLOBAL, - FUNC, - TYPE, - TABLE, - MEMORY, - LABEL, // br / br_if / br_table relative depth - DATA, - ELEM, -} - -Operand_Flags :: bit_field u8 { - symbolic: bool | 1, // INDEX value is a label id needing a relocation - is_float: bool | 1, // IMMEDIATE holds float bits (vs a signed integer) - _: u8 | 6, -} - -// Load/store immediate: alignment hint (log2 bytes) + static offset. -Memarg :: struct #packed { - offset: u32, - align: u32, -} -#assert(size_of(Memarg) == 8) - -// Block signature. Negative sentinels are the s33 single-byte forms; a -// non-negative value is a type index encoded as a positive signed LEB128. -Block_Type :: enum i64 { - EMPTY = -64, // 0x40 - I32 = -1, // 0x7F - I64 = -2, // 0x7E - F32 = -3, // 0x7D - F64 = -4, // 0x7C - V128 = -5, // 0x7B - FUNCREF = -16, // 0x70 - EXTERNREF = -17, // 0x6F -} - -Operand :: struct #packed { - using _: struct #raw_union { - reg: Register, // REGISTER (vestigial) - memarg: Memarg, // MEMARG - immediate: i64, // IMMEDIATE (int value or float bits) / BLOCK_TYPE (s33) - index: u32, // INDEX (value, or label id when symbolic) - }, - kind: Operand_Kind, - idx_kind: Index_Kind, - size: u8, // value width in bytes where meaningful (4/8) - flags: Operand_Flags, -} -#assert(size_of(Operand) == 12) - -// ----------------------------------------------------------------------------- -// Generic constructors (contract surface) -// ----------------------------------------------------------------------------- - -@(require_results) -op_imm :: #force_inline proc "contextless" (v: i64, size: u8) -> Operand { - return Operand{immediate = v, kind = .IMMEDIATE, size = size} -} - -@(require_results) -op_mem :: #force_inline proc "contextless" (m: Memarg, size: u8 = 0) -> Operand { - return Operand{memarg = m, kind = .MEMARG, size = size} -} - -// Symbolic function reference: emitted as a relocatable funcidx placeholder. -@(require_results) -op_label :: #force_inline proc "contextless" (label_id: u32, size: u8 = 5) -> Operand { - return Operand{index = label_id, kind = .INDEX, idx_kind = .FUNC, size = size, flags = {symbolic = true}} -} - -// ----------------------------------------------------------------------------- -// Numeric constants -// ----------------------------------------------------------------------------- - -@(require_results) -op_i32 :: #force_inline proc "contextless" (v: i32) -> Operand { - return Operand{immediate = i64(v), kind = .IMMEDIATE, size = 4} -} -@(require_results) -op_i64 :: #force_inline proc "contextless" (v: i64) -> Operand { - return Operand{immediate = v, kind = .IMMEDIATE, size = 8} -} -@(require_results) -op_f32 :: #force_inline proc "contextless" (v: f32) -> Operand { - return Operand{immediate = i64(transmute(u32)v), kind = .IMMEDIATE, size = 4, flags = {is_float = true}} -} -@(require_results) -op_f64 :: #force_inline proc "contextless" (v: f64) -> Operand { - return Operand{immediate = transmute(i64)v, kind = .IMMEDIATE, size = 8, flags = {is_float = true}} -} - -// ----------------------------------------------------------------------------- -// Memory argument + block type -// ----------------------------------------------------------------------------- - -@(require_results) -memarg :: #force_inline proc "contextless" (align, offset: u32) -> Memarg { - return Memarg{align = align, offset = offset} -} -@(require_results) -op_memarg :: #force_inline proc "contextless" (align, offset: u32) -> Operand { - return Operand{memarg = Memarg{align = align, offset = offset}, kind = .MEMARG} -} - -@(require_results) -op_blocktype :: #force_inline proc "contextless" (bt: Block_Type) -> Operand { - return Operand{immediate = i64(bt), kind = .BLOCK_TYPE} -} -@(require_results) -op_block_typeidx :: #force_inline proc "contextless" (type_index: u32) -> Operand { - return Operand{immediate = i64(type_index), kind = .BLOCK_TYPE} -} - -// ----------------------------------------------------------------------------- -// Index-space constructors (one per space; all unsigned LEB128 on the wire) -// ----------------------------------------------------------------------------- - -@(require_results) -op_index :: #force_inline proc "contextless" (kind: Index_Kind, value: u32) -> Operand { - return Operand{index = value, kind = .INDEX, idx_kind = kind} -} - -@(require_results) op_local :: #force_inline proc "contextless" (n: u32) -> Operand { return op_index(.LOCAL, n) } -@(require_results) op_global :: #force_inline proc "contextless" (n: u32) -> Operand { return op_index(.GLOBAL, n) } -@(require_results) op_func :: #force_inline proc "contextless" (n: u32) -> Operand { return op_index(.FUNC, n) } -@(require_results) op_type :: #force_inline proc "contextless" (n: u32) -> Operand { return op_index(.TYPE, n) } -@(require_results) op_table :: #force_inline proc "contextless" (n: u32) -> Operand { return op_index(.TABLE, n) } -@(require_results) op_memory :: #force_inline proc "contextless" (n: u32) -> Operand { return op_index(.MEMORY, n) } -@(require_results) op_data :: #force_inline proc "contextless" (n: u32) -> Operand { return op_index(.DATA, n) } -@(require_results) op_elem :: #force_inline proc "contextless" (n: u32) -> Operand { return op_index(.ELEM, n) } - -// Branch label depth (number of enclosing blocks to break out of). -@(require_results) op_labelidx :: #force_inline proc "contextless" (depth: u32) -> Operand { return op_index(.LABEL, depth) } - -// ref.null heap type (encoded as a single value-type byte). -@(require_results) -op_reftype :: #force_inline proc "contextless" (t: Value_Type) -> Operand { - return Operand{immediate = i64(t), kind = .IMMEDIATE, size = 1} -} - -// SIMD lane index (single byte) for extract_lane / replace_lane / load_lane / -// store_lane operators. -@(require_results) -op_lane :: #force_inline proc "contextless" (n: u8) -> Operand { - return Operand{immediate = i64(n), kind = .IMMEDIATE, size = 1} -} diff --git a/core/rexcode/wasm/printer.odin b/core/rexcode/wasm/printer.odin deleted file mode 100644 index 4c002800f..000000000 --- a/core/rexcode/wasm/printer.odin +++ /dev/null @@ -1,351 +0,0 @@ -// rexcode · Brendan Punsky (dotbmp@github), original author -// Ginger Bill (gingerBill@github) - -package rexcode_wasm - -import "core:strings" -import "core:strconv" -import "core:os" -import "core:io" -import "core:rexcode/isa" - -// ============================================================================= -// WebAssembly PRINTER -// ============================================================================= -// -// Emits WebAssembly text-format (WAT) instruction syntax: the folded-stack -// form is not reconstructed (that needs structure the linear stream does not -// carry); instead each instruction prints on its own line as -// -// * -// -// Examples: -// -// i32.const 42 -// local.get 0 -// i32.add -// call 3 -// block (result i32) -// i32.load offset=8 align=2 -// br_table 0 1 2 ; cases 0 1, default 2 -// ref.null func -// f64.const 3.14 -// -// Mnemonic spelling comes from the explicit MNEMONIC_NAMES table (WASM mixes -// '.' and '_' irregularly, e.g. `local.get` vs `i32.trunc_f32_s`). WASM has -// no register file, so register printing is vestigial. - -Token :: isa.Token -Token_Kind :: isa.Token_Kind -Print_Options :: isa.Print_Options -Print_Result :: isa.Print_Result -DEFAULT_PRINT_OPTIONS :: isa.DEFAULT_PRINT_OPTIONS - -@(require_results) -mnemonic_to_string :: proc(m: Mnemonic, lowercase: bool = true, allocator := context.temp_allocator) -> string { - sb := strings.builder_make(allocator) - write_mnemonic(&sb, m, !lowercase) - return strings.to_string(sb) -} - -// ============================================================================= -// Core sbprint -// ============================================================================= - -sbprint :: proc( - sb: ^strings.Builder, - instructions: []Instruction, - inst_info: []Instruction_Info, - options: ^Print_Options = nil, - label_names: ^map[u32]string = nil, -) { - opts := options - if opts == nil { - @(static) defaults := DEFAULT_PRINT_OPTIONS - opts = &defaults - } - - for &inst, i in instructions { - offset := inst_info[i].offset if i < len(inst_info) else u32(0) - - strings.write_string(sb, opts.indent) - if opts.show_offsets { - isa.print_hex(sb, u64(offset), opts) - strings.write_string(sb, ": ") - } - - write_mnemonic(sb, inst.mnemonic, opts.uppercase) - - // br_table prints its case vector followed by the default depth. - #partial switch inst.mnemonic { - case .BR_TABLE: - for t in inst.targets { - strings.write_byte(sb, ' ') - strings.write_u64(sb, u64(t)) - } - strings.write_byte(sb, ' ') - strings.write_u64(sb, u64(inst.ops[0].index)) - case .V128_CONST: - strings.write_string(sb, " i8x16") - for bb in inst.bytes { - strings.write_byte(sb, ' ') - isa.print_hex(sb, u64(bb), opts) - } - case .I8X16_SHUFFLE: - for bb in inst.bytes { - strings.write_byte(sb, ' ') - strings.write_u64(sb, u64(u32(bb))) - } - - case .CALL_INDIRECT: - - case: - for slot in 0.. string { - sb := strings.builder_make(allocator) - sbprint(&sb, instructions, inst_info, options, label_names) - return strings.to_string(sb) -} - -aprintln :: proc( - instructions: []Instruction, inst_info: []Instruction_Info, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, - allocator := context.allocator, -) -> string { - sb := strings.builder_make(allocator) - sbprintln(&sb, instructions, inst_info, options, label_names) - return strings.to_string(sb) -} - -tprint :: proc( - instructions: []Instruction, inst_info: []Instruction_Info, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, -) -> string { - sb := strings.builder_make(context.temp_allocator) - sbprint(&sb, instructions, inst_info, options, label_names) - return strings.to_string(sb) -} - -tprintln :: proc( - instructions: []Instruction, inst_info: []Instruction_Info, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, -) -> string { - sb := strings.builder_make(context.temp_allocator) - sbprintln(&sb, instructions, inst_info, options, label_names) - return strings.to_string(sb) -} - -bprint :: proc( - buf: []u8, - instructions: []Instruction, inst_info: []Instruction_Info, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, -) -> string { - sb := strings.builder_from_bytes(buf) - sbprint(&sb, instructions, inst_info, options, label_names) - return strings.to_string(sb) -} - -bprintln :: proc( - buf: []u8, - instructions: []Instruction, inst_info: []Instruction_Info, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, -) -> string { - sb := strings.builder_from_bytes(buf) - sbprintln(&sb, instructions, inst_info, options, label_names) - return strings.to_string(sb) -} - -fprint :: proc( - fd: ^os.File, - instructions: []Instruction, inst_info: []Instruction_Info, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, -) { - sb := strings.builder_make(context.temp_allocator) - sbprint(&sb, instructions, inst_info, options, label_names) - os.write_string(fd, strings.to_string(sb)) -} - -fprintln :: proc( - fd: ^os.File, - instructions: []Instruction, inst_info: []Instruction_Info, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, -) { - sb := strings.builder_make(context.temp_allocator) - sbprintln(&sb, instructions, inst_info, options, label_names) - os.write_string(fd, strings.to_string(sb)) -} - -wprint :: proc( - w: io.Writer, - instructions: []Instruction, inst_info: []Instruction_Info, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, -) { - sb := strings.builder_make(context.temp_allocator) - sbprint(&sb, instructions, inst_info, options, label_names) - io.write_string(w, strings.to_string(sb)) -} - -wprintln :: proc( - w: io.Writer, - instructions: []Instruction, inst_info: []Instruction_Info, options: ^Print_Options = nil, label_names: ^map[u32]string = nil, -) { - sb := strings.builder_make(context.temp_allocator) - sbprintln(&sb, instructions, inst_info, options, label_names) - io.write_string(w, strings.to_string(sb)) -} - -// ============================================================================= -// Internal writers -// ============================================================================= - -write_mnemonic :: proc(sb: ^strings.Builder, m: Mnemonic, uppercase: bool) { - name := MNEMONIC_NAMES[m] - if name == "" { strings.write_string(sb, ""); return } - if uppercase { - for i in 0.. u8 { - return u8(r) & 0xFF -} - -@(require_results) -reg_class :: #force_inline proc "contextless" (r: Register) -> u16 { - return u16(r) & 0xFF00 -} - -@(require_results) -reg_size :: #force_inline proc "contextless" (_: Register) -> u8 { - return 0 // no fixed width: the value stack is implicit -} - -// ----------------------------------------------------------------------------- -// Value types (the bytes WASM actually uses where a register would otherwise -// appear: block result/param types, ref.null heap types, select t* types). -// -// The numeric byte is the WASM binary encoding; the same byte sign-extends to -// the negative s33 value used inside a blocktype. See operands.odin / -// Block_Type for how these participate in block / loop / if. -// ----------------------------------------------------------------------------- - -Value_Type :: enum u8 { - I32 = 0x7F, - I64 = 0x7E, - F32 = 0x7D, - F64 = 0x7C, - V128 = 0x7B, - FUNCREF = 0x70, - EXTERNREF = 0x6F, -} - -@(require_results) -value_type_is_num :: #force_inline proc "contextless" (t: Value_Type) -> bool { - #partial switch t { - case .I32, .I64, .F32, .F64: return true - } - return false -} - -@(require_results) -value_type_is_ref :: #force_inline proc "contextless" (t: Value_Type) -> bool { - #partial switch t { - case .FUNCREF, .EXTERNREF: return true - } - return false -} diff --git a/core/rexcode/wasm/reloc.odin b/core/rexcode/wasm/reloc.odin deleted file mode 100644 index 685986080..000000000 --- a/core/rexcode/wasm/reloc.odin +++ /dev/null @@ -1,44 +0,0 @@ -// rexcode · Brendan Punsky (dotbmp@github), original author -// Ginger Bill (gingerBill@github) -// Ginger Bill (gingerBill@github) - -package rexcode_wasm - -// ============================================================================= -// WebAssembly RELOCATIONS -// ============================================================================= -// -// Per the cross-arch design (§2.4) each arch owns its Relocation_Type. WASM's -// relocations are the object-file ("linking") relocations: symbolic index -// references the linker fixes up. They are emitted, never PC-relative -- WASM -// control flow uses structured label depths, not byte offsets, so the encoder -// does not resolve these in a pass 2; it records them and leaves the patching -// to the linker. The relocatable LEB encodings are written as fixed-width -// 5-byte placeholders so the patched value always fits. -// -// The subset modelled mirrors the names from the tool-conventions linking -// spec used by LLVM / wasm-ld. - -Relocation_Type :: enum u8 { - NONE = 0, - - FUNCTION_INDEX_LEB, // funcidx, 5-byte ULEB (call, ref.func) - TABLE_INDEX_SLEB, // 5-byte SLEB table element index - TABLE_INDEX_I32, // 4-byte LE table element index - MEMORY_ADDR_LEB, // linear-memory address, 5-byte ULEB - MEMORY_ADDR_SLEB, // linear-memory address, 5-byte SLEB - MEMORY_ADDR_I32, // linear-memory address, 4-byte LE - TYPE_INDEX_LEB, // typeidx, 5-byte ULEB (call_indirect) - GLOBAL_INDEX_LEB, // globalidx, 5-byte ULEB - TABLE_NUMBER_LEB, // tableidx, 5-byte ULEB -} - -Relocation :: struct #packed { - offset: u32, // byte offset of the relocatable field - label_id: u32, // symbol / target label id - addend: i32, - type: Relocation_Type, - size: u8, // bytes occupied by the field (5 for LEB, 4 for I32) - inst_idx: u16, -} -#assert(size_of(Relocation) == 16) diff --git a/core/rexcode/wasm/tests/pipeline_smoke.odin b/core/rexcode/wasm/tests/pipeline_smoke.odin deleted file mode 100644 index ee68a9355..000000000 --- a/core/rexcode/wasm/tests/pipeline_smoke.odin +++ /dev/null @@ -1,142 +0,0 @@ -// rexcode · Brendan Punsky (dotbmp@github), original author -// Ginger Bill (gingerBill@github) - -package rexcode_wasm_tests - -// End-to-end WASM pipeline: build a short instruction sequence, encode it, -// assert the exact byte stream against hand-computed LEB128 encodings, then -// decode the bytes back and confirm the mnemonics/operands round-trip, and -// finally print the decoded form and check the WAT text. -// -// Covers: nullary ops, signed-LEB constants, index immediates, a blocktype, -// a memarg, and the br_table vector form. -// -// Run with: odin run wasm/tests - -import "core:fmt" -import "core:os" -import wasm "../" - -@(private="file") rpasses := 0 -@(private="file") rfailures := 0 - -@(private="file") -ok :: proc(name: string, cond: bool) { - if cond { - fmt.printfln(" [ok] %s", name) - rpasses += 1 - } else { - fmt.printfln(" [FAIL] %s", name) - rfailures += 1 - } -} - -@(private="file") -eq_bytes :: proc(name: string, got, want: []u8) { - if string(got) == string(want) { - fmt.printfln(" [ok] %s (% x)", name, got) - rpasses += 1 - } else { - fmt.printfln(" [FAIL] %-18s got=[% x] want=[% x]", name, got, want) - rfailures += 1 - } -} - -@(private="file") -eq_str :: proc(name, got, want: string) { - if got == want { - fmt.printfln(" [ok] %-18s %q", name, got) - rpasses += 1 - } else { - fmt.printfln(" [FAIL] %-18s got=%q want=%q", name, got, want) - rfailures += 1 - } -} - -main :: proc() { - fmt.println("== wasm encode/decode/print pipeline ==") - - insts := []wasm.Instruction{ - wasm.inst_i(.I32_CONST, wasm.op_i32(42)), // 0x41 0x2A - wasm.inst_idx(.LOCAL_GET, wasm.op_local(0)), // 0x20 0x00 - wasm.inst_none(.I32_ADD), // 0x6A - wasm.inst_idx(.CALL, wasm.op_func(3)), // 0x10 0x03 - wasm.inst_block(.BLOCK, .I32), // 0x02 0x7F - wasm.inst_memarg(.I32_LOAD, wasm.memarg(2, 8)), // 0x28 0x02 0x08 - wasm.inst_none(.END), // 0x0B - } - - code := make([]u8, wasm.encode_max_code_size(len(insts))) - defer delete(code) - relocs: [dynamic]wasm.Relocation - errors: [dynamic]wasm.Error - defer delete(relocs) - defer delete(errors) - - n, enc_ok := wasm.encode(insts, nil, code, &relocs, &errors) - ok("encode ok", enc_ok && len(errors) == 0) - - want := []u8{ - 0x41, 0x2A, - 0x20, 0x00, - 0x6A, - 0x10, 0x03, - 0x02, 0x7F, - 0x28, 0x02, 0x08, - 0x0B, - } - eq_bytes("byte stream", code[:n], want) - - // ---- br_table on its own (vector immediate) ---------------------------- - bt := []wasm.Instruction{ - wasm.inst_br_table([]u32{0, 1}, 2), // 0x0E 0x02 0x00 0x01 0x02 - } - bt_code := make([]u8, 32) - defer delete(bt_code) - bt_relocs: [dynamic]wasm.Relocation - bt_errors: [dynamic]wasm.Error - defer delete(bt_relocs) - defer delete(bt_errors) - bn, _ := wasm.encode(bt, nil, bt_code, &bt_relocs, &bt_errors) - eq_bytes("br_table bytes", bt_code[:bn], []u8{0x0E, 0x02, 0x00, 0x01, 0x02}) - - // ---- decode round-trip -------------------------------------------------- - dinsts: [dynamic]wasm.Instruction - dinfo: [dynamic]wasm.Instruction_Info - dlabels:[dynamic]wasm.Label_Definition - derrs: [dynamic]wasm.Error - defer delete(dinsts) - defer delete(dinfo) - defer delete(dlabels) - defer delete(derrs) - - dn, dec_ok := wasm.decode(code[:n], nil, &dinsts, &dinfo, &dlabels, &derrs) - ok("decode ok", dec_ok && len(derrs) == 0) - ok("decode byte count", dn == n) - ok("decode count", len(dinsts) == len(insts)) - - if len(dinsts) == len(insts) { - ok("m[0] i32.const", dinsts[0].mnemonic == .I32_CONST && dinsts[0].ops[0].immediate == 42) - ok("m[1] local.get", dinsts[1].mnemonic == .LOCAL_GET && dinsts[1].ops[0].index == 0) - ok("m[2] i32.add", dinsts[2].mnemonic == .I32_ADD && dinsts[2].operand_count == 0) - ok("m[3] call", dinsts[3].mnemonic == .CALL && dinsts[3].ops[0].idx_kind == .FUNC) - ok("m[4] block", dinsts[4].mnemonic == .BLOCK && dinsts[4].ops[0].kind == .BLOCK_TYPE) - ok("m[5] i32.load", dinsts[5].mnemonic == .I32_LOAD && dinsts[5].ops[0].memarg.offset == 8) - ok("m[6] end", dinsts[6].mnemonic == .END) - } - - // ---- print -------------------------------------------------------------- - text := wasm.tprint(dinsts[:], dinfo[:], dlabels[:]) - want_text := - " i32.const 42\n" + - " local.get 0\n" + - " i32.add\n" + - " call 3\n" + - " block (result i32)\n" + - " i32.load offset=8 align=2\n" + - " end\n" - eq_str("disassembly", text, want_text) - - fmt.printfln("\n%d passed, %d failed", rpasses, rfailures) - if rfailures > 0 { os.exit(1) } -} diff --git a/core/rexcode/wasm/tests/smoke.odin b/core/rexcode/wasm/tests/smoke.odin deleted file mode 100644 index f81226b96..000000000 --- a/core/rexcode/wasm/tests/smoke.odin +++ /dev/null @@ -1,86 +0,0 @@ -// rexcode · Brendan Punsky (dotbmp@github), original author -// Ginger Bill (gingerBill@github) - -package rexcode_wasm_tests - -// Spot-check ENCODING_TABLE entries against the canonical opcode bytes from -// the WebAssembly core specification (binary format, §5.4). One or two -// representatives from each opcode region, plus both 0xFC misc endpoints. -// -// Run with: odin run wasm/tests - -import "core:fmt" -import "core:os" -import wasm "../" - -@(private="file") passes := 0 -@(private="file") failures := 0 - -@(private="file") -check :: proc(name: string, m: wasm.Mnemonic, want_prefix: u8, want_opcode: u16) { - e := wasm.ENCODING_TABLE[m] - if e.prefix != want_prefix || e.opcode != want_opcode { - fmt.printfln(" [FAIL] %-22s got prefix=%02x op=%02x want prefix=%02x op=%02x", - name, e.prefix, e.opcode, want_prefix, want_opcode) - failures += 1 - return - } - fmt.printfln(" [ok] %-22s prefix=%02x op=%02x", name, e.prefix, e.opcode) - passes += 1 -} - -main :: proc() { - fmt.println("== wasm encoding-table spot checks ==") - - // control - check("unreachable", .UNREACHABLE, 0x00, 0x00) - check("block", .BLOCK, 0x00, 0x02) - check("br_table", .BR_TABLE, 0x00, 0x0E) - check("call", .CALL, 0x00, 0x10) - check("call_indirect", .CALL_INDIRECT, 0x00, 0x11) - - // parametric / variable - check("drop", .DROP, 0x00, 0x1A) - check("local.get", .LOCAL_GET, 0x00, 0x20) - check("global.set", .GLOBAL_SET, 0x00, 0x24) - - // memory - check("i32.load", .I32_LOAD, 0x00, 0x28) - check("i64.store32", .I64_STORE32, 0x00, 0x3E) - check("memory.size", .MEMORY_SIZE, 0x00, 0x3F) - check("memory.grow", .MEMORY_GROW, 0x00, 0x40) - - // numeric - check("i32.const", .I32_CONST, 0x00, 0x41) - check("f64.const", .F64_CONST, 0x00, 0x44) - check("i32.add", .I32_ADD, 0x00, 0x6A) - check("i64.mul", .I64_MUL, 0x00, 0x7E) - check("f32.add", .F32_ADD, 0x00, 0x92) - check("f64.sqrt", .F64_SQRT, 0x00, 0x9F) - - // conversions / sign-extension / reftypes - check("i32.wrap_i64", .I32_WRAP_I64, 0x00, 0xA7) - check("i32.extend8_s", .I32_EXTEND8_S, 0x00, 0xC0) - check("ref.null", .REF_NULL, 0x00, 0xD0) - check("ref.func", .REF_FUNC, 0x00, 0xD2) - - // 0xFC misc group endpoints - check("i32.trunc_sat_f32_s", .I32_TRUNC_SAT_F32_S, 0xFC, 0) - check("memory.init", .MEMORY_INIT, 0xFC, 8) - check("table.fill", .TABLE_FILL, 0xFC, 17) - - // 0xFD SIMD group - check("v128.load", .V128_LOAD, 0xFD, 0x00) - check("v128.const", .V128_CONST, 0xFD, 0x0C) - check("i8x16.shuffle", .I8X16_SHUFFLE, 0xFD, 0x0D) - check("i32x4.add", .I32X4_ADD, 0xFD, 0xAE) - check("simd hi (relaxed)", .I32X4_RELAXED_DOT_I8X16_I7X16_ADD_S, 0xFD, 0x113) - - // 0xFE threads / atomics group - check("memory.atomic.notify", .MEMORY_ATOMIC_NOTIFY, 0xFE, 0x00) - check("atomic.fence", .ATOMIC_FENCE, 0xFE, 0x03) - check("i32.atomic.load", .I32_ATOMIC_LOAD, 0xFE, 0x10) - - fmt.printfln("\n%d passed, %d failed", passes, failures) - if failures > 0 { os.exit(1) } -} diff --git a/core/rexcode/wasm/tools/dump_verify_input.odin b/core/rexcode/wasm/tools/dump_verify_input.odin deleted file mode 100644 index ae7329b18..000000000 --- a/core/rexcode/wasm/tools/dump_verify_input.odin +++ /dev/null @@ -1,99 +0,0 @@ -// rexcode · Brendan Punsky (dotbmp@github), original author - -package main - -// ============================================================================= -// WebAssembly verification manifest dumper -// ============================================================================= -// -// Encodes one representative instruction per mnemonic (synthesising operands -// that fit the entry's immediate layout) and writes: -// -// /tmp/rexcode_wasm_input.hex -- comma-separated LE hex bytes, one row each -// /tmp/rexcode_wasm_meta.txt -- "\t\t\t" -// -// The canonical external oracle for cross-checking these bytes is wabt's -// `wasm-objdump` / `wasm2wat`, or LLVM's `llvm-mc -triple=wasm32`. Feed the -// hex rows through the disassembler and diff its mnemonics against the meta -// file. -// -// Run: cd wasm && odin run tools/dump_verify_input.odin -file - -import "core:fmt" -import "core:os" -import "core:strings" - -import w "../" - -main :: proc() { - fmt.println("Dumping WASM verification manifest...") - - hex_buf, meta_buf: strings.Builder - strings.builder_init(&hex_buf) - strings.builder_init(&meta_buf) - defer strings.builder_destroy(&hex_buf) - defer strings.builder_destroy(&meta_buf) - - code: [32]u8 - count := 0 - - for mn in w.Mnemonic { - if mn == .INVALID { continue } - form := w.ENCODING_TABLE[mn] - - inst := synth(mn, form) - one := []w.Instruction{inst} - - relocs: [dynamic]w.Relocation - errors: [dynamic]w.Error - defer delete(relocs) - defer delete(errors) - - n := w.encode(one, nil, code[:], &relocs, &errors) or_continue - - for i in 0.. 0 { strings.write_byte(&hex_buf, ',') } - fmt.sbprintf(&hex_buf, "0x%02x", code[i]) - } - strings.write_byte(&hex_buf, '\n') - - fmt.sbprintf(&meta_buf, "%v\t0x%02x\t0x%02x\t%d\n", mn, form.prefix, form.opcode, n) - count += 1 - } - - _ = os.write_entire_file("/tmp/rexcode_wasm_input.hex", hex_buf.buf[:]) - _ = os.write_entire_file("/tmp/rexcode_wasm_meta.txt", meta_buf.buf[:]) - - fmt.printf("Wrote %d entries.\n", count) -} - -// Build a minimal valid instruction for `mn` whose operands satisfy the -// immediate layout in `form`. -synth :: proc(mn: w.Mnemonic, form: w.Encoding) -> w.Instruction { - if mn == .BR_TABLE { - @(static) tbl := [1]u32{0} - return w.inst_br_table(tbl[:], 0) - } - - inst := w.Instruction{mnemonic = mn} - slot := 0 - for k in form.imm { - switch k { - case .NONE, .ZERO_BYTE: - // no operand - case .BLOCKTYPE: inst.ops[slot] = w.op_blocktype(.EMPTY); slot += 1 - case .I32: inst.ops[slot] = w.op_i32(1); slot += 1 - case .I64: inst.ops[slot] = w.op_i64(1); slot += 1 - case .F32: inst.ops[slot] = w.op_f32(1); slot += 1 - case .F64: inst.ops[slot] = w.op_f64(1); slot += 1 - case .IDX: inst.ops[slot] = w.op_func(0); slot += 1 - case .MEMARG: inst.ops[slot] = w.op_memarg(0, 0); slot += 1 - case .REFTYPE: inst.ops[slot] = w.op_reftype(.FUNCREF); slot += 1 - case .LANE: inst.ops[slot] = w.op_lane(0); slot += 1 - case .LANES16: // 16-byte value lives in inst.bytes (left zero), no operand - case .BR_TABLE: // handled above - } - } - inst.operand_count = u8(slot) - return inst -}