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

Commit

Permalink
Make architecture configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
jyn514 committed Mar 12, 2020
1 parent 859f869 commit 0f38f43
Show file tree
Hide file tree
Showing 12 changed files with 354 additions and 230 deletions.
157 changes: 106 additions & 51 deletions src/arch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,98 +40,146 @@ lazy_static! {
pub(crate) static ref CALLING_CONVENTION: CallConv = CallConv::triple_default(&TARGET);
}

mod x64;
pub(crate) use x64::*;
pub(crate) trait Arch {
fn size_max(&self) -> u64;

fn float_size(&self) -> u16;
fn double_size(&self) -> u16;

fn long_size(&self) -> u16;
fn int_size(&self) -> u16;
fn short_size(&self) -> u16;
fn bool_size(&self) -> u16;

// number of bits in a byte
fn char_bit(&self) -> u16;
}

impl Arch for Triple {
fn size_max(&self) -> u64 {
std::u64::MAX
}

fn float_size(&self) -> u16 {
4
}
fn double_size(&self) -> u16 {
8
}

fn long_size(&self) -> u16 {
8
}
fn int_size(&self) -> u16 {
4
}
fn short_size(&self) -> u16 {
2
}
fn bool_size(&self) -> u16 {
1
}

// number of bits in a byte
fn char_bit(&self) -> u16 {
8
}
}

impl StructType {
/// Get the offset of the given struct member.
pub(crate) fn offset(&self, member: InternedStr) -> u64 {
pub(crate) fn offset(&self, member: InternedStr, target: &Triple) -> u64 {
let members = self.members();
let mut current_offset = 0;
for formal in members.iter() {
if formal.id == member {
return current_offset;
}
current_offset = Self::next_offset(current_offset, &formal.ctype)
current_offset = Self::next_offset(current_offset, &formal.ctype, target)
.expect("structs should have valid size and alignment");
}
unreachable!("cannot call struct_offset for member not in struct");
}
/// Get the offset of the next struct member given the current offset.
fn next_offset(mut current_offset: u64, ctype: &Type) -> Result<u64, &'static str> {
let align = ctype.alignof()?;
fn next_offset(
mut current_offset: u64,
ctype: &Type,
target: &Triple,
) -> Result<u64, &'static str> {
let align = ctype.alignof(target)?;
// round up to the nearest multiple of align
let rem = current_offset % align;
if rem != 0 {
// for example: 7%4 == 3; 7 + ((4 - 3) = 1) == 8; 8 % 4 == 0
current_offset += align - rem;
}
Ok(current_offset + ctype.sizeof()?)
Ok(current_offset + ctype.sizeof(target)?)
}
/// Calculate the size of a struct: the sum of all member sizes
pub(crate) fn struct_size(&self) -> Result<SIZE_T, &'static str> {
pub(crate) fn struct_size(&self, target: &Triple) -> Result<u64, &'static str> {
let symbols = &self.members();

symbols
.iter()
.try_fold(0, |offset, symbol| {
Ok(StructType::next_offset(offset, &symbol.ctype)?)
Ok(StructType::next_offset(offset, &symbol.ctype, target)?)
})
.and_then(|size_t| {
let align_minus_one = self.align()? - 1;
let align_minus_one = self.align(target)? - 1;

// Rounds up to the next multiple of `align`
Ok((size_t + align_minus_one) & !align_minus_one)
})
}
/// Calculate the size of a union: the max of all member sizes
pub(crate) fn union_size(&self) -> Result<SIZE_T, &'static str> {
pub(crate) fn union_size(&self, target: &Triple) -> Result<u64, &'static str> {
let symbols = &self.members();
symbols
.iter()
.map(|symbol| symbol.ctype.sizeof())
.map(|symbol| symbol.ctype.sizeof(target))
// max of member sizes
.try_fold(1, |n, size| Ok(max(n, size?)))
}
/// Calculate the alignment of a struct: the max of all member alignments
pub(crate) fn align(&self) -> Result<SIZE_T, &'static str> {
pub(crate) fn align(&self, target: &Triple) -> Result<u64, &'static str> {
let members = &self.members();
members.iter().try_fold(0, |max, member| {
Ok(std::cmp::max(member.ctype.alignof()?, max))
Ok(std::cmp::max(member.ctype.alignof(target)?, max))
})
}
}

