Skip to main content

ruda_kernel/dsl/frontend/operation/
base.rs

1use ruda_core::ir::{
2    Arithmetic, BinaryOperator, Comparison, ElemType, IndexAssignOperator, IndexOperator,
3    Instruction, ManagedVariable, Operation, Operator, Scope, Type, UnaryOperator, Variable,
4    VariableKind, VectorSize,
5};
6use ruda_kernel_macros::ruda;
7
8use crate::dsl::{
9    prelude::{RudaIndex, RudaType, Int, NativeExpand, eq, rem},
10};
11
12pub(crate) fn binary_expand<F, Op>(
13    scope: &mut Scope,
14    lhs: ManagedVariable,
15    rhs: ManagedVariable,
16    func: F,
17) -> ManagedVariable
18where
19    F: Fn(BinaryOperator) -> Op,
20    Op: Into<Operation>,
21{
22    let lhs = lhs.consume();
23    let rhs = rhs.consume();
24
25    let item_lhs = lhs.ty;
26    let item_rhs = rhs.ty;
27
28    let vector_size = find_vectorization(item_lhs, item_rhs);
29
30    let item = item_lhs.with_vector_size(vector_size);
31
32    let output = scope.create_local(item);
33    let out = *output;
34
35    let op = func(BinaryOperator { lhs, rhs });
36
37    scope.register(Instruction::new(op, out));
38
39    output
40}
41
42pub(crate) fn index_expand_no_vec<F>(
43    scope: &mut Scope,
44    list: ManagedVariable,
45    index: ManagedVariable,
46    func: F,
47) -> ManagedVariable
48where
49    F: Fn(IndexOperator) -> Operator,
50{
51    let list = list.consume();
52    let index = index.consume();
53
54    let item_lhs = list.ty;
55
56    let item = item_lhs.with_vector_size(0);
57
58    let output = scope.create_local(item);
59    let out = *output;
60
61    let op = func(IndexOperator {
62        list,
63        index,
64        vector_size: 0,
65        unroll_factor: 1,
66    });
67
68    scope.register(Instruction::new(op, out));
69
70    output
71}
72pub(crate) fn index_expand<F, Op>(
73    scope: &mut Scope,
74    list: ManagedVariable,
75    index: ManagedVariable,
76    vector_size: Option<VectorSize>,
77    func: F,
78) -> ManagedVariable
79where
80    F: Fn(IndexOperator) -> Op,
81    Op: Into<Operation>,
82{
83    let list = list.consume();
84    let index = index.consume();
85
86    let item_lhs = list.ty;
87    let item_rhs = index.ty;
88
89    let vec = if let Some(vector_size) = vector_size {
90        vector_size
91    } else {
92        find_vectorization(item_lhs, item_rhs)
93    };
94
95    let item = item_lhs.with_vector_size(vec);
96
97    let output = scope.create_local(item);
98    let out = *output;
99
100    let op = func(IndexOperator {
101        list,
102        index,
103        vector_size: vector_size.unwrap_or(0),
104        unroll_factor: 1,
105    });
106
107    scope.register(Instruction::new(op, out));
108
109    output
110}
111
112pub(crate) fn binary_expand_fixed_output<F>(
113    scope: &mut Scope,
114    lhs: ManagedVariable,
115    rhs: ManagedVariable,
116    out_item: Type,
117    func: F,
118) -> ManagedVariable
119where
120    F: Fn(BinaryOperator) -> Arithmetic,
121{
122    let lhs_var = lhs.consume();
123    let rhs_var = rhs.consume();
124
125    let out = scope.create_local(out_item);
126
127    let out_var = *out;
128
129    let op = func(BinaryOperator {
130        lhs: lhs_var,
131        rhs: rhs_var,
132    });
133
134    scope.register(Instruction::new(op, out_var));
135
136    out
137}
138
139pub(crate) fn cmp_expand<F>(
140    scope: &mut Scope,
141    lhs: ManagedVariable,
142    rhs: ManagedVariable,
143    func: F,
144) -> ManagedVariable
145where
146    F: Fn(BinaryOperator) -> Comparison,
147{
148    let lhs = lhs.consume();
149    let rhs = rhs.consume();
150
151    let item_lhs = lhs.ty;
152    let item_rhs = rhs.ty;
153
154    let vector_size = find_vectorization(item_lhs, item_rhs);
155
156    let out_item = Type::scalar(ElemType::Bool).with_vector_size(vector_size);
157
158    let out = scope.create_local(out_item);
159    let out_var = *out;
160
161    let op = func(BinaryOperator { lhs, rhs });
162
163    scope.register(Instruction::new(op, out_var));
164
165    out
166}
167
168pub(crate) fn assign_op_expand<F, Op>(
169    scope: &mut Scope,
170    lhs: ManagedVariable,
171    rhs: ManagedVariable,
172    func: F,
173) -> ManagedVariable
174where
175    F: Fn(BinaryOperator) -> Op,
176    Op: Into<Operation>,
177{
178    if lhs.is_immutable() {
179        panic!("Can't have a mutable operation on a const variable. Try to use `RuntimeCell`.");
180    }
181    let lhs_var: Variable = *lhs;
182    let rhs: Variable = *rhs;
183
184    let op = func(BinaryOperator { lhs: lhs_var, rhs });
185
186    scope.register(Instruction::new(op, lhs_var));
187
188    lhs
189}
190
191pub fn unary_expand<F, Op>(scope: &mut Scope, input: ManagedVariable, func: F) -> ManagedVariable
192where
193    F: Fn(UnaryOperator) -> Op,
194    Op: Into<Operation>,
195{
196    let input = input.consume();
197    let item = input.ty;
198
199    let out = scope.create_local(item);
200    let out_var = *out;
201
202    let op = func(UnaryOperator { input });
203
204    scope.register(Instruction::new(op, out_var));
205
206    out
207}
208
209pub fn unary_expand_fixed_output<F, Op>(
210    scope: &mut Scope,
211    input: ManagedVariable,
212    out_item: Type,
213    func: F,
214) -> ManagedVariable
215where
216    F: Fn(UnaryOperator) -> Op,
217    Op: Into<Operation>,
218{
219    let input = input.consume();
220    let output = scope.create_local(out_item);
221    let out = *output;
222
223    let op = func(UnaryOperator { input });
224
225    scope.register(Instruction::new(op, out));
226
227    output
228}
229
230pub fn init_expand<F>(
231    scope: &mut Scope,
232    input: ManagedVariable,
233    mutable: bool,
234    func: F,
235) -> ManagedVariable
236where
237    F: Fn(Variable) -> Operation,
238{
239    let input_var: Variable = *input;
240    let item = input.ty;
241
242    let out = if mutable {
243        scope.create_local_mut(item)
244    } else {
245        scope.create_local(item)
246    };
247
248    let out_var = *out;
249
250    let op = func(input_var);
251    scope.register(Instruction::new(op, out_var));
252
253    out
254}
255
256pub(crate) fn find_vectorization(lhs: Type, rhs: Type) -> VectorSize {
257    if matches!(lhs, Type::Scalar(_)) && matches!(rhs, Type::Scalar(_)) {
258        0
259    } else {
260        lhs.vector_size().max(rhs.vector_size())
261    }
262}
263
264pub fn array_assign_binary_op_expand<
265    A: RudaType + RudaIndex,
266    V: RudaType,
267    F: Fn(BinaryOperator) -> Op,
268    Op: Into<Operation>,
269>(
270    scope: &mut Scope,
271    array: NativeExpand<A>,
272    index: NativeExpand<usize>,
273    value: NativeExpand<V>,
274    func: F,
275) where
276    A::Output: RudaType + Sized,
277{
278    let array: ManagedVariable = array.into();
279    let index: ManagedVariable = index.into();
280    let value: ManagedVariable = value.into();
281
282    let array_item = match array.kind {
283        // In that case, the array is a vector.
284        VariableKind::LocalMut { .. } => array.ty.with_vector_size(0),
285        _ => array.ty,
286    };
287    let array_value = scope.create_local(array_item);
288
289    let read = Instruction::new(
290        Operator::Index(IndexOperator {
291            list: *array,
292            index: *index,
293            vector_size: 0,
294            unroll_factor: 1,
295        }),
296        *array_value,
297    );
298    let array_value = array_value.consume();
299    let op_out = scope.create_local(array_item);
300    let calculate = Instruction::new(
301        func(BinaryOperator {
302            lhs: array_value,
303            rhs: *value,
304        }),
305        *op_out,
306    );
307
308    let write = Operator::IndexAssign(IndexAssignOperator {
309        index: *index,
310        value: op_out.consume(),
311        vector_size: 0,
312        unroll_factor: 1,
313    });
314    scope.register(read);
315    scope.register(calculate);
316    scope.register(Instruction::new(write, *array));
317}
318
319pub trait DivCeil: Int + RudaType<ExpandType: DivCeilExpand<Self>> {
320    fn div_ceil(self, divisor: Self) -> Self;
321
322    fn __expand_div_ceil(
323        scope: &mut Scope,
324        a: NativeExpand<Self>,
325        b: NativeExpand<Self>,
326    ) -> NativeExpand<Self> {
327        a.__expand_div_ceil_method(scope, b)
328    }
329}
330
331pub trait DivCeilExpand<E: Int> {
332    fn __expand_div_ceil_method(self, scope: &mut Scope, divisor: Self) -> Self;
333}
334
335impl<E: DivCeil> DivCeilExpand<E> for NativeExpand<E> {
336    fn __expand_div_ceil_method(
337        self,
338        scope: &mut Scope,
339        divisor: NativeExpand<E>,
340    ) -> NativeExpand<E> {
341        div_ceil::expand::<E>(scope, self, divisor)
342    }
343}
344
345macro_rules! impl_div_ceil {
346    ($($ty:ty),*) => {
347        $(
348            impl DivCeil for $ty {
349                #[allow(clippy::manual_div_ceil)] // Need to define div_ceil to use div_ceil!
350                fn div_ceil(self, divisor: Self) -> Self {
351                    (self + divisor - 1) / divisor
352                }
353            }
354        )*
355    };
356}
357
358impl_div_ceil!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
359
360impl<E: Int> NativeExpand<E> {
361    pub fn __expand_is_multiple_of_method(
362        self,
363        scope: &mut Scope,
364        factor: NativeExpand<E>,
365    ) -> NativeExpand<bool> {
366        let modulo = rem::expand(scope, self, factor);
367        eq::expand(scope, modulo, E::from_int(0).into())
368    }
369}
370
371#[ruda]
372pub fn div_ceil<E: Int>(a: E, b: E) -> E {
373    (a + b - E::new(1)) / b
374}