pub struct ChunkBuilder { /* private fields */ }Expand description
Fluent assembler for Chunks.
This exists because hand-computing relative jump offsets (design notes
§7 shows raw opcodes) is exactly the kind of bookkeeping that produces
off-by-one bytecode bugs that only show up as a wrong branch at runtime.
The builder defers that arithmetic: emit a Jump/Branch against a
Label, bind the label once you know where it lands, and the builder
back-patches every use.
This is the only supported way to hand-author a Chunk in this crate;
a source-level compiler (design notes’ long-term “Rust → bytecode” path)
would sit on top of this same API.
Implementations§
Source§impl ChunkBuilder
impl ChunkBuilder
pub fn const_(&mut self, v: Value) -> u32
pub fn new_label(&mut self) -> Label
Sourcepub fn bind_label(&mut self, label: Label)
pub fn bind_label(&mut self, label: Label)
Bind label to the next instruction that will be emitted.
pub fn emit_halt(&mut self)
pub fn emit_load_const(&mut self, dst: u8, konst: u32)
Sourcepub fn emit_load_imm(&mut self, dst: u8, imm: i32)
pub fn emit_load_imm(&mut self, dst: u8, imm: i32)
pub fn emit_move(&mut self, dst: u8, src: u8)
pub fn emit_binop(&mut self, op: Opcode, dst: u8, lhs: u8, rhs: u8)
pub fn emit_neg(&mut self, dst: u8, src: u8)
pub fn emit_jump(&mut self, target: Label)
pub fn emit_branch(&mut self, cond: u8, target: Label)
pub fn emit_spawn(&mut self, dst: u8, function: u32, argc: u8)
pub fn emit_yield(&mut self)
pub fn emit_sleep(&mut self, millis_reg: u8)
pub fn emit_exit(&mut self, reg: u8)
Sourcepub fn emit_self_pid(&mut self, dst: u8)
pub fn emit_self_pid(&mut self, dst: u8)
Write a self Cap (SEND|ASK) into dst (opcode still named SelfPid).
Sourcepub fn emit_send(&mut self, target_cap_reg: u8, msg_reg: u8)
pub fn emit_send(&mut self, target_cap_reg: u8, msg_reg: u8)
Fire-and-forget Atomic Hop: r[target_cap_reg] must be Cap; r[msg_reg] Message.
pub fn emit_receive(&mut self, dst: u8)
pub fn emit_receive_timeout(&mut self, dst: u8, millis_reg: u8)
Sourcepub fn emit_receive_match(&mut self, dst: u8, tag_reg: u8)
pub fn emit_receive_match(&mut self, dst: u8, tag_reg: u8)
Selective Atomic Hop: wait for Message with tag == r[tag_reg].
Sourcepub fn emit_receive_match_imm(&mut self, dst: u8, tag: u16)
pub fn emit_receive_match_imm(&mut self, dst: u8, tag: u16)
Selective Atomic Hop with an immediate u16 tag.
Sourcepub fn emit_ask(&mut self, dest: u8, target_cap_reg: u8, msg_reg: u8)
pub fn emit_ask(&mut self, dest: u8, target_cap_reg: u8, msg_reg: u8)
Atomic request/reply hop: deliver r[msg_reg] to r[target_cap_reg] (Cap),
then wait for a correlated reply into dst.
Encoding: Ask ra, rb, rc → a=dest, b=target Cap, c=request Message.
The worker authenticates the request (sender + reply_cap) before delivery
and completes only when the reply’s sender equals the resolved FlowId.
pub fn emit_trap(&mut self, code: i32)
pub fn emit_call(&mut self, dst: u8, function: u32, argc: u8)
Sourcepub fn emit_call_native(&mut self, dst: u8, native_index: u32, argc: u8)
pub fn emit_call_native(&mut self, dst: u8, native_index: u32, argc: u8)
Emit a call through the runtime’s native (FFI) function table
(design notes §30-31). native_index is resolved by name against a
crate::NativeTable at the call site — the assembler has no
knowledge of what natives exist, on purpose (see
crate::verify’s note on why CallNative targets aren’t
range-checked statically).
Sourcepub fn emit_native1_from(&mut self, dst: u8, src: u8, native_index: u32)
pub fn emit_native1_from(&mut self, dst: u8, src: u8, native_index: u32)
Move src into dst, then CallNative(dst, native_index, 1).
§The contract this exists to protect: CallNative clobbers its argument
Opcode::CallNative ra, fb, nc reads nc arguments from
r[a..a+nc] and writes the result back into r[a]. For nc == 1
the argument and result are the same slot — calling a one-arg native
straight on a register you still need destroys it.
The textbook case is unpacking several fields from one Message in
r0 (msg_sender, msg_tag, …). emit_native1_from always operates
on a copy (dst), so src survives:
b.emit_native1_from(1, 0, native_msg_sender); // r1 = sender(r0)
b.emit_native1_from(2, 0, native_msg_request_id);If you don’t need src afterwards, call emit_call_native directly —
the Move would be pure overhead. See crate::emit_native1_from for
the macro-sugar form that forwards here.
Sourcepub fn emit_native_n(&mut self, base: u8, native_index: u32, argc: u8)
pub fn emit_native_n(&mut self, base: u8, native_index: u32, argc: u8)
CallNative(base, native_index, argc) when argc args are already
contiguous at r[base..base+argc].
No behavior beyond Self::emit_call_native — exists so the call site
reads as “args already packed”. See crate::emit_native_n.
Sourcepub fn emit_return(&mut self, reg: u8)
pub fn emit_return(&mut self, reg: u8)
Sourcepub fn begin_function(
&mut self,
name: impl Into<String>,
arity: u8,
num_registers: u8,
) -> u32
pub fn begin_function( &mut self, name: impl Into<String>, arity: u8, num_registers: u8, ) -> u32
Mark the start of a bytecode function at the current position and
register it in the function table under name. Returns the function
index, usable with ChunkBuilder::emit_call/ChunkBuilder::emit_spawn
even before the function’s body is emitted (functions may call
themselves or each other, forward or backward).
pub fn function_index(&self, name: &str) -> Option<u32>
Sourcepub fn set_num_registers(&mut self, function_index: u32, num_registers: u8)
pub fn set_num_registers(&mut self, function_index: u32, num_registers: u8)
Patch the register-file size of an already-begin_function’d
function. Exists for assemblers whose register count is only known
after emitting the body — begin_function must still be called
first so entry captures the current code cursor.