use crate::ir::builder::GlobalBuilder;
use crate::ir::dfg::DataFlowGraph;
use crate::ir::idman::{BasicBlockId, FunctionId, ValueId};
use crate::ir::idman::{is_global_id, next_func_id, next_global_value_id};
use crate::ir::layout::Layout;
use crate::ir::types::Type;
use crate::ir::values;
use std::cell::{Ref, RefCell};
use std::collections::{HashMap, HashSet};
use std::rc::{Rc, Weak};
#[derive(Default)]
pub struct Program {
pub(in crate::ir) values: Rc<RefCell<HashMap<Value, ValueData>>>,
pub(in crate::ir) inst_layout: Vec<Value>,
funcs: HashMap<Function, FunctionData>,
func_tys: Rc<RefCell<HashMap<Function, Type>>>,
func_layout: Vec<Function>,
}
macro_rules! data_mut {
($self:ident, $value:expr) => {
$self
.values
.borrow_mut()
.get_mut(&$value)
.expect("value does not exist")
};
}
impl Program {
pub fn new() -> Self {
Self::default()
}
pub fn new_value(&mut self) -> GlobalBuilder<'_> {
GlobalBuilder { program: self }
}
pub(in crate::ir) fn new_value_data(&mut self, data: ValueData) -> Value {
let value = Value(next_global_value_id());
for v in data.kind().value_uses() {
data_mut!(self, v).used_by.insert(value);
}
self.values.borrow_mut().insert(value, data);
value
}
pub fn remove_value(&mut self, value: Value) -> ValueData {
let data = self
.values
.borrow_mut()
.remove(&value)
.expect("`value` does not exist");
if data.kind().is_global_alloc() {
self
.inst_layout
.remove(self.inst_layout.iter().position(|v| *v == value).unwrap());
}
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);
}
data
}
pub fn set_value_name(&mut self, value: Value, name: Option<String>) {
self
.values
.borrow_mut()
.get_mut(&value)
.expect("`value` does not exist")
.set_name(name);
}
pub fn borrow_values(&self) -> Ref<'_, HashMap<Value, ValueData>> {
self.values.borrow()
}
pub fn inst_layout(&self) -> &[Value] {
&self.inst_layout
}
pub fn borrow_value(&self, value: Value) -> Ref<'_, ValueData> {
Ref::map(self.values.borrow(), |m| {
m.get(&value).expect("`value` does not exist")
})
}
#[deprecated(since = "0.0.10", note = "call `new_func_def` instead")]
pub fn new_func(&mut self, mut data: FunctionData) -> Function {
let func = Function(next_func_id());
data.dfg.globals = Rc::downgrade(&self.values);
data.dfg.func_tys = Rc::downgrade(&self.func_tys);
self.func_tys.borrow_mut().insert(func, data.ty.clone());
self.funcs.insert(func, data);
self.func_layout.push(func);
func
}
pub fn new_func_def(&mut self, name: String, params_ty: Vec<Type>, ret_ty: Type) -> Function {
#[allow(deprecated)]
self.new_func(FunctionData::new(name, params_ty, ret_ty))
}
pub fn new_func_def_with_param_names(
&mut self,
name: String,
params: Vec<(Option<String>, Type)>,
ret_ty: Type,
) -> Function {
#[allow(deprecated)]
self.new_func(FunctionData::with_param_names(name, params, ret_ty))
}
pub fn new_func_decl(&mut self, name: String, params_ty: Vec<Type>, ret_ty: Type) -> Function {
#[allow(deprecated)]
self.new_func(FunctionData::new_decl(name, params_ty, ret_ty))
}
pub fn remove_func(&mut self, func: Function) -> Option<FunctionData> {
self.func_tys.borrow_mut().remove(&func);
self
.func_layout
.remove(self.func_layout.iter().position(|f| *f == func).unwrap());
self.funcs.remove(&func)
}
pub fn funcs(&self) -> &HashMap<Function, FunctionData> {
&self.funcs
}
pub fn funcs_mut(&mut self) -> &mut HashMap<Function, FunctionData> {
&mut self.funcs
}
pub fn func_layout(&self) -> &[Function] {
&self.func_layout
}
pub fn func(&self, func: Function) -> &FunctionData {
self.funcs.get(&func).expect("`func` does not exist")
}
pub fn func_mut(&mut self, func: Function) -> &mut FunctionData {
self.funcs.get_mut(&func).expect("`func` does not exist")
}
}
pub(in crate::ir) type GlobalValueMapCell = Weak<RefCell<HashMap<Value, ValueData>>>;
pub(in crate::ir) type FuncTypeMapCell = Weak<RefCell<HashMap<Function, Type>>>;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Function(FunctionId);
pub struct FunctionData {
ty: Type,
name: String,
params: Vec<Value>,
dfg: DataFlowGraph,
layout: Layout,
}
impl FunctionData {
#[deprecated(since = "0.0.10", note = "call `Program::new_func_def` instead")]
pub fn new(name: String, params_ty: Vec<Type>, ret_ty: Type) -> Self {
use crate::ir::values::FuncArgRef;
Self::check_sanity(&name, params_ty.iter());
let mut dfg = DataFlowGraph::new();
let params = params_ty
.iter()
.enumerate()
.map(|(i, ty)| dfg.new_value_data(FuncArgRef::new_data(i, ty.clone())))
.collect();
Self {
ty: Type::get_function(params_ty, ret_ty),
name,
params,
dfg,
layout: Layout::new(),
}
}
#[deprecated(
since = "0.0.10",
note = "call `Program::new_func_with_param_names` instead"
)]
pub fn with_param_names(name: String, params: Vec<(Option<String>, Type)>, ret_ty: Type) -> Self {
use crate::ir::values::FuncArgRef;
Self::check_sanity(&name, params.iter().map(|(_, ty)| ty));
let mut dfg = DataFlowGraph::new();
let (params, params_ty) = params
.into_iter()
.enumerate()
.map(|(i, (n, ty))| {
let v = dfg.new_value_data(FuncArgRef::new_data(i, ty.clone()));
dfg.set_value_name(v, n);
(v, ty)
})
.unzip();
Self {
ty: Type::get_function(params_ty, ret_ty),
name,
params,
dfg,
layout: Layout::new(),
}
}
#[deprecated(since = "0.0.10", note = "call `Program::new_func_decl` instead")]
pub fn new_decl(name: String, params_ty: Vec<Type>, ret_ty: Type) -> Self {
Self::check_sanity(&name, params_ty.iter());
Self {
ty: Type::get_function(params_ty, ret_ty),
name,
params: Vec::new(),
dfg: DataFlowGraph::new(),
layout: Layout::new(),
}
}
fn check_sanity<'a, T>(name: &str, mut params: T)
where
T: Iterator<Item = &'a Type>,
{
assert!(
name.len() > 1 && (name.starts_with('%') || name.starts_with('@')),
"invalid function name"
);
assert!(
params.all(|p| !p.is_unit()),
"parameter type must not be `unit`!"
);
}
pub fn ty(&self) -> &Type {
&self.ty
}
pub fn name(&self) -> &str {
&self.name
}
pub fn set_name(&mut self, name: String) {
self.name = name;
}
pub fn params(&self) -> &[Value] {
&self.params
}
pub fn dfg(&self) -> &DataFlowGraph {
&self.dfg
}
pub fn dfg_mut(&mut self) -> &mut DataFlowGraph {
&mut self.dfg
}
pub fn layout(&self) -> &Layout {
&self.layout
}
pub fn layout_mut(&mut self) -> &mut Layout {
&mut self.layout
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct BasicBlock(pub(in crate::ir) BasicBlockId);
pub struct BasicBlockData {
name: Option<String>,
params: Vec<Value>,
pub(in crate::ir) used_by: HashSet<Value>,
}
impl BasicBlockData {
pub(in crate::ir) fn new(name: Option<String>) -> Self {
Self {
name,
params: Vec::new(),
used_by: HashSet::new(),
}
}
pub(in crate::ir) fn with_params(name: Option<String>, params: Vec<Value>) -> Self {
Self {
name,
params,
used_by: HashSet::new(),
}
}
pub fn name(&self) -> &Option<String> {
&self.name
}
pub fn set_name(&mut self, name: Option<String>) {
self.name = name;
}
pub fn params(&self) -> &[Value] {
&self.params
}
pub fn params_mut(&mut self) -> &mut Vec<Value> {
&mut self.params
}
pub fn used_by(&self) -> &HashSet<Value> {
&self.used_by
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Value(pub(in crate::ir) ValueId);
impl Value {
pub fn is_global(self) -> bool {
is_global_id(self.0)
}
}
#[derive(Debug)]
pub struct ValueData {
ty: Type,
name: Option<String>,
kind: ValueKind,
pub(in crate::ir) used_by: HashSet<Value>,
}
impl ValueData {
pub(in crate::ir) fn new(ty: Type, kind: ValueKind) -> Self {
Self {
ty,
name: None,
kind,
used_by: HashSet::new(),
}
}
pub fn ty(&self) -> &Type {
&self.ty
}
pub fn name(&self) -> &Option<String> {
&self.name
}
pub(in crate::ir) fn set_name(&mut self, name: Option<String>) {
assert!(
name
.as_ref()
.is_none_or(|n| n.len() > 1 && (n.starts_with('%') || n.starts_with('@'))),
"invalid value name"
);
self.name = name;
}
pub fn kind(&self) -> &ValueKind {
&self.kind
}
pub fn kind_mut(&mut self) -> &mut ValueKind {
&mut self.kind
}
pub fn used_by(&self) -> &HashSet<Value> {
&self.used_by
}
}
impl Clone for ValueData {
fn clone(&self) -> Self {
Self {
ty: self.ty.clone(),
name: self.name.clone(),
kind: self.kind.clone(),
used_by: HashSet::new(),
}
}
}
#[derive(Clone, Debug)]
pub enum ValueKind {
Integer(values::Integer),
ZeroInit(values::ZeroInit),
Undef(values::Undef),
Aggregate(values::Aggregate),
FuncArgRef(values::FuncArgRef),
BlockArgRef(values::BlockArgRef),
Alloc(values::Alloc),
GlobalAlloc(values::GlobalAlloc),
Load(values::Load),
Store(values::Store),
GetPtr(values::GetPtr),
GetElemPtr(values::GetElemPtr),
Binary(values::Binary),
Branch(values::Branch),
Jump(values::Jump),
Call(values::Call),
Return(values::Return),
}
impl ValueKind {
pub fn value_uses(&self) -> ValueUses<'_> {
ValueUses {
kind: self,
index: 0,
}
}
pub fn bb_uses(&self) -> BasicBlockUses<'_> {
BasicBlockUses {
kind: self,
index: 0,
}
}
pub fn is_const(&self) -> bool {
matches!(
self,
ValueKind::Integer(..)
| ValueKind::ZeroInit(..)
| ValueKind::Undef(..)
| ValueKind::Aggregate(..)
)
}
pub fn is_global_alloc(&self) -> bool {
matches!(self, ValueKind::GlobalAlloc(..))
}
pub fn is_local_inst(&self) -> bool {
matches!(
self,
ValueKind::Alloc(..)
| ValueKind::Load(..)
| ValueKind::Store(..)
| ValueKind::GetPtr(..)
| ValueKind::GetElemPtr(..)
| ValueKind::Binary(..)
| ValueKind::Branch(..)
| ValueKind::Jump(..)
| ValueKind::Call(..)
| ValueKind::Return(..)
)
}
}
pub struct ValueUses<'a> {
kind: &'a ValueKind,
index: usize,
}
impl Iterator for ValueUses<'_> {
type Item = Value;
fn next(&mut self) -> Option<Self::Item> {
let cur = self.index;
self.index += 1;
macro_rules! vec_use {
($vec:expr) => {
if cur < $vec.len() {
Some($vec[cur])
} else {
None
}
};
}
macro_rules! field_use {
($($field:expr),+) => {
field_use!(@expand 0 $(,$field)+)
};
(@expand $index:expr) => {
None
};
(@expand $index:expr, $head:expr $(,$tail:expr)*) => {
if cur == $index {
Some($head)
} else {
field_use!(@expand $index + 1 $(,$tail)*)
}
};
}
match self.kind {
ValueKind::Aggregate(v) => vec_use!(v.elems()),
ValueKind::GlobalAlloc(v) => field_use!(v.init()),
ValueKind::Load(v) => field_use!(v.src()),
ValueKind::Store(v) => field_use!(v.value(), v.dest()),
ValueKind::GetPtr(v) => field_use!(v.src(), v.index()),
ValueKind::GetElemPtr(v) => field_use!(v.src(), v.index()),
ValueKind::Binary(v) => field_use!(v.lhs(), v.rhs()),
ValueKind::Branch(v) => {
let tlen = v.true_args().len();
if cur == 0 {
Some(v.cond())
} else if cur >= 1 && cur <= tlen {
Some(v.true_args()[cur - 1])
} else if cur > tlen && cur <= tlen + v.false_args().len() {
Some(v.false_args()[cur - tlen - 1])
} else {
None
}
}
ValueKind::Jump(v) => vec_use!(v.args()),
ValueKind::Call(v) => vec_use!(v.args()),
ValueKind::Return(v) => match cur {
0 => v.value(),
_ => None,
},
_ => None,
}
}
}
pub struct BasicBlockUses<'a> {
kind: &'a ValueKind,
index: usize,
}
impl Iterator for BasicBlockUses<'_> {
type Item = BasicBlock;
fn next(&mut self) -> Option<Self::Item> {
let cur = self.index;
self.index += 1;
match self.kind {
ValueKind::Branch(br) => match cur {
0 => Some(br.true_bb()),
1 => Some(br.false_bb()),
_ => None,
},
ValueKind::Jump(jump) => match cur {
0 => Some(jump.target()),
_ => None,
},
_ => None,
}
}
}