mirror of
https://github.com/odin-lang/Odin.git
synced 2026-07-22 15:32:35 +00:00
96 lines
4.0 KiB
Odin
96 lines
4.0 KiB
Odin
// 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
|
|
}
|