/*! [`Module`](super::Module) processing functionality. */ mod constant_evaluator; mod emitter; pub mod index; mod layouter; mod namer; mod terminator; mod typifier; pub use constant_evaluator::{ ConstantEvaluator, ConstantEvaluatorError, ExpressionConstnessTracker, }; pub use emitter::Emitter; pub use index::{BoundsCheckPolicies, BoundsCheckPolicy, IndexableLength, IndexableLengthError}; pub use layouter::{Alignment, LayoutError, LayoutErrorInner, Layouter, TypeLayout}; pub use namer::{EntryPointIndex, NameKey, Namer}; pub use terminator::ensure_block_returns; pub use typifier::{ResolveContext, ResolveError, TypeResolution}; impl From for super::ScalarKind { fn from(format: super::StorageFormat) -> Self { use super::{ScalarKind as Sk, StorageFormat as Sf}; match format { Sf::R8Unorm => Sk::Float, Sf::R8Snorm => Sk::Float, Sf::R8Uint => Sk::Uint, Sf::R8Sint => Sk::Sint, Sf::R16Uint => Sk::Uint, Sf::R16Sint => Sk::Sint, Sf::R16Float => Sk::Float, Sf::Rg8Unorm => Sk::Float, Sf::Rg8Snorm => Sk::Float, Sf::Rg8Uint => Sk::Uint, Sf::Rg8Sint => Sk::Sint, Sf::R32Uint => Sk::Uint, Sf::R32Sint => Sk::Sint, Sf::R32Float => Sk::Float, Sf::Rg16Uint => Sk::Uint, Sf::Rg16Sint => Sk::Sint, Sf::Rg16Float => Sk::Float, Sf::Rgba8Unorm => Sk::Float, Sf::Rgba8Snorm => Sk::Float, Sf::Rgba8Uint => Sk::Uint, Sf::Rgba8Sint => Sk::Sint, Sf::Bgra8Unorm => Sk::Float, Sf::Rgb10a2Uint => Sk::Uint, Sf::Rgb10a2Unorm => Sk::Float, Sf::Rg11b10Float => Sk::Float, Sf::Rg32Uint => Sk::Uint, Sf::Rg32Sint => Sk::Sint, Sf::Rg32Float => Sk::Float, Sf::Rgba16Uint => Sk::Uint, Sf::Rgba16Sint => Sk::Sint, Sf::Rgba16Float => Sk::Float, Sf::Rgba32Uint => Sk::Uint, Sf::Rgba32Sint => Sk::Sint, Sf::Rgba32Float => Sk::Float, Sf::R16Unorm => Sk::Float, Sf::R16Snorm => Sk::Float, Sf::Rg16Unorm => Sk::Float, Sf::Rg16Snorm => Sk::Float, Sf::Rgba16Unorm => Sk::Float, Sf::Rgba16Snorm => Sk::Float, } } } impl super::ScalarKind { pub const fn is_numeric(self) -> bool { match self { crate::ScalarKind::Sint | crate::ScalarKind::Uint | crate::ScalarKind::Float | crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => true, crate::ScalarKind::Bool => false, } } } impl super::Scalar { pub const I32: Self = Self { kind: crate::ScalarKind::Sint, width: 4, }; pub const U32: Self = Self { kind: crate::ScalarKind::Uint, width: 4, }; pub const F32: Self = Self { kind: crate::ScalarKind::Float, width: 4, }; pub const F64: Self = Self { kind: crate::ScalarKind::Float, width: 8, }; pub const I64: Self = Self { kind: crate::ScalarKind::Sint, width: 8, }; pub const U64: Self = Self { kind: crate::ScalarKind::Uint, width: 8, }; pub const BOOL: Self = Self { kind: crate::ScalarKind::Bool, width: crate::BOOL_WIDTH, }; pub const ABSTRACT_INT: Self = Self { kind: crate::ScalarKind::AbstractInt, width: crate::ABSTRACT_WIDTH, }; pub const ABSTRACT_FLOAT: Self = Self { kind: crate::ScalarKind::AbstractFloat, width: crate::ABSTRACT_WIDTH, }; pub const fn is_abstract(self) -> bool { match self.kind { crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => true, crate::ScalarKind::Sint | crate::ScalarKind::Uint | crate::ScalarKind::Float | crate::ScalarKind::Bool => false, } } /// Construct a float `Scalar` with the given width. /// /// This is especially common when dealing with /// `TypeInner::Matrix`, where the scalar kind is implicit. pub const fn float(width: crate::Bytes) -> Self { Self { kind: crate::ScalarKind::Float, width, } } pub const fn to_inner_scalar(self) -> crate::TypeInner { crate::TypeInner::Scalar(self) } pub const fn to_inner_vector(self, size: crate::VectorSize) -> crate::TypeInner { crate::TypeInner::Vector { size, scalar: self } } pub const fn to_inner_atomic(self) -> crate::TypeInner { crate::TypeInner::Atomic(self) } } impl PartialEq for crate::Literal { fn eq(&self, other: &Self) -> bool { match (*self, *other) { (Self::F64(a), Self::F64(b)) => a.to_bits() == b.to_bits(), (Self::F32(a), Self::F32(b)) => a.to_bits() == b.to_bits(), (Self::U32(a), Self::U32(b)) => a == b, (Self::I32(a), Self::I32(b)) => a == b, (Self::U64(a), Self::U64(b)) => a == b, (Self::I64(a), Self::I64(b)) => a == b, (Self::Bool(a), Self::Bool(b)) => a == b, _ => false, } } } impl Eq for crate::Literal {} impl std::hash::Hash for crate::Literal { fn hash(&self, hasher: &mut H) { match *self { Self::F64(v) | Self::AbstractFloat(v) => { hasher.write_u8(0); v.to_bits().hash(hasher); } Self::F32(v) => { hasher.write_u8(1); v.to_bits().hash(hasher); } Self::U32(v) => { hasher.write_u8(2); v.hash(hasher); } Self::I32(v) => { hasher.write_u8(3); v.hash(hasher); } Self::Bool(v) => { hasher.write_u8(4); v.hash(hasher); } Self::I64(v) => { hasher.write_u8(5); v.hash(hasher); } Self::U64(v) => { hasher.write_u8(6); v.hash(hasher); } Self::AbstractInt(v) => { hasher.write_u8(7); v.hash(hasher); } } } } impl crate::Literal { pub const fn new(value: u8, scalar: crate::Scalar) -> Option { match (value, scalar.kind, scalar.width) { (value, crate::ScalarKind::Float, 8) => Some(Self::F64(value as _)), (value, crate::ScalarKind::Float, 4) => Some(Self::F32(value as _)), (value, crate::ScalarKind::Uint, 4) => Some(Self::U32(value as _)), (value, crate::ScalarKind::Sint, 4) => Some(Self::I32(value as _)), (value, crate::ScalarKind::Uint, 8) => Some(Self::U64(value as _)), (value, crate::ScalarKind::Sint, 8) => Some(Self::I64(value as _)), (1, crate::ScalarKind::Bool, 4) => Some(Self::Bool(true)), (0, crate::ScalarKind::Bool, 4) => Some(Self::Bool(false)), _ => None, } } pub const fn zero(scalar: crate::Scalar) -> Option { Self::new(0, scalar) } pub const fn one(scalar: crate::Scalar) -> Option { Self::new(1, scalar) } pub const fn width(&self) -> crate::Bytes { match *self { Self::F64(_) | Self::I64(_) | Self::U64(_) => 8, Self::F32(_) | Self::U32(_) | Self::I32(_) => 4, Self::Bool(_) => crate::BOOL_WIDTH, Self::AbstractInt(_) | Self::AbstractFloat(_) => crate::ABSTRACT_WIDTH, } } pub const fn scalar(&self) -> crate::Scalar { match *self { Self::F64(_) => crate::Scalar::F64, Self::F32(_) => crate::Scalar::F32, Self::U32(_) => crate::Scalar::U32, Self::I32(_) => crate::Scalar::I32, Self::U64(_) => crate::Scalar::U64, Self::I64(_) => crate::Scalar::I64, Self::Bool(_) => crate::Scalar::BOOL, Self::AbstractInt(_) => crate::Scalar::ABSTRACT_INT, Self::AbstractFloat(_) => crate::Scalar::ABSTRACT_FLOAT, } } pub const fn scalar_kind(&self) -> crate::ScalarKind { self.scalar().kind } pub const fn ty_inner(&self) -> crate::TypeInner { crate::TypeInner::Scalar(self.scalar()) } } pub const POINTER_SPAN: u32 = 4; impl super::TypeInner { /// Return the scalar type of `self`. /// /// If `inner` is a scalar, vector, or matrix type, return /// its scalar type. Otherwise, return `None`. pub const fn scalar(&self) -> Option { use crate::TypeInner as Ti; match *self { Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => Some(scalar), Ti::Matrix { scalar, .. } => Some(scalar), _ => None, } } pub fn scalar_kind(&self) -> Option { self.scalar().map(|scalar| scalar.kind) } pub fn scalar_width(&self) -> Option { self.scalar().map(|scalar| scalar.width * 8) } pub const fn pointer_space(&self) -> Option { match *self { Self::Pointer { space, .. } => Some(space), Self::ValuePointer { space, .. } => Some(space), _ => None, } } pub fn is_atomic_pointer(&self, types: &crate::UniqueArena) -> bool { match *self { crate::TypeInner::Pointer { base, .. } => match types[base].inner { crate::TypeInner::Atomic { .. } => true, _ => false, }, _ => false, } } /// Get the size of this type. pub fn size(&self, _gctx: GlobalCtx) -> u32 { match *self { Self::Scalar(scalar) | Self::Atomic(scalar) => scalar.width as u32, Self::Vector { size, scalar } => size as u32 * scalar.width as u32, // matrices are treated as arrays of aligned columns Self::Matrix { columns, rows, scalar, } => Alignment::from(rows) * scalar.width as u32 * columns as u32, Self::Pointer { .. } | Self::ValuePointer { .. } => POINTER_SPAN, Self::Array { base: _, size, stride, } => { let count = match size { super::ArraySize::Constant(count) => count.get(), // A dynamically-sized array has to have at least one element super::ArraySize::Dynamic => 1, }; count * stride } Self::Struct { span, .. } => span, Self::Image { .. } | Self::Sampler { .. } | Self::AccelerationStructure | Self::RayQuery | Self::BindingArray { .. } => 0, } } /// Return the canonical form of `self`, or `None` if it's already in /// canonical form. /// /// Certain types have multiple representations in `TypeInner`. This /// function converts all forms of equivalent types to a single /// representative of their class, so that simply applying `Eq` to the /// result indicates whether the types are equivalent, as far as Naga IR is /// concerned. pub fn canonical_form( &self, types: &crate::UniqueArena, ) -> Option { use crate::TypeInner as Ti; match *self { Ti::Pointer { base, space } => match types[base].inner { Ti::Scalar(scalar) => Some(Ti::ValuePointer { size: None, scalar, space, }), Ti::Vector { size, scalar } => Some(Ti::ValuePointer { size: Some(size), scalar, space, }), _ => None, }, _ => None, } } /// Compare `self` and `rhs` as types. /// /// This is mostly the same as `::eq`, but it treats /// `ValuePointer` and `Pointer` types as equivalent. /// /// When you know that one side of the comparison is never a pointer, it's /// fine to not bother with canonicalization, and just compare `TypeInner` /// values with `==`. pub fn equivalent( &self, rhs: &crate::TypeInner, types: &crate::UniqueArena, ) -> bool { let left = self.canonical_form(types); let right = rhs.canonical_form(types); left.as_ref().unwrap_or(self) == right.as_ref().unwrap_or(rhs) } pub fn is_dynamically_sized(&self, types: &crate::UniqueArena) -> bool { use crate::TypeInner as Ti; match *self { Ti::Array { size, .. } => size == crate::ArraySize::Dynamic, Ti::Struct { ref members, .. } => members .last() .map(|last| types[last.ty].inner.is_dynamically_sized(types)) .unwrap_or(false), _ => false, } } pub fn components(&self) -> Option { Some(match *self { Self::Vector { size, .. } => size as u32, Self::Matrix { columns, .. } => columns as u32, Self::Array { size: crate::ArraySize::Constant(len), .. } => len.get(), Self::Struct { ref members, .. } => members.len() as u32, _ => return None, }) } pub fn component_type(&self, index: usize) -> Option { Some(match *self { Self::Vector { scalar, .. } => TypeResolution::Value(crate::TypeInner::Scalar(scalar)), Self::Matrix { rows, scalar, .. } => { TypeResolution::Value(crate::TypeInner::Vector { size: rows, scalar }) } Self::Array { base, size: crate::ArraySize::Constant(_), .. } => TypeResolution::Handle(base), Self::Struct { ref members, .. } => TypeResolution::Handle(members[index].ty), _ => return None, }) } } impl super::AddressSpace { pub fn access(self) -> crate::StorageAccess { use crate::StorageAccess as Sa; match self { crate::AddressSpace::Function | crate::AddressSpace::Private | crate::AddressSpace::WorkGroup => Sa::LOAD | Sa::STORE, crate::AddressSpace::Uniform => Sa::LOAD, crate::AddressSpace::Storage { access } => access, crate::AddressSpace::Handle => Sa::LOAD, crate::AddressSpace::PushConstant => Sa::LOAD, } } } impl super::MathFunction { pub const fn argument_count(&self) -> usize { match *self { // comparison Self::Abs => 1, Self::Min => 2, Self::Max => 2, Self::Clamp => 3, Self::Saturate => 1, // trigonometry Self::Cos => 1, Self::Cosh => 1, Self::Sin => 1, Self::Sinh => 1, Self::Tan => 1, Self::Tanh => 1, Self::Acos => 1, Self::Asin => 1, Self::Atan => 1, Self::Atan2 => 2, Self::Asinh => 1, Self::Acosh => 1, Self::Atanh => 1, Self::Radians => 1, Self::Degrees => 1, // decomposition Self::Ceil => 1, Self::Floor => 1, Self::Round => 1, Self::Fract => 1, Self::Trunc => 1, Self::Modf => 1, Self::Frexp => 1, Self::Ldexp => 2, // exponent Self::Exp => 1, Self::Exp2 => 1, Self::Log => 1, Self::Log2 => 1, Self::Pow => 2, // geometry Self::Dot => 2, Self::Outer => 2, Self::Cross => 2, Self::Distance => 2, Self::Length => 1, Self::Normalize => 1, Self::FaceForward => 3, Self::Reflect => 2, Self::Refract => 3, // computational Self::Sign => 1, Self::Fma => 3, Self::Mix => 3, Self::Step => 2, Self::SmoothStep => 3, Self::Sqrt => 1, Self::InverseSqrt => 1, Self::Inverse => 1, Self::Transpose => 1, Self::Determinant => 1, // bits Self::CountTrailingZeros => 1, Self::CountLeadingZeros => 1, Self::CountOneBits => 1, Self::ReverseBits => 1, Self::ExtractBits => 3, Self::InsertBits => 4, Self::FindLsb => 1, Self::FindMsb => 1, // data packing Self::Pack4x8snorm => 1, Self::Pack4x8unorm => 1, Self::Pack2x16snorm => 1, Self::Pack2x16unorm => 1, Self::Pack2x16float => 1, // data unpacking Self::Unpack4x8snorm => 1, Self::Unpack4x8unorm => 1, Self::Unpack2x16snorm => 1, Self::Unpack2x16unorm => 1, Self::Unpack2x16float => 1, } } } impl crate::Expression { /// Returns true if the expression is considered emitted at the start of a function. pub const fn needs_pre_emit(&self) -> bool { match *self { Self::Literal(_) | Self::Constant(_) | Self::ZeroValue(_) | Self::FunctionArgument(_) | Self::GlobalVariable(_) | Self::LocalVariable(_) => true, _ => false, } } /// Return true if this expression is a dynamic array index, for [`Access`]. /// /// This method returns true if this expression is a dynamically computed /// index, and as such can only be used to index matrices and arrays when /// they appear behind a pointer. See the documentation for [`Access`] for /// details. /// /// Note, this does not check the _type_ of the given expression. It's up to /// the caller to establish that the `Access` expression is well-typed /// through other means, like [`ResolveContext`]. /// /// [`Access`]: crate::Expression::Access /// [`ResolveContext`]: crate::proc::ResolveContext pub fn is_dynamic_index(&self, module: &crate::Module) -> bool { match *self { Self::Literal(_) | Self::ZeroValue(_) => false, Self::Constant(handle) => { let constant = &module.constants[handle]; !matches!(constant.r#override, crate::Override::None) } _ => true, } } } impl crate::Function { /// Return the global variable being accessed by the expression `pointer`. /// /// Assuming that `pointer` is a series of `Access` and `AccessIndex` /// expressions that ultimately access some part of a `GlobalVariable`, /// return a handle for that global. /// /// If the expression does not ultimately access a global variable, return /// `None`. pub fn originating_global( &self, mut pointer: crate::Handle, ) -> Option> { loop { pointer = match self.expressions[pointer] { crate::Expression::Access { base, .. } => base, crate::Expression::AccessIndex { base, .. } => base, crate::Expression::GlobalVariable(handle) => return Some(handle), crate::Expression::LocalVariable(_) => return None, crate::Expression::FunctionArgument(_) => return None, // There are no other expressions that produce pointer values. _ => unreachable!(), } } } } impl crate::SampleLevel { pub const fn implicit_derivatives(&self) -> bool { match *self { Self::Auto | Self::Bias(_) => true, Self::Zero | Self::Exact(_) | Self::Gradient { .. } => false, } } } impl crate::Binding { pub const fn to_built_in(&self) -> Option { match *self { crate::Binding::BuiltIn(built_in) => Some(built_in), Self::Location { .. } => None, } } } impl super::SwizzleComponent { pub const XYZW: [Self; 4] = [Self::X, Self::Y, Self::Z, Self::W]; pub const fn index(&self) -> u32 { match *self { Self::X => 0, Self::Y => 1, Self::Z => 2, Self::W => 3, } } pub const fn from_index(idx: u32) -> Self { match idx { 0 => Self::X, 1 => Self::Y, 2 => Self::Z, _ => Self::W, } } } impl super::ImageClass { pub const fn is_multisampled(self) -> bool { match self { crate::ImageClass::Sampled { multi, .. } | crate::ImageClass::Depth { multi } => multi, crate::ImageClass::Storage { .. } => false, } } pub const fn is_mipmapped(self) -> bool { match self { crate::ImageClass::Sampled { multi, .. } | crate::ImageClass::Depth { multi } => !multi, crate::ImageClass::Storage { .. } => false, } } } impl crate::Module { pub const fn to_ctx(&self) -> GlobalCtx<'_> { GlobalCtx { types: &self.types, constants: &self.constants, const_expressions: &self.const_expressions, } } } #[derive(Debug)] pub(super) enum U32EvalError { NonConst, Negative, } #[derive(Clone, Copy)] pub struct GlobalCtx<'a> { pub types: &'a crate::UniqueArena, pub constants: &'a crate::Arena, pub const_expressions: &'a crate::Arena, } impl GlobalCtx<'_> { /// Try to evaluate the expression in `self.const_expressions` using its `handle` and return it as a `u32`. #[allow(dead_code)] pub(super) fn eval_expr_to_u32( &self, handle: crate::Handle, ) -> Result { self.eval_expr_to_u32_from(handle, self.const_expressions) } /// Try to evaluate the expression in the `arena` using its `handle` and return it as a `u32`. pub(super) fn eval_expr_to_u32_from( &self, handle: crate::Handle, arena: &crate::Arena, ) -> Result { match self.eval_expr_to_literal_from(handle, arena) { Some(crate::Literal::U32(value)) => Ok(value), Some(crate::Literal::I32(value)) => { value.try_into().map_err(|_| U32EvalError::Negative) } _ => Err(U32EvalError::NonConst), } } #[allow(dead_code)] pub(crate) fn eval_expr_to_literal( &self, handle: crate::Handle, ) -> Option { self.eval_expr_to_literal_from(handle, self.const_expressions) } fn eval_expr_to_literal_from( &self, handle: crate::Handle, arena: &crate::Arena, ) -> Option { fn get( gctx: GlobalCtx, handle: crate::Handle, arena: &crate::Arena, ) -> Option { match arena[handle] { crate::Expression::Literal(literal) => Some(literal), crate::Expression::ZeroValue(ty) => match gctx.types[ty].inner { crate::TypeInner::Scalar(scalar) => crate::Literal::zero(scalar), _ => None, }, _ => None, } } match arena[handle] { crate::Expression::Constant(c) => { get(*self, self.constants[c].init, self.const_expressions) } _ => get(*self, handle, arena), } } } /// Return an iterator over the individual components assembled by a /// `Compose` expression. /// /// Given `ty` and `components` from an `Expression::Compose`, return an /// iterator over the components of the resulting value. /// /// Normally, this would just be an iterator over `components`. However, /// `Compose` expressions can concatenate vectors, in which case the i'th /// value being composed is not generally the i'th element of `components`. /// This function consults `ty` to decide if this concatenation is occurring, /// and returns an iterator that produces the components of the result of /// the `Compose` expression in either case. pub fn flatten_compose<'arenas>( ty: crate::Handle, components: &'arenas [crate::Handle], expressions: &'arenas crate::Arena, types: &'arenas crate::UniqueArena, ) -> impl Iterator> + 'arenas { // Returning `impl Iterator` is a bit tricky. We may or may not // want to flatten the components, but we have to settle on a // single concrete type to return. This function returns a single // iterator chain that handles both the flattening and // non-flattening cases. let (size, is_vector) = if let crate::TypeInner::Vector { size, .. } = types[ty].inner { (size as usize, true) } else { (components.len(), false) }; /// Flatten `Compose` expressions if `is_vector` is true. fn flatten_compose<'c>( component: &'c crate::Handle, is_vector: bool, expressions: &'c crate::Arena, ) -> &'c [crate::Handle] { if is_vector { if let crate::Expression::Compose { ty: _, components: ref subcomponents, } = expressions[*component] { return subcomponents; } } std::slice::from_ref(component) } /// Flatten `Splat` expressions if `is_vector` is true. fn flatten_splat<'c>( component: &'c crate::Handle, is_vector: bool, expressions: &'c crate::Arena, ) -> impl Iterator> { let mut expr = *component; let mut count = 1; if is_vector { if let crate::Expression::Splat { size, value } = expressions[expr] { expr = value; count = size as usize; } } std::iter::repeat(expr).take(count) } // Expressions like `vec4(vec3(vec2(6, 7), 8), 9)` require us to // flatten up to two levels of `Compose` expressions. // // Expressions like `vec4(vec3(1.0), 1.0)` require us to flatten // `Splat` expressions. Fortunately, the operand of a `Splat` must // be a scalar, so we can stop there. components .iter() .flat_map(move |component| flatten_compose(component, is_vector, expressions)) .flat_map(move |component| flatten_compose(component, is_vector, expressions)) .flat_map(move |component| flatten_splat(component, is_vector, expressions)) .take(size) } #[test] fn test_matrix_size() { let module = crate::Module::default(); assert_eq!( crate::TypeInner::Matrix { columns: crate::VectorSize::Tri, rows: crate::VectorSize::Tri, scalar: crate::Scalar::F32, } .size(module.to_ctx()), 48, ); }