use super::{
ast::StructLayout,
error::{Error, ErrorKind},
Span,
};
use crate::{front::align_up, Arena, Constant, Handle, Type, TypeInner, UniqueArena};
#[derive(Debug)]
pub struct TypeAlignSpan {
pub ty: Handle<Type>,
pub align: u32,
pub span: u32,
}
pub fn calculate_offset(
mut ty: Handle<Type>,
meta: Span,
layout: StructLayout,
types: &mut UniqueArena<Type>,
constants: &Arena<Constant>,
errors: &mut Vec<Error>,
) -> TypeAlignSpan {
let (align, span) = match types[ty].inner {
TypeInner::Scalar { width, .. } => (width as u32, width as u32),
TypeInner::Vector { size, width, .. } => match size {
crate::VectorSize::Tri => (4 * width as u32, 3 * width as u32),
_ => (size as u32 * width as u32, size as u32 * width as u32),
},
TypeInner::Array { base, size, .. } => {
let info = calculate_offset(base, meta, layout, types, constants, errors);
let name = types[ty].name.clone();
let mut align = info.align;
let mut stride = (align).max(info.span);
if StructLayout::Std430 != layout {
stride = align_up(stride, 16);
align = align_up(align, 16);
}
let span = match size {
crate::ArraySize::Constant(s) => {
constants[s].to_array_length().unwrap_or(1) * stride
}
crate::ArraySize::Dynamic => stride,
};
let ty_span = types.get_span(ty);
ty = types.insert(
Type {
name,
inner: TypeInner::Array {
base: info.ty,
size,
stride,
},
},
ty_span,
);
(align, span)
}
TypeInner::Matrix {
columns,
rows,
width,
} => {
let mut align = match rows {
crate::VectorSize::Tri => (4 * width as u32),
_ => (rows as u32 * width as u32),
};
if StructLayout::Std430 != layout {
align = align_up(align, 16);
}
(align, align * columns as u32)
}
TypeInner::Struct { ref members, .. } => {
let mut span = 0;
let mut align = 0;
let mut members = members.clone();
let name = types[ty].name.clone();
for member in members.iter_mut() {
let info = calculate_offset(member.ty, meta, layout, types, constants, errors);
span = align_up(span, info.align);
align = align.max(info.align);
member.ty = info.ty;
member.offset = span;
span += info.span;
}
span = align_up(span, align);
let ty_span = types.get_span(ty);
ty = types.insert(
Type {
name,
inner: TypeInner::Struct { members, span },
},
ty_span,
);
(align, span)
}
_ => {
errors.push(Error {
kind: ErrorKind::SemanticError("Invalid struct member type".into()),
meta,
});
(1, 0)
}
};
TypeAlignSpan { ty, align, span }
}