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    /// Returns true if `self` is a constructible type.
378    pub fn is_constructible(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
379        use crate::TypeInner as Ti;
380        match *self {
381            Ti::Array { base, size, .. } => {
382                let fixed_size = match size {
383                    ir::ArraySize::Constant(_) => true,
384                    ir::ArraySize::Pending(_) | ir::ArraySize::Dynamic => false,
385                };
386                fixed_size && types[base].inner.is_constructible(types)
387            }
388            Ti::Struct { ref members, .. } => members
389                .iter()
390                .all(|member| types[member.ty].inner.is_constructible(types)),
391            Ti::Atomic(_)
392            | Ti::Pointer { .. }
393            | Ti::ValuePointer { .. }
394            | Ti::Image { .. }
395            | Ti::Sampler { .. }
396            | Ti::AccelerationStructure { .. }
397            | Ti::BindingArray { .. } => false,
398            Ti::Scalar(_)
399            | Ti::Vector { .. }
400            | Ti::Matrix { .. }
401            | Ti::RayQuery { .. }
402            | Ti::CooperativeMatrix { .. } => true,
403        }
404    }
405
406    pub const fn components(&self) -> Option<u32> {
407        Some(match *self {
408            Self::Vector { size, .. } => size as u32,
409            Self::Matrix { columns, .. } => columns as u32,
410            Self::Array {
411                size: crate::ArraySize::Constant(len),
412                ..
413            } => len.get(),
414            Self::Struct { ref members, .. } => members.len() as u32,
415            _ => return None,
416        })
417    }
418
419    pub fn component_type(&self, index: usize) -> Option<TypeResolution> {
420        Some(match *self {
421            Self::Vector { scalar, .. } => TypeResolution::Value(crate::TypeInner::Scalar(scalar)),
422            Self::Matrix { rows, scalar, .. } => {
423                TypeResolution::Value(crate::TypeInner::Vector { size: rows, scalar })
424            }
425            Self::Array {
426                base,
427                size: crate::ArraySize::Constant(_),
428                ..
429            } => TypeResolution::Handle(base),
430            Self::Struct { ref members, .. } => TypeResolution::Handle(members[index].ty),
431            _ => return None,
432        })
433    }
434
435    /// If the type is a scalar or vector (not a matrix), return a tuple of the vector
436    /// size (or `None` for scalars), and the scalar kind. Returns `None` for other types.
437    pub const fn vector_size_and_scalar(
438        &self,
439    ) -> Option<(Option<crate::VectorSize>, crate::Scalar)> {
440        match *self {
441            crate::TypeInner::Scalar(scalar) => Some((None, scalar)),
442            crate::TypeInner::Vector { size, scalar } => Some((Some(size), scalar)),
443            crate::TypeInner::Matrix { .. }
444            | crate::TypeInner::CooperativeMatrix { .. }
445            | crate::TypeInner::Atomic(_)
446            | crate::TypeInner::Pointer { .. }
447            | crate::TypeInner::ValuePointer { .. }
448            | crate::TypeInner::Array { .. }
449            | crate::TypeInner::Struct { .. }
450            | crate::TypeInner::Image { .. }
451            | crate::TypeInner::Sampler { .. }
452            | crate::TypeInner::AccelerationStructure { .. }
453            | crate::TypeInner::RayQuery { .. }
454            | crate::TypeInner::BindingArray { .. } => None,
455        }
456    }
457
458    /// Return true if `self` is an abstract type.
459    ///
460    /// Use `types` to look up type handles. This is necessary to
461    /// recognize abstract arrays.
462    pub fn is_abstract(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
463        match *self {
464            crate::TypeInner::Scalar(scalar)
465            | crate::TypeInner::Vector { scalar, .. }
466            | crate::TypeInner::Matrix { scalar, .. }
467            | crate::TypeInner::Atomic(scalar) => scalar.is_abstract(),
468            crate::TypeInner::Array { base, .. } => types[base].inner.is_abstract(types),
469            crate::TypeInner::CooperativeMatrix { .. }
470            | crate::TypeInner::ValuePointer { .. }
471            | crate::TypeInner::Pointer { .. }
472            | crate::TypeInner::Struct { .. }
473            | crate::TypeInner::Image { .. }
474            | crate::TypeInner::Sampler { .. }
475            | crate::TypeInner::AccelerationStructure { .. }
476            | crate::TypeInner::RayQuery { .. }
477            | crate::TypeInner::BindingArray { .. } => false,
478        }
479    }
480
481    /// Determine whether `self` automatically converts to `goal`.
482    ///
483    /// If Naga IR's automatic conversions will convert `self` to
484    /// `goal`, then return a pair `(from, to)`, where `from` and `to`
485    /// are the scalar types of the leaf values of `self` and `goal`.
486    ///
487    /// If `self` and `goal` are the same type, this will simply return
488    /// a pair `(S, S)`.
489    ///
490    /// If the automatic conversions cannot convert `self` to `goal`,
491    /// return `None`.
492    ///
493    /// Naga IR's automatic conversions will convert:
494    ///
495    /// - [`AbstractInt`] scalars to [`AbstractFloat`] or any numeric scalar type
496    ///
497    /// - [`AbstractFloat`] scalars to any floating-point scalar type
498    ///
499    /// - A [`Vector`] `{ size, scalar: S }` to `{ size, scalar: T }`
500    ///   if they would convert `S` to `T`
501    ///
502    /// - An [`Array`] `{ base: S, size, stride }` to `{ base: T, size, stride }`
503    ///   if they would convert `S` to `T`
504    ///
505    /// [`AbstractInt`]: crate::ScalarKind::AbstractInt
506    /// [`AbstractFloat`]: crate::ScalarKind::AbstractFloat
507    /// [`Vector`]: crate::TypeInner::Vector
508    /// [`Array`]: crate::TypeInner::Array
509    pub fn automatically_converts_to(
510        &self,
511        goal: &Self,
512        types: &crate::UniqueArena<crate::Type>,
513    ) -> Option<(crate::Scalar, crate::Scalar)> {
514        use crate::ScalarKind as Sk;
515        use crate::TypeInner as Ti;
516
517        // Automatic conversions only change the scalar type of a value's leaves
518        // (e.g., `vec4<AbstractFloat>` to `vec4<f32>`), never the type
519        // constructors applied to those scalar types (e.g., never scalar to
520        // `vec4`, or `vec2` to `vec3`). So first we check that the type
521        // constructors match, extracting the leaf scalar types in the process.
522        let expr_scalar;
523        let goal_scalar;
524        match (self, goal) {
525            (&Ti::Scalar(expr), &Ti::Scalar(goal)) => {
526                expr_scalar = expr;
527                goal_scalar = goal;
528            }
529            (
530                &Ti::Vector {
531                    size: expr_size,
532                    scalar: expr,
533                },
534                &Ti::Vector {
535                    size: goal_size,
536                    scalar: goal,
537                },
538            ) if expr_size == goal_size => {
539                expr_scalar = expr;
540                goal_scalar = goal;
541            }
542            (
543                &Ti::Matrix {
544                    rows: expr_rows,
545                    columns: expr_columns,
546                    scalar: expr,
547                },
548                &Ti::Matrix {
549                    rows: goal_rows,
550                    columns: goal_columns,
551                    scalar: goal,
552                },
553            ) if expr_rows == goal_rows && expr_columns == goal_columns => {
554                expr_scalar = expr;
555                goal_scalar = goal;
556            }
557            (
558                &Ti::Array {
559                    base: expr_base,
560                    size: expr_size,
561                    stride: _,
562                },
563                &Ti::Array {
564                    base: goal_base,
565                    size: goal_size,
566                    stride: _,
567                },
568            ) if expr_size == goal_size => {
569                return types[expr_base]
570                    .inner
571                    .automatically_converts_to(&types[goal_base].inner, types);
572            }
573            _ => return None,
574        }
575
576        match (expr_scalar.kind, goal_scalar.kind) {
577            (Sk::AbstractFloat, Sk::Float) => {}
578            (Sk::AbstractInt, Sk::Sint | Sk::Uint | Sk::AbstractFloat | Sk::Float) => {}
579            _ => return None,
580        }
581
582        log::trace!("      okay: expr {expr_scalar:?}, goal {goal_scalar:?}");
583        Some((expr_scalar, goal_scalar))
584    }
585}
586
587/// Helper trait for providing the min and max values exactly representable by
588/// the integer type `Self` and floating point type `F`.
589pub trait IntFloatLimits<F>
590where
591    F: num_traits::Float,
592{
593    /// Returns the minimum value exactly representable by the integer type
594    /// `Self` and floating point type `F`.
595    fn min_float() -> F;
596    /// Returns the maximum value exactly representable by the integer type
597    /// `Self` and floating point type `F`.
598    fn max_float() -> F;
599}
600
601macro_rules! define_int_float_limits {
602    ($int:ty, $float:ty, $min:expr, $max:expr) => {
603        impl IntFloatLimits<$float> for $int {
604            fn min_float() -> $float {
605                $min
606            }
607            fn max_float() -> $float {
608                $max
609            }
610        }
611    };
612}
613
614// i16 range [-32768, 32767] fits exactly in f16 (max 65504), f32, and f64.
615// u16 range [0, 65535] fits exactly in f32 and f64. For f16, max exactly
616// representable is 65504 (f16::MAX).
617define_int_float_limits!(i16, half::f16, half::f16::MIN, half::f16::MAX);
618define_int_float_limits!(u16, half::f16, half::f16::ZERO, half::f16::MAX);
619define_int_float_limits!(i16, f32, -32768.0f32, 32767.0f32);
620define_int_float_limits!(u16, f32, 0.0f32, 65535.0f32);
621define_int_float_limits!(i16, f64, -32768.0f64, 32767.0f64);
622define_int_float_limits!(u16, f64, 0.0f64, 65535.0f64);
623define_int_float_limits!(i32, half::f16, half::f16::MIN, half::f16::MAX);
624define_int_float_limits!(u32, half::f16, half::f16::ZERO, half::f16::MAX);
625define_int_float_limits!(i64, half::f16, half::f16::MIN, half::f16::MAX);
626define_int_float_limits!(u64, half::f16, half::f16::ZERO, half::f16::MAX);
627define_int_float_limits!(i32, f32, -2147483648.0f32, 2147483520.0f32);
628define_int_float_limits!(u32, f32, 0.0f32, 4294967040.0f32);
629define_int_float_limits!(
630    i64,
631    f32,
632    -9223372036854775808.0f32,
633    9223371487098961920.0f32
634);
635define_int_float_limits!(u64, f32, 0.0f32, 18446742974197923840.0f32);
636define_int_float_limits!(i32, f64, -2147483648.0f64, 2147483647.0f64);
637define_int_float_limits!(u32, f64, 0.0f64, 4294967295.0f64);
638define_int_float_limits!(
639    i64,
640    f64,
641    -9223372036854775808.0f64,
642    9223372036854774784.0f64
643);
644define_int_float_limits!(u64, f64, 0.0f64, 18446744073709549568.0f64);
645
646/// Returns a tuple of [`crate::Literal`]s representing the minimum and maximum
647/// float values exactly representable by the provided float and integer types.
648/// Panics if `float` is not one of `F16`, `F32`, or `F64`, or `int` is
649/// not one of `I16`, `U16`, `I32`, `U32`, `I64`, or `U64`.
650pub fn min_max_float_representable_by(
651    float: crate::Scalar,
652    int: crate::Scalar,
653) -> (crate::Literal, crate::Literal) {
654    match (float, int) {
655        (crate::Scalar::F16, crate::Scalar::I16) => (
656            crate::Literal::F16(i16::min_float()),
657            crate::Literal::F16(i16::max_float()),
658        ),
659        (crate::Scalar::F16, crate::Scalar::U16) => (
660            crate::Literal::F16(u16::min_float()),
661            crate::Literal::F16(u16::max_float()),
662        ),
663        (crate::Scalar::F16, crate::Scalar::I32) => (
664            crate::Literal::F16(i32::min_float()),
665            crate::Literal::F16(i32::max_float()),
666        ),
667        (crate::Scalar::F16, crate::Scalar::U32) => (
668            crate::Literal::F16(u32::min_float()),
669            crate::Literal::F16(u32::max_float()),
670        ),
671        (crate::Scalar::F16, crate::Scalar::I64) => (
672            crate::Literal::F16(i64::min_float()),
673            crate::Literal::F16(i64::max_float()),
674        ),
675        (crate::Scalar::F16, crate::Scalar::U64) => (
676            crate::Literal::F16(u64::min_float()),
677            crate::Literal::F16(u64::max_float()),
678        ),
679        (crate::Scalar::F32, crate::Scalar::I16) => (
680            crate::Literal::F32(i16::min_float()),
681            crate::Literal::F32(i16::max_float()),
682        ),
683        (crate::Scalar::F32, crate::Scalar::U16) => (
684            crate::Literal::F32(u16::min_float()),
685            crate::Literal::F32(u16::max_float()),
686        ),
687        (crate::Scalar::F32, crate::Scalar::I32) => (
688            crate::Literal::F32(i32::min_float()),
689            crate::Literal::F32(i32::max_float()),
690        ),
691        (crate::Scalar::F32, crate::Scalar::U32) => (
692            crate::Literal::F32(u32::min_float()),
693            crate::Literal::F32(u32::max_float()),
694        ),
695        (crate::Scalar::F32, crate::Scalar::I64) => (
696            crate::Literal::F32(i64::min_float()),
697            crate::Literal::F32(i64::max_float()),
698        ),
699        (crate::Scalar::F32, crate::Scalar::U64) => (
700            crate::Literal::F32(u64::min_float()),
701            crate::Literal::F32(u64::max_float()),
702        ),
703        (crate::Scalar::F64, crate::Scalar::I16) => (
704            crate::Literal::F64(i16::min_float()),
705            crate::Literal::F64(i16::max_float()),
706        ),
707        (crate::Scalar::F64, crate::Scalar::U16) => (
708            crate::Literal::F64(u16::min_float()),
709            crate::Literal::F64(u16::max_float()),
710        ),
711        (crate::Scalar::F64, crate::Scalar::I32) => (
712            crate::Literal::F64(i32::min_float()),
713            crate::Literal::F64(i32::max_float()),
714        ),
715        (crate::Scalar::F64, crate::Scalar::U32) => (
716            crate::Literal::F64(u32::min_float()),
717            crate::Literal::F64(u32::max_float()),
718        ),
719        (crate::Scalar::F64, crate::Scalar::I64) => (
720            crate::Literal::F64(i64::min_float()),
721            crate::Literal::F64(i64::max_float()),
722        ),
723        (crate::Scalar::F64, crate::Scalar::U64) => (
724            crate::Literal::F64(u64::min_float()),
725            crate::Literal::F64(u64::max_float()),
726        ),
727        _ => unreachable!(),
728    }
729}
730
731/// Helper function that returns the string corresponding to the [`VectorSize`](crate::VectorSize)
732pub const fn vector_size_str(size: crate::VectorSize) -> &'static str {
733    match size {
734        crate::VectorSize::Bi => "2",
735        crate::VectorSize::Tri => "3",
736        crate::VectorSize::Quad => "4",
737    }
738}