1use crate::arena::Handle;
4
5pub type Bytes = u8;
7
8#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
10pub enum ScalarKind {
11 Bool,
13 Sint,
15 Uint,
17 Float,
19 BFloat,
21}
22
23#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
25pub struct Scalar {
26 pub kind: ScalarKind,
28 pub width: Bytes,
30}
31
32impl Scalar {
33 pub const BOOL: Self = Self {
34 kind: ScalarKind::Bool,
35 width: 1,
36 };
37 pub const I32: Self = Self {
38 kind: ScalarKind::Sint,
39 width: 4,
40 };
41 pub const U32: Self = Self {
42 kind: ScalarKind::Uint,
43 width: 4,
44 };
45 pub const F16: Self = Self {
46 kind: ScalarKind::Float,
47 width: 2,
48 };
49 pub const F32: Self = Self {
50 kind: ScalarKind::Float,
51 width: 4,
52 };
53 pub const I8: Self = Self {
54 kind: ScalarKind::Sint,
55 width: 1,
56 };
57 pub const U8: Self = Self {
58 kind: ScalarKind::Uint,
59 width: 1,
60 };
61 pub const BF16: Self = Self {
62 kind: ScalarKind::BFloat,
63 width: 2,
64 };
65}
66
67#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
69pub enum VectorSize {
70 Bi = 2,
72 Tri = 3,
74 Quad = 4,
76}
77
78#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
80pub enum ArraySize {
81 Constant(u32),
83 Dynamic,
85}
86
87#[derive(Clone, Debug, Hash, Eq, PartialEq)]
90pub enum Dimension {
91 Fixed(u32),
93 Dynamic(Option<String>),
95 Symbolic(String),
101}
102
103impl Dimension {
104 pub fn is_fixed(&self) -> bool {
106 matches!(self, Self::Fixed(_))
107 }
108
109 pub fn is_dynamic(&self) -> bool {
111 matches!(self, Self::Dynamic(_))
112 }
113
114 pub fn is_symbolic(&self) -> bool {
116 matches!(self, Self::Symbolic(_))
117 }
118
119 pub fn fixed_size(&self) -> Option<u32> {
121 match self {
122 Self::Fixed(n) => Some(*n),
123 Self::Dynamic(_) | Self::Symbolic(_) => None,
124 }
125 }
126
127 pub fn symbolic_name(&self) -> Option<&str> {
129 match self {
130 Self::Symbolic(name) => Some(name),
131 Self::Dynamic(Some(name)) => Some(name),
132 _ => None,
133 }
134 }
135}
136
137#[derive(Clone, Debug, Hash, Eq, PartialEq)]
142pub struct TensorShape {
143 pub dims: Vec<Dimension>,
145}
146
147impl TensorShape {
148 pub fn fixed(sizes: &[u32]) -> Self {
150 Self {
151 dims: sizes.iter().map(|&s| Dimension::Fixed(s)).collect(),
152 }
153 }
154
155 pub fn all_dynamic(rank: usize) -> Self {
157 Self {
158 dims: (0..rank).map(|_| Dimension::Dynamic(None)).collect(),
159 }
160 }
161
162 pub fn rank(&self) -> usize {
164 self.dims.len()
165 }
166
167 pub fn is_fully_static(&self) -> bool {
169 self.dims.iter().all(|d| d.is_fixed())
170 }
171
172 pub fn is_fully_dynamic(&self) -> bool {
174 self.dims.iter().all(|d| d.is_dynamic())
175 }
176
177 pub fn has_dynamic_dims(&self) -> bool {
180 self.dims.iter().any(|d| !d.is_fixed())
181 }
182
183 pub fn is_mixed(&self) -> bool {
185 !self.is_fully_static() && !self.is_fully_dynamic()
186 }
187}
188
189#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
195pub enum MemoryLayout {
196 RowMajor,
198 ColMajor,
200 Nhwc,
202 Nchw,
204}
205
206impl MemoryLayout {
207 pub fn name(self) -> &'static str {
209 match self {
210 Self::RowMajor => "row_major",
211 Self::ColMajor => "col_major",
212 Self::Nhwc => "nhwc",
213 Self::Nchw => "nchw",
214 }
215 }
216}
217
218#[derive(Clone, Debug, Hash, Eq, PartialEq)]
220pub struct StructMember {
221 pub name: Option<String>,
223 pub ty: Handle<Type>,
225 pub offset: u32,
227}
228
229#[derive(Clone, Debug, Hash, Eq, PartialEq)]
231pub struct Type {
232 pub name: Option<String>,
234 pub inner: TypeInner,
236}
237
238#[derive(Clone, Debug, Hash, Eq, PartialEq)]
240pub enum TypeInner {
241 Scalar(Scalar),
243 Vector { size: VectorSize, scalar: Scalar },
245 Matrix {
247 columns: VectorSize,
248 rows: VectorSize,
249 scalar: Scalar,
250 },
251 Atomic(Scalar),
253 Pointer {
255 base: Handle<Type>,
256 space: crate::AddressSpace,
257 },
258 Array {
260 base: Handle<Type>,
261 size: ArraySize,
262 stride: u32,
263 },
264 Struct {
266 members: Vec<StructMember>,
267 span: u32,
268 },
269 Tensor {
274 scalar: Scalar,
276 shape: TensorShape,
278 },
279}
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284 use crate::arena::UniqueArena;
285
286 #[test]
287 fn scalar_constants() {
288 assert_eq!(Scalar::F32.kind, ScalarKind::Float);
289 assert_eq!(Scalar::F32.width, 4);
290 assert_eq!(Scalar::U32.kind, ScalarKind::Uint);
291 assert_eq!(Scalar::U32.width, 4);
292 assert_eq!(Scalar::BOOL.width, 1);
293 assert_eq!(Scalar::F16.width, 2);
294 }
295
296 #[test]
297 fn type_dedup() {
298 let mut types = UniqueArena::new();
299 let t0 = types.insert(Type {
300 name: None,
301 inner: TypeInner::Scalar(Scalar::F32),
302 });
303 let t1 = types.insert(Type {
304 name: None,
305 inner: TypeInner::Scalar(Scalar::F32),
306 });
307 assert_eq!(t0, t1);
308 assert_eq!(types.len(), 1);
309 }
310
311 #[test]
312 fn different_types_not_deduped() {
313 let mut types = UniqueArena::new();
314 let t0 = types.insert(Type {
315 name: None,
316 inner: TypeInner::Scalar(Scalar::F32),
317 });
318 let t1 = types.insert(Type {
319 name: None,
320 inner: TypeInner::Scalar(Scalar::I32),
321 });
322 assert_ne!(t0, t1);
323 assert_eq!(types.len(), 2);
324 }
325
326 #[test]
327 fn vector_type() {
328 let ty = TypeInner::Vector {
329 size: VectorSize::Tri,
330 scalar: Scalar::F32,
331 };
332 if let TypeInner::Vector { size, scalar } = ty {
333 assert_eq!(size, VectorSize::Tri);
334 assert_eq!(scalar, Scalar::F32);
335 } else {
336 panic!("expected Vector");
337 }
338 }
339
340 #[test]
341 fn vector_size_values() {
342 assert_eq!(VectorSize::Bi as u32, 2);
343 assert_eq!(VectorSize::Tri as u32, 3);
344 assert_eq!(VectorSize::Quad as u32, 4);
345 }
346
347 #[test]
348 fn dimension_fixed() {
349 let d = Dimension::Fixed(224);
350 assert!(d.is_fixed());
351 assert!(!d.is_dynamic());
352 assert_eq!(d.fixed_size(), Some(224));
353 }
354
355 #[test]
356 fn dimension_dynamic() {
357 let d = Dimension::Dynamic(Some("batch".into()));
358 assert!(!d.is_fixed());
359 assert!(d.is_dynamic());
360 assert!(!d.is_symbolic());
361 assert_eq!(d.fixed_size(), None);
362 assert_eq!(d.symbolic_name(), Some("batch"));
363 }
364
365 #[test]
366 fn dimension_symbolic() {
367 let d = Dimension::Symbolic("batch".into());
368 assert!(!d.is_fixed());
369 assert!(!d.is_dynamic());
370 assert!(d.is_symbolic());
371 assert_eq!(d.fixed_size(), None);
372 assert_eq!(d.symbolic_name(), Some("batch"));
373 }
374
375 #[test]
376 fn tensor_shape_has_dynamic_dims() {
377 let static_shape = TensorShape::fixed(&[1, 224, 224, 3]);
378 assert!(!static_shape.has_dynamic_dims());
379
380 let mixed_shape = TensorShape {
381 dims: vec![Dimension::Symbolic("batch".into()), Dimension::Fixed(224)],
382 };
383 assert!(mixed_shape.has_dynamic_dims());
384
385 let dynamic_shape = TensorShape::all_dynamic(3);
386 assert!(dynamic_shape.has_dynamic_dims());
387 }
388
389 #[test]
390 fn tensor_shape_fixed() {
391 let shape = TensorShape::fixed(&[1, 224, 224, 3]);
392 assert_eq!(shape.rank(), 4);
393 assert!(shape.is_fully_static());
394 assert!(!shape.is_fully_dynamic());
395 assert!(!shape.is_mixed());
396 }
397
398 #[test]
399 fn tensor_shape_all_dynamic() {
400 let shape = TensorShape::all_dynamic(3);
401 assert_eq!(shape.rank(), 3);
402 assert!(shape.is_fully_dynamic());
403 assert!(!shape.is_fully_static());
404 assert!(!shape.is_mixed());
405 }
406
407 #[test]
408 fn tensor_shape_mixed() {
409 let shape = TensorShape {
410 dims: vec![
411 Dimension::Dynamic(Some("batch".into())),
412 Dimension::Fixed(224),
413 Dimension::Fixed(224),
414 Dimension::Fixed(3),
415 ],
416 };
417 assert_eq!(shape.rank(), 4);
418 assert!(shape.is_mixed());
419 assert!(!shape.is_fully_static());
420 assert!(!shape.is_fully_dynamic());
421 }
422
423 #[test]
424 fn tensor_type_inner() {
425 let mut types = UniqueArena::new();
426 let t = types.insert(Type {
427 name: Some("image".into()),
428 inner: TypeInner::Tensor {
429 scalar: Scalar::F32,
430 shape: TensorShape {
431 dims: vec![
432 Dimension::Dynamic(Some("batch".into())),
433 Dimension::Fixed(224),
434 Dimension::Fixed(224),
435 Dimension::Fixed(3),
436 ],
437 },
438 },
439 });
440 let ty = &types[t];
441 match &ty.inner {
442 TypeInner::Tensor { scalar, shape } => {
443 assert_eq!(*scalar, Scalar::F32);
444 assert_eq!(shape.rank(), 4);
445 assert!(shape.is_mixed());
446 }
447 _ => panic!("expected Tensor"),
448 }
449 }
450}