use crate::{
impl_table_indexing,
ir::{Arg, Block, ExtUnit, ExtUnitData, Inst, InstData, Signature, Value, ValueData},
table::{PrimaryTable, SecondaryTable, TableKey},
ty::{void_ty, Type},
};
use std::collections::HashMap;
#[derive(Default, Serialize, Deserialize)]
pub struct DataFlowGraph {
pub(crate) insts: PrimaryTable<Inst, InstData>,
pub(crate) results: SecondaryTable<Inst, Value>,
pub(crate) values: PrimaryTable<Value, ValueData>,
pub(crate) args: SecondaryTable<Arg, Value>,
pub(crate) ext_units: PrimaryTable<ExtUnit, ExtUnitData>,
pub(crate) names: HashMap<Value, String>,
pub(crate) anonymous_hints: HashMap<Value, u32>,
}
impl_table_indexing!(DataFlowGraph, insts, Inst, InstData);
impl_table_indexing!(DataFlowGraph, values, Value, ValueData);
impl_table_indexing!(DataFlowGraph, ext_units, ExtUnit, ExtUnitData);
impl DataFlowGraph {
pub fn new() -> Self {
Default::default()
}
pub fn add_placeholder(&mut self, ty: Type) -> Value {
self.values.add(ValueData::Placeholder { ty })
}
pub fn remove_placeholder(&mut self, value: Value) {
assert!(!self.has_uses(value));
assert!(self[value].is_placeholder());
self.values.remove(value);
}
pub fn is_placeholder(&self, value: Value) -> bool {
self[value].is_placeholder()
}
pub fn add_inst(&mut self, data: InstData, ty: Type) -> Inst {
let inst = self.insts.add(data);
if !ty.is_void() {
let result = self.values.add(ValueData::Inst { ty, inst });
self.results.add(inst, result);
}
inst
}
pub fn remove_inst(&mut self, inst: Inst) {
if self.has_result(inst) {
let value = self.inst_result(inst);
assert!(!self.has_uses(value));
self.values.remove(value);
}
self.insts.remove(inst);
self.results.remove(inst);
}
pub fn has_result(&self, inst: Inst) -> bool {
self.results.storage.contains_key(&inst.index())
}
pub fn inst_result(&self, inst: Inst) -> Value {
self.results[inst]
}
pub fn arg_value(&self, arg: Arg) -> Value {
self.args[arg]
}
pub(crate) fn make_args_for_signature(&mut self, sig: &Signature) {
for arg in sig.args() {
let value = self.values.add(ValueData::Arg {
ty: sig.arg_type(arg),
arg: arg,
});
self.args.add(arg, value);
}
}
pub fn value_type(&self, value: Value) -> Type {
match &self[value] {
ValueData::Inst { ty, .. } => ty.clone(),
ValueData::Arg { ty, .. } => ty.clone(),
ValueData::Placeholder { ty, .. } => ty.clone(),
}
}
pub fn inst_type(&self, inst: Inst) -> Type {
if self.has_result(inst) {
self.value_type(self.inst_result(inst))
} else {
void_ty()
}
}
pub fn get_value_inst(&self, value: Value) -> Option<Inst> {
match self[value] {
ValueData::Inst { inst, .. } => Some(inst),
_ => None,
}
}
pub fn value_inst(&self, value: Value) -> Inst {
match self.get_value_inst(value) {
Some(inst) => inst,
None => panic!("value {} not the result of an instruction", value),
}
}
pub fn get_name(&self, value: Value) -> Option<&str> {
self.names.get(&value).map(AsRef::as_ref)
}
pub fn set_name(&mut self, value: Value, name: String) {
self.names.insert(value, name);
}
pub fn clear_name(&mut self, value: Value) -> Option<String> {
self.names.remove(&value)
}
pub fn get_anonymous_hint(&self, value: Value) -> Option<u32> {
self.anonymous_hints.get(&value).cloned()
}
pub fn set_anonymous_hint(&mut self, value: Value, hint: u32) {
self.anonymous_hints.insert(value, hint);
}
pub fn clear_anonymous_hint(&mut self, value: Value) -> Option<u32> {
self.anonymous_hints.remove(&value)
}
pub fn replace_use(&mut self, from: Value, to: Value) -> usize {
let mut count = 0;
for inst in self.insts.storage.values_mut() {
count += inst.replace_value(from, to);
}
count
}
pub fn uses(&self, value: Value) -> impl Iterator<Item = (Inst, usize)> {
let mut uses = vec![];
for inst in self.insts.keys() {
for (i, arg) in self[inst].args().iter().cloned().enumerate() {
if arg == value {
uses.push((inst, i));
}
}
}
uses.into_iter()
}
pub fn has_uses(&self, value: Value) -> bool {
self.uses(value).count() > 0
}
pub fn has_one_use(&self, value: Value) -> bool {
self.uses(value).count() == 1
}
pub fn replace_block_use(&mut self, from: Block, to: Block) -> usize {
let mut count = 0;
for inst in self.insts.storage.values_mut() {
count += inst.replace_block(from, to);
}
count
}
pub fn get_const(&self, value: Value) -> Option<crate::Value> {
use super::Opcode;
let inst = self.get_value_inst(value)?;
match self[inst].opcode() {
Opcode::ConstInt => self.get_const_int(value).cloned().map(Into::into),
Opcode::ConstTime => self.get_const_time(value).cloned().map(Into::into),
Opcode::Array | Opcode::ArrayUniform => self.get_const_array(value).map(Into::into),
Opcode::Struct => self.get_const_struct(value).map(Into::into),
_ => None,
}
}
pub fn get_const_time(&self, value: Value) -> Option<&crate::TimeValue> {
let inst = self.get_value_inst(value)?;
self[inst].get_const_time()
}
pub fn get_const_int(&self, value: Value) -> Option<&crate::IntValue> {
let inst = self.get_value_inst(value)?;
self[inst].get_const_int()
}
pub fn get_const_array(&self, value: Value) -> Option<crate::ArrayValue> {
use super::Opcode;
let inst = self.get_value_inst(value)?;
match self[inst].opcode() {
Opcode::Array => {
let args: Option<Vec<_>> = self[inst]
.args()
.iter()
.map(|&a| self.get_const(a))
.collect();
Some(crate::ArrayValue::new(args?))
}
Opcode::ArrayUniform => Some(crate::ArrayValue::new_uniform(
self[inst].imms()[0],
self.get_const(self[inst].args()[0])?,
)),
_ => None,
}
}
pub fn get_const_struct(&self, value: Value) -> Option<crate::StructValue> {
use super::Opcode;
let inst = self.get_value_inst(value)?;
match self[inst].opcode() {
Opcode::Struct => {
let args: Option<Vec<_>> = self[inst]
.args()
.iter()
.map(|&a| self.get_const(a))
.collect();
Some(crate::StructValue::new(args?))
}
_ => None,
}
}
}