use crate::ir::ConstantScalarValue;
use super::{
cpa, processing::ScopeProcessing, Elem, Item, Matrix, Operation, Operator, UnaryOperator,
Variable,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[allow(missing_docs)]
pub struct Scope {
pub depth: u8,
pub operations: Vec<Operation>,
pub locals: Vec<Variable>,
matrices: Vec<Variable>,
slices: Vec<Variable>,
shared_memories: Vec<Variable>,
pub const_arrays: Vec<(Variable, Vec<Variable>)>,
local_arrays: Vec<Variable>,
reads_global: Vec<(Variable, ReadingStrategy, Variable, Variable)>,
index_offset_with_output_layout_position: Vec<usize>,
writes_global: Vec<(Variable, Variable, Variable)>,
reads_scalar: Vec<(Variable, Variable)>,
pub layout_ref: Option<Variable>,
pub undeclared: u16,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Hash, Eq)]
#[allow(missing_docs)]
pub enum ReadingStrategy {
OutputLayout,
Plain,
}
impl Scope {
pub fn root() -> Self {
Self {
depth: 0,
operations: Vec::new(),
locals: Vec::new(),
matrices: Vec::new(),
slices: Vec::new(),
local_arrays: Vec::new(),
shared_memories: Vec::new(),
const_arrays: Vec::new(),
reads_global: Vec::new(),
index_offset_with_output_layout_position: Vec::new(),
writes_global: Vec::new(),
reads_scalar: Vec::new(),
layout_ref: None,
undeclared: 0,
}
}
pub fn zero<I: Into<Item>>(&mut self, item: I) -> Variable {
let local = self.create_local(item);
let zero: Variable = 0u32.into();
cpa!(self, local = zero);
local
}
pub fn create_with_value<E, I>(&mut self, value: E, item: I) -> Variable
where
E: num_traits::ToPrimitive,
I: Into<Item> + Copy,
{
let item: Item = item.into();
let value = match item.elem() {
Elem::Float(kind) => ConstantScalarValue::Float(value.to_f64().unwrap(), kind),
Elem::Int(kind) => ConstantScalarValue::Int(value.to_i64().unwrap(), kind),
Elem::AtomicInt(kind) => ConstantScalarValue::Int(value.to_i64().unwrap(), kind),
Elem::UInt => ConstantScalarValue::UInt(value.to_u64().unwrap()),
Elem::AtomicUInt => ConstantScalarValue::UInt(value.to_u64().unwrap()),
Elem::Bool => ConstantScalarValue::Bool(value.to_u32().unwrap() == 1),
};
let local = self.create_local(item);
let value = Variable::ConstantScalar(value);
cpa!(self, local = value);
local
}
pub fn create_matrix(&mut self, matrix: Matrix) -> Variable {
let index = self.matrices.len() as u16;
let variable = Variable::Matrix {
id: index,
mat: matrix,
depth: self.depth,
};
self.matrices.push(variable);
variable
}
pub fn create_slice(&mut self, item: Item) -> Variable {
let id = self.slices.len() as u16;
let variable = Variable::Slice {
id,
item,
depth: self.depth,
};
self.slices.push(variable);
variable
}
pub fn create_local<I: Into<Item>>(&mut self, item: I) -> Variable {
let item = item.into();
let index = self.new_local_index();
let local = Variable::Local {
id: index,
item,
depth: self.depth,
};
self.locals.push(local);
local
}
pub fn create_local_undeclared(&mut self, item: Item) -> Variable {
let index = self.new_local_index();
let local = Variable::Local {
id: index,
item,
depth: self.depth,
};
self.undeclared += 1;
local
}
pub fn create_local_binding(&mut self, item: Item) -> Variable {
let index = self.new_local_index();
let local = Variable::LocalBinding {
id: index,
item,
depth: self.depth,
};
self.undeclared += 1;
local
}
pub fn read_array<I: Into<Item>>(
&mut self,
index: u16,
item: I,
position: Variable,
) -> Variable {
self.read_input_strategy(index, item.into(), ReadingStrategy::OutputLayout, position)
}
pub fn read_scalar(&mut self, index: u16, elem: Elem) -> Variable {
let local = Variable::LocalBinding {
id: self.new_local_index(),
item: Item::new(elem),
depth: self.depth,
};
let scalar = Variable::GlobalScalar { id: index, elem };
self.reads_scalar.push((local, scalar));
local
}
pub fn last_local_index(&self) -> Option<&Variable> {
self.locals.last()
}
pub fn write_global(&mut self, input: Variable, output: Variable, position: Variable) {
if self.layout_ref.is_none() {
self.layout_ref = Some(output);
}
self.writes_global.push((input, output, position));
}
pub fn write_global_custom(&mut self, output: Variable) {
if self.layout_ref.is_none() {
self.layout_ref = Some(output);
}
}
pub(crate) fn update_read(&mut self, index: u16, strategy: ReadingStrategy) {
if let Some((_, strategy_old, _, _position)) = self
.reads_global
.iter_mut()
.find(|(var, _, _, _)| var.index() == Some(index))
{
*strategy_old = strategy;
}
}
#[allow(dead_code)]
pub fn read_globals(&self) -> Vec<(u16, ReadingStrategy)> {
self.reads_global
.iter()
.map(|(var, strategy, _, _)| match var {
Variable::GlobalInputArray { id, .. } => (*id, *strategy),
_ => panic!("Can only read global input arrays."),
})
.collect()
}
pub fn register<T: Into<Operation>>(&mut self, operation: T) {
self.operations.push(operation.into())
}
pub fn child(&mut self) -> Self {
Self {
depth: self.depth + 1,
operations: Vec::new(),
locals: Vec::new(),
matrices: Vec::new(),
slices: Vec::new(),
shared_memories: Vec::new(),
const_arrays: Vec::new(),
local_arrays: Vec::new(),
reads_global: Vec::new(),
index_offset_with_output_layout_position: Vec::new(),
writes_global: Vec::new(),
reads_scalar: Vec::new(),
layout_ref: self.layout_ref,
undeclared: 0,
}
}
pub fn process(&mut self) -> ScopeProcessing {
self.undeclared += self.locals.len() as u16;
let mut variables = core::mem::take(&mut self.locals);
for var in self.matrices.drain(..) {
variables.push(var);
}
for var in self.slices.drain(..) {
variables.push(var);
}
let mut operations = Vec::new();
for (local, scalar) in self.reads_scalar.drain(..) {
operations.push(
Operator::Assign(UnaryOperator {
input: scalar,
out: local,
})
.into(),
);
variables.push(local);
}
for op in self.operations.drain(..) {
operations.push(op);
}
ScopeProcessing {
variables,
operations,
}
.optimize()
}
pub fn new_local_index(&self) -> u16 {
self.locals.len() as u16 + self.undeclared
}
fn new_shared_index(&self) -> u16 {
self.shared_memories.len() as u16
}
fn new_const_array_index(&self) -> u16 {
self.const_arrays.len() as u16
}
fn new_local_array_index(&self) -> u16 {
self.local_arrays.len() as u16
}
fn read_input_strategy(
&mut self,
index: u16,
item: Item,
strategy: ReadingStrategy,
position: Variable,
) -> Variable {
let item_global = match item.elem() {
Elem::Bool => Item {
elem: Elem::UInt,
vectorization: item.vectorization,
},
_ => item,
};
let input = Variable::GlobalInputArray {
id: index,
item: item_global,
};
let index = self.new_local_index();
let local = Variable::Local {
id: index,
item,
depth: self.depth,
};
self.reads_global.push((input, strategy, local, position));
self.locals.push(local);
local
}
pub fn create_shared<I: Into<Item>>(&mut self, item: I, shared_memory_size: u32) -> Variable {
let item = item.into();
let index = self.new_shared_index();
let shared_memory = Variable::SharedMemory {
id: index,
item,
length: shared_memory_size,
};
self.shared_memories.push(shared_memory);
shared_memory
}
pub fn create_const_array<I: Into<Item>>(&mut self, item: I, data: Vec<Variable>) -> Variable {
let item = item.into();
let index = self.new_const_array_index();
let const_array = Variable::ConstantArray {
id: index,
item,
length: data.len() as u32,
};
self.const_arrays.push((const_array, data));
const_array
}
pub fn create_local_array<I: Into<Item>>(&mut self, item: I, array_size: u32) -> Variable {
let item = item.into();
let index = self.new_local_array_index();
let local_array = Variable::LocalArray {
id: index,
item,
depth: self.depth,
length: array_size,
};
self.local_arrays.push(local_array);
local_array
}
}