Skip to main content

FunctionBuilder

Struct FunctionBuilder 

Source
pub struct FunctionBuilder { /* private fields */ }
Expand description

Builds one function. Created by ModuleBuilder::function, added back with ModuleBuilder::add_function.

The parameters are registers r0..rN with the declared parameter types; reg declares more, in order, so consecutive calls return consecutive registers (what call windows need).

§Examples

use bytecode_lang::{Inst, IntOp, IntTy, ModuleBuilder, ValType};

// fn count(n: i64) -> i64 { let i = 0; while i < n { i = i + 1 } i }
let mut m = ModuleBuilder::new();
let mut f = m.function("count", &[ValType::I64], &[ValType::I64]);
let n = f.param(0);
let (i, one, more) = (f.reg(ValType::I64), f.reg(ValType::I64), f.reg(ValType::Bool));
let i64op = IntOp::new(IntTy::I64);
f.emit(Inst::LoadInt { dst: i, val: 0, ty: IntTy::I64 });
f.emit(Inst::LoadInt { dst: one, val: 1, ty: IntTy::I64 });
let (head, done) = (f.label(), f.label());
f.bind(head);
f.emit(Inst::ILt { dst: more, lhs: i, rhs: n, ty: IntTy::I64 });
f.jmp_if_not(more, done);
f.emit(Inst::IAdd { dst: i, lhs: i, rhs: one, op: i64op });
f.emit(Inst::Safepoint {});
f.jmp(head);
f.bind(done);
f.ret(i);
m.add_function(f).unwrap();
let module = m.finish().unwrap();
assert_eq!(module.functions()[0].code().len(), 8);

Implementations§

Source§

impl FunctionBuilder

Source

pub fn id(&self) -> FuncId

The id this function will have in the module.

§Examples
use bytecode_lang::{FuncId, ModuleBuilder};

let mut m = ModuleBuilder::new();
let f = m.function("f", &[], &[]);
assert_eq!(f.id(), FuncId(0));
Source

pub fn param(&self, index: u16) -> Reg

The register holding parameter index: parameters occupy the first registers, in order, so this is Reg(index).

§Examples
use bytecode_lang::{ModuleBuilder, Reg, ValType};

let mut m = ModuleBuilder::new();
let f = m.function("f", &[ValType::I64, ValType::F64], &[]);
assert_eq!(f.param(1), Reg(1));
assert_eq!(f.param_count(), 2);
Source

pub fn param_count(&self) -> u16

The number of parameters.

§Examples
use bytecode_lang::{ModuleBuilder, ValType};

let mut m = ModuleBuilder::new();
assert_eq!(m.function("f", &[ValType::Dyn], &[]).param_count(), 1);
Source

pub fn reg(&mut self, ty: ValType) -> Reg

Declares a register of type ty and returns it. Registers are numbered in declaration order.

§Examples
use bytecode_lang::{ModuleBuilder, Reg, ValType};

let mut m = ModuleBuilder::new();
let mut f = m.function("f", &[ValType::I64], &[]);
assert_eq!(f.reg(ValType::Dyn), Reg(1));
assert_eq!(f.reg(ValType::Dyn), Reg(2));
Source

pub fn regs(&mut self, types: &[ValType]) -> Reg

Declares one register per type, consecutively, and returns the first: a call window (dst then the arguments) in one step.

§Examples
use bytecode_lang::{ModuleBuilder, Reg, ValType};

let mut m = ModuleBuilder::new();
let mut f = m.function("f", &[], &[]);
let window = f.regs(&[ValType::I64, ValType::I64, ValType::I64]);
assert_eq!(window, Reg(0));
assert_eq!(f.reg(ValType::Bool), Reg(3));
Source

pub fn capture(&mut self, ty: ValType) -> UpvalIdx

Declares a captured value of type ty (read with Inst::GetUpval) and returns its index.

§Examples
use bytecode_lang::{ModuleBuilder, UpvalIdx, ValType};

let mut m = ModuleBuilder::new();
let mut f = m.function("f", &[], &[]);
assert_eq!(f.capture(ValType::I64), UpvalIdx(0));
assert_eq!(f.capture(ValType::Dyn), UpvalIdx(1));
Source

pub fn name_ref(&mut self, name: StrId) -> NameRef

