1use alloc::vec;
2use core::f32::consts::PI;
3
4use cubecl_ir::{Type, cube_op, interfaces::TypedExt, prelude::*};
5use num_traits::One;
6
7use crate::prelude::*;
8use crate::{self as cubecl, unexpanded};
9
10define_scalar!(ElemA);
11define_size!(SizeA);
12
13pub fn set_polyfill<E: Scalar, N: Size>(_elem: Type) {
19 unexpanded!()
20}
21
22pub mod set_polyfill {
24 use super::*;
25
26 pub fn expand<E: Scalar, N: Size>(scope: &Scope, ty: Type) {
28 scope.register_type::<E>(ty.elem_type());
29 scope.register_size::<N>(ty.vector_size());
30 }
31}
32
33pub fn expand_signed_mod_floor(scope: &Scope, lhs: Value, rhs: Value) -> Value {
36 define_scalar!(I);
37 define_size!(N);
38 scope.register_value_type::<I, N>(lhs);
39 signed_mod_floor::expand::<I, N>(scope, lhs.into(), rhs.into()).read_value(scope)
40}
41
42#[cube]
43fn signed_mod_floor<I: Int, N: Size>(lhs: Vector<I, N>, rhs: Vector<I, N>) -> Vector<I, N> {
44 let remainder = lhs % rhs;
45 let zero = Vector::new(I::from_int(0));
46 let different_signs = (remainder ^ rhs).less_than(&zero);
47 let adjust = remainder.not_equal(&zero).vec_and(different_signs);
48 remainder + select_many(adjust, rhs, zero)
49}
50
51pub fn expand_clamp_nan(scope: &Scope, input: Value, min: Value, max: Value) -> Value {
53 use cubecl_ir::dialect::cmp::{FMaxNanOp, FMinNanOp};
54 let upper = FMinNanOp::new(scope.ctx_mut(), input, max);
55 let upper = scope.register_with_result(&upper);
56 let lower = FMaxNanOp::new(scope.ctx_mut(), upper, min);
57 scope.register_with_result(&lower)
58}
59
60pub fn expand_min_nan(scope: &Scope, lhs: Value, rhs: Value) -> Value {
62 define_scalar!(F);
63 define_size!(N);
64 scope.register_value_type::<F, N>(lhs);
65 min_nan_fallback::expand::<F, N>(scope, lhs.into(), rhs.into()).read_value(scope)
66}
67
68#[cube]
69fn min_nan_fallback<F: Float, N: Size>(lhs: Vector<F, N>, rhs: Vector<F, N>) -> Vector<F, N> {
70 let value = lhs.min(rhs);
71 let value = select_many(rhs.is_nan(), rhs, value);
72 select_many(lhs.is_nan(), lhs, value)
73}
74
75pub fn expand_max_nan(scope: &Scope, lhs: Value, rhs: Value) -> Value {
77 define_scalar!(F);
78 define_size!(N);
79 scope.register_value_type::<F, N>(lhs);
80 max_nan_fallback::expand::<F, N>(scope, lhs.into(), rhs.into()).read_value(scope)
81}
82
83#[cube]
84fn max_nan_fallback<F: Float, N: Size>(lhs: Vector<F, N>, rhs: Vector<F, N>) -> Vector<F, N> {
85 let value = lhs.max(rhs);
86 let value = select_many(rhs.is_nan(), rhs, value);
87 select_many(lhs.is_nan(), lhs, value)
88}
89
90#[cube]
91pub fn erf<F: Float, N: Size>(x: Vector<F, N>) -> Vector<F, N> {
92 let erf = erf_positive(x.abs());
93 select_many(x.less_than(&Vector::new(F::new(0f32))), -erf, erf)
94}
95
96#[cube]
101fn erf_positive<F: Float, N: Size>(x: Vector<F, N>) -> Vector<F, N> {
102 let p = Vector::new(F::new(0.3275911_f32));
103 let a1 = Vector::new(F::new(0.2548296_f32));
104 let a2 = Vector::new(F::new(-0.28449674_f32));
105 let a3 = Vector::new(F::new(1.4214137_f32));
106 let a4 = Vector::new(F::new(-1.453152_f32));
107 let a5 = Vector::new(F::new(1.0614054_f32));
108 let one = Vector::new(F::new(1.0_f32));
109
110 let t = one / (one + p * x);
111 let tmp = ((((a5 * t + a4) * t) + a3) * t + a2) * t + a1;
112
113 one - (tmp * t * (-x * x).exp())
114}
115
116#[cube]
117fn himul_i64<I: Int, N: Size>(lhs: Vector<I, N>, rhs: Vector<I, N>) -> Vector<I, N> {
118 let shift = Vector::new(32);
119 let mul = (Vector::<i64, N>::cast_from(lhs) * Vector::<i64, N>::cast_from(rhs)) >> shift;
120 Vector::cast_from(mul)
121}
122
123#[cube]
124fn himul_u64<I: Int, N: Size>(lhs: Vector<I, N>, rhs: Vector<I, N>) -> Vector<I, N> {
125 let shift = Vector::new(32);
126 let mul = (Vector::<u64, N>::cast_from(lhs) * Vector::<u64, N>::cast_from(rhs)) >> shift;
127 Vector::cast_from(mul)
128}
129
130#[allow(missing_docs)]
131pub fn expand_s_himul_64(scope: &Scope, lhs: Value, rhs: Value) -> Value {
132 scope.register_value_type::<ElemA, SizeA>(lhs);
133 himul_i64::expand::<ElemA, SizeA>(scope, lhs.into(), rhs.into()).value(scope)
134}
135
136#[allow(missing_docs)]
137pub fn expand_u_himul_64(scope: &Scope, lhs: Value, rhs: Value) -> Value {
138 scope.register_value_type::<ElemA, SizeA>(lhs);
139 himul_u64::expand::<ElemA, SizeA>(scope, lhs.into(), rhs.into()).value(scope)
140}
141
142#[cube]
143fn himul_sim<T: Int, N: Size>(lhs: Vector<T, N>, rhs: Vector<T, N>) -> Vector<T, N> {
144 let half_bits = T::size_bits().comptime() / 2;
145 let low_mask = Vector::new(T::new(comptime!((1i64 << half_bits) - 1)));
146 let shift = Vector::new(T::new(half_bits as i64));
147
148 let lhs_low = lhs & low_mask;
149 let lhs_hi = (lhs >> shift) & low_mask;
150 let rhs_low = rhs & low_mask;
151 let rhs_hi = (rhs >> shift) & low_mask;
152
153 let low_low = lhs_low * rhs_low;
154 let high_low = lhs_hi * rhs_low;
155 let low_high = lhs_low * rhs_hi;
156 let high_high = lhs_hi * rhs_hi;
157
158 let mid = ((low_low >> shift) & low_mask) + (high_low & low_mask) + (low_high & low_mask);
159 high_high
160 + ((high_low >> shift) & low_mask)
161 + ((low_high >> shift) & low_mask)
162 + ((mid >> shift) & low_mask)
163}
164
165#[allow(missing_docs)]
166pub fn expand_himul_sim(scope: &Scope, lhs: Value, rhs: Value) -> Value {
167 scope.register_value_type::<ElemA, SizeA>(lhs);
168 himul_sim::expand::<ElemA, SizeA>(scope, lhs.into(), rhs.into()).value(scope)
169}
170
171#[cube]
173pub fn dp4a_polyfill<N: Size>(
174 a: Vector<i32, N>,
175 b: Vector<i32, N>,
176 c: Vector<i32, N>,
177) -> Vector<i32, N> {
178 let a = Vector::<u32, N>::reinterpret(a);
179 let b = Vector::<u32, N>::reinterpret(b);
180 let shift_8 = Vector::new(8);
181 let shift_16 = Vector::new(16);
182 let shift_24 = Vector::new(24);
183 let byte_mask = Vector::new(0xff);
184 let sign_mask = Vector::new(0x80);
185 let sign_offset = Vector::<i32, N>::new(0x80);
186
187 let a0 = Vector::<i32, N>::cast_from((a & byte_mask) ^ sign_mask) - sign_offset;
188 let a1 = Vector::<i32, N>::cast_from(((a >> shift_8) & byte_mask) ^ sign_mask) - sign_offset;
189 let a2 = Vector::<i32, N>::cast_from(((a >> shift_16) & byte_mask) ^ sign_mask) - sign_offset;
190 let a3 = Vector::<i32, N>::cast_from((a >> shift_24) ^ sign_mask) - sign_offset;
191
192 let b0 = Vector::<i32, N>::cast_from((b & byte_mask) ^ sign_mask) - sign_offset;
193 let b1 = Vector::<i32, N>::cast_from(((b >> shift_8) & byte_mask) ^ sign_mask) - sign_offset;
194 let b2 = Vector::<i32, N>::cast_from(((b >> shift_16) & byte_mask) ^ sign_mask) - sign_offset;
195 let b3 = Vector::<i32, N>::cast_from((b >> shift_24) ^ sign_mask) - sign_offset;
196
197 c + a0 * b0 + a1 * b1 + a2 * b2 + a3 * b3
198}
199
200#[allow(missing_docs)]
201pub fn expand_dp4a_polyfill(scope: &Scope, a: Value, b: Value, c: Value) -> Value {
202 scope.register_size::<SizeA>(a.vector_size(scope.ctx()));
203 dp4a_polyfill::expand::<SizeA>(scope, a.into(), b.into(), c.into()).value(scope)
204}
205
206#[cube]
207pub fn log1p<T: Float, N: Size>(input: Vector<T, N>) -> Vector<T, N> {
208 (input + Vector::one()).ln()
209}
210
211#[cube]
212pub fn expm1<T: Float, N: Size>(x: Vector<T, N>) -> Vector<T, N> {
213 let sq = x * x;
214 let a = sq * Vector::new(T::new(0.5_f32));
215 let b = sq * x * Vector::new(T::new(1.0_f32 / 6.0_f32));
216 let taylor = x + a + b;
217 let is_small = x.abs().less_than(&Vector::new(T::new(1e-5_f32)));
218 select_many(is_small, taylor, x.exp() - Vector::one())
219}
220
221#[cube_op(name = "polyfill.simple_pow")]
224#[result_ty(same_as = base)]
225pub struct SimplePowOp {
226 pub base: Value,
227 pub exp: Value,
228}
229
230#[cube]
232fn simple_pow<T: Float, N: Size>(base: Vector<T, N>, exp: Vector<T, N>) -> Vector<T, N> {
233 intrinsic!(|scope| {
234 let base = base.read_value(scope);
235 let exp = exp.read_value(scope);
236 let powf = SimplePowOp::new(scope.ctx_mut(), base, exp);
237 scope.register_with_result(&powf).into()
238 })
239}
240
241#[cube]
242pub fn powf<T: Float, N: Size>(base: Vector<T, N>, exp: Vector<T, N>) -> Vector<T, N> {
243 let modulo = exp.mod_floor(Vector::new(T::new(2.0_f32)));
244 let is_even = modulo.equal(&Vector::zero());
245 let is_odd = modulo.equal(&Vector::one());
246 let is_neg_base = base.less_than(&Vector::zero());
247
248 let even_res = simple_pow(base.abs(), exp);
249 let odd_neg_res = -(simple_pow(-base, exp));
250 let default = simple_pow(base, exp);
251
252 let sel1 = select_many(is_odd.vec_and(is_neg_base), odd_neg_res, default);
253 select_many(is_even, even_res, sel1)
254}
255
256#[cube]
257pub fn powi<T: Float, N: Size>(base: Vector<T, N>, exp: Vector<i32, N>) -> Vector<T, N> {
258 let is_even = exp.is_multiple_of(2);
259 let is_neg_base = base.less_than(&Vector::zero());
260 let exp = Vector::cast_from(exp);
261
262 let even_res = simple_pow(base.abs(), exp);
263 let odd_neg_res = -(simple_pow(-base, exp));
264 let default = simple_pow(base, exp);
265
266 let sel1 = select_many((!is_even).vec_and(is_neg_base), odd_neg_res, default);
267 select_many(is_even, even_res, sel1)
268}
269
270#[cube]
272pub fn powi_int<T: Int, N: Size>(base: Vector<T, N>, exp: Vector<i32, N>) -> Vector<T, N> {
273 if T::size_bits().comptime() <= 32 {
277 Vector::cast_from(powi_int_unsigned::<u32, N>(Vector::cast_from(base), exp))
278 } else {
279 Vector::cast_from(powi_int_unsigned::<u64, N>(Vector::cast_from(base), exp))
280 }
281}
282
283#[cube]
285fn powi_int_unsigned<T: Int, N: Size>(base: Vector<T, N>, exp: Vector<i32, N>) -> Vector<T, N> {
286 let one_u = Vector::<u32, N>::new(1);
287 let one_t = Vector::<T, N>::new(T::from_int(1));
288
289 let mut exp = Vector::<u32, N>::cast_from(exp);
291 let mut result = one_t;
292 let mut factor = base;
293
294 #[unroll]
295 for _ in 0..32 {
296 result *= select_many((exp & one_u).equal(&one_u), factor, one_t);
298 factor *= factor;
299 exp >>= one_u;
300 }
301
302 result
303}
304
305#[cube]
306pub fn recip<T: Float, N: Size>(input: Vector<T, N>) -> Vector<T, N> {
307 Vector::one() / input
308}
309
310#[cube]
311pub fn to_degrees<T: Float, N: Size>(input: Vector<T, N>) -> Vector<T, N> {
312 input * Vector::new(T::new(comptime!(180.0_f32 / PI)))
313}
314
315#[cube]
316pub fn to_radians<T: Float, N: Size>(input: Vector<T, N>) -> Vector<T, N> {
317 input * Vector::new(T::new(comptime!(PI / 180.0_f32)))
318}
319
320pub mod bitwise {
321 use super::*;
322
323 #[cube]
324 pub fn u64_leading_zeros<I: Int, N: Size>(x: Vector<I, N>) -> Vector<u32, N> {
325 let shift = Vector::new(I::new(32));
326
327 let low = Vector::<u32, N>::cast_from(x);
328 let high = Vector::<u32, N>::cast_from(x >> shift);
329 let low_zeros = Vector::leading_zeros(low);
330 let high_zeros = Vector::leading_zeros(high);
331
332 select_many(
333 high_zeros.equal(&Vector::new(32)),
334 low_zeros + high_zeros,
335 high_zeros,
336 )
337 }
338
339 #[cube]
340 pub fn u64_trailing_zeros<I: Int, N: Size>(x: Vector<I, N>) -> Vector<u32, N> {
341 let shift = Vector::new(I::new(32));
342
343 let low = Vector::<u32, N>::cast_from(x);
344 let high = Vector::<u32, N>::cast_from(x >> shift);
345 let low_tz = Vector::trailing_zeros(low);
346 let high_tz = Vector::trailing_zeros(high);
347
348 let high_tz = select_many(
349 high_tz.equal(&Vector::new(32)),
350 Vector::new(64),
351 high_tz + Vector::new(32),
352 );
353 select_many(low_tz.equal(&Vector::new(32)), high_tz, low_tz)
354 }
355
356 #[cube]
357 pub fn u64_ffs<I: Int, N: Size>(x: Vector<I, N>) -> Vector<u32, N> {
358 let shift = Vector::new(I::new(32));
359
360 let low = Vector::<u32, N>::cast_from(x);
361 let high = Vector::<u32, N>::cast_from(x >> shift);
362 let low_ffs = Vector::find_first_set(low);
363 let high_ffs = Vector::find_first_set(high);
364
365 let high_ffs = select_many(
366 high_ffs.equal(&Vector::new(0)),
367 high_ffs,
368 high_ffs + Vector::new(32),
369 );
370 select_many(low_ffs.equal(&Vector::new(0)), high_ffs, low_ffs)
371 }
372}
373
374pub mod plane {
379 use super::*;
380 use crate::prelude::{
381 CUBE_DIM, CubeAdd, CubeMul, CubePartialOrd, PLANE_DIM, UNIT_POS_PLANE, max, min,
382 plane_shuffle_up, plane_shuffle_xor, select,
383 };
384
385 #[cube]
386 pub trait PlaneOp<T: Scalar, N: Size> {
387 fn apply(lhs: Vector<T, N>, rhs: Vector<T, N>) -> Vector<T, N>;
388 }
389
390 pub struct OpAdd;
391 pub struct OpMul;
392 pub struct OpMin;
393 pub struct OpMax;
394
395 #[cube]
396 impl<T: Scalar + CubeAdd, N: Size> PlaneOp<T, N> for OpAdd {
397 fn apply(lhs: Vector<T, N>, rhs: Vector<T, N>) -> Vector<T, N> {
398 lhs + rhs
399 }
400 }
401 #[cube]
402 impl<T: Scalar + CubeMul, N: Size> PlaneOp<T, N> for OpMul {
403 fn apply(lhs: Vector<T, N>, rhs: Vector<T, N>) -> Vector<T, N> {
404 lhs * rhs
405 }
406 }
407 #[cube]
408 impl<T: Scalar + CubePartialOrd, N: Size> PlaneOp<T, N> for OpMin {
409 fn apply(lhs: Vector<T, N>, rhs: Vector<T, N>) -> Vector<T, N> {
410 min(lhs, rhs)
411 }
412 }
413 #[cube]
414 impl<T: Scalar + CubePartialOrd, N: Size> PlaneOp<T, N> for OpMax {
415 fn apply(lhs: Vector<T, N>, rhs: Vector<T, N>) -> Vector<T, N> {
416 max(lhs, rhs)
417 }
418 }
419
420 #[cube]
421 fn plane_dim_checked() -> u32 {
422 min(PLANE_DIM, CUBE_DIM)
423 }
424
425 #[cube]
426 pub fn plane_reduce<T: Scalar, N: Size, Op: PlaneOp<T, N>>(val: Vector<T, N>) -> Vector<T, N> {
427 let plane_dim = plane_dim_checked();
428 let mut acc = val;
429 let mut offset = 1;
430 while offset < plane_dim {
431 acc = Op::apply(acc, plane_shuffle_xor(acc, offset));
432 offset *= 2;
433 }
434 acc
435 }
436
437 #[cube]
438 pub fn plane_reduce_inclusive<T: Scalar, N: Size, Op: PlaneOp<T, N>>(
439 val: Vector<T, N>,
440 ) -> Vector<T, N> {
441 let plane_dim = plane_dim_checked();
442 let mut acc = val;
443 let mut offset = 1;
444 while offset < plane_dim {
445 let tmp = Op::apply(acc, plane_shuffle_up(acc, offset));
446 if UNIT_POS_PLANE >= offset {
447 acc = tmp;
448 }
449 offset *= 2;
450 }
451 acc
452 }
453
454 #[cube]
455 pub fn plane_reduce_exclusive<T: Numeric, N: Size, Op: PlaneOp<T, N>>(
456 val: Vector<T, N>,
457 #[comptime] default: i64,
458 ) -> Vector<T, N> {
459 let inclusive = plane_reduce_inclusive::<T, N, Op>(val);
460 let shfl = plane_shuffle_up(inclusive, 1);
461 select(UNIT_POS_PLANE == 0, Vector::new(T::from_int(default)), shfl)
462 }
463}
464
465pub mod mma {
477 use super::*;
478 use crate::ir::types::MatrixIdent;
479
480 #[cube]
482 pub fn row_index(
483 lane_id: u32,
484 i: u32,
485 #[comptime] elems_per_reg: usize,
486 #[comptime] ident: MatrixIdent,
487 ) -> u32 {
488 let elems_per_reg = elems_per_reg as u32;
489 match ident {
490 MatrixIdent::A => {
491 let group_id = lane_id / 4;
492 let odd_register = (i / elems_per_reg) & 1;
493 group_id + odd_register * 8
494 }
495 MatrixIdent::B => {
496 let thread_id_in_group = lane_id % 4;
497 let offset = thread_id_in_group * elems_per_reg + (i % elems_per_reg);
498 let reg = i / elems_per_reg;
499 offset + elems_per_reg * 4 * reg
500 }
501 MatrixIdent::Accumulator => {
502 let group_id = lane_id / 4;
503 let offset = (i << 2) & 8;
504 group_id + offset
505 }
506 }
507 }
508
509 #[cube]
511 pub fn col_index(
512 lane_id: u32,
513 i: u32,
514 #[comptime] elems_per_reg: usize,
515 #[comptime] ident: MatrixIdent,
516 ) -> u32 {
517 let elems_per_reg = elems_per_reg as u32;
518 match ident {
519 MatrixIdent::A => {
520 let thread_id_in_group = lane_id % 4;
521 let offset = thread_id_in_group * elems_per_reg + (i % elems_per_reg);
522 let group_2 = (i / (2 * elems_per_reg)) & 1;
523 offset + 4 * elems_per_reg * group_2
524 }
525 MatrixIdent::B => lane_id >> 2,
526 MatrixIdent::Accumulator => {
527 let thread_id_in_group = lane_id % 4;
528 (thread_id_in_group * 2) + (i % 2)
529 }
530 }
531 }
532}