Skip to content

Commit

Permalink
Split structures into inner and outer definitions to support packed-a…
Browse files Browse the repository at this point in the history
…ligned structures (#131)

Implement support for packed-aligned structures in several steps:

* Unify bitfield and non-bitfield struct implementations into a single one, and rename bitfields.rs to structs.rs

* Implement support for packed-aligned structures by splitting them into an outer-aligned newtype containing an inner-packed structure with the actual fields

* Implement support for aligned structure fields in packed structures by computing the amount of padding and adding the padding manually

* Pre-declare all the field names (checking for duplicates) in mod.rs instead of declaring them on demand

* Use the field renamer to manage names for padding fields in structures

* Add a set of tests for packed and/or aligned structures
  • Loading branch information
ahomescu authored Aug 5, 2019
1 parent e2dc471 commit 49d2b57
Show file tree
Hide file tree
Showing 9 changed files with 762 additions and 549 deletions.
9 changes: 7 additions & 2 deletions c2rust-ast-builder/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1548,18 +1548,23 @@ impl Builder {
})
}

pub fn struct_item<I>(self, name: I, fields: Vec<StructField>) -> P<Item>
pub fn struct_item<I>(self, name: I, fields: Vec<StructField>, tuple: bool) -> P<Item>
where
I: Make<Ident>,
{
let name = name.make(&self);
let variant_data = if tuple {
VariantData::Tuple(fields, DUMMY_NODE_ID)
} else {
VariantData::Struct(fields, false)
};
Self::item(
name,
self.attrs,
self.vis,
self.span,
self.id,
ItemKind::Struct(VariantData::Struct(fields, false), self.generics),
ItemKind::Struct(variant_data, self.generics),
)
}

Expand Down
2 changes: 1 addition & 1 deletion c2rust-refactor/src/transform/statics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ impl Transform for CollectToStruct {
fn build_collected_struct(name: &str, matches: &[Bindings]) -> P<Item> {
let fields = matches.iter().map(
|bnd| mk().struct_field(bnd.get::<_, Ident>("__x").unwrap(), bnd.get::<_, P<Ty>>("__t").unwrap())).collect::<Vec<_>>();
mk().struct_item(name, fields)
mk().struct_item(name, fields, false)
}

fn build_struct_instance(struct_name: &str,
Expand Down
31 changes: 31 additions & 0 deletions c2rust-transpile/src/c_ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,37 @@ impl TypedAstContext {
});
self.c_decls_top = decls_top;
}

pub fn has_inner_struct_decl(&self, decl_id: CDeclId) -> bool {
match self.index(decl_id).kind {
CDeclKind::Struct { manual_alignment: Some(_), .. } => true,
_ => false
}
}

pub fn is_packed_struct_decl(&self, decl_id: CDeclId) -> bool {
match self.index(decl_id).kind {
CDeclKind::Struct { is_packed: true, .. } => true,
CDeclKind::Struct { max_field_alignment: Some(_), .. } => true,
_ => false
}
}

pub fn is_aligned_struct_type(&self, typ: CTypeId) -> bool {
if let Some(decl_id) = self
.resolve_type(typ)
.kind
.as_underlying_decl()
{
if let CDeclKind::Struct {
manual_alignment: Some(_),
..
} = self.index(decl_id).kind {
return true;
}
}
false
}
}