impl Type {
/// Returns true if `other` can be converted to `self` without losing infomation.
pub fn can_represent(&self, other: &Type) -> bool {
pub fn can_represent(&self, other: &Type, target: &Triple) -> bool {
let (self_size, other_size) = (self.sizeof(target), other.sizeof(target));
self == other
|| *self == Type::Double && *other == Type::Float
|| (self.is_integral() && other.is_integral())
&& (self.sizeof() > other.sizeof()
|| self.sizeof() == other.sizeof() && self.is_signed() == other.is_signed())
&& (self_size > other_size
|| self_size == other_size && self.is_signed() == other.is_signed())
}

/// Get the size of a type in bytes.
///
/// This is the `sizeof` operator in C.
pub fn sizeof(&self) -> Result<SIZE_T, &'static str> {
pub fn sizeof(&self, target: &Triple) -> Result<u64, &'static str> {
match self {
Bool => Ok(BOOL_SIZE.into()),
Bool => Ok(target.bool_size().into()),
Char(_) => Ok(CHAR_SIZE.into()),
Short(_) => Ok(SHORT_SIZE.into()),
Int(_) => Ok(INT_SIZE.into()),
Long(_) => Ok(LONG_SIZE.into()),
Float => Ok(FLOAT_SIZE.into()),
Double => Ok(DOUBLE_SIZE.into()),
Pointer(_) => Ok(PTR_SIZE.into()),
Short(_) => Ok(target.short_size().into()),
Int(_) => Ok(target.int_size().into()),
Long(_) => Ok(target.long_size().into()),
Float => Ok(target.float_size().into()),
Double => Ok(target.double_size().into()),
Pointer(_) => Ok(target.pointer_width().unwrap().bytes().into()),
// now for the hard ones
Array(t, ArrayType::Fixed(l)) => t
.sizeof()
.sizeof(target)
.and_then(|n| n.checked_mul(*l).ok_or("overflow in array size")),
Array(_, ArrayType::Unbounded) => Err("cannot take sizeof variable length array"),
Enum(_, symbols) => {
let uchar = CHAR_BIT as usize;
let uchar = target.char_bit() as usize;
// integer division, but taking the ceiling instead of the floor
// https://stackoverflow.com/a/17974/7669110
Ok(match (symbols.len() + uchar - 1) / uchar {
Expand All @@ -142,8 +190,8 @@ impl Type {
_ => return Err("enum cannot be represented in SIZE_T bits"),
})
}
Union(struct_type) => struct_type.union_size(),
Struct(struct_type) => struct_type.struct_size(),
Union(struct_type) => struct_type.union_size(target),
Struct(struct_type) => struct_type.struct_size(target),
Bitfield(_) => unimplemented!("sizeof(bitfield)"),
// illegal operations
Function(_) => Err("cannot take `sizeof` a function"),
Expand All @@ -153,7 +201,7 @@ impl Type {
}
}
/// Get the alignment of a type in bytes.
pub fn alignof(&self) -> Result<SIZE_T, &'static str> {
pub fn alignof(&self, target: &Triple) -> Result<u64, &'static str> {
match self {
Bool
| Char(_)
Expand All @@ -163,12 +211,12 @@ impl Type {
| Float
| Double
| Pointer(_)
| Enum(_, _) => self.sizeof(),
Array(t, _) => t.alignof(),
| Enum(_, _) => self.sizeof(target),
Array(t, _) => t.alignof(target),
// 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
Union(struct_type) | Struct(struct_type) => struct_type.align(),
Union(struct_type) | Struct(struct_type) => struct_type.align(target),
Bitfield(_) => unimplemented!("alignof bitfield"),
Function(_) => Err("cannot take `alignof` function"),
Void => Err("cannot take `alignof` void"),
Expand All @@ -177,18 +225,24 @@ impl Type {
}
}
/// Return an IR integer type large enough to contain a pointer.
pub fn ptr_type() -> IrType {
IrType::int(CHAR_BIT * PTR_SIZE).expect("pointer size should be valid")
///
/// You can also return a multiple of a pointer by setting multiple to a non-unit value.
pub fn ptr_type(target: &Triple) -> IrType {
let ptr_size = target
.pointer_width()
.expect("pointer size should be valid")
.bits();
IrType::int(ptr_size.into()).expect("pointer type was not valid")
}
/// Return an IR type which can represent this C type
pub fn as_ir_type(&self) -> IrType {
pub fn as_ir_type(&self, target: &Triple) -> IrType {
match self {
// Integers
Bool => types::B1,
Char(_) | Short(_) | Int(_) | Long(_) | Pointer(_) | Enum(_, _) => {
let int_size = SIZE_T::from(CHAR_BIT)
let int_size = u64::from(target.char_bit())
* self
.sizeof()
.sizeof(target)
.expect("integers should always have a valid size");
IrType::int(int_size.try_into().unwrap_or_else(|_| {
panic!(
Expand All @@ -206,8 +260,7 @@ impl Type {

// Aggregates
// arrays and functions decay to pointers
Function(_) | Array(_, _) => IrType::int(PTR_SIZE * CHAR_BIT)
.unwrap_or_else(|| panic!("unsupported size of IR: {}", PTR_SIZE)),
Function(_) | Array(_, _) => Self::ptr_type(target),
// void cannot be loaded or stored
_ => types::INVALID,
}
Expand All @@ -217,13 +270,14 @@ impl Type {
impl FunctionType {
/// Generate the IR function signature for `self`
pub fn signature(&self, isa: &dyn TargetIsa) -> Signature {
let target = isa.triple();
let mut params = if self.params.len() == 1 && self.params[0].ctype == Type::Void {
// no arguments
Vec::new()
} else {
self.params
.iter()
.map(|param| AbiParam::new(param.ctype.as_ir_type()))
.map(|param| AbiParam::new(param.ctype.as_ir_type(target)))
.collect()
};
if self.varargs {
Expand All @@ -240,7 +294,7 @@ impl FunctionType {
let return_type = if !self.should_return() {
vec![]
} else {
vec![AbiParam::new(self.return_type.as_ir_type())]
vec![AbiParam::new(self.return_type.as_ir_type(target))]
};
Signature {
call_conv: *CALLING_CONVENTION,
Expand All @@ -259,12 +313,13 @@ mod tests {
};

fn type_for_size(size: u16) -> Type {
// TODO: this is hopelessly broken on x32
match size {
0 => Type::Void,
BOOL_SIZE => Type::Bool,
SHORT_SIZE => Type::Short(true),
INT_SIZE => Type::Int(true),
LONG_SIZE => Type::Long(true),
1 => Type::Bool,
2 => Type::Short(true),
4 => Type::Int(true),
8 => Type::Long(true),
_ => struct_for_types(vec![Type::Char(true); size as usize]),
}
}
Expand Down Expand Up @@ -299,7 +354,7 @@ mod tests {
unreachable!()
};
let member = (struct_type.members())[member_index].id;
assert_eq!(struct_type.offset(member), offset);
assert_eq!(struct_type.offset(member, &Triple::host()), offset);
}
#[test]
fn first_member() {
Expand All @@ -318,7 +373,7 @@ mod tests {
#[test]
fn align() {
for size in 1..128 {
let align = type_for_size(size).alignof().unwrap();
let align = type_for_size(size).alignof(&Triple::host()).unwrap();
assert_eq!(align, align.next_power_of_two());
}
}
Expand All @@ -337,13 +392,13 @@ mod tests {
#[test]
fn char_struct() {
let char_struct = type_for_size(5);
assert_eq!(char_struct.alignof().unwrap(), 1);
assert_eq!(char_struct.alignof(&Triple::host()).unwrap(), 1);
assert_offset(vec![Type::Int(true), Type::Char(true)], 1, 4);
assert_eq!(char_struct.sizeof().unwrap(), 5);
assert_eq!(char_struct.sizeof(&Triple::host()).unwrap(), 5);
}
#[test]
fn align_of_non_char_struct() {
let ty = struct_for_types(vec![Pointer(Box::new(Int(true))), Int(true)]);
assert_eq!(ty.alignof(), Ok(8));
assert_eq!(ty.alignof(&Triple::host()), Ok(8));
}
}
26 changes: 9 additions & 17 deletions src/arch/x64.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
// https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models
#[allow(non_camel_case_types)]
pub(crate) type SIZE_T = u64;
#[allow(dead_code)]
pub(crate) const SIZE_MAX: SIZE_T = SIZE_T::max_value();

pub(crate) const FLOAT_SIZE: u16 = 4;
pub(crate) const DOUBLE_SIZE: u16 = 8;

pub(crate) const LONG_SIZE: u16 = 8;
pub(crate) const INT_SIZE: u16 = 4;
pub(crate) const SHORT_SIZE: u16 = 2;
pub(crate) const BOOL_SIZE: u16 = 1;

pub(crate) const PTR_SIZE: u16 = 8;

pub(crate) const CHAR_BIT: u16 = 8; // number of bits in a byte
use super::Archictecture;

struct x86_64;
impl Archictecture for x86_64 {
// https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models
#[allow(non_camel_case_types)]
type SIZE_T = u64;
const SIZE_MAX: u64 = u64::max_value();
}
9 changes: 5 additions & 4 deletions src/data/types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use target_lexicon::Triple;

use super::Symbol;
use crate::arch::SIZE_T;
use crate::intern::InternedStr;
pub use struct_ref::{StructRef, StructType};

Expand Down Expand Up @@ -158,7 +159,7 @@ pub enum Type {

#[derive(Clone, Debug)]
pub enum ArrayType {
Fixed(SIZE_T),
Fixed(u64),
Unbounded,
}

Expand Down Expand Up @@ -245,9 +246,9 @@ impl Type {
_ => false,
}
}
pub(crate) fn member_offset(&self, member: InternedStr) -> Result<u64, ()> {
pub(crate) fn member_offset(&self, member: InternedStr, target: &Triple) -> Result<u64, ()> {
match self {
Type::Struct(stype) => Ok(stype.offset(member)),
Type::Struct(stype) => Ok(stype.offset(member, target)),
Type::Union(_) => Ok(0),
_ => Err(()),
}
Expand Down
Loading

0 comments on commit 0f38f43

Please sign in to comment.