Skip to main content

ChunkBuilder

Struct ChunkBuilder 

Source
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

Source

pub fn new(name: impl Into<String>) -> Self

Examples found in repository?
examples/throughput.rs (line 12)
11fn trivial_chunk() -> byteflow::Chunk {
12    let mut b = ChunkBuilder::new("throughput");
13    b.begin_function("worker", 0, 1);
14    b.emit_load_imm(0, 1);
15    b.emit_return(0);
16    b.finish()
17}
Source

pub fn const_(&mut self, v: Value) -> u32

Source

pub fn new_label(&mut self) -> Label

Source

pub fn bind_label(&mut self, label: Label)

Bind label to the next instruction that will be emitted.

Source

pub fn emit_halt(&mut self)

Source

pub fn emit_load_const(&mut self, dst: u8, konst: u32)

Source

pub fn emit_load_imm(&mut self, dst: u8, imm: i32)

Examples found in repository?
examples/throughput.rs (line 14)
11fn trivial_chunk() -> byteflow::Chunk {
12    let mut b = ChunkBuilder::new("throughput");
13    b.begin_function("worker", 0, 1);
14    b.emit_load_imm(0, 1);
15    b.emit_return(0);
16    b.finish()
17}
Source

pub fn emit_move(&mut self, dst: u8, src: u8)

Source

pub fn emit_binop(&mut self, op: Opcode, dst: u8, lhs: u8, rhs: u8)

Source

pub fn emit_neg(&mut self, dst: u8, src: u8)

Source

pub fn emit_jump(&mut self, target: Label)

Source

pub fn emit_branch(&mut self, cond: u8, target: Label)

Source

pub fn emit_spawn(&mut self, dst: u8, function: u32, argc: u8)

Source

pub fn emit_yield(&mut self)

Source

pub fn emit_sleep(&mut self, millis_reg: u8)

Source

pub fn emit_exit(&mut self, reg: u8)

Source

pub fn emit_self_pid(&mut self, dst: u8)

Write a self Cap (SEND|ASK) into dst (opcode still named SelfPid).

Source

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.

Source

pub fn emit_receive(&mut self, dst: u8)

Source

pub fn emit_receive_timeout(&mut self, dst: u8, millis_reg: u8)

Source

pub fn emit_receive_match(&mut self, dst: u8, tag_reg: u8)

Selective Atomic Hop: wait for Message with tag == r[tag_reg].

Source

pub fn emit_receive_match_imm(&mut self, dst: u8, tag: u16)

Selective Atomic Hop with an immediate u16 tag.

Source

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, rca=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.

Source

pub fn emit_trap(&mut self, code: i32)

Source

pub fn emit_call(&mut self, dst: u8, function: u32, argc: u8)

Source

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).

Source

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.

Source

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.

Source

pub fn emit_return(&mut self, reg: u8)

Examples found in repository?
examples/throughput.rs (line 15)
11fn trivial_chunk() -> byteflow::Chunk {
12    let mut b = ChunkBuilder::new("throughput");
13    b.begin_function("worker", 0, 1);
14    b.emit_load_imm(0, 1);
15    b.emit_return(0);
16    b.finish()
17}
Source

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).

Examples found in repository?
examples/throughput.rs (line 13)
11fn trivial_chunk() -> byteflow::Chunk {
12    let mut b = ChunkBuilder::new("throughput");
13    b.begin_function("worker", 0, 1);
14    b.emit_load_imm(0, 1);
15    b.emit_return(0);
16    b.finish()
17}
Source

pub fn function_index(&self, name: &str) -> Option<u32>

Source

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.

Source

pub fn finish(self) -> Chunk

Resolve every pending jump against its bound label and produce the final immutable Chunk. Panics (a build-time bug, not a runtime fault) if a label was referenced but never bound.

Examples found in repository?
examples/throughput.rs (line 16)
11fn trivial_chunk() -> byteflow::Chunk {
12    let mut b = ChunkBuilder::new("throughput");
13    b.begin_function("worker", 0, 1);
14    b.emit_load_imm(0, 1);
15    b.emit_return(0);
16    b.finish()
17}

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.