diff --git a/src/proc/layouter.rs b/src/proc/layouter.rs index 2ce334492f..7c1f47438e 100644 --- a/src/proc/layouter.rs +++ b/src/proc/layouter.rs @@ -3,7 +3,7 @@ use std::{num::NonZeroU32, ops}; pub type Alignment = NonZeroU32; -/// Alignment information for a type. +/// Size and alignment information for a type. #[derive(Clone, Copy, Debug, Hash, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Serialize))] #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] @@ -13,12 +13,19 @@ pub struct TypeLayout { } /// Helper processor that derives the sizes of all types. -/// It uses the default layout algorithm/table, described in -/// +/// +/// `Layouter` uses the default layout algorithm/table, described in +/// [WGSL §4.3.7, "Memory Layout"] +/// +/// A `Layouter` may be indexed by `Handle` values: `layouter[handle]` is the +/// layout of the type whose handle is `handle`. +/// +/// [WGSL §4.3.7, "Memory Layout"](https://gpuweb.github.io/gpuweb/wgsl/#memory-layouts) #[derive(Debug, Default)] #[cfg_attr(feature = "serialize", derive(serde::Serialize))] #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] pub struct Layouter { + /// Layouts for types in an arena, indexed by `Handle` index. layouts: Vec, } @@ -30,7 +37,7 @@ impl ops::Index> for Layouter { } #[derive(Clone, Copy, Debug, PartialEq, thiserror::Error)] -pub enum TypeLayoutError { +pub enum LayoutErrorInner { #[error("Array element type {0:?} doesn't exist")] InvalidArrayElementType(Handle), #[error("Struct member[{0}] type {1:?} doesn't exist")] @@ -45,20 +52,22 @@ pub enum TypeLayoutError { #[error("Error laying out type {ty:?}: {inner}")] pub struct LayoutError { pub ty: Handle, - pub inner: TypeLayoutError, + pub inner: LayoutErrorInner, } -impl TypeLayoutError { +impl LayoutErrorInner { fn with(self, ty: Handle) -> LayoutError { LayoutError { ty, inner: self } } } impl Layouter { + /// Remove all entries from this `Layouter`, retaining storage. pub fn clear(&mut self) { self.layouts.clear(); } + /// Round `offset` up to the nearest `alignment` boundary. pub fn round_up(alignment: Alignment, offset: u32) -> u32 { match offset & (alignment.get() - 1) { 0 => offset, @@ -66,6 +75,15 @@ impl Layouter { } } + /// Return the offset and span of a struct member. + /// + /// The member must fall at or after `offset`. The member's alignment and + /// size are `align` and `size` if given, defaulting to the values this + /// `Layouter` has previously determined for `ty`. + /// + /// The return value is the range of offsets within the containing struct to + /// reserve for this member, along with the alignment used. The containing + /// struct must have sufficient space and alignment to accommodate these. pub fn member_placement( &self, offset: u32, @@ -83,6 +101,19 @@ impl Layouter { (start..start + span, alignment) } + /// Extend this `Layouter` with layouts for any new entries in `types`. + /// + /// Ensure that every type in `types` has a corresponding [TypeLayout] in + /// [`self.layouts`]. + /// + /// Some front ends need to be able to compute layouts for existing types + /// while module construction is still in progress and new types are still + /// being added. This function assumes that the `TypeLayout` values already + /// present in `self.layouts` cover their corresponding entries in `types`, + /// and extends `self.layouts` as needed to cover the rest. Thus, a front + /// end can call this function at any time, passing its current type and + /// constant arenas, and then assume that layouts are available for all + /// types. #[allow(clippy::or_fun_call)] pub fn update( &mut self, @@ -95,12 +126,12 @@ impl Layouter { let size = ty .inner .try_size(constants) - .map_err(|error| TypeLayoutError::BadHandle(error).with(ty_handle))?; + .map_err(|error| LayoutErrorInner::BadHandle(error).with(ty_handle))?; let layout = match ty.inner { Ti::Scalar { width, .. } | Ti::Atomic { width, .. } => TypeLayout { size, alignment: Alignment::new(width as u32) - .ok_or(TypeLayoutError::ZeroWidth.with(ty_handle))?, + .ok_or(LayoutErrorInner::ZeroWidth.with(ty_handle))?, }, Ti::Vector { size: vec_size, @@ -115,7 +146,7 @@ impl Layouter { 2 }; Alignment::new(count * width as u32) - .ok_or(TypeLayoutError::ZeroWidth.with(ty_handle))? + .ok_or(LayoutErrorInner::ZeroWidth.with(ty_handle))? }, }, Ti::Matrix { @@ -127,7 +158,7 @@ impl Layouter { alignment: { let count = if rows >= crate::VectorSize::Tri { 4 } else { 2 }; Alignment::new(count * width as u32) - .ok_or(TypeLayoutError::ZeroWidth.with(ty_handle))? + .ok_or(LayoutErrorInner::ZeroWidth.with(ty_handle))? }, }, Ti::Pointer { .. } | Ti::ValuePointer { .. } => TypeLayout { @@ -143,7 +174,7 @@ impl Layouter { alignment: if base < ty_handle { self[base].alignment } else { - return Err(TypeLayoutError::InvalidArrayElementType(base).with(ty_handle)); + return Err(LayoutErrorInner::InvalidArrayElementType(base).with(ty_handle)); }, }, Ti::Struct { span, ref members } => { @@ -152,7 +183,7 @@ impl Layouter { alignment = if member.ty < ty_handle { alignment.max(self[member.ty].alignment) } else { - return Err(TypeLayoutError::InvalidStructMemberType( + return Err(LayoutErrorInner::InvalidStructMemberType( index as u32, member.ty, ) diff --git a/src/proc/mod.rs b/src/proc/mod.rs index c5bb1954b2..47a99cff3f 100644 --- a/src/proc/mod.rs +++ b/src/proc/mod.rs @@ -9,7 +9,7 @@ mod typifier; use std::cmp::PartialEq; pub use index::{BoundsCheckPolicies, BoundsCheckPolicy, IndexableLength, IndexableLengthError}; -pub use layouter::{Alignment, LayoutError, Layouter, TypeLayout, TypeLayoutError}; +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};