mirror of
https://github.com/odin-lang/Odin.git
synced 2026-07-17 13:11:07 +00:00
Refactor core:rexcode/wasm to use the new ir structure and move to core:rexcode/ir/wasm
This commit is contained in:
155
core/rexcode/ir/wasm/builder.odin
Normal file
155
core/rexcode/ir/wasm/builder.odin
Normal file
@@ -0,0 +1,155 @@
|
||||
// rexcode · Brendan Punsky (dotbmp@github), original author
|
||||
// Ginger Bill (gingerBill@github)
|
||||
|
||||
package rexcode_wasm
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
// =============================================================================
|
||||
// SECTION: Builder (constructing ir.Operations for a WASM function body)
|
||||
// =============================================================================
|
||||
//
|
||||
// The IR leaf is `ir.Operation` -- an opcode (u16), a variable-arity `[]Operand`
|
||||
// (caller-owned, unlike the old ISA `Instruction`'s inline [2]Operand), and an
|
||||
// optional typed `Result` (always `.id == ID_NONE` here: WASM is a stack machine
|
||||
// and names no results). So every builder that carries operands must allocate
|
||||
// their backing store; the `Builder` below owns that store and accumulates ops
|
||||
// into the current block, mirroring `spirv.Builder`.
|
||||
|
||||
// Operation flags derived from the opcode's ENCODING_TABLE form, plus the
|
||||
// terminator bit for the ops that end a stack-IR region.
|
||||
@(require_results)
|
||||
op_flags_for :: proc "contextless" (opcode: Opcode) -> Operation_Flags {
|
||||
e := ENCODING_TABLE[opcode]
|
||||
f: Operation_Flags
|
||||
f.control = e.flags.control
|
||||
f.memory = e.flags.memory
|
||||
#partial switch opcode {
|
||||
case .RETURN, .UNREACHABLE,
|
||||
.BR, .BR_TABLE,
|
||||
.RETURN_CALL, .RETURN_CALL_INDIRECT:
|
||||
f.terminator = true
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// The stack-IR result: WASM defines no SSA value, so every operation's result is
|
||||
// empty. (Kept as a helper so the intent is explicit at every call site.)
|
||||
@(require_results)
|
||||
no_result :: #force_inline proc "contextless" () -> Result {
|
||||
return Result{id = ID_NONE, type = TYPE_NONE}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Stateless construction (caller owns `operands`)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Build an Operation over an already-owned operand slice (no allocation).
|
||||
@(require_results)
|
||||
operation :: #force_inline proc "contextless" (opcode: Opcode, operands: []Operand = nil) -> Operation {
|
||||
return Operation{
|
||||
opcode = u16(opcode),
|
||||
operands = operands,
|
||||
result = no_result(),
|
||||
flags = op_flags_for(opcode),
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Builder
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
Builder :: struct {
|
||||
alloc: runtime.Allocator,
|
||||
ops: [dynamic]Operation, // current block
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
builder_make :: proc(allocator := context.allocator) -> Builder {
|
||||
b: Builder
|
||||
b.alloc = allocator
|
||||
b.ops.allocator = allocator
|
||||
return b
|
||||
}
|
||||
|
||||
// Detach the accumulated operations as a block body (and reset for the next).
|
||||
@(require_results)
|
||||
take_block :: proc(b: ^Builder, label: Id = ID_NONE) -> Block {
|
||||
blk := Block{id = label, ops = b.ops[:]}
|
||||
b.ops = nil
|
||||
b.ops.allocator = b.alloc
|
||||
return blk
|
||||
}
|
||||
|
||||
// Stable per-operation operand backing.
|
||||
@(require_results)
|
||||
opbuf :: proc(b: ^Builder, ops: ..Operand) -> []Operand {
|
||||
if len(ops) == 0 {
|
||||
return nil
|
||||
}
|
||||
buf := make([]Operand, len(ops), b.alloc)
|
||||
copy(buf, ops)
|
||||
return buf
|
||||
}
|
||||
|
||||
// Append an operation with the given operands, allocating their backing.
|
||||
emit :: proc(b: ^Builder, opcode: Opcode, operands: ..Operand) {
|
||||
append(&b.ops, operation(opcode, opbuf(b, ..operands)))
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Convenience emitters (a representative set; `emit` covers the rest)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
emit_none :: proc(b: ^Builder, opcode: Opcode) { emit(b, opcode) }
|
||||
|
||||
emit_i32 :: proc(b: ^Builder, v: i32) { emit(b, .I32_CONST, op_i32(v)) }
|
||||
emit_i64 :: proc(b: ^Builder, v: i64) { emit(b, .I64_CONST, op_i64(v)) }
|
||||
emit_f32 :: proc(b: ^Builder, v: f32) { emit(b, .F32_CONST, op_f32(v)) }
|
||||
emit_f64 :: proc(b: ^Builder, v: f64) { emit(b, .F64_CONST, op_f64(v)) }
|
||||
|
||||
emit_local_get :: proc(b: ^Builder, n: u32) { emit(b, .LOCAL_GET, op_local(n)) }
|
||||
emit_local_set :: proc(b: ^Builder, n: u32) { emit(b, .LOCAL_SET, op_local(n)) }
|
||||
emit_local_tee :: proc(b: ^Builder, n: u32) { emit(b, .LOCAL_TEE, op_local(n)) }
|
||||
emit_global_get :: proc(b: ^Builder, n: u32) { emit(b, .GLOBAL_GET, op_global(n)) }
|
||||
emit_global_set :: proc(b: ^Builder, n: u32) { emit(b, .GLOBAL_SET, op_global(n)) }
|
||||
|
||||
emit_call :: proc(b: ^Builder, funcidx: u32) { emit(b, .CALL, op_func(funcidx)) }
|
||||
emit_call_indirect :: proc(b: ^Builder, typeidx: u32, tableidx: u32 = 0) {
|
||||
emit(b, .CALL_INDIRECT, op_typeidx(typeidx), op_table(tableidx))
|
||||
}
|
||||
|
||||
emit_block :: proc(b: ^Builder, bt: Block_Type = .EMPTY) { emit(b, .BLOCK, op_blocktype(bt)) }
|
||||
emit_loop :: proc(b: ^Builder, bt: Block_Type = .EMPTY) { emit(b, .LOOP, op_blocktype(bt)) }
|
||||
emit_if :: proc(b: ^Builder, bt: Block_Type = .EMPTY) { emit(b, .IF, op_blocktype(bt)) }
|
||||
emit_else :: proc(b: ^Builder) { emit(b, .ELSE) }
|
||||
emit_end :: proc(b: ^Builder) { emit(b, .END) }
|
||||
|
||||
emit_br :: proc(b: ^Builder, depth: u32) { emit(b, .BR, op_labelidx(depth)) }
|
||||
emit_br_if :: proc(b: ^Builder, depth: u32) { emit(b, .BR_IF, op_labelidx(depth)) }
|
||||
|
||||
// br_table: operands are [default, case0, case1, ...], every entry a label depth.
|
||||
emit_br_table :: proc(b: ^Builder, targets: []u32, default_depth: u32) {
|
||||
buf := make([]Operand, len(targets)+1, b.alloc)
|
||||
buf[0] = op_labelidx(default_depth)
|
||||
for t, i in targets {
|
||||
buf[i+1] = op_labelidx(t)
|
||||
}
|
||||
append(&b.ops, operation(.BR_TABLE, buf))
|
||||
}
|
||||
|
||||
emit_return :: proc(b: ^Builder) { emit(b, .RETURN) }
|
||||
emit_unreachable :: proc(b: ^Builder) { emit(b, .UNREACHABLE) }
|
||||
|
||||
emit_load :: proc(b: ^Builder, opcode: Opcode, ma: Memarg) { emit(b, opcode, op_memarg(ma)) }
|
||||
emit_store :: proc(b: ^Builder, opcode: Opcode, ma: Memarg) { emit(b, opcode, op_memarg(ma)) }
|
||||
|
||||
// v128.const / i8x16.shuffle: the 16-byte immediate as two ATTRIBUTE halves.
|
||||
emit_v128_const :: proc(b: ^Builder, value: [16]u8) {
|
||||
lo, hi := op_v128(value)
|
||||
emit(b, .V128_CONST, lo, hi)
|
||||
}
|
||||
emit_shuffle :: proc(b: ^Builder, lanes: [16]u8) {
|
||||
lo, hi := op_v128(lanes)
|
||||
emit(b, .I8X16_SHUFFLE, lo, hi)
|
||||
}
|
||||
242
core/rexcode/ir/wasm/decoder.odin
Normal file
242
core/rexcode/ir/wasm/decoder.odin
Normal file
@@ -0,0 +1,242 @@
|
||||
// rexcode · Brendan Punsky (dotbmp@github), original author
|
||||
// Ginger Bill (gingerBill@github)
|
||||
|
||||
package rexcode_wasm
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
// =============================================================================
|
||||
// SECTION: Decoder (WASM instruction byte stream -> ir.Operations / Module)
|
||||
// =============================================================================
|
||||
//
|
||||
// Single forward pass, mirroring the encoder. Each step reads an opcode (a
|
||||
// leading 0xFC/0xFD/0xFE switches to a prefix group whose sub-opcode is an
|
||||
// unsigned LEB128; otherwise the single byte is the opcode), looks its form up
|
||||
// in ENCODING_TABLE, and reads the immediates in declaration order, building the
|
||||
// operation's variable-arity `[]Operand` as it goes.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// decode: parse a WASM `expr` byte stream into a single-function, single-block
|
||||
// Module (dataflow = .STACK). The reusable stream decoder is `decode_ops`.
|
||||
decode :: proc(
|
||||
data: []u8,
|
||||
m: ^Module,
|
||||
errors: ^[dynamic]Error,
|
||||
allocator := context.allocator,
|
||||
) -> (byte_count: u32, ok: bool) {
|
||||
ops: []Operation
|
||||
ops, byte_count, ok = decode_ops(data, nil, errors, allocator)
|
||||
|
||||
blocks := make([]Block, 1, allocator)
|
||||
blocks[0] = Block{id = ID_NONE, ops = ops}
|
||||
funcs := make([]Function, 1, allocator)
|
||||
funcs[0] = Function{blocks = blocks, signature = TYPE_NONE}
|
||||
|
||||
m.base.dataflow = .STACK
|
||||
m.base.functions = funcs
|
||||
m.version = WASM_VERSION
|
||||
m.start = -1
|
||||
return
|
||||
}
|
||||
|
||||
// decode_ops: the reusable instruction-stream decoder. `relocs` (may be nil) are
|
||||
// the input relocations to re-attach to symbolic index fields.
|
||||
decode_ops :: proc(
|
||||
data: []u8,
|
||||
relocs: []Relocation,
|
||||
errors: ^[dynamic]Error,
|
||||
allocator := context.allocator,
|
||||
) -> (ops: []Operation, byte_count: u32, ok: bool) {
|
||||
errors_start := u32(len(errors))
|
||||
acc := make([dynamic]Operation, allocator)
|
||||
n := u32(len(data))
|
||||
|
||||
for byte_count < n {
|
||||
op, next, dok := decode_one(data, relocs, byte_count, allocator)
|
||||
if !dok {
|
||||
append(errors, Error{location = byte_count, code = .INVALID_OPCODE})
|
||||
append(&acc, operation(.INVALID))
|
||||
byte_count += 1
|
||||
continue
|
||||
}
|
||||
append(&acc, op)
|
||||
byte_count = next
|
||||
}
|
||||
|
||||
ops = acc[:]
|
||||
ok = u32(len(errors)) == errors_start
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
decode_one :: proc(
|
||||
data: []u8,
|
||||
relocs: []Relocation,
|
||||
pc: u32,
|
||||
allocator: runtime.Allocator,
|
||||
) -> (op: Operation, next: u32, ok: bool) {
|
||||
off := pc
|
||||
if off >= u32(len(data)) {
|
||||
next = pc
|
||||
return
|
||||
}
|
||||
|
||||
b0 := data[off]
|
||||
off += 1
|
||||
|
||||
m: Opcode = .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)
|
||||
operands := make([dynamic]Operand, allocator)
|
||||
defer if !ok { delete(operands) }
|
||||
|
||||
for k, ki in form.imm {
|
||||
switch k {
|
||||
case .NONE:
|
||||
// nothing
|
||||
|
||||
case .BLOCKTYPE:
|
||||
v := read_sleb(data, &off) or_return
|
||||
append(&operands, Operand{kind = .ATTRIBUTE, imm = v, aux = u16(Attr.BLOCKTYPE)})
|
||||
|
||||
case .I32:
|
||||
v := read_sleb(data, &off) or_return
|
||||
append(&operands, op_int(v))
|
||||
|
||||
case .I64:
|
||||
v := read_sleb(data, &off) or_return
|
||||
append(&operands, op_int(v))
|
||||
|
||||
case .F32:
|
||||
b := read_u32_block(data, &off) or_return
|
||||
append(&operands, op_float(u64(b), 32))
|
||||
|
||||
case .F64:
|
||||
b := read_u64_block(data, &off) or_return
|
||||
append(&operands, op_float(b, 64))
|
||||
|
||||
case .IDX:
|
||||
field := off
|
||||
raw := read_uleb(data, &off) or_return
|
||||
kind := idx_kind_for(m, ki)
|
||||
o := op_wasm_index(kind, u32(raw))
|
||||
// An index is symbolic only when an input relocation actually lands
|
||||
// on this field; otherwise it is a concrete, self-contained index
|
||||
// (so a relocation-free stream round-trips byte-for-byte).
|
||||
if lid, found := reloc_label_at(relocs, field); found {
|
||||
o = op_wasm_index(kind, lid, symbolic = true)
|
||||
}
|
||||
append(&operands, o)
|
||||
|
||||
case .MEMARG:
|
||||
align := read_uleb(data, &off) or_return
|
||||
offset := read_uleb(data, &off) or_return
|
||||
// Stored as log2 on the wire; expand back to a byte alignment.
|
||||
append(&operands, op_memarg(Memarg{align = u32(1 << align), offset = u32(offset)}))
|
||||
|
||||
case .REFTYPE:
|
||||
if off >= u32(len(data)) {
|
||||
next = pc
|
||||
return
|
||||
}
|
||||
t := data[off]
|
||||
off += 1
|
||||
append(&operands, Operand{kind = .ATTRIBUTE, imm = i64(t), aux = u16(Attr.REFTYPE)})
|
||||
|
||||
case .BR_TABLE:
|
||||
count := int(read_uleb(data, &off) or_return)
|
||||
reserve(&operands, len(operands)+count+1)
|
||||
operands_default_index := len(operands)
|
||||
for _ in 0..<count {
|
||||
t := read_uleb(data, &off) or_return
|
||||
append(&operands, op_labelidx(u32(t)))
|
||||
}
|
||||
def := read_uleb(data, &off) or_return
|
||||
// default first
|
||||
inject_at(&operands, operands_default_index, op_labelidx(u32(def)))
|
||||
|
||||
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
|
||||
append(&operands, Operand{kind = .ATTRIBUTE, imm = i64(l), aux = u16(Attr.LANE)})
|
||||
|
||||
case .LANES16:
|
||||
if off + 16 > u32(len(data)) {
|
||||
next = pc
|
||||
return
|
||||
}
|
||||
bytes: [16]u8
|
||||
copy(bytes[:], data[off:][:16])
|
||||
off += 16
|
||||
lo, hi := op_v128(bytes)
|
||||
append(&operands, lo)
|
||||
append(&operands, hi)
|
||||
}
|
||||
}
|
||||
|
||||
op = operation(m, operands[:])
|
||||
next = off
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
// Which index space the IDX immediate in operand slot `which` addresses, by
|
||||
// opcode. Mirrors how the builders tag each operand.
|
||||
@(private="file", require_results)
|
||||
idx_kind_for :: #force_inline proc "contextless" (m: Opcode, 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
|
||||
}
|
||||
560
core/rexcode/ir/wasm/decoding_tables.odin
Normal file
560
core/rexcode/ir/wasm/decoding_tables.odin
Normal file
@@ -0,0 +1,560 @@
|
||||
// rexcode · Brendan Punsky (dotbmp@github), original author
|
||||
// Ginger Bill (gingerBill@github)
|
||||
|
||||
package rexcode_wasm
|
||||
|
||||
// =============================================================================
|
||||
// WebAssembly DECODE DISPATCH TABLES
|
||||
// =============================================================================
|
||||
//
|
||||
// Reverse maps from wire opcode to Opcode. 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]Opcode{
|
||||
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]Opcode{
|
||||
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]Opcode{
|
||||
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]Opcode{
|
||||
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,
|
||||
}
|
||||
243
core/rexcode/ir/wasm/encoder.odin
Normal file
243
core/rexcode/ir/wasm/encoder.odin
Normal file
@@ -0,0 +1,243 @@
|
||||
// rexcode · Brendan Punsky (dotbmp@github), original author
|
||||
// Ginger Bill (gingerBill@github)
|
||||
|
||||
package rexcode_wasm
|
||||
|
||||
import "core:math/bits"
|
||||
|
||||
// =============================================================================
|
||||
// SECTION: Encoder (Module -> WASM instruction byte stream)
|
||||
// =============================================================================
|
||||
//
|
||||
// Variable-length, byte-oriented, LEB128-heavy. Encoding is a single forward
|
||||
// pass: each operation writes its 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 -- which is exactly why the ir verbs
|
||||
// (docs/ir_design.md §4) drop the ISA `label_defs`/`resolve`/`base_address`.
|
||||
// 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 always fits.
|
||||
//
|
||||
// The reusable core is `encode_ops` (an operation stream = a WASM `expr`). The
|
||||
// Module verb `encode` drives it over each function's body blocks. Byte-level
|
||||
// container framing (the type/function/code section wrappers) is a separate,
|
||||
// symmetric concern -- the sibling container reader lives in the WASM `module`
|
||||
// parsing path -- and is not part of this instruction-stream codec.
|
||||
|
||||
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 op without a br_table: a 3-byte opcode plus the largest
|
||||
// single immediate, v128.const's 16 raw bytes. br_table is unbounded in its
|
||||
// target count; callers encoding those 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: serialize the module's function bodies into `code`, in order. Returns
|
||||
// the total byte count written. (A Module built as one function / one block
|
||||
// reproduces the old flat `encode([]Instruction)` behavior exactly.)
|
||||
encode :: proc(m: Module, code: []u8, relocs: ^[dynamic]Relocation, errors: ^[dynamic]Error) -> (byte_count: u32, ok: bool) {
|
||||
errors_start := u32(len(errors))
|
||||
op_index := u16(0)
|
||||
for fn in m.functions {
|
||||
for blk in fn.blocks {
|
||||
for &op in blk.ops {
|
||||
n := encode_operation(&op, byte_count, op_index, code, relocs, errors) or_return
|
||||
byte_count += n
|
||||
op_index += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
ok = u32(len(errors)) == errors_start
|
||||
return
|
||||
}
|
||||
|
||||
// encode_ops: the reusable instruction-stream encoder (a WASM `expr`).
|
||||
encode_ops :: proc(ops: []Operation, code: []u8, relocs: ^[dynamic]Relocation, errors: ^[dynamic]Error) -> (byte_count: u32, ok: bool) {
|
||||
errors_start := u32(len(errors))
|
||||
for &op, i in ops {
|
||||
n := encode_operation(&op, byte_count, u16(i), code, relocs, errors) or_return
|
||||
byte_count += n
|
||||
}
|
||||
ok = u32(len(errors)) == errors_start
|
||||
return
|
||||
}
|
||||
|
||||
encode_operation :: proc(
|
||||
op: ^Operation,
|
||||
pc: u32,
|
||||
op_index: u16,
|
||||
code: []u8,
|
||||
relocs: ^[dynamic]Relocation,
|
||||
errors: ^[dynamic]Error,
|
||||
) -> (size: u32, ok: bool) {
|
||||
opcode := Opcode(op.opcode)
|
||||
if opcode == .INVALID {
|
||||
append(errors, Error{location = u32(op_index), code = .INVALID_OPCODE})
|
||||
return
|
||||
}
|
||||
form := encoding_form(opcode)
|
||||
|
||||
need := encoded_size(op, form)
|
||||
if pc + need > u32(len(code)) {
|
||||
append(errors, Error{location = u32(op_index), 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, op.operands[opi].imm)
|
||||
opi += 1
|
||||
|
||||
case .F32:
|
||||
write_u32_block(code, &off, u32(op.operands[opi].imm))
|
||||
opi += 1
|
||||
|
||||
case .F64:
|
||||
write_u64_block(code, &off, u64(op.operands[opi].imm))
|
||||
opi += 1
|
||||
|
||||
case .IDX:
|
||||
o := op.operands[opi]
|
||||
if operand_symbolic(o) {
|
||||
append(relocs, Relocation{
|
||||
offset = off, label_id = operand_index(o), addend = 0,
|
||||
type = reloc_type_for(operand_index_kind(o)), size = 5, inst_idx = op_index,
|
||||
})
|
||||
write_uleb_padded5(code, &off, u64(operand_index(o)))
|
||||
} else {
|
||||
write_uleb(code, &off, u64(operand_index(o)))
|
||||
}
|
||||
opi += 1
|
||||
|
||||
case .MEMARG:
|
||||
ma := operand_memarg(op.operands[opi])
|
||||
// NOTE(bill): stored as log2 even though the spec text reads otherwise.
|
||||
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(op.operands[opi].imm)
|
||||
off += 1
|
||||
opi += 1
|
||||
|
||||
case .BR_TABLE:
|
||||
// operands = [default, case0, case1, ...] -- every entry a label depth.
|
||||
cases := op.operands[opi + 1:]
|
||||
write_uleb(code, &off, u64(len(cases)))
|
||||
for c in cases {
|
||||
write_uleb(code, &off, u64(operand_index(c)))
|
||||
}
|
||||
write_uleb(code, &off, u64(operand_index(op.operands[opi]))) // default
|
||||
opi = len(op.operands)
|
||||
|
||||
case .ZERO_BYTE:
|
||||
code[off] = 0x00
|
||||
off += 1
|
||||
|
||||
case .LANE:
|
||||
code[off] = u8(op.operands[opi].imm)
|
||||
off += 1
|
||||
opi += 1
|
||||
|
||||
case .LANES16:
|
||||
bytes := operand_v128(op.operands[opi], op.operands[opi + 1])
|
||||
for bb in bytes {
|
||||
code[off] = bb
|
||||
off += 1
|
||||
}
|
||||
opi += 2
|
||||
}
|
||||
}
|
||||
|
||||
return off - pc, true
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
encoded_size :: proc(op: ^Operation, 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(op.operands[opi].imm)
|
||||
opi += 1
|
||||
case .F32:
|
||||
size += 4
|
||||
opi += 1
|
||||
case .F64:
|
||||
size += 8
|
||||
opi += 1
|
||||
case .IDX:
|
||||
o := op.operands[opi]
|
||||
size += operand_symbolic(o) ? 5 : uleb_size(u64(operand_index(o)))
|
||||
opi += 1
|
||||
case .MEMARG:
|
||||
ma := operand_memarg(op.operands[opi])
|
||||
size += uleb_size(bits.log2(u64(ma.align))) + uleb_size(u64(ma.offset))
|
||||
opi += 1
|
||||
case .REFTYPE:
|
||||
size += 1
|
||||
opi += 1
|
||||
case .BR_TABLE:
|
||||
cases := op.operands[opi + 1:]
|
||||
size += uleb_size(u64(len(cases)))
|
||||
for c in cases {
|
||||
size += uleb_size(u64(operand_index(c)))
|
||||
}
|
||||
size += uleb_size(u64(operand_index(op.operands[opi])))
|
||||
opi = len(op.operands)
|
||||
case .ZERO_BYTE:
|
||||
size += 1
|
||||
case .LANE:
|
||||
size += 1
|
||||
opi += 1
|
||||
case .LANES16:
|
||||
size += 16
|
||||
opi += 2
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
95
core/rexcode/ir/wasm/encoding.odin
Normal file
95
core/rexcode/ir/wasm/encoding.odin
Normal file
@@ -0,0 +1,95 @@
|
||||
// rexcode · Brendan Punsky (dotbmp@github), original author
|
||||
// Ginger Bill (gingerBill@github)
|
||||
|
||||
package rexcode_wasm
|
||||
|
||||
// =============================================================================
|
||||
// SECTION: Encoding fundamentals (the table-driven codec vocabulary)
|
||||
// =============================================================================
|
||||
//
|
||||
// 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..0x113 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 opcode, so dispatch is a direct
|
||||
// `ENCODING_TABLE[opcode]` lookup (O(1)) -- the docs/ir_design.md §5 "table-
|
||||
// driven" strategy, literally the ISA `ENCODING_TABLE` shape. The immediate
|
||||
// layout is described declaratively by `imm: [2]Imm_Kind`, walked in order by
|
||||
// the encoder and decoder, each kind consuming zero or more `ir.Operand`s (see
|
||||
// operands.odin for how a WASM immediate maps onto an `ir.Operand`).
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
Encoding :: struct #packed {
|
||||
mnemonic: Opcode, // 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
|
||||
imm: [2]Imm_Kind, // 2 -- immediate layout, walked in order
|
||||
flags: Encoding_Flags, // 1
|
||||
inputs: i8, // 1 -- stack operands consumed; -1 when it varies
|
||||
outputs: i8, // 1 -- stack results produced; -1 when it varies
|
||||
}
|
||||
#assert(size_of(Encoding) == 10)
|
||||
|
||||
// `inputs`/`outputs` give an instruction's fixed stack arity: how many values
|
||||
// it pops and pushes. They are -1 when the count is not a constant of the opcode
|
||||
// -- i.e. it depends on a type/blocktype immediate or the surrounding control
|
||||
// frame (call/call_indirect, block/loop/if/else/end, br*/return, unreachable).
|
||||
|
||||
// =============================================================================
|
||||
// SECTION: Value types (the leaf types WASM names -- 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 used in a blocktype.
|
||||
// =============================================================================
|
||||
|
||||
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
|
||||
}
|
||||
505
core/rexcode/ir/wasm/encoding_table.odin
Normal file
505
core/rexcode/ir/wasm/encoding_table.odin
Normal file
@@ -0,0 +1,505 @@
|
||||
// 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 Opcode 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 := [Opcode]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: Opcode) -> ^Encoding {
|
||||
return &ENCODING_TABLE[m]
|
||||
}
|
||||
162
core/rexcode/ir/wasm/leb.odin
Normal file
162
core/rexcode/ir/wasm/leb.odin
Normal file
@@ -0,0 +1,162 @@
|
||||
// rexcode · Brendan Punsky (dotbmp@github), original author
|
||||
// Ginger Bill (gingerBill@github)
|
||||
|
||||
package rexcode_wasm
|
||||
|
||||
// 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
|
||||
}
|
||||
105
core/rexcode/ir/wasm/module.odin
Normal file
105
core/rexcode/ir/wasm/module.odin
Normal file
@@ -0,0 +1,105 @@
|
||||
// rexcode · Brendan Punsky (dotbmp@github), original author
|
||||
// Ginger Bill (gingerBill@github)
|
||||
|
||||
package rexcode_wasm
|
||||
|
||||
import "core:rexcode/ir"
|
||||
|
||||
// =============================================================================
|
||||
// SECTION: Module (the WASM module -- ir core + WASM's own container sections)
|
||||
// =============================================================================
|
||||
//
|
||||
// `wasm.Module` is the `ir.Module` STACK core (functions -> blocks -> operations,
|
||||
// dataflow = .STACK) plus the WASM binary-container sections the shared core has
|
||||
// no slot for. A concrete IR carries those "alongside the core" per
|
||||
// docs/ir_design.md §3; here that is literal -- `using base: ir.Module` embeds
|
||||
// the core so `m.functions`, `m.globals`, ... read straight through -- exactly
|
||||
// the shape `spirv.Module` uses for its preamble / debug / annotation sections.
|
||||
//
|
||||
// Mapping the WASM container onto the ir core:
|
||||
//
|
||||
// * A WASM function body is one `expr` (a flat instruction stream). It lowers
|
||||
// to a single `ir.Block` of `ir.Operation`s under one `ir.Function`; the
|
||||
// structured control ops (block/loop/if/else/end) remain *operations* within
|
||||
// that block rather than nested `ir.Block`s -- WASM's structure is carried
|
||||
// by those ops and their label depths, matching how the decoder produced a
|
||||
// flat stream. (Nesting into regions is a valid later refinement.)
|
||||
//
|
||||
// * WASM function signatures are `[]Value_Type -> []Value_Type`, not the ir
|
||||
// type table's `Type_Kind`. Rather than lower every primitive into
|
||||
// `ir.Module.types`, WASM keeps its value-typed signatures in the parallel
|
||||
// `func_types` side table (analogous to how `spirv.Module` keeps
|
||||
// `opaque_info` for what the shared core does not slot). `ir.Function.
|
||||
// signature` indexes `func_types` by convention.
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
External_Kind :: enum u8 {
|
||||
FUNC = 0,
|
||||
TABLE = 1,
|
||||
MEMORY = 2,
|
||||
GLOBAL = 3,
|
||||
}
|
||||
|
||||
// A WASM function signature: value-typed params and results.
|
||||
Func_Type :: struct {
|
||||
params: []Value_Type,
|
||||
results: []Value_Type,
|
||||
}
|
||||
|
||||
Import :: struct {
|
||||
kind: External_Kind,
|
||||
module_name: string,
|
||||
field_name: string,
|
||||
index: u32, // typeidx for FUNC, 0 for other kinds
|
||||
}
|
||||
|
||||
Export :: struct {
|
||||
kind: External_Kind,
|
||||
name: string,
|
||||
index: u32,
|
||||
}
|
||||
|
||||
// The WASM module -- the unit the verbs operate on. Embeds the ir core; adds the
|
||||
// container metadata parsed from / emitted to the binary sections.
|
||||
Module :: struct {
|
||||
using base: ir.Module, // functions, globals, symbols, dataflow (= .STACK), target
|
||||
|
||||
version: u32, // WASM_VERSION if 0 on encode
|
||||
|
||||
// --- container sections (the WASM-specific data the ir core has no slot for) ---
|
||||
func_types: []Func_Type, // the type section; ir.Function.signature indexes here
|
||||
imports: []Import,
|
||||
exports: []Export,
|
||||
start: i64, // -1 if absent, else the start funcidx
|
||||
|
||||
// --- side tables parallel to the ir core arrays ---
|
||||
// A WASM function's declared locals (the code section's local groups), kept
|
||||
// parallel to base.functions since ir.Function has no locals slot.
|
||||
function_locals: [][]Value_Type,
|
||||
}
|
||||
|
||||
// A freshly-made WASM module declares STACK dataflow so the shared verbs and the
|
||||
// printer pick the stack-machine path (no SSA results, no value refs).
|
||||
@(require_results)
|
||||
make_module :: proc "contextless" () -> Module {
|
||||
m: Module
|
||||
m.base.dataflow = .STACK
|
||||
m.version = WASM_VERSION
|
||||
m.start = -1
|
||||
return m
|
||||
}
|
||||
471
core/rexcode/ir/wasm/opcodes.odin
Normal file
471
core/rexcode/ir/wasm/opcodes.odin
Normal file
@@ -0,0 +1,471 @@
|
||||
// rexcode · Brendan Punsky (dotbmp@github), original author
|
||||
// Ginger Bill (gingerBill@github)
|
||||
|
||||
package rexcode_wasm
|
||||
|
||||
// =============================================================================
|
||||
// WebAssembly OPCODES
|
||||
// =============================================================================
|
||||
//
|
||||
// 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`.
|
||||
|
||||
Opcode :: 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 Opcode; INVALID maps to "<invalid>".
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@(rodata)
|
||||
MNEMONIC_NAMES := [Opcode]string{
|
||||
.INVALID = "<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",
|
||||
}
|
||||
217
core/rexcode/ir/wasm/operands.odin
Normal file
217
core/rexcode/ir/wasm/operands.odin
Normal file
@@ -0,0 +1,217 @@
|
||||
// rexcode · Brendan Punsky (dotbmp@github), original author
|
||||
// Ginger Bill (gingerBill@github)
|
||||
|
||||
package rexcode_wasm
|
||||
|
||||
// =============================================================================
|
||||
// SECTION: Operands (WASM immediates expressed as ir.Operand)
|
||||
// =============================================================================
|
||||
//
|
||||
// The old ISA-shaped WASM package had its own `Operand` (a #raw_union of
|
||||
// immediate / index / memarg / blocktype). Under the IR contract the operand
|
||||
// model is *shared* -- `ir.Operand` is one discriminated value (LIT_INT /
|
||||
// LIT_FLOAT / REF / TYPE / ATTRIBUTE) -- and a dialect's structured immediates
|
||||
// "ride in `aux` + the IR's opcode table" (docs/ir_design.md §2). So WASM maps
|
||||
// its immediates onto `ir.Operand` like this:
|
||||
//
|
||||
// i32.const / i64.const value -> LIT_INT (op_int)
|
||||
// f32.const / f64.const -> LIT_FLOAT (op_float, aux = width)
|
||||
// an index (local/func/...) -> REF (op_wasm_index; space is the
|
||||
// best-fit ir.Ref_Space, and
|
||||
// the exact WASM Index_Kind is
|
||||
// kept in `aux`, losslessly)
|
||||
// a branch label depth -> REF (.BLOCK) (op_labelidx)
|
||||
// a blocktype (s33) -> ATTRIBUTE (op_blocktype)
|
||||
// a memarg (align, offset) -> ATTRIBUTE (op_memarg; both packed in imm)
|
||||
// a ref.null heap type -> ATTRIBUTE (op_reftype)
|
||||
// a SIMD lane index -> ATTRIBUTE (op_lane)
|
||||
// a v128 literal / shuffle mask -> two ATTRIBUTE (op_v128 -> lo, hi halves)
|
||||
//
|
||||
// Nothing needs an inline byte blob: the one 16-byte immediate (v128.const,
|
||||
// i8x16.shuffle) is carried as two 64-bit ATTRIBUTE halves. `br_table`'s label
|
||||
// vector is not a special field either -- with `ir.Operation.operands` now being
|
||||
// variable-arity, it is just `[default, case0, case1, ...]`, every entry a BLOCK
|
||||
// ref (see builder.odin / the encoder's BR_TABLE case).
|
||||
|
||||
// Which WASM index space an index immediate addresses. Drives the on-wire
|
||||
// encoding, relocation-type selection, and printer annotation. Kept in
|
||||
// `ir.Operand.aux` for every REF operand so the exact space always round-trips,
|
||||
// even where the shared `ir.Ref_Space` has no dedicated member (TABLE/DATA/ELEM).
|
||||
Index_Kind :: enum u16 {
|
||||
NONE,
|
||||
LOCAL,
|
||||
GLOBAL,
|
||||
FUNC,
|
||||
TYPE,
|
||||
TABLE,
|
||||
MEMORY,
|
||||
LABEL, // br / br_if / br_table relative depth
|
||||
DATA,
|
||||
ELEM,
|
||||
}
|
||||
|
||||
// The `ir.Operand.aux` tag for the ATTRIBUTE-kind WASM immediates. (REF operands
|
||||
// instead put an `Index_Kind` in `aux`; the opcode's ENCODING_TABLE form always
|
||||
// says which of the two an operand slot is, so the two uses never collide.)
|
||||
Attr :: enum u16 {
|
||||
NONE,
|
||||
BLOCKTYPE,
|
||||
MEMARG,
|
||||
REFTYPE,
|
||||
LANE,
|
||||
V128_LO, // low 8 bytes of a v128 literal / shuffle mask
|
||||
V128_HI, // high 8 bytes
|
||||
}
|
||||
|
||||
// `ir.Operand.flags` bit: a REF whose index is a symbol id the linker fixes up
|
||||
// (emitted as a fixed-width 5-byte LEB placeholder plus a Relocation).
|
||||
FLAG_SYMBOLIC :: u32(1)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Index_Kind <-> ir.Ref_Space
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Best-fit shared space for a WASM index space (used for printer annotation and
|
||||
// generic ir tooling). The exact `Index_Kind` still lives in `aux`, so spaces
|
||||
// the shared enum lacks (TABLE/DATA/ELEM) map to NONE here without loss.
|
||||
@(require_results)
|
||||
ref_space_for :: #force_inline proc "contextless" (k: Index_Kind) -> Ref_Space {
|
||||
switch k {
|
||||
case .LOCAL: return .VALUE // a local / value-stack slot (ir_design: VALUE)
|
||||
case .GLOBAL: return .GLOBAL
|
||||
case .FUNC: return .FUNCTION
|
||||
case .TYPE: return .TYPE
|
||||
case .MEMORY: return .MEMORY
|
||||
case .LABEL: return .BLOCK
|
||||
case .TABLE, .DATA, .ELEM: return .NONE
|
||||
case .NONE: return .NONE
|
||||
}
|
||||
return .NONE
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Numeric constants
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@(require_results) op_i32 :: #force_inline proc "contextless" (v: i32) -> Operand { return op_int(i64(v)) }
|
||||
@(require_results) op_i64 :: #force_inline proc "contextless" (v: i64) -> Operand { return op_int(v) }
|
||||
@(require_results) op_f32 :: #force_inline proc "contextless" (v: f32) -> Operand { return op_float(u64(transmute(u32)v), 32) }
|
||||
@(require_results) op_f64 :: #force_inline proc "contextless" (v: f64) -> Operand { return op_float(transmute(u64)v, 64) }
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Index-space constructors (one per space; all unsigned LEB128 on the wire)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@(require_results)
|
||||
op_wasm_index :: #force_inline proc "contextless" (kind: Index_Kind, value: u32, symbolic := false) -> Operand {
|
||||
o := op_ref(ref_space_for(kind), Id(value))
|
||||
o.aux = u16(kind)
|
||||
if symbolic { o.flags |= FLAG_SYMBOLIC }
|
||||
return o
|
||||
}
|
||||
|
||||
@(require_results) op_local :: #force_inline proc "contextless" (n: u32) -> Operand { return op_wasm_index(.LOCAL, n) }
|
||||
@(require_results) op_global :: #force_inline proc "contextless" (n: u32) -> Operand { return op_wasm_index(.GLOBAL, n) }
|
||||
@(require_results) op_func :: #force_inline proc "contextless" (n: u32) -> Operand { return op_wasm_index(.FUNC, n) }
|
||||
@(require_results) op_typeidx:: #force_inline proc "contextless" (n: u32) -> Operand { return op_wasm_index(.TYPE, n) }
|
||||
@(require_results) op_table :: #force_inline proc "contextless" (n: u32) -> Operand { return op_wasm_index(.TABLE, n) }
|
||||
@(require_results) op_memory :: #force_inline proc "contextless" (n: u32) -> Operand { return op_wasm_index(.MEMORY, n) }
|
||||
@(require_results) op_data :: #force_inline proc "contextless" (n: u32) -> Operand { return op_wasm_index(.DATA, n) }
|
||||
@(require_results) op_elem :: #force_inline proc "contextless" (n: u32) -> Operand { return op_wasm_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_wasm_index(.LABEL, depth) }
|
||||
|
||||
// Symbolic function reference: emitted as a relocatable funcidx placeholder.
|
||||
@(require_results) op_label :: #force_inline proc "contextless" (label_id: u32) -> Operand { return op_wasm_index(.FUNC, label_id, symbolic = true) }
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Dialect ATTRIBUTE immediates
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@(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" (ma: Memarg) -> Operand {
|
||||
packed := i64(u64(ma.offset) | (u64(ma.align) << 32))
|
||||
return Operand{kind = .ATTRIBUTE, imm = packed, aux = u16(Attr.MEMARG)}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
op_blocktype :: #force_inline proc "contextless" (bt: Block_Type) -> Operand {
|
||||
return Operand{kind = .ATTRIBUTE, imm = i64(bt), aux = u16(Attr.BLOCKTYPE)}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
op_block_typeidx :: #force_inline proc "contextless" (type_index: u32) -> Operand {
|
||||
return Operand{kind = .ATTRIBUTE, imm = i64(type_index), aux = u16(Attr.BLOCKTYPE)}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
op_reftype :: #force_inline proc "contextless" (t: Value_Type) -> Operand {
|
||||
return Operand{kind = .ATTRIBUTE, imm = i64(t), aux = u16(Attr.REFTYPE)}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
op_lane :: #force_inline proc "contextless" (n: u8) -> Operand {
|
||||
return Operand{kind = .ATTRIBUTE, imm = i64(n), aux = u16(Attr.LANE)}
|
||||
}
|
||||
|
||||
// A 16-byte v128 literal (v128.const) or shuffle mask (i8x16.shuffle), as two
|
||||
// little-endian 64-bit ATTRIBUTE halves.
|
||||
@(require_results)
|
||||
op_v128 :: #force_inline proc "contextless" (bytes: [16]u8) -> (lo, hi: Operand) {
|
||||
l, h: u64
|
||||
for i in 0..<8 { l |= u64(bytes[i + 0]) << u64(i * 8) }
|
||||
for i in 0..<8 { h |= u64(bytes[i + 8]) << u64(i * 8) }
|
||||
lo = Operand{kind = .ATTRIBUTE, imm = i64(l), aux = u16(Attr.V128_LO)}
|
||||
hi = Operand{kind = .ATTRIBUTE, imm = i64(h), aux = u16(Attr.V128_HI)}
|
||||
return
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Accessors (reconstruct the WASM payload carried by an ir.Operand)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@(require_results) operand_index :: #force_inline proc "contextless" (o: Operand) -> u32 { return u32(o.imm) }
|
||||
@(require_results) operand_index_kind :: #force_inline proc "contextless" (o: Operand) -> Index_Kind { return Index_Kind(o.aux) }
|
||||
@(require_results) operand_symbolic :: #force_inline proc "contextless" (o: Operand) -> bool { return o.flags & FLAG_SYMBOLIC != 0 }
|
||||
@(require_results) operand_attr :: #force_inline proc "contextless" (o: Operand) -> Attr { return Attr(o.aux) }
|
||||
|
||||
@(require_results)
|
||||
operand_memarg :: #force_inline proc "contextless" (o: Operand) -> Memarg {
|
||||
u := u64(o.imm)
|
||||
return Memarg{offset = u32(u), align = u32(u >> 32)}
|
||||
}
|
||||
|
||||
// v128 bytes from a lo/hi ATTRIBUTE pair.
|
||||
@(require_results)
|
||||
operand_v128 :: #force_inline proc "contextless" (lo, hi: Operand) -> (bytes: [16]u8) {
|
||||
l, h := u64(lo.imm), u64(hi.imm)
|
||||
for i in 0..<8 { bytes[i + 0] = u8(l >> u64(i * 8)) }
|
||||
for i in 0..<8 { bytes[i + 8] = u8(h >> u64(i * 8)) }
|
||||
return
|
||||
}
|
||||
291
core/rexcode/ir/wasm/printer.odin
Normal file
291
core/rexcode/ir/wasm/printer.odin
Normal file
@@ -0,0 +1,291 @@
|
||||
// rexcode · Brendan Punsky (dotbmp@github), original author
|
||||
// Ginger Bill (gingerBill@github)
|
||||
|
||||
package rexcode_wasm
|
||||
|
||||
import "core:strings"
|
||||
import "core:strconv"
|
||||
|
||||
// =============================================================================
|
||||
// SECTION: Printer (Module -> WebAssembly text listing)
|
||||
// =============================================================================
|
||||
//
|
||||
// The third ir verb (docs/ir_design.md §4), same signature as the SPIR-V
|
||||
// printer: `print(m, sb, options)`, disassembling the module into a caller-owned
|
||||
// builder. It walks functions -> blocks -> operations and emits one instruction
|
||||
// per line in the linear (unfolded) WAT form
|
||||
//
|
||||
// <mnemonic> <immediate>*
|
||||
//
|
||||
// e.g.
|
||||
//
|
||||
// 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
|
||||
// v128.const i8x16 0x01 0x02 ...
|
||||
//
|
||||
// The folded-stack form is not reconstructed (that needs structure the linear
|
||||
// stream does not carry). Mnemonic spelling comes from the explicit
|
||||
// MNEMONIC_NAMES table (WASM mixes '.' and '_' irregularly, e.g. `local.get`
|
||||
// vs `i32.trunc_f32_s`). Immediates are recovered from the shared `ir.Operand`
|
||||
// model via the operands.odin accessors -- structured payloads (memarg,
|
||||
// blocktype, reftype, lanes, v128) ride in ATTRIBUTE operands tagged in `aux`.
|
||||
|
||||
// print: disassemble `m` into the caller-owned builder `sb`. The canonical ir
|
||||
// print verb; `sbprint` / `tprint` below are thin convenience sinks over it.
|
||||
print :: proc(m: Module, sb: ^strings.Builder, options: ^Print_Options = nil) {
|
||||
opts := DEFAULT_PRINT_OPTIONS
|
||||
if options != nil { opts = options^ }
|
||||
|
||||
for fn in m.functions {
|
||||
if fn.name != "" {
|
||||
strings.write_string(sb, "func ")
|
||||
strings.write_string(sb, fn.name)
|
||||
strings.write_string(sb, ":")
|
||||
strings.write_string(sb, opts.separator)
|
||||
}
|
||||
for blk in fn.blocks {
|
||||
for &op in blk.ops {
|
||||
write_operation(sb, &op, &opts)
|
||||
strings.write_string(sb, opts.separator)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sbprint: disassemble a bare operation stream (a WASM `expr`) -- the reusable
|
||||
// core that `print` runs per block, and the mirror of the encoder's
|
||||
// `encode_ops` / decoder's `decode_ops`.
|
||||
sbprint :: proc(sb: ^strings.Builder, ops: []Operation, options: ^Print_Options = nil) {
|
||||
opts := DEFAULT_PRINT_OPTIONS
|
||||
if options != nil { opts = options^ }
|
||||
for &op in ops {
|
||||
write_operation(sb, &op, &opts)
|
||||
strings.write_string(sb, opts.separator)
|
||||
}
|
||||
}
|
||||
|
||||
// tprint: temp-allocated string of an operation stream (spot-checks / debugging).
|
||||
@(require_results)
|
||||
tprint :: proc(ops: []Operation, options: ^Print_Options = nil) -> string {
|
||||
sb := strings.builder_make(context.temp_allocator)
|
||||
sbprint(&sb, ops, options)
|
||||
return strings.to_string(sb)
|
||||
}
|
||||
|
||||
// aprint: caller-allocated string of an operation stream.
|
||||
@(require_results)
|
||||
aprint :: proc(ops: []Operation, options: ^Print_Options = nil, allocator := context.allocator) -> string {
|
||||
sb := strings.builder_make(allocator)
|
||||
sbprint(&sb, ops, options)
|
||||
return strings.to_string(sb)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
mnemonic_to_string :: proc(op: Opcode, uppercase := false, allocator := context.temp_allocator) -> string {
|
||||
sb := strings.builder_make(allocator)
|
||||
write_mnemonic(&sb, op, uppercase)
|
||||
return strings.to_string(sb)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Per-operation writer
|
||||
// =============================================================================
|
||||
|
||||
write_operation :: proc(sb: ^strings.Builder, op: ^Operation, opts: ^Print_Options) {
|
||||
strings.write_string(sb, opts.indent)
|
||||
|
||||
opcode := Opcode(op.opcode)
|
||||
write_mnemonic(sb, opcode, opts.uppercase)
|
||||
|
||||
// A couple of opcodes carry a bespoke operand layout the generic per-operand
|
||||
// walk would mis-order; specialise those, and fall through for the rest.
|
||||
#partial switch opcode {
|
||||
case .BR_TABLE:
|
||||
// operands = [default, case0, case1, ...]; WAT prints the cases first,
|
||||
// then the default depth, matching the binary order.
|
||||
for c in op.operands[1:] {
|
||||
strings.write_byte(sb, ' ')
|
||||
strings.write_u64(sb, u64(operand_index(c)))
|
||||
}
|
||||
if len(op.operands) > 0 {
|
||||
strings.write_byte(sb, ' ')
|
||||
strings.write_u64(sb, u64(operand_index(op.operands[0])))
|
||||
}
|
||||
|
||||
case .V128_CONST:
|
||||
strings.write_string(sb, " i8x16")
|
||||
if len(op.operands) >= 2 {
|
||||
bytes := operand_v128(op.operands[0], op.operands[1])
|
||||
for bb in bytes {
|
||||
strings.write_byte(sb, ' ')
|
||||
print_hex(sb, u64(bb), opts)
|
||||
}
|
||||
}
|
||||
|
||||
case .I8X16_SHUFFLE:
|
||||
if len(op.operands) >= 2 {
|
||||
bytes := operand_v128(op.operands[0], op.operands[1])
|
||||
for bb in bytes {
|
||||
strings.write_byte(sb, ' ')
|
||||
strings.write_u64(sb, u64(bb))
|
||||
}
|
||||
}
|
||||
|
||||
case:
|
||||
for &o in op.operands {
|
||||
write_operand(sb, &o, opcode, opts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Internal writers
|
||||
// =============================================================================
|
||||
|
||||
write_mnemonic :: proc(sb: ^strings.Builder, op: Opcode, uppercase: bool) {
|
||||
name := MNEMONIC_NAMES[op]
|
||||
if name == "" {
|
||||
strings.write_string(sb, "<?>")
|
||||
return
|
||||
}
|
||||
if uppercase {
|
||||
for i in 0..<len(name) {
|
||||
switch c := name[i]; c { // force ASCII
|
||||
case 'a'..='z':
|
||||
strings.write_byte(sb, c - 32)
|
||||
case:
|
||||
strings.write_byte(sb, c)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
strings.write_string(sb, name)
|
||||
}
|
||||
}
|
||||
|
||||
// One `ir.Operand`, dispatched on its shared kind (then, for ATTRIBUTE, on the
|
||||
// WASM `Attr` tag in `aux`). A leading space separates it from the mnemonic /
|
||||
// previous operand -- matching the old ISA printer's spacing.
|
||||
write_operand :: proc(sb: ^strings.Builder, o: ^Operand, opcode: Opcode, opts: ^Print_Options) {
|
||||
switch o.kind {
|
||||
case .NONE:
|
||||
// nothing
|
||||
|
||||
case .LIT_INT:
|
||||
strings.write_byte(sb, ' ')
|
||||
strings.write_i64(sb, o.imm)
|
||||
|
||||
case .LIT_FLOAT:
|
||||
strings.write_byte(sb, ' ')
|
||||
write_float(sb, o)
|
||||
|
||||
case .REF:
|
||||
strings.write_byte(sb, ' ')
|
||||
if operand_symbolic(o^) {
|
||||
write_label(sb, operand_index(o^))
|
||||
} else {
|
||||
strings.write_u64(sb, u64(operand_index(o^)))
|
||||
}
|
||||
|
||||
case .TYPE:
|
||||
strings.write_byte(sb, ' ')
|
||||
strings.write_string(sb, "(type ")
|
||||
strings.write_u64(sb, u64(u32(operand_type(o^))))
|
||||
strings.write_byte(sb, ')')
|
||||
|
||||
case .ATTRIBUTE:
|
||||
write_attribute(sb, o, opts)
|
||||
}
|
||||
}
|
||||
|
||||
write_attribute :: proc(sb: ^strings.Builder, o: ^Operand, opts: ^Print_Options) {
|
||||
switch operand_attr(o^) {
|
||||
case .NONE:
|
||||
// nothing
|
||||
|
||||
case .BLOCKTYPE:
|
||||
write_block_type(sb, o.imm)
|
||||
|
||||
case .MEMARG:
|
||||
// WAT prints non-trivial memargs as `offset=N align=N`, omitting either
|
||||
// when it is the natural default. (`align` here is the byte alignment,
|
||||
// already expanded from the on-wire log2 by the decoder.)
|
||||
ma := operand_memarg(o^)
|
||||
if ma.offset != 0 {
|
||||
strings.write_string(sb, " offset=")
|
||||
strings.write_u64(sb, u64(ma.offset))
|
||||
}
|
||||
if ma.align != 0 {
|
||||
strings.write_string(sb, " align=")
|
||||
strings.write_u64(sb, u64(ma.align))
|
||||
}
|
||||
|
||||
case .REFTYPE:
|
||||
strings.write_byte(sb, ' ')
|
||||
write_heap_type(sb, u8(o.imm))
|
||||
|
||||
case .LANE:
|
||||
strings.write_byte(sb, ' ')
|
||||
strings.write_u64(sb, u64(u8(o.imm)))
|
||||
|
||||
case .V128_LO, .V128_HI:
|
||||
// v128 literals are printed by the V128_CONST / I8X16_SHUFFLE special
|
||||
// cases in write_operation; a stray half here needs no output.
|
||||
}
|
||||
}
|
||||
|
||||
// A blocktype annotation. Returns nothing meaningful; kept a proc for symmetry
|
||||
// with the old printer. EMPTY prints nothing (the common `block`/`loop` case).
|
||||
write_block_type :: proc(sb: ^strings.Builder, v: i64) -> (printed: bool) {
|
||||
switch Block_Type(v) {
|
||||
case .EMPTY: // no result annotation
|
||||
case .I32: strings.write_string(sb, " (result i32)"); return true
|
||||
case .I64: strings.write_string(sb, " (result i64)"); return true
|
||||
case .F32: strings.write_string(sb, " (result f32)"); return true
|
||||
case .F64: strings.write_string(sb, " (result f64)"); return true
|
||||
case .V128: strings.write_string(sb, " (result v128)"); return true
|
||||
case .FUNCREF: strings.write_string(sb, " (result funcref)"); return true
|
||||
case .EXTERNREF: strings.write_string(sb, " (result externref)"); return true
|
||||
case:
|
||||
if v >= 0 {
|
||||
// a type index (positive s33)
|
||||
strings.write_string(sb, " (type ")
|
||||
strings.write_u64(sb, u64(u32(v)))
|
||||
strings.write_byte(sb, ')')
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
write_heap_type :: proc(sb: ^strings.Builder, b: u8) {
|
||||
#partial switch Value_Type(b) {
|
||||
case .FUNCREF: strings.write_string(sb, "func")
|
||||
case .EXTERNREF: strings.write_string(sb, "extern")
|
||||
case: strings.write_u64(sb, u64(u32(b)))
|
||||
}
|
||||
}
|
||||
|
||||
write_float :: proc(sb: ^strings.Builder, o: ^Operand) {
|
||||
buf: [40]u8
|
||||
if o.aux == 32 {
|
||||
f := transmute(f32)u32(o.imm)
|
||||
s := strconv.write_float(buf[:], f64(f), 'g', -1, 32)
|
||||
strings.write_string(sb, s)
|
||||
} else {
|
||||
f := transmute(f64)u64(o.imm)
|
||||
s := strconv.write_float(buf[:], f, 'g', -1, 64)
|
||||
strings.write_string(sb, s)
|
||||
}
|
||||
}
|
||||
|
||||
write_label :: proc(sb: ^strings.Builder, label_id: u32) {
|
||||
strings.write_byte(sb, '$')
|
||||
strings.write_u64(sb, u64(label_id))
|
||||
}
|
||||
43
core/rexcode/ir/wasm/reloc.odin
Normal file
43
core/rexcode/ir/wasm/reloc.odin
Normal file
@@ -0,0 +1,43 @@
|
||||
// rexcode · Brendan Punsky (dotbmp@github), original author
|
||||
// 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)
|
||||
110
core/rexcode/ir/wasm/wasm.odin
Normal file
110
core/rexcode/ir/wasm/wasm.odin
Normal file
@@ -0,0 +1,110 @@
|
||||
// rexcode · Brendan Punsky (dotbmp@github), original author
|
||||
// Ginger Bill (gingerBill@github)
|
||||
|
||||
// rexcode/ir/wasm -- the WebAssembly intermediate representation.
|
||||
//
|
||||
// WASM is a stack bytecode: a byte stream with LEB128 immediates, one encoding
|
||||
// form per opcode, over an *implicit value stack* with a minimal type system.
|
||||
// On the two axes that sort the IR family (docs/ir_design.md §2) it is
|
||||
// **table-driven** on encoding -- a static `opcode -> operand-layout` table,
|
||||
// exactly the ISA `ENCODING_TABLE` shape -- and **STACK** on dataflow (the one
|
||||
// outlier among the modelled IRs; SPIR-V/LLVM are SSA).
|
||||
//
|
||||
// So this package is a sibling of the SPIR-V package under `ir/`: it re-exports
|
||||
// the shared `ir` vocabulary (Module / Function / Block / Operation / Operand /
|
||||
// Type / Id / ...), owns a `u16` `Opcode` (the WASM opcode set, with the 0xFC/
|
||||
// 0xFD/0xFE prefix groups folded in), a static `ENCODING_TABLE` that drives a
|
||||
// byte/LEB codec, the three Module-based verbs (`encode` / `decode` / `print`),
|
||||
// a `Relocation` for object-file index fixups, and the WASM value-type model.
|
||||
//
|
||||
// This is the migration anticipated by docs/ir_design.md §4: WASM was shaped as
|
||||
// an ISA arch package (`core:rexcode/wasm`); its real `encode`/`decode` already
|
||||
// dropped the ISA verbs' `label_defs`/`resolve`/`base_address` (WASM has no
|
||||
// PC-relative pass -- branches carry structured label depths), so moving it onto
|
||||
// the `ir` Module/Operation/Operand model is a change of *leaf shape*, not of
|
||||
// codec. The table-driven codec below is carried over intact; what changed is
|
||||
// the leaf (`ir.Operation` in place of the old fixed-operand `Instruction`), the
|
||||
// operand model (`ir.Operand`, with WASM memarg/blocktype/lane riding in `aux`),
|
||||
// and the unit of work (an `ir.Module` of functions->blocks->operations).
|
||||
package rexcode_wasm
|
||||
|
||||
import "core:rexcode/ir"
|
||||
|
||||
// =============================================================================
|
||||
// SECTION: Re-exported shared vocabulary (the IR naming contract, ir_design §4)
|
||||
// =============================================================================
|
||||
//
|
||||
// A consumer sees one namespace -- `wasm.Operation`, `wasm.Operand`, ... -- the
|
||||
// way an arch package re-exports `isa`. `Module` is NOT re-exported: WASM's
|
||||
// module carries container sections (types / imports / exports / ...) the shared
|
||||
// core has no slot for, so it is a superset struct embedding `ir.Module`; see
|
||||
// module.odin (exactly as `spirv.Module` does).
|
||||
|
||||
Function :: ir.Function
|
||||
Block :: ir.Block
|
||||
Global :: ir.Global
|
||||
Operation :: ir.Operation
|
||||
Operation_Flags :: ir.Operation_Flags
|
||||
Operand :: ir.Operand
|
||||
Operand_Kind :: ir.Operand_Kind
|
||||
Result :: ir.Result
|
||||
Type :: ir.Type
|
||||
Type_Ref :: ir.Type_Ref
|
||||
Type_Kind :: ir.Type_Kind
|
||||
Id :: ir.Id
|
||||
Ref :: ir.Ref
|
||||
Ref_Space :: ir.Ref_Space
|
||||
Symbol_Table :: ir.Symbol_Table
|
||||
Dataflow :: ir.Dataflow
|
||||
Error :: ir.Error
|
||||
Error_Code :: ir.Error_Code
|
||||
Token :: ir.Token
|
||||
Token_Kind :: ir.Token_Kind
|
||||
Print_Options :: ir.Print_Options
|
||||
Print_Result :: ir.Print_Result
|
||||
|
||||
ID_NONE :: ir.ID_NONE
|
||||
TYPE_NONE :: ir.TYPE_NONE
|
||||
DEFAULT_PRINT_OPTIONS :: ir.DEFAULT_PRINT_OPTIONS
|
||||
|
||||
print_hex :: ir.print_hex
|
||||
print_decimal :: ir.print_decimal
|
||||
|
||||
// Shared operand / type / ref constructors (WASM adds the dialect helpers -- a
|
||||
// memarg, a blocktype, a lane index -- in operands.odin).
|
||||
op_int :: ir.op_int
|
||||
op_float :: ir.op_float
|
||||
op_type :: ir.op_type
|
||||
op_ref :: ir.op_ref
|
||||
op_value :: ir.op_value
|
||||
op_block :: ir.op_block
|
||||
operand_id :: ir.operand_id
|
||||
operand_type :: ir.operand_type
|
||||
ref :: ir.ref
|
||||
|
||||
type_void :: ir.type_void
|
||||
type_int :: ir.type_int
|
||||
type_float :: ir.type_float
|
||||
type_vector :: ir.type_vector
|
||||
type_pointer :: ir.type_pointer
|
||||
|
||||
symbol_table_init :: ir.symbol_table_init
|
||||
symbol_table_destroy :: ir.symbol_table_destroy
|
||||
symbol_define :: ir.symbol_define
|
||||
symbol_reserve :: ir.symbol_reserve
|
||||
symbol_lookup :: ir.symbol_lookup
|
||||
|
||||
// =============================================================================
|
||||
// SECTION: Physical format (WebAssembly core spec §5 -- the byte stream)
|
||||
// =============================================================================
|
||||
|
||||
// The four bytes that begin every module: "\0asm" as a little-endian u32.
|
||||
WASM_MAGIC :: u32(0x6d73_6100)
|
||||
WASM_VERSION :: u32(1)
|
||||
|
||||
// Opcode-space prefix bytes. A prefixed opcode is `prefix byte` then an unsigned
|
||||
// LEB128 sub-opcode (SIMD reaches 0x113, so the sub-opcode can be two 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
|
||||
Reference in New Issue
Block a user