Skip to content
This repository has been archived by the owner on Jun 3, 2021. It is now read-only.

Commit

Permalink
MVP for structs
Browse files Browse the repository at this point in the history
  • Loading branch information
jyn514 committed Sep 11, 2019
1 parent 0450a3a commit 01f53f2
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 33 deletions.
29 changes: 27 additions & 2 deletions src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,12 @@ impl Type {
| Pointer(_, _)
// TODO: is this correct? still need to worry about padding
| Union(_, _)
| Enum(_, _)
| Struct(_, _) => self.sizeof(),
| Enum(_, _) => self.sizeof(),
Array(t, _) => t.alignof(),
// Clang uses the largest alignment of any element as the alignment of the whole
// Not sure why, but who am I to argue
// Anyway, Faerie panics if the alignment isn't a power of two so it's probably for the best
Struct(_, members) => members.iter().try_fold(0, |max, member| Ok(std::cmp::max(member.ctype.sizeof()?, max))),
Bitfield(_) => unimplemented!("alignof bitfield"),
Function(_) => Err("cannot take `alignof` function"),
Void => Err("cannot take `alignof` void"),
Expand All @@ -100,6 +103,28 @@ impl Type {
pub fn ptr_type() -> IrType {
IrType::int(CHAR_BIT * PTR_SIZE).expect("pointer size should be valid")
}
pub fn struct_offset(&self, member: &str) -> u64 {
let members = match self {
Type::Struct(_, members) => members,
Type::Union(_, members) => return 0,
_ => unreachable!("only structs and unions can have members"),
};
let mut current_offset = 0;
for formal in members {
if formal.id == member {
return current_offset;
}
let size = formal
.ctype
.sizeof()
.expect("struct members should have complete object type");
let align = self.alignof().expect("struct should have valid alignment");
// round up to the nearest multiple of align
let padded_size = size + (align - size) % align;
current_offset += padded_size;
}
unreachable!("cannot call struct_offset for member not in struct");
}
pub fn as_ir_type(&self) -> Result<IrType, String> {
match self {
// Integers
Expand Down
62 changes: 37 additions & 25 deletions src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,23 @@ impl Compiler {
self.compile_expr(*left, builder)?;
self.compile_expr(*right, builder)
}
ExprType::Member(cstruct, id) => {
let ctype = cstruct.ctype.clone();
let pointer = self.compile_expr(*cstruct, builder)?;
let id = if let Token::Id(id) = id {
id
} else {
unreachable!("parser should only pass ids to ExprType::Member");
};
let offset = builder
.ins()
.iconst(Type::ptr_type(), ctype.struct_offset(&id) as i64);
Ok(Value {
ir_val: builder.ins().iadd(pointer.ir_val, offset),
ir_type,
ctype,
})
}
x => {
unimplemented!("{:?}", x);
}
Expand Down Expand Up @@ -762,32 +779,20 @@ impl Compiler {
location: Location,
builder: &mut FunctionBuilder,
) -> IrResult {
match self.scope.get(&var.id).unwrap() {
let ptr_type = Type::ptr_type();
let ir_val = match self.scope.get(&var.id).unwrap() {
Id::Function(_) => unimplemented!("address of function"),
Id::Global(static_id) => {
let ir_type = var
.ctype
.as_ir_type()
.map_err(|data| Locatable { data, location })?;
let global = self.module.declare_data_in_func(*static_id, builder.func);
Ok(Value {
ir_type,
ir_val: builder.ins().global_value(Type::ptr_type(), global),
ctype: var.ctype,
})
builder.ins().global_value(ptr_type, global)
}
Id::Local(stack_slot) => {
let ir_type = var
.ctype
.as_ir_type()
.map_err(|data| Locatable { data, location })?;
Ok(Value {
ir_type,
ir_val: builder.ins().stack_addr(Type::ptr_type(), *stack_slot, 0),
ctype: var.ctype,
})
}
}
Id::Local(stack_slot) => builder.ins().stack_addr(ptr_type, *stack_slot, 0),
};
Ok(Value {
ir_type: ptr_type,
ir_val,
ctype: var.ctype,
})
}
fn compare(
&mut self,
Expand Down Expand Up @@ -841,10 +846,17 @@ impl Compiler {
let ir_target = target.ir_val;
// need to deref explicitly to get an rval, the frontend didn't do it for us
if is_id {
target.ir_val =
builder
let ir_type = match target.ctype.as_ir_type() {
Ok(ty) => ty,
Err(data) => err!(data, location),
};
target = Value {
ir_val: builder
.ins()
.load(target.ir_type, MemFlags::new(), target.ir_val, 0);
.load(ir_type, MemFlags::new(), target.ir_val, 0),
ir_type,
ctype: target.ctype,
};
}
if target.ir_type != value.ir_type {
unimplemented!("binary promotion for complex assignment");
Expand Down
8 changes: 6 additions & 2 deletions src/parse/decl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ impl<I: Iterator<Item = Lexeme>> Parser<I> {
let ctype = if kind == Keyword::Enum {
self.enumerators(ident)
} else {
self.struct_declaration_list(ident)
self.struct_declaration_list(ident, kind == Keyword::Struct)
}?;
self.expect(Token::RightBrace)?;
Ok(ctype)
Expand Down Expand Up @@ -591,6 +591,7 @@ impl<I: Iterator<Item = Lexeme>> Parser<I> {
fn struct_declaration_list(
&mut self,
ident: Option<Locatable<String>>,
c_struct: bool,
) -> SemanticResult<Type> {
let mut members = vec![];
loop {
Expand Down Expand Up @@ -625,7 +626,10 @@ impl<I: Iterator<Item = Lexeme>> Parser<I> {
self.next_location().clone()
);
}
Ok(Type::Struct(ident.map(|i| i.data), members))
Ok(if c_struct { Type::Struct } else { Type::Union }(
ident.map(|i| i.data),
members,
))
}
/*
* function parameters
Expand Down
12 changes: 8 additions & 4 deletions src/parse/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -702,10 +702,14 @@ impl<I: Iterator<Item = Lexeme>> Parser<I> {
};
match &expr.ctype {
Type::Struct(_, members) | Type::Union(_, members) => {
if members.iter().any(|member| member.id == id) {
unimplemented!(
"struct and union members are very much not implemented"
)
if let Some(member) = members.iter().find(|member| member.id == id) {
Ok(Expr {
ctype: member.ctype.clone(),
constexpr: expr.constexpr,
lval: true,
location,
expr: ExprType::Member(Box::new(expr), Token::Id(id)),
})
} else {
Err(Locatable {
data: format!("no member named '{}' in '{}'", id, expr.ctype),
Expand Down
30 changes: 30 additions & 0 deletions tests/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,33 @@ fn enumeration() {
1,
);
}

#[test]
fn union() {
utils::assert_code(
"
union u {
int i;
char c;
} u;
int main() {
u.i = 1;
return u.c;
}",
1,
)
}

#[test]
fn cstruct() {
utils::assert_code(
"
struct s {
int i, j, k;
} s;
int main() {
return s.i = s.j = s.k = 1;
}",
1,
);
}

0 comments on commit 01f53f2

Please sign in to comment.