use ir_lang::{BinOp, Builder, Type, Value};
enum Expr {
Param(usize),
Int(i64),
Arith(BinOp, Box<Expr>, Box<Expr>),
IfLess {
lhs: Box<Expr>,
rhs: Box<Expr>,
then: Box<Expr>,
els: Box<Expr>,
},
}
fn lower(b: &mut Builder, expr: &Expr, params: &[Value]) -> Value {
match expr {
Expr::Param(i) => params[*i],
Expr::Int(n) => b.iconst(*n),
Expr::Arith(op, lhs, rhs) => {
let l = lower(b, lhs, params);
let r = lower(b, rhs, params);
b.bin(*op, l, r)
}
Expr::IfLess {
lhs,
rhs,
then,
els,
} => {
let l = lower(b, lhs, params);
let r = lower(b, rhs, params);
let cond = b.bin(BinOp::Lt, l, r);
let join = b.create_block(&[Type::Int]);
let then_blk = b.create_block(&[]);
let else_blk = b.create_block(&[]);
b.branch(cond, then_blk, &[], else_blk, &[]);
b.switch_to(then_blk);
let then_val = lower(b, then, params);
b.jump(join, &[then_val]);
b.switch_to(else_blk);
let else_val = lower(b, els, params);
b.jump(join, &[else_val]);
b.switch_to(join);
b.block_params(join)[0]
}
}
}
fn lower_function(name: &str, arity: usize, body: &Expr) -> ir_lang::Function {
let param_types = vec![Type::Int; arity];
let mut b = Builder::new(name, ¶m_types, Type::Int);
let params: Vec<Value> = b.block_params(b.entry()).to_vec();
let result = lower(&mut b, body, ¶ms);
b.ret(Some(result));
b.finish()
}
fn main() {
let relu = lower_function(
"relu",
1,
&Expr::IfLess {
lhs: Box::new(Expr::Param(0)),
rhs: Box::new(Expr::Int(0)),
then: Box::new(Expr::Int(0)),
els: Box::new(Expr::Param(0)),
},
);
let poly = lower_function(
"poly",
2,
&Expr::Arith(
BinOp::Mul,
Box::new(Expr::Arith(
BinOp::Add,
Box::new(Expr::Param(0)),
Box::new(Expr::Param(1)),
)),
Box::new(Expr::Arith(
BinOp::Sub,
Box::new(Expr::Param(0)),
Box::new(Expr::Param(1)),
)),
),
);
for func in [&relu, &poly] {
match func.validate() {
Ok(()) => {
println!("{func}");
println!("// validated ok\n");
}
Err(e) => println!("// {} failed validation: {e}\n", func.name()),
}
}
}