1use core::ops::Neg;
2
3use cubecl_macros_internal::{const_eval, cube_op, simplify};
4use half::{bf16, f16};
5use num_integer::Integer;
6use num_traits::Float;
7use pliron::{
8 attribute::AttrObj,
9 builtin::{attributes::IntegerAttr, types::IntegerType},
10 utils::apint::{APInt, bw},
11};
12
13use crate::{
14 CanMaterialize, ConstantValue, NoMemoryEffect, NoSideEffects, PropagatesUniformity, Pure,
15 attributes::{BoolAttr, FloatAttr, IndexAttr, IntAttrExt},
16 dialect::{pure_binop, pure_unop},
17 interfaces::{TriviallyUnrollable, TypedExt},
18 prelude::*,
19 types::{VectorType, scalar::BoolType},
20};
21
22pure_unop!("math.s_abs", SAbsOp);
23const_eval!(SAbsOp, {
24 [IntegerAttr(i8, i16, i32, i64)]: |inp| inp.abs(),
25});
26
27pure_unop!("math.f_abs", FAbsOp);
28const_eval!(FAbsOp, {
29 [FloatAttr(f16, bf16, f32, f64)]: |inp| inp.abs(),
30});
31
32#[cube_op(name = "math.c_abs")]
33#[result_ty(argument)]
34#[op_interfaces(TriviallyUnrollable)]
35#[op_traits(Pure, CanMaterialize)]
36pub struct CAbsOp {
37 pub input: Value,
38}
39
40pure_unop!("math.c_conj", CConjOp);
41
42#[cube_op(name = "math.c_real")]
43#[result_ty(argument)]
44#[op_interfaces(TriviallyUnrollable)]
45#[op_traits(Pure, CanMaterialize)]
46pub struct CRealOp {
47 pub input: Value,
48}
49
50#[cube_op(name = "math.c_imag")]
51#[result_ty(argument)]
52#[op_interfaces(TriviallyUnrollable)]
53#[op_traits(Pure, CanMaterialize)]
54pub struct CImagOp {
55 pub input: Value,
56}
57
58pure_unop!("math.exp", ExpOp);
59const_eval!(ExpOp, {
60 FloatAttr(f16, bf16, f32, f64): |inp| inp.exp(),
61});
62
63pure_unop!("math.log", LogOp);
64const_eval!(LogOp, {
65 FloatAttr(f16, bf16, f32, f64): |inp| inp.ln(),
66});
67
68pure_unop!("math.log1p", Log1pOp);
69const_eval!(Log1pOp, {
70 FloatAttr(f16, bf16, f32, f64): |inp| inp.ln_1p(),
71});
72
73pure_unop!("math.expm1", Expm1Op);
74const_eval!(Expm1Op, {
75 FloatAttr(f16, bf16, f32, f64): |inp| inp.exp_m1(),
76});
77
78pure_unop!("math.sin", SinOp);
79const_eval!(SinOp, {
80 FloatAttr(f16, bf16, f32, f64): |inp| inp.sin(),
81});
82
83pure_unop!("math.cos", CosOp);
84const_eval!(CosOp, {
85 FloatAttr(f16, bf16, f32, f64): |inp| inp.cos(),
86});
87
88pure_unop!("math.tan", TanOp);
89const_eval!(TanOp, {
90 FloatAttr(f16, bf16, f32, f64): |inp| inp.tan(),
91});
92
93pure_unop!("math.sinh", SinhOp);
94const_eval!(SinhOp, {
95 FloatAttr(f16, bf16, f32, f64): |inp| inp.sinh(),
96});
97
98pure_unop!("math.cosh", CoshOp);
99const_eval!(CoshOp, {
100 FloatAttr(f16, bf16, f32, f64): |inp| inp.cosh(),
101});
102
103pure_unop!("math.tanh", TanhOp);
104const_eval!(TanhOp, {
105 FloatAttr(f16, bf16, f32, f64): |inp| inp.tanh(),
106});
107
108pure_unop!("math.arcsin", ArcSinOp);
109const_eval!(ArcSinOp, {
110 FloatAttr(f16, bf16, f32, f64): |inp| inp.asin(),
111});
112
113pure_unop!("math.arccos", ArcCosOp);
114const_eval!(ArcCosOp, {
115 FloatAttr(f16, bf16, f32, f64): |inp| inp.acos(),
116});
117
118pure_unop!("math.arctan", ArcTanOp);
119const_eval!(ArcTanOp, {
120 FloatAttr(f16, bf16, f32, f64): |inp| inp.atan(),
121});
122
123pure_unop!("math.arcsinh", ArcSinhOp);
124const_eval!(ArcSinhOp, {
125 FloatAttr(f16, bf16, f32, f64): |inp| inp.asinh(),
126});
127
128pure_unop!("math.arccosh", ArcCoshOp);
129const_eval!(ArcCoshOp, {
130 FloatAttr(f16, bf16, f32, f64): |inp| inp.acosh(),
131});
132
133pure_unop!("math.arctanh", ArcTanhOp);
134const_eval!(ArcTanhOp, {
135 FloatAttr(f16, bf16, f32, f64): |inp| inp.atanh(),
136});
137
138pure_unop!("math.degrees", DegreesOp);
139const_eval!(DegreesOp, {
140 FloatAttr(f16, bf16, f32, f64): |inp| inp.to_degrees(),
141});
142
143pure_unop!("math.radians", RadiansOp);
144const_eval!(RadiansOp, {
145 FloatAttr(f16, bf16, f32, f64): |inp| inp.to_radians(),
146});
147
148pure_unop!("math.sqrt", SqrtOp);
149const_eval!(SqrtOp, {
150 FloatAttr(f16, bf16, f32, f64): |inp| inp.sqrt(),
151});
152
153pure_unop!("math.rsqrt", RsqrtOp);
154const_eval!(RsqrtOp, {
155 FloatAttr(f16, bf16, f32, f64): |inp| inp.sqrt().recip(),
156});
157
158pure_unop!("math.round", RoundOp);
159const_eval!(RoundOp, {
160 FloatAttr(f16, bf16, f32, f64): |inp| inp.round(),
161});
162
163pure_unop!("math.floor", FloorOp);
164const_eval!(FloorOp, {
165 FloatAttr(f16, bf16, f32, f64): |inp| inp.floor(),
166});
167
168pure_unop!("math.ceil", CeilOp);
169const_eval!(CeilOp, {
170 FloatAttr(f16, bf16, f32, f64): |inp| inp.ceil(),
171});
172
173pure_unop!("math.trunc", TruncOp);
174const_eval!(TruncOp, {
175 FloatAttr(f16, bf16, f32, f64): |inp| inp.trunc(),
176});
177
178pure_unop!("math.erf", ErfOp);
179pure_unop!("math.recip", RecipOp);
185const_eval!(RecipOp, {
186 FloatAttr(f16, bf16, f32, f64): |inp| inp.recip(),
187});
188
189pure_unop!("math.s_neg", SNegOp);
190const_eval!(SNegOp, {
191 [IntegerAttr(i8, i16, i32, i64)]: |inp| inp.neg(),
192});
193
194pure_unop!("math.f_neg", FNegOp);
195const_eval!(FNegOp, {
196 [FloatAttr(f16, bf16, f32, f64)]: |inp| inp.neg(),
197});
198
199#[cube_op(name = "math.is_nan")]
200#[result_ty(from_inputs = pred_result_ty)]
201#[op_interfaces(TriviallyUnrollable)]
202#[op_traits(Pure, CanMaterialize, PropagatesUniformity)]
203pub struct IsNanOp {
204 pub input: Value,
205}
206const_eval!(IsNanOp, {
207 FloatAttr(f16, bf16, f32, f64): |inp| -> BoolAttr { inp.is_nan().into() }
208});
209
210#[cube_op(name = "math.is_inf")]
211#[result_ty(from_inputs = pred_result_ty)]
212#[op_interfaces(TriviallyUnrollable)]
213#[op_traits(Pure, CanMaterialize, PropagatesUniformity)]
214pub struct IsInfOp {
215 pub input: Value,
216}
217const_eval!(IsInfOp, {
218 FloatAttr(f16, bf16, f32, f64): |inp| -> BoolAttr { inp.is_infinite().into() }
219});
220
221fn pred_result_ty(ctx: &Context, input: &Value) -> TypeHandle {
222 let vectorization = input.vector_size(ctx);
223 let bool = BoolType::get(ctx).into();
224 if vectorization == 1 {
225 bool
226 } else {
227 VectorType::get(ctx, bool, vectorization).into()
228 }
229}
230
231pure_binop!("math.i_add", IAddOp);
232const_eval!(IAddOp, {
233 [IndexAttr, IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| lhs.wrapping_add(rhs),
234});
235simplify!(IAddOp, {
236 |lhs, _| match lhs?.as_int(ctx)?.is_zero() {
237 true => Some(self.rhs(ctx)),
238 _ => None,
239 },
240 |_, rhs| match rhs?.as_int(ctx)?.is_zero() {
241 true => Some(self.lhs(ctx)),
242 _ => None,
243 }
244});
245
246pure_binop!("math.f_add", FAddOp);
247const_eval!(FAddOp, {
248 FloatAttr(f16, bf16, f32, f64): |lhs, rhs| lhs + rhs
249});
250simplify!(FAddOp, {
251 |lhs, _| match lhs?.float_as_f64(ctx) {
252 Some(0.0) => Some(self.rhs(ctx)),
253 _ => None?,
254 },
255 |_, rhs| match rhs?.float_as_f64(ctx) {
256 Some(0.0) => Some(self.lhs(ctx)),
257 _ => None?,
258 }
259});
260
261pure_binop!("math.saturating_s_add", SaturatingSAddOp);
262const_eval!(SaturatingSAddOp, {
263 [IntegerAttr(i8, i16, i32, i64)]: |lhs, rhs| lhs.saturating_add(rhs)
264});
265simplify!(SaturatingSAddOp, {
266 |lhs, _| match lhs?.as_int(ctx)?.is_zero() {
267 true => Some(self.rhs(ctx)),
268 false => None,
269 },
270 |_, rhs| match rhs?.as_int(ctx)?.is_zero() {
271 true => Some(self.lhs(ctx)),
272 false => None,
273 }
274});
275
276pure_binop!("math.saturating_u_add", SaturatingUAddOp);
277const_eval!(SaturatingUAddOp, {
278 [IndexAttr, IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| lhs.saturating_add(rhs)
279});
280simplify!(SaturatingUAddOp, {
281 |lhs, _| match lhs?.as_int(ctx)?.is_zero() {
282 true => Some(self.rhs(ctx)),
283 false => None,
284 },
285 |_, rhs| match rhs?.as_int(ctx)?.is_zero() {
286 true => Some(self.lhs(ctx)),
287 false => None,
288 }
289});
290
291pure_binop!("math.i_sub", ISubOp);
292const_eval!(ISubOp, {
293 [IndexAttr, IntegerAttr(i8, i16, i32, i64), IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| lhs.wrapping_sub(rhs),
294 custom: |_, _| {
296 let result = self.get_result(ctx);
297 if self.lhs(ctx) == self.rhs(ctx) && result.vector_size(ctx) == 1 {
298 Some(int_attr(ctx, result.get_type(ctx), 0))
299 } else {
300 None
301 }
302 }
303});
304simplify!(ISubOp, {
305 |_, rhs| match rhs?.as_int(ctx)?.is_zero() {
306 true => Some(self.lhs(ctx)),
307 false => None,
308 }
309});
310
311pure_binop!("math.f_sub", FSubOp);
312const_eval!(FSubOp, {
313 FloatAttr(f16, bf16, f32, f64): |lhs, rhs| lhs - rhs,
314 custom: |_, _| {
316 let result = self.get_result(ctx);
317 if self.lhs(ctx) == self.rhs(ctx) && result.vector_size(ctx) == 1 {
318 Some(float_attr(ctx, result.get_type(ctx), 0.0))
319 } else {
320 None
321 }
322 }
323});
324simplify!(FSubOp, {
325 |_, rhs| match rhs?.as_const_val(ctx) {
326 ConstantValue::Float(val) if val.to_bits() == 0 => Some(self.lhs(ctx)),
327 _ => None?,
328 }
329});
330
331pure_binop!("math.saturating_s_sub", SaturatingSSubOp);
332const_eval!(SaturatingSSubOp, {
333 [IntegerAttr(i8, i16, i32, i64)]: |lhs, rhs| lhs.saturating_sub(rhs)
334});
335simplify!(SaturatingSSubOp, {
336 |_, rhs| match rhs?.as_int(ctx)?.is_zero() {
337 true => Some(self.lhs(ctx)),
338 false => None,
339 }
340});
341
342pure_binop!("math.saturating_u_sub", SaturatingUSubOp);
343const_eval!(SaturatingUSubOp, {
344 [IndexAttr, IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| lhs.saturating_sub(rhs)
345});
346simplify!(SaturatingUSubOp, {
347 |_, rhs| match rhs?.as_int(ctx)?.is_zero() {
348 true => Some(self.lhs(ctx)),
349 false => None,
350 }
351});
352
353pure_binop!("math.i_mul", IMulOp);
354const_eval!(IMulOp, {
355 [IndexAttr, IntegerAttr(i8, i16, i32, i64), IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| lhs.wrapping_mul(rhs),
356 custom: |lhs, rhs| match lhs.or(rhs)?.as_int(ctx)?.is_zero() {
358 true => Some(int_attr(ctx, self.result_type(ctx), 0)),
359 false => None?
360 }
361});
362simplify!(IMulOp, {
363 |lhs, _| match lhs?.as_const_val(ctx) {
364 ConstantValue::Int(1) | ConstantValue::UInt(1) => {
365 Some(self.rhs(ctx))
366 }
367 _ => None?,
368 },
369 |_, rhs| match rhs?.as_const_val(ctx) {
370 ConstantValue::Int(1) | ConstantValue::UInt(1) => {
371 Some(self.lhs(ctx))
372 }
373 _ => None?,
374 }
375});
376
377pure_binop!("math.f_mul", FMulOp);
378const_eval!(FMulOp, {
379 FloatAttr(f16, bf16, f32, f64): |lhs, rhs| lhs * rhs,
380 custom: |lhs, rhs| {
382 let const_val = lhs.or(rhs)?;
383 Some(match const_val.float_as_f64(ctx) {
384 Some(0.0) => float_attr(ctx, const_val.get_type(ctx), 0.0),
385 _ => None?
386 })
387 }
388});
389simplify!(FMulOp, {
390 |lhs, _| match lhs?.float_as_f64(ctx) {
391 Some(1.0) => Some(self.rhs(ctx)),
392 _ => None?,
393 },
394 |_, rhs| match rhs?.float_as_f64(ctx) {
395 Some(1.0) => Some(self.lhs(ctx)),
396 _ => None?,
397 }
398});
399
400#[cube_op(name = "math.s_div")]
401#[result_ty(same_as = lhs)]
402#[op_interfaces(SameOperandsType, SameOperandsAndResultType, TriviallyUnrollable)]
403#[op_traits(CanMaterialize, NoSideEffects, NoMemoryEffect, PropagatesUniformity)] pub struct SDivOp {
405 pub lhs: Value,
406 pub rhs: Value,
407}
408const_eval!(SDivOp, {
409 [IntegerAttr(i8, i16, i32, i64)]: |lhs, rhs| lhs.wrapping_div(rhs),
410 custom: |lhs, _| match lhs?.as_int(ctx)?.is_zero() {
412 true => Some(int_attr(ctx, self.result_type(ctx), 0)),
413 false => None
414 },
415 custom: |_, _| {
417 let result = self.get_result(ctx);
418 if self.lhs(ctx) == self.rhs(ctx) && result.vector_size(ctx) == 1 {
419 Some(int_attr(ctx, result.get_type(ctx), 1))
420 } else {
421 None
422 }
423 }
424});
425simplify!(SDivOp, {
426 |_, rhs| match rhs?.as_const_val(ctx) {
427 ConstantValue::Int(1) => Some(self.lhs(ctx)),
428 _ => None?,
429 }
430});
431
432#[cube_op(name = "math.u_div")]
433#[result_ty(same_as = lhs)]
434#[op_interfaces(SameOperandsType, SameOperandsAndResultType, TriviallyUnrollable)]
435#[op_traits(CanMaterialize, NoSideEffects, NoMemoryEffect, PropagatesUniformity)] pub struct UDivOp {
437 pub lhs: Value,
438 pub rhs: Value,
439}
440const_eval!(UDivOp, {
441 [IndexAttr, IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| lhs.wrapping_div(rhs),
442 custom: |lhs, _| match lhs?.as_int(ctx)?.is_zero() {
444 true => Some(int_attr(ctx, lhs?.get_type(ctx), 0)),
445 false => None
446 },
447 custom: |_, _| {
449 let result = self.get_result(ctx);
450 if self.lhs(ctx) == self.rhs(ctx) && result.vector_size(ctx) == 1 {
451 Some(int_attr(ctx, result.get_type(ctx), 1))
452 } else {
453 None
454 }
455 }
456});
457simplify!(UDivOp, {
458 |_, rhs| match rhs?.as_const_val(ctx) {
459 ConstantValue::UInt(1) => Some(self.lhs(ctx)),
460 _ => None?,
461 }
462});
463
464pure_binop!("math.f_div", FDivOp);
465const_eval!(FDivOp, {
466 FloatAttr(f16, bf16, f32, f64): |lhs, rhs| lhs / rhs,
467 custom: |lhs, _| {
469 Some(match lhs?.float_as_f64(ctx) {
470 Some(0.0) => float_attr(ctx, lhs?.get_type(ctx), 0.0),
471 _ => None?
472 })
473 },
474});
475simplify!(FDivOp, {
476 |_, rhs| match rhs?.float_as_f64(ctx) {
477 Some(1.0) => Some(self.lhs(ctx)),
478 _ => None?,
479 }
480});
481
482pure_binop!("math.arc_tan2", ArcTan2Op);
483const_eval!(ArcTan2Op, {
484 FloatAttr(f16, bf16, f32, f64): |lhs, rhs| lhs.atan2(rhs),
485});
486
487pure_binop!("math.powf", PowfOp);
488const_eval!(PowfOp, {
489 FloatAttr(f16, bf16, f32, f64): |lhs, rhs| lhs.powf(rhs),
490});
491
492#[cube_op(name = "math.powi")]
493#[result_ty(same_as = lhs)]
494#[op_traits(Pure, CanMaterialize, PropagatesUniformity)]
495pub struct PowiOp {
496 pub lhs: Value,
497 pub rhs: Value,
498}
499
500pure_binop!("math.hypot", HypotOp);
503const_eval!(HypotOp, {
504 FloatAttr(f16, bf16, f32, f64): |lhs, rhs| lhs.hypot(rhs),
505});
506
507pure_binop!("math.rhypot", RhypotOp);
508const_eval!(RhypotOp, {
509 FloatAttr(f16, bf16, f32, f64): |lhs, rhs| lhs.hypot(rhs).recip(),
510});
511
512#[cube_op(name = "math.s_rem")]
513#[result_ty(same_as = lhs)]
514#[op_interfaces(SameOperandsType, SameOperandsAndResultType, TriviallyUnrollable)]
515#[op_traits(CanMaterialize, NoSideEffects, NoMemoryEffect, PropagatesUniformity)] pub struct SRemOp {
517 pub lhs: Value,
518 pub rhs: Value,
519}
520const_eval!(SRemOp, {
521 [IntegerAttr(i8, i16, i32, i64)]: |lhs, rhs| lhs % rhs,
522 custom: |lhs, _| match lhs?.as_const_val(ctx) {
524 ConstantValue::Int(0) => Some(int_attr(ctx, lhs?.get_type(ctx), 0)),
525 _ => None
526 },
527 custom: |_, rhs| match rhs?.as_const_val(ctx) {
529 ConstantValue::Int(1) => Some(int_attr(ctx, rhs?.get_type(ctx), 0)),
530 _ => None
531 }
532});
533simplify!(SRemOp, {
534 |_, rhs| match rhs?.as_const_val(ctx) {
535 ConstantValue::Int(1) => Some(self.lhs(ctx)),
536 _ => None?,
537 }
538});
539
540#[cube_op(name = "math.u_rem")]
541#[result_ty(same_as = lhs)]
542#[op_interfaces(SameOperandsType, SameOperandsAndResultType, TriviallyUnrollable)]
543#[op_traits(CanMaterialize, NoSideEffects, NoMemoryEffect, PropagatesUniformity)] pub struct URemOp {
545 pub lhs: Value,
546 pub rhs: Value,
547}
548const_eval!(URemOp, {
549 [IndexAttr, IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| lhs % rhs,
550 custom: |lhs, _| match lhs?.as_const_val(ctx) {
552 ConstantValue::UInt(0) => Some(int_attr(ctx, lhs?.get_type(ctx), 0)),
553 _ => None
554 },
555 custom: |_, rhs| match rhs?.as_const_val(ctx) {
557 ConstantValue::UInt(1) => Some(int_attr(ctx, rhs?.get_type(ctx), 0)),
558 _ => None
559 }
560});
561simplify!(URemOp, {
562 |_, rhs| match rhs?.as_const_val(ctx) {
563 ConstantValue::UInt(1) => Some(self.lhs(ctx)),
564 _ => None?,
565 }
566});
567
568pure_binop!("math.f_rem", FRemOp);
569const_eval!(FRemOp, {
570 [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| lhs % rhs,
571 custom: |lhs, _| match lhs?.float_as_f64(ctx) {
573 Some(0.0) => Some(float_attr(ctx, lhs?.get_type(ctx), 0.0)),
574 _ => None
575 },
576});
577simplify!(FRemOp, {
578 |_, rhs| match rhs?.float_as_f64(ctx) {
579 Some(1.0) => Some(self.lhs(ctx)),
580 _ => None?,
581 }
582});
583
584#[cube_op(name = "math.s_mod_floor")]
585#[result_ty(same_as = lhs)]
586#[op_interfaces(SameOperandsType, SameOperandsAndResultType, TriviallyUnrollable)]
587#[op_traits(CanMaterialize, NoSideEffects, NoMemoryEffect, PropagatesUniformity)] pub struct SModFloorOp {
589 pub lhs: Value,
590 pub rhs: Value,
591}
592const_eval!(SModFloorOp, {
593 IntegerAttr(i8, i16, i32, i64): |lhs, rhs| lhs.mod_floor(&rhs),
594 custom: |lhs, _| match lhs?.as_const_val(ctx) {
596 ConstantValue::Int(0) => Some(int_attr(ctx, lhs?.get_type(ctx), 0)),
597 _ => None?
598 },
599 custom: |_, rhs| match rhs?.as_const_val(ctx) {
601 ConstantValue::Int(1) => Some(int_attr(ctx, rhs?.get_type(ctx), 0)),
602 _ => None?
603 }
604});
605
606pure_binop!("math.f_mod_floor", FModFloorOp);
607const_eval!(FModFloorOp, {
608 FloatAttr(f16, bf16, f32, f64): |lhs, rhs| lhs - (lhs / rhs).floor() * rhs,
609 custom: |lhs, _| match lhs?.float_as_f64(ctx) {
611 Some(0.0) => Some(float_attr(ctx, lhs?.get_type(ctx), 0.0)),
612 _ => None?
613 },
614});
615
616pure_binop!("math.s_mul_hi", SMulHiOp);
617const_eval!(SMulHiOp, {
618 IntegerAttr(i64): |lhs, rhs| ((lhs as i128 * rhs as i128) >> 64) as i64,
619 IntegerAttr(i32): |lhs, rhs| ((lhs as i64 * rhs as i64) >> 32) as i32,
620 custom: |lhs, rhs| match lhs.or(rhs)?.as_const_val(ctx) {
622 ConstantValue::Int(0) => Some(int_attr(ctx, self.result_type(ctx), 0)),
623 _ => None?
624 }
625});
626simplify!(SMulHiOp, {
627 |lhs, _| match lhs?.as_const_val(ctx) {
628 ConstantValue::Int(1) => Some(self.rhs(ctx)),
629 _ => None?,
630 },
631 |_, rhs| match rhs?.as_const_val(ctx) {
632 ConstantValue::Int(1) => Some(self.lhs(ctx)),
633 _ => None?,
634 }
635});
636
637pure_binop!("math.u_mul_hi", UMulHiOp);
638const_eval!(UMulHiOp, {
639 IndexAttr: |lhs, rhs| ((lhs as u128 * rhs as u128) >> 64) as usize,
640 IntegerAttr(u64): |lhs, rhs| ((lhs as u128 * rhs as u128) >> 64) as u64,
641 IntegerAttr(u32): |lhs, rhs| ((lhs as u64 * rhs as u64) >> 32) as u32,
642 custom: |lhs, rhs| match lhs.or(rhs)?.as_const_val(ctx) {
644 ConstantValue::UInt(0) => Some(int_attr(ctx, self.result_type(ctx), 0)),
645 _ => None?
646 }
647});
648simplify!(UMulHiOp, {
649 |lhs, _| match lhs?.as_const_val(ctx) {
650 ConstantValue::UInt(1) => Some(self.rhs(ctx)),
651 _ => None?,
652 },
653 |_, rhs| match rhs?.as_const_val(ctx) {
654 ConstantValue::UInt(1) => Some(self.lhs(ctx)),
655 _ => None?,
656 }
657});
658
659pub(super) fn index_attr(val: usize) -> AttrObj {
660 AttrObj::from(IndexAttr::new(val))
661}
662
663pub(super) fn int_attr(ctx: &Context, ty: TypeHandle, val: i128) -> AttrObj {
664 if ty.is_index(ctx) {
665 IndexAttr::new(val as usize).into()
666 } else {
667 let ty = TypedHandle::<IntegerType>::from_handle(ty, ctx).unwrap();
668 let width = bw(ty.deref(ctx).width() as usize);
669 let val = APInt::from_i128(val, width);
670 AttrObj::from(IntegerAttr::new(ty, val))
671 }
672}
673
674pub(super) fn float_attr(ctx: &Context, ty: TypeHandle, val: f64) -> AttrObj {
675 AttrObj::from(FloatAttr::from_f64(ctx, ty, val))
676}
677
678#[cube_op(name = "math.fma")]
679#[result_ty(same_as = a)]
680#[op_interfaces(SameOperandsType, SameOperandsAndResultType, TriviallyUnrollable)]
681#[op_traits(Pure, CanMaterialize, PropagatesUniformity)]
682pub struct FmaOp {
683 pub a: Value,
684 pub b: Value,
685 pub c: Value,
686}
687const_eval!(FmaOp, {
688 FloatAttr(f16, bf16, f32, f64): |a, b, c| a * b + c,
689});
690
691#[cube_op(name = "math.dp4a")]
693#[result_ty(same_as = a)]
694#[op_interfaces(SameOperandsType, SameOperandsAndResultType, TriviallyUnrollable)]
695#[op_traits(Pure, CanMaterialize)]
696pub struct Dp4aOp {
697 pub a: Value,
698 pub b: Value,
699 pub c: Value,
700}
701const_eval!(Dp4aOp, {
702 IntegerAttr(i32): |a, b, c| dp4a_const(a, b, c),
703});
704
705fn dp4a_const(a: i32, b: i32, c: i32) -> i32 {
706 let byte = |value: i32, shift: u32| ((value as u32 >> shift) as u8) as i8 as i32;
707 [0, 8, 16, 24].into_iter().fold(c, |acc, shift| {
708 acc.wrapping_add(byte(a, shift).wrapping_mul(byte(b, shift)))
709 })
710}
711
712#[cfg(test)]
713mod dp4a_tests {
714 use super::dp4a_const;
715
716 #[test]
717 fn evaluates_signed_bytes_and_wraps_accumulator() {
718 let a = i32::from_le_bytes([1, -2i8 as u8, 127, -128i8 as u8]);
719 let b = i32::from_le_bytes([-3i8 as u8, 4, -5i8 as u8, 6]);
720 let dot = -3 - 8 - 635 - 768;
721
722 assert_eq!(dp4a_const(a, b, 10), dot + 10);
723 assert_eq!(dp4a_const(a, b, i32::MIN), i32::MIN.wrapping_add(dot));
724 }
725}