logicaffeine_compile/codegen_cuda/lane/
graph.rs1use std::collections::{BTreeMap, BTreeSet};
17
18use crate::spatial::passes::recognize_stages::{RecognizedProgram, TensorOpSpec};
19use crate::tensor_graph::graph::{Storage, TensorGraph};
20
21pub use crate::tensor_graph::graph::{GraphError, Role};
22
23#[derive(Clone, Debug, PartialEq, Eq)]
25pub struct Tensor {
26 pub name: String,
27 pub rows: usize,
28 pub cols: usize,
29 pub role: Role,
30}
31
32impl Tensor {
33 pub fn len(&self) -> usize {
34 self.rows * self.cols
35 }
36}
37
38#[derive(Clone, Debug)]
40pub struct Op {
41 pub spec: TensorOpSpec,
42 pub ins: Vec<String>,
43 pub out: String,
44}
45
46#[derive(Clone, Debug)]
49pub struct StepGraph {
50 pub tensors: BTreeMap<String, Tensor>,
51 pub ops: Vec<Op>,
52 pub lifted: TensorGraph,
53}
54
55impl StepGraph {
56 pub fn build(rp: &RecognizedProgram) -> Result<StepGraph, GraphError> {
58 let lifted = TensorGraph::lift(rp, None, None, &|_| Storage::Values)?;
59 let tensors = lifted
60 .tensors
61 .values()
62 .map(|t| (t.name.clone(), Tensor { name: t.name.clone(), rows: t.rows, cols: t.cols, role: t.role }))
63 .collect();
64 let ops = lifted
65 .ops
66 .iter()
67 .map(|o| Op { spec: o.prim().expect("a lifted op is a primitive").clone(), ins: o.ins.clone(), out: o.out.clone() })
68 .collect();
69 Ok(StepGraph { tensors, ops, lifted })
70 }
71
72 pub fn of_role(&self, role: Role) -> impl Iterator<Item = &Tensor> {
74 self.tensors.values().filter(move |t| t.role == role)
75 }
76
77 pub fn readers(&self, tensor: &str) -> Vec<usize> {
79 self.ops.iter().enumerate().filter(|(_, o)| o.ins.iter().any(|x| x == tensor)).map(|(i, _)| i).collect()
80 }
81
82 pub fn caches(&self) -> BTreeSet<String> {
85 let producer: BTreeMap<&str, &Op> = self.ops.iter().map(|o| (o.out.as_str(), o)).collect();
86 let mut out = BTreeSet::new();
87 for op in self.ops.iter().filter(|o| matches!(o.spec, TensorOpSpec::Attention(_))) {
88 for read in &op.ins[1..3] {
89 let mut t = read.as_str();
90 loop {
91 if self.tensors.get(t).is_some_and(|x| x.role == Role::State) {
92 out.insert(t.to_string());
93 break;
94 }
95 match producer.get(t) {
96 Some(p) if matches!(p.spec, TensorOpSpec::HeadSlice(_)) => t = &p.ins[0],
97 _ => break,
98 }
99 }
100 }
101 }
102 out
103 }
104}