Skip to main content

Builder

Struct Builder 

Source
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

Source

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);
Source

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);
Source

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);
Source

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);
Source

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);
Source

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);
Source

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));
Source

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));
Source

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));
Source

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));
Source

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));
Source

pub fn ret(&mut self, value: Option<Value>)

Sets the current block’s terminator to a return.

Some(v) returns the value v; None returns from a function whose return type is Unit.

§Examples
use ir_lang::{Builder, Type};

let mut b = Builder::new("f", &[], Type::Unit);
b.ret(None);
assert!(b.finish().validate().is_ok());
Source

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());
Source

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());
Source

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

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, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

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.