Skip to main content

cubecl_ir/dialect/
cmp.rs

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, ConstantValue, Pure,
10    attributes::{BoolAttr, FloatAttr, IndexAttr, IntAttrExt},
11    dialect::{base::pure_binop, math::int_attr},
12    interfaces::{TriviallyUnrollable, TypedExt},
13    prelude::*,
14    types::{VectorType, scalar::BoolType},
15};
16
17pure_binop!("cmp.s_min", SMinOp);
18const_eval!(SMinOp, {
19    [IndexAttr, IntegerAttr(i8, i16, i32, i64)]: |lhs, rhs| lhs.min(rhs),
20    // min(min_int, x) -> min_int
21    custom: |lhs, rhs| {
22        let const_val = lhs.or(rhs)?;
23        let ty = const_val.get_type(ctx);
24        match const_val.as_const_val(ctx) {
25            ConstantValue::Int(val) if is_min_int(ctx, ty, val) => Some(int_attr(ctx, ty, val as i128)),
26            _ => None
27        }
28    }
29});
30simplify!(SMinOp, {
31    // min(max_int, x) -> x
32    |lhs, _| {
33        let ty = lhs?.get_type(ctx);
34        match lhs?.as_const_val(ctx) {
35            ConstantValue::Int(val) if is_max_int(ctx, ty, val) => Some(self.rhs(ctx)),
36            _ => None,
37        }
38    },
39    // min(x, max_int) -> x
40    |_, rhs| {
41        let ty = rhs?.get_type(ctx);
42        match rhs?.as_const_val(ctx) {
43            ConstantValue::Int(val) if is_max_int(ctx, ty, val) => Some(self.lhs(ctx)),
44            _ => None,
45        }
46    },
47    // min(x, x) -> x
48    |_, _| match self.lhs(ctx) == self.rhs(ctx) {
49        true => Some(self.lhs(ctx)),
50        false => None
51    }
52});
53
54pure_binop!("cmp.u_min", UMinOp);
55const_eval!(UMinOp, {
56    [IndexAttr, IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| lhs.min(rhs),
57    // min(min_int, x) -> min_int
58    custom: |lhs, rhs| {
59        let const_val = lhs.or(rhs)?;
60        let ty = const_val.get_type(ctx);
61        match const_val.as_const_val(ctx) {
62            ConstantValue::UInt(0) => Some(int_attr(ctx, ty, 0)),
63            _ => None
64        }
65    }
66});
67simplify!(UMinOp, {
68    // min(max_int, x) -> x
69    |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    // min(x, max_int) -> x
77    |_, 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    // min(x, x) -> x
85    |_, _| 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);
92const_eval!(FMinOp, { [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| lhs.min(rhs) });
93simplify!(FMinOp, {
94    // min(x, x) -> x
95    |_, _| match self.lhs(ctx) == self.rhs(ctx) {
96        true => Some(self.lhs(ctx)),
97        false => None,
98    }
99});
100
101pure_binop!("cmp.s_max", SMaxOp);
102const_eval!(SMaxOp, {
103    [IndexAttr, IntegerAttr(i8, i16, i32, i64)]: |lhs, rhs| lhs.max(rhs),
104    // max(max_int, x) -> max_int
105    custom: |lhs, rhs| {
106        let const_val = lhs.or(rhs)?;
107        let ty = const_val.get_type(ctx);
108        match const_val.as_const_val(ctx) {
109            ConstantValue::Int(val) if is_max_int(ctx, ty, val) => Some(int_attr(ctx, ty, val as i128)),
110            _ => None
111        }
112    }
113});
114simplify!(SMaxOp, {
115    // max(min_int, x) -> x
116    |lhs, _| {
117        let ty = lhs?.get_type(ctx);
118        match lhs?.as_const_val(ctx) {
119            ConstantValue::Int(val) if is_min_int(ctx, ty, val) => Some(self.rhs(ctx)),
120            _ => None,
121        }
122    },
123    // max(x, min_int) -> x
124    |_, rhs| {
125        let ty = rhs?.get_type(ctx);
126        match rhs?.as_const_val(ctx) {
127            ConstantValue::Int(val) if is_min_int(ctx, ty, val) => Some(self.lhs(ctx)),
128            _ => None,
129        }
130    },
131    // max(x, x) -> x
132    |_, _| match self.lhs(ctx) == self.rhs(ctx) {
133        true => Some(self.lhs(ctx)),
134        false => None,
135    }
136});
137
138pure_binop!("cmp.u_max", UMaxOp);
139const_eval!(UMaxOp, {
140    [IndexAttr, IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| lhs.max(rhs),
141    // max(max_int, x) -> max_int
142    custom: |lhs, rhs| {
143        let const_val = lhs.or(rhs)?;
144        let ty = const_val.get_type(ctx);
145        match const_val.as_const_val(ctx) {
146            ConstantValue::UInt(val) if is_max_uint(ctx, ty, val) => Some(int_attr(ctx, ty, val as i128)),
147            _ => None
148        }
149    }
150});
151simplify!(UMaxOp, {
152    // max(min_int, x) -> x
153    |lhs, _| {
154        match lhs?.as_const_val(ctx) {
155            ConstantValue::UInt(0) => Some(self.rhs(ctx)),
156            _ => None,
157        }
158    },
159    // max(x, min_int) -> x
160    |_, rhs| {
161        match rhs?.as_const_val(ctx) {
162            ConstantValue::UInt(0) => Some(self.lhs(ctx)),
163            _ => None,
164        }
165    },
166    // max(x, x) -> x
167    |_, _| match self.lhs(ctx) == self.rhs(ctx) {
168        true => Some(self.lhs(ctx)),
169        false => None,
170    }
171});
172
173pure_binop!("cmp.f_max", FMaxOp);
174const_eval!(FMaxOp, {
175    [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| lhs.max(rhs),
176});
177simplify!(FMaxOp, {
178    // max(x, x) -> x
179    |_, _| 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)]
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    // clamp(x, max_int, y) -> max_int
197    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    // clamp(x, y, min_int) -> min_int
205    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)]
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    // clamp(x, max_int, y) -> max_int
226    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    // clamp(x, y, min_int) -> min_int
234    custom: |_, _, max| {
235        let ty = max?.get_type(ctx);
236        match max?.as_const_val(ctx) {
237            ConstantValue::UInt(0) => Some(int_attr(ctx, ty, 0)),
238            _ => None
239        }
240    }
241});
242
243#[cube_op(name = "cmp.f_clamp")]
244#[result_ty(same_as = input)]
245#[op_interfaces(SameOperandsType, SameOperandsAndResultType, TriviallyUnrollable)]
246#[op_traits(Pure, CanMaterialize)]
247pub struct FClampOp {
248    pub input: Value,
249    pub min: Value,
250    pub max: Value,
251}
252const_eval!(FClampOp, {
253    [FloatAttr(f16, bf16, f32, f64)]: |inp, min, max| inp.clamp(min, max),
254});
255
256macro_rules! cmp_binop {
257    ($name: literal, $ty: ident) => {
258        #[cubecl_macros_internal::cube_op(name = $name)]
259        #[result_ty(from_inputs = cmp_result_ty)]
260        #[$crate::prelude::op_interfaces(SameOperandsType, TriviallyUnrollable)]
261        #[op_traits(Pure, CanMaterialize)]
262        pub struct $ty {
263            pub lhs: Value,
264            pub rhs: Value,
265        }
266    };
267}
268
269cmp_binop!("cmp.s_less_than", SLessThanOp);
270const_eval!(SLessThanOp, {
271    [IndexAttr, IntegerAttr(i8, i16, i32, i64)]: |lhs, rhs| -> BoolAttr {
272        (lhs < rhs).into()
273    },
274    // (x < x) -> false;
275    custom: |_, _| {
276        if self.lhs(ctx) == self.rhs(ctx) {
277            BoolAttr::per_lane(ctx, self.get_result(ctx), false)
278        } else {
279            None
280        }
281    },
282    // int < min_int -> false
283    custom: |_, rhs| {
284        let ty = rhs?.get_type(ctx);
285        match rhs?.as_const_val(ctx) {
286            ConstantValue::Int(val) if is_min_int(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
287            _ => None
288        }
289    },
290    // max_int < int -> false
291    custom: |lhs, _| {
292        let ty = lhs?.get_type(ctx);
293        match lhs?.as_const_val(ctx) {
294            ConstantValue::Int(val) if is_max_int(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
295            _ => None
296        }
297    },
298});
299
300cmp_binop!("cmp.u_less_than", ULessThanOp);
301const_eval!(ULessThanOp, {
302    [IndexAttr, IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| -> BoolAttr {
303        (lhs < rhs).into()
304    },
305    // (x < x) -> false;
306    custom: |_, _| {
307        if self.lhs(ctx) == self.rhs(ctx) {
308            BoolAttr::per_lane(ctx, self.get_result(ctx), false)
309        } else {
310            None
311        }
312    },
313    // int < min_int -> false
314    custom: |_, rhs| {
315        match rhs?.as_const_val(ctx) {
316            ConstantValue::UInt(0) => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
317            _ => None
318        }
319    },
320    // max_int < int -> false
321    custom: |lhs, _| {
322        let ty = lhs?.get_type(ctx);
323        match lhs?.as_const_val(ctx) {
324            ConstantValue::UInt(val) if is_max_uint(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
325            _ => None
326        }
327    },
328});
329
330cmp_binop!("cmp.f_less_than", FLessThanOp);
331const_eval!(FLessThanOp, {
332    [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| -> BoolAttr {
333        (lhs < rhs).into()
334    },
335});
336
337cmp_binop!("cmp.s_greater_than", SGreaterThanOp);
338const_eval!(SGreaterThanOp, {
339    [IndexAttr, IntegerAttr(i8, i16, i32, i64)]: |lhs, rhs| -> BoolAttr {
340        (lhs > rhs).into()
341    },
342    // (x > x) -> false;
343    custom: |_, _| {
344        if self.lhs(ctx) == self.rhs(ctx) {
345            BoolAttr::per_lane(ctx, self.get_result(ctx), false)
346        } else {
347            None
348        }
349    },
350    // min_int > int -> false
351    custom: |lhs, _| {
352        let ty = lhs?.get_type(ctx);
353        match lhs?.as_const_val(ctx) {
354            ConstantValue::Int(val) if is_min_int(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
355            _ => None
356        }
357    },
358    // int > max_int -> false
359    custom: |_, rhs| {
360        let ty = rhs?.get_type(ctx);
361        match rhs?.as_const_val(ctx) {
362            ConstantValue::Int(val) if is_max_int(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
363            _ => None
364        }
365    }
366});
367
368cmp_binop!("cmp.u_greater_than", UGreaterThanOp);
369const_eval!(UGreaterThanOp, {
370    [IndexAttr, IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| -> BoolAttr {
371        (lhs > rhs).into()
372    },
373    // (x > x) -> false
374    custom: |_, _| {
375        if self.lhs(ctx) == self.rhs(ctx) {
376            BoolAttr::per_lane(ctx, self.get_result(ctx), false)
377        } else {
378            None
379        }
380    },
381    // min_int > int -> false
382    custom: |lhs, _| {
383        match lhs?.as_const_val(ctx) {
384            ConstantValue::UInt(0) => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
385            _ => None
386        }
387    },
388    // int > max_int -> false
389    custom: |_, rhs| {
390        let ty = rhs?.get_type(ctx);
391        match rhs?.as_const_val(ctx) {
392            ConstantValue::UInt(val) if is_max_uint(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), false),
393            _ => None
394        }
395    }
396});
397
398cmp_binop!("cmp.f_greater_than", FGreaterThanOp);
399const_eval!(FGreaterThanOp, {
400    [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| -> BoolAttr {
401        (lhs > rhs).into()
402    },
403});
404
405cmp_binop!("cmp.s_less_than_or_equal", SLessThanOrEqualOp);
406const_eval!(SLessThanOrEqualOp, {
407    [IndexAttr, IntegerAttr(i8, i16, i32, i64)]: |lhs, rhs| -> BoolAttr {
408        (lhs <= rhs).into()
409    },
410    // (x <= x) -> true
411    custom: |_, _| {
412        if self.lhs(ctx) == self.rhs(ctx) {
413            BoolAttr::per_lane(ctx, self.get_result(ctx), true)
414        } else {
415            None
416        }
417    },
418    // min_int <= int -> true
419    custom: |lhs, _| {
420        let ty = lhs?.get_type(ctx);
421        match lhs?.as_const_val(ctx) {
422            ConstantValue::Int(val) if is_min_int(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
423            _ => None
424        }
425    },
426    // int <= max_int -> true
427    custom: |_, rhs| {
428        let ty = rhs?.get_type(ctx);
429        match rhs?.as_const_val(ctx) {
430            ConstantValue::Int(val) if is_max_int(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
431            _ => None
432        }
433    }
434});
435
436cmp_binop!("cmp.u_less_than_or_equal", ULessThanOrEqualOp);
437const_eval!(ULessThanOrEqualOp, {
438    [IndexAttr, IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| -> BoolAttr {
439        (lhs <= rhs).into()
440    },
441    // (x <= x) -> true
442    custom: |_, _| {
443        if self.lhs(ctx) == self.rhs(ctx) {
444            BoolAttr::per_lane(ctx, self.get_result(ctx), true)
445        } else {
446            None
447        }
448    },
449    // min_int <= int -> true
450    custom: |lhs, _| {
451        match lhs?.as_const_val(ctx) {
452            ConstantValue::UInt(0) => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
453            _ => None
454        }
455    },
456    // int <= max_int -> true
457    custom: |_, rhs| {
458        let ty = rhs?.get_type(ctx);
459        match rhs?.as_const_val(ctx) {
460            ConstantValue::UInt(val) if is_max_uint(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
461            _ => None
462        }
463    }
464});
465
466cmp_binop!("cmp.f_less_than_or_equal", FLessThanOrEqualOp);
467const_eval!(FLessThanOrEqualOp, {
468    [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| -> BoolAttr {
469        (lhs <= rhs).into()
470    },
471});
472
473cmp_binop!("cmp.s_greater_than_or_equal", SGreaterThanOrEqualOp);
474const_eval!(SGreaterThanOrEqualOp, {
475    [IndexAttr, IntegerAttr(i8, i16, i32, i64)]: |lhs, rhs| -> BoolAttr {
476        (lhs >= rhs).into()
477    },
478    // (x >= x) -> true
479    custom: |_, _| {
480        if self.lhs(ctx) == self.rhs(ctx) {
481            BoolAttr::per_lane(ctx, self.get_result(ctx), true)
482        } else {
483            None
484        }
485    },
486    // int >= min_int -> true
487    custom: |_, rhs| {
488        let ty = rhs?.get_type(ctx);
489        match rhs?.as_const_val(ctx) {
490            ConstantValue::Int(val) if is_min_int(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
491            _ => None
492        }
493    },
494    // max_int >= int -> true
495    custom: |lhs, _| {
496        let ty = lhs?.get_type(ctx);
497        match lhs?.as_const_val(ctx) {
498            ConstantValue::Int(val) if is_max_int(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
499            _ => None
500        }
501    }
502});
503
504cmp_binop!("cmp.u_greater_than_or_equal", UGreaterThanOrEqualOp);
505const_eval!(UGreaterThanOrEqualOp, {
506    [IndexAttr, IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| -> BoolAttr {
507        (lhs >= rhs).into()
508    },
509    // (x >= x) -> true
510    custom: |_, _| {
511        if self.lhs(ctx) == self.rhs(ctx) {
512            BoolAttr::per_lane(ctx, self.get_result(ctx), true)
513        } else {
514            None
515        }
516    },
517    // int >= min_int -> true
518    custom: |_, rhs| {
519        match rhs?.as_const_val(ctx) {
520            ConstantValue::UInt(0) => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
521            _ => None
522        }
523    },
524    // max_int >= int -> true
525    custom: |lhs, _| {
526        let ty = lhs?.get_type(ctx);
527        match lhs?.as_const_val(ctx) {
528            ConstantValue::UInt(val) if is_max_uint(ctx, ty, val) => BoolAttr::per_lane(ctx, self.get_result(ctx), true),
529            _ => None
530        }
531    }
532});
533
534cmp_binop!("cmp.f_greater_than_or_equal", FGreaterThanOrEqualOp);
535const_eval!(FGreaterThanOrEqualOp, {
536    [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| -> BoolAttr {
537        (lhs >= rhs).into()
538    },
539});
540
541cmp_binop!("cmp.i_equal", IEqualOp);
542const_eval!(IEqualOp, {
543    [IndexAttr, IntegerAttr(i8, i16, i32, i64), IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| -> BoolAttr {
544        (lhs == rhs).into()
545    },
546    // (x == x) -> true; exclude float
547    custom: |_, _| {
548        if self.lhs(ctx) == self.rhs(ctx) {
549            BoolAttr::per_lane(ctx, self.get_result(ctx), true)
550        } else {
551            None
552        }
553    }
554});
555
556cmp_binop!("cmp.f_equal", FEqualOp);
557const_eval!(FEqualOp, {
558    [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| -> BoolAttr {
559        (lhs == rhs).into()
560    },
561});
562
563cmp_binop!("cmp.bool_equal", BoolEqualOp);
564const_eval!(BoolEqualOp, {
565    [BoolAttr]: |lhs, rhs| lhs == rhs,
566    // (x == x) -> true; exclude float
567    custom: |_, _| {
568        if self.lhs(ctx) == self.rhs(ctx) {
569            BoolAttr::per_lane(ctx, self.get_result(ctx), true)
570        } else {
571            None
572        }
573    }
574});
575
576cmp_binop!("cmp.i_not_equal", INotEqualOp);
577const_eval!(INotEqualOp, {
578    [IndexAttr, IntegerAttr(i8, i16, i32, i64), IntegerAttr(u8, u16, u32, u64)]: |lhs, rhs| -> BoolAttr {
579        (lhs != rhs).into()
580    },
581    // (x != x) == false; exclude float
582    custom: |_, _| {
583        if self.lhs(ctx) == self.rhs(ctx) {
584            BoolAttr::per_lane(ctx, self.get_result(ctx), false)
585        } else {
586            None
587        }
588    }
589});
590
591cmp_binop!("cmp.f_not_equal", FNotEqualOp);
592const_eval!(FNotEqualOp, {
593    [FloatAttr(f16, bf16, f32, f64)]: |lhs, rhs| -> BoolAttr {
594        (lhs != rhs).into()
595    },
596});
597
598cmp_binop!("cmp.bool_not_equal", BoolNotEqualOp);
599const_eval!(BoolNotEqualOp, {
600    [BoolAttr]: |lhs, rhs| lhs != rhs,
601    // (x != x) == false; exclude float
602    custom: |_, _| {
603        if self.lhs(ctx) == self.rhs(ctx) {
604            BoolAttr::per_lane(ctx, self.get_result(ctx), false)
605        } else {
606            None
607        }
608    }
609});
610
611fn cmp_result_ty(ctx: &Context, lhs: &Value, _: &Value) -> TypeHandle {
612    let vectorization = lhs.vector_size(ctx);
613    let bool = BoolType::get(ctx).into();
614    if vectorization == 1 {
615        bool
616    } else {
617        VectorType::get(ctx, bool, vectorization).into()
618    }
619}
620
621pub(super) fn width(ctx: &Context, ty: TypeHandle) -> usize {
622    ty.size(ctx) * 8
623}
624
625fn is_min_int(ctx: &Context, ty: TypeHandle, val: i64) -> bool {
626    let ty = TypedHandle::<IntegerType>::from_handle(ty, ctx).unwrap();
627    val == min_int(ty.deref(ctx).width() as usize)
628}
629
630pub(super) fn is_max_int(ctx: &Context, ty: TypeHandle, val: i64) -> bool {
631    let ty = TypedHandle::<IntegerType>::from_handle(ty, ctx).unwrap();
632    val == max_int(ty.deref(ctx).width() as usize)
633}
634
635pub(super) fn is_max_uint(ctx: &Context, ty: TypeHandle, val: u64) -> bool {
636    val == max_uint(ty.size_bits(ctx))
637}
638
639fn min_int(width: usize) -> i64 {
640    if width >= 64 {
641        i64::MIN
642    } else {
643        -(1i64 << (width - 1))
644    }
645}
646
647fn max_int(width: usize) -> i64 {
648    if width >= 64 {
649        i64::MAX
650    } else {
651        (1i64 << (width - 1)) - 1
652    }
653}
654
655fn max_uint(width: usize) -> u64 {
656    if width >= 64 {
657        u64::MAX
658    } else {
659        (1u64 << width) - 1
660    }
661}