naga/proc/
type_methods.rs

1//! Methods on or related to [`TypeInner`], [`Scalar`], [`ScalarKind`], and [`VectorSize`].
2//!
3//! [`TypeInner`]: crate::TypeInner
4//! [`Scalar`]: crate::Scalar
5//! [`ScalarKind`]: crate::ScalarKind
6//! [`VectorSize`]: crate::VectorSize
7
8use crate::{ir, valid::MAX_TYPE_SIZE};
9
10use super::TypeResolution;
11
12impl crate::ScalarKind {
13    pub const fn is_numeric(self) -> bool {
14        match self {
15            crate::ScalarKind::Sint
16            | crate::ScalarKind::Uint
17            | crate::ScalarKind::Float
18            | crate::ScalarKind::AbstractInt
19            | crate::ScalarKind::AbstractFloat => true,
20            crate::ScalarKind::Bool => false,
21        }
22    }
23}
24
25impl crate::Scalar {
26    pub const I16: Self = Self {
27        kind: crate::ScalarKind::Sint,
28        width: 2,
29    };
30    pub const U16: Self = Self {
31        kind: crate::ScalarKind::Uint,
32        width: 2,
33    };
34    pub const I32: Self = Self {
35        kind: crate::ScalarKind::Sint,
36        width: 4,
37    };
38    pub const U32: Self = Self {
39        kind: crate::ScalarKind::Uint,
40        width: 4,
41    };
42    pub const F16: Self = Self {
43        kind: crate::ScalarKind::Float,
44        width: 2,
45    };
46    pub const F32: Self = Self {
47        kind: crate::ScalarKind::Float,
48        width: 4,
49    };
50    pub const F64: Self = Self {
51        kind: crate::ScalarKind::Float,
52        width: 8,
53    };
54    pub const I64: Self = Self {
55        kind: crate::ScalarKind::Sint,
56        width: 8,
57    };
58    pub const U64: Self = Self {
59        kind: crate::ScalarKind::Uint,
60        width: 8,
61    };
62    pub const BOOL: Self = Self {
63        kind: crate::ScalarKind::Bool,
64        width: crate::BOOL_WIDTH,
65    };
66    pub const ABSTRACT_INT: Self = Self {
67        kind: crate::ScalarKind::AbstractInt,
68        width: crate::ABSTRACT_WIDTH,
69    };
70    pub const ABSTRACT_FLOAT: Self = Self {
71        kind: crate::ScalarKind::AbstractFloat,
72        width: crate::ABSTRACT_WIDTH,
73    };
74
75    pub const fn is_abstract(self) -> bool {
76        match self.kind {
77            crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => true,
78            crate::ScalarKind::Sint
79            | crate::ScalarKind::Uint
80            | crate::ScalarKind::Float
81            | crate::ScalarKind::Bool => false,
82        }
83    }
84
85    /// Construct a float `Scalar` with the given width.
86    ///
87    /// This is especially common when dealing with
88    /// `TypeInner::Matrix`, where the scalar kind is implicit.
89    pub const fn float(width: crate::Bytes) -> Self {
90        Self {
91            kind: crate::ScalarKind::Float,
92            width,
93        }
94    }
95
96    pub const fn to_inner_scalar(self) -> crate::TypeInner {
97        crate::TypeInner::Scalar(self)
98    }
99
100    pub const fn to_inner_vector(self, size: crate::VectorSize) -> crate::TypeInner {
101        crate::TypeInner::Vector { size, scalar: self }
102    }
103
104    pub const fn to_inner_atomic(self) -> crate::TypeInner {
105        crate::TypeInner::Atomic(self)
106    }
107}
108
109/// Produce all concrete integer [`ir::Scalar`]s.
110///
111/// Note that `I32` and `U32` must come first; this represents conversion rank
112/// in overload resolution.
113pub fn concrete_int_scalars() -> impl Iterator<Item = ir::Scalar> {
114    [
115        ir::Scalar::I32,
116        ir::Scalar::U32,
117        ir::Scalar::I16,
118        ir::Scalar::U16,
119        ir::Scalar::I64,
120        ir::Scalar::U64,
121    ]
122    .into_iter()
123}
124
125/// Produce all vector sizes.
126pub fn vector_sizes() -> impl Iterator<Item = ir::VectorSize> + Clone {
127    static SIZES: [ir::VectorSize; 3] = [
128        ir::VectorSize::Bi,
129        ir::VectorSize::Tri,
130        ir::VectorSize::Quad,
131    ];
132
133    SIZES.iter().cloned()
134}
135
136const POINTER_SPAN: u32 = 4;
137
138impl crate::TypeInner {
139    /// Return the scalar type of `self`.
140    ///
141    /// If `inner` is a scalar, vector, or matrix type, return
142    /// its scalar type. Otherwise, return `None`.
143    ///
144    /// Note that this doesn't inspect [`Array`] types, as required
145    /// for automatic conversions. For that, see [`scalar_for_conversions`].
146    ///
147    /// [`Array`]: crate::TypeInner::Array
148    /// [`scalar_for_conversions`]: crate::TypeInner::scalar_for_conversions
149    pub const fn scalar(&self) -> Option<crate::Scalar> {
150        use crate::TypeInner as Ti;
151        match *self {
152            Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => Some(scalar),
153            Ti::Matrix { scalar, .. } => Some(scalar),
154            Ti::CooperativeMatrix { scalar, .. } => Some(scalar),
155            _ => None,
156        }
157    }
158
159    pub fn scalar_kind(&self) -> Option<crate::ScalarKind> {
160        self.scalar().map(|scalar| scalar.kind)
161    }
162
163    /// Returns the scalar width in bytes
164    pub fn scalar_width(&self) -> Option<u8> {
165        self.scalar().map(|scalar| scalar.width)
166    }
167
168    /// Return the leaf scalar type of `self`, as needed for automatic conversions.
169    ///
170    /// Unlike the [`scalar`] method, which only retrieves scalars for
171    /// [`Scalar`], [`Vector`], and [`Matrix`] this also looks into
172    /// [`Array`] types to find the leaf scalar.
173    ///
174    /// [`scalar`]: crate::TypeInner::scalar
175    /// [`Scalar`]: crate::TypeInner::Scalar
176    /// [`Vector`]: crate::TypeInner::Vector
177    /// [`Matrix`]: crate::TypeInner::Matrix
178    /// [`Array`]: crate::TypeInner::Array
179    pub fn scalar_for_conversions(
180        &self,
181        types: &crate::UniqueArena<crate::Type>,
182    ) -> Option<crate::Scalar> {
183        use crate::TypeInner as Ti;
184        match *self {
185            Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
186                Some(scalar)
187            }
188            Ti::Array { base, .. } => types[base].inner.scalar_for_conversions(types),
189            _ => None,
190        }
191    }
192
193    pub const fn pointer_space(&self) -> Option<crate::AddressSpace> {
194        match *self {
195            Self::Pointer { space, .. } => Some(space),
196            Self::ValuePointer { space, .. } => Some(space),
197            _ => None,
198        }
199    }
200
201    /// If `self` is a pointer type, return its base type.
202    pub const fn pointer_base_type(&self) -> Option<TypeResolution> {
203        match *self {
204            crate::TypeInner::Pointer { base, .. } => Some(TypeResolution::Handle(base)),
205            crate::TypeInner::ValuePointer {
206                size: None, scalar, ..
207            } => Some(TypeResolution::Value(crate::TypeInner::Scalar(scalar))),
208            crate::TypeInner::ValuePointer {
209                size: Some(size),
210                scalar,
211                ..
212            } => Some(TypeResolution::Value(crate::TypeInner::Vector {
213                size,
214                scalar,
215            })),
216            _ => None,
217        }
218    }
219
220    pub fn is_atomic_pointer(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
221        match *self {
222            Self::Pointer { base, .. } => match types[base].inner {
223                Self::Atomic { .. } => true,
224                _ => false,
225            },
226            _ => false,
227        }
228    }
229
230    /// Attempt to calculate the size of this type. Returns `None` if the size
231    /// exceeds the limit of [`crate::valid::MAX_TYPE_SIZE`].
232    pub fn try_size(&self, gctx: super::GlobalCtx) -> Option<u32> {
233        match *self {
234            Self::Scalar(scalar) | Self::Atomic(scalar) => Some(scalar.width as u32),
235            Self::Vector { size, scalar } => Some(size as u32 * scalar.width as u32),
236            // matrices are treated as arrays of aligned columns
237            Self::Matrix {
238                columns,
239                rows,
240                scalar,
241            } => Some(super::Alignment::from(rows) * scalar.width as u32 * columns as u32),
242            Self::CooperativeMatrix {
243                columns,
244                rows,
245                scalar,
246                role: _,
247            } => Some(columns as u32 * rows as u32 * scalar.width as u32),
248            Self::Pointer { .. } | Self::ValuePointer { .. } => Some(POINTER_SPAN),
249            Self::Array {
250                base: _,
251                size,
252                stride,
253            } => {
254                let count = match size.resolve(gctx) {
255                    Ok(crate::proc::IndexableLength::Known(count)) => count,
256                    // any struct member or array element needing a size at pipeline-creation time
257                    // must have a creation-fixed footprint
258                    Err(_) => 0,
259                    // A dynamically-sized array has to have at least one element
260                    Ok(crate::proc::IndexableLength::Dynamic) => 1,
261                };
262                if count > MAX_TYPE_SIZE {
263                    // It shouldn't be possible to have an array of a zero-sized type, but
264                    // let's check just in case.
265                    None
266                } else {
267                    count
268                        .checked_mul(stride)
269                        .filter(|size| *size <= MAX_TYPE_SIZE)
270                }
271            }
272            Self::Struct { span, .. } => Some(span),
273            Self::Image { .. }
274            | Self::Sampler { .. }
275            | Self::AccelerationStructure { .. }
276            | Self::RayQuery { .. }
277            | Self::BindingArray { .. } => Some(0),
278        }
279    }
280
281    /// Get the size of this type.
282    ///
283    /// Panics if the size exceeds the limit of [`crate::valid::MAX_TYPE_SIZE`].
284    /// Validated modules should not contain such types. Code working with
285    /// modules prior to validation should use [`Self::try_size`] and handle the
286    /// error appropriately.
287    pub fn size(&self, gctx: super::GlobalCtx) -> u32 {
288        self.try_size(gctx).expect("type is too large")
289    }
290
291    /// Return the canonical form of `self`, or `None` if it's already in
292    /// canonical form.
293    ///
294    /// Certain types have multiple representations in `TypeInner`. This
295    /// function converts all forms of equivalent types to a single
296    /// representative of their class, so that simply applying `Eq` to the
297    /// result indicates whether the types are equivalent, as far as Naga IR is
298    /// concerned.
299    pub fn canonical_form(
300        &self,
301        types: &crate::UniqueArena<crate::Type>,
302    ) -> Option<crate::TypeInner> {
303        use crate::TypeInner as Ti;
304        match *self {
305            Ti::Pointer { base, space } => match types[base].inner {
306                Ti::Scalar(scalar) => Some(Ti::ValuePointer {
307                    size: None,
308                    scalar,
309                    space,
310                }),
311                Ti::Vector { size, scalar } => Some(Ti::ValuePointer {
312                    size: Some(size),
313                    scalar,
314                    space,
315                }),
316                _ => None,
317            },
318            _ => None,
319        }
320    }
321
322    /// Compare value type `self` and `rhs` as types.
323    ///
324    /// This is mostly the same as `<TypeInner as Eq>::eq`, but it treats
325    /// [`ValuePointer`] and [`Pointer`] types as equivalent. This method
326    /// cannot be used for structs, because it cannot distinguish two
327    /// structs with different names but the same members. For structs,
328    /// use [`compare_types`].
329    ///
330    /// When you know that one side of the comparison is never a pointer or
331    /// struct, it's fine to not bother with canonicalization, and just
332    /// compare `TypeInner` values with `==`.
333    ///
334    /// # Panics
335    ///
336    /// If both `self` and `rhs` are structs.
337    ///
338    /// [`compare_types`]: crate::proc::compare_types
339    /// [`ValuePointer`]: ir::TypeInner::ValuePointer
340    /// [`Pointer`]: ir::TypeInner::Pointer
341    pub fn non_struct_equivalent(
342        &self,
343        rhs: &ir::TypeInner,
344        types: &crate::UniqueArena<crate::Type>,
345    ) -> bool {
346        let left = self.canonical_form(types);
347        let right = rhs.canonical_form(types);
348
349        let left_struct = matches!(*self, ir::TypeInner::Struct { .. });
350        let right_struct = matches!(*rhs, ir::TypeInner::Struct { .. });
351
352        assert!(!left_struct || !right_struct);
353
354        left.as_ref().unwrap_or(self) == right.as_ref().unwrap_or(rhs)
355    }
356
357    /// Returns true if `self` is runtime- or override-sized.
358    pub fn is_dynamically_sized(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
359        use crate::TypeInner as Ti;
360        match *self {
361            Ti::Array {
362                size: crate::ArraySize::Constant(_),
363                ..
364            } => false,
365            Ti::Array {
366                size: crate::ArraySize::Pending(_) | crate::ArraySize::Dynamic,
367                ..
368            } => true,
369            Ti::Struct { ref members, .. } => members
370                .last()
371                .map(|last| types[last.ty].inner.is_dynamically_sized(types))
372                .unwrap_or(false),
373            _ => false,
374        }
375    }
376
377    /// True when host code may need a storage buffer byte size for this type, for bounds checks
378    /// or WGSL `arrayLength`. Includes runtime-sized arrays and `binding_array` of element types
379    /// that qualify.
380    ///
381    /// The Metal backend treats this like “needs array length metadata”: it decides which globals
382    /// get entries in the synthesized `_mslBufferSizes` struct that those entry points take.
383    ///
384    /// A struct matches only if its last field is a runtime-sized array. This method does not open
385    /// a trailing struct to look at its members. [`Self::is_dynamically_sized`] does.
386    pub fn needs_host_buffer_byte_size(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
387        use crate::TypeInner as Ti;
388        match *self {
389            Ti::Struct { ref members, .. } => members.last().is_some_and(|m| {
390                matches!(
391                    types[m.ty].inner,
392                    Ti::Array {
393                        size: crate::ArraySize::Dynamic,
394                        ..
395                    }
396                )
397            }),
398            Ti::Array {
399                size: crate::ArraySize::Dynamic,
400                ..
401            } => true,
402            Ti::BindingArray { base, .. } => types[base].inner.needs_host_buffer_byte_size(types),
403            _ => false,
404        }
405    }
406
407    /// Returns true if `self` is a constructible type.
408    pub fn is_constructible(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
409        use crate::TypeInner as Ti;
410        match *self {
411            Ti::Array { base, size, .. } => {
412                let fixed_size = match size {
413                    ir::ArraySize::Constant(_) => true,
414                    ir::ArraySize::Pending(_) | ir::ArraySize::Dynamic => false,
415                };
416                fixed_size && types[base].inner.is_constructible(types)
417            }
418            Ti::Struct { ref members, .. } => members
419                .iter()
420                .all(|member| types[member.ty].inner.is_constructible(types)),
421            Ti::Atomic(_)
422            | Ti::Pointer { .. }
423            | Ti::ValuePointer { .. }
424            | Ti::Image { .. }
425            | Ti::Sampler { .. }
426            | Ti::AccelerationStructure { .. }
427            | Ti::BindingArray { .. } => false,
428            Ti::Scalar(_)
429            | Ti::Vector { .. }
430            | Ti::Matrix { .. }
431            | Ti::RayQuery { .. }
432            | Ti::CooperativeMatrix { .. } => true,
433        }
434    }
435
436    pub const fn components(&self) -> Option<u32> {
437        Some(match *self {
438            Self::Vector { size, .. } => size as u32,
439            Self::Matrix { columns, .. } => columns as u32,
440            Self::Array {
441                size: crate::ArraySize::Constant(len),
442                ..
443            } => len.get(),
444            Self::Struct { ref members, .. } => members.len() as u32,
445            _ => return None,
446        })
447    }
448
449    pub fn component_type(&self, index: usize) -> Option<TypeResolution> {
450        Some(match *self {
451            Self::Vector { scalar, .. } => TypeResolution::Value(crate::TypeInner::Scalar(scalar)),
452            Self::Matrix { rows, scalar, .. } => {
453                TypeResolution::Value(crate::TypeInner::Vector { size: rows, scalar })
454            }
455            Self::Array {
456                base,
457                size: crate::ArraySize::Constant(_),
458                ..
459            } => TypeResolution::Handle(base),
460            Self::Struct { ref members, .. } => TypeResolution::Handle(members[index].ty),
461            _ => return None,
462        })
463    }
464
465    /// If the type is a scalar or vector (not a matrix), return a tuple of the vector
466    /// size (or `None` for scalars), and the scalar kind. Returns `None` for other types.
467    pub const fn vector_size_and_scalar(
468        &self,
469    ) -> Option<(Option<crate::VectorSize>, crate::Scalar)> {
470        match *self {
471            crate::TypeInner::Scalar(scalar) => Some((None, scalar)),
472            crate::TypeInner::Vector { size, scalar } => Some((Some(size), scalar)),
473            crate::TypeInner::Matrix { .. }
474            | crate::TypeInner::CooperativeMatrix { .. }
475            | crate::TypeInner::Atomic(_)
476            | crate::TypeInner::Pointer { .. }
477            | crate::TypeInner::ValuePointer { .. }
478            | crate::TypeInner::Array { .. }
479            | crate::TypeInner::Struct { .. }
480            | crate::TypeInner::Image { .. }
481            | crate::TypeInner::Sampler { .. }
482            | crate::TypeInner::AccelerationStructure { .. }
483            | crate::TypeInner::RayQuery { .. }
484            | crate::TypeInner::BindingArray { .. } => None,
485        }
486    }
487
488    /// Return true if `self` is an abstract type.
489    ///
490    /// Use `types` to look up type handles. This is necessary to
491    /// recognize abstract arrays.
492    pub fn is_abstract(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
493        match *self {
494            crate::TypeInner::Scalar(scalar)
495            | crate::TypeInner::Vector { scalar, .. }
496            | crate::TypeInner::Matrix { scalar, .. }
497            | crate::TypeInner::Atomic(scalar) => scalar.is_abstract(),
498            crate::TypeInner::Array { base, .. } => types[base].inner.is_abstract(types),
499            crate::TypeInner::CooperativeMatrix { .. }
500            | crate::TypeInner::ValuePointer { .. }
501            | crate::TypeInner::Pointer { .. }
502            | crate::TypeInner::Struct { .. }
503            | crate::TypeInner::Image { .. }
504            | crate::TypeInner::Sampler { .. }
505            | crate::TypeInner::AccelerationStructure { .. }
506            | crate::TypeInner::RayQuery { .. }
507            | crate::TypeInner::BindingArray { .. } => false,
508        }
509    }
510
511    /// Determine whether `self` automatically converts to `goal`.
512    ///
513    /// If Naga IR's automatic conversions will convert `self` to
514    /// `goal`, then return a pair `(from, to)`, where `from` and `to`
515    /// are the scalar types of the leaf values of `self` and `goal`.
516    ///
517    /// If `self` and `goal` are the same type, this will simply return
518    /// a pair `(S, S)`.
519    ///
520    /// If the automatic conversions cannot convert `self` to `goal`,
521    /// return `None`.
522    ///
523    /// Naga IR's automatic conversions will convert:
524    ///
525    /// - [`AbstractInt`] scalars to [`AbstractFloat`] or any numeric scalar type
526    ///
527    /// - [`AbstractFloat`] scalars to any floating-point scalar type
528    ///
529    /// - A [`Vector`] `{ size, scalar: S }` to `{ size, scalar: T }`
530    ///   if they would convert `S` to `T`
531    ///
532    /// - An [`Array`] `{ base: S, size, stride }` to `{ base: T, size, stride }`
533    ///   if they would convert `S` to `T`
534    ///
535    /// [`AbstractInt`]: crate::ScalarKind::AbstractInt
536    /// [`AbstractFloat`]: crate::ScalarKind::AbstractFloat
537    /// [`Vector`]: crate::TypeInner::Vector
538    /// [`Array`]: crate::TypeInner::Array
539    pub fn automatically_converts_to(
540        &self,
541        goal: &Self,
542        types: &crate::UniqueArena<crate::Type>,
543    ) -> Option<(crate::Scalar, crate::Scalar)> {
544        use crate::ScalarKind as Sk;
545        use crate::TypeInner as Ti;
546
547        // Automatic conversions only change the scalar type of a value's leaves
548        // (e.g., `vec4<AbstractFloat>` to `vec4<f32>`), never the type
549        // constructors applied to those scalar types (e.g., never scalar to
550        // `vec4`, or `vec2` to `vec3`). So first we check that the type
551        // constructors match, extracting the leaf scalar types in the process.
552        let expr_scalar;
553        let goal_scalar;
554        match (self, goal) {
555            (&Ti::Scalar(expr), &Ti::Scalar(goal)) => {
556                expr_scalar = expr;
557                goal_scalar = goal;
558            }
559            (
560                &Ti::Vector {
561                    size: expr_size,
562                    scalar: expr,
563                },
564                &Ti::Vector {
565                    size: goal_size,
566                    scalar: goal,
567                },
568            ) if expr_size == goal_size => {
569                expr_scalar = expr;
570                goal_scalar = goal;
571            }
572            (
573                &Ti::Matrix {
574                    rows: expr_rows,
575                    columns: expr_columns,
576                    scalar: expr,
577                },
578                &Ti::Matrix {
579                    rows: goal_rows,
580                    columns: goal_columns,
581                    scalar: goal,
582                },
583            ) if expr_rows == goal_rows && expr_columns == goal_columns => {
584                expr_scalar = expr;
585                goal_scalar = goal;
586            }
587            (
588                &Ti::Array {
589                    base: expr_base,
590                    size: expr_size,
591                    stride: _,
592                },
593                &Ti::Array {
594                    base: goal_base,
595                    size: goal_size,
596                    stride: _,
597                },
598            ) if expr_size == goal_size => {
599                return types[expr_base]
600                    .inner
601                    .automatically_converts_to(&types[goal_base].inner, types);
602            }
603            _ => return None,
604        }
605
606        match (expr_scalar.kind, goal_scalar.kind) {
607            (Sk::AbstractFloat, Sk::Float) => {}
608            (Sk::AbstractInt, Sk::Sint | Sk::Uint | Sk::AbstractFloat | Sk::Float) => {}
609            _ => return None,
610        }
611
612        log::trace!("      okay: expr {expr_scalar:?}, goal {goal_scalar:?}");
613        Some((expr_scalar, goal_scalar))
614    }
615}
616
617/// Helper trait for providing the min and max values exactly representable by
618/// the integer type `Self` and floating point type `F`.
619pub trait IntFloatLimits<F>
620where
621    F: num_traits::Float,
622{
623    /// Returns the minimum value exactly representable by the integer type
624    /// `Self` and floating point type `F`.
625    fn min_float() -> F;
626    /// Returns the maximum value exactly representable by the integer type
627    /// `Self` and floating point type `F`.
628    fn max_float() -> F;
629}
630
631macro_rules! define_int_float_limits {
632    ($int:ty, $float:ty, $min:expr, $max:expr) => {
633        impl IntFloatLimits<$float> for $int {
634            fn min_float() -> $float {
635                $min
636            }
637            fn max_float() -> $float {
638                $max
639            }
640        }
641    };
642}
643
644// i16 range [-32768, 32767] fits exactly in f16 (max 65504), f32, and f64.
645// u16 range [0, 65535] fits exactly in f32 and f64. For f16, max exactly
646// representable is 65504 (f16::MAX).
647define_int_float_limits!(i16, half::f16, half::f16::MIN, half::f16::MAX);
648define_int_float_limits!(u16, half::f16, half::f16::ZERO, half::f16::MAX);
649define_int_float_limits!(i16, f32, -32768.0f32, 32767.0f32);
650define_int_float_limits!(u16, f32, 0.0f32, 65535.0f32);
651define_int_float_limits!(i16, f64, -32768.0f64, 32767.0f64);
652define_int_float_limits!(u16, f64, 0.0f64, 65535.0f64);
653define_int_float_limits!(i32, half::f16, half::f16::MIN, half::f16::MAX);
654define_int_float_limits!(u32, half::f16, half::f16::ZERO, half::f16::MAX);
655define_int_float_limits!(i64, half::f16, half::f16::MIN, half::f16::MAX);
656define_int_float_limits!(u64, half::f16, half::f16::ZERO, half::f16::MAX);
657define_int_float_limits!(i32, f32, -2147483648.0f32, 2147483520.0f32);
658define_int_float_limits!(u32, f32, 0.0f32, 4294967040.0f32);
659define_int_float_limits!(
660    i64,
661    f32,
662    -9223372036854775808.0f32,
663    9223371487098961920.0f32
664);
665define_int_float_limits!(u64, f32, 0.0f32, 18446742974197923840.0f32);
666define_int_float_limits!(i32, f64, -2147483648.0f64, 2147483647.0f64);
667define_int_float_limits!(u32, f64, 0.0f64, 4294967295.0f64);
668define_int_float_limits!(
669    i64,
670    f64,
671    -9223372036854775808.0f64,
672    9223372036854774784.0f64
673);
674define_int_float_limits!(u64, f64, 0.0f64, 18446744073709549568.0f64);
675
676/// Returns a tuple of [`crate::Literal`]s representing the minimum and maximum
677/// float values exactly representable by the provided float and integer types.
678/// Panics if `float` is not one of `F16`, `F32`, or `F64`, or `int` is
679/// not one of `I16`, `U16`, `I32`, `U32`, `I64`, or `U64`.
680pub fn min_max_float_representable_by(
681    float: crate::Scalar,
682    int: crate::Scalar,
683) -> (crate::Literal, crate::Literal) {
684    match (float, int) {
685        (crate::Scalar::F16, crate::Scalar::I16) => (
686            crate::Literal::F16(i16::min_float()),
687            crate::Literal::F16(i16::max_float()),
688        ),
689        (crate::Scalar::F16, crate::Scalar::U16) => (
690            crate::Literal::F16(u16::min_float()),
691            crate::Literal::F16(u16::max_float()),
692        ),
693        (crate::Scalar::F16, crate::Scalar::I32) => (
694            crate::Literal::F16(i32::min_float()),
695            crate::Literal::F16(i32::max_float()),
696        ),
697        (crate::Scalar::F16, crate::Scalar::U32) => (
698            crate::Literal::F16(u32::min_float()),
699            crate::Literal::F16(u32::max_float()),
700        ),
701        (crate::Scalar::F16, crate::Scalar::I64) => (
702            crate::Literal::F16(i64::min_float()),
703            crate::Literal::F16(i64::max_float()),
704        ),
705        (crate::Scalar::F16, crate::Scalar::U64) => (
706            crate::Literal::F16(u64::min_float()),
707            crate::Literal::F16(u64::max_float()),
708        ),
709        (crate::Scalar::F32, crate::Scalar::I16) => (
710            crate::Literal::F32(i16::min_float()),
711            crate::Literal::F32(i16::max_float()),
712        ),
713        (crate::Scalar::F32, crate::Scalar::U16) => (
714            crate::Literal::F32(u16::min_float()),
715            crate::Literal::F32(u16::max_float()),
716        ),
717        (crate::Scalar::F32, crate::Scalar::I32) => (
718            crate::Literal::F32(i32::min_float()),
719            crate::Literal::F32(i32::max_float()),
720        ),
721        (crate::Scalar::F32, crate::Scalar::U32) => (
722            crate::Literal::F32(u32::min_float()),
723            crate::Literal::F32(u32::max_float()),
724        ),
725        (crate::Scalar::F32, crate::Scalar::I64) => (
726            crate::Literal::F32(i64::min_float()),
727            crate::Literal::F32(i64::max_float()),
728        ),
729        (crate::Scalar::F32, crate::Scalar::U64) => (
730            crate::Literal::F32(u64::min_float()),
731            crate::Literal::F32(u64::max_float()),
732        ),
733        (crate::Scalar::F64, crate::Scalar::I16) => (
734            crate::Literal::F64(i16::min_float()),
735            crate::Literal::F64(i16::max_float()),
736        ),
737        (crate::Scalar::F64, crate::Scalar::U16) => (
738            crate::Literal::F64(u16::min_float()),
739            crate::Literal::F64(u16::max_float()),
740        ),
741        (crate::Scalar::F64, crate::Scalar::I32) => (
742            crate::Literal::F64(i32::min_float()),
743            crate::Literal::F64(i32::max_float()),
744        ),
745        (crate::Scalar::F64, crate::Scalar::U32) => (
746            crate::Literal::F64(u32::min_float()),
747            crate::Literal::F64(u32::max_float()),
748        ),
749        (crate::Scalar::F64, crate::Scalar::I64) => (
750            crate::Literal::F64(i64::min_float()),
751            crate::Literal::F64(i64::max_float()),
752        ),
753        (crate::Scalar::F64, crate::Scalar::U64) => (
754            crate::Literal::F64(u64::min_float()),
755            crate::Literal::F64(u64::max_float()),
756        ),
757        _ => unreachable!(),
758    }
759}
760
761/// Helper function that returns the string corresponding to the [`VectorSize`](crate::VectorSize)
762pub const fn vector_size_str(size: crate::VectorSize) -> &'static str {
763    match size {
764        crate::VectorSize::Bi => "2",
765        crate::VectorSize::Tri => "3",
766        crate::VectorSize::Quad => "4",
767    }
768}