New struct layout IR

This commit is contained in:
Dzmitry Malyshau 2021-04-04 10:26:28 -04:00 committed by Dzmitry Malyshau
parent 5a0e3ad5ff
commit 7326ba6ddb
16 changed files with 312 additions and 494 deletions

View File

@ -1389,6 +1389,14 @@ impl<W: Write> Writer<W> {
first_time: false, first_time: false,
}; };
writeln!(self.out, "{}{} {};", INDENT, base_name, member_name)?; writeln!(self.out, "{}{} {};", INDENT, base_name, member_name)?;
// quick and dirty way to figure out if we need this...
if member.binding.is_none() {
let pad =
member.span - module.types[member.ty].inner.span(&module.constants);
if pad != 0 {
writeln!(self.out, "{}char _pad{}[{}];", INDENT, index, pad)?;
}
}
} }
writeln!(self.out, "}};")?; writeln!(self.out, "}};")?;
} }

View File

@ -3,7 +3,7 @@ use super::{
}; };
use crate::{ use crate::{
arena::{Arena, Handle}, arena::{Arena, Handle},
proc::{Layouter, TypeResolution}, proc::TypeResolution,
valid::{FunctionInfo, ModuleInfo}, valid::{FunctionInfo, ModuleInfo},
}; };
use spirv::Word; use spirv::Word;
@ -760,10 +760,10 @@ impl Writer {
fn write_type_declaration_arena( fn write_type_declaration_arena(
&mut self, &mut self,
arena: &Arena<crate::Type>, arena: &Arena<crate::Type>,
layouter: &Layouter,
handle: Handle<crate::Type>, handle: Handle<crate::Type>,
) -> Result<Word, Error> { ) -> Result<Word, Error> {
let ty = &arena[handle]; let ty = &arena[handle];
let decorate_layout = true; //TODO?
let id = if let Some(local) = self.physical_layout.make_local(&ty.inner) { let id = if let Some(local) = self.physical_layout.make_local(&ty.inner) {
match self.lookup_type.entry(LookupType::Local(local)) { match self.lookup_type.entry(LookupType::Local(local)) {
@ -845,11 +845,11 @@ impl Writer {
} }
crate::TypeInner::Sampler { comparison: _ } => Instruction::type_sampler(id), crate::TypeInner::Sampler { comparison: _ } => Instruction::type_sampler(id),
crate::TypeInner::Array { base, size, stride } => { crate::TypeInner::Array { base, size, stride } => {
if let Some(array_stride) = stride { if decorate_layout {
self.annotations.push(Instruction::decorate( self.annotations.push(Instruction::decorate(
id, id,
Decoration::ArrayStride, Decoration::ArrayStride,
&[array_stride.get()], &[stride],
)); ));
} }
@ -871,14 +871,15 @@ impl Writer {
let mut current_offset = 0; let mut current_offset = 0;
let mut member_ids = Vec::with_capacity(members.len()); let mut member_ids = Vec::with_capacity(members.len());
for (index, member) in members.iter().enumerate() { for (index, member) in members.iter().enumerate() {
let (placement, _) = layouter.member_placement(current_offset, member); if decorate_layout {
self.annotations.push(Instruction::member_decorate( self.annotations.push(Instruction::member_decorate(
id, id,
index as u32, index as u32,
Decoration::Offset, Decoration::Offset,
&[placement.start], &[current_offset],
)); ));
current_offset = placement.end; current_offset += member.span;
}
if self.flags.contains(WriterFlags::DEBUG) { if self.flags.contains(WriterFlags::DEBUG) {
if let Some(ref name) = member.name { if let Some(ref name) = member.name {
@ -1073,11 +1074,8 @@ impl Writer {
match *binding { match *binding {
crate::Binding::Location(location, interpolation) => { crate::Binding::Location(location, interpolation) => {
self.annotations.push(Instruction::decorate( self.annotations
id, .push(Instruction::decorate(id, Decoration::Location, &[location]));
Decoration::Location,
&[location],
));
let interp_decoration = match interpolation { let interp_decoration = match interpolation {
Some(crate::Interpolation::Linear) => Some(Decoration::NoPerspective), Some(crate::Interpolation::Linear) => Some(Decoration::NoPerspective),
Some(crate::Interpolation::Flat) => Some(Decoration::Flat), Some(crate::Interpolation::Flat) => Some(Decoration::Flat),
@ -2540,7 +2538,7 @@ impl Writer {
// then all types, some of them may rely on constants and struct type set // then all types, some of them may rely on constants and struct type set
for (handle, _) in ir_module.types.iter() { for (handle, _) in ir_module.types.iter() {
self.write_type_declaration_arena(&ir_module.types, &mod_info.layouter, handle)?; self.write_type_declaration_arena(&ir_module.types, handle)?;
} }
// the all the composite constants, they rely on types // the all the composite constants, they rely on types

View File

@ -729,8 +729,7 @@ pomelo! {
binding: None, //TODO binding: None, //TODO
//TODO: if the struct is a uniform struct, these values have to reflect //TODO: if the struct is a uniform struct, these values have to reflect
// std140 layout. Otherwise, std430. // std140 layout. Otherwise, std430.
size: None, span: extra.module.types[ty].inner.span(&extra.module.constants),
align: None,
}).collect() }).collect()
} else { } else {
return Err(ErrorKind::SemanticError("Struct member can't be void".into())) return Err(ErrorKind::SemanticError("Struct member can't be void".into()))

View File

@ -248,13 +248,7 @@ impl<I: Iterator<Item = u32>> super::Parser<I> {
// unrecognized binding, skip // unrecognized binding, skip
continue; continue;
} }
members.push(crate::StructMember { members.push(sm.clone());
name: sm.name.clone(),
ty: sm.ty,
binding: sm.binding.clone(),
size: None,
align: None,
});
components.push(function.expressions.append( components.push(function.expressions.append(
crate::Expression::AccessIndex { crate::Expression::AccessIndex {
base: expr_handle, base: expr_handle,
@ -268,8 +262,7 @@ impl<I: Iterator<Item = u32>> super::Parser<I> {
name: None, name: None,
ty: result.ty, ty: result.ty,
binding: result.binding.clone(), binding: result.binding.clone(),
size: None, span: module.types[result.ty].inner.span(&module.constants),
align: None,
}); });
// populate just the globals first, then do `Load` in a // populate just the globals first, then do `Load` in a
// separate step, so that we can get a range. // separate step, so that we can get a range.

View File

@ -2419,17 +2419,21 @@ impl<I: Iterator<Item = u32>> Parser<I> {
let length_id = self.next()?; let length_id = self.next()?;
let length_const = self.lookup_constant.lookup(length_id)?; let length_const = self.lookup_constant.lookup(length_id)?;
let decor = self.future_decor.remove(&id); let decor = self.future_decor.remove(&id).unwrap_or_default();
let base = self.lookup_type.lookup(type_id)?.handle;
let inner = crate::TypeInner::Array { let inner = crate::TypeInner::Array {
base: self.lookup_type.lookup(type_id)?.handle, base,
size: crate::ArraySize::Constant(length_const.handle), size: crate::ArraySize::Constant(length_const.handle),
stride: decor.as_ref().and_then(|dec| dec.array_stride), stride: match decor.array_stride {
Some(stride) => stride.get(),
None => module.types[base].inner.span(&module.constants),
},
}; };
self.lookup_type.insert( self.lookup_type.insert(
id, id,
LookupType { LookupType {
handle: module.types.append(crate::Type { handle: module.types.append(crate::Type {
name: decor.and_then(|dec| dec.name), name: decor.name,
inner, inner,
}), }),
base_id: Some(type_id), base_id: Some(type_id),
@ -2448,17 +2452,21 @@ impl<I: Iterator<Item = u32>> Parser<I> {
let id = self.next()?; let id = self.next()?;
let type_id = self.next()?; let type_id = self.next()?;
let decor = self.future_decor.remove(&id); let decor = self.future_decor.remove(&id).unwrap_or_default();
let base = self.lookup_type.lookup(type_id)?.handle;
let inner = crate::TypeInner::Array { let inner = crate::TypeInner::Array {
base: self.lookup_type.lookup(type_id)?.handle, base: self.lookup_type.lookup(type_id)?.handle,
size: crate::ArraySize::Dynamic, size: crate::ArraySize::Dynamic,
stride: decor.as_ref().and_then(|dec| dec.array_stride), stride: match decor.array_stride {
Some(stride) => stride.get(),
None => module.types[base].inner.span(&module.constants),
},
}; };
self.lookup_type.insert( self.lookup_type.insert(
id, id,
LookupType { LookupType {
handle: module.types.append(crate::Type { handle: module.types.append(crate::Type {
name: decor.and_then(|dec| dec.name), name: decor.name,
inner, inner,
}), }),
base_id: Some(type_id), base_id: Some(type_id),
@ -2478,8 +2486,9 @@ impl<I: Iterator<Item = u32>> Parser<I> {
let parent_decor = self.future_decor.remove(&id); let parent_decor = self.future_decor.remove(&id);
let block_decor = parent_decor.as_ref().and_then(|decor| decor.block.clone()); let block_decor = parent_decor.as_ref().and_then(|decor| decor.block.clone());
let mut members = Vec::with_capacity(inst.wc as usize - 2); let mut members = Vec::<crate::StructMember>::with_capacity(inst.wc as usize - 2);
let mut member_type_ids = Vec::with_capacity(members.capacity()); let mut member_type_ids = Vec::with_capacity(members.capacity());
let mut last_offset = 0;
for i in 0..u32::from(inst.wc) - 2 { for i in 0..u32::from(inst.wc) - 2 {
let type_id = self.next()?; let type_id = self.next()?;
member_type_ids.push(type_id); member_type_ids.push(type_id);
@ -2489,12 +2498,24 @@ impl<I: Iterator<Item = u32>> Parser<I> {
.remove(&(id, i)) .remove(&(id, i))
.unwrap_or_default(); .unwrap_or_default();
let binding = decor.io_binding().ok(); let binding = decor.io_binding().ok();
if let Some(offset) = decor.offset {
if offset != last_offset {
if let Some(member) = members.last_mut() {
// add padding, if necessary
member.span = offset - (last_offset - member.span);
}
last_offset = offset;
}
}
let span = module.types[ty].inner.span(&module.constants);
last_offset += span;
members.push(crate::StructMember { members.push(crate::StructMember {
name: decor.name, name: decor.name,
ty, ty,
binding, binding,
size: None, //TODO span,
align: None,
}); });
} }

View File

@ -1,5 +1,5 @@
use crate::arena::{Arena, Handle}; use crate::arena::{Arena, Handle};
use std::{num::NonZeroU32, ops}; use std::num::NonZeroU32;
pub type Alignment = NonZeroU32; pub type Alignment = NonZeroU32;
@ -22,14 +22,17 @@ pub struct Layouter {
layouts: Vec<TypeLayout>, layouts: Vec<TypeLayout>,
} }
pub struct Placement {
pub pad: crate::Span,
pub span: crate::Span,
}
impl Layouter { impl Layouter {
pub fn new(types: &Arena<crate::Type>, constants: &Arena<crate::Constant>) -> Self { pub fn clear(&mut self) {
let mut this = Layouter::default(); self.layouts.clear();
this.initialize(types, constants);
this
} }
pub fn round_up(alignment: NonZeroU32, offset: u32) -> u32 { pub fn round_up(alignment: Alignment, offset: u32) -> u32 {
match offset & (alignment.get() - 1) { match offset & (alignment.get() - 1) {
0 => offset, 0 => offset,
other => offset + alignment.get() - other, other => offset + alignment.get() - other,
@ -39,78 +42,66 @@ impl Layouter {
pub fn member_placement( pub fn member_placement(
&self, &self,
offset: u32, offset: u32,
member: &crate::StructMember, ty: Handle<crate::Type>,
) -> (ops::Range<u32>, NonZeroU32) { align: Option<Alignment>,
let layout = self.layouts[member.ty.index()]; size: Option<NonZeroU32>,
let alignment = member.align.unwrap_or(layout.alignment); ) -> Placement {
let layout = self.layouts[ty.index()];
let alignment = align.unwrap_or(layout.alignment);
let start = Self::round_up(alignment, offset); let start = Self::round_up(alignment, offset);
let end = start let span = match size {
+ match member.size { Some(size) => size.get(),
Some(size) => size.get(), None => layout.size,
None => layout.size, };
}; Placement {
(start..end, alignment) pad: start - offset,
span,
}
} }
pub fn initialize(&mut self, types: &Arena<crate::Type>, constants: &Arena<crate::Constant>) { pub fn update(&mut self, types: &Arena<crate::Type>, constants: &Arena<crate::Constant>) {
use crate::TypeInner as Ti; use crate::TypeInner as Ti;
for (_, ty) in types.iter().skip(self.layouts.len()) {
self.layouts.clear(); let size = ty.inner.span(constants);
self.layouts.reserve(types.len()); let layout = match ty.inner {
Ti::Scalar { width, .. } => TypeLayout {
for (_, ty) in types.iter() { size,
self.layouts.push(match ty.inner {
Ti::Scalar { kind: _, width } => TypeLayout {
size: width as u32,
alignment: Alignment::new(width as u32).unwrap(), alignment: Alignment::new(width as u32).unwrap(),
}, },
Ti::Vector { Ti::Vector {
size, size: vec_size,
kind: _,
width, width,
..
} => TypeLayout { } => TypeLayout {
size: (size as u8 * width) as u32, size,
alignment: { alignment: {
let count = if size >= crate::VectorSize::Tri { 4 } else { 2 }; let count = if vec_size >= crate::VectorSize::Tri {
4
} else {
2
};
Alignment::new((count * width) as u32).unwrap() Alignment::new((count * width) as u32).unwrap()
}, },
}, },
Ti::Matrix { Ti::Matrix {
columns, columns: _,
rows, rows,
width, width,
} => TypeLayout { } => TypeLayout {
size: (columns as u8 * rows as u8 * width) as u32, size,
alignment: { alignment: {
let count = if rows >= crate::VectorSize::Tri { 4 } else { 2 }; let count = if rows >= crate::VectorSize::Tri { 4 } else { 2 };
Alignment::new((count * width) as u32).unwrap() Alignment::new((count * width) as u32).unwrap()
}, },
}, },
Ti::Pointer { .. } | Ti::ValuePointer { .. } => TypeLayout { Ti::Pointer { .. } | Ti::ValuePointer { .. } => TypeLayout {
size: 4, size,
alignment: Alignment::new(1).unwrap(), alignment: Alignment::new(1).unwrap(),
}, },
Ti::Array { base, size, stride } => { Ti::Array { stride, .. } => TypeLayout {
let count = match size { size,
crate::ArraySize::Constant(handle) => { alignment: Alignment::new(stride).unwrap(),
constants[handle].to_array_length().unwrap() },
}
// A dynamically-sized array has to have at least one element
crate::ArraySize::Dynamic => 1,
};
let stride = match stride {
Some(value) => value,
None => {
let layout = &self.layouts[base.index()];
let stride = Self::round_up(layout.alignment, layout.size);
Alignment::new(stride).unwrap()
}
};
TypeLayout {
size: count * stride.get(),
alignment: stride,
}
}
Ti::Struct { Ti::Struct {
block: _, block: _,
ref members, ref members,
@ -118,9 +109,9 @@ impl Layouter {
let mut total = 0; let mut total = 0;
let mut biggest_alignment = Alignment::new(1).unwrap(); let mut biggest_alignment = Alignment::new(1).unwrap();
for member in members { for member in members {
let (placement, alignment) = self.member_placement(total, member); let layout = self.layouts[member.ty.index()];
biggest_alignment = biggest_alignment.max(alignment); biggest_alignment = biggest_alignment.max(layout.alignment);
total = placement.end; total += member.span;
} }
TypeLayout { TypeLayout {
size: Self::round_up(biggest_alignment, total), size: Self::round_up(biggest_alignment, total),
@ -128,17 +119,12 @@ impl Layouter {
} }
} }
Ti::Image { .. } | Ti::Sampler { .. } => TypeLayout { Ti::Image { .. } | Ti::Sampler { .. } => TypeLayout {
size: 0, size,
alignment: Alignment::new(1).unwrap(), alignment: Alignment::new(1).unwrap(),
}, },
}); };
debug_assert!(ty.inner.span(constants) <= layout.size);
self.layouts.push(layout);
} }
} }
} }
impl ops::Index<Handle<crate::Type>> for Layouter {
type Output = TypeLayout;
fn index(&self, handle: Handle<crate::Type>) -> &TypeLayout {
&self.layouts[handle.index()]
}
}

View File

@ -3,6 +3,7 @@
//! [wgsl]: https://gpuweb.github.io/gpuweb/wgsl.html //! [wgsl]: https://gpuweb.github.io/gpuweb/wgsl.html
mod conv; mod conv;
mod layout;
mod lexer; mod lexer;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -560,6 +561,7 @@ impl<'a> std::error::Error for ParseError<'a> {
pub struct Parser { pub struct Parser {
scopes: Vec<Scope>, scopes: Vec<Scope>,
lookup_type: FastHashMap<String, Handle<crate::Type>>, lookup_type: FastHashMap<String, Handle<crate::Type>>,
layouter: layout::Layouter,
} }
impl Parser { impl Parser {
@ -567,6 +569,7 @@ impl Parser {
Parser { Parser {
scopes: Vec::new(), scopes: Vec::new(),
lookup_type: FastHashMap::default(), lookup_type: FastHashMap::default(),
layouter: Default::default(),
} }
} }
@ -1507,7 +1510,9 @@ impl Parser {
type_arena: &mut Arena<crate::Type>, type_arena: &mut Arena<crate::Type>,
const_arena: &mut Arena<crate::Constant>, const_arena: &mut Arena<crate::Constant>,
) -> Result<Vec<crate::StructMember>, Error<'a>> { ) -> Result<Vec<crate::StructMember>, Error<'a>> {
let mut offset = 0;
let mut members = Vec::new(); let mut members = Vec::new();
lexer.expect(Token::Paren('{'))?; lexer.expect(Token::Paren('{'))?;
loop { loop {
let (mut size, mut align) = (None, None); let (mut size, mut align) = (None, None);
@ -1558,12 +1563,18 @@ impl Parser {
let (ty, _access) = self.parse_type_decl(lexer, None, type_arena, const_arena)?; let (ty, _access) = self.parse_type_decl(lexer, None, type_arena, const_arena)?;
lexer.expect(Token::Separator(';'))?; lexer.expect(Token::Separator(';'))?;
self.layouter.update(type_arena, const_arena);
let placement = self.layouter.member_placement(offset, ty, align, size);
if placement.pad != 0 {
members.last_mut().unwrap().span += placement.pad;
}
offset += placement.pad + placement.span;
members.push(crate::StructMember { members.push(crate::StructMember {
name: Some(name.to_owned()), name: Some(name.to_owned()),
ty, ty,
binding: bind_parser.finish()?, binding: bind_parser.finish()?,
size, span: placement.span,
align,
}); });
} }
} }
@ -1708,12 +1719,12 @@ impl Parser {
crate::ArraySize::Dynamic crate::ArraySize::Dynamic
}; };
lexer.expect_generic_paren('>')?; lexer.expect_generic_paren('>')?;
let stride = match attribute.stride {
Some(stride) => stride.get(),
None => type_arena[base].inner.span(const_arena),
};
crate::TypeInner::Array { crate::TypeInner::Array { base, size, stride }
base,
size,
stride: attribute.stride,
}
} }
"sampler" => crate::TypeInner::Sampler { comparison: false }, "sampler" => crate::TypeInner::Sampler { comparison: false },
"sampler_comparison" => crate::TypeInner::Sampler { comparison: true }, "sampler_comparison" => crate::TypeInner::Sampler { comparison: true },
@ -1916,14 +1927,8 @@ impl Parser {
let storage_access = attribute.access; let storage_access = attribute.access;
let name = lexer.next_ident()?; let name = lexer.next_ident()?;
let handle = self.parse_type_decl_name( let handle =
lexer, self.parse_type_decl_name(lexer, name, debug_name, attribute, type_arena, const_arena)?;
name,
debug_name,
attribute,
type_arena,
const_arena,
)?;
self.scopes.pop(); self.scopes.pop();
Ok((handle, storage_access)) Ok((handle, storage_access))
} }
@ -2671,6 +2676,7 @@ impl Parser {
pub fn parse<'a>(&mut self, source: &'a str) -> Result<crate::Module, ParseError<'a>> { pub fn parse<'a>(&mut self, source: &'a str) -> Result<crate::Module, ParseError<'a>> {
self.scopes.clear(); self.scopes.clear();
self.lookup_type.clear(); self.lookup_type.clear();
self.layouter.clear();
let mut module = crate::Module::default(); let mut module = crate::Module::default();
let mut lexer = Lexer::new(source); let mut lexer = Lexer::new(source);

View File

@ -50,7 +50,6 @@ pub use crate::arena::{Arena, Handle, Range};
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
hash::BuildHasherDefault, hash::BuildHasherDefault,
num::NonZeroU32,
}; };
#[cfg(feature = "deserialize")] #[cfg(feature = "deserialize")]
@ -166,8 +165,10 @@ pub enum BuiltIn {
WorkGroupSize, WorkGroupSize,
} }
/// Number of bytes. /// Number of bytes per scalar.
pub type Bytes = u8; pub type Bytes = u8;
/// Number of bytes per complex type.
pub type Span = u32;
/// Number of components in a vector. /// Number of components in a vector.
#[repr(u8)] #[repr(u8)]
@ -244,10 +245,8 @@ pub struct StructMember {
pub ty: Handle<Type>, pub ty: Handle<Type>,
/// For I/O structs, defines the binding. /// For I/O structs, defines the binding.
pub binding: Option<Binding>, pub binding: Option<Binding>,
/// Overrides the size computed off the type. /// Size occupied by the member.
pub size: Option<NonZeroU32>, pub span: Span,
/// Overrides the alignment computed off the type.
pub align: Option<NonZeroU32>,
} }
/// The number of dimensions an image has. /// The number of dimensions an image has.
@ -392,7 +391,7 @@ pub enum TypeInner {
Array { Array {
base: Handle<Type>, base: Handle<Type>,
size: ArraySize, size: ArraySize,
stride: Option<NonZeroU32>, stride: Span,
}, },
/// User-defined structure. /// User-defined structure.
Struct { Struct {

View File

@ -1,11 +1,9 @@
//! Module processing functionality. //! Module processing functionality.
mod layouter;
mod namer; mod namer;
mod terminator; mod terminator;
mod typifier; mod typifier;
pub use layouter::{Alignment, Layouter, TypeLayout};
pub use namer::{EntryPointIndex, NameKey, Namer}; pub use namer::{EntryPointIndex, NameKey, Namer};
pub use terminator::ensure_block_returns; pub use terminator::ensure_block_returns;
pub use typifier::{ResolveContext, ResolveError, TypeResolution}; pub use typifier::{ResolveContext, ResolveError, TypeResolution};
@ -61,6 +59,8 @@ impl super::ScalarValue {
} }
} }
pub const POINTER_SPAN: u32 = 4;
impl super::TypeInner { impl super::TypeInner {
pub fn scalar_kind(&self) -> Option<super::ScalarKind> { pub fn scalar_kind(&self) -> Option<super::ScalarKind> {
match *self { match *self {
@ -71,6 +71,50 @@ impl super::TypeInner {
_ => None, _ => None,
} }
} }
pub fn span(&self, constants: &super::Arena<super::Constant>) -> u32 {
match *self {
Self::Scalar { kind: _, width } => width as u32,
Self::Vector {
size,
kind: _,
width,
} => {
//let count = if size >= super::VectorSize::Tri { 4 } else { 2 };
let count = size as u8; //TEMP
(count * width) as u32
}
Self::Matrix {
columns,
rows,
width,
} => {
//let count = if rows >= super::VectorSize::Tri { 4 } else { 2 };
let count = rows as u8; //TEMP
(columns as u8 * count * width) as u32
}
Self::Pointer { .. } | Self::ValuePointer { .. } => POINTER_SPAN,
Self::Array {
base: _,
size,
stride,
} => {
let count = match size {
super::ArraySize::Constant(handle) => {
constants[handle].to_array_length().unwrap()
}
// A dynamically-sized array has to have at least one element
super::ArraySize::Dynamic => 1,
};
count * stride
}
Self::Struct {
block: _,
ref members,
} => members.iter().map(|m| m.span).sum(),
Self::Image { .. } | Self::Sampler { .. } => 0,
}
}
} }
impl super::MathFunction { impl super::MathFunction {

View File

@ -272,12 +272,9 @@ impl super::Validator {
let (allowed_storage_access, required_type_flags, is_resource) = match var.class { let (allowed_storage_access, required_type_flags, is_resource) = match var.class {
crate::StorageClass::Function => return Err(GlobalVariableError::InvalidUsage), crate::StorageClass::Function => return Err(GlobalVariableError::InvalidUsage),
crate::StorageClass::Storage => { crate::StorageClass::Storage => {
if let Err((ty_handle, ref disalignment)) = type_info.storage_layout { if let Err((ty_handle, disalignment)) = type_info.storage_layout {
if self.flags.contains(ValidationFlags::STRUCT_LAYOUTS) { if self.flags.contains(ValidationFlags::STRUCT_LAYOUTS) {
return Err(GlobalVariableError::Alignment( return Err(GlobalVariableError::Alignment(ty_handle, disalignment));
ty_handle,
disalignment.clone(),
));
} }
} }
( (
@ -287,12 +284,9 @@ impl super::Validator {
) )
} }
crate::StorageClass::Uniform => { crate::StorageClass::Uniform => {
if let Err((ty_handle, ref disalignment)) = type_info.uniform_layout { if let Err((ty_handle, disalignment)) = type_info.uniform_layout {
if self.flags.contains(ValidationFlags::STRUCT_LAYOUTS) { if self.flags.contains(ValidationFlags::STRUCT_LAYOUTS) {
return Err(GlobalVariableError::Alignment( return Err(GlobalVariableError::Alignment(ty_handle, disalignment));
ty_handle,
disalignment.clone(),
));
} }
} }
( (

View File

@ -6,7 +6,6 @@ mod r#type;
use crate::{ use crate::{
arena::{Arena, Handle}, arena::{Arena, Handle},
proc::Layouter,
FastHashSet, FastHashSet,
}; };
use bit_set::BitSet; use bit_set::BitSet;
@ -54,7 +53,6 @@ bitflags::bitflags! {
pub struct ModuleInfo { pub struct ModuleInfo {
functions: Vec<FunctionInfo>, functions: Vec<FunctionInfo>,
entry_points: Vec<FunctionInfo>, entry_points: Vec<FunctionInfo>,
pub layouter: Layouter,
} }
impl ops::Index<Handle<crate::Function>> for ModuleInfo { impl ops::Index<Handle<crate::Function>> for ModuleInfo {
@ -221,8 +219,6 @@ impl Validator {
pub fn validate(&mut self, module: &crate::Module) -> Result<ModuleInfo, ValidationError> { pub fn validate(&mut self, module: &crate::Module) -> Result<ModuleInfo, ValidationError> {
self.reset_types(module.types.len()); self.reset_types(module.types.len());
let layouter = Layouter::new(&module.types, &module.constants);
for (handle, constant) in module.constants.iter() { for (handle, constant) in module.constants.iter() {
self.validate_constant(handle, &module.constants, &module.types) self.validate_constant(handle, &module.constants, &module.types)
.map_err(|error| ValidationError::Constant { .map_err(|error| ValidationError::Constant {
@ -235,7 +231,7 @@ impl Validator {
// doing after the globals, so that `type_flags` is ready // doing after the globals, so that `type_flags` is ready
for (handle, ty) in module.types.iter() { for (handle, ty) in module.types.iter() {
let ty_info = self let ty_info = self
.validate_type(ty, handle, &module.constants, &layouter) .validate_type(handle, &module.types, &module.constants)
.map_err(|error| ValidationError::Type { .map_err(|error| ValidationError::Type {
handle, handle,
name: ty.name.clone().unwrap_or_default(), name: ty.name.clone().unwrap_or_default(),
@ -256,7 +252,6 @@ impl Validator {
let mut mod_info = ModuleInfo { let mut mod_info = ModuleInfo {
functions: Vec::with_capacity(module.functions.len()), functions: Vec::with_capacity(module.functions.len()),
entry_points: Vec::with_capacity(module.entry_points.len()), entry_points: Vec::with_capacity(module.entry_points.len()),
layouter,
}; };
for (handle, fun) in module.functions.iter() { for (handle, fun) in module.functions.iter() {

View File

@ -1,7 +1,6 @@
use crate::{ use crate::arena::{Arena, Handle};
arena::{Arena, Handle},
proc::Layouter, pub type Alignment = u32;
};
bitflags::bitflags! { bitflags::bitflags! {
#[repr(transparent)] #[repr(transparent)]
@ -19,7 +18,7 @@ bitflags::bitflags! {
} }
} }
#[derive(Clone, Debug, thiserror::Error)] #[derive(Clone, Copy, Debug, thiserror::Error)]
pub enum Disalignment { pub enum Disalignment {
#[error("The array stride {stride} is not a multiple of the required alignment {alignment}")] #[error("The array stride {stride} is not a multiple of the required alignment {alignment}")]
ArrayStride { stride: u32, alignment: u32 }, ArrayStride { stride: u32, alignment: u32 },
@ -49,10 +48,6 @@ pub enum TypeError {
InvalidArrayBaseType(Handle<crate::Type>), InvalidArrayBaseType(Handle<crate::Type>),
#[error("The constant {0:?} can not be used for an array size")] #[error("The constant {0:?} can not be used for an array size")]
InvalidArraySizeConstant(Handle<crate::Constant>), InvalidArraySizeConstant(Handle<crate::Constant>),
#[error(
"Array stride {stride} is not a multiple of the base element alignment {base_alignment}"
)]
UnalignedArrayStride { stride: u32, base_alignment: u32 },
#[error("Array stride {stride} is smaller than the base element size {base_size}")] #[error("Array stride {stride} is smaller than the base element size {base_size}")]
InsufficientArrayStride { stride: u32, base_size: u32 }, InsufficientArrayStride { stride: u32, base_size: u32 },
#[error("Field '{0}' can't be dynamically-sized, has type {1:?}")] #[error("Field '{0}' can't be dynamically-sized, has type {1:?}")]
@ -68,7 +63,7 @@ pub enum TypeError {
} }
// Only makes sense if `flags.contains(HOST_SHARED)` // Only makes sense if `flags.contains(HOST_SHARED)`
type LayoutCompatibility = Result<(), (Handle<crate::Type>, Disalignment)>; type LayoutCompatibility = Result<Alignment, (Handle<crate::Type>, Disalignment)>;
// For the uniform buffer alignment, array strides and struct sizes must be multiples of 16. // For the uniform buffer alignment, array strides and struct sizes must be multiples of 16.
const UNIFORM_LAYOUT_ALIGNMENT_MASK: u32 = 0xF; const UNIFORM_LAYOUT_ALIGNMENT_MASK: u32 = 0xF;
@ -81,19 +76,19 @@ pub(super) struct TypeInfo {
} }
impl TypeInfo { impl TypeInfo {
fn new() -> Self { fn dummy() -> Self {
TypeInfo { TypeInfo {
flags: TypeFlags::empty(), flags: TypeFlags::empty(),
uniform_layout: Ok(()), uniform_layout: Ok(0),
storage_layout: Ok(()), storage_layout: Ok(0),
} }
} }
fn from_flags(flags: TypeFlags) -> Self { fn new(flags: TypeFlags, alignment: crate::Span) -> Self {
TypeInfo { TypeInfo {
flags, flags,
uniform_layout: Ok(()), uniform_layout: Ok(alignment),
storage_layout: Ok(()), storage_layout: Ok(alignment),
} }
} }
} }
@ -108,45 +103,64 @@ impl super::Validator {
pub(super) fn reset_types(&mut self, size: usize) { pub(super) fn reset_types(&mut self, size: usize) {
self.types.clear(); self.types.clear();
self.types.resize(size, TypeInfo::new()); self.types.resize(size, TypeInfo::dummy());
} }
pub(super) fn validate_type( pub(super) fn validate_type(
&self, &self,
ty: &crate::Type,
handle: Handle<crate::Type>, handle: Handle<crate::Type>,
types: &Arena<crate::Type>,
constants: &Arena<crate::Constant>, constants: &Arena<crate::Constant>,
layouter: &Layouter,
) -> Result<TypeInfo, TypeError> { ) -> Result<TypeInfo, TypeError> {
use crate::TypeInner as Ti; use crate::TypeInner as Ti;
Ok(match ty.inner { Ok(match types[handle].inner {
Ti::Scalar { kind, width } | Ti::Vector { kind, width, .. } => { Ti::Scalar { kind, width } => {
if !Self::check_width(kind, width) { if !Self::check_width(kind, width) {
return Err(TypeError::InvalidWidth(kind, width)); return Err(TypeError::InvalidWidth(kind, width));
} }
TypeInfo::from_flags( TypeInfo::new(
TypeFlags::DATA TypeFlags::DATA
| TypeFlags::SIZED | TypeFlags::SIZED
| TypeFlags::INTERFACE | TypeFlags::INTERFACE
| TypeFlags::HOST_SHARED, | TypeFlags::HOST_SHARED,
width as u32,
) )
} }
Ti::Matrix { width, .. } => { Ti::Vector { size, kind, width } => {
if !Self::check_width(kind, width) {
return Err(TypeError::InvalidWidth(kind, width));
}
let count = if size >= crate::VectorSize::Tri { 4 } else { 2 };
TypeInfo::new(
TypeFlags::DATA
| TypeFlags::SIZED
| TypeFlags::INTERFACE
| TypeFlags::HOST_SHARED,
count * (width as u32),
)
}
Ti::Matrix {
columns: _,
rows,
width,
} => {
if !Self::check_width(crate::ScalarKind::Float, width) { if !Self::check_width(crate::ScalarKind::Float, width) {
return Err(TypeError::InvalidWidth(crate::ScalarKind::Float, width)); return Err(TypeError::InvalidWidth(crate::ScalarKind::Float, width));
} }
TypeInfo::from_flags( let count = if rows >= crate::VectorSize::Tri { 4 } else { 2 };
TypeInfo::new(
TypeFlags::DATA TypeFlags::DATA
| TypeFlags::SIZED | TypeFlags::SIZED
| TypeFlags::INTERFACE | TypeFlags::INTERFACE
| TypeFlags::HOST_SHARED, | TypeFlags::HOST_SHARED,
count * (width as u32),
) )
} }
Ti::Pointer { base, class: _ } => { Ti::Pointer { base, class: _ } => {
if base >= handle { if base >= handle {
return Err(TypeError::UnresolvedBase(base)); return Err(TypeError::UnresolvedBase(base));
} }
TypeInfo::from_flags(TypeFlags::DATA | TypeFlags::SIZED) TypeInfo::new(TypeFlags::DATA | TypeFlags::SIZED, 0)
} }
Ti::ValuePointer { Ti::ValuePointer {
size: _, size: _,
@ -157,7 +171,7 @@ impl super::Validator {
if !Self::check_width(kind, width) { if !Self::check_width(kind, width) {
return Err(TypeError::InvalidWidth(kind, width)); return Err(TypeError::InvalidWidth(kind, width));
} }
TypeInfo::from_flags(TypeFlags::SIZED) TypeInfo::new(TypeFlags::SIZED, 0)
} }
Ti::Array { base, size, stride } => { Ti::Array { base, size, stride } => {
if base >= handle { if base >= handle {
@ -171,23 +185,35 @@ impl super::Validator {
return Err(TypeError::NestedBlock); return Err(TypeError::NestedBlock);
} }
let base_layout = &layouter[base]; let base_size = types[base].inner.span(constants);
if let Some(stride) = stride { if stride < base_size {
if stride.get() % base_layout.alignment.get() != 0 { return Err(TypeError::InsufficientArrayStride { stride, base_size });
return Err(TypeError::UnalignedArrayStride {
stride: stride.get(),
base_alignment: base_layout.alignment.get(),
});
}
if stride.get() < base_layout.size {
return Err(TypeError::InsufficientArrayStride {
stride: stride.get(),
base_size: base_layout.size,
});
}
} }
let (sized_flag, uniform_layout) = match size { let uniform_layout = match base_info.uniform_layout {
Ok(base_alignment) => {
// combine the alignment requirements
let alignment = ((base_alignment - 1) | UNIFORM_LAYOUT_ALIGNMENT_MASK) + 1;
if stride % alignment != 0 {
Err((handle, Disalignment::ArrayStride { stride, alignment }))
} else {
Ok(alignment)
}
}
Err(e) => Err(e),
};
let storage_layout = match base_info.storage_layout {
Ok(alignment) => {
if stride % alignment != 0 {
Err((handle, Disalignment::ArrayStride { stride, alignment }))
} else {
Ok(alignment)
}
}
Err(e) => Err(e),
};
let sized_flag = match size {
crate::ArraySize::Constant(const_handle) => { crate::ArraySize::Constant(const_handle) => {
match constants.try_get(const_handle) { match constants.try_get(const_handle) {
Some(&crate::Constant { Some(&crate::Constant {
@ -216,33 +242,17 @@ impl super::Validator {
} }
} }
let effective_stride = match stride { TypeFlags::SIZED
Some(stride) => stride.get(),
None => base_layout.size,
};
let uniform_layout =
if effective_stride & UNIFORM_LAYOUT_ALIGNMENT_MASK == 0 {
base_info.uniform_layout.clone()
} else {
Err((
handle,
Disalignment::ArrayStride {
stride: effective_stride,
alignment: UNIFORM_LAYOUT_ALIGNMENT_MASK + 1,
},
))
};
(TypeFlags::SIZED, uniform_layout)
} }
//Note: this will be detected at the struct level //Note: this will be detected at the struct level
crate::ArraySize::Dynamic => (TypeFlags::empty(), Ok(())), crate::ArraySize::Dynamic => TypeFlags::empty(),
}; };
let base_mask = TypeFlags::HOST_SHARED | TypeFlags::INTERFACE; let base_mask = TypeFlags::HOST_SHARED | TypeFlags::INTERFACE;
TypeInfo { TypeInfo {
flags: TypeFlags::DATA | (base_info.flags & base_mask) | sized_flag, flags: TypeFlags::DATA | (base_info.flags & base_mask) | sized_flag,
uniform_layout, uniform_layout,
storage_layout: base_info.storage_layout.clone(), storage_layout,
} }
} }
Ti::Struct { block, ref members } => { Ti::Struct { block, ref members } => {
@ -250,8 +260,8 @@ impl super::Validator {
| TypeFlags::SIZED | TypeFlags::SIZED
| TypeFlags::HOST_SHARED | TypeFlags::HOST_SHARED
| TypeFlags::INTERFACE; | TypeFlags::INTERFACE;
let mut uniform_layout = Ok(()); let mut uniform_layout = Ok(1);
let mut storage_layout = Ok(()); let mut storage_layout = Ok(1);
let mut offset = 0; let mut offset = 0;
for (i, member) in members.iter().enumerate() { for (i, member) in members.iter().enumerate() {
if member.ty >= handle { if member.ty >= handle {
@ -269,28 +279,54 @@ impl super::Validator {
} }
flags &= base_info.flags; flags &= base_info.flags;
let base_layout = &layouter[member.ty]; let base_size = types[member.ty].inner.span(constants);
let (range, _alignment) = layouter.member_placement(offset, member); if member.span < base_size {
if range.end - range.start < base_layout.size {
return Err(TypeError::InsufficientMemberSize { return Err(TypeError::InsufficientMemberSize {
index: i as u32, index: i as u32,
size: range.end - range.start, size: member.span,
base_size: base_layout.size, base_size,
}); });
} }
if range.start % base_layout.alignment.get() != 0 {
let result = Err(( uniform_layout = match (uniform_layout, base_info.uniform_layout) {
handle, (Ok(cur_alignment), Ok(alignment)) => {
Disalignment::Member { if offset % alignment != 0 {
index: i as u32, Err((
offset: range.start, handle,
alignment: base_layout.alignment.get(), Disalignment::Member {
}, index: i as u32,
)); offset,
uniform_layout = uniform_layout.or_else(|_| result.clone()); alignment,
storage_layout = storage_layout.or(result); },
} ))
offset = range.end; } else {
let combined_alignment =
((cur_alignment - 1) | (alignment - 1)) + 1;
Ok(combined_alignment)
}
}
(Err(e), _) | (_, Err(e)) => Err(e),
};
storage_layout = match (storage_layout, base_info.storage_layout) {
(Ok(cur_alignment), Ok(alignment)) => {
if offset % alignment != 0 {
Err((
handle,
Disalignment::Member {
index: i as u32,
offset,
alignment,
},
))
} else {
let combined_alignment =
((cur_alignment - 1) | (alignment - 1)) + 1;
Ok(combined_alignment)
}
}
(Err(e), _) | (_, Err(e)) => Err(e),
};
offset += member.span;
// only the last field can be unsized // only the last field can be unsized
if !base_info.flags.contains(TypeFlags::SIZED) { if !base_info.flags.contains(TypeFlags::SIZED) {
@ -303,9 +339,6 @@ impl super::Validator {
Err((handle, Disalignment::UnsizedMember { index: i as u32 })); Err((handle, Disalignment::UnsizedMember { index: i as u32 }));
} }
} }
uniform_layout = uniform_layout.or_else(|_| base_info.uniform_layout.clone());
storage_layout = storage_layout.or_else(|_| base_info.storage_layout.clone());
} }
if block { if block {
flags |= TypeFlags::BLOCK; flags |= TypeFlags::BLOCK;
@ -331,7 +364,7 @@ impl super::Validator {
storage_layout, storage_layout,
} }
} }
Ti::Image { .. } | Ti::Sampler { .. } => TypeInfo::from_flags(TypeFlags::empty()), Ti::Image { .. } | Ti::Sampler { .. } => TypeInfo::new(TypeFlags::empty(), 0),
}) })
} }
} }

View File

@ -498,24 +498,4 @@ expression: output
], ],
), ),
], ],
layouter: (
layouts: [
(
size: 4,
alignment: 4,
),
(
size: 4,
alignment: 4,
),
(
size: 4,
alignment: 4,
),
(
size: 12,
alignment: 16,
),
],
),
) )

View File

@ -16,7 +16,7 @@ expression: output
inner: Array( inner: Array(
base: 1, base: 1,
size: Dynamic, size: Dynamic,
stride: Some(4), stride: 4,
), ),
), ),
( (
@ -28,8 +28,7 @@ expression: output
name: Some("data"), name: Some("data"),
ty: 2, ty: 2,
binding: None, binding: None,
size: None, span: 4,
align: None,
), ),
], ],
), ),

View File

@ -2873,236 +2873,4 @@ expression: output
], ],
), ),
], ],
layouter: (
layouts: [
(
size: 4,
alignment: 4,
),
(
size: 12,
alignment: 16,
),
(
size: 4,
alignment: 4,
),
(
size: 16,
alignment: 16,
),
(
size: 1,
alignment: 1,
),
(
size: 8,
alignment: 8,
),
(
size: 0,
alignment: 1,
),
(
size: 0,
alignment: 1,
),
(
size: 8,
alignment: 8,
),
(
size: 4,
alignment: 4,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 16,
alignment: 16,
),
(
size: 16,
alignment: 16,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 64,
alignment: 16,
),
(
size: 96,
alignment: 16,
),
(
size: 96,
alignment: 96,
),
(
size: 128,
alignment: 96,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 4,
alignment: 1,
),
(
size: 0,
alignment: 1,
),
(
size: 0,
alignment: 1,
),
],
),
) )

View File

@ -112,8 +112,7 @@ expression: output
name: Some("num_lights"), name: Some("num_lights"),
ty: 13, ty: 13,
binding: None, binding: None,
size: None, span: 16,
align: None,
), ),
], ],
), ),
@ -156,22 +155,19 @@ expression: output
name: Some("proj"), name: Some("proj"),
ty: 18, ty: 18,
binding: None, binding: None,
size: None, span: 64,
align: None,
), ),
( (
name: Some("pos"), name: Some("pos"),
ty: 4, ty: 4,
binding: None, binding: None,
size: None, span: 16,
align: None,
), ),
( (
name: Some("color"), name: Some("color"),
ty: 4, ty: 4,
binding: None, binding: None,
size: None, span: 16,
align: None,
), ),
], ],
), ),
@ -181,7 +177,7 @@ expression: output
inner: Array( inner: Array(
base: 19, base: 19,
size: Dynamic, size: Dynamic,
stride: Some(96), stride: 96,
), ),
), ),
( (
@ -193,8 +189,7 @@ expression: output
name: Some("data"), name: Some("data"),
ty: 20, ty: 20,
binding: None, binding: None,
size: None, span: 96,
align: None,
), ),
], ],
), ),