Skip to main content

cubecl_core/frontend/element/
numeric.rs

1use cubecl_ir::{ConstantValue, Instruction, Operator, Value};
2use cubecl_runtime::runtime::Runtime;
3use num_traits::{NumCast, One, Zero};
4
5use crate::compute::KernelLauncher;
6use crate::{IntoRuntime, ScalarArgType, compute::KernelBuilder};
7use crate::{
8    frontend::{Abs, ModFloor, VectorSum},
9    unexpanded,
10};
11use crate::{
12    frontend::{CubePrimitive, CubeType},
13    prelude::InputScalar,
14};
15use crate::{ir::Scope, prelude::Scalar};
16
17use super::{LaunchArg, NativeAssign, NativeExpand};
18
19/// Type that encompasses both (unsigned or signed) integers and floats
20/// Used in kernels that should work for both.
21pub trait Numeric:
22    Copy
23    + Abs
24    + VectorSum
25    + ModFloor
26    + Scalar
27    + NativeAssign
28    + Into<NativeExpand<Self>>
29    + Into<ConstantValue>
30    + num_traits::NumCast
31    + num_traits::NumAssign
32    + core::cmp::PartialOrd
33    + core::cmp::PartialEq
34    + core::fmt::Debug
35    + bytemuck::Zeroable
36{
37    fn min_value() -> Self;
38    fn max_value() -> Self;
39
40    fn __expand_min_value(scope: &Scope) -> <Self as CubeType>::ExpandType {
41        let elem = Self::__expand_as_type(scope).elem_type();
42        let val = elem.min_variable();
43        val.into()
44    }
45
46    fn __expand_max_value(scope: &Scope) -> <Self as CubeType>::ExpandType {
47        let elem = Self::__expand_as_type(scope).elem_type();
48        let val = elem.max_variable();
49        val.into()
50    }
51
52    /// Create a new constant numeric.
53    ///
54    /// Note: since this must work for both integer and float
55    /// only the less expressive of both can be created (int)
56    /// If a number with decimals is needed, use `Float::new`.
57    ///
58    /// This method panics when unexpanded. For creating an element
59    /// with a val, use the new method of the sub type.
60    fn from_int(val: i64) -> Self {
61        <Self as NumCast>::from(val).unwrap()
62    }
63
64    /// Create a new constant numeric. Uses `i128` to be able to represent both signed integers, and
65    /// `u64::MAX`.
66    ///
67    /// Note: since this must work for both integer and float
68    /// only the less expressive of both can be created (int)
69    /// If a number with decimals is needed, use `Float::new`.
70    ///
71    /// This method panics when unexpanded. For creating an element
72    /// with a val, use the new method of the sub type.
73    fn from_int_128(val: i128) -> Self {
74        <Self as NumCast>::from(val).unwrap()
75    }
76
77    fn from_vec<const D: usize>(_vec: [u32; D]) -> Self {
78        unexpanded!()
79    }
80
81    fn __expand_from_int(scope: &Scope, val: NativeExpand<i64>) -> <Self as CubeType>::ExpandType {
82        let elem = Self::__expand_as_type(scope).elem_type();
83        let val: Value = elem.constant(val.constant().unwrap());
84
85        val.into()
86    }
87}
88
89/// Similar to [`ArgSettings`], however only for scalar types that don't depend on the [Runtime]
90/// trait.
91pub trait ScalarArgSettings: Send + Sync + CubePrimitive {
92    /// Register the information to the [`KernelLauncher`].
93    fn register<R: Runtime>(&self, launcher: &mut KernelLauncher<R>);
94    fn expand_scalar(builder: &mut KernelBuilder) -> NativeExpand<Self> {
95        let ty = Self::__expand_as_type(&builder.scope);
96        let out = builder.create_value(ty);
97        let id = builder.scalar(ty.storage_type());
98        builder.register(Instruction::new(Operator::ReadScalar(id), out));
99        out.into()
100    }
101}
102
103impl<E: ScalarArgType> ScalarArgSettings for E {
104    fn register<R: Runtime>(&self, launcher: &mut KernelLauncher<R>) {
105        launcher.register_scalar(*self);
106    }
107}
108
109impl ScalarArgSettings for usize {
110    fn register<R: Runtime>(&self, launcher: &mut KernelLauncher<R>) {
111        let value = InputScalar::new(*self, launcher.settings.address_type.unsigned_type());
112        InputScalar::register(value, launcher);
113    }
114}
115
116impl ScalarArgSettings for isize {
117    fn register<R: Runtime>(&self, launcher: &mut KernelLauncher<R>) {
118        let value = InputScalar::new(*self, launcher.settings.address_type.signed_type());
119        InputScalar::register(value, launcher);
120    }
121}
122
123macro_rules! impl_scalar_launch {
124    ($ty: ty) => {
125        impl LaunchArg for $ty {
126            type RuntimeArg<R: Runtime> = $ty;
127            type CompilationArg = ();
128
129            fn register<R: Runtime>(arg: Self::RuntimeArg<R>, launcher: &mut KernelLauncher<R>) {
130                arg.register(launcher);
131            }
132
133            fn expand(_: &(), builder: &mut KernelBuilder) -> NativeExpand<Self> {
134                <$ty>::expand_scalar(builder)
135            }
136        }
137    };
138}
139pub(crate) use impl_scalar_launch;
140
141pub trait ZeroExpand: CubeType + Zero {
142    fn __expand_zero(scope: &Scope) -> Self::ExpandType;
143}
144
145pub trait OneExpand: CubeType + One {
146    fn __expand_one(scope: &Scope) -> Self::ExpandType;
147}
148
149impl<T: CubeType + Zero + IntoRuntime> ZeroExpand for T {
150    fn __expand_zero(scope: &Scope) -> Self::ExpandType {
151        T::zero().__expand_runtime_method(scope)
152    }
153}
154
155impl<T: CubeType + One + IntoRuntime> OneExpand for T {
156    fn __expand_one(scope: &Scope) -> Self::ExpandType {
157        T::one().__expand_runtime_method(scope)
158    }
159}