pub struct Builder { /* private fields */ }Expand description
Constructs a Function one instruction at a time.
The builder is ir-lang’s lowering interface. A front-end walks its own syntax
tree and, for each construct, calls a builder method: a literal becomes a
constant, an operator becomes a bin or un, an
if becomes two blocks and a branch, a join becomes a block
with parameters reached by jump. The builder mints a fresh
Value for every result and hands it back, so the tree’s structure is captured
as flat SSA without the caller tracking numbering. Result types are inferred from
the operation, so they never have to be supplied.
Construction does not check well-formedness as it goes — that keeps the hot path
of lowering allocation-light and lets the caller emit blocks in whatever order is
convenient. Call Function::validate on the result
(or build it and validate before handing it to a pass) to confirm the IR is
sound.
§Examples
Lower fn max(a: int, b: int) -> int { if a < b { b } else { a } }:
use ir_lang::{Builder, BinOp, Type};
let mut b = Builder::new("max", &[Type::Int, Type::Int], Type::Int);
let entry = b.entry();
let a = b.block_params(entry)[0];
let bb = b.block_params(entry)[1];
// The join block takes the chosen value as a parameter.
let join = b.create_block(&[Type::Int]);
let then_blk = b.create_block(&[]);
let else_blk = b.create_block(&[]);
let cond = b.bin(BinOp::Lt, a, bb);
b.branch(cond, then_blk, &[], else_blk, &[]);
b.switch_to(then_blk);
b.jump(join, &[bb]);
b.switch_to(else_blk);
b.jump(join, &[a]);
b.switch_to(join);
let result = b.block_params(join)[0];
b.ret(Some(result));
let func = b.finish();
assert!(func.validate().is_ok());Implementations§
Source§impl Builder
impl Builder
Sourcepub fn new(name: impl Into<String>, params: &[Type], ret: Type) -> Self
pub fn new(name: impl Into<String>, params: &[Type], ret: Type) -> Self
Starts a new function with the given name, parameter types, and return type.
The entry block is created automatically with one parameter per function
parameter; those parameter values are the function’s inputs and are read with
block_params on entry. The
entry block is the current block, so emission can begin immediately.
§Examples
use ir_lang::{Builder, Type};
let b = Builder::new("identity", &[Type::Int], Type::Int);
assert_eq!(b.block_params(b.entry()).len(), 1);Sourcepub const fn entry(&self) -> Block
pub const fn entry(&self) -> Block
Returns the entry block.
§Examples
use ir_lang::{Builder, Type};
let b = Builder::new("f", &[], Type::Unit);
assert_eq!(b.entry().index(), 0);Sourcepub const fn current_block(&self) -> Block
pub const fn current_block(&self) -> Block
Returns the block that emission currently targets — the one the next instruction or terminator is added to.
§Examples
use ir_lang::{Builder, Type};
let mut b = Builder::new("f", &[], Type::Unit);
let next = b.create_block(&[]);
b.switch_to(next);
assert_eq!(b.current_block(), next);Sourcepub fn create_block(&mut self, params: &[Type]) -> Block
pub fn create_block(&mut self, params: &[Type]) -> Block
Creates a new block with the given parameter types and returns its handle.
Block parameters are how a value crosses a control-flow join in SSA form:
each predecessor passes a matching argument on its
jump or branch. The new block does
not become current — call switch_to to emit into it.
§Examples
use ir_lang::{Builder, Type};
let mut b = Builder::new("f", &[], Type::Int);
let join = b.create_block(&[Type::Int]);
assert_eq!(b.block_params(join).len(), 1);Sourcepub fn block_params(&self, block: Block) -> &[Value]
pub fn block_params(&self, block: Block) -> &[Value]
Returns a block’s parameter values, in order, or an empty slice if the block handle is out of range.
§Examples
use ir_lang::{Builder, Type};
let b = Builder::new("f", &[Type::Bool], Type::Unit);
assert_eq!(b.block_params(b.entry()).len(), 1);Sourcepub fn switch_to(&mut self, block: Block)
pub fn switch_to(&mut self, block: Block)
Switches emission to block. Subsequent instructions and the terminator are
added to it.
§Examples
use ir_lang::{Builder, Type};
let mut b = Builder::new("f", &[], Type::Unit);
let other = b.create_block(&[]);
b.switch_to(other);
b.ret(None);Sourcepub fn iconst(&mut self, value: i64) -> Value
pub fn iconst(&mut self, value: i64) -> Value
Emits an integer constant into the current block and returns its value.
§Examples
use ir_lang::{Builder, Type};
let mut b = Builder::new("f", &[], Type::Int);
let n = b.iconst(42);
b.ret(Some(n));
assert_eq!(b.finish().value_type(n), Some(Type::Int));Sourcepub fn fconst(&mut self, value: f64) -> Value
pub fn fconst(&mut self, value: f64) -> Value
Emits a floating-point constant into the current block and returns its value.
§Examples
use ir_lang::{Builder, Type};
let mut b = Builder::new("f", &[], Type::Float);
let pi = b.fconst(3.14);
b.ret(Some(pi));
assert_eq!(b.finish().value_type(pi), Some(Type::Float));Sourcepub fn bconst(&mut self, value: bool) -> Value
pub fn bconst(&mut self, value: bool) -> Value
Emits a boolean constant into the current block and returns its value.
§Examples
use ir_lang::{Builder, Type};
let mut b = Builder::new("f", &[], Type::Bool);
let t = b.bconst(true);
b.ret(Some(t));
assert_eq!(b.finish().value_type(t), Some(Type::Bool));Sourcepub fn bin(&mut self, op: BinOp, lhs: Value, rhs: Value) -> Value
pub fn bin(&mut self, op: BinOp, lhs: Value, rhs: Value) -> Value
Emits a binary operation over two values and returns the result.
The result type follows the operation: a comparison or a logical operation
yields Bool; an arithmetic operation yields the type
of its operands. Whether the operands actually satisfy the operation is
checked by validate, not here.
§Examples
use ir_lang::{Builder, BinOp, Type};
let mut b = Builder::new("f", &[Type::Int, Type::Int], Type::Bool);
let a = b.block_params(b.entry())[0];
let c = b.block_params(b.entry())[1];
let lt = b.bin(BinOp::Lt, a, c);
b.ret(Some(lt));
assert_eq!(b.finish().value_type(lt), Some(Type::Bool));Sourcepub fn un(&mut self, op: UnOp, operand: Value) -> Value
pub fn un(&mut self, op: UnOp, operand: Value) -> Value
Emits a unary operation over one value and returns the result.
Neg yields the operand’s type;
Not yields Bool.
§Examples
use ir_lang::{Builder, UnOp, Type};
let mut b = Builder::new("f", &[Type::Int], Type::Int);
let x = b.block_params(b.entry())[0];
let neg = b.un(UnOp::Neg, x);
b.ret(Some(neg));
assert_eq!(b.finish().value_type(neg), Some(Type::Int));Sourcepub fn jump(&mut self, target: Block, args: &[Value])
pub fn jump(&mut self, target: Block, args: &[Value])
Sets the current block’s terminator to an unconditional jump, passing one argument per parameter of the target block.
§Examples
use ir_lang::{Builder, Type};
let mut b = Builder::new("f", &[], Type::Int);
let exit = b.create_block(&[Type::Int]);
let n = b.iconst(7);
b.jump(exit, &[n]);
b.switch_to(exit);
let r = b.block_params(exit)[0];
b.ret(Some(r));
assert!(b.finish().validate().is_ok());Sourcepub fn branch(
&mut self,
cond: Value,
then_block: Block,
then_args: &[Value],
else_block: Block,
else_args: &[Value],
)
pub fn branch( &mut self, cond: Value, then_block: Block, then_args: &[Value], else_block: Block, else_args: &[Value], )
Sets the current block’s terminator to a conditional branch.
Control takes then_block (with then_args) when cond is true, and
else_block (with else_args) otherwise. Each arm’s arguments are matched
against the parameters of the block it targets.
§Examples
use ir_lang::{Builder, Type};
let mut b = Builder::new("f", &[Type::Bool], Type::Unit);
let cond = b.block_params(b.entry())[0];
let yes = b.create_block(&[]);
let no = b.create_block(&[]);
b.branch(cond, yes, &[], no, &[]);
b.switch_to(yes);
b.ret(None);
b.switch_to(no);
b.ret(None);
assert!(b.finish().validate().is_ok());Sourcepub fn finish(self) -> Function
pub fn finish(self) -> Function
Finishes construction and returns the assembled Function.
The function is not validated by this call; run
Function::validate on the result before relying
on it.
§Examples
use ir_lang::{Builder, Type};
let mut b = Builder::new("f", &[], Type::Unit);
b.ret(None);
let func = b.finish();
assert_eq!(func.block_count(), 1);