1use crate::lir::LirModule;
11use crate::{DType, NodeId, Shape};
12
13#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct IoBindingEntry {
16 pub name: String,
17 pub node: NodeId,
18 pub dtype: DType,
19 pub shape: Shape,
20 pub elem_count: usize,
21 pub byte_size: usize,
22 pub arena_offset: Option<usize>,
23 pub arena_size: Option<usize>,
24 pub is_view: bool,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct BindingManifest {
30 pub graph_name: String,
31 pub arena_size: usize,
32 pub alignment: usize,
33 pub inputs: Vec<IoBindingEntry>,
34 pub params: Vec<IoBindingEntry>,
35 pub outputs: Vec<IoBindingEntry>,
36}
37
38impl BindingManifest {
39 pub fn from_lir(lir: &LirModule) -> Self {
40 let graph = lir.as_graph();
41 let plan = lir.plan();
42 let io = &plan.io;
43
44 let mut inputs = Vec::new();
45 for (name, id) in &io.inputs {
46 if let Some(e) = entry_for_node(graph, plan, name.clone(), *id) {
47 inputs.push(e);
48 }
49 }
50
51 let mut params = Vec::new();
52 for (name, id) in &io.params {
53 if let Some(e) = entry_for_node(graph, plan, name.clone(), *id) {
54 params.push(e);
55 }
56 }
57
58 let mut outputs = Vec::new();
59 for (i, id) in io.outputs.iter().enumerate() {
60 let name = format!("output{i}");
61 if let Some(e) = entry_for_node(graph, plan, name, *id) {
62 outputs.push(e);
63 }
64 }
65
66 Self {
67 graph_name: lir.name().to_string(),
68 arena_size: plan.arena_size,
69 alignment: plan.alignment,
70 inputs,
71 params,
72 outputs,
73 }
74 }
75
76 pub fn param_names(&self) -> impl Iterator<Item = &str> {
77 self.params.iter().map(|p| p.name.as_str())
78 }
79
80 pub fn input_names(&self) -> impl Iterator<Item = &str> {
81 self.inputs.iter().map(|p| p.name.as_str())
82 }
83
84 pub fn param_byte_size(&self, name: &str) -> Option<usize> {
85 self.params
86 .iter()
87 .find(|p| p.name == name)
88 .map(|p| p.byte_size)
89 }
90
91 pub fn total_param_bytes(&self) -> usize {
92 self.params.iter().map(|p| p.byte_size).sum()
93 }
94
95 pub fn weight_blocks(&self) -> Vec<WeightBlock> {
97 let mut blocks: std::collections::HashMap<String, Vec<IoBindingEntry>> =
98 std::collections::HashMap::new();
99 for p in &self.params {
100 let block = p.name.split('.').next().unwrap_or(&p.name).to_string();
101 blocks.entry(block).or_default().push(p.clone());
102 }
103 let mut out: Vec<WeightBlock> = blocks
104 .into_iter()
105 .map(|(prefix, params)| {
106 let byte_size = params.iter().map(|e| e.byte_size).sum();
107 WeightBlock {
108 prefix,
109 params,
110 byte_size,
111 }
112 })
113 .collect();
114 out.sort_by(|a, b| a.prefix.cmp(&b.prefix));
115 out
116 }
117}
118
119#[derive(Debug, Clone, PartialEq, Eq)]
121pub struct WeightBlock {
122 pub prefix: String,
123 pub params: Vec<IoBindingEntry>,
124 pub byte_size: usize,
125}
126
127fn entry_for_node(
128 graph: &crate::Graph,
129 plan: &crate::lir::LirBufferPlan,
130 name: String,
131 id: NodeId,
132) -> Option<IoBindingEntry> {
133 let node = graph.node(id);
134 let elem_count = node.shape.num_elements().unwrap_or(0);
135 let byte_size = elem_count * node.shape.dtype().size_bytes();
136 let (arena_offset, arena_size, is_view) = if let Some(alias) = plan.view_aliases.get(&id) {
137 let root_slot = plan.slot(alias.root)?;
138 (
139 Some(root_slot.offset + alias.byte_offset),
140 Some(byte_size),
141 true,
142 )
143 } else if let Some(slot) = plan.slot(id) {
144 (Some(slot.offset), Some(slot.size), false)
145 } else {
146 (None, None, false)
147 };
148 Some(IoBindingEntry {
149 name,
150 node: id,
151 dtype: node.shape.dtype(),
152 shape: node.shape.clone(),
153 elem_count,
154 byte_size,
155 arena_offset,
156 arena_size,
157 is_view,
158 })
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164 use crate::Graph;
165 use crate::lir::{LirBufferPlan, LirBufferSlot, LirIoManifest};
166
167 #[test]
168 fn manifest_lists_params_with_sizes() {
169 let mut g = Graph::new("t");
170 let x = g.input("x", Shape::new(&[2, 4], DType::F32));
171 let w = g.param("w", Shape::new(&[4, 3], DType::F32));
172 let mm = g.matmul(x, w, Shape::new(&[2, 3], DType::F32));
173 g.set_outputs(vec![mm]);
174
175 let mut plan = LirBufferPlan {
176 io: LirIoManifest::collect(&g),
177 ..Default::default()
178 };
179 plan.assignments.insert(
180 x,
181 LirBufferSlot {
182 offset: 0,
183 size: 32,
184 },
185 );
186 plan.assignments.insert(
187 w,
188 LirBufferSlot {
189 offset: 32,
190 size: 48,
191 },
192 );
193 plan.assignments.insert(
194 mm,
195 LirBufferSlot {
196 offset: 80,
197 size: 24,
198 },
199 );
200 plan.arena_size = 104;
201
202 let lir = LirModule::new(crate::MirModule::from_graph(g), plan);
203 let m = BindingManifest::from_lir(&lir);
204 assert_eq!(m.params.len(), 1);
205 assert_eq!(m.params[0].name, "w");
206 assert_eq!(m.params[0].byte_size, 48);
207 assert_eq!(m.inputs[0].name, "x");
208 }
209}