Skip to main content

ruda_kernel/dsl/frontend/
unaligned_vector.rs

1use crate::dsl::intrinsic;
2use crate::dsl::ir::{IndexAssignOperator, IndexOperator, Instruction, Operator};
3use crate::dsl::{prelude::*};
4
5/// An extension trait for expanding the ruda frontend with the ability to
6/// request unaligned vector reads and writes
7///
8/// Typically in ruda, a buffer is declared as having a certain vector size
9/// at kernel compilation time. The buffer can then be indexed to produce
10/// vectors that are aligned to the `vector_size`.
11///
12/// This trait allows the user to request a `vector_read` from a buffer where the
13/// start of the read is not aligned to the `vector_read` requested.
14///
15/// As an example, imagine a buffer of scalar length 4. With `vector_size` = 1,
16/// this could be illustrated like so
17/// [1, 2, 3, 4]
18///
19/// Imagine the same buffer, now with `vector_size` = 2.
20/// [[1, 2], [3, 4]]
21///
22/// Vectors can now be accessed from this buffer, but only those that that are aligned
23/// with the `vector_size`. I.e. we can get the vectors [1, 2] or [3, 4], but not [2, 3]
24///
25/// This trait allows you to treat the buffer as having no `vector_size` = 1, yet asking
26/// for a vector of some kernel-compile-time known length at some offset in the buffer.
27/// I.e. if for the buffer `buf = [1, 2, 3, 4]`, `buf.unaligned_vector_read(1, 2)`
28/// will produce the vector `[2, 3]`.
29#[ruda]
30pub trait UnalignedVector<E: Scalar, N: Size>: RudaType + Sized {
31    /// Perform an unchecked read of a vector of the given length at the given index
32    ///
33    /// # Safety
34    /// Out of bounds indexing causes undefined behaviour and may segfault. Ensure `index..index+vector_size` is
35    /// always in bounds
36    fn unaligned_vector_read(&self, index: usize) -> Vector<E, N>;
37
38    /// Perform an unchecked write of a vector of the given length at the given index
39    ///
40    /// # Safety
41    /// Out of bounds indexing causes undefined behaviour and may segfault. Ensure `index..index+vector_size` is
42    /// always in bounds
43    fn unaligned_vector_write(&mut self, index: usize, value: Vector<E, N>);
44}
45
46macro_rules! impl_unaligned_vector {
47    ($type:ident) => {
48        paste::paste! {
49            type [<$type Expand>]<E> = NativeExpand<$type<E>>;
50        }
51        #[ruda]
52        impl<E: Scalar, N: Size> UnalignedVector<E, N> for $type<E> {
53            fn unaligned_vector_read(&self, index: usize) -> Vector<E, N> {
54                unaligned_vector_read::<$type<E>, E, N>(self, index)
55            }
56
57            fn unaligned_vector_write(&mut self, index: usize, value: Vector<E, N>) {
58                unaligned_vector_write::<$type<E>, E, N>(self, index, value)
59            }
60        }
61    };
62}
63
64impl_unaligned_vector!(Array);
65impl_unaligned_vector!(Tensor);
66impl_unaligned_vector!(SharedMemory);
67
68// TODO: Maybe impl unaligned IO on slices?
69// The last dimension will have to be contiguous for this to make sense,
70// as the unaligned IO isn't gather / scatter from arbitrary memory locations
71// and still needs the loaded elements to be contiguous
72
73#[ruda]
74#[allow(unused_variables)]
75fn unaligned_vector_read<T: RudaType<ExpandType = NativeExpand<T>>, E: Scalar, N: Size>(
76    this: &T,
77    index: usize,
78) -> Vector<E, N> {
79    intrinsic!(|scope| {
80        if !matches!(this.expand.ty, crate::dsl::ir::Type::Scalar(_)) {
81            todo!("Unaligned reads are only allowed on scalar arrays for now");
82        }
83        let vector_size = N::__expand_value(scope);
84        let out = scope.create_local(this.expand.ty.with_vector_size(vector_size));
85        scope.register(Instruction::new(
86            Operator::UncheckedIndex(IndexOperator {
87                list: *this.expand,
88                index: index.expand.consume(),
89                vector_size: 0,
90                unroll_factor: 1,
91            }),
92            *out,
93        ));
94        out.into()
95    })
96}
97
98#[ruda]
99#[allow(unused_variables)]
100fn unaligned_vector_write<T: RudaType<ExpandType = NativeExpand<T>>, E: Scalar, N: Size>(
101    this: &mut T,
102    index: usize,
103    value: Vector<E, N>,
104) {
105    intrinsic!(|scope| {
106        if !matches!(this.expand.ty, crate::dsl::ir::Type::Scalar(_)) {
107            todo!("Unaligned reads are only allowed on scalar arrays for now");
108        }
109        let vector_size = N::__expand_value(scope);
110        scope.register(Instruction::new(
111            Operator::UncheckedIndexAssign(IndexAssignOperator {
112                index: index.expand.consume(),
113                value: value.expand.consume(),
114                vector_size: 0,
115                unroll_factor: 1,
116            }),
117            *this.expand,
118        ));
119    })
120}