The function-local NameRef for the string name, adding it to the function’s name list on first use.

§Examples
use bytecode_lang::{ModuleBuilder, NameRef};

let mut m = ModuleBuilder::new();
let (x, y) = (m.string("x"), m.string("y"));
let mut f = m.function("f", &[], &[]);
assert_eq!(f.name_ref(y), NameRef(0));
assert_eq!(f.name_ref(x), NameRef(1));
assert_eq!(f.name_ref(y), NameRef(0));
Source

pub fn type_ref(&mut self, ty: TypeId) -> TypeRef

The function-local TypeRef for the type ty, adding it to the function’s type list on first use.

§Examples
use bytecode_lang::{ModuleBuilder, TypeDef, TypeRef, ValType};

let mut m = ModuleBuilder::new();
let arr = m.add_type(TypeDef::Array(ValType::Dyn));
let mut f = m.function("f", &[], &[]);
assert_eq!(f.type_ref(arr), TypeRef(0));
assert_eq!(f.type_ref(arr), TypeRef(0));
Source

pub fn set_params(&mut self, list: ParamList)

Gives the function a dynamic-call signature: parameter names, kinds, by-reference flags, and defaults, which dcall/dcall_shape bind arguments to (specs/LSB.md §5.15). The list is checked when the function is added: its own rules (ParamList::validate) and that it fits the signature (ParamList::fits: one parameter per entry, and a trailing i64 presence mask when an entry has a default).

§Examples
use bytecode_lang::{ModuleBuilder, Param, ParamKind, ParamList, ValType};

// PHP: function f($x, ...$rest) — every function value of a dynamic
// language takes its arguments this way.
let mut m = ModuleBuilder::new();
let x = m.string("x");
let mut f = m.function("f", &[ValType::Dyn, ValType::Dyn], &[]);
f.set_params(ParamList::new(vec![Param::normal(x), Param::new(ParamKind::RestMap, None)]));
f.ret_void();
assert!(m.add_function(f).is_ok());
Source

pub fn call_shape(&mut self, args: &[ArgKind]) -> ShapeId

The function-local ShapeId for the call shape args, adding it to the function’s shape table on first use. A malformed shape (see CallShape::validate) is recorded and reported when the function is added.

§Examples
use bytecode_lang::{ArgKind, ModuleBuilder, ShapeId};

let mut m = ModuleBuilder::new();
let flag = m.string("flag");
let mut f = m.function("f", &[], &[]);
let s = f.call_shape(&[ArgKind::Spread, ArgKind::Named(flag)]);
assert_eq!(s, ShapeId(0));
assert_eq!(f.call_shape(&[ArgKind::Spread, ArgKind::Named(flag)]), s);
Source

pub fn dcall_shape(&mut self, dst: Reg, callee: Reg, args: &[ArgKind]) -> u32

Emits dcall_shape dst, callee, csN with the shape args (interned by call_shape): a dynamic call whose window dst+1 ..= dst+args.len() holds positional, named, and spread arguments.

§Examples
use bytecode_lang::{ArgKind, Inst, ModuleBuilder, ShapeId, ValType};

let mut m = ModuleBuilder::new();
let x = m.string("x");
let mut f = m.function("f", &[ValType::Dyn], &[]);
let window = f.regs(&[ValType::Dyn, ValType::Dyn]);
f.dcall_shape(window, f.param(0), &[ArgKind::Named(x)]);
f.ret_void();
let id = m.add_function(f).unwrap();
let module = m.finish().unwrap();
assert_eq!(
    module.function(id).unwrap().code()[0],
    Inst::DCallShape { dst: window, callee: bytecode_lang::Reg(0), shape: ShapeId(0) },
);
Source

pub fn set_location(&mut self, file: StrId, line: u32, column: u32)

Sets the source position of the instructions emitted from now on. A line-table row is added at the next instruction only if the position changed.

§Examples
use bytecode_lang::{Inst, ModuleBuilder};

let mut m = ModuleBuilder::new();
let file = m.string("a.mox");
let mut f = m.function("f", &[], &[]);
f.set_location(file, 1, 1);
f.emit(Inst::Nop {});
f.emit(Inst::Nop {}); // same position: no new row
f.set_location(file, 2, 1);
f.ret_void();
let id = m.add_function(f).unwrap();
let module = m.finish().unwrap();
let pcs: Vec<u32> = module.function(id).unwrap().lines().iter().map(|r| r.pc).collect();
assert_eq!(pcs, [0, 2]);
Source

