use serde::{Deserialize, Serialize};
use crate::ir::ConstantScalarValue;
use super::{
cpa, processing::ScopeProcessing, Allocator, Elem, Id, Instruction, Item, Operation, UIntKind,
Variable, VariableKind,
};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[allow(missing_docs)]
pub struct Scope {
pub depth: u8,
pub operations: Vec<Instruction>,
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>,
#[serde(skip)]
pub allocator: Allocator,
}
#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, Serialize, Deserialize)]
#[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,
allocator: Allocator::default(),
}
}
pub fn zero<I: Into<Item>>(&mut self, item: I) -> Variable {
let local = self.create_local(item.into());
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) | Elem::AtomicFloat(kind) => {
ConstantScalarValue::Float(value.to_f64().unwrap(), kind)
}
Elem::Int(kind) | Elem::AtomicInt(kind) => {
ConstantScalarValue::Int(value.to_i64().unwrap(), kind)
}
Elem::UInt(kind) | Elem::AtomicUInt(kind) => {
ConstantScalarValue::UInt(value.to_u64().unwrap(), kind)
}
Elem::Bool => ConstantScalarValue::Bool(value.to_u32().unwrap() == 1),
};
let local = self.create_local(item);
let value = Variable::constant(value);
cpa!(self, local = value);
local
}
pub fn add_matrix(&mut self, variable: Variable) {
self.matrices.push(variable);
}
pub fn add_slice(&mut self, slice: Variable) {
self.slices.push(slice);
}
pub fn create_local_mut<I: Into<Item>>(&mut self, item: I) -> Variable {
let id = self.new_local_index();
let local = Variable::new(VariableKind::LocalMut { id }, item.into());
self.add_local_mut(local);
local
}
pub fn add_local_mut(&mut self, var: Variable) {
if !self.locals.contains(&var) {
self.locals.push(var);
}
}
pub fn create_local_restricted(&mut self, item: Item) -> Variable {
*self.allocator.create_local_restricted(item)
}
pub fn create_local(&mut self, item: Item) -> Variable {
*self.allocator.create_local(item)
}
pub fn read_array<I: Into<Item>>(
&mut self,
index: Id,
item: I,
position: Variable,
) -> Variable {
self.read_input_strategy(index, item.into(), ReadingStrategy::OutputLayout, position)
}
pub fn read_scalar(&mut self, index: Id, elem: Elem) -> Variable {
let id = self.new_local_index();
let local = Variable::new(VariableKind::LocalConst { id }, Item::new(elem));
let scalar = Variable::new(VariableKind::GlobalScalar(index), Item::new(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: Id, 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<(Id, ReadingStrategy)> {
self.reads_global
.iter()
.map(|(var, strategy, _, _)| match var.kind {
VariableKind::GlobalInputArray(id) => (id, *strategy),
_ => panic!("Can only read global input arrays."),
})
.collect()
}
pub fn register<T: Into<Instruction>>(&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,
allocator: self.allocator.clone(),
}
}
pub fn process(&mut self) -> ScopeProcessing {
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(Instruction::new(Operation::Copy(scalar), local));
variables.push(local);
}
for op in self.operations.drain(..) {
operations.push(op);
}
ScopeProcessing {
variables,
operations,
}
.optimize()
}
pub fn new_local_index(&self) -> u32 {
self.allocator.new_local_index()
}
fn new_shared_index(&self) -> Id {
self.shared_memories.len() as Id
}
fn new_const_array_index(&self) -> Id {
self.const_arrays.len() as Id
}
fn read_input_strategy(
&mut self,
index: Id,
item: Item,
strategy: ReadingStrategy,
position: Variable,
) -> Variable {
let item_global = match item.elem() {
Elem::Bool => Item {
elem: Elem::UInt(UIntKind::U32),
vectorization: item.vectorization,
},
_ => item,
};
let input = Variable::new(VariableKind::GlobalInputArray(index), item_global);
let id = self.new_local_index();
let local = Variable::new(VariableKind::LocalMut { id }, item);
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::new(
VariableKind::SharedMemory {
id: index,
length: shared_memory_size,
},
item,
);
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::new(
VariableKind::ConstantArray {
id: index,
length: data.len() as u32,
},
item,
);
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 local_array = self.allocator.create_local_array(item.into(), array_size);
self.add_local_array(*local_array);
*local_array
}
pub fn add_local_array(&mut self, var: Variable) {
self.local_arrays.push(var);
}
}