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