pub fn pc(&self) -> u32

The position the next instruction will have.

§Examples
use bytecode_lang::{Inst, ModuleBuilder};

let mut m = ModuleBuilder::new();
let mut f = m.function("f", &[], &[]);
assert_eq!(f.pc(), 0);
f.emit(Inst::Nop {});
assert_eq!(f.pc(), 1);
Source

pub fn emit(&mut self, inst: Inst) -> u32

Appends inst and returns its pc.

A jmp, jmp_if, or jmp_if_not emitted this way carries a raw Target; it is checked against the final code length when the function is added. Prefer jmp and friends, which take labels.

§Examples
use bytecode_lang::{Inst, ModuleBuilder};

let mut m = ModuleBuilder::new();
let mut f = m.function("f", &[], &[]);
assert_eq!(f.emit(Inst::Safepoint {}), 0);
assert_eq!(f.emit(Inst::RetVoid {}), 1);
Source

pub fn mov(&mut self, dst: Reg, src: Reg) -> u32

Emits mov dst, src.

§Examples
use bytecode_lang::{Inst, ModuleBuilder, Reg, ValType};

let mut m = ModuleBuilder::new();
let mut f = m.function("f", &[ValType::I64], &[]);
let copy = f.reg(ValType::I64);
f.mov(copy, Reg(0));
f.ret_void();
let id = m.add_function(f).unwrap();
let module = m.finish().unwrap();
assert_eq!(module.function(id).unwrap().code()[0], Inst::Mov { dst: copy, src: Reg(0) });
Source

pub fn ret(&mut self, src: Reg) -> u32

Emits ret src.

§Examples
use bytecode_lang::{Inst, ModuleBuilder, ValType};

let mut m = ModuleBuilder::new();
let mut f = m.function("f", &[ValType::Dyn], &[ValType::Dyn]);
let x = f.param(0);
assert_eq!(f.ret(x), 0);
Source

pub fn ret_void(&mut self) -> u32

Emits ret_void.

§Examples
use bytecode_lang::ModuleBuilder;

let mut m = ModuleBuilder::new();
let mut f = m.function("f", &[], &[]);
assert_eq!(f.ret_void(), 0);
Source

pub fn label(&mut self) -> Label

A new, unbound label.

§Examples
use bytecode_lang::ModuleBuilder;

let mut m = ModuleBuilder::new();
let mut f = m.function("f", &[], &[]);
let (a, b) = (f.label(), f.label());
assert_ne!(a, b);
Source

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

Binds label to the position of the next instruction emitted. Binding a label twice is an error.

§Examples
use bytecode_lang::{BuildError, ModuleBuilder};

let mut m = ModuleBuilder::new();
let mut f = m.function("f", &[], &[]);
let l = f.label();
f.bind(l);
f.bind(l);
f.ret_void();
assert!(matches!(m.add_function(f), Err(BuildError::LabelRebound { .. })));
Source

pub fn jmp(&mut self, label: Label) -> u32

Emits jmp label.

§Examples
use bytecode_lang::{Inst, ModuleBuilder, Target};

let mut m = ModuleBuilder::new();
let mut f = m.function("f", &[], &[]);
let top = f.label();
f.bind(top);
f.emit(Inst::Safepoint {});
f.jmp(top);
let id = m.add_function(f).unwrap();
let module = m.finish().unwrap();
assert_eq!(module.function(id).unwrap().code()[1], Inst::Jmp { target: Target(0) });
Source

pub fn jmp_if(&mut self, cond: Reg, label: Label) -> u32

Emits jmp_if cond, label.

§Examples
use bytecode_lang::{Inst, ModuleBuilder, Target, ValType};

let mut m = ModuleBuilder::new();
let mut f = m.function("f", &[ValType::Bool], &[]);
let c = f.param(0);
let out = f.label();
f.jmp_if(c, out);
f.bind(out);
f.ret_void();
let id = m.add_function(f).unwrap();
let module = m.finish().unwrap();
assert_eq!(module.function(id).unwrap().code()[0], Inst::JmpIf { cond: c, target: Target(1) });
Source

pub fn jmp_if_not(&mut self, cond: Reg, label: Label) -> u32

