use crate::ir::dfg::DataFlowGraph;
use crate::ir::entities::{BasicBlock, BasicBlockData, Function, Program, Value, ValueData};
use crate::ir::types::{Type, TypeKind};
use crate::ir::values::*;
pub trait EntityInfoQuerier {
fn value_type(&self, value: Value) -> Type;
fn is_const(&self, value: Value) -> bool;
fn bb_params(&self, bb: BasicBlock) -> &[Value];
fn func_type(&self, func: Function) -> Type;
}
pub trait ValueInserter {
fn insert_value(&mut self, data: ValueData) -> Value;
}
pub trait ValueBuilder: Sized + EntityInfoQuerier + ValueInserter {
fn raw(mut self, data: ValueData) -> Value {
self.insert_value(data)
}
fn integer(mut self, value: i32) -> Value {
self.insert_value(Integer::new_data(value))
}
fn zero_init(mut self, ty: Type) -> Value {
assert!(!ty.is_unit(), "`ty` can not be unit");
self.insert_value(ZeroInit::new_data(ty))
}
fn undef(mut self, ty: Type) -> Value {
assert!(!ty.is_unit(), "`ty` can not be unit");
self.insert_value(Undef::new_data(ty))
}
fn aggregate(mut self, elems: Vec<Value>) -> Value {
assert!(!elems.is_empty(), "`elems` must not be empty");
assert!(
elems.iter().all(|e| self.is_const(*e)),
"`elems` must all be constants"
);
assert!(
elems
.windows(2)
.all(|e| self.value_type(e[0]) == self.value_type(e[1])),
"type mismatch in `elems`"
);
let base = self.value_type(elems[0]);
assert!(!base.is_unit(), "base type must not be `unit`");
let ty = Type::get_array(base, elems.len());
self.insert_value(Aggregate::new_data(elems, ty))
}
}
pub trait GlobalInstBuilder: ValueBuilder {
fn global_alloc(mut self, init: Value) -> Value {
let init_ty = self.value_type(init);
assert!(!init_ty.is_unit(), "the type of `init` must not be unit");
let ty = Type::get_pointer(init_ty);
self.insert_value(GlobalAlloc::new_data(init, ty))
}
}
pub trait LocalInstBuilder: ValueBuilder {
fn alloc(mut self, ty: Type) -> Value {
assert!(!ty.is_unit(), "`ty` can not be unit");
self.insert_value(Alloc::new_data(Type::get_pointer(ty)))
}
fn load(mut self, src: Value) -> Value {
let src_ty = self.value_type(src);
let ty = match src_ty.kind() {
TypeKind::Pointer(ty) => ty.clone(),
_ => panic!("expected a pointer type"),
};
self.insert_value(Load::new_data(src, ty))
}
fn store(mut self, value: Value, dest: Value) -> Value {
assert!(
Type::get_pointer(self.value_type(value)) == self.value_type(dest),
"the type of `dest` must be the pointer of `value`'s type"
);
self.insert_value(Store::new_data(value, dest))
}
fn get_ptr(mut self, src: Value, index: Value) -> Value {
let src_ty = self.value_type(src);
assert!(
matches!(src_ty.kind(), TypeKind::Pointer(..)),
"`src` must be a pointer"
);
assert!(
self.value_type(index).is_i32(),
"`index` must be an integer"
);
self.insert_value(GetPtr::new_data(src, index, src_ty))
}
fn get_elem_ptr(mut self, src: Value, index: Value) -> Value {
assert!(
self.value_type(index).is_i32(),
"`index` must be an integer"
);
let ty = match self.value_type(src).kind() {
TypeKind::Pointer(ty) => match ty.kind() {
TypeKind::Array(base, _) => Type::get_pointer(base.clone()),
_ => panic!("`src` must be a pointer of array"),
},
_ => panic!("`src` must be a pointer of array"),
};
self.insert_value(GetElemPtr::new_data(src, index, ty))
}
fn binary(mut self, op: BinaryOp, lhs: Value, rhs: Value) -> Value {
let lhs_ty = self.value_type(lhs);
let rhs_ty = self.value_type(rhs);
assert!(
lhs_ty.is_i32() && lhs_ty == rhs_ty,
"both `lhs` and `rhs` must be integer"
);
self.insert_value(Binary::new_data(op, lhs, rhs, lhs_ty))
}
fn branch(mut self, cond: Value, true_bb: BasicBlock, false_bb: BasicBlock) -> Value {
assert!(self.value_type(cond).is_i32(), "`cond` must be integer");
assert!(
self.bb_params(true_bb).is_empty(),
"`true_bb` must not have parameters"
);
assert!(
self.bb_params(false_bb).is_empty(),
"`false_bb` must not have parameters"
);
self.insert_value(Branch::new_data(cond, true_bb, false_bb))
}
fn branch_with_args(
mut self,
cond: Value,
true_bb: BasicBlock,
false_bb: BasicBlock,
true_args: Vec<Value>,
false_args: Vec<Value>,
) -> Value {
assert!(self.value_type(cond).is_i32(), "`cond` must be integer");
assert!(
true_bb != false_bb || (true_args.is_empty() && false_args.is_empty()),
"branches with same targets and one or more arguments are illegal"
);
check_bb_arg_types(&self, self.bb_params(true_bb), &true_args);
check_bb_arg_types(&self, self.bb_params(false_bb), &false_args);
self.insert_value(Branch::with_args(
cond, true_bb, false_bb, true_args, false_args,
))
}
fn jump(mut self, target: BasicBlock) -> Value {
assert!(
self.bb_params(target).is_empty(),
"`target` must not have parameters"
);
self.insert_value(Jump::new_data(target))
}
fn jump_with_args(mut self, target: BasicBlock, args: Vec<Value>) -> Value {
check_bb_arg_types(&self, self.bb_params(target), &args);
self.insert_value(Jump::with_args(target, args))
}
fn call(mut self, callee: Function, args: Vec<Value>) -> Value {
let ty = match self.func_type(callee).kind() {
TypeKind::Function(params, ret) => {
assert!(
params.len() == args.len(),
"argument count mismatch: expected {} arguments, but got {}",
params.len(),
args.len()
);
for (i, (expected, arg)) in params.iter().zip(args.iter()).enumerate() {
let actual = self.value_type(*arg);
assert!(
expected == &actual,
"argument type mismatch at parameter {}: expected {}, but got {}",
i,
expected,
actual
);
}
ret.clone()
}
t => panic!("expected a function type, but got {}", t),
};
self.insert_value(Call::new_data(callee, args, ty))
}
fn ret(mut self, value: Option<Value>) -> Value {
assert!(
value.is_none_or(|v| !self.value_type(v).is_unit()),
"the type of `value` must not be `unit`"
);
self.insert_value(Return::new_data(value))
}
}
pub trait BasicBlockBuilder: Sized + ValueInserter {
fn insert_bb(&mut self, data: BasicBlockData) -> BasicBlock;
fn basic_block(mut self, name: Option<String>) -> BasicBlock {
check_bb_name(&name);
self.insert_bb(BasicBlockData::new(name))
}
fn basic_block_with_params(mut self, name: Option<String>, params_ty: Vec<Type>) -> BasicBlock {
check_bb_name(&name);
assert!(
params_ty.iter().all(|p| !p.is_unit()),
"parameter type must not be `unit`!"
);
let params = params_ty
.iter()
.enumerate()
.map(|(i, ty)| self.insert_value(BlockArgRef::new_data(i, ty.clone())))
.collect();
self.insert_bb(BasicBlockData::with_params(name, params))
}
fn basic_block_with_param_names(
mut self,
name: Option<String>,
params: Vec<(Option<String>, Type)>,
) -> BasicBlock {
check_bb_name(&name);
assert!(
params.iter().all(|(_, p)| !p.is_unit()),
"parameter type must not be `unit`!"
);
let params = params
.into_iter()
.enumerate()
.map(|(i, (n, ty))| {
let mut arg = BlockArgRef::new_data(i, ty);
arg.set_name(n);
self.insert_value(arg)
})
.collect();
self.insert_bb(BasicBlockData::with_params(name, params))
}
}
fn check_bb_arg_types(querier: &impl EntityInfoQuerier, params: &[Value], args: &[Value]) {
assert!(
params.len() == args.len(),
"basic block argument count mismatch: expected {} arguments, but got {}",
params.len(),
args.len()
);
for (i, (param, arg)) in params.iter().zip(args.iter()).enumerate() {
let expected = querier.value_type(*param);
let actual = querier.value_type(*arg);
assert!(
expected == actual,
"basic block argument type mismatch at parameter {}: expected {}, but got {}",
i,
expected,
actual
);
}
}
fn check_bb_name(name: &Option<String>) {
assert!(
name
.as_ref()
.is_none_or(|n| n.len() > 1 && (n.starts_with('%') || n.starts_with('@'))),
"invalid basic block name: {}, expected a '%' or '@' prefix followed by at least one character",
name.as_deref().unwrap()
);
}
pub trait DfgBasedInfoQuerier {
fn dfg(&self) -> &DataFlowGraph;
}
impl<T: DfgBasedInfoQuerier> EntityInfoQuerier for T {
fn value_type(&self, value: Value) -> Type {
self
.dfg()
.globals
.upgrade()
.expect("`FunctionData` must be added to `Program` before use")
.borrow()
.get(&value)
.or_else(|| self.dfg().values().get(&value))
.expect("value does not exist")
.ty()
.clone()
}
fn is_const(&self, value: Value) -> bool {
self
.dfg()
.globals
.upgrade()
.expect("`FunctionData` must be added to `Program` before use")
.borrow()
.get(&value)
.or_else(|| self.dfg().values().get(&value))
.expect("value does not exist")
.kind()
.is_const()
}
fn bb_params(&self, bb: BasicBlock) -> &[Value] {
self
.dfg()
.bbs()
.get(&bb)
.expect("basic block does not exist")
.params()
}
fn func_type(&self, func: Function) -> Type {
self
.dfg()
.func_tys
.upgrade()
.expect("`FunctionData` must be added to `Program` before use")
.borrow()
.get(&func)
.expect("function does not exist")
.clone()
}
}
pub struct LocalBuilder<'a> {
pub(in crate::ir) dfg: &'a mut DataFlowGraph,
}
impl DfgBasedInfoQuerier for LocalBuilder<'_> {
fn dfg(&self) -> &DataFlowGraph {
self.dfg
}
}
impl ValueInserter for LocalBuilder<'_> {
fn insert_value(&mut self, data: ValueData) -> Value {
self.dfg.new_value_data(data)
}
}
impl ValueBuilder for LocalBuilder<'_> {}
impl LocalInstBuilder for LocalBuilder<'_> {}
pub struct BlockBuilder<'a> {
pub(in crate::ir) dfg: &'a mut DataFlowGraph,
}
impl ValueInserter for BlockBuilder<'_> {
fn insert_value(&mut self, data: ValueData) -> Value {
self.dfg.new_value_data(data)
}
}
impl BasicBlockBuilder for BlockBuilder<'_> {
fn insert_bb(&mut self, data: BasicBlockData) -> BasicBlock {
self.dfg.new_bb_data(data)
}
}
pub struct ReplaceBuilder<'a> {
pub(in crate::ir) dfg: &'a mut DataFlowGraph,
pub(in crate::ir) value: Value,
}
impl DfgBasedInfoQuerier for ReplaceBuilder<'_> {
fn dfg(&self) -> &DataFlowGraph {
self.dfg
}
}
impl ValueInserter for ReplaceBuilder<'_> {
fn insert_value(&mut self, data: ValueData) -> Value {
self.dfg.replace_value_with_data(self.value, data);
self.value
}
}
impl ValueBuilder for ReplaceBuilder<'_> {}
impl LocalInstBuilder for ReplaceBuilder<'_> {}
pub struct GlobalBuilder<'a> {
pub(in crate::ir) program: &'a mut Program,
}
impl EntityInfoQuerier for GlobalBuilder<'_> {
fn value_type(&self, value: Value) -> Type {
self
.program
.values
.borrow()
.get(&value)
.expect("value does not exist")
.ty()
.clone()
}
fn is_const(&self, value: Value) -> bool {
self
.program
.values
.borrow()
.get(&value)
.expect("value does not exist")
.kind()
.is_const()
}
fn bb_params(&self, _: BasicBlock) -> &[Value] {
unimplemented!()
}
fn func_type(&self, _: Function) -> Type {
unimplemented!()
}
}
impl ValueInserter for GlobalBuilder<'_> {
fn insert_value(&mut self, data: ValueData) -> Value {
let is_inst = data.kind().is_global_alloc();
let value = self.program.new_value_data(data);
if is_inst {
self.program.inst_layout.push(value);
}
value
}
}
impl ValueBuilder for GlobalBuilder<'_> {}
impl GlobalInstBuilder for GlobalBuilder<'_> {}