Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compute layout of datatypes according to C ABI #181

Merged
merged 6 commits into from
Dec 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 66 additions & 29 deletions tools/witx/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use std::collections::HashMap;

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SizeAlign {
size: usize,
align: usize,
pub size: usize,
pub align: usize,
}

pub trait Layout {
Expand All @@ -23,14 +23,14 @@ impl TypeRef {
return *hit;
}
let layout = match &*self.type_() {
Type::Enum(e) => e.repr.layout(),
Type::Flags(f) => f.repr.layout(),
Type::Enum(e) => e.repr.mem_size_align(),
Type::Flags(f) => f.repr.mem_size_align(),
Type::Struct(s) => s.layout(cache),
Type::Union(u) => u.layout(cache),
Type::Handle { .. } => BuiltinType::U32.layout(),
Type::Array { .. } => BuiltinType::String.layout(),
Type::Pointer { .. } | Type::ConstPointer { .. } => BuiltinType::U32.layout(),
Type::Builtin(b) => b.layout(),
Type::Handle(h) => h.mem_size_align(),
Type::Array { .. } => BuiltinType::String.mem_size_align(),
Type::Pointer { .. } | Type::ConstPointer { .. } => BuiltinType::U32.mem_size_align(),
Type::Builtin(b) => b.mem_size_align(),
};
cache.insert(self.clone(), layout);
layout
Expand All @@ -43,28 +43,28 @@ impl Layout for TypeRef {
self.layout(&mut cache)
}
}

impl IntRepr {
pub fn layout(&self) -> SizeAlign {
impl Layout for IntRepr {
fn mem_size_align(&self) -> SizeAlign {
match self {
IntRepr::U8 => BuiltinType::U8.layout(),
IntRepr::U16 => BuiltinType::U16.layout(),
IntRepr::U32 => BuiltinType::U32.layout(),
IntRepr::U64 => BuiltinType::U64.layout(),
IntRepr::U8 => BuiltinType::U8.mem_size_align(),
IntRepr::U16 => BuiltinType::U16.mem_size_align(),
IntRepr::U32 => BuiltinType::U32.mem_size_align(),
IntRepr::U64 => BuiltinType::U64.mem_size_align(),
}
}
}

pub struct StructMemberLayout<'a> {
member: &'a StructMember,
offset: usize,
pub member: &'a StructMember,
pub offset: usize,
}

impl StructDatatype {
pub fn member_layout(
&self,
cache: &mut HashMap<TypeRef, SizeAlign>,
) -> Vec<StructMemberLayout> {
pub fn member_layout(&self) -> Vec<StructMemberLayout> {
self.member_layout_(&mut HashMap::new())
}

fn member_layout_(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> Vec<StructMemberLayout> {
let mut members = Vec::new();
let mut offset = 0;
for m in self.members.iter() {
Expand All @@ -76,19 +76,27 @@ impl StructDatatype {
members
}

pub fn layout(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> SizeAlign {
let members = self.member_layout(cache);
fn layout(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> SizeAlign {
let members = self.member_layout_(cache);
let align = members
.iter()
.map(|m| m.member.tref.layout(cache).align)
.max()
.expect("nonzero struct members");
let last_offset = members.last().expect("nonzero struct members").offset;
let size = align_to(last_offset, align);
let last = members.last().expect("nonzero struct members");
let size = last.offset + last.member.tref.layout(cache).size;
let size = align_to(size, align);
SizeAlign { size, align }
}
}

impl Layout for StructDatatype {
fn mem_size_align(&self) -> SizeAlign {
let mut cache = HashMap::new();
self.layout(&mut cache)
}
}

/// If the next free byte in the struct is `offs`, and the next
/// element has alignment `alignment`, determine the offset at
/// which to place that element.
Expand Down Expand Up @@ -124,13 +132,42 @@ mod test {
}

impl UnionDatatype {
pub fn layout(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> SizeAlign {
unimplemented!()
fn layout(&self, cache: &mut HashMap<TypeRef, SizeAlign>) -> SizeAlign {
let sas = self
.variants
.iter()
.map(|v| v.tref.layout(cache))
.collect::<Vec<SizeAlign>>();
let size = sas
.iter()
.map(|sa| sa.size)
.max()
.expect("nonzero variants");
let align = sas
.iter()
.map(|sa| sa.align)
.max()
.expect("nonzero variants");
let size = align_to(size, align);
SizeAlign { size, align }
}
}

impl Layout for UnionDatatype {
fn mem_size_align(&self) -> SizeAlign {
let mut cache = HashMap::new();
self.layout(&mut cache)
}
}

impl BuiltinType {
pub fn layout(&self) -> SizeAlign {
impl Layout for HandleDatatype {
fn mem_size_align(&self) -> SizeAlign {
BuiltinType::U32.mem_size_align()
}
}

impl Layout for BuiltinType {
fn mem_size_align(&self) -> SizeAlign {
match self {
BuiltinType::String => SizeAlign { size: 8, align: 4 }, // Pointer and Length
BuiltinType::U8 | BuiltinType::S8 => SizeAlign { size: 1, align: 1 },
Expand Down
3 changes: 2 additions & 1 deletion tools/witx/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ mod docs;
/// Interface for filesystem or mock IO
mod io;
/// Calculate memory layout of types
pub mod layout;
mod layout;
/// Witx syntax parsing from SExprs
mod parser;
/// Calculate required polyfill between interfaces
Expand All @@ -31,6 +31,7 @@ pub use ast::{
pub use coretypes::{AtomType, CoreFuncType, CoreParamSignifies, CoreParamType, TypePassedBy};
pub use docs::Documentation;
pub use io::{Filesystem, MockFs, WitxIo};
pub use layout::{Layout, SizeAlign, StructMemberLayout};
pub use parser::DeclSyntax;
pub use render::SExpr;
pub use representation::{RepEquality, Representable};
Expand Down
10 changes: 5 additions & 5 deletions tools/witx/src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,19 @@ impl fmt::Display for SExpr {
}

impl SExpr {
fn word(s: &str) -> SExpr {
pub fn word(s: &str) -> SExpr {
SExpr::Word(s.to_string())
}
fn ident(s: &str) -> SExpr {
pub fn ident(s: &str) -> SExpr {
SExpr::Ident(s.to_string())
}
fn quote(s: &str) -> SExpr {
pub fn quote(s: &str) -> SExpr {
SExpr::Quote(s.to_string())
}
fn annot(s: &str) -> SExpr {
pub fn annot(s: &str) -> SExpr {
SExpr::Annot(s.to_string())
}
fn docs(d: &str, s: SExpr) -> SExpr {
pub fn docs(d: &str, s: SExpr) -> SExpr {
if d.is_empty() {
s
} else {
Expand Down