naga/proc/
type_methods.rs

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