1use cubecl_macros_internal::{const_eval, cube_op, simplify};
2use half::{bf16, f16};
3use pliron::{
4 builtin::{attributes::IntegerAttr, types::IntegerType},
5 r#type::TypeHandle,
6};
7
8use crate::{
9 CanMaterialize, Commutative, ConstantValue, PropagatesUniformity, Pure,
10 attributes::{BoolAttr, FloatAttr, IndexAttr, IntAttrExt},
11 dialect::{base::pure_binop, math::int_attr},
12 interfaces::{
13 Expression, ExpressionCanonicalize, ExpressionValue, TriviallyUnrollable, TypedExt,
14 },
15 prelude::*,
16 types::{VectorType, scalar::BoolType},
17};
18
19pure_binop!("cmp.s_min", SMinOp);
20Commutative!(SMinOp);
21const_eval!(SMinOp, {
22 [IndexAttr, IntegerAttr(i8, i16, i32, i64)]: |lhs, rhs| lhs.min(rhs),
23 custom: |lhs, rhs| {
25 let const_val = lhs.or(rhs)?;
26 let ty = const_val.get_type(ctx);
27 match const_val.as_const_val(ctx) {
28 ConstantValue::Int(val) if is_min_int(ctx, ty, val) => Some(int_attr(ctx, ty, val as i128)),
29 _ => None
30 }
31 }
32});
33simplify!(SMinOp, {
34 |lhs, _| {
36 let ty = lhs?.get_type(ctx);
37 match lhs?.as_const_val(ctx) {
38 ConstantValue::Int(val) if is_max_int(ctx, ty, val) => Some(self.rhs(ctx)),
39 _ => None,
40 }
41 },
42 |_, rhs| {
44 let ty = rhs?.get_type(ctx);
45 match rhs?.as_const_val(ctx) {
46 ConstantValue::Int(val) if is_max_int(ctx, ty, val) => Some(self.lhs(ctx)),
47 _ => None,
48 }
49 },
50 |_, _| match self.lhs(ctx) == self.rhs(ctx) {
52 true => Some(self.lhs(ctx)),
53 false => None
54 }
55});
56
57pure_binop!("cmp.u_min", UMinOp);
58Commutative!(UMinOp);
59const_eval!(UMinOp, {
60 [IndexAttr, IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| lhs.min(rhs),
61 custom: |lhs, rhs| match lhs.or(rhs)?.as_int(ctx)?.is_zero() {
63 true => Some(int_attr(ctx, self.result_type(ctx), 0)),
64 false => None,
65 }
66});
67simplify!(UMinOp, {
68 |lhs, _| {
70 let ty = lhs?.get_type(ctx);
71 match lhs?.as_const_val(ctx) {
72 ConstantValue::UInt(val) if is_max_uint(ctx, ty, val) => Some(self.rhs(ctx)),
73 _ => None,
74 }
75 },
76 |_, rhs| {
78 let ty = rhs?.get_type(ctx);
79 match rhs?.as_const_val(ctx) {
80 ConstantValue::UInt(val) if is_max_uint(ctx, ty, val) => Some(self.lhs(ctx)),
81 _ => None,
82 }
83 },
84 |_, _| match self.lhs(ctx) == self.rhs(ctx) {
86 true => Some(self.lhs(ctx)),
87 false => None
88 }
89});
90
91pure_binop!("cmp.f_min", FMinOp);
92Commutative!(FMinOp);
93const_eval!(FMinOp, { [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| lhs.min(rhs) });
94simplify!(FMinOp, {
95 |_, _| match self.lhs(ctx) == self.rhs(ctx) {
97 true => Some(self.lhs(ctx)),
98 false => None,
99 }
100});
101
102pure_binop!("cmp.s_max", SMaxOp);
103Commutative!(SMaxOp);
104const_eval!(SMaxOp, {
105 [IndexAttr, IntegerAttr(i8, i16, i32, i64)]: |lhs, rhs| lhs.max(rhs),
106 custom: |lhs, rhs| {
108 let const_val = lhs.or(rhs)?;
109 let ty = const_val.get_type(ctx);
110 match const_val.as_const_val(ctx) {
111 ConstantValue::Int(val) if is_max_int(ctx, ty, val) => Some(int_attr(ctx, ty, val as i128)),
112 _ => None
113 }
114 }
115});
116simplify!(SMaxOp, {
117 |lhs, _| {
119 let ty = lhs?.get_type(ctx);
120 match lhs?.as_const_val(ctx) {
121 ConstantValue::Int(val) if is_min_int(ctx, ty, val) => Some(self.rhs(ctx)),
122 _ => None,
123 }
124 },
125 |_, rhs| {
127 let ty = rhs?.get_type(ctx);
128 match rhs?.as_const_val(ctx) {
129 ConstantValue::Int(val) if is_min_int(ctx, ty, val) => Some(self.lhs(ctx)),
130 _ => None,
131 }
132 },
133 |_, _| match self.lhs(ctx) == self.rhs(ctx) {
135 true => Some(self.lhs(ctx)),
136 false => None,
137 }
138});
139
140pure_binop!("cmp.u_max", UMaxOp);
141Commutative!(UMaxOp);
142const_eval!(UMaxOp, {
143 [IndexAttr, IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| lhs.max(rhs),
144 custom: |lhs, rhs| {
146 let const_val = lhs.or(rhs)?;
147 let ty = const_val.get_type(ctx);
148 match const_val.as_const_val(ctx) {
149 ConstantValue::UInt(val) if is_max_uint(ctx, ty, val) => Some(int_attr(ctx, ty, val as i128)),
150 _ => None
151 }
152 }
153});
154simplify!(UMaxOp, {
155 |lhs, _| match lhs?.as_int(ctx)?.is_zero() {
157 true => Some(self.rhs(ctx)),
158 false => None,
159 },
160 |_, rhs| match rhs?.as_int(ctx)?.is_zero() {
162 true => Some(self.lhs(ctx)),
163 false => None,
164 },
165 |_, _| match self.lhs(ctx) == self.rhs(ctx) {
167 true => Some(self.lhs(ctx)),
168 false => None,
169 }
170});
171
172pure_binop!("cmp.f_max", FMaxOp);
173Commutative!(FMaxOp);
174const_eval!(FMaxOp, {
175 [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| lhs.max(rhs),
176});
177simplify!(FMaxOp, {
178 |_, _| match self.lhs(ctx) == self.rhs(ctx) {
180 true => Some(self.lhs(ctx)),
181 false => None,
182 }
183});
184
185#[cube_op(name = "cmp.s_clamp")]
186#[result_ty(same_as = input)]
187#[op_interfaces(SameOperandsType, SameOperandsAndResultType, TriviallyUnrollable)]
188#[op_traits(Pure, CanMaterialize, PropagatesUniformity)]
189pub struct SClampOp {
190 pub input: Value,
191 pub min: Value,
192 pub max: Value,
193}
194const_eval!(SClampOp, {
195 [IndexAttr, IntegerAttr(i8, i16, i32, i64)]: |inp, min, max| inp.clamp(min, max),
196 custom: |_, min, _| {
198 let ty = min?.get_type(ctx);
199 match min?.as_const_val(ctx) {
200 ConstantValue::Int(val) if is_max_int(ctx, ty, val) => Some(int_attr(ctx, ty, val as i128)),
201 _ => None
202 }
203 },
204 custom: |_, _, max| {
206 let ty = max?.get_type(ctx);
207 match max?.as_const_val(ctx) {
208 ConstantValue::Int(val) if is_min_int(ctx, ty, val) => Some(int_attr(ctx, ty, val as i128)),
209 _ => None
210 }
211 }
212});
213
214#[cube_op(name = "cmp.u_clamp")]
215#[result_ty(same_as = input)]
216#[op_interfaces(SameOperandsType, SameOperandsAndResultType, TriviallyUnrollable)]
217#[op_traits(Pure, CanMaterialize, PropagatesUniformity)]
218pub struct UClampOp {
219 pub input: Value,
220 pub min: Value,
221 pub max: Value,
222}
223const_eval!(UClampOp, {
224 [IndexAttr, IntegerAttr(u8, u16, u32, u64)]: |inp, min, max| inp.clamp(min, max),
225 custom: |_, min, _| {
227 let ty = min?.get_type(ctx);
228 match min?.as_const_val(ctx) {
229 ConstantValue::UInt(val) if is_max_uint(ctx, ty, val) => Some(int_attr(ctx, ty, val as i128)),
230 _ => None
231 }
232 },
233 custom: |_, _, max| match max?.as_int(ctx)?.is_zero() {
235 true => Some(int_attr(ctx, self.result_type(ctx), 0)),
236 false => None
237 }
238});
239
240#[cube_op(name = "cmp.f_clamp")]
241#[result_ty(same_as = input)]
242#[op_interfaces(SameOperandsType, SameOperandsAndResultType, TriviallyUnrollable)]
243#[op_traits(Pure, CanMaterialize, PropagatesUniformity)]
244pub struct FClampOp {
245 pub input: Value,
246 pub min: Value,
247 pub max: Value,
248}
249const_eval!(FClampOp, {
250 [FloatAttr(f16, bf16, f32, f64)]: |inp, min, max| inp.clamp(min, max),
251});
252
253#[cube_op(name = "cmp.f_min_nan")]
256#[result_ty(same_as = lhs)]
257#[op_interfaces(SameOperandsType, SameOperandsAndResultType, TriviallyUnrollable)]
258#[op_traits(Pure, CanMaterialize, PropagatesUniformity)]
259pub struct FMinNanOp {
260 pub lhs: Value,
261 pub rhs: Value,
262}
263const_eval!(FMinNanOp, {
264 [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| {
265 if lhs.is_nan() { lhs }
266 else if rhs.is_nan() { rhs }
267 else { lhs.min(rhs) }
268 },
269});
270
271#[cube_op(name = "cmp.f_max_nan")]
274#[result_ty(same_as = lhs)]
275#[op_interfaces(SameOperandsType, SameOperandsAndResultType, TriviallyUnrollable)]
276#[op_traits(Pure, CanMaterialize, PropagatesUniformity)]
277pub struct FMaxNanOp {
278 pub lhs: Value,
279 pub rhs: Value,
280}
281const_eval!(FMaxNanOp, {
282 [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| {
283 if lhs.is_nan() { lhs }
284 else if rhs.is_nan() { rhs }
285 else { lhs.max(rhs) }
286 },
287});
288
289#[cube_op(name = "cmp.f_clamp_nan")]
295#[result_ty(same_as = input)]
296#[op_interfaces(SameOperandsType, SameOperandsAndResultType, TriviallyUnrollable)]
297#[op_traits(Pure, CanMaterialize, PropagatesUniformity)]
298pub struct FClampNanOp {
299 pub input: Value,
300 pub min: Value,
301 pub max: Value,
302}
303const_eval!(FClampNanOp, {
304 [FloatAttr(f16, bf16, f32, f64)]: |inp, min, max| {
305 if inp.is_nan() { inp }
306 else if min.is_nan() { min }
307 else if max.is_nan() { max }
308 else { inp.min(max).max(min) }
309 },
310});
311
312macro_rules! cmp_binop {
313 ($name: literal, $ty: ident) => {
314 #[cubecl_macros_internal::cube_op(name = $name)]
315 #[result_ty(from_inputs = cmp_result_ty)]
316 #[$crate::prelude::op_interfaces(SameOperandsType, TriviallyUnrollable)]
317 #[op_traits(Pure, CanMaterialize, PropagatesUniformity)]
318 pub struct $ty {
319 pub lhs: Value,
320 pub rhs: Value,
321 }
322 };
323}
324
325cmp_binop!("cmp.s_less_than", SLessThanOp);
326const_eval!(SLessThanOp, {
327 [IndexAttr, IntegerAttr(i8, i16, i32, i64)]: |lhs, rhs| -> BoolAttr {
328 (lhs < rhs).into()
329 },
330 custom: |_, _| match self.lhs(ctx) == self.rhs(ctx) {
332 true => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
333 false => None
334 },
335 custom: |_, rhs| {
337 let ty = rhs?.get_type(ctx);
338 match rhs?.as_const_val(ctx) {
339 ConstantValue::Int(val) if is_min_int(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
340 _ => None
341 }
342 },
343 custom: |lhs, _| {
345 let ty = lhs?.get_type(ctx);
346 match lhs?.as_const_val(ctx) {
347 ConstantValue::Int(val) if is_max_int(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
348 _ => None
349 }
350 },
351});
352
353cmp_binop!("cmp.u_less_than", ULessThanOp);
354const_eval!(ULessThanOp, {
355 [IndexAttr, IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| -> BoolAttr {
356 (lhs < rhs).into()
357 },
358 custom: |_, _| match self.lhs(ctx) == self.rhs(ctx) {
360 true => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
361 false => None
362 },
363 custom: |_, rhs| match rhs?.as_int(ctx)?.is_zero() {
365 true => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
366 false => None
367 },
368 custom: |lhs, _| {
370 let ty = lhs?.get_type(ctx);
371 match lhs?.as_const_val(ctx) {
372 ConstantValue::UInt(val) if is_max_uint(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
373 _ => None
374 }
375 },
376});
377
378cmp_binop!("cmp.f_less_than", FLessThanOp);
379const_eval!(FLessThanOp, {
380 [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| -> BoolAttr {
381 (lhs < rhs).into()
382 },
383});
384
385macro_rules! invert_cmp_expression {
386 ($ty: ty => $to: ty) => {
387 #[op_interface_impl]
388 impl ExpressionCanonicalize for $ty {
389 fn canonical_expression(
390 &self,
391 ctx: &Context,
392 mut operands: Vec<ExpressionValue>,
393 ) -> Expression {
394 operands.swap(0, 1);
395 Expression::new(
396 self.result_type(ctx),
397 <$to>::get_opid_static(),
398 operands,
399 Default::default(),
400 )
401 }
402 }
403 };
404}
405
406cmp_binop!("cmp.s_greater_than", SGreaterThanOp);
407invert_cmp_expression!(SGreaterThanOp => SLessThanOp);
408const_eval!(SGreaterThanOp, {
409 [IndexAttr, IntegerAttr(i8, i16, i32, i64)]: |lhs, rhs| -> BoolAttr {
410 (lhs > rhs).into()
411 },
412 custom: |_, _| match self.lhs(ctx) == self.rhs(ctx) {
414 true => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
415 false => None
416 },
417 custom: |lhs, _| {
419 let ty = lhs?.get_type(ctx);
420 match lhs?.as_const_val(ctx) {
421 ConstantValue::Int(val) if is_min_int(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
422 _ => None
423 }
424 },
425 custom: |_, rhs| {
427 let ty = rhs?.get_type(ctx);
428 match rhs?.as_const_val(ctx) {
429 ConstantValue::Int(val) if is_max_int(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
430 _ => None
431 }
432 }
433});
434
435cmp_binop!("cmp.u_greater_than", UGreaterThanOp);
436invert_cmp_expression!(UGreaterThanOp => ULessThanOp);
437const_eval!(UGreaterThanOp, {
438 [IndexAttr, IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| -> BoolAttr {
439 (lhs > rhs).into()
440 },
441 custom: |_, _| match self.lhs(ctx) == self.rhs(ctx) {
443 true => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
444 false => None
445 },
446 custom: |lhs, _| match lhs?.as_int(ctx)?.is_zero() {
448 true => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
449 false => None
450 },
451 custom: |_, rhs| {
453 let ty = rhs?.get_type(ctx);
454 match rhs?.as_const_val(ctx) {
455 ConstantValue::UInt(val) if is_max_uint(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
456 _ => None
457 }
458 }
459});
460
461cmp_binop!("cmp.f_greater_than", FGreaterThanOp);
462invert_cmp_expression!(FGreaterThanOp => FLessThanOp);
463const_eval!(FGreaterThanOp, {
464 [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| -> BoolAttr {
465 (lhs > rhs).into()
466 },
467});
468
469cmp_binop!("cmp.s_less_than_or_equal", SLessThanOrEqualOp);
470const_eval!(SLessThanOrEqualOp, {
471 [IndexAttr, IntegerAttr(i8, i16, i32, i64)]: |lhs, rhs| -> BoolAttr {
472 (lhs <= rhs).into()
473 },
474 custom: |_, _| match self.lhs(ctx) == self.rhs(ctx) {
476 true => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
477 false => None
478 },
479 custom: |lhs, _| {
481 let ty = lhs?.get_type(ctx);
482 match lhs?.as_const_val(ctx) {
483 ConstantValue::Int(val) if is_min_int(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
484 _ => None
485 }
486 },
487 custom: |_, rhs| {
489 let ty = rhs?.get_type(ctx);
490 match rhs?.as_const_val(ctx) {
491 ConstantValue::Int(val) if is_max_int(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
492 _ => None
493 }
494 }
495});
496
497cmp_binop!("cmp.u_less_than_or_equal", ULessThanOrEqualOp);
498const_eval!(ULessThanOrEqualOp, {
499 [IndexAttr, IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| -> BoolAttr {
500 (lhs <= rhs).into()
501 },
502 custom: |_, _| match self.lhs(ctx) == self.rhs(ctx) {
504 true => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
505 false => None
506 },
507 custom: |lhs, _| match lhs?.as_int(ctx)?.is_zero() {
509 true => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
510 false => None
511 },
512 custom: |_, rhs| {
514 let ty = rhs?.get_type(ctx);
515 match rhs?.as_const_val(ctx) {
516 ConstantValue::UInt(val) if is_max_uint(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
517 _ => None
518 }
519 }
520});
521
522cmp_binop!("cmp.f_less_than_or_equal", FLessThanOrEqualOp);
523const_eval!(FLessThanOrEqualOp, {
524 [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| -> BoolAttr {
525 (lhs <= rhs).into()
526 },
527});
528
529cmp_binop!("cmp.s_greater_than_or_equal", SGreaterThanOrEqualOp);
530invert_cmp_expression!(SGreaterThanOrEqualOp => SLessThanOrEqualOp);
531const_eval!(SGreaterThanOrEqualOp, {
532 [IndexAttr, IntegerAttr(i8, i16, i32, i64)]: |lhs, rhs| -> BoolAttr {
533 (lhs >= rhs).into()
534 },
535 custom: |_, _| match self.lhs(ctx) == self.rhs(ctx) {
537 true => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
538 false => None
539 },
540 custom: |_, rhs| {
542 let ty = rhs?.get_type(ctx);
543 match rhs?.as_const_val(ctx) {
544 ConstantValue::Int(val) if is_min_int(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
545 _ => None
546 }
547 },
548 custom: |lhs, _| {
550 let ty = lhs?.get_type(ctx);
551 match lhs?.as_const_val(ctx) {
552 ConstantValue::Int(val) if is_max_int(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
553 _ => None
554 }
555 }
556});
557
558cmp_binop!("cmp.u_greater_than_or_equal", UGreaterThanOrEqualOp);
559invert_cmp_expression!(UGreaterThanOrEqualOp => ULessThanOrEqualOp);
560const_eval!(UGreaterThanOrEqualOp, {
561 [IndexAttr, IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| -> BoolAttr {
562 (lhs >= rhs).into()
563 },
564 custom: |_, _| match self.lhs(ctx) == self.rhs(ctx) {
566 true => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
567 false => None
568 },
569 custom: |_, rhs| match rhs?.as_int(ctx)?.is_zero() {
571 true => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
572 false => None
573 },
574 custom: |lhs, _| {
576 let ty = lhs?.get_type(ctx);
577 match lhs?.as_const_val(ctx) {
578 ConstantValue::UInt(val) if is_max_uint(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
579 _ => None
580 }
581 }
582});
583
584cmp_binop!("cmp.f_greater_than_or_equal", FGreaterThanOrEqualOp);
585invert_cmp_expression!(FGreaterThanOrEqualOp => FLessThanOrEqualOp);
586const_eval!(FGreaterThanOrEqualOp, {
587 [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| -> BoolAttr {
588 (lhs >= rhs).into()
589 },
590});
591
592cmp_binop!("cmp.i_equal", IEqualOp);
593Commutative!(IEqualOp);
594const_eval!(IEqualOp, {
595 [IndexAttr, IntegerAttr(i8, i16, i32, i64), IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| -> BoolAttr {
596 (lhs == rhs).into()
597 },
598 custom: |_, _| match self.lhs(ctx) == self.rhs(ctx) {
600 true => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
601 false => None
602 }
603});
604
605cmp_binop!("cmp.f_equal", FEqualOp);
606Commutative!(FEqualOp);
607const_eval!(FEqualOp, {
608 [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| -> BoolAttr {
609 (lhs == rhs).into()
610 },
611});
612
613cmp_binop!("cmp.bool_equal", BoolEqualOp);
614Commutative!(BoolEqualOp);
615const_eval!(BoolEqualOp, {
616 [BoolAttr]: |lhs, rhs| lhs == rhs,
617 custom: |_, _| match self.lhs(ctx) == self.rhs(ctx) {
619 true => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
620 false => None
621 }
622});
623
624cmp_binop!("cmp.i_not_equal", INotEqualOp);
625Commutative!(INotEqualOp);
626const_eval!(INotEqualOp, {
627 [IndexAttr, IntegerAttr(i8, i16, i32, i64), IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| -> BoolAttr {
628 (lhs != rhs).into()
629 },
630 custom: |_, _| match self.lhs(ctx) == self.rhs(ctx) {
632 true => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
633 false => None
634 },
635});
636
637cmp_binop!("cmp.f_not_equal", FNotEqualOp);
638Commutative!(FNotEqualOp);
639const_eval!(FNotEqualOp, {
640 [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| -> BoolAttr {
641 (lhs != rhs).into()
642 },
643});
644
645cmp_binop!("cmp.bool_not_equal", BoolNotEqualOp);
646Commutative!(BoolNotEqualOp);
647const_eval!(BoolNotEqualOp, {
648 [BoolAttr]: |lhs, rhs| lhs != rhs,
649 custom: |_, _| match self.lhs(ctx) == self.rhs(ctx) {
651 true => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
652 false => None
653 }
654});
655
656fn cmp_result_ty(ctx: &Context, lhs: &Value, _: &Value) -> TypeHandle {
657 let vectorization = lhs.vector_size(ctx);
658 let bool = BoolType::get(ctx).into();
659 if vectorization == 1 {
660 bool
661 } else {
662 VectorType::get(ctx, bool, vectorization).into()
663 }
664}
665
666pub(super) fn width(ctx: &Context, ty: TypeHandle) -> usize {
667 ty.size(ctx) * 8
668}
669
670fn is_min_int(ctx: &Context, ty: TypeHandle, val: i64) -> bool {
671 let ty = TypedHandle::<IntegerType>::from_handle(ty, ctx).unwrap();
672 val == min_int(ty.deref(ctx).width() as usize)
673}
674
675pub(super) fn is_max_int(ctx: &Context, ty: TypeHandle, val: i64) -> bool {
676 let ty = TypedHandle::<IntegerType>::from_handle(ty, ctx).unwrap();
677 val == max_int(ty.deref(ctx).width() as usize)
678}
679
680pub(super) fn is_max_uint(ctx: &Context, ty: TypeHandle, val: u64) -> bool {
681 val == max_uint(ty.size_bits(ctx))
682}
683
684fn min_int(width: usize) -> i64 {
685 if width >= 64 {
686 i64::MIN
687 } else {
688 -(1i64 << (width - 1))
689 }
690}
691
692fn max_int(width: usize) -> i64 {
693 if width >= 64 {
694 i64::MAX
695 } else {
696 (1i64 << (width - 1)) - 1
697 }
698}
699
700fn max_uint(width: usize) -> u64 {
701 if width >= 64 {
702 u64::MAX
703 } else {
704 (1u64 << width) - 1
705 }
706}
707
708#[cfg(test)]
709mod clamp_nan_tests {
710 use super::*;
711 use crate::types::scalar::Float32Type;
712 use alloc::boxed::Box;
713 use pliron::{
714 attribute::AttrObj, builtin::ops::ConstantOp, opts::constants::ConstFoldInterface,
715 };
716
717 fn constants<const N: usize>(
718 ctx: &mut Context,
719 values: [f64; N],
720 ) -> ([Value; N], [Option<AttrObj>; N]) {
721 let ty = Float32Type::get(ctx).to_handle();
722 let attrs = values.map(|v| FloatAttr::from_f64(ctx, ty, v));
723 let operands = attrs
724 .each_ref()
725 .map(|attr| ConstantOp::new(ctx, Box::new(attr.clone())).get_result(ctx));
726 (operands, attrs.map(|attr| Some(Box::new(attr) as AttrObj)))
727 }
728
729 fn assert_folded(ctx: &Context, folded: &[Option<AttrObj>], expected: f64) {
730 let actual = folded[0]
731 .as_ref()
732 .unwrap()
733 .downcast_ref::<FloatAttr>()
734 .unwrap();
735 let actual = actual.float_type(ctx).value_to_f64(actual.val);
736 assert!(
737 actual == expected || (actual.is_nan() && expected.is_nan()),
738 "expected {expected}, got {actual}"
739 );
740 }
741
742 #[test]
743 fn min_max_nan_constant_folding() {
744 let mut ctx = Context::default();
745 for (lhs, rhs, expected_min, expected_max) in [
746 (f64::NAN, 3.0, f64::NAN, f64::NAN),
747 (3.0, f64::NAN, f64::NAN, f64::NAN),
748 (f64::NAN, f64::NAN, f64::NAN, f64::NAN),
749 (-2.0, 3.0, -2.0, 3.0),
750 ] {
751 let ([lhs, rhs], attrs) = constants(&mut ctx, [lhs, rhs]);
752 let min = FMinNanOp::new(&mut ctx, lhs, rhs).check_fold(&ctx, &attrs);
753 let max = FMaxNanOp::new(&mut ctx, lhs, rhs).check_fold(&ctx, &attrs);
754 assert_folded(&ctx, &min, expected_min);
755 assert_folded(&ctx, &max, expected_max);
756 }
757 }
758
759 #[test]
760 fn clamp_nan_constant_folding() {
761 let mut ctx = Context::default();
762 for (input, min, max, expected) in [
763 (f64::NAN, 0.0, 1.0, f64::NAN),
764 (0.5, f64::NAN, 1.0, f64::NAN),
765 (0.5, 0.0, f64::NAN, f64::NAN),
766 (2.0, 0.0, 1.0, 1.0),
767 ] {
768 let ([input, min, max], attrs) = constants(&mut ctx, [input, min, max]);
769 let folded = FClampNanOp::new(&mut ctx, input, min, max).check_fold(&ctx, &attrs);
770 assert_folded(&ctx, &folded, expected);
771 }
772 }
773}