impl CommentContext {
Expand Down
51 changes: 47 additions & 4 deletions c2rust-transpile/src/convert_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,17 @@ use std::ops::Index;
use syntax::ast::*;
use syntax::ptr::P;

#[derive(Debug, Hash, PartialEq, Eq, Clone)]
enum FieldKey {
Field(CFieldId),
Padding(usize),
}

pub struct TypeConverter {
pub translate_valist: bool,
renamer: Renamer<CDeclId>,
fields: HashMap<CDeclId, Renamer<CFieldId>>,
fields: HashMap<CDeclId, Renamer<FieldKey>>,
suffix_names: HashMap<(CDeclId, &'static str), String>,
features: HashSet<&'static str>,
emit_no_std: bool,
}
Expand Down Expand Up @@ -131,6 +138,7 @@ impl TypeConverter {
translate_valist: false,
renamer: Renamer::new(&RESERVED_NAMES),
fields: HashMap::new(),
suffix_names: HashMap::new(),
features: HashSet::new(),
emit_no_std,
}
Expand All @@ -154,6 +162,19 @@ impl TypeConverter {
self.renamer.get(&decl_id)
}

pub fn resolve_decl_suffix_name(&mut self, decl_id: CDeclId, suffix: &'static str) -> &str {
let key = (decl_id, suffix);
if !self.suffix_names.contains_key(&key) {
let mut suffix_name = self.resolve_decl_name(decl_id)
.unwrap_or_else(|| "C2RustUnnamed".to_string());
suffix_name += suffix;

let suffix_name = self.renamer.pick_name(&suffix_name);
self.suffix_names.insert(key, suffix_name);
}
self.suffix_names.get(&key).unwrap()
}

pub fn declare_field_name(
&mut self,
record_id: CRecordId,
Expand All @@ -169,10 +190,31 @@ impl TypeConverter {
self.fields
.get_mut(&record_id)
.unwrap()
.insert(field_id, name)
.insert(FieldKey::Field(field_id), name)
.expect("Field already declared")
}

pub fn declare_padding(
&mut self,
record_id: CRecordId,
padding_idx: usize,
) -> String {
if !self.fields.contains_key(&record_id) {
self.fields.insert(record_id, Renamer::new(&RESERVED_NAMES));
}

let key = FieldKey::Padding(padding_idx);
if let Some(name) = self.fields.get(&record_id).unwrap().get(&key) {
name
} else {
self.fields
.get_mut(&record_id)
.unwrap()
.insert(key, "c2rust_padding")
.unwrap()
}
}

/** Resolve the Rust name associated with a field declaration. The optional record_id
is used as a hint to speed up the process of finding the field's name.
*/
Expand All @@ -181,9 +223,10 @@ impl TypeConverter {
record_id: Option<CRecordId>,
field_id: CFieldId,
) -> Option<String> {
let key = FieldKey::Field(field_id);
match record_id {
Some(record_id) => self.fields.get(&record_id).and_then(|x| x.get(&field_id)),
None => self.fields.values().flat_map(|x| x.get(&field_id)).next(),
Some(record_id) => self.fields.get(&record_id).and_then(|x| x.get(&key)),
None => self.fields.values().flat_map(|x| x.get(&key)).next(),
}
}

Expand Down
111 changes: 17 additions & 94 deletions c2rust-transpile/src/translator/literals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,23 @@ impl<'c> Translation<'c> {
}
}
CTypeKind::Struct(struct_id) => {
self.convert_struct_literal(ctx, struct_id, ids.as_ref())
let mut literal = self.convert_struct_literal(ctx, struct_id, ids.as_ref());
if self.ast_context.has_inner_struct_decl(struct_id) {
// If the structure is split into an outer/inner,
// wrap the inner initializer using the outer structure
let outer_name = self.type_converter
.borrow()
.resolve_decl_name(struct_id)
.unwrap();

let outer_path = mk().path_expr(vec![outer_name]);
literal = literal.map(|lit_ws| {
lit_ws.map(|lit| {
mk().call_expr(outer_path, vec![lit])
})
});
};
literal
}
CTypeKind::Union(union_id) => {
self.convert_union_literal(ctx, union_id, ids.as_ref(), ty, opt_union_field_id)
Expand Down Expand Up @@ -307,97 +323,4 @@ impl<'c> Translation<'c> {
_ => panic!("Expected union decl"),
}
}

fn convert_struct_literal(
&self,
ctx: ExprContext,
struct_id: CRecordId,
ids: &[CExprId],
) -> Result<WithStmts<P<Expr>>, TranslationError> {
let mut has_bitfields = false;
let (field_decls, platform_byte_size) = match self.ast_context.index(struct_id).kind {
CDeclKind::Struct {
ref fields,
platform_byte_size,
..
} => {
let mut fieldnames = vec![];

let fields = match fields {
&Some(ref fields) => fields,
&None => {
return Err(TranslationError::generic(
"Attempted to construct forward-declared struct",
))
}
};

for &x in fields {
let name = self
.type_converter
.borrow()
.resolve_field_name(Some(struct_id), x)
.unwrap();
if let CDeclKind::Field {
typ,
bitfield_width,
platform_type_bitwidth,
platform_bit_offset,
..
} = self.ast_context.index(x).kind
{
has_bitfields |= bitfield_width.is_some();

fieldnames.push((
name,
typ,
bitfield_width,
platform_bit_offset,
platform_type_bitwidth,
));
} else {
panic!("Struct field decl type mismatch")
}
}

(fieldnames, platform_byte_size)
}
_ => panic!("Struct literal declaration mismatch"),
};

let struct_name = self
.type_converter
.borrow()
.resolve_decl_name(struct_id)
.unwrap();

if has_bitfields {
return self.convert_bitfield_struct_literal(
struct_name,
platform_byte_size,
ids,
field_decls,
ctx,
);
}

Ok(field_decls.iter()
.zip(ids.iter().map(Some).chain(iter::repeat(None)))

// We're now iterating over pairs of (field_decl, Option<id>). The id
// is Some until we run out of specified initializers, when we switch
// to implicit default initializers.
.map(|(decl, maybe_id)| {
let &(ref field_name, ty, _, _, _) = decl;
let field_init = match maybe_id {
Some(id) => self.convert_expr(ctx.used(), *id)?,
None => self.implicit_default_expr(ty.ctype, ctx.is_static)?,
};
Ok(field_init.map(|expr| mk().field(field_name, expr)))
})
.collect::<Result<WithStmts<Vec<ast::Field>>, TranslationError>>()?
.map(|fields| {
mk().struct_expr(vec![mk().path_segment(struct_name)], fields)
}))
}
}
Loading

0 comments on commit 49d2b57

Please sign in to comment.