Skip to content

std/inst

std/wasm/inst — control-flow, constant, and variable instruction encoders for the WebAssembly Core 1.0 binary format. Spec: https://webassembly.github.io/spec/core/binary/instructions.html Third slice of Phase 1 of docs/TOOLCHAIN-SELF-HOSTING.md. After leb128 (variable-length ints) and encode (module container, sections, functypes), this slice handles the instruction subset needed to assemble a function body: - constants (i32 / i64 / f32 / f64) - parametric: drop, select - variable: local.get/set/tee, global.get/set - control: unreachable, nop, block, loop, if/else, end, br, br_if, return, call, call_indirect - code-section helpers: put_function_body, put_locals_* Arithmetic, comparison, memory, and conversion instructions (the bulk of the opcode space) are deferred to a later slice — each one is a single function (opcode byte + maybe an immediate) so they’re cheap to add, but together they bloat this PR more than is useful for review. Same shape as the rest of std/wasm: every encoder takes a u8[] and appends. The block / loop / if forms emit only the start of the construct (opcode + blocktype); the caller appends the body and calls inst_end to close it. That matches the stream-y nature of wasm function bodies and avoids a tree- shaped intermediate.

pub function blocktype_empty(): u8
pub function put_blocktype_typeidx(buf: u8[], idx: i32): u8[]
pub function inst_i32_const(buf: u8[], v: i32): u8[]
pub function inst_i64_const(buf: u8[], v: i64): u8[]
pub function inst_f32_const(buf: u8[], bits: u32): u8[]

f32.const carries the IEEE-754 bit pattern as a fixed 4-byte little-endian u32 — no leb. The caller is responsible for having the bits in u32 form (float.bits-equivalent); this encoder just lays the bytes down.

pub function inst_f64_const(buf: u8[], bits: u64): u8[]

f64.const carries an 8-byte little-endian u64 of the bit pattern, same shape as f32 but doubled.

pub function inst_drop(buf: u8[]): u8[]
pub function inst_select(buf: u8[]): u8[]
pub function inst_local_get(buf: u8[], idx: u32): u8[]
pub function inst_local_set(buf: u8[], idx: u32): u8[]
pub function inst_local_tee(buf: u8[], idx: u32): u8[]
pub function inst_global_get(buf: u8[], idx: u32): u8[]
pub function inst_global_set(buf: u8[], idx: u32): u8[]
pub function inst_unreachable(buf: u8[]): u8[]
pub function inst_nop(buf: u8[]): u8[]
pub function inst_block_start(buf: u8[], blocktype: u8): u8[]

inst_block_start appends block + the blocktype byte. The caller fills the body, then calls inst_end to write 0x0B. blocktype is either blocktype_empty() (0x40) or one of the encode.valtype_* bytes — anything that fits in a single byte. For typeidx-encoded blocktypes, the caller composes 0x02 + sleb_i32(idx) manually using put_blocktype_typeidx.

pub function inst_loop_start(buf: u8[], blocktype: u8): u8[]
pub function inst_if_start(buf: u8[], blocktype: u8): u8[]
pub function inst_else(buf: u8[]): u8[]
pub function inst_end(buf: u8[]): u8[]
pub function inst_br(buf: u8[], labelidx: u32): u8[]
pub function inst_br_if(buf: u8[], labelidx: u32): u8[]
pub function inst_return(buf: u8[]): u8[]
pub function inst_call(buf: u8[], funcidx: u32): u8[]
pub function inst_call_indirect(buf: u8[], typeidx: u32, tableidx: u32): u8[]

call_indirect carries both a type index (the expected signature) and a table index (0 in MVP, but the encoding reserves the slot for the multi-table proposal). Stack shape at the call site: … args, callee_index.

pub function put_locals_empty(buf: u8[]): u8[]

put_locals_empty — locals_vec of length 0: a single uleb(0).

pub function put_locals_one_group(buf: u8[], count: u32, vt: u8): u8[]

put_locals_one_group — count locals of a single valtype. Emits uleb(1) + uleb(count) + valtype byte. Covers the “function needs N i32 spill slots” case the existing wasm backend hits most often.

pub function put_function_body(buf: u8[], locals_bytes: u8[], body_bytes: u8[]): u8[]

put_function_body wraps a fully-assembled function body — locals_bytes (produced by one of the put_locals_* helpers) + body_bytes (the instruction sequence, NOT including the final 0x0B — this helper appends it) — with the size prefix the code section expects.