1use core::cell::{Ref, RefMut};
2
3use alloc::boxed::Box;
4use derive_new::new;
5use pliron::{
6 attribute::{AttrObj, Attribute, AttributeDict},
7 builtin::{
8 attributes::{DictAttr, UnitAttr, VecAttr},
9 ops::FuncOp,
10 },
11 dict_key,
12 identifier::Identifier,
13};
14
15use crate::{prelude::*, settings::Dim3};
16
17#[pliron_attr(
18 name = "cube.entrypoint_abi",
19 format = "`<cube_dim: ` $cube_dim opt($cluster_dim) `>`",
20 verifier = "succ"
21)]
22#[derive(new, PartialEq, Clone, Debug, Hash)]
23pub struct EntrypointAbiAttr {
24 pub cube_dim: Dim3,
25 pub cluster_dim: Option<Dim3>,
26}
27
28dict_key!(
29 ATTR_KEY_ENTRY_POINT, "entry_point"
31);
32dict_key!(ATTR_BUFFER_BINDING, "buffer_binding");
33dict_key!(ATTR_TENSOR_MAP_BINDING, "tensor_map_binding");
34dict_key!(
35 ATTR_PEER_BINDING, "peer_binding"
39);
40
41#[pliron_attr(name = "cube.buffer_io", format, verifier = "succ")]
53#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
54#[derive(new, PartialEq, Eq, Clone, Copy, Debug, Hash)]
55pub enum BufferIOAttr {
56 ReadOnly,
58 WriteOnly,
60 ReadWrite,
62 Dead,
64}
65
66impl BufferIOAttr {
67 pub fn is_readable(&self) -> bool {
70 matches!(self, BufferIOAttr::ReadOnly | BufferIOAttr::ReadWrite)
71 }
72
73 pub fn is_writable(&self) -> bool {
76 matches!(self, BufferIOAttr::WriteOnly | BufferIOAttr::ReadWrite)
77 }
78
79 pub fn is_dead(&self) -> bool {
81 matches!(self, BufferIOAttr::Dead)
82 }
83}
84
85dict_key!(ATTR_BUFFER_IO, "binding_io");
86
87#[pliron_attr(
88 name = "cube.buffer_binding",
89 format = "`<(` $buffer_pos `, ` opt($ext_meta_pos) `)`",
90 verifier = "succ"
91)]
92#[derive(new, PartialEq, Clone, Copy, Debug, Hash)]
93pub struct BufferBindingAttr {
94 pub buffer_pos: usize,
95 pub ext_meta_pos: Option<usize>,
96}
97
98#[op_interface]
105pub trait EntrypointInterface {
106 verify_op_succ!();
107
108 fn get_entrypoint_abi(&self, ctx: &Context) -> Option<EntrypointAbiAttr> {
110 let self_op = self.get_operation().deref(ctx);
111 let s_attr = self_op
112 .attributes
113 .get::<EntrypointAbiAttr>(&ATTR_KEY_ENTRY_POINT);
114 s_attr.cloned()
115 }
116
117 fn set_entrypoint_abi(&self, ctx: &mut Context, entry_point_abi: EntrypointAbiAttr) {
119 let mut self_op = self.get_operation().deref_mut(ctx);
120 self_op
121 .attributes
122 .set(ATTR_KEY_ENTRY_POINT.clone(), entry_point_abi);
123 }
124}
125
126#[op_interface_impl]
127impl EntrypointInterface for FuncOp {}
128
129dict_key!(ATTR_KEY_ARG_ATTRS, "arg_attrs");
130dict_key!(ATTR_KEY_RES_ATTRS, "res_attrs");
131
132pub trait FuncInterface: Op {
140 fn get_arg_attrs<'a>(&self, ctx: &'a Context, arg_idx: usize) -> Option<Ref<'a, DictAttr>> {
141 let self_op = self.get_operation().deref(ctx);
142 Ref::filter_map(self_op, |self_op| {
143 let args_attrs = self_op.attributes.get::<VecAttr>(&ATTR_KEY_ARG_ATTRS)?;
144 args_attrs.0.get(arg_idx)?.downcast_ref()
145 })
146 .ok()
147 }
148
149 fn get_arg_attr<'a, T: Attribute>(
150 &self,
151 ctx: &'a Context,
152 arg_idx: usize,
153 key: &Identifier,
154 ) -> Option<Ref<'a, T>> {
155 let arg_attrs = self.get_arg_attrs(ctx, arg_idx)?;
156 Ref::filter_map(arg_attrs, |arg_attrs| {
157 arg_attrs.lookup(key).and_then(|attr| attr.downcast_ref())
158 })
159 .ok()
160 }
161
162 fn has_arg_attr(&self, ctx: &Context, arg_idx: usize, key: &Identifier) -> bool {
163 self.get_arg_attrs(ctx, arg_idx)
164 .is_some_and(|arg_attrs| arg_attrs.lookup(key).is_some())
165 }
166
167 fn get_arg_attrs_mut<'a>(&self, ctx: &'a Context, arg_idx: usize) -> RefMut<'a, DictAttr> {
168 let self_op = self.get_operation().deref_mut(ctx);
169 RefMut::map(self_op, |self_op| {
170 get_arg_or_init_mut(&mut self_op.attributes, arg_idx)
171 })
172 }
173
174 fn get_res_attrs<'a>(&self, ctx: &'a Context, res_idx: usize) -> Option<Ref<'a, DictAttr>> {
175 let self_op = self.get_operation().deref(ctx);
176 Ref::filter_map(self_op, |self_op| {
177 let res_attrs = self_op.attributes.get::<VecAttr>(&ATTR_KEY_RES_ATTRS)?;
178 res_attrs.0.get(res_idx)?.downcast_ref()
179 })
180 .ok()
181 }
182
183 fn get_res_attr<'a, T: Attribute>(
184 &self,
185 ctx: &'a Context,
186 res_idx: usize,
187 key: &Identifier,
188 ) -> Option<Ref<'a, T>> {
189 let res_attrs = self.get_res_attrs(ctx, res_idx)?;
190 Ref::filter_map(res_attrs, |res_attrs| {
191 res_attrs.lookup(key).and_then(|attr| attr.downcast_ref())
192 })
193 .ok()
194 }
195
196 fn has_res_attr(&self, ctx: &Context, res_idx: usize, key: &Identifier) -> bool {
197 self.get_res_attrs(ctx, res_idx)
198 .is_some_and(|res_attrs| res_attrs.lookup(key).is_some())
199 }
200
201 fn get_res_attrs_mut<'a>(&self, ctx: &'a Context, res_idx: usize) -> RefMut<'a, DictAttr> {
202 let self_op = self.get_operation().deref_mut(ctx);
203 RefMut::map(self_op, |self_op| {
204 get_res_or_init_mut(&mut self_op.attributes, res_idx)
205 })
206 }
207
208 fn set_arg_attrs(&self, ctx: &Context, arg_idx: usize, dict: DictAttr) {
209 *self.get_arg_attrs_mut(ctx, arg_idx) = dict;
210 }
211
212 fn set_arg_attr(&self, ctx: &Context, arg_idx: usize, key: &Identifier, value: AttrObj) {
213 self.get_arg_attrs_mut(ctx, arg_idx).insert(key, value);
214 }
215
216 fn set_arg_attr_unit(&self, ctx: &Context, arg_idx: usize, key: &Identifier) {
217 self.get_arg_attrs_mut(ctx, arg_idx)
218 .insert(key, Box::new(UnitAttr::new()));
219 }
220
221 fn remove_arg_attr(&self, ctx: &Context, arg_idx: usize, key: &Identifier) {
222 self.get_arg_attrs_mut(ctx, arg_idx).remove(key);
223 }
224
225 fn set_res_attrs(&self, ctx: &Context, res_idx: usize, dict: DictAttr) {
226 *self.get_res_attrs_mut(ctx, res_idx) = dict;
227 }
228
229 fn set_res_attr(&self, ctx: &Context, res_idx: usize, key: &Identifier, value: AttrObj) {
230 self.get_res_attrs_mut(ctx, res_idx).insert(key, value);
231 }
232
233 fn remove_res_attr(&self, ctx: &Context, res_idx: usize, key: &Identifier) {
234 self.get_res_attrs_mut(ctx, res_idx).remove(key);
235 }
236
237 fn set_res_attr_unit(&self, ctx: &Context, res_idx: usize, key: &Identifier) {
238 self.get_res_attrs_mut(ctx, res_idx)
239 .insert(key, Box::new(UnitAttr::new()));
240 }
241}
242
243pub fn peer_buffer_positions(ctx: &Context, func: FuncOp) -> alloc::vec::Vec<usize> {
256 let num_args = func.get_entry_block(ctx).deref(ctx).get_num_arguments();
257 (0..num_args)
258 .filter(|arg| func.has_arg_attr(ctx, *arg, &ATTR_PEER_BINDING))
259 .filter_map(|arg| {
260 func.get_arg_attr::<BufferBindingAttr>(ctx, arg, &ATTR_BUFFER_BINDING)
261 .map(|binding| binding.buffer_pos)
262 })
263 .collect()
264}
265
266pub fn buffer_io_by_position(ctx: &Context, func: FuncOp) -> alloc::vec::Vec<BufferIOAttr> {
267 let num_args = func.get_entry_block(ctx).deref(ctx).get_num_arguments();
268 let mut io = alloc::vec::Vec::new();
269 for arg in 0..num_args {
270 let binding = func
271 .get_arg_attr::<BufferBindingAttr>(ctx, arg, &ATTR_BUFFER_BINDING)
272 .map(|it| *it);
273 let Some(binding) = binding else {
274 continue;
275 };
276 let stamped = func
277 .get_arg_attr::<BufferIOAttr>(ctx, arg, &ATTR_BUFFER_IO)
278 .map(|it| *it)
279 .unwrap_or(BufferIOAttr::ReadWrite);
280 if io.len() <= binding.buffer_pos {
281 io.resize(binding.buffer_pos + 1, BufferIOAttr::ReadWrite);
282 }
283 io[binding.buffer_pos] = stamped;
284 }
285 io
286}
287
288fn get_arg_or_init_mut(dict: &mut AttributeDict, arg_idx: usize) -> &mut DictAttr {
289 let args_attrs = attr_get_or_insert_mut(dict, &ATTR_KEY_ARG_ATTRS, || VecAttr::new(vec![]))
290 .expect("Should be `VecAttr`");
291 vec_get_or_insert_mut(args_attrs, arg_idx, || DictAttr::new(vec![]))
292 .expect("Should be `DictAttr`")
293}
294
295fn get_res_or_init_mut(dict: &mut AttributeDict, arg_idx: usize) -> &mut DictAttr {
296 let res_attrs = attr_get_or_insert_mut(dict, &ATTR_KEY_RES_ATTRS, || VecAttr::new(vec![]))
297 .expect("Should be `VecAttr`");
298 vec_get_or_insert_mut(res_attrs, arg_idx, || DictAttr::new(vec![]))
299 .expect("Should be `DictAttr`")
300}
301
302fn attr_get_or_insert_mut<'a, T: Attribute>(
303 dict: &'a mut AttributeDict,
304 key: &Identifier,
305 init: impl FnOnce() -> T,
306) -> Option<&'a mut T> {
307 if !dict.0.contains_key(key) {
308 dict.set(key.clone(), init());
309 }
310 dict.get_mut(key)
311}
312
313fn vec_get_or_insert_mut<T: Attribute>(
314 vec: &mut VecAttr,
315 idx: usize,
316 mut init: impl FnMut() -> T,
317) -> Option<&mut T> {
318 if vec.0.len() <= idx {
319 vec.0.resize_with(idx + 1, || Box::new(init()));
320 }
321 vec.0[idx].downcast_mut()
322}
323
324impl FuncInterface for FuncOp {}