Emits jmp_if_not cond, label.

§Examples
use bytecode_lang::{Inst, ModuleBuilder, Target, ValType};

let mut m = ModuleBuilder::new();
let mut f = m.function("f", &[ValType::Bool], &[]);
let c = f.param(0);
let out = f.label();
f.jmp_if_not(c, out);
f.bind(out);
f.ret_void();
let id = m.add_function(f).unwrap();
let module = m.finish().unwrap();
assert_eq!(module.function(id).unwrap().code()[0], Inst::JmpIfNot { cond: c, target: Target(1) });
Source

pub fn switch( &mut self, ty: IntTy, src: Reg, targets: &[Label], default: Label, ) -> TableId

Emits switch.ty src, jtN with a new jump table: targets[v] for a selector v in range, default otherwise. Returns the table’s id.

§Examples
use bytecode_lang::{IntTy, ModuleBuilder, TableId, ValType};

let mut m = ModuleBuilder::new();
let mut f = m.function("f", &[ValType::U8], &[]);
let sel = f.param(0);
let (zero, other) = (f.label(), f.label());
assert_eq!(f.switch(IntTy::U8, sel, &[zero], other), TableId(0));
f.bind(zero);
f.bind(other);
f.ret_void();
assert!(m.add_function(f).is_ok());
Source

pub fn try_region( &mut self, start: Label, end: Label, handler: Label, catch: Reg, )

Declares a try region: an error raised at a pc in start..end continues at handler with the error value in catch (a dyn register). Declare inner regions before the regions that enclose them; the first matching region wins.

§Examples
use bytecode_lang::{Inst, ModuleBuilder, Reg, ValType};

let mut m = ModuleBuilder::new();
let mut f = m.function("f", &[ValType::Dyn], &[]);
let err = f.reg(ValType::Dyn);
let (start, end, handler) = (f.label(), f.label(), f.label());
f.bind(start);
f.emit(Inst::Throw { src: Reg(0) });
f.bind(end);
f.bind(handler);
f.ret_void();
f.try_region(start, end, handler, err);
let id = m.add_function(f).unwrap();
let module = m.finish().unwrap();
assert_eq!(module.function(id).unwrap().handlers()[0].catch, err);
Source

pub fn local(&mut self, reg: Reg, name: StrId, start: Label, end: Label)

Names the register reg as the source variable name over the range start..end, for debuggers.

§Examples
use bytecode_lang::{ModuleBuilder, ValType};

let mut m = ModuleBuilder::new();
let name = m.string("count");
let mut f = m.function("f", &[ValType::I64], &[]);
let (s, e) = (f.label(), f.label());
f.bind(s);
f.ret_void();
f.bind(e);
f.local(f.param(0), name, s, e);
let id = m.add_function(f).unwrap();
let module = m.finish().unwrap();
assert_eq!(module.function(id).unwrap().locals()[0].name, name);
Source

pub fn parallel_move(&mut self, moves: &[(Reg, Reg)])

Emits moves that copy every src to its dst simultaneously: each destination ends up with the value its source held before any move ran, even when sources and destinations overlap or form cycles ((a, b), (b, a) swaps). Moves whose source is their destination are dropped. Cycles are broken through a temporary register of the saved register’s type. Two moves writing one register are an error.

This is the correct lowering of block arguments (the bytecode form of SSA phis): a back edge that permutes its own parameters must not be emitted as plain sequential moves.

§Examples
use bytecode_lang::{Inst, ModuleBuilder, Reg, ValType};

let mut m = ModuleBuilder::new();
let mut f = m.function("swap", &[ValType::I64, ValType::I64], &[]);
let (a, b) = (f.param(0), f.param(1));
f.parallel_move(&[(a, b), (b, a)]);
f.ret_void();
let id = m.add_function(f).unwrap();
let module = m.finish().unwrap();
let tmp = Reg(2); // allocated to break the cycle
assert_eq!(
    &module.function(id).unwrap().code()[..3],
    &[
        Inst::Mov { dst: tmp, src: a },
        Inst::Mov { dst: a, src: b },
        Inst::Mov { dst: b, src: tmp },
    ],
);

Trait Implementations§

Source§

impl Clone for FunctionBuilder

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for FunctionBuilder

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. 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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. 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, !>

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.