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
impl FunctionBuilder
Sourcepub fn id(&self) -> FuncId
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));Sourcepub fn param(&self, index: u16) -> Reg
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);Sourcepub fn param_count(&self) -> u16
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);Sourcepub fn reg(&mut self, ty: ValType) -> Reg
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));Sourcepub fn regs(&mut self, types: &[ValType]) -> Reg
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));Sourcepub fn capture(&mut self, ty: ValType) -> UpvalIdx
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));Sourcepub fn name_ref(&mut self, name: StrId) -> NameRef
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));Sourcepub fn type_ref(&mut self, ty: TypeId) -> TypeRef
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));Sourcepub fn set_params(&mut self, list: ParamList)
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());Sourcepub fn call_shape(&mut self, args: &[ArgKind]) -> ShapeId
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);Sourcepub fn dcall_shape(&mut self, dst: Reg, callee: Reg, args: &[ArgKind]) -> u32
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) },
);Sourcepub fn set_location(&mut self, file: StrId, line: u32, column: u32)
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]);Sourcepub fn pc(&self) -> u32
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);Sourcepub fn emit(&mut self, inst: Inst) -> u32
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);Sourcepub fn mov(&mut self, dst: Reg, src: Reg) -> u32
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) });Sourcepub fn ret(&mut self, src: Reg) -> u32
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);Sourcepub fn ret_void(&mut self) -> u32
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);Sourcepub fn label(&mut self) -> Label
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);Sourcepub fn bind(&mut self, label: Label)
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 { .. })));Sourcepub fn jmp(&mut self, label: Label) -> u32
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) });Sourcepub fn jmp_if(&mut self, cond: Reg, label: Label) -> u32
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) });Sourcepub fn jmp_if_not(&mut self, cond: Reg, label: Label) -> u32
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) });Sourcepub fn switch(
&mut self,
ty: IntTy,
src: Reg,
targets: &[Label],
default: Label,
) -> TableId
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());Sourcepub fn try_region(
&mut self,
start: Label,
end: Label,
handler: Label,
catch: Reg,
)
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);Sourcepub fn local(&mut self, reg: Reg, name: StrId, start: Label, end: Label)
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);Sourcepub fn parallel_move(&mut self, moves: &[(Reg, Reg)])
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 },
],
);