From fa858f768b5b76854705f4f6369e37d263c5e871 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Wed, 11 Mar 2020 23:20:24 -0400 Subject: [PATCH 1/4] Make architecture configurable --- src/analyze/expr.rs | 98 ++++++++++++++--------------- src/analyze/mod.rs | 28 +++++---- src/analyze/stmt.rs | 2 +- src/arch/mod.rs | 140 ++++++++++++++++++++++++++++-------------- src/arch/x64.rs | 26 +++----- src/data/types.rs | 5 +- src/fold.rs | 105 +++++++++++++++++-------------- src/ir/expr.rs | 44 +++++++------ src/ir/mod.rs | 46 ++++++++------ src/ir/static_init.rs | 36 ++++++----- src/lex/cpp.rs | 9 +-- src/macros.rs | 10 --- src/parse/implicit.c | 1 + 13 files changed, 310 insertions(+), 240 deletions(-) create mode 100644 src/parse/implicit.c diff --git a/src/analyze/expr.rs b/src/analyze/expr.rs index 9390e42e..cbb5d215 100644 --- a/src/analyze/expr.rs +++ b/src/analyze/expr.rs @@ -1,9 +1,10 @@ use super::Analyzer; -use crate::arch; use crate::data::{hir::*, lex::ComparisonToken, *}; use crate::intern::InternedStr; use crate::parse::Lexer; +use target_lexicon::Triple; + impl Analyzer { pub fn parse_expr(&mut self, expr: ast::Expr) -> Expr { use ast::ExprType::*; @@ -213,7 +214,7 @@ impl Analyzer { if let Some(ctype) = non_scalar { self.err(SemanticError::NonIntegralExpr(ctype.clone()), location); } - let (promoted_expr, next) = Expr::binary_promote(left, right, &mut self.error_handler); + let (promoted_expr, next) = self.binary_promote(left, right); Expr { ctype: next.ctype.clone(), expr: ExprType::Binary(op, Box::new(promoted_expr), Box::new(next)), @@ -275,7 +276,7 @@ impl Analyzer { // i == i if left.ctype.is_arithmetic() && right.ctype.is_arithmetic() { - let tmp = Expr::binary_promote(left, right, &mut self.error_handler); + let tmp = self.binary_promote(left, right); left = tmp.0; right = tmp.1; } else { @@ -336,7 +337,7 @@ impl Analyzer { location, ); } - let (left, right) = Expr::binary_promote(left, right, &mut self.error_handler); + let (left, right) = self.binary_promote(left, right); Expr { ctype: left.ctype.clone(), location, @@ -370,7 +371,7 @@ impl Analyzer { }; // `i + i` let (ctype, lval) = if left.ctype.is_arithmetic() && right.ctype.is_arithmetic() { - let tmp = Expr::binary_promote(left, right, &mut self.error_handler); + let tmp = self.binary_promote(left, right); left = tmp.0; right = tmp.1; (left.ctype.clone(), false) @@ -451,7 +452,7 @@ impl Analyzer { ctype: base.ctype.clone(), } .rval(); - let size = match pointee.sizeof() { + let size = match pointee.sizeof(&self.target) { Ok(s) => s, Err(_) => { self.err( @@ -543,7 +544,7 @@ impl Analyzer { /// 'default promotions' from 6.5.2.2p6 fn default_promote(&mut self, expr: Expr) -> Expr { let expr = expr.rval(); - let ctype = expr.ctype.clone().default_promote(); + let ctype = expr.ctype.clone().default_promote(&self.target); expr.implicit_cast(&ctype, &mut self.error_handler) } // parse a struct member @@ -653,7 +654,7 @@ impl Analyzer { } // _Alignof(int) fn align(&mut self, ctype: Type, location: Location) -> Expr { - let align = ctype.alignof().unwrap_or_else(|err| { + let align = ctype.alignof(&self.target).unwrap_or_else(|err| { self.err(err.into(), location); 1 }); @@ -662,7 +663,7 @@ impl Analyzer { // sizeof(int) // 6.5.3.4 The sizeof and _Alignof operators fn sizeof(&mut self, ctype: Type, location: Location) -> Expr { - let align = ctype.sizeof().unwrap_or_else(|err| { + let align = ctype.sizeof(&self.target).unwrap_or_else(|err| { self.err(err.into(), location); 1 }); @@ -679,7 +680,7 @@ impl Analyzer { ); expr } else { - let expr = expr.integer_promote(&mut self.error_handler); + let expr = self.integer_promote(expr); Expr { lval: false, ctype: expr.ctype.clone(), @@ -696,7 +697,7 @@ impl Analyzer { self.err(SemanticError::NotArithmetic(expr.ctype.clone()), location); return expr; } - let expr = expr.integer_promote(&mut self.error_handler); + let expr = self.integer_promote(expr); if add { Expr { lval: false, @@ -759,7 +760,7 @@ impl Analyzer { let mut otherwise = self.parse_expr(otherwise).rval(); if then.ctype.is_arithmetic() && otherwise.ctype.is_arithmetic() { - let (tmp1, tmp2) = Expr::binary_promote(then, otherwise, &mut self.error_handler); + let (tmp1, tmp2) = self.binary_promote(then, otherwise); then = tmp1; otherwise = tmp2; } else if !pointer_promote(&mut then, &mut otherwise) { @@ -884,6 +885,34 @@ impl Analyzer { SubEqual => self.add(left, right, BinaryOp::Sub), } } + + // Perform a binary conversion, including all relevant casts. + // + // See `Type::binary_promote` for conversion rules. + fn binary_promote(&mut self, left: Expr, right: Expr) -> (Expr, Expr) { + let (left, right) = (left.rval(), right.rval()); + let ctype = Type::binary_promote(left.ctype.clone(), right.ctype.clone(), &self.target); + match ctype { + Ok(promoted) => ( + left.implicit_cast(&promoted, &mut self.error_handler), + right.implicit_cast(&promoted, &mut self.error_handler), + ), + Err(non_int) => { + // TODO: this location is wrong + self.err(SemanticError::NonIntegralExpr(non_int), right.location); + (left, right) + } + } + } + + // Perform an integer conversion, including all relevant casts. + // + // See `Type::integer_promote` for conversion rules. + fn integer_promote(&mut self, expr: Expr) -> Expr { + let expr = expr.rval(); + let ctype = expr.ctype.clone().integer_promote(&self.target); + expr.implicit_cast(&ctype, &mut self. error_handler) + } } // literal @@ -896,7 +925,7 @@ pub(super) fn literal(literal: Literal, location: Location) -> Expr { Literal::UnsignedInt(_) => Type::Long(false), Literal::Float(_) => Type::Double, Literal::Str(s) => { - let len = s.len() as arch::SIZE_T; + let len = s.len() as u64; Type::Array(Box::new(Type::Char(true)), ArrayType::Fixed(len)) } }; @@ -991,9 +1020,9 @@ impl Type { } } // Subclause 2 of 6.3.1.1 Boolean, characters, and integers - 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) @@ -1003,15 +1032,15 @@ impl Type { } } // 6.3.1.8 Usual arithmetic conversions - fn binary_promote(mut left: Type, mut right: Type) -> Result { + fn binary_promote(mut left: Type, mut right: Type, target: &Triple) -> Result { use Type::*; if left == Double || right == Double { return Ok(Double); // toil and trouble } else if left == Float || right == Float { return Ok(Float); } - left = left.integer_promote(); - right = right.integer_promote(); + left = left.integer_promote(target); + right = right.integer_promote(target); // TODO: we know that `left` can't be used after a move, // but rustc isn't smart enough to figure it out and let us remove the clone let signs = ( @@ -1031,7 +1060,7 @@ impl Type { } else { (right, left) }; - if signed.can_represent(&unsigned) { + if signed.can_represent(&unsigned, target) { Ok(signed) } else { Ok(unsigned) @@ -1042,9 +1071,9 @@ impl Type { /// > the integer promotions are performed on each argument, /// > and arguments that have type float are promoted to double. /// > These are called the default argument promotions. - 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 { @@ -1132,33 +1161,6 @@ impl Expr { } } - // Perform an integer conversion, including all relevant casts. - // - // See `Type::integer_promote` for conversion rules. - fn integer_promote(self, error_handler: &mut ErrorHandler) -> Expr { - let expr = self.rval(); - let ctype = expr.ctype.clone().integer_promote(); - expr.implicit_cast(&ctype, error_handler) - } - - // Perform a binary conversion, including all relevant casts. - // - // See `Type::binary_promote` for conversion rules. - fn binary_promote(left: Expr, right: Expr, error_handler: &mut ErrorHandler) -> (Expr, Expr) { - let (left, right) = (left.rval(), right.rval()); - let ctype = Type::binary_promote(left.ctype.clone(), right.ctype.clone()); - match ctype { - Ok(promoted) => ( - left.implicit_cast(&promoted, error_handler), - right.implicit_cast(&promoted, error_handler), - ), - Err(non_int) => { - // TODO: this location is wrong - error_handler.error(SemanticError::NonIntegralExpr(non_int), right.location); - (left, right) - } - } - } // ensure an expression has a value. convert // - arrays -> pointers // - functions -> pointers diff --git a/src/analyze/mod.rs b/src/analyze/mod.rs index 1066a9c5..86a7ce37 100644 --- a/src/analyze/mod.rs +++ b/src/analyze/mod.rs @@ -6,7 +6,9 @@ use std::collections::{HashSet, VecDeque}; use std::convert::TryInto; use counter::Counter; +use target_lexicon::Triple; +use crate::arch::Arch; use crate::data::{error::Warning, hir::*, lex::Keyword, *}; use crate::intern::InternedStr; use crate::parse::{Lexer, Parser}; @@ -42,6 +44,8 @@ pub struct Analyzer { initialized: HashSet, /// Internal API which makes it easier to return errors lazily error_handler: ErrorHandler, + /// Which platform are we compiling for? + target: Triple, /// Hack to make compound assignment work /// /// For `a += b`, `a` must only be evaluated once. @@ -90,6 +94,8 @@ impl Analyzer { tag_scope: Scope::new(), pending: VecDeque::new(), initialized: HashSet::new(), + // TODO: allow cross-compilation + target: Triple::host(), decl_side_channel: Vec::new(), } } @@ -580,14 +586,14 @@ impl Analyzer { }; // struct s { int i: 5 }; if let Some(bitfield) = bitfield { - let bit_size = match Self::const_uint(self.parse_expr(bitfield)) { + let bit_size = match Self::const_uint(self.parse_expr(bitfield), &self.target) { Ok(e) => e, Err(err) => { self.error_handler.push_back(err); 1 } }; - 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 = SemanticError::from(format!( "C does not have zero-sized types. hint: omit the declarator {}", @@ -595,7 +601,7 @@ impl Analyzer { )); self.err(err, location); // struct s { int i: 65 } - } 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 = SemanticError::from(format!( "cannot have bitfield {} with size {} larger than containing type {}", symbol.id, bit_size, symbol.ctype @@ -687,7 +693,7 @@ impl Analyzer { for (name, maybe_value) in ast_members { // enum E { A = 5 }; if let Some(value) = maybe_value { - discriminant = Self::const_sint(self.parse_expr(value)).unwrap_or_else(|err| { + discriminant = Self::const_sint(self.parse_expr(value), &self.target).unwrap_or_else(|err| { self.error_handler.push_back(err); std::i64::MIN }); @@ -835,7 +841,7 @@ impl Analyzer { Array { of, size } => { // int a[5] let size = if let Some(expr) = size { - let size = Self::const_uint(self.parse_expr(*expr)).unwrap_or_else(|err| { + let size = Self::const_uint(self.parse_expr(*expr), &self.target).unwrap_or_else(|err| { self.error_handler.push_back(err); 1 }); @@ -937,9 +943,9 @@ impl Analyzer { } } // used for arrays like `int a[BUF_SIZE - 1];` and enums like `enum { A = 1 }` - fn const_literal(expr: Expr) -> CompileResult { + fn const_literal(expr: Expr, target: &Triple) -> CompileResult { let location = expr.location; - expr.const_fold()?.into_literal().or_else(|runtime_expr| { + expr.const_fold(target)?.into_literal().or_else(|runtime_expr| { Err(Locatable::new( SemanticError::NotConstant(runtime_expr).into(), location, @@ -947,11 +953,11 @@ impl Analyzer { }) } /// Return an unsigned integer that can be evaluated at compile time, or an error otherwise. - fn const_uint(expr: Expr) -> CompileResult { + fn const_uint(expr: Expr, target: &Triple) -> CompileResult { use Literal::*; let location = expr.location; - match Self::const_literal(expr)? { + match Self::const_literal(expr, target)? { UnsignedInt(i) => Ok(i), Int(i) => { if i < 0 { @@ -971,11 +977,11 @@ impl Analyzer { } } /// Return a signed integer that can be evaluated at compile time, or an error otherwise. - fn const_sint(expr: Expr) -> CompileResult { + fn const_sint(expr: Expr, target: &Triple) -> CompileResult { use Literal::*; let location = expr.location; - match Self::const_literal(expr)? { + match Self::const_literal(expr, target)? { UnsignedInt(u) => match u.try_into() { Ok(i) => Ok(i), Err(_) => Err(Locatable::new( diff --git a/src/analyze/stmt.rs b/src/analyze/stmt.rs index d5369d1d..b848d9c7 100644 --- a/src/analyze/stmt.rs +++ b/src/analyze/stmt.rs @@ -120,7 +120,7 @@ impl FunctionAnalyzer<'_, T> { use super::expr::literal; use crate::data::lex::Literal; - let expr = match self.parse_expr(expr).const_fold() { + let expr = match self.parse_expr(expr).const_fold(&self.analyzer.target) { Ok(e) => e, Err(err) => { self.analyzer.error_handler.push_back(err); diff --git a/src/arch/mod.rs b/src/arch/mod.rs index 30ca77f8..983924ec 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -25,98 +25,146 @@ const CHAR_SIZE: u16 = 1; /// information like ABI and endianness. pub(crate) const TARGET: Triple = Triple::host(); -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 { @@ -127,8 +175,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), // illegal operations Function(_) => Err("cannot take `sizeof` a function"), Void => Err("cannot take `sizeof` void"), @@ -137,7 +185,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(_) @@ -147,12 +195,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), Function(_) => Err("cannot take `alignof` function"), Void => Err("cannot take `alignof` void"), VaList => Err("cannot take `alignof` va_list"), @@ -164,6 +212,7 @@ impl Type { #[cfg(test)] mod tests { use proptest::prelude::*; + use target_lexicon::HOST; use crate::data::{ hir::Metadata, @@ -175,12 +224,13 @@ mod tests { use super::*; 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]), } } @@ -214,7 +264,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() { @@ -233,7 +283,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()); } } @@ -252,9 +302,9 @@ 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() { @@ -262,7 +312,7 @@ mod tests { Pointer(Box::new(Int(true)), Qualifiers::default()), Int(true), ]); - assert_eq!(ty.alignof(), Ok(8)); + assert_eq!(ty.alignof(&HOST), Ok(8)); } proptest! { @@ -271,15 +321,15 @@ mod tests { #[test] fn proptest_align_power_of_two(t in arb_type()) { - if let Ok(align) = t.alignof() { + if let Ok(align) = t.alignof(&HOST) { prop_assert!(align.is_power_of_two()); } } #[test] fn proptest_sizeof_multiple_of_alignof(t in arb_type()) { - if let Ok(sizeof) = t.sizeof() { - prop_assert_eq!(sizeof % t.alignof().unwrap(), 0); + if let Ok(sizeof) = t.sizeof(&HOST) { + prop_assert_eq!(sizeof % t.alignof(&HOST).unwrap(), 0); } } } 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 9644db93..fa23b497 100644 --- a/src/data/types.rs +++ b/src/data/types.rs @@ -2,6 +2,7 @@ use super::hir::{Metadata, MetadataRef}; use crate::intern::InternedStr; #[cfg(test)] use proptest_derive::Arbitrary; +use target_lexicon::Triple; use std::fmt::{self, Formatter}; pub use struct_ref::{StructRef, StructType}; @@ -240,9 +241,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 07333f8b..d2e6c41a 100644 --- a/src/fold.rs +++ b/src/fold.rs @@ -1,8 +1,9 @@ -use crate::arch::CHAR_BIT; -use crate::data::hir::*; -use crate::data::lex::Literal::*; +use crate::data::hir::{BinaryOp, Expr, ExprType}; +use crate::arch::Arch; use crate::data::*; use std::ops::{Add, Div, Mul, Sub}; +use target_lexicon::Triple; +use Literal::*; macro_rules! fold_int_bin_op { ($op: tt) => { @@ -45,8 +46,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) { @@ -95,8 +96,9 @@ impl Expr { _ => Err(self), } } - 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), @@ -105,7 +107,7 @@ impl Expr { _ => Err(folded.location.error(SemanticError::NotConstant(folded))), } } - pub fn const_fold(self) -> CompileResult { + pub fn const_fold(self, target: &Triple) -> CompileResult { let location = self.location; let folded = match self.expr { ExprType::Literal(_) => self.expr, @@ -120,13 +122,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) => { @@ -146,7 +148,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)), @@ -157,10 +159,10 @@ impl Expr { ExprType::BitwiseNot, )?, ExprType::Binary(op, left, right) => { - fold_binary(*left, *right, op, &self.ctype, location)? + fold_binary(*left, *right, op, &self.ctype, target, location)? } 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.is_constexpr() { right.expr @@ -169,11 +171,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 { return Err(Locatable::new( SemanticError::NullPointerDereference.into(), @@ -184,9 +186,9 @@ impl Expr { } 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, @@ -197,11 +199,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 @@ -209,16 +211,16 @@ 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::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::StaticRef(inner) => ExprType::StaticRef(Box::new(inner.const_fold()?)), + ExprType::Cast(expr) => cast(*expr, &self.ctype, target)?, + ExprType::StaticRef(inner) => ExprType::StaticRef(Box::new(inner.const_fold(target)?)), }; Ok(Expr { expr: folded, @@ -237,11 +239,12 @@ impl Expr { location: &Location, fold_func: F, op: BinaryOp, + target: &Triple, ) -> CompileResult where F: FnOnce(&Literal, &Literal, &Type) -> Result, SemanticError>, { - 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) { @@ -280,13 +283,14 @@ fn fold_binary( right: Expr, op: BinaryOp, parent_type: &Type, + target: &Triple, location: Location, ) -> CompileResult { use lex::ComparisonToken::*; use BinaryOp::*; - let left = left.const_fold()?; - let right = right.const_fold()?; + let left = left.const_fold(target)?; + let right = right.const_fold(target)?; match op { Add => left.literal_bin_op( @@ -299,6 +303,7 @@ fn fold_binary( u8::wrapping_add, ), Add, + target, ), Sub => left.literal_bin_op( right, @@ -310,6 +315,7 @@ fn fold_binary( u8::wrapping_sub, ), Sub, + target, ), Mul => left.literal_bin_op( right, @@ -321,6 +327,7 @@ fn fold_binary( u8::wrapping_mul, ), Mul, + target, ), Div => { if right.is_zero() { @@ -336,6 +343,7 @@ fn fold_binary( u8::wrapping_div, ), Div, + target, ) } Mod => { @@ -361,13 +369,14 @@ fn fold_binary( (_, _) => Ok(None), }, Mod, + target, ) } - Xor => left.literal_bin_op(right, &location, fold_int_bin_op!(^), Xor), - BitwiseAnd => left.literal_bin_op(right, &location, fold_int_bin_op!(&), BitwiseAnd), - BitwiseOr => left.literal_bin_op(right, &location, fold_int_bin_op!(|), BitwiseOr), - Shl => shift_left(left, right, parent_type, &location), - Shr => shift_right(left, right, parent_type, &location), + Xor => left.literal_bin_op(right, &location, fold_int_bin_op!(^), Xor, target), + BitwiseAnd => left.literal_bin_op(right, &location, fold_int_bin_op!(&), BitwiseAnd, target), + BitwiseOr => left.literal_bin_op(right, &location, fold_int_bin_op!(|), BitwiseOr, target), + Shl => shift_left(left, right, parent_type, target, &location), + Shr => shift_right(left, right, parent_type, target, &location), LogicalAnd => left.literal_bin_op( right, &location, @@ -377,6 +386,7 @@ fn fold_binary( _ => Ok(None), }, LogicalAnd, + target, ), LogicalOr => left.literal_bin_op( right, @@ -387,6 +397,7 @@ fn fold_binary( _ => Ok(None), }, LogicalOr, + target, ), Assign => { // TODO: could we propagate this information somehow? @@ -397,12 +408,12 @@ fn fold_binary( Box::new(right), )) } - Compare(Less) => Ok(fold_compare_op!(left, right, Compare, <, Less)), - Compare(LessEqual) => Ok(fold_compare_op!(left, right, Compare, <=, LessEqual)), - Compare(Greater) => Ok(fold_compare_op!(left, right, Compare, >, Greater)), - Compare(GreaterEqual) => Ok(fold_compare_op!(left, right, Compare, >=, GreaterEqual)), - Compare(EqualEqual) => Ok(fold_compare_op!(left, right, Compare, ==, EqualEqual)), - Compare(NotEqual) => Ok(fold_compare_op!(left, right, Compare, !=, NotEqual)), + Compare(Less) => Ok(fold_compare_op!(left, right, Compare, <, Less, target)), + Compare(LessEqual) => Ok(fold_compare_op!(left, right, Compare, <=, LessEqual, target)), + Compare(Greater) => Ok(fold_compare_op!(left, right, Compare, >, Greater, target)), + Compare(GreaterEqual) => Ok(fold_compare_op!(left, right, Compare, >=, GreaterEqual, target)), + Compare(EqualEqual) => Ok(fold_compare_op!(left, right, Compare, ==, EqualEqual, target)), + Compare(NotEqual) => Ok(fold_compare_op!(left, right, Compare, !=, NotEqual, target)), } } @@ -417,8 +428,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) @@ -471,9 +482,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, @@ -481,7 +493,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, })?; @@ -522,9 +534,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, @@ -534,7 +547,7 @@ 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) => { return Err(Locatable::new( @@ -543,7 +556,7 @@ fn shift_left( )) } }; - 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, @@ -589,7 +602,7 @@ mod tests { use crate::data::*; fn test_const_fold(s: &str) -> CompileResult { - analyze_expr(s).unwrap().const_fold() + analyze_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 a5b59002..84674ed7 100644 --- a/src/ir/expr.rs +++ b/src/ir/expr.rs @@ -1,6 +1,7 @@ use cranelift::codegen::ir::{condcodes, types, MemFlags}; use cranelift::prelude::{FunctionBuilder, InstBuilder, Type as IrType, Value as IrValue}; use cranelift_module::Backend; +use target_lexicon::Triple; use super::{Compiler, Id}; use crate::data::*; @@ -28,12 +29,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) => { @@ -92,9 +93,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, @@ -107,7 +110,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, @@ -151,7 +154,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)?; @@ -225,7 +228,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"), }; @@ -261,19 +266,20 @@ impl Compiler { self.compile_expr(left, builder)?, self.compile_expr(right, builder)?, ); - Self::binary_assign_ir(left, right, ctype, op, builder) + Self::binary_assign_ir(left, right, ctype, op, &self.target, builder) } fn binary_assign_ir( left: Value, right: Value, ctype: Type, op: BinaryOp, + target: &Triple, builder: &mut FunctionBuilder, ) -> IrResult { use cranelift::codegen::ir::InstBuilder as b; use BinaryOp::*; 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 (op, ir_type, signed) { (Add, ty, _) if ty.is_int() => b::iadd, @@ -319,7 +325,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, @@ -413,7 +419,7 @@ impl Compiler { } fn load_addr(&self, var: MetadataRef, builder: &mut FunctionBuilder) -> IrResult { let metadata = var.get(); - let ptr_type = Type::ptr_type(); + let ptr_type = Type::ptr_type(&self.target); let ir_val = match self .declarations .get(&var) @@ -472,10 +478,12 @@ impl Compiler { ); if let Type::Union(_) | Type::Struct(_) = ctype { 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( @@ -564,7 +572,7 @@ impl Compiler { let abi_params = ftype .params .into_iter() - .map(|param| AbiParam::new(param.get().ctype.as_ir_type())) + .map(|param| AbiParam::new(param.get().ctype.as_ir_type(&self.target))) .chain(std::iter::once(float_arg)) .collect(); builder.func.dfg.signatures[call_sig].params = abi_params; @@ -572,7 +580,7 @@ impl Compiler { call } FuncCall::Indirect(callee) => { - let sig = ftype.signature(self.module.isa()); + let sig = ftype.signature(self.module.isa(), &self.target); let sigref = builder.import_signature(sig); builder .ins() @@ -586,7 +594,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 ce6c20ec..aff54199 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -2,10 +2,10 @@ mod expr; mod static_init; mod stmt; +use crate::arch::{Arch, TARGET}; use std::collections::{HashMap, VecDeque}; use std::convert::TryFrom; -use crate::arch::{CHAR_BIT, PTR_SIZE, SIZE_T, TARGET}; use crate::data::lex::ComparisonToken; use cranelift::codegen::{ self, @@ -25,6 +25,7 @@ use cranelift::prelude::{Block, FunctionBuilder, FunctionBuilderContext}; use cranelift_module::{self, Backend, DataId, FuncId, Linkage, Module}; use cranelift_object::{ObjectBackend, ObjectBuilder}; use lazy_static::lazy_static; +use target_lexicon::Triple; use crate::data::{ hir::{Declaration, Initializer, MetadataRef, Stmt}, @@ -92,6 +93,7 @@ struct Compiler { // we didn't see a default case switches: Vec<(Switch, Option, Block)>, labels: HashMap, + target: Triple, error_handler: ErrorHandler, } @@ -146,6 +148,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, } @@ -172,7 +175,7 @@ impl Compiler { Type::Function(func_type) => func_type, _ => unreachable!("bug in backend: only functions should be passed to `declare_func`"), }; - let signature = func_type.signature(self.module.isa()); + let signature = func_type.signature(self.module.isa(), &self.target); let linkage = match metadata.storage_class { StorageClass::Auto | StorageClass::Extern if is_definition => Linkage::Export, StorageClass::Auto | StorageClass::Extern => Linkage::Import, @@ -201,7 +204,7 @@ impl Compiler { self.declare_func(decl.symbol, false)?; return Ok(()); } - let u64_size = match meta.ctype.sizeof() { + let u64_size = match meta.ctype.sizeof(&self.target) { Ok(size) => size, Err(err) => { return Err(CompileError::semantic(Locatable { @@ -241,7 +244,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"), @@ -262,12 +267,12 @@ impl Compiler { let ir_vals: Vec<_> = params .iter() .map(|param| { - let ir_type = param.get().ctype.as_ir_type(); + let ir_type = param.get().ctype.as_ir_type(&self.target); Ok(builder.append_block_param(func_start, ir_type)) }) .collect::>()?; for (¶m, ir_val) in params.iter().zip(ir_vals) { - let u64_size = match param.get().ctype.sizeof() { + let u64_size = match param.get().ctype.sizeof(&self.target) { Err(data) => semantic_err!(data.into(), *location), Ok(size) => size, }; @@ -291,7 +296,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.declarations.insert(param, Id::Local(slot)); } @@ -307,7 +314,7 @@ impl Compiler { let func_id = self.declare_func(symbol, true)?; // TODO: make declare_func should take a `signature` after all? // This just calculates it twice, it's probably fine - let signature = func_type.signature(self.module.isa()); + let signature = func_type.signature(self.module.isa(), &self.target); // external name is meant to be a lookup in a symbol table, // but we just give it garbage values @@ -334,7 +341,7 @@ impl Compiler { if !builder.is_filled() { let id = symbol.get().id; 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 { @@ -388,14 +395,14 @@ impl FunctionType { } /// Generate the IR function signature for `self` - pub fn signature(&self, isa: &dyn TargetIsa) -> Signature { + pub fn signature(&self, isa: &dyn TargetIsa, target: &Triple) -> Signature { let mut params = if self.params.len() == 1 && self.params[0].get().ctype == Type::Void { // no arguments Vec::new() } else { self.params .iter() - .map(|param| AbiParam::new(param.get().ctype.as_ir_type())) + .map(|param| AbiParam::new(param.get().ctype.as_ir_type(&isa.triple()))) .collect() }; if self.varargs { @@ -412,7 +419,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, @@ -451,23 +458,23 @@ impl ComparisonToken { } } -use std::convert::TryInto; 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") + pub fn ptr_type(target: &Triple) -> IrType { + IrType::triple_pointer_type(&target) } /// 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 { + use std::convert::TryInto; use Type::*; 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!( @@ -485,8 +492,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(_, _) => Type::ptr_type(target), // void cannot be loaded or stored _ => types::INVALID, } diff --git a/src/ir/static_init.rs b/src/ir/static_init.rs index 6b1e7b1e..39e263a6 100644 --- a/src/ir/static_init.rs +++ b/src/ir/static_init.rs @@ -5,9 +5,10 @@ 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::*; use crate::data::{ hir::{Expr, ExprType, Initializer, MetadataRef}, @@ -16,9 +17,6 @@ use crate::data::{ 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, @@ -37,7 +35,7 @@ impl Compiler { }; let align = metadata .ctype - .alignof() + .alignof(&self.target) .map_err(|err| err.to_string()) .and_then(|size| { size.try_into() @@ -86,7 +84,7 @@ impl Compiler { *size = ArrayType::Fixed(len.try_into().unwrap()); }; } - let size_t = ctype.sizeof().map_err(|err| Locatable { + let size_t = ctype.sizeof(&self.target).map_err(|err| Locatable { data: err.to_string(), location, })?; @@ -101,7 +99,7 @@ impl Compiler { ctx.define_zeroinit( metadata .ctype - .sizeof() + .sizeof(&self.target) .map_err(|err| err_closure(err.to_string()))? as usize, ); }; @@ -155,7 +153,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 { @@ -165,12 +164,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); @@ -184,8 +183,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!( @@ -263,7 +266,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"); @@ -303,7 +306,7 @@ impl Compiler { ); } let inner_size: usize = inner_type - .sizeof() + .sizeof(&self.target) .map_err(|err| Locatable { data: err.to_string(), location: *location, @@ -362,9 +365,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 55b84ab6..41cddd1d 100644 --- a/src/lex/cpp.rs +++ b/src/lex/cpp.rs @@ -735,12 +735,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(&mut self.error_handler) - .constexpr()? - .data - { + // TODO: should this use the target arch or the host arch? + let target = target_lexicon::Triple::host(); + match self.cpp_expr()?.truthy(&mut self.error_handler).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/implicit.c b/src/parse/implicit.c new file mode 100644 index 00000000..ce78cae0 --- /dev/null +++ b/src/parse/implicit.c @@ -0,0 +1 @@ +int f((())); From c869bb952e87088673c6bee0638c8bea65ce7f24 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Wed, 11 Mar 2020 23:53:22 -0400 Subject: [PATCH 2/4] Make target configurable It still does nothing even if you configure it lol --- src/analyze/mod.rs | 4 ++-- src/data/mod.rs | 1 + src/ir/mod.rs | 7 ++++--- src/lex/cpp.rs | 5 +++-- src/lib.rs | 29 ++++++++++++++++++++++++++--- src/main.rs | 5 +++++ src/parse/mod.rs | 4 ++-- 7 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/analyze/mod.rs b/src/analyze/mod.rs index 86a7ce37..9449aee2 100644 --- a/src/analyze/mod.rs +++ b/src/analyze/mod.rs @@ -86,7 +86,7 @@ impl Iterator for Analyzer { } impl Analyzer { - pub fn new(parser: Parser) -> Self { + pub fn new(parser: Parser, target: Triple) -> Self { Self { declarations: parser, error_handler: ErrorHandler::new(), @@ -95,7 +95,7 @@ impl Analyzer { pending: VecDeque::new(), initialized: HashSet::new(), // TODO: allow cross-compilation - target: Triple::host(), + target, decl_side_channel: Vec::new(), } } diff --git a/src/data/mod.rs b/src/data/mod.rs index 7c03708b..5fb4915c 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -89,6 +89,7 @@ impl TryFrom for Radix { mod tests { use crate::analyze::test::analyze; use crate::Parser; + use target_lexicon::Triple; #[test] fn type_display() { diff --git a/src/ir/mod.rs b/src/ir/mod.rs index aff54199..60dd76e1 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -101,11 +101,12 @@ struct Compiler { pub(crate) fn compile( module: Module, program: Vec>, + target: Triple, debug: bool, ) -> (Result, CompileError>, VecDeque) { // really we'd like to have all errors but that requires a refactor let mut err = None; - let mut compiler = Compiler::new(module, debug); + let mut compiler = Compiler::new(module, dbg!(target), debug); for decl in program { let meta = decl.data.symbol.get(); let current = match &meta.ctype { @@ -138,7 +139,7 @@ pub(crate) fn compile( } impl Compiler { - fn new(module: Module, debug: bool) -> Compiler { + fn new(module: Module, target: Triple, debug: bool) -> Compiler { Compiler { module, declarations: HashMap::new(), @@ -148,7 +149,7 @@ impl Compiler { // the initial value doesn't really matter last_saw_loop: true, strings: Default::default(), - target: Triple::host(), + target, error_handler: Default::default(), debug, } diff --git a/src/lex/cpp.rs b/src/lex/cpp.rs index 41cddd1d..f094e697 100644 --- a/src/lex/cpp.rs +++ b/src/lex/cpp.rs @@ -7,6 +7,7 @@ use std::path::{Path, PathBuf}; use std::rc::Rc; use codespan::FileId; +use target_lexicon::Triple; use super::{Lexer, Token}; use crate::arch::TARGET; @@ -736,7 +737,7 @@ impl<'a> PreProcessor<'a> { fn boolean_expr(&mut self) -> Result { // TODO: is this unwrap safe? there should only be scalar types in a cpp directive... // TODO: should this use the target arch or the host arch? - let target = target_lexicon::Triple::host(); + let target = target_lexicon::HOST; match self.cpp_expr()?.truthy(&mut self.error_handler).constexpr(&target)?.data { (Literal::Int(i), Type::Bool) => Ok(i != 0), _ => unreachable!("bug in const_fold or parser: cpp cond should be boolean"), @@ -873,7 +874,7 @@ impl<'a> PreProcessor<'a> { // TODO: catch expressions that aren't allowed // (see https://github.com/jyn514/rcc/issues/5#issuecomment-575339427) // TODO: can semantic errors happen here? should we check? - Ok(Analyzer::new(parser).parse_expr(expr)) + Ok(Analyzer::new(parser, Triple::host()).parse_expr(expr)) } /// We saw an `#if`, `#ifdef`, or `#ifndef` token at the start of the line /// and want to either take the branch or ignore the tokens within the directive. diff --git a/src/lib.rs b/src/lib.rs index dc95e22f..97b3de76 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,7 @@ use cranelift_module::{Backend, Module}; #[cfg(feature = "codegen")] pub use ir::initialize_aot_module; +use target_lexicon::Triple; /// The `Source` type for `codespan::Files`. /// @@ -93,7 +94,7 @@ impl From> for Error { } } -#[derive(Debug, Default)] +#[derive(Debug)] pub struct Opt { /// If set, print all tokens found by the lexer in addition to compiling. pub debug_lex: bool, @@ -117,6 +118,28 @@ pub struct Opt { /// The directories to consider as part of the search path. pub search_path: Vec, + + /// The target triple to compile to. + /// + /// Defaults to the host target. + pub target: Triple, +} + +// We can't derive(Default) for Opt because Triple::default() is Triple::Unknown :( +impl Default for Opt { + fn default() -> Self { + Opt { + debug_lex: false, + debug_ast: false, + debug_asm: false, + no_link: false, + #[cfg(feature = "jit")] + jit: false, + max_errors: None, + search_path: Vec::new(), + target: Triple::host(), + } + } } /// Preprocess the source and return the tokens. @@ -189,7 +212,7 @@ pub fn check_semantics( }; let mut hir = vec![]; - let mut parser = Analyzer::new(Parser::new(first, &mut cpp, opt.debug_ast)); + let mut parser = Analyzer::new(Parser::new(first, &mut cpp, opt.debug_ast), opt.target.clone()); for res in &mut parser { match res { Ok(decl) => hir.push(decl), @@ -219,7 +242,7 @@ pub fn compile( (Err(errs), warnings) => return (Err(Error::Source(errs)), warnings), (Ok(hir), warnings) => (hir, warnings), }; - let (result, ir_warnings) = ir::compile(module, hir, opt.debug_asm); + let (result, ir_warnings) = ir::compile(module, hir, opt.target.clone(), opt.debug_asm); warnings.extend(ir_warnings); (result.map_err(Error::from), warnings) } diff --git a/src/main.rs b/src/main.rs index d108a1ff..de46e606 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,7 @@ use rcc::{ link, preprocess, Error, Files, Opt, }; use std::ffi::OsStr; +use target_lexicon::Triple; use tempfile::NamedTempFile; static ERRORS: AtomicUsize = AtomicUsize::new(0); @@ -55,6 +56,7 @@ OPTIONS: --max-errors The maximum number of errors to allow before giving up. Use 0 to allow unlimited errors. [default: 10] -I, --include Add a directory to the local include path (`#include \"file.h\"`) + --target The target platform to compile to. Allows cross-compilation. ARGS: The file to read C source from. \"-\" means stdin (use ./- to read a file called '-'). @@ -263,6 +265,9 @@ fn parse_args() -> Result<(BinOpt, PathBuf), pico_args::Error> { debug_asm: input.contains("--debug-asm"), debug_ast: input.contains(["-a", "--debug-ast"]), no_link: input.contains(["-c", "--no-link"]), + target: dbg!(input + .opt_value_from_str("--target")? + .unwrap_or_else(Triple::host)), #[cfg(feature = "jit")] jit: input.contains("--jit"), max_errors, diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 326aeba1..78407181 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -403,7 +403,7 @@ pub(crate) mod test { first in any::(), tokens in arb_vec_result_locatable_token() ) { - let mut parser = Parser::new(Locatable { data: first, location: Location::default() }, tokens.into_iter(), false); + let mut parser = Parser::new(Locatable { data: first, location: Location::default() }, tokens.into_iter(), Triple::host(), false); let peek = parser.peek_token().cloned(); let next = parser.next_token().map(|l| l.data); @@ -416,7 +416,7 @@ pub(crate) mod test { first in any::(), tokens in arb_vec_result_locatable_token() ) { - let mut parser = Parser::new(Locatable { data: first, location: Location::default() }, tokens.into_iter(), false); + let mut parser = Parser::new(Locatable { data: first, location: Location::default() }, tokens.into_iter(), Triple::host(), false); let peek = parser.peek_next_token().cloned(); parser.next_token(); From 5e3289d8931f14a0f51a9b5fed907bb01ebe2c51 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 1 May 2020 22:19:23 -0400 Subject: [PATCH 3/4] Fix tests --- src/analyze/expr.rs | 2 +- src/analyze/mod.rs | 40 ++++++++++++++++++++++------------------ src/data/mod.rs | 1 - src/data/types.rs | 2 +- src/fold.rs | 14 ++++++++++---- src/lex/cpp.rs | 7 ++++++- src/lib.rs | 5 ++++- src/parse/mod.rs | 4 ++-- 8 files changed, 46 insertions(+), 29 deletions(-) diff --git a/src/analyze/expr.rs b/src/analyze/expr.rs index cbb5d215..e16ca1bb 100644 --- a/src/analyze/expr.rs +++ b/src/analyze/expr.rs @@ -911,7 +911,7 @@ impl Analyzer { fn integer_promote(&mut self, expr: Expr) -> Expr { let expr = expr.rval(); let ctype = expr.ctype.clone().integer_promote(&self.target); - expr.implicit_cast(&ctype, &mut self. error_handler) + expr.implicit_cast(&ctype, &mut self.error_handler) } } diff --git a/src/analyze/mod.rs b/src/analyze/mod.rs index 9449aee2..8e05cf48 100644 --- a/src/analyze/mod.rs +++ b/src/analyze/mod.rs @@ -94,7 +94,6 @@ impl Analyzer { tag_scope: Scope::new(), pending: VecDeque::new(), initialized: HashSet::new(), - // TODO: allow cross-compilation target, decl_side_channel: Vec::new(), } @@ -693,10 +692,11 @@ impl Analyzer { for (name, maybe_value) in ast_members { // enum E { A = 5 }; if let Some(value) = maybe_value { - discriminant = Self::const_sint(self.parse_expr(value), &self.target).unwrap_or_else(|err| { - self.error_handler.push_back(err); - std::i64::MIN - }); + discriminant = Self::const_sint(self.parse_expr(value), &self.target) + .unwrap_or_else(|err| { + self.error_handler.push_back(err); + std::i64::MIN + }); } members.push((name, discriminant)); // TODO: this is such a hack @@ -841,10 +841,11 @@ impl Analyzer { Array { of, size } => { // int a[5] let size = if let Some(expr) = size { - let size = Self::const_uint(self.parse_expr(*expr), &self.target).unwrap_or_else(|err| { - self.error_handler.push_back(err); - 1 - }); + let size = Self::const_uint(self.parse_expr(*expr), &self.target) + .unwrap_or_else(|err| { + self.error_handler.push_back(err); + 1 + }); ArrayType::Fixed(size) } else { // int a[] @@ -945,12 +946,14 @@ impl Analyzer { // used for arrays like `int a[BUF_SIZE - 1];` and enums like `enum { A = 1 }` fn const_literal(expr: Expr, target: &Triple) -> CompileResult { let location = expr.location; - expr.const_fold(target)?.into_literal().or_else(|runtime_expr| { - Err(Locatable::new( - SemanticError::NotConstant(runtime_expr).into(), - location, - )) - }) + expr.const_fold(target)? + .into_literal() + .or_else(|runtime_expr| { + Err(Locatable::new( + SemanticError::NotConstant(runtime_expr).into(), + location, + )) + }) } /// Return an unsigned integer that can be evaluated at compile time, or an error otherwise. fn const_uint(expr: Expr, target: &Triple) -> CompileResult { @@ -1308,6 +1311,7 @@ pub(crate) mod test { use crate::data::types::{ArrayType, FunctionType, Type::*}; use crate::lex::PreProcessor; use crate::parse::test::*; + use target_lexicon::HOST; pub(crate) fn analyze<'c, 'input: 'c, P, A, R, S, E>( input: &'input str, @@ -1321,7 +1325,7 @@ pub(crate) mod test { { let mut p = parser(input); let ast = parse_func(&mut p)?; - let mut a = Analyzer::new(p); + let mut a = Analyzer::new(p, HOST); let e = analyze_func(&mut a, ast); if let Some(err) = a.error_handler.pop_front() { return Err(err); @@ -1338,13 +1342,13 @@ pub(crate) mod test { } pub(crate) fn decls(s: &str) -> Vec> { - Analyzer::new(parser(s)) + Analyzer::new(parser(s), HOST) .map(|o| o.map(|l| l.data)) .collect() } pub(crate) fn assert_errs_decls(input: &str, errs: usize, warnings: usize, decls: usize) { - let mut a = Analyzer::new(parser(input)); + let mut a = Analyzer::new(parser(input), HOST); let (mut a_errs, mut a_decls) = (0, 0); for res in &mut a { if res.is_err() { diff --git a/src/data/mod.rs b/src/data/mod.rs index 5fb4915c..7c03708b 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -89,7 +89,6 @@ impl TryFrom for Radix { mod tests { use crate::analyze::test::analyze; use crate::Parser; - use target_lexicon::Triple; #[test] fn type_display() { diff --git a/src/data/types.rs b/src/data/types.rs index fa23b497..b5f22c8a 100644 --- a/src/data/types.rs +++ b/src/data/types.rs @@ -2,9 +2,9 @@ use super::hir::{Metadata, MetadataRef}; use crate::intern::InternedStr; #[cfg(test)] use proptest_derive::Arbitrary; -use target_lexicon::Triple; use std::fmt::{self, Formatter}; pub use struct_ref::{StructRef, StructType}; +use target_lexicon::Triple; mod struct_ref { use std::cell::RefCell; diff --git a/src/fold.rs b/src/fold.rs index d2e6c41a..e9d3ad47 100644 --- a/src/fold.rs +++ b/src/fold.rs @@ -1,5 +1,5 @@ -use crate::data::hir::{BinaryOp, Expr, ExprType}; use crate::arch::Arch; +use crate::data::hir::{BinaryOp, Expr, ExprType}; use crate::data::*; use std::ops::{Add, Div, Mul, Sub}; use target_lexicon::Triple; @@ -373,7 +373,9 @@ fn fold_binary( ) } Xor => left.literal_bin_op(right, &location, fold_int_bin_op!(^), Xor, target), - BitwiseAnd => left.literal_bin_op(right, &location, fold_int_bin_op!(&), BitwiseAnd, target), + BitwiseAnd => { + left.literal_bin_op(right, &location, fold_int_bin_op!(&), BitwiseAnd, target) + } BitwiseOr => left.literal_bin_op(right, &location, fold_int_bin_op!(|), BitwiseOr, target), Shl => shift_left(left, right, parent_type, target, &location), Shr => shift_right(left, right, parent_type, target, &location), @@ -411,7 +413,9 @@ fn fold_binary( Compare(Less) => Ok(fold_compare_op!(left, right, Compare, <, Less, target)), Compare(LessEqual) => Ok(fold_compare_op!(left, right, Compare, <=, LessEqual, target)), Compare(Greater) => Ok(fold_compare_op!(left, right, Compare, >, Greater, target)), - Compare(GreaterEqual) => Ok(fold_compare_op!(left, right, Compare, >=, GreaterEqual, target)), + Compare(GreaterEqual) => { + Ok(fold_compare_op!(left, right, Compare, >=, GreaterEqual, target)) + } Compare(EqualEqual) => Ok(fold_compare_op!(left, right, Compare, ==, EqualEqual, target)), Compare(NotEqual) => Ok(fold_compare_op!(left, right, Compare, !=, NotEqual, target)), } @@ -602,7 +606,9 @@ mod tests { use crate::data::*; fn test_const_fold(s: &str) -> CompileResult { - analyze_expr(s).unwrap().const_fold(&target_lexicon::Triple::host()) + analyze_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/lex/cpp.rs b/src/lex/cpp.rs index f094e697..19ace99c 100644 --- a/src/lex/cpp.rs +++ b/src/lex/cpp.rs @@ -738,7 +738,12 @@ impl<'a> PreProcessor<'a> { // TODO: is this unwrap safe? there should only be scalar types in a cpp directive... // TODO: should this use the target arch or the host arch? let target = target_lexicon::HOST; - match self.cpp_expr()?.truthy(&mut self.error_handler).constexpr(&target)?.data { + match self + .cpp_expr()? + .truthy(&mut self.error_handler) + .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/lib.rs b/src/lib.rs index 97b3de76..e9b24bc2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -212,7 +212,10 @@ pub fn check_semantics( }; let mut hir = vec![]; - let mut parser = Analyzer::new(Parser::new(first, &mut cpp, opt.debug_ast), opt.target.clone()); + let mut parser = Analyzer::new( + Parser::new(first, &mut cpp, opt.debug_ast), + opt.target.clone(), + ); for res in &mut parser { match res { Ok(decl) => hir.push(decl), diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 78407181..326aeba1 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -403,7 +403,7 @@ pub(crate) mod test { first in any::(), tokens in arb_vec_result_locatable_token() ) { - let mut parser = Parser::new(Locatable { data: first, location: Location::default() }, tokens.into_iter(), Triple::host(), false); + let mut parser = Parser::new(Locatable { data: first, location: Location::default() }, tokens.into_iter(), false); let peek = parser.peek_token().cloned(); let next = parser.next_token().map(|l| l.data); @@ -416,7 +416,7 @@ pub(crate) mod test { first in any::(), tokens in arb_vec_result_locatable_token() ) { - let mut parser = Parser::new(Locatable { data: first, location: Location::default() }, tokens.into_iter(), Triple::host(), false); + let mut parser = Parser::new(Locatable { data: first, location: Location::default() }, tokens.into_iter(), false); let peek = parser.peek_next_token().cloned(); parser.next_token(); From 97a283a901f5d3e0cab3031f21daa5990867aa15 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 1 May 2020 22:58:27 -0400 Subject: [PATCH 4/4] Fix more tests and incorrect targets --- src/arch/mod.rs | 9 --------- src/data/lex.rs | 4 +++- src/ir/mod.rs | 23 +++++++++-------------- src/ir/static_init.rs | 3 +-- src/lex/cpp.rs | 21 +++++++++++++++------ src/lib.rs | 11 ++++++++--- src/main.rs | 2 +- tests/utils/mod.rs | 3 ++- 8 files changed, 39 insertions(+), 37 deletions(-) diff --git a/src/arch/mod.rs b/src/arch/mod.rs index 983924ec..b382ba9e 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -16,15 +16,6 @@ use Type::*; /// http://port70.net/~nsz/c/c11/n1570.html#6.5.3.5 const CHAR_SIZE: u16 = 1; -// TODO: allow this to be configured at runtime -/// The target triple for the host. -/// -/// A "target triple" is used to represent information about a compiler target. -/// Traditionaly, the target triple uses this format: `--` -/// The target triple is represented as a struct and contains additional -/// information like ABI and endianness. -pub(crate) const TARGET: Triple = Triple::host(); - pub(crate) trait Arch { fn size_max(&self) -> u64; diff --git a/src/data/lex.rs b/src/data/lex.rs index f8de4392..1e42446c 100644 --- a/src/data/lex.rs +++ b/src/data/lex.rs @@ -427,9 +427,11 @@ pub(crate) mod test { } /// Create a new preprocessor with `s` as the input, but without a trailing newline pub(crate) fn cpp_no_newline(s: &str) -> PreProcessor { + use target_lexicon::HOST; + let mut files: Files = Default::default(); let id = files.add("", String::new().into()); - PreProcessor::new(id, s, false, vec![], Box::leak(Box::new(files))) + PreProcessor::new(id, s, false, vec![], Box::leak(Box::new(files)), &HOST) } #[test] diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 60dd76e1..0f6f81d3 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -2,7 +2,7 @@ mod expr; mod static_init; mod stmt; -use crate::arch::{Arch, TARGET}; +use crate::arch::Arch; use std::collections::{HashMap, VecDeque}; use std::convert::TryFrom; @@ -24,7 +24,6 @@ use cranelift::frontend::Switch; use cranelift::prelude::{Block, FunctionBuilder, FunctionBuilderContext}; use cranelift_module::{self, Backend, DataId, FuncId, Linkage, Module}; use cranelift_object::{ObjectBackend, ObjectBuilder}; -use lazy_static::lazy_static; use target_lexicon::Triple; use crate::data::{ @@ -32,14 +31,8 @@ use crate::data::{ types::FunctionType, StorageClass, *, }; -// TODO: make this const when const_if_match is stabilized -// TODO: see https://github.com/rust-lang/rust/issues/49146 -lazy_static! { - /// The calling convention for the current target. - pub(crate) static ref CALLING_CONVENTION: CallConv = CallConv::triple_default(&TARGET); -} -pub(crate) fn get_isa(jit: bool) -> Box { +pub(crate) fn get_isa(jit: bool, target: Triple) -> Box { let mut flags_builder = cranelift::codegen::settings::builder(); // `simplejit` requires non-PIC code if !jit { @@ -61,14 +54,14 @@ pub(crate) fn get_isa(jit: bool) -> Box { .set("enable_probestack", "false") .expect("enable_probestack should be a valid option"); let flags = Flags::new(flags_builder); - cranelift::codegen::isa::lookup(TARGET) - .unwrap_or_else(|_| panic!("platform not supported: {}", TARGET)) + cranelift::codegen::isa::lookup(target) + .unwrap_or_else(|e| panic!("platform not supported: {}", e)) .finish(flags) } -pub fn initialize_aot_module(name: String) -> Module { +pub fn initialize_aot_module(name: String, target: Triple) -> Module { Module::new(ObjectBuilder::new( - get_isa(false), + get_isa(false, target), name, cranelift_module::default_libcall_names(), )) @@ -422,8 +415,10 @@ impl FunctionType { } else { vec![AbiParam::new(self.return_type.as_ir_type(target))] }; + // The calling convention for the current target. + let call_conv = CallConv::triple_default(target); Signature { - call_conv: *CALLING_CONVENTION, + call_conv, params, returns: return_type, } diff --git a/src/ir/static_init.rs b/src/ir/static_init.rs index 39e263a6..382272ab 100644 --- a/src/ir/static_init.rs +++ b/src/ir/static_init.rs @@ -8,7 +8,6 @@ use cranelift_module::{Backend, DataContext, DataId, Linkage}; use target_lexicon::Triple; use super::{Compiler, Id}; -use crate::arch::TARGET; use crate::data::*; use crate::data::{ hir::{Expr, ExprType, Initializer, MetadataRef}, @@ -369,7 +368,7 @@ impl Literal { error_handler: &mut ErrorHandler, ) -> CompileResult> { let ir_type = ctype.as_ir_type(target); - let big_endian = TARGET + let big_endian = target .endianness() .expect("target should be big or little endian") == target_lexicon::Endianness::Big; diff --git a/src/lex/cpp.rs b/src/lex/cpp.rs index 19ace99c..2440c0b4 100644 --- a/src/lex/cpp.rs +++ b/src/lex/cpp.rs @@ -7,10 +7,9 @@ use std::path::{Path, PathBuf}; use std::rc::Rc; use codespan::FileId; -use target_lexicon::Triple; +use target_lexicon::{Triple, HOST}; use super::{Lexer, Token}; -use crate::arch::TARGET; use crate::data::error::CppError; use crate::data::lex::{Keyword, Literal}; use crate::data::*; @@ -43,6 +42,8 @@ pub struct PreProcessorBuilder<'a> { debug: bool, /// The paths to search for `#include`d files search_path: Vec>, + /// The target to compile and set `#define`s for + target: Option<&'a Triple>, } impl<'a> PreProcessorBuilder<'a> { @@ -57,6 +58,7 @@ impl<'a> PreProcessorBuilder<'a> { file, buf: buf.into(), search_path: Vec::new(), + target: None, } } pub fn debug(mut self, yes: bool) -> Self { @@ -67,6 +69,10 @@ impl<'a> PreProcessorBuilder<'a> { self.search_path.push(path.into()); self } + pub fn target(mut self, target: &'a Triple) -> Self { + self.target = Some(target); + self + } pub fn build(self) -> PreProcessor<'a> { PreProcessor::new( self.file, @@ -74,6 +80,7 @@ impl<'a> PreProcessorBuilder<'a> { self.debug, self.search_path, self.files, + &self.target.unwrap_or(&HOST), ) } } @@ -98,12 +105,13 @@ impl<'a> PreProcessorBuilder<'a> { /// /// ``` /// use rcc::{Files, PreProcessor, Source}; +/// use target_lexicon::HOST; /// /// let mut files = Files::new(); /// let code = String::from("int main(void) { char *hello = \"hi\"; }\n").into(); /// let src = Source { path: "example.c".into(), code: std::rc::Rc::clone(&code) }; /// let file = files.add("example.c", src); -/// let cpp = PreProcessor::new(file, code, false, vec![], &mut files); +/// let cpp = PreProcessor::new(file, code, false, vec![], &mut files, &HOST); /// for token in cpp { /// assert!(token.is_ok()); /// } @@ -339,10 +347,11 @@ impl<'a> PreProcessor<'a> { debug: bool, user_search_path: I, files: &'files mut Files, + target: &Triple, ) -> Self { let system_path = format!( "{}-{}-{}", - TARGET.architecture, TARGET.operating_system, TARGET.environment + target.architecture, target.operating_system, target.environment ); let int = |i| Definition::Object(vec![Token::Literal(Literal::Int(i))]); let mut search_path = vec![ @@ -357,8 +366,8 @@ impl<'a> PreProcessor<'a> { first_lexer: Lexer::new(file, chars, debug), includes: Default::default(), definitions: map! { - format!("__{}__", TARGET.architecture).into() => int(1), - format!("__{}__", TARGET.operating_system).into() => int(1), + format!("__{}__", target.architecture).into() => int(1), + format!("__{}__", target.operating_system).into() => int(1), "__STDC__".into() => int(1), "__STDC_HOSTED__".into() => int(1), "__STDC_VERSION__".into() => int(2011_12), diff --git a/src/lib.rs b/src/lib.rs index e9b24bc2..cb69e402 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -150,7 +150,7 @@ pub fn preprocess( files: &mut Files, ) -> WarningResult>> { let path = opt.search_path.iter().map(|p| p.into()); - let mut cpp = PreProcessor::new(file, buf, opt.debug_lex, path, files); + let mut cpp = PreProcessor::new(file, buf, opt.debug_lex, path, files, &opt.target); let mut tokens = VecDeque::new(); let mut errs = VecDeque::new(); @@ -176,7 +176,7 @@ pub fn check_semantics( files: &mut Files, ) -> WarningResult>> { let path = opt.search_path.iter().map(|p| p.into()); - let mut cpp = PreProcessor::new(file, buf, opt.debug_lex, path, files); + let mut cpp = PreProcessor::new(file, buf, opt.debug_lex, path, files, &opt.target); let mut errs = VecDeque::new(); macro_rules! handle_err { @@ -293,10 +293,15 @@ mod jit { use crate::ir::get_isa; use cranelift_simplejit::{SimpleJITBackend, SimpleJITBuilder}; use std::convert::TryFrom; + use target_lexicon::HOST; pub fn initialize_jit_module() -> Module { let libcall_names = cranelift_module::default_libcall_names(); - Module::new(SimpleJITBuilder::with_isa(get_isa(true), libcall_names)) + Module::new(SimpleJITBuilder::with_isa( + // it doesn't make sense to cross compile for a JIT + get_isa(true, HOST), + libcall_names, + )) } /// Structure used to handle compiling C code to memory instead of to disk. diff --git a/src/main.rs b/src/main.rs index de46e606..7c6aa8a9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -132,7 +132,7 @@ fn aot_main( file_db: &mut Files, output: &Path, ) -> Result<(), Error> { - let module = rcc::initialize_aot_module("rccmain".to_owned()); + let module = rcc::initialize_aot_module("rccmain".to_owned(), opt.target.clone()); let (result, warnings) = compile(module, buf, opt, file_id, file_db); handle_warnings(warnings, file_db); diff --git a/tests/utils/mod.rs b/tests/utils/mod.rs index 047cd7b6..8e2a07ba 100644 --- a/tests/utils/mod.rs +++ b/tests/utils/mod.rs @@ -10,6 +10,7 @@ extern crate tempfile; use log::info; use rcc::Error; +use target_lexicon::HOST; pub fn init() { env_logger::builder().is_test(true).init(); @@ -49,7 +50,7 @@ pub fn compile(program: &str, path: PathBuf, no_link: bool) -> Result", source); - let module = rcc::initialize_aot_module(program.to_owned()); + let module = rcc::initialize_aot_module(program.to_owned(), HOST); let (result, _warnings) = rcc::compile(module, program, &opts, id, &mut files); let module = result?.finish(); let output = tempfile::NamedTempFile::new()