naga/proc/
mod.rs

1/*!
2[`Module`](super::Module) processing functionality.
3*/
4
5pub mod index;
6mod layouter;
7mod namer;
8mod terminator;
9mod typifier;
10
11pub use index::{BoundsCheckPolicies, BoundsCheckPolicy, IndexableLength, IndexableLengthError};
12pub use layouter::{Alignment, LayoutError, LayoutErrorInner, Layouter, TypeLayout};
13pub use namer::{EntryPointIndex, NameKey, Namer};
14pub use terminator::ensure_block_returns;
15pub use typifier::{ResolveContext, ResolveError, TypeResolution};
16
17impl From<super::StorageFormat> for super::ScalarKind {
18    fn from(format: super::StorageFormat) -> Self {
19        use super::{ScalarKind as Sk, StorageFormat as Sf};
20        match format {
21            Sf::R8Unorm => Sk::Float,
22            Sf::R8Snorm => Sk::Float,
23            Sf::R8Uint => Sk::Uint,
24            Sf::R8Sint => Sk::Sint,
25            Sf::R16Uint => Sk::Uint,
26            Sf::R16Sint => Sk::Sint,
27            Sf::R16Float => Sk::Float,
28            Sf::Rg8Unorm => Sk::Float,
29            Sf::Rg8Snorm => Sk::Float,
30            Sf::Rg8Uint => Sk::Uint,
31            Sf::Rg8Sint => Sk::Sint,
32            Sf::R32Uint => Sk::Uint,
33            Sf::R32Sint => Sk::Sint,
34            Sf::R32Float => Sk::Float,
35            Sf::Rg16Uint => Sk::Uint,
36            Sf::Rg16Sint => Sk::Sint,
37            Sf::Rg16Float => Sk::Float,
38            Sf::Rgba8Unorm => Sk::Float,
39            Sf::Rgba8Snorm => Sk::Float,
40            Sf::Rgba8Uint => Sk::Uint,
41            Sf::Rgba8Sint => Sk::Sint,
42            Sf::Rgb10a2Unorm => Sk::Float,
43            Sf::Rg11b10Float => Sk::Float,
44            Sf::Rg32Uint => Sk::Uint,
45            Sf::Rg32Sint => Sk::Sint,
46            Sf::Rg32Float => Sk::Float,
47            Sf::Rgba16Uint => Sk::Uint,
48            Sf::Rgba16Sint => Sk::Sint,
49            Sf::Rgba16Float => Sk::Float,
50            Sf::Rgba32Uint => Sk::Uint,
51            Sf::Rgba32Sint => Sk::Sint,
52            Sf::Rgba32Float => Sk::Float,
53            Sf::R16Unorm => Sk::Float,
54            Sf::R16Snorm => Sk::Float,
55            Sf::Rg16Unorm => Sk::Float,
56            Sf::Rg16Snorm => Sk::Float,
57            Sf::Rgba16Unorm => Sk::Float,
58            Sf::Rgba16Snorm => Sk::Float,
59        }
60    }
61}
62
63impl super::ScalarKind {
64    pub const fn is_numeric(self) -> bool {
65        match self {
66            crate::ScalarKind::Sint | crate::ScalarKind::Uint | crate::ScalarKind::Float => true,
67            crate::ScalarKind::Bool => false,
68        }
69    }
70}
71
72impl PartialEq for crate::Literal {
73    fn eq(&self, other: &Self) -> bool {
74        match (*self, *other) {
75            (Self::F64(a), Self::F64(b)) => a.to_bits() == b.to_bits(),
76            (Self::F32(a), Self::F32(b)) => a.to_bits() == b.to_bits(),
77            (Self::U32(a), Self::U32(b)) => a == b,
78            (Self::I32(a), Self::I32(b)) => a == b,
79            (Self::Bool(a), Self::Bool(b)) => a == b,
80            _ => false,
81        }
82    }
83}
84impl Eq for crate::Literal {}
85impl std::hash::Hash for crate::Literal {
86    fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
87        match *self {
88            Self::F64(v) => {
89                hasher.write_u8(0);
90                v.to_bits().hash(hasher);
91            }
92            Self::F32(v) => {
93                hasher.write_u8(1);
94                v.to_bits().hash(hasher);
95            }
96            Self::U32(v) => {
97                hasher.write_u8(2);
98                v.hash(hasher);
99            }
100            Self::I32(v) => {
101                hasher.write_u8(3);
102                v.hash(hasher);
103            }
104            Self::Bool(v) => {
105                hasher.write_u8(4);
106                v.hash(hasher);
107            }
108        }
109    }
110}
111
112impl crate::Literal {
113    pub const fn new(value: u8, kind: crate::ScalarKind, width: crate::Bytes) -> Option<Self> {
114        match (value, kind, width) {
115            (value, crate::ScalarKind::Float, 8) => Some(Self::F64(value as _)),
116            (value, crate::ScalarKind::Float, 4) => Some(Self::F32(value as _)),
117            (value, crate::ScalarKind::Uint, 4) => Some(Self::U32(value as _)),
118            (value, crate::ScalarKind::Sint, 4) => Some(Self::I32(value as _)),
119            (1, crate::ScalarKind::Bool, 4) => Some(Self::Bool(true)),
120            (0, crate::ScalarKind::Bool, 4) => Some(Self::Bool(false)),
121            _ => None,
122        }
123    }
124
125    pub const fn zero(kind: crate::ScalarKind, width: crate::Bytes) -> Option<Self> {
126        Self::new(0, kind, width)
127    }
128
129    pub const fn one(kind: crate::ScalarKind, width: crate::Bytes) -> Option<Self> {
130        Self::new(1, kind, width)
131    }
132
133    pub const fn width(&self) -> crate::Bytes {
134        match *self {
135            Self::F64(_) => 8,
136            Self::F32(_) | Self::U32(_) | Self::I32(_) => 4,
137            Self::Bool(_) => 1,
138        }
139    }
140    pub const fn scalar_kind(&self) -> crate::ScalarKind {
141        match *self {
142            Self::F64(_) | Self::F32(_) => crate::ScalarKind::Float,
143            Self::U32(_) => crate::ScalarKind::Uint,
144            Self::I32(_) => crate::ScalarKind::Sint,
145            Self::Bool(_) => crate::ScalarKind::Bool,
146        }
147    }
148    pub const fn ty_inner(&self) -> crate::TypeInner {
149        crate::TypeInner::Scalar {
150            kind: self.scalar_kind(),
151            width: self.width(),
152        }
153    }
154}
155
156pub const POINTER_SPAN: u32 = 4;
157
158impl super::TypeInner {
159    pub const fn scalar_kind(&self) -> Option<super::ScalarKind> {
160        match *self {
161            super::TypeInner::Scalar { kind, .. } | super::TypeInner::Vector { kind, .. } => {
162                Some(kind)
163            }
164            super::TypeInner::Matrix { .. } => Some(super::ScalarKind::Float),
165            _ => None,
166        }
167    }
168
169    pub const fn scalar_width(&self) -> Option<u8> {
170        // Multiply by 8 to get the bit width
171        match *self {
172            super::TypeInner::Scalar { width, .. } | super::TypeInner::Vector { width, .. } => {
173                Some(width * 8)
174            }
175            super::TypeInner::Matrix { width, .. } => Some(width * 8),
176            _ => None,
177        }
178    }
179
180    pub const fn pointer_space(&self) -> Option<crate::AddressSpace> {
181        match *self {
182            Self::Pointer { space, .. } => Some(space),
183            Self::ValuePointer { space, .. } => Some(space),
184            _ => None,
185        }
186    }
187
188    pub fn is_atomic_pointer(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
189        match *self {
190            crate::TypeInner::Pointer { base, .. } => match types[base].inner {
191                crate::TypeInner::Atomic { .. } => true,
192                _ => false,
193            },
194            _ => false,
195        }
196    }
197
198    /// Get the size of this type.
199    pub fn size(&self, _gctx: GlobalCtx) -> u32 {
200        match *self {
201            Self::Scalar { kind: _, width } | Self::Atomic { kind: _, width } => width as u32,
202            Self::Vector {
203                size,
204                kind: _,
205                width,
206            } => size as u32 * width as u32,
207            // matrices are treated as arrays of aligned columns
208            Self::Matrix {
209                columns,
210                rows,
211                width,
212            } => Alignment::from(rows) * width as u32 * columns as u32,
213            Self::Pointer { .. } | Self::ValuePointer { .. } => POINTER_SPAN,
214            Self::Array {
215                base: _,
216                size,
217                stride,
218            } => {
219                let count = match size {
220                    super::ArraySize::Constant(count) => count.get(),
221                    // A dynamically-sized array has to have at least one element
222                    super::ArraySize::Dynamic => 1,
223                };
224                count * stride
225            }
226            Self::Struct { span, .. } => span,
227            Self::Image { .. }
228            | Self::Sampler { .. }
229            | Self::AccelerationStructure
230            | Self::RayQuery
231            | Self::BindingArray { .. } => 0,
232        }
233    }
234
235    /// Return the canonical form of `self`, or `None` if it's already in
236    /// canonical form.
237    ///
238    /// Certain types have multiple representations in `TypeInner`. This
239    /// function converts all forms of equivalent types to a single
240    /// representative of their class, so that simply applying `Eq` to the
241    /// result indicates whether the types are equivalent, as far as Naga IR is
242    /// concerned.
243    pub fn canonical_form(
244        &self,
245        types: &crate::UniqueArena<crate::Type>,
246    ) -> Option<crate::TypeInner> {
247        use crate::TypeInner as Ti;
248        match *self {
249            Ti::Pointer { base, space } => match types[base].inner {
250                Ti::Scalar { kind, width } => Some(Ti::ValuePointer {
251                    size: None,
252                    kind,
253                    width,
254                    space,
255                }),
256                Ti::Vector { size, kind, width } => Some(Ti::ValuePointer {
257                    size: Some(size),
258                    kind,
259                    width,
260                    space,
261                }),
262                _ => None,
263            },
264            _ => None,
265        }
266    }
267
268    /// Compare `self` and `rhs` as types.
269    ///
270    /// This is mostly the same as `<TypeInner as Eq>::eq`, but it treats
271    /// `ValuePointer` and `Pointer` types as equivalent.
272    ///
273    /// When you know that one side of the comparison is never a pointer, it's
274    /// fine to not bother with canonicalization, and just compare `TypeInner`
275    /// values with `==`.
276    pub fn equivalent(
277        &self,
278        rhs: &crate::TypeInner,
279        types: &crate::UniqueArena<crate::Type>,
280    ) -> bool {
281        let left = self.canonical_form(types);
282        let right = rhs.canonical_form(types);
283        left.as_ref().unwrap_or(self) == right.as_ref().unwrap_or(rhs)
284    }
285
286    pub fn is_dynamically_sized(&self, types: &crate::UniqueArena<crate::Type>) -> bool {
287        use crate::TypeInner as Ti;
288        match *self {
289            Ti::Array { size, .. } => size == crate::ArraySize::Dynamic,
290            Ti::Struct { ref members, .. } => members
291                .last()
292                .map(|last| types[last.ty].inner.is_dynamically_sized(types))
293                .unwrap_or(false),
294            _ => false,
295        }
296    }
297
298    pub fn components(&self) -> Option<u32> {
299        Some(match *self {
300            Self::Vector { size, .. } => size as u32,
301            Self::Matrix { columns, .. } => columns as u32,
302            Self::Array {
303                size: crate::ArraySize::Constant(len),
304                ..
305            } => len.get(),
306            Self::Struct { ref members, .. } => members.len() as u32,
307            _ => return None,
308        })
309    }
310
311    pub fn component_type(&self, index: usize) -> Option<TypeResolution> {
312        Some(match *self {
313            Self::Vector { kind, width, .. } => {
314                TypeResolution::Value(crate::TypeInner::Scalar { kind, width })
315            }
316            Self::Matrix { rows, width, .. } => TypeResolution::Value(crate::TypeInner::Vector {
317                size: rows,
318                kind: crate::ScalarKind::Float,
319                width,
320            }),
321            Self::Array {
322                base,
323                size: crate::ArraySize::Constant(_),
324                ..
325            } => TypeResolution::Handle(base),
326            Self::Struct { ref members, .. } => TypeResolution::Handle(members[index].ty),
327            _ => return None,
328        })
329    }
330}
331
332impl super::AddressSpace {
333    pub fn access(self) -> crate::StorageAccess {
334        use crate::StorageAccess as Sa;
335        match self {
336            crate::AddressSpace::Function
337            | crate::AddressSpace::Private
338            | crate::AddressSpace::WorkGroup => Sa::LOAD | Sa::STORE,
339            crate::AddressSpace::Uniform => Sa::LOAD,
340            crate::AddressSpace::Storage { access } => access,
341            crate::AddressSpace::Handle => Sa::LOAD,
342            crate::AddressSpace::PushConstant => Sa::LOAD,
343        }
344    }
345}
346
347impl super::MathFunction {
348    pub const fn argument_count(&self) -> usize {
349        match *self {
350            // comparison
351            Self::Abs => 1,
352            Self::Min => 2,
353            Self::Max => 2,
354            Self::Clamp => 3,
355            Self::Saturate => 1,
356            // trigonometry
357            Self::Cos => 1,
358            Self::Cosh => 1,
359            Self::Sin => 1,
360            Self::Sinh => 1,
361            Self::Tan => 1,
362            Self::Tanh => 1,
363            Self::Acos => 1,
364            Self::Asin => 1,
365            Self::Atan => 1,
366            Self::Atan2 => 2,
367            Self::Asinh => 1,
368            Self::Acosh => 1,
369            Self::Atanh => 1,
370            Self::Radians => 1,
371            Self::Degrees => 1,
372            // decomposition
373            Self::Ceil => 1,
374            Self::Floor => 1,
375            Self::Round => 1,
376            Self::Fract => 1,
377            Self::Trunc => 1,
378            Self::Modf => 2,
379            Self::Frexp => 2,
380            Self::Ldexp => 2,
381            // exponent
382            Self::Exp => 1,
383            Self::Exp2 => 1,
384            Self::Log => 1,
385            Self::Log2 => 1,
386            Self::Pow => 2,
387            // geometry
388            Self::Dot => 2,
389            Self::Outer => 2,
390            Self::Cross => 2,
391            Self::Distance => 2,
392            Self::Length => 1,
393            Self::Normalize => 1,
394            Self::FaceForward => 3,
395            Self::Reflect => 2,
396            Self::Refract => 3,
397            // computational
398            Self::Sign => 1,
399            Self::Fma => 3,
400            Self::Mix => 3,
401            Self::Step => 2,
402            Self::SmoothStep => 3,
403            Self::Sqrt => 1,
404            Self::InverseSqrt => 1,
405            Self::Inverse => 1,
406            Self::Transpose => 1,
407            Self::Determinant => 1,
408            // bits
409            Self::CountTrailingZeros => 1,
410            Self::CountLeadingZeros => 1,
411            Self::CountOneBits => 1,
412            Self::ReverseBits => 1,
413            Self::ExtractBits => 3,
414            Self::InsertBits => 4,
415            Self::FindLsb => 1,
416            Self::FindMsb => 1,
417            // data packing
418            Self::Pack4x8snorm => 1,
419            Self::Pack4x8unorm => 1,
420            Self::Pack2x16snorm => 1,
421            Self::Pack2x16unorm => 1,
422            Self::Pack2x16float => 1,
423            // data unpacking
424            Self::Unpack4x8snorm => 1,
425            Self::Unpack4x8unorm => 1,
426            Self::Unpack2x16snorm => 1,
427            Self::Unpack2x16unorm => 1,
428            Self::Unpack2x16float => 1,
429        }
430    }
431}
432
433impl crate::Expression {
434    /// Returns true if the expression is considered emitted at the start of a function.
435    pub const fn needs_pre_emit(&self) -> bool {
436        match *self {
437            Self::Literal(_)
438            | Self::Constant(_)
439            | Self::ZeroValue(_)
440            | Self::FunctionArgument(_)
441            | Self::GlobalVariable(_)
442            | Self::LocalVariable(_) => true,
443            _ => false,
444        }
445    }
446
447    /// Return true if this expression is a dynamic array index, for [`Access`].
448    ///
449    /// This method returns true if this expression is a dynamically computed
450    /// index, and as such can only be used to index matrices and arrays when
451    /// they appear behind a pointer. See the documentation for [`Access`] for
452    /// details.
453    ///
454    /// Note, this does not check the _type_ of the given expression. It's up to
455    /// the caller to establish that the `Access` expression is well-typed
456    /// through other means, like [`ResolveContext`].
457    ///
458    /// [`Access`]: crate::Expression::Access
459    /// [`ResolveContext`]: crate::proc::ResolveContext
460    pub fn is_dynamic_index(&self, module: &crate::Module) -> bool {
461        if let Self::Constant(handle) = *self {
462            let constant = &module.constants[handle];
463            !matches!(constant.r#override, crate::Override::None)
464        } else {
465            true
466        }
467    }
468}
469
470impl crate::Function {
471    /// Return the global variable being accessed by the expression `pointer`.
472    ///
473    /// Assuming that `pointer` is a series of `Access` and `AccessIndex`
474    /// expressions that ultimately access some part of a `GlobalVariable`,
475    /// return a handle for that global.
476    ///
477    /// If the expression does not ultimately access a global variable, return
478    /// `None`.
479    pub fn originating_global(
480        &self,
481        mut pointer: crate::Handle<crate::Expression>,
482    ) -> Option<crate::Handle<crate::GlobalVariable>> {
483        loop {
484            pointer = match self.expressions[pointer] {
485                crate::Expression::Access { base, .. } => base,
486                crate::Expression::AccessIndex { base, .. } => base,
487                crate::Expression::GlobalVariable(handle) => return Some(handle),
488                crate::Expression::LocalVariable(_) => return None,
489                crate::Expression::FunctionArgument(_) => return None,
490                // There are no other expressions that produce pointer values.
491                _ => unreachable!(),
492            }
493        }
494    }
495}
496
497impl crate::SampleLevel {
498    pub const fn implicit_derivatives(&self) -> bool {
499        match *self {
500            Self::Auto | Self::Bias(_) => true,
501            Self::Zero | Self::Exact(_) | Self::Gradient { .. } => false,
502        }
503    }
504}
505
506impl crate::Binding {
507    pub const fn to_built_in(&self) -> Option<crate::BuiltIn> {
508        match *self {
509            crate::Binding::BuiltIn(built_in) => Some(built_in),
510            Self::Location { .. } => None,
511        }
512    }
513}
514
515impl super::SwizzleComponent {
516    pub const XYZW: [Self; 4] = [Self::X, Self::Y, Self::Z, Self::W];
517
518    pub const fn index(&self) -> u32 {
519        match *self {
520            Self::X => 0,
521            Self::Y => 1,
522            Self::Z => 2,
523            Self::W => 3,
524        }
525    }
526    pub const fn from_index(idx: u32) -> Self {
527        match idx {
528            0 => Self::X,
529            1 => Self::Y,
530            2 => Self::Z,
531            _ => Self::W,
532        }
533    }
534}
535
536impl super::ImageClass {
537    pub const fn is_multisampled(self) -> bool {
538        match self {
539            crate::ImageClass::Sampled { multi, .. } | crate::ImageClass::Depth { multi } => multi,
540            crate::ImageClass::Storage { .. } => false,
541        }
542    }
543
544    pub const fn is_mipmapped(self) -> bool {
545        match self {
546            crate::ImageClass::Sampled { multi, .. } | crate::ImageClass::Depth { multi } => !multi,
547            crate::ImageClass::Storage { .. } => false,
548        }
549    }
550}
551
552impl crate::Module {
553    pub const fn to_ctx(&self) -> GlobalCtx<'_> {
554        GlobalCtx {
555            types: &self.types,
556            constants: &self.constants,
557            const_expressions: &self.const_expressions,
558        }
559    }
560}
561
562#[derive(Debug)]
563pub(super) enum U32EvalError {
564    NonConst,
565    Negative,
566}
567
568#[derive(Clone, Copy)]
569pub struct GlobalCtx<'a> {
570    pub types: &'a crate::UniqueArena<crate::Type>,
571    pub constants: &'a crate::Arena<crate::Constant>,
572    pub const_expressions: &'a crate::Arena<crate::Expression>,
573}
574
575impl GlobalCtx<'_> {
576    /// Try to evaluate the expression in `self.const_expressions` using its `handle` and return it as a `u32`.
577    #[allow(dead_code)]
578    pub(super) fn eval_expr_to_u32(
579        &self,
580        handle: crate::Handle<crate::Expression>,
581    ) -> Result<u32, U32EvalError> {
582        self.eval_expr_to_u32_from(handle, self.const_expressions)
583    }
584
585    /// Try to evaluate the expression in the `arena` using its `handle` and return it as a `u32`.
586    pub(super) fn eval_expr_to_u32_from(
587        &self,
588        handle: crate::Handle<crate::Expression>,
589        arena: &crate::Arena<crate::Expression>,
590    ) -> Result<u32, U32EvalError> {
591        fn get(
592            gctx: GlobalCtx,
593            handle: crate::Handle<crate::Expression>,
594            arena: &crate::Arena<crate::Expression>,
595        ) -> Result<u32, U32EvalError> {
596            match arena[handle] {
597                crate::Expression::Literal(crate::Literal::U32(value)) => Ok(value),
598                crate::Expression::Literal(crate::Literal::I32(value)) => {
599                    value.try_into().map_err(|_| U32EvalError::Negative)
600                }
601                crate::Expression::ZeroValue(ty)
602                    if matches!(
603                        gctx.types[ty].inner,
604                        crate::TypeInner::Scalar {
605                            kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
606                            width: _
607                        }
608                    ) =>
609                {
610                    Ok(0)
611                }
612                _ => Err(U32EvalError::NonConst),
613            }
614        }
615        match arena[handle] {
616            crate::Expression::Constant(c) => {
617                get(*self, self.constants[c].init, self.const_expressions)
618            }
619            _ => get(*self, handle, arena),
620        }
621    }
622}
623
624#[test]
625fn test_matrix_size() {
626    let module = crate::Module::default();
627    assert_eq!(
628        crate::TypeInner::Matrix {
629            columns: crate::VectorSize::Tri,
630            rows: crate::VectorSize::Tri,
631            width: 4
632        }
633        .size(module.to_ctx()),
634        48,
635    );
636}