use crate::ir::builder::{BlockBuilder, LocalBuilder, ReplaceBuilder};
use crate::ir::entities::{BasicBlock, BasicBlockData, Value, ValueData};
use crate::ir::entities::{FuncTypeMapCell, GlobalValueMapCell};
use crate::ir::idman::{next_bb_id, next_local_value_id};
use std::collections::HashMap;
pub struct DataFlowGraph {
pub(in crate::ir) globals: GlobalValueMapCell,
pub(in crate::ir) func_tys: FuncTypeMapCell,
values: HashMap<Value, ValueData>,
bbs: HashMap<BasicBlock, BasicBlockData>,
}
macro_rules! data {
($self:ident, $value:expr) => {
$self
.globals
.upgrade()
.unwrap()
.borrow()
.get(&$value)
.or_else(|| $self.values.get(&$value))
.expect("value does not exist")
};
}
macro_rules! data_mut {
($self:ident, $value:expr) => {
$self
.globals
.upgrade()
.unwrap()
.borrow_mut()
.get_mut(&$value)
.or_else(|| $self.values.get_mut(&$value))
.expect("value does not exist")
};
}
impl DataFlowGraph {
pub(in crate::ir) fn new() -> Self {
Self {
globals: GlobalValueMapCell::new(),
func_tys: FuncTypeMapCell::new(),
values: HashMap::new(),
bbs: HashMap::new(),
}
}
pub fn new_value(&mut self) -> LocalBuilder<'_> {
LocalBuilder { dfg: self }
}
pub(in crate::ir) fn new_value_data(&mut self, data: ValueData) -> Value {
let value = Value(next_local_value_id());
for v in data.kind().value_uses() {
data_mut!(self, v).used_by.insert(value);
}
for bb in data.kind().bb_uses() {
self.bb_mut(bb).used_by.insert(value);
}
self.values.insert(value, data);
value
}
pub fn replace_value_with(&mut self, value: Value) -> ReplaceBuilder<'_> {
ReplaceBuilder { dfg: self, value }
}
pub(in crate::ir) fn replace_value_with_data(&mut self, value: Value, mut data: ValueData) {
let old = self.values.remove(&value).expect("`value` does not exist");
for v in old.kind().value_uses() {
data_mut!(self, v).used_by.remove(&value);
}
for bb in old.kind().bb_uses() {
self.bb_mut(bb).used_by.remove(&value);
}
for v in data.kind().value_uses() {
data_mut!(self, v).used_by.insert(value);
}
for bb in data.kind().bb_uses() {
self.bb_mut(bb).used_by.insert(value);
}
data.used_by = old.used_by;
self.values.insert(value, data);
}
pub fn update_value<F>(&mut self, value: Value, f: F)
where
F: FnOnce(&mut ValueData),
{
let mut data = self.values.remove(&value).expect("`value` does not exist");
for v in data.kind().value_uses() {
data_mut!(self, v).used_by.remove(&value);
}
for bb in data.kind().bb_uses() {
self.bb_mut(bb).used_by.remove(&value);
}
f(&mut data);
for v in data.kind().value_uses() {
data_mut!(self, v).used_by.insert(value);
}
for bb in data.kind().bb_uses() {
self.bb_mut(bb).used_by.insert(value);
}
self.values.insert(value, data);
}
pub fn remove_value(&mut self, value: Value) -> ValueData {
let data = self.values.remove(&value).expect("`value` does not exist");
assert!(data.used_by.is_empty(), "`value` is used by other values");
for v in data.kind().value_uses() {
data_mut!(self, v).used_by.remove(&value);
}
for bb in data.kind().bb_uses() {
self.bb_mut(bb).used_by.remove(&value);
}
data
}
pub fn set_value_name(&mut self, value: Value, name: Option<String>) {
self
.values
.get_mut(&value)
.expect("`value` does not exist")
.set_name(name);
}
pub fn value(&self, value: Value) -> &ValueData {
self.values.get(&value).expect("`value` does not exist")
}
pub fn values(&self) -> &HashMap<Value, ValueData> {
&self.values
}
pub fn value_eq(&self, lhs: Value, rhs: Value) -> bool {
self.data_eq(data!(self, lhs), data!(self, rhs))
}
pub fn data_eq(&self, lhs: &ValueData, rhs: &ValueData) -> bool {
use crate::ir::entities::ValueKind::*;
macro_rules! return_if {
($e:expr) => {
if $e {
return false;
}
};
}
return_if!(lhs.ty() != rhs.ty());
match (lhs.kind(), rhs.kind()) {
(Integer(l), Integer(r)) => return_if!(l.value() != r.value()),
(ZeroInit(_), ZeroInit(_)) => return true,
(Undef(_), Undef(_)) => return true,
(Aggregate(l), Aggregate(r)) => return_if!(l.elems().len() != r.elems().len()),
(FuncArgRef(l), FuncArgRef(r)) => return_if!(l.index() != r.index()),
(BlockArgRef(l), BlockArgRef(r)) => return_if!(l.index() != r.index()),
(Alloc(_), Alloc(_)) => return true,
(GlobalAlloc(_), GlobalAlloc(_)) => (),
(Load(_), Load(_)) => (),
(Store(_), Store(_)) => (),
(GetPtr(_), GetPtr(_)) => (),
(GetElemPtr(_), GetElemPtr(_)) => (),
(Binary(l), Binary(r)) => return_if!(l.op() != r.op()),
(Branch(l), Branch(r)) => {
return_if!(
l.true_bb() != r.true_bb()
|| l.false_bb() != r.false_bb()
|| l.true_args().len() != r.true_args().len()
|| l.false_args().len() != r.false_args().len()
)
}
(Jump(l), Jump(r)) => {
return_if!(l.target() != r.target() || l.args().len() != r.args().len())
}
(Call(l), Call(r)) => {
return_if!(l.callee() != r.callee() || l.args().len() != r.args().len())
}
(Return(l), Return(r)) => return_if!(l.value().xor(r.value()).is_some()),
_ => return false,
}
for (lu, ru) in lhs.kind().value_uses().zip(rhs.kind().value_uses()) {
return_if!(!self.value_eq(lu, ru));
}
true
}
pub fn new_bb(&mut self) -> BlockBuilder<'_> {
BlockBuilder { dfg: self }
}
pub(in crate::ir) fn new_bb_data(&mut self, data: BasicBlockData) -> BasicBlock {
let bb = BasicBlock(next_bb_id());
self.bbs.insert(bb, data);
bb
}
pub fn remove_bb(&mut self, bb: BasicBlock) -> BasicBlockData {
let data = self.bbs.remove(&bb).expect("`bb` does not exist");
assert!(data.used_by.is_empty(), "`bb` is used by other values");
for p in data.params() {
let param = self
.values
.remove(p)
.expect("basic block parameter does not exist");
assert!(
param.used_by.is_empty(),
"basic block parameter is used by other values"
);
}
data
}
pub fn bb(&self, bb: BasicBlock) -> &BasicBlockData {
self.bbs.get(&bb).expect("`bb` does not exist")
}
pub fn bb_mut(&mut self, bb: BasicBlock) -> &mut BasicBlockData {
self.bbs.get_mut(&bb).expect("`bb` does not exist")
}
pub fn bbs(&self) -> &HashMap<BasicBlock, BasicBlockData> {
&self.bbs
}
pub fn bbs_mut(&mut self) -> &mut HashMap<BasicBlock, BasicBlockData> {
&mut self.bbs
}
}
#[cfg(test)]
mod test {
use crate::ir::builder_traits::*;
use crate::ir::{BinaryOp, Program, Type};
#[test]
fn value_eq() {
let mut program = Program::new();
let func = program.new_func_def("@test".into(), vec![], Type::get_unit());
let func = program.func_mut(func);
let int1 = func.dfg_mut().new_value().integer(10);
let int2 = func.dfg_mut().new_value().integer(10);
assert!(func.dfg().value_eq(int1, int2));
let add1 = func.dfg_mut().new_value().binary(BinaryOp::Add, int1, int1);
let sub1 = func.dfg_mut().new_value().binary(BinaryOp::Sub, add1, int1);
let add2 = func.dfg_mut().new_value().binary(BinaryOp::Add, int2, int2);
let sub2 = func.dfg_mut().new_value().binary(BinaryOp::Sub, add2, int2);
assert!(func.dfg().value_eq(sub1, sub2));
let int1 = func.dfg_mut().new_value().integer(10);
let int2 = func.dfg_mut().new_value().integer(9);
assert!(!func.dfg().value_eq(int1, int2));
let add1 = func.dfg_mut().new_value().binary(BinaryOp::Add, int1, int1);
let sub1 = func.dfg_mut().new_value().binary(BinaryOp::Sub, add1, int1);
let add2 = func.dfg_mut().new_value().binary(BinaryOp::Add, int1, int2);
let sub2 = func.dfg_mut().new_value().binary(BinaryOp::Sub, add2, int2);
assert!(!func.dfg().value_eq(sub1, sub2));
}
}