Skip to main content

cubecl_core/frontend/operation/
base.rs

1use cubecl_ir::{
2    Arithmetic, BinaryOperands, Comparison, ElemType, IndexOperands, Instruction, Memory,
3    Operation, Scope, Type, UnaryOperands, Value, VectorSize,
4};
5use cubecl_macros::cube;
6
7use crate::{self as cubecl, prelude::*};
8
9pub(crate) fn read_value(scope: &Scope, val: Value) -> Value {
10    if let Type::Pointer(inner, _) = val.ty {
11        let out = scope.create_value(*inner);
12        scope.register(Instruction::new(Memory::Load(val), out));
13        out
14    } else {
15        val
16    }
17}
18
19pub(crate) fn binary_expand<F, Op>(scope: &Scope, lhs: Value, rhs: Value, func: F) -> Value
20where
21    F: Fn(BinaryOperands) -> Op,
22    Op: Into<Operation>,
23{
24    let item_lhs = lhs.value_type();
25    let item_rhs = rhs.value_type();
26
27    let vector_size = find_vectorization(item_lhs, item_rhs);
28
29    let item = item_lhs.with_vector_size(vector_size);
30
31    let output = scope.create_value(item);
32
33    let op = func(BinaryOperands { lhs, rhs });
34
35    scope.register(Instruction::new(op, output));
36
37    output
38}
39
40pub(crate) fn index_expand(scope: &Scope, list: Value, index: Value, checked: bool) -> Value {
41    let ty = list.value_type();
42
43    let class = list.address_space();
44    let output = scope.create_value(Type::pointer(ty, class));
45
46    let op = Memory::Index(IndexOperands {
47        list,
48        index,
49        unroll_factor: 1,
50        checked,
51    });
52
53    scope.register(Instruction::new(op, output));
54
55    output
56}
57
58pub(crate) fn binary_expand_fixed_output<F>(
59    scope: &Scope,
60    lhs: Value,
61    rhs: Value,
62    out_item: Type,
63    func: F,
64) -> Value
65where
66    F: Fn(BinaryOperands) -> Arithmetic,
67{
68    let out = scope.create_value(out_item);
69    let op = func(BinaryOperands { lhs, rhs });
70
71    scope.register(Instruction::new(op, out));
72
73    out
74}
75
76pub(crate) fn cmp_expand<F>(scope: &Scope, lhs: Value, rhs: Value, func: F) -> Value
77where
78    F: Fn(BinaryOperands) -> Comparison,
79{
80    let item_lhs = lhs.value_type();
81    let item_rhs = rhs.value_type();
82
83    let vector_size = find_vectorization(item_lhs, item_rhs);
84
85    let out_item = Type::scalar(ElemType::Bool).with_vector_size(vector_size);
86
87    let out = scope.create_value(out_item);
88
89    let op = func(BinaryOperands { lhs, rhs });
90
91    scope.register(Instruction::new(op, out));
92
93    out
94}
95
96pub(crate) fn assign_op_expand<T: CubeType, Op>(
97    scope: &Scope,
98    lhs: &mut NativeExpand<T>,
99    rhs: NativeExpand<T>,
100    func: impl Fn(BinaryOperands) -> Op,
101) where
102    Op: Into<Operation>,
103    NativeExpand<T>: DerefExpand<Target = NativeExpand<T>>,
104{
105    let lhs_value = lhs.__expand_deref_method(scope).expand;
106    let lhs = lhs.expand;
107    let rhs = rhs.expand;
108
109    if lhs.is_immutable() {
110        panic!("Can't have a mutable operation on a const variable. Try to use `RuntimeCell`.");
111    }
112
113    let tmp = scope.create_value(lhs.value_type());
114    let op = func(BinaryOperands {
115        lhs: lhs_value,
116        rhs,
117    });
118
119    scope.register(Instruction::new(op, tmp));
120    assign::expand_element(scope, tmp, lhs);
121}
122
123pub fn unary_expand<F, Op>(scope: &Scope, input: Value, func: F) -> Value
124where
125    F: Fn(UnaryOperands) -> Op,
126    Op: Into<Operation>,
127{
128    let item = input.value_type();
129
130    let out = scope.create_value(item);
131
132    let op = func(UnaryOperands { input });
133
134    scope.register(Instruction::new(op, out));
135
136    out
137}
138
139pub fn unary_expand_fixed_output<F, Op>(
140    scope: &Scope,
141    input: Value,
142    out_item: Type,
143    func: F,
144) -> Value
145where
146    F: Fn(UnaryOperands) -> Op,
147    Op: Into<Operation>,
148{
149    let output = scope.create_value(out_item);
150
151    let op = func(UnaryOperands { input });
152
153    scope.register(Instruction::new(op, output));
154
155    output
156}
157
158pub fn init_expand(scope: &Scope, input: Value, mutable: bool) -> Value {
159    let input = read_value(scope, input);
160    let ty = input.ty;
161
162    let out = if mutable {
163        scope.create_local_mut(ty)
164    } else {
165        scope.create_value(ty)
166    };
167
168    assign::expand_element(scope, input, out);
169
170    out
171}
172
173pub(crate) fn find_vectorization(lhs: Type, rhs: Type) -> VectorSize {
174    if matches!(lhs, Type::Scalar(_)) && matches!(rhs, Type::Scalar(_)) {
175        0
176    } else {
177        lhs.vector_size().max(rhs.vector_size())
178    }
179}
180
181pub fn assign_binary_op_expand<
182    A: CubeType,
183    V: CubeType,
184    F: Fn(BinaryOperands) -> Op,
185    Op: Into<Operation>,
186>(
187    scope: &Scope,
188    lhs: &mut NativeExpand<A>,
189    rhs: NativeExpand<V>,
190    func: F,
191) where
192    NativeExpand<A>: DerefExpand<Target = NativeExpand<A>> + Assign,
193{
194    let lhs_value = lhs.__expand_deref_method(scope).expand;
195    let rhs: Value = rhs.into();
196    let out = scope.create_value(lhs.expand.ty);
197
198    scope.register(Instruction::new(
199        func(BinaryOperands {
200            lhs: lhs_value,
201            rhs,
202        }),
203        out,
204    ));
205    lhs.__expand_assign_method(scope, out.into());
206}
207
208pub trait DivCeil: Int + CubeType<ExpandType: DivCeilExpand<Self>> {
209    fn div_ceil(self, divisor: Self) -> Self;
210
211    fn __expand_div_ceil(
212        scope: &Scope,
213        a: NativeExpand<Self>,
214        b: NativeExpand<Self>,
215    ) -> NativeExpand<Self> {
216        a.__expand_div_ceil_method(scope, b)
217    }
218}
219
220pub trait DivCeilExpand<E: Int> {
221    fn __expand_div_ceil_method(self, scope: &Scope, divisor: Self) -> Self;
222}
223
224impl<E: DivCeil> DivCeilExpand<E> for NativeExpand<E> {
225    fn __expand_div_ceil_method(self, scope: &Scope, divisor: NativeExpand<E>) -> NativeExpand<E> {
226        div_ceil::expand::<E>(scope, self, divisor)
227    }
228}
229
230macro_rules! impl_div_ceil {
231    ($($ty:ty),*) => {
232        $(
233            impl DivCeil for $ty {
234                #[allow(clippy::manual_div_ceil)] // Need to define div_ceil to use div_ceil!
235                fn div_ceil(self, divisor: Self) -> Self {
236                    (self + divisor - 1) / divisor
237                }
238            }
239        )*
240    };
241}
242
243impl_div_ceil!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
244
245impl<E: Int> NativeExpand<E> {
246    pub fn __expand_is_multiple_of_method(
247        self,
248        scope: &Scope,
249        factor: NativeExpand<E>,
250    ) -> NativeExpand<bool> {
251        let modulo = self.__expand_rem_method(scope, factor);
252        modulo.__expand_eq_method(scope, &E::from_int(0).into())
253    }
254}
255
256#[cube]
257pub fn div_ceil<E: Int>(a: E, b: E) -> E {
258    (a + b - E::new(1)) / b
259}