diff --git a/src/arch/mod.rs b/src/arch/mod.rs index 9a08d6ab..bde5b5b9 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -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 { - let align = ctype.alignof()?; + fn next_offset( + mut current_offset: u64, + ctype: &Type, + target: &Triple, + ) -> Result { + 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 { + pub(crate) fn struct_size(&self, target: &Triple) -> Result { 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 { + pub(crate) fn union_size(&self, target: &Triple) -> Result { 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 { + pub(crate) fn align(&self, target: &Triple) -> Result { 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 { + pub fn sizeof(&self, target: &Triple) -> Result { 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 { @@ -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"), @@ -153,7 +201,7 @@ impl Type { } } /// Get the alignment of a type in bytes. - pub fn alignof(&self) -> Result { + pub fn alignof(&self, target: &Triple) -> Result { match self { Bool | Char(_) @@ -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"), @@ -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!( @@ -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, } @@ -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 { @@ -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, @@ -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]), } } @@ -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() { @@ -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()); } } @@ -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)); } } diff --git a/src/arch/x64.rs b/src/arch/x64.rs index 77f83657..8bc7e95d 100644 --- a/src/arch/x64.rs +++ b/src/arch/x64.rs @@ -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(); +} \ No newline at end of file diff --git a/src/data/types.rs b/src/data/types.rs index a27fb059..d737bd42 100644 --- a/src/data/types.rs +++ b/src/data/types.rs @@ -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}; @@ -158,7 +159,7 @@ pub enum Type { #[derive(Clone, Debug)] pub enum ArrayType { - Fixed(SIZE_T), + Fixed(u64), Unbounded, } @@ -245,9 +246,9 @@ impl Type { _ => false, } } - pub(crate) fn member_offset(&self, member: InternedStr) -> Result { + pub(crate) fn member_offset(&self, member: InternedStr, target: &Triple) -> Result { match self { - Type::Struct(stype) => Ok(stype.offset(member)), + Type::Struct(stype) => Ok(stype.offset(member, target)), Type::Union(_) => Ok(0), _ => Err(()), } diff --git a/src/fold.rs b/src/fold.rs index a674bf62..6ccb2dfa 100644 --- a/src/fold.rs +++ b/src/fold.rs @@ -1,6 +1,7 @@ -use crate::arch::CHAR_BIT; +use crate::arch::Arch; use crate::data::prelude::*; use std::ops::{Add, Div, Mul, Sub}; +use target_lexicon::Triple; use Literal::*; macro_rules! fold_int_bin_op { @@ -44,8 +45,8 @@ fn fold_scalar_bin_op( } macro_rules! fold_compare_op { -($left: expr, $right: expr, $constructor: ident, $op: tt, $compare: expr) => {{ - let (left, right) = ($left.const_fold()?, $right.const_fold()?); +($left: expr, $right: expr, $constructor: ident, $op: tt, $compare: expr, $target: expr) => {{ + let (left, right) = ($left.const_fold($target)?, $right.const_fold($target)?); match (&left.expr, &right.expr) { (ExprType::Literal(a), ExprType::Literal(b)) => { match (a, b) { @@ -76,8 +77,8 @@ impl Expr { false } } - pub(crate) fn constexpr(self) -> CompileResult> { - let folded = self.const_fold()?; + pub(crate) fn constexpr(self, target: &Triple) -> CompileResult> { + let folded = self.const_fold(target)?; match folded.expr { ExprType::Literal(token) => Ok(Locatable { data: (token, folded.ctype), @@ -86,7 +87,7 @@ impl Expr { _ => Err(folded.location.error(SemanticError::NotConstant(folded))), } } - pub fn const_fold(self) -> CompileResult { + pub fn const_fold(self, target: &Triple) -> CompileResult { use crate::data::lex::ComparisonToken::*; let location = self.location; let folded = match self.expr { @@ -100,13 +101,13 @@ impl Expr { _ => self.expr, }, ExprType::Sizeof(ctype) => { - let sizeof = ctype.sizeof().map_err(|data| Locatable { + let sizeof = ctype.sizeof(target).map_err(|data| Locatable { data: data.to_string(), location, })?; ExprType::Literal(UnsignedInt(sizeof)) } - ExprType::Negate(expr) => expr.const_fold()?.map_literal( + ExprType::Negate(expr) => expr.const_fold(target)?.map_literal( &location, |token| match token { Int(i) => { @@ -126,7 +127,7 @@ impl Expr { }, ExprType::Negate, )?, - ExprType::BitwiseNot(expr) => expr.const_fold()?.map_literal( + ExprType::BitwiseNot(expr) => expr.const_fold(target)?.map_literal( &location, |token| match token { Int(i) => Ok(Int(!i)), @@ -137,7 +138,7 @@ impl Expr { ExprType::BitwiseNot, )?, ExprType::Comma(left, right) => { - let (left, right) = (left.const_fold()?, right.const_fold()?); + let (left, right) = (left.const_fold(target)?, right.const_fold(target)?); // check if we can ignore left or it has side effects if left.constexpr { right.expr @@ -146,11 +147,11 @@ impl Expr { } } ExprType::Noop(inner) => { - let inner = inner.const_fold()?; + let inner = inner.const_fold(target)?; ExprType::Noop(Box::new(inner)) } ExprType::Deref(expr) => { - let folded = expr.const_fold()?; + let folded = expr.const_fold(target)?; if let ExprType::Literal(Int(0)) = folded.expr { semantic_err!("cannot dereference NULL pointer".into(), folded.location); } @@ -166,6 +167,7 @@ impl Expr { u8::wrapping_add, ), ExprType::Add, + target, )?, ExprType::Sub(left, right) => left.literal_bin_op( *right, @@ -177,6 +179,7 @@ impl Expr { u8::wrapping_sub, ), ExprType::Sub, + target, )?, ExprType::Mul(left, right) => left.literal_bin_op( *right, @@ -188,9 +191,10 @@ impl Expr { u8::wrapping_mul, ), ExprType::Mul, + target, )?, ExprType::Div(left, right) => { - let right = right.const_fold()?; + let right = right.const_fold(target)?; if right.is_zero() { return Err(location.error(SemanticError::DivideByZero)); } @@ -204,10 +208,11 @@ impl Expr { u8::wrapping_div, ), ExprType::Div, + target, )? } ExprType::Mod(left, right) => { - let right = right.const_fold()?; + let right = right.const_fold(target)?; if right.is_zero() { return Err(location.error(SemanticError::DivideByZero)); } @@ -232,44 +237,59 @@ impl Expr { (_, _) => Ok(None), }, ExprType::Mod, + target, )? } - ExprType::Xor(left, right) => { - left.literal_bin_op(*right, &location, fold_int_bin_op!(^), ExprType::Xor)? - } - ExprType::BitwiseAnd(left, right) => { - left.literal_bin_op(*right, &location, fold_int_bin_op!(&), ExprType::BitwiseAnd)? - } - ExprType::BitwiseOr(left, right) => { - left.literal_bin_op(*right, &location, fold_int_bin_op!(|), ExprType::BitwiseOr)? - } + ExprType::Xor(left, right) => left.literal_bin_op( + *right, + &location, + fold_int_bin_op!(^), + ExprType::Xor, + target, + )?, + ExprType::BitwiseAnd(left, right) => left.literal_bin_op( + *right, + &location, + fold_int_bin_op!(&), + ExprType::BitwiseAnd, + target, + )?, + ExprType::BitwiseOr(left, right) => left.literal_bin_op( + *right, + &location, + fold_int_bin_op!(|), + ExprType::BitwiseOr, + target, + )?, ExprType::Shift(left, right, true) => { - shift_left(*left, *right, &self.ctype, &location)? + shift_left(*left, *right, &self.ctype, target, &location)? } ExprType::Shift(left, right, false) => { - shift_right(*left, *right, &self.ctype, &location)? + shift_right(*left, *right, &self.ctype, target, &location)? + } + ExprType::Compare(left, right, Less) => { + fold_compare_op!(left, right, Compare, <, Less, target) } - ExprType::Compare(left, right, Less) => fold_compare_op!(left, right, Compare, <, Less), ExprType::Compare(left, right, LessEqual) => { - fold_compare_op!(left, right, Compare, <=, LessEqual) + fold_compare_op!(left, right, Compare, <=, LessEqual, target) } ExprType::Compare(left, right, Greater) => { - fold_compare_op!(left, right, Compare, >, Greater) + fold_compare_op!(left, right, Compare, >, Greater, target) } ExprType::Compare(left, right, GreaterEqual) => { - fold_compare_op!(left, right, Compare, >=, GreaterEqual) + fold_compare_op!(left, right, Compare, >=, GreaterEqual, target) } ExprType::Compare(left, right, EqualEqual) => { - fold_compare_op!(left, right, Compare, ==, EqualEqual) + fold_compare_op!(left, right, Compare, ==, EqualEqual, target) } ExprType::Compare(left, right, NotEqual) => { - fold_compare_op!(left, right, Compare, !=, NotEqual) + fold_compare_op!(left, right, Compare, !=, NotEqual, target) } ExprType::Ternary(condition, then, otherwise) => { let (condition, then, otherwise) = ( - condition.const_fold()?, - then.const_fold()?, - otherwise.const_fold()?, + condition.const_fold(target)?, + then.const_fold(target)?, + otherwise.const_fold(target)?, ); match condition.expr { ExprType::Literal(Int(0)) => otherwise.expr, @@ -280,11 +300,11 @@ impl Expr { } } ExprType::FuncCall(func, params) => { - let func = func.const_fold()?; + let func = func.const_fold(target)?; #[rustfmt::skip] let params: Vec = params .into_iter() - .map(Self::const_fold) + .map(|e| e.const_fold(target)) .collect::>()?; // function calls are always non-constant // TODO: if we have access to the full source of a function, could we try to @@ -292,21 +312,21 @@ impl Expr { ExprType::FuncCall(Box::new(func), params) } ExprType::Member(expr, member) => { - let expr = expr.const_fold()?; + let expr = expr.const_fold(target)?; ExprType::Member(Box::new(expr), member) } - ExprType::Assign(target, value, token) => { - let (target, value) = (target.const_fold()?, value.const_fold()?); + ExprType::Assign(lval, value, token) => { + let (lval, value) = (lval.const_fold(target)?, value.const_fold(target)?); // TODO: could we propagate this information somehow? // e.g. fold `int main() { int x = 1; return x; }` to `return 1;` - ExprType::Assign(Box::new(target), Box::new(value), token) + ExprType::Assign(Box::new(lval), Box::new(value), token) } ExprType::PostIncrement(expr, increase) => { - let expr = expr.const_fold()?; + let expr = expr.const_fold(target)?; // this isn't constant for the same reason assignment isn't constant ExprType::PostIncrement(Box::new(expr), increase) } - ExprType::Cast(expr) => cast(*expr, &self.ctype)?, + ExprType::Cast(expr) => cast(*expr, &self.ctype, target)?, ExprType::LogicalAnd(left, right) => left.literal_bin_op( *right, &location, @@ -316,6 +336,7 @@ impl Expr { _ => Ok(None), }, ExprType::LogicalAnd, + target, )?, ExprType::LogicalOr(left, right) => left.literal_bin_op( *right, @@ -326,8 +347,9 @@ impl Expr { _ => Ok(None), }, ExprType::LogicalOr, + target, )?, - ExprType::StaticRef(inner) => ExprType::StaticRef(Box::new(inner.const_fold()?)), + ExprType::StaticRef(inner) => ExprType::StaticRef(Box::new(inner.const_fold(target)?)), }; let is_constexpr = match folded { ExprType::Literal(_) => true, @@ -352,12 +374,13 @@ impl Expr { location: &Location, fold_func: F, constructor: C, + target: &Triple, ) -> CompileResult where F: FnOnce(&Literal, &Literal, &Type) -> Result, SemanticError>, C: FnOnce(Box, Box) -> ExprType, { - let (left, right) = (self.const_fold()?, other.const_fold()?); + let (left, right) = (self.const_fold(target)?, other.const_fold(target)?); let literal: Option = match (&left.expr, &right.expr) { (ExprType::Literal(left_token), ExprType::Literal(right_token)) => { match fold_func(left_token, right_token, &left.ctype) { @@ -402,8 +425,8 @@ impl Literal { } } -fn cast(expr: Expr, ctype: &Type) -> CompileResult { - let expr = expr.const_fold()?; +fn cast(expr: Expr, ctype: &Type, target: &Triple) -> CompileResult { + let expr = expr.const_fold(target)?; Ok(if let ExprType::Literal(ref token) = expr.expr { if let Some(token) = const_cast(token, ctype) { ExprType::Literal(token) @@ -456,9 +479,10 @@ fn shift_right( left: Expr, right: Expr, ctype: &Type, + target: &Triple, location: &Location, ) -> CompileResult { - let (left, right) = (left.const_fold()?, right.const_fold()?); + let (left, right) = (left.const_fold(target)?, right.const_fold(target)?); if let ExprType::Literal(token) = right.expr { let shift = match token.non_negative_int() { Ok(u) => u, @@ -466,7 +490,7 @@ fn shift_right( return Err(location.error(SemanticError::NegativeShift { is_left: false })); } }; - let sizeof = ctype.sizeof().map_err(|err| Locatable { + let sizeof = ctype.sizeof(&target).map_err(|err| Locatable { data: err.to_string(), location: *location, })?; @@ -503,9 +527,10 @@ fn shift_left( left: Expr, right: Expr, ctype: &Type, + target: &Triple, location: &Location, ) -> CompileResult { - let (left, right) = (left.const_fold()?, right.const_fold()?); + let (left, right) = (left.const_fold(target)?, right.const_fold(target)?); if let ExprType::Literal(token) = right.expr { let shift = match token.non_negative_int() { Ok(u) => u, @@ -515,11 +540,11 @@ fn shift_left( }; if left.ctype.is_signed() { - let size = match left.ctype.sizeof() { + let size = match left.ctype.sizeof(&target) { Ok(s) => s, Err(err) => semantic_err!(err.into(), *location), }; - let max_shift = u64::from(CHAR_BIT) * size; + let max_shift = u64::from(target.char_bit()) * size; if shift >= max_shift { return Err(location.error(SemanticError::TooManyShiftBits { is_left: true, @@ -560,7 +585,9 @@ mod tests { use crate::parse::tests::parse_expr; fn test_const_fold(s: &str) -> CompileResult { - parse_expr(s).unwrap().const_fold() + parse_expr(s) + .unwrap() + .const_fold(&target_lexicon::Triple::host()) } fn assert_fold(original: &str, expected: &str) { let (folded_a, folded_b) = ( diff --git a/src/ir/expr.rs b/src/ir/expr.rs index 24376797..81bb4a68 100644 --- a/src/ir/expr.rs +++ b/src/ir/expr.rs @@ -2,6 +2,7 @@ use cranelift::codegen::ir::{condcodes, types, MemFlags}; use cranelift::prelude::{FunctionBuilder, InstBuilder, Type as IrType, Value as IrValue}; use cranelift_module::Backend; use log::debug; +use target_lexicon::Triple; use super::{Compiler, Id}; use crate::data::prelude::*; @@ -29,12 +30,12 @@ impl Compiler { // it can't be any smaller without supporting fewer features #[allow(clippy::cognitive_complexity)] pub(super) fn compile_expr(&mut self, expr: Expr, builder: &mut FunctionBuilder) -> IrResult { - let expr = expr.const_fold()?; + let expr = expr.const_fold(&self.target)?; let location = expr.location; let ir_type = if expr.lval { - Type::ptr_type() + Type::ptr_type(&self.target) } else { - expr.ctype.as_ir_type() + expr.ctype.as_ir_type(&self.target) }; match expr.expr { ExprType::Literal(token) => { @@ -117,9 +118,11 @@ impl Compiler { let ctype = cstruct.ctype.clone(); let pointer = self.compile_expr(*cstruct, builder)?; let offset = ctype - .member_offset(id) + .member_offset(id, &self.target) .expect("only structs and unions can have members"); - let ir_offset = builder.ins().iconst(Type::ptr_type(), offset as i64); + let ir_offset = builder + .ins() + .iconst(Type::ptr_type(&self.target), offset as i64); Ok(Value { ir_val: builder.ins().iadd(pointer.ir_val, ir_offset), ir_type, @@ -132,7 +135,7 @@ impl Compiler { Type::Pointer(t) => *t, _ => lval.ctype, }; - let ir_type = loaded_ctype.as_ir_type(); + let ir_type = loaded_ctype.as_ir_type(&self.target); let previous_value = Value { ir_val: builder.ins().load(ir_type, MemFlags::new(), lval.ir_val, 0), ir_type, @@ -178,7 +181,7 @@ impl Compiler { builder: &mut FunctionBuilder, ) -> IrResult { let target_block = builder.create_block(); - let target_type = left.ctype.as_ir_type(); + let target_type = left.ctype.as_ir_type(&self.target); builder.append_block_param(target_block, target_type); let condition = self.compile_expr(condition, builder)?; @@ -252,7 +255,9 @@ impl Compiler { (Literal::Str(string), _) => { let str_id = self.compile_string(string, location)?; let str_addr = self.module.declare_data_in_func(str_id, builder.func); - builder.ins().global_value(Type::ptr_type(), str_addr) + builder + .ins() + .global_value(Type::ptr_type(&self.target), str_addr) } _ => unimplemented!("aggregate literals"), }; @@ -288,18 +293,19 @@ impl Compiler { self.compile_expr(left, builder)?, self.compile_expr(right, builder)?, ); - Self::binary_assign_ir(left, right, ctype, token, builder) + Self::binary_assign_ir(left, right, ctype, token, &self.target, builder) } fn binary_assign_ir( left: Value, right: Value, ctype: Type, token: Token, + target: &Triple, builder: &mut FunctionBuilder, ) -> IrResult { use cranelift::codegen::ir::InstBuilder as b; assert_eq!(left.ir_type, right.ir_type); - let ir_type = ctype.as_ir_type(); + let ir_type = ctype.as_ir_type(&target); let signed = ctype.is_signed(); let func = match (token, ir_type, signed) { (Token::Plus, ty, _) if ty.is_int() => b::iadd, @@ -341,7 +347,7 @@ impl Compiler { // this cast is a no-op, it's just here for the frontend return Ok(original); } - let cast_type = ctype.as_ir_type(); + let cast_type = ctype.as_ir_type(&self.target); let cast = Self::cast_ir( original.ir_type, cast_type, @@ -434,7 +440,7 @@ impl Compiler { }) } fn load_addr(&self, var: Symbol, builder: &mut FunctionBuilder) -> IrResult { - let ptr_type = Type::ptr_type(); + let ptr_type = Type::ptr_type(&self.target); let ir_val = match self.scope.get(&var.id).unwrap() { Id::Function(func_id) => { let func_ref = self.module.declare_func_in_func(*func_id, builder.func); @@ -503,10 +509,12 @@ impl Compiler { unreachable!("struct should not have a valid complex assignment"); } use std::convert::TryInto; - let size = ctype.sizeof().map_err(|e| location.with(e.to_string()))?; + let size = ctype + .sizeof(&self.target) + .map_err(|e| location.with(e.to_string()))?; let align = ctype - .alignof() - .expect("if sizeof() succeeds so should alignof()") + .alignof(&self.target) + .expect("if sizeof(&self.target) succeeds so should alignof()") .try_into() .expect("align should never be more than 255 bytes"); builder.emit_small_memory_copy( @@ -526,7 +534,7 @@ impl Compiler { let mut value = value; if token != AssignmentToken::Equal { // need to deref explicitly to get an rval, the frontend didn't do it for us - let ir_type = ctype.as_ir_type(); + let ir_type = ctype.as_ir_type(&self.target); let target = Value { ir_val: builder .ins() @@ -541,8 +549,14 @@ impl Compiler { target.ir_type ); } - value = - Self::binary_assign_ir(target, value, ctype, token.without_assignment(), builder)?; + value = Self::binary_assign_ir( + target, + value, + ctype, + token.without_assignment(), + &self.target, + builder, + )?; } builder .ins() @@ -616,7 +630,7 @@ impl Compiler { let abi_params = ftype .params .into_iter() - .map(|param| AbiParam::new(param.ctype.as_ir_type())) + .map(|param| AbiParam::new(param.ctype.as_ir_type(&self.target))) .chain(std::iter::once(float_arg)) .collect(); builder.func.dfg.signatures[call_sig].params = abi_params; @@ -638,7 +652,7 @@ impl Compiler { }; Ok(Value { ir_val, - ir_type: ftype.return_type.as_ir_type(), + ir_type: ftype.return_type.as_ir_type(&self.target), ctype: *ftype.return_type, }) } diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 2ed32982..c407d11d 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -19,6 +19,7 @@ use cranelift::codegen::{ use cranelift::frontend::Switch; use cranelift::prelude::{Block, FunctionBuilder, FunctionBuilderContext, Signature}; use cranelift_module::{self, Backend, DataId, FuncId, Linkage, Module}; +use target_lexicon::Triple; enum Id { Function(FuncId), @@ -39,6 +40,7 @@ struct Compiler { // we didn't see a default case switches: Vec<(Switch, Option, Block)>, labels: HashMap, + target: Triple, error_handler: ErrorHandler, } @@ -99,6 +101,7 @@ impl Compiler { // the initial value doesn't really matter last_saw_loop: true, strings: Default::default(), + target: Triple::host(), error_handler: Default::default(), debug, } @@ -155,7 +158,7 @@ impl Compiler { )?; return Ok(()); } - let u64_size = match decl.symbol.ctype.sizeof() { + let u64_size = match decl.symbol.ctype.sizeof(&self.target) { Ok(size) => size, Err(err) => { return Err(CompileError::semantic(Locatable { @@ -195,7 +198,9 @@ impl Compiler { let val = self.compile_expr(*expr, builder)?; // TODO: replace with `builder.ins().stack_store(val.ir_val, stack_slot, 0);` // when Cranelift implements stack_store for i8 and i16 - let addr = builder.ins().stack_addr(Type::ptr_type(), stack_slot, 0); + let addr = builder + .ins() + .stack_addr(Type::ptr_type(&self.target), stack_slot, 0); builder.ins().store(MemFlags::new(), val.ir_val, addr, 0); } Initializer::InitializerList(_) => unimplemented!("aggregate dynamic initialization"), @@ -216,12 +221,12 @@ impl Compiler { let ir_vals: Vec<_> = params .iter() .map(|param| { - let ir_type = param.ctype.as_ir_type(); + let ir_type = param.ctype.as_ir_type(&self.target); Ok(builder.append_block_param(func_start, ir_type)) }) .collect::>()?; for (param, ir_val) in params.into_iter().zip(ir_vals) { - let u64_size = match param.ctype.sizeof() { + let u64_size = match param.ctype.sizeof(&self.target) { Err(data) => semantic_err!(data.into(), *location), Ok(size) => size, }; @@ -245,7 +250,9 @@ impl Compiler { // stores for i8 and i16 // then this can be replaced with `builder.ins().stack_store(ir_val, slot, 0);` // See https://github.com/CraneStation/cranelift/issues/433 - let addr = builder.ins().stack_addr(Type::ptr_type(), slot, 0); + let addr = builder + .ins() + .stack_addr(Type::ptr_type(&self.target), slot, 0); builder.ins().store(MemFlags::new(), ir_val, addr, 0); self.scope.insert(param.id, Id::Local(slot)); } @@ -281,7 +288,7 @@ impl Compiler { self.compile_all(stmts, &mut builder)?; if !builder.is_filled() { if id == InternedStr::get_or_intern("main") { - let ir_int = func_type.return_type.as_ir_type(); + let ir_int = func_type.return_type.as_ir_type(&self.target); let zero = [builder.ins().iconst(ir_int, 0)]; builder.ins().return_(&zero); } else if should_ret { diff --git a/src/ir/static_init.rs b/src/ir/static_init.rs index e9e15dd7..676ade95 100644 --- a/src/ir/static_init.rs +++ b/src/ir/static_init.rs @@ -5,15 +5,13 @@ use std::convert::{TryFrom, TryInto}; use cranelift::codegen::ir::types; use cranelift_module::{Backend, DataContext, DataId, Linkage}; +use target_lexicon::Triple; use super::{Compiler, Id}; -use crate::arch::{PTR_SIZE, TARGET}; +use crate::arch::TARGET; use crate::data::prelude::*; use crate::data::{lex::Literal, types::ArrayType, Initializer, StorageClass}; -const_assert!(PTR_SIZE <= std::usize::MAX as u16); -const ZERO_PTR: [u8; PTR_SIZE as usize] = [0; PTR_SIZE as usize]; - impl Compiler { pub(super) fn store_static( &mut self, @@ -29,7 +27,7 @@ impl Compiler { let linkage = symbol.storage_class.try_into().map_err(err_closure)?; let align = symbol .ctype - .alignof() + .alignof(&self.target) .map_err(|err| err.to_string()) .and_then(|size| { size.try_into() @@ -74,7 +72,7 @@ impl Compiler { *size = ArrayType::Fixed(len.try_into().unwrap()); }; } - let size_t = symbol.ctype.sizeof().map_err(|err| Locatable { + let size_t = symbol.ctype.sizeof(&self.target).map_err(|err| Locatable { data: err.to_string(), location, })?; @@ -89,7 +87,7 @@ impl Compiler { ctx.define_zeroinit( symbol .ctype - .sizeof() + .sizeof(&self.target) .map_err(|err| err_closure(err.to_string()))? as usize, ); }; @@ -140,7 +138,8 @@ impl Compiler { offset: u32, expr: Expr, ) -> CompileResult<()> { - let expr = expr.const_fold()?; + let expr = expr.const_fold(&self.target)?; + let zero_ptr = vec![0; self.target.pointer_width().unwrap().bytes().into()]; // static address-of match expr.expr { ExprType::StaticRef(inner) => match inner.expr { @@ -150,12 +149,12 @@ impl Compiler { let str_addr = self.module.declare_data_in_data(str_id, ctx); ctx.write_data_addr(offset, str_addr, 0); } - ExprType::Literal(ref token) if token.is_zero() => buf.copy_from_slice(&ZERO_PTR), - ExprType::Cast(ref inner) if inner.is_zero() => buf.copy_from_slice(&ZERO_PTR), + ExprType::Literal(ref token) if token.is_zero() => buf.copy_from_slice(&zero_ptr), + ExprType::Cast(ref inner) if inner.is_zero() => buf.copy_from_slice(&zero_ptr), ExprType::Member(struct_expr, member) => { let member_offset = struct_expr .ctype - .member_offset(member) + .member_offset(member, &self.target) .expect("parser shouldn't allow Member for non-struct types"); if let ExprType::Id(symbol) = struct_expr.expr { self.static_ref(symbol, member_offset.try_into().unwrap(), offset, ctx); @@ -169,8 +168,12 @@ impl Compiler { _ => semantic_err!("cannot take the address of an rvalue".into(), expr.location), }, ExprType::Literal(token) => { - let bytes = - token.into_bytes(&expr.ctype, &expr.location, &mut self.error_handler)?; + let bytes = token.into_bytes( + &expr.ctype, + &expr.location, + &self.target, + &mut self.error_handler, + )?; buf.copy_from_slice(&bytes); } _ => semantic_err!( @@ -242,7 +245,7 @@ impl Compiler { { let size_host: usize = member .ctype - .sizeof() + .sizeof(&self.target) .map_err(|err| CompileError::semantic(location.with(err.to_string())))? .try_into() .expect("cannot initialize struct larger than u32"); @@ -284,7 +287,7 @@ impl Compiler { ); } let inner_size: usize = inner_type - .sizeof() + .sizeof(&self.target) .map_err(|err| Locatable { data: err.to_string(), location: *location, @@ -343,9 +346,10 @@ impl Literal { self, ctype: &Type, location: &Location, + target: &Triple, error_handler: &mut ErrorHandler, ) -> CompileResult> { - let ir_type = ctype.as_ir_type(); + let ir_type = ctype.as_ir_type(target); let big_endian = TARGET .endianness() .expect("target should be big or little endian") diff --git a/src/lex/cpp.rs b/src/lex/cpp.rs index bf3f8947..9c58737c 100644 --- a/src/lex/cpp.rs +++ b/src/lex/cpp.rs @@ -720,7 +720,9 @@ impl<'a> PreProcessor<'a> { // convienience function around cpp_expr fn boolean_expr(&mut self) -> Result { // TODO: is this unwrap safe? there should only be scalar types in a cpp directive... - match self.cpp_expr()?.truthy().unwrap().constexpr()?.data { + // TODO: should this use the target arch or the host arch? + let target = target_lexicon::Triple::host(); + match self.cpp_expr()?.truthy().unwrap().constexpr(&target)?.data { (Literal::Int(i), Type::Bool) => Ok(i != 0), _ => unreachable!("bug in const_fold or parser: cpp cond should be boolean"), } diff --git a/src/macros.rs b/src/macros.rs index 7a793b17..f26ff3e1 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,13 +1,3 @@ -/// ensure that a condition is true at compile time -/// thanks to https://nikolaivazquez.com/posts/programming/rust-static-assertions/ -macro_rules! const_assert { - ($condition:expr) => { - #[deny(const_err)] - #[allow(dead_code)] - const ASSERT: usize = 0 - !$condition as usize; - }; -} - /// A simple macro to create a HashMap with minimal fuss. /// /// Example: diff --git a/src/parse/decl.rs b/src/parse/decl.rs index 48ba8816..d6cff57f 100644 --- a/src/parse/decl.rs +++ b/src/parse/decl.rs @@ -3,8 +3,10 @@ use std::convert::TryFrom; use std::iter::{FromIterator, Iterator}; use std::mem; +use target_lexicon::Triple; + use super::{FunctionData, Lexeme, Parser, SyntaxResult, TagEntry}; -use crate::arch::SIZE_T; +use crate::arch::Arch; use crate::data::{ lex::Keyword, prelude::*, @@ -610,11 +612,14 @@ impl> Parser { _ => unreachable!("expect is broken"), }; if self.match_next(&Token::EQUAL).is_some() { - let constant = self.constant_expr()?.constexpr().unwrap_or_else(|err| { - let location = err.location(); - self.error_handler.push_back(err); - location.with((Literal::Int(-1), Type::Error)) - }); + let constant = self + .constant_expr()? + .constexpr(&self.target) + .unwrap_or_else(|err| { + let location = err.location(); + self.error_handler.push_back(err); + location.with((Literal::Int(-1), Type::Error)) + }); current = match constant.data.0 { Literal::Int(i) => i, Literal::UnsignedInt(u) => match i64::try_from(u) { @@ -760,11 +765,14 @@ impl> Parser { Ok(constructor(StructType::Anonymous(Rc::new(members)))) } } - fn bitfield(&mut self) -> SyntaxResult { - Ok(self.constant_expr()?.const_int().unwrap_or_else(|err| { - self.error_handler.push_back(err); - 1 - })) + fn bitfield(&mut self) -> SyntaxResult { + Ok(self + .constant_expr()? + .const_int(&self.target) + .unwrap_or_else(|err| { + self.error_handler.push_back(err); + 1 + })) } /* struct_declarator_list: struct_declarator (',' struct_declarator)* ; @@ -807,14 +815,14 @@ impl> Parser { }; if let Some(token) = self.match_next(&Token::Colon) { let bit_size = self.bitfield()?; - let type_size = symbol.ctype.sizeof().unwrap_or(0); + let type_size = symbol.ctype.sizeof(&self.target).unwrap_or(0); if bit_size == 0 { let err = format!( "C does not have zero-sized types. hint: omit the declarator {}", symbol.id ); self.semantic_err(err, self.last_location); - } else if bit_size > type_size * u64::from(crate::arch::CHAR_BIT) { + } else if bit_size > type_size * u64::from(self.target.char_bit()) { let err = format!( "cannot have bitfield {} with size {} larger than containing type {}", symbol.id, bit_size, symbol.ctype @@ -1043,7 +1051,7 @@ impl> Parser { let expr = self.constant_expr()?; self.expect(Token::RightBracket)?; - let length = expr.const_int().unwrap_or_else(|err| { + let length = expr.const_int(&self.target).unwrap_or_else(|err| { self.error_handler.push_back(err); 1 }); @@ -1601,12 +1609,12 @@ impl From for &'static str { } impl Expr { - fn const_int(self) -> CompileResult { + fn const_int(self, target: &Triple) -> CompileResult { use std::convert::TryInto; if !self.ctype.is_integral() { semantic_err!(LengthError::NonIntegral.into(), self.location,); } - let literal = self.constexpr()?; + let literal = self.constexpr(target)?; match literal.data.0 { Literal::UnsignedInt(u) => Ok(u), Literal::Int(x) => x.try_into().map_err(|_| { diff --git a/src/parse/expr.rs b/src/parse/expr.rs index 7cb7e94c..c55cb26a 100644 --- a/src/parse/expr.rs +++ b/src/parse/expr.rs @@ -1,5 +1,6 @@ +use target_lexicon::Triple; + use super::{Lexeme, Parser, SyntaxResult}; -use crate::arch::SIZE_T; use crate::data::prelude::*; use crate::data::{ lex::{AssignmentToken, ComparisonToken, Keyword}, @@ -46,7 +47,7 @@ impl> Parser { self.left_associative_binary_op( Self::assignment_expr, &[&Token::Comma], - |left, right, token| { + |left, right, token, _| { let right = right.rval(); Ok(Expr { ctype: right.ctype.clone(), @@ -152,8 +153,8 @@ impl> Parser { self.expect(Token::Colon)?; let mut otherwise = self.conditional_expr()?.rval(); if then.ctype.is_arithmetic() && otherwise.ctype.is_arithmetic() { - let (tmp1, tmp2) = - Expr::binary_promote(then, otherwise).recover(&mut self.error_handler); + let (tmp1, tmp2) = Expr::binary_promote(then, otherwise, &self.target) + .recover(&mut self.error_handler); then = tmp1; otherwise = tmp2; } else if !Type::pointer_promote(&mut then, &mut otherwise) { @@ -340,25 +341,25 @@ impl> Parser { self.left_associative_binary_op( Self::multiplicative_expr, &[&Token::Plus, &Token::Minus], - |mut left, mut right, token| { + |mut left, mut right, token, target| { match (&left.ctype, &right.ctype) { (Type::Pointer(to), i) | (Type::Array(to, _), i) if i.is_integral() && to.is_complete() => { let to = to.clone(); let (left, right) = (left.rval(), right.rval()); - return Expr::pointer_arithmetic(left, right, &*to, token.location); + return Expr::pointer_arithmetic(left, right, &*to, token.location, target); } (i, Type::Pointer(to)) // `i - p` for pointer p is not valid | (i, Type::Array(to, _)) if i.is_integral() && token.data == Token::Plus && to.is_complete() => { let to = to.clone(); let (left, right) = (left.rval(), right.rval()); - return Expr::pointer_arithmetic(right, left, &*to, token.location); + return Expr::pointer_arithmetic(right, left, &*to, token.location, target); } _ => {} }; let (ctype, lval) = if left.ctype.is_arithmetic() && right.ctype.is_arithmetic() { - let tmp = Expr::binary_promote(*left, *right).map_err(flatten)?; + let tmp = Expr::binary_promote(*left, *right, target).map_err(flatten)?; *left = tmp.0; right = Box::new(tmp.1); (left.ctype.clone(), false) @@ -401,7 +402,7 @@ impl> Parser { self.left_associative_binary_op( Self::cast_expr, &[&Token::Star, &Token::Divide, &Token::Mod], - |left, right, token| { + |left, right, token, target| { if token.data == Token::Mod && !(left.ctype.is_integral() && right.ctype.is_integral()) { @@ -417,7 +418,7 @@ impl> Parser { token.data, left.ctype, right.ctype ))), *left)); } - let (p_left, right) = Expr::binary_promote(*left, *right).map_err(flatten)?; + let (p_left, right) = Expr::binary_promote(*left, *right, target).map_err(flatten)?; Ok(Expr { ctype: p_left.ctype.clone(), location: token.location, @@ -631,7 +632,9 @@ impl> Parser { ); Ok(expr) } else { - let expr = expr.integer_promote().recover(&mut self.error_handler); + let expr = expr + .integer_promote(&self.target) + .recover(&mut self.error_handler); Ok(Expr { lval: false, location, @@ -647,7 +650,9 @@ impl> Parser { ); Ok(expr) } else { - let expr = expr.integer_promote().recover(&mut self.error_handler); + let expr = expr + .integer_promote(&self.target) + .recover(&mut self.error_handler); Ok(Expr { lval: false, ctype: expr.ctype.clone(), @@ -665,7 +670,9 @@ impl> Parser { ); Ok(expr) } else { - let expr = expr.integer_promote().recover(&mut self.error_handler); + let expr = expr + .integer_promote(&self.target) + .recover(&mut self.error_handler); Ok(Expr { lval: false, ctype: expr.ctype.clone(), @@ -717,8 +724,14 @@ impl> Parser { continue; } }; - let mut addr = Expr::pointer_arithmetic(array, index, &target_type, location) - .recover(&mut self.error_handler); + let mut addr = Expr::pointer_arithmetic( + array, + index, + &target_type, + location, + &self.target, + ) + .recover(&mut self.error_handler); addr.ctype = target_type; addr.lval = true; addr @@ -772,7 +785,7 @@ impl> Parser { for (i, arg) in args.into_iter().enumerate() { let maybe_err = match functype.params.get(i) { Some(expected) => arg.rval().cast(&expected.ctype), - None => arg.default_promote(), + None => arg.default_promote(&self.target), }; let promoted = maybe_err.recover(&mut self.error_handler); promoted_args.push(promoted); @@ -989,7 +1002,7 @@ impl> Parser { E: Fn(Box, Box) -> Result, Expr)>, G: Fn(&mut Self) -> SyntaxResult, { - self.left_associative_binary_op(next_grammar_func, &[token], move |left, right, token| { + self.left_associative_binary_op(next_grammar_func, &[token], move |left, right, token, _| { let non_scalar = if !left.ctype.is_scalar() { Some(left.ctype.clone()) } else if !right.ctype.is_scalar() { @@ -1035,7 +1048,7 @@ impl> Parser { E: Fn(Expr, Expr, Locatable) -> RecoverableResult>, G: Fn(&mut Self) -> SyntaxResult, { - self.left_associative_binary_op(next_grammar_func, tokens, |expr, next, token| { + self.left_associative_binary_op(next_grammar_func, tokens, |expr, next, token, target| { let non_scalar = if !expr.ctype.is_integral() { Some(&expr.ctype) } else if !next.ctype.is_integral() { @@ -1055,7 +1068,8 @@ impl> Parser { *expr, )); } - let (promoted_expr, next) = Expr::binary_promote(*expr, *next).map_err(flatten)?; + let (promoted_expr, next) = + Expr::binary_promote(*expr, *next, target).map_err(flatten)?; expr_func(promoted_expr, next, token) }) } @@ -1081,13 +1095,14 @@ impl> Parser { Box, Box, Locatable, + &Triple, ) -> RecoverableResult>, G: Fn(&mut Self) -> SyntaxResult, { let mut expr = next_grammar_func(self)?; while let Some(locatable) = self.match_any(tokens) { let next = next_grammar_func(self)?; - match expr_func(Box::new(expr), Box::new(next), locatable) { + match expr_func(Box::new(expr), Box::new(next), locatable, &self.target) { Ok(combined) => expr = combined, Err((err, original)) => { expr = original; @@ -1219,17 +1234,17 @@ impl Expr { _ => self, } } - fn default_promote(self) -> RecoverableResult> { + fn default_promote(self, target: &Triple) -> RecoverableResult> { let expr = self.rval(); - let ctype = expr.ctype.clone().default_promote(); + let ctype = expr.ctype.clone().default_promote(target); expr.cast(&ctype) } // Perform an integer conversion, including all relevant casts. // // See `Type::integer_promote` for conversion rules. - fn integer_promote(self) -> RecoverableResult> { + fn integer_promote(self, target: &Triple) -> RecoverableResult> { let expr = self.rval(); - let ctype = expr.ctype.clone().integer_promote(); + let ctype = expr.ctype.clone().integer_promote(target); expr.cast(&ctype) } // Perform a binary conversion, including all relevant casts. @@ -1238,9 +1253,10 @@ impl Expr { fn binary_promote( left: Expr, right: Expr, + target: &Triple, ) -> RecoverableResult<(Expr, Expr), Locatable> { let (left, right) = (left.rval(), right.rval()); - let ctype = Type::binary_promote(left.ctype.clone(), right.ctype.clone()); + let ctype = Type::binary_promote(left.ctype.clone(), right.ctype.clone(), target); match (left.cast(&ctype), right.cast(&ctype)) { (Ok(left_cast), Ok(right_cast)) => Ok((left_cast, right_cast)), (Err((err, left)), Ok(right)) | (Ok(left), Err((err, right))) @@ -1349,6 +1365,7 @@ impl Expr { index: Expr, pointee: &Type, location: Location, + target: &Triple, ) -> RecoverableResult> { let offset = Expr { lval: false, @@ -1358,7 +1375,7 @@ impl Expr { ctype: base.ctype.clone(), } .rval(); - let size = match pointee.sizeof() { + let size = match pointee.sizeof(target) { Ok(s) => s, Err(_) => { return Err(( @@ -1475,13 +1492,14 @@ impl Expr { mut left: Box, mut right: Box, token: Locatable, + target: &Triple, ) -> RecoverableResult> { let token = match token.data { Token::Comparison(c) => token.location.with(c), _ => unreachable!("bad use of relational_expr"), }; if left.ctype.is_arithmetic() && right.ctype.is_arithmetic() { - let tmp = Expr::binary_promote(*left, *right).map_err(flatten)?; + let tmp = Expr::binary_promote(*left, *right, target).map_err(flatten)?; *left = tmp.0; right = Box::new(tmp.1); } else { @@ -1541,7 +1559,7 @@ impl From<(Literal, Location)> for Expr { Literal::Int(_) => Type::Long(true), Literal::UnsignedInt(_) => Type::Long(false), Literal::Float(_) => Type::Double, - Literal::Str(s) => Type::for_string_literal(s.len() as SIZE_T), + Literal::Str(s) => Type::for_string_literal(s.len() as u64), }; Expr { constexpr: true, @@ -1602,18 +1620,18 @@ impl Type { } } // Perform the 'default promotions' from 6.5.2.2.6 - fn default_promote(self) -> Type { + fn default_promote(self, target: &Triple) -> Type { if self.is_integral() { - self.integer_promote() + self.integer_promote(target) } else if self == Type::Float { Type::Double } else { self } } - fn integer_promote(self) -> Type { + fn integer_promote(self, target: &Triple) -> Type { if self.rank() <= Type::Int(true).rank() { - if Type::Int(true).can_represent(&self) { + if Type::Int(true).can_represent(&self, target) { Type::Int(true) } else { Type::Int(false) @@ -1639,15 +1657,15 @@ impl Type { /// /// Trying to promote derived types (pointers, functions, etc.) is an error. /// Pointer arithmetic should not promote either argument, see 6.5.6 of the C standard. - fn binary_promote(mut left: Type, mut right: Type) -> Type { + fn binary_promote(mut left: Type, mut right: Type, target: &Triple) -> Type { use Type::*; if left == Double || right == Double { return Double; // toil and trouble } else if left == Float || right == Float { return Float; } - left = left.integer_promote(); - right = right.integer_promote(); + left = left.integer_promote(target); + right = right.integer_promote(target); let signs = (left.sign(), right.sign()); // same sign if signs.0 == signs.1 { @@ -1662,7 +1680,7 @@ impl Type { } else { (right, left) }; - if signed.can_represent(&unsigned) { + if signed.can_represent(&unsigned, target) { signed } else { unsigned @@ -1725,7 +1743,7 @@ impl Type { _ => std::usize::MAX, } } - fn for_string_literal(len: SIZE_T) -> Type { + fn for_string_literal(len: u64) -> Type { Type::Array(Box::new(Type::Char(true)), ArrayType::Fixed(len)) } } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 34d13a31..8389e82a 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -8,6 +8,8 @@ use std::iter::Iterator; use std::mem; use std::rc::Rc; +use target_lexicon::Triple; + use crate::data::{prelude::*, Scope}; type Lexeme = CompileResult>; @@ -60,6 +62,8 @@ pub struct Parser> { current_function: Option, /// whether to debug each declaration debug: bool, + /// Which platform are we compiling for? + target: Triple, /// Internal API which makes it easier to return errors lazily error_handler: ErrorHandler, /// Internal API which prevents segfaults due to stack overflow @@ -99,6 +103,8 @@ where next: None, current_function: None, debug, + // TODO: allow cross-compilation + target: Triple::host(), error_handler: ErrorHandler::new(), recursion_guard: Default::default(), }