From efd0ea5b20a72a0967f80ea3a63f6dc4d1434ce0 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Mon, 18 Apr 2016 16:03:16 +0300 Subject: [PATCH] Parse data-layout specifications. --- src/librustc/ty/context.rs | 6 + src/librustc/ty/layout.rs | 248 +++++++++++++++++++++++++++++++++++++ src/librustc/ty/mod.rs | 1 + 3 files changed, 255 insertions(+) create mode 100644 src/librustc/ty/layout.rs diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 61c49475ac4..a6d05cf0b24 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -31,6 +31,7 @@ use hir::FreevarMap; use ty::{BareFnTy, InferTy, ParamTy, ProjectionTy, TraitTy}; use ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid}; use ty::TypeVariants::*; +use ty::layout::TargetDataLayout; use ty::maps; use util::common::MemoizationMap; use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet}; @@ -419,6 +420,9 @@ pub struct TyCtxt<'tcx> { /// The definite name of the current crate after taking into account /// attributes, commandline parameters, etc. pub crate_name: token::InternedString, + + /// Data layout specification for the current target. + pub data_layout: TargetDataLayout, } impl<'tcx> TyCtxt<'tcx> { @@ -531,6 +535,7 @@ impl<'tcx> TyCtxt<'tcx> { f: F) -> R where F: FnOnce(&TyCtxt<'tcx>) -> R { + let data_layout = TargetDataLayout::parse(s); let interner = RefCell::new(FnvHashMap()); let common_types = CommonTypes::new(&arenas.type_, &interner); let dep_graph = map.dep_graph.clone(); @@ -589,6 +594,7 @@ impl<'tcx> TyCtxt<'tcx> { cast_kinds: RefCell::new(NodeMap()), fragment_infos: RefCell::new(DefIdMap()), crate_name: token::intern_and_get_ident(crate_name), + data_layout: data_layout, }, f) } } diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs new file mode 100644 index 00000000000..8c1078fbbe2 --- /dev/null +++ b/src/librustc/ty/layout.rs @@ -0,0 +1,248 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use session::Session; + +use std::cmp; + +/// Parsed [Data layout](http://llvm.org/docs/LangRef.html#data-layout) +/// for a target, which contains everything needed to compute layouts. +pub struct TargetDataLayout { + pub endian: Endian, + pub i1_align: Align, + pub i8_align: Align, + pub i16_align: Align, + pub i32_align: Align, + pub i64_align: Align, + pub f32_align: Align, + pub f64_align: Align, + pub pointer_size: Size, + pub pointer_align: Align, + pub aggregate_align: Align, + + /// Alignments for vector types, sorted by size. + pub vector_align: Vec<(Size, Align)> +} + +impl Default for TargetDataLayout { + fn default() -> TargetDataLayout { + TargetDataLayout { + endian: Endian::Big, + i1_align: Align::from_bits(8, 8).unwrap(), + i8_align: Align::from_bits(8, 8).unwrap(), + i16_align: Align::from_bits(16, 16).unwrap(), + i32_align: Align::from_bits(32, 32).unwrap(), + i64_align: Align::from_bits(32, 64).unwrap(), + f32_align: Align::from_bits(32, 32).unwrap(), + f64_align: Align::from_bits(64, 64).unwrap(), + pointer_size: Size::from_bits(64), + pointer_align: Align::from_bits(64, 64).unwrap(), + aggregate_align: Align::from_bits(0, 64).unwrap(), + vector_align: vec![(Size::from_bits(128), + Align::from_bits(128, 128).unwrap())] + } + } +} + +impl TargetDataLayout { + pub fn parse(sess: &Session) -> TargetDataLayout { + // Parse a bit count from a string. + let parse_bits = |s: &str, kind: &str, cause: &str| { + s.parse::().unwrap_or_else(|err| { + sess.err(&format!("invalid {} `{}` for `{}` in \"data-layout\": {}", + kind, s, cause, err)); + 0 + }) + }; + + // Parse a size string. + let size = |s: &str, cause: &str| { + Size::from_bits(parse_bits(s, "size", cause)) + }; + + // Parse an alignment string. + let align = |s: &[&str], cause: &str| { + if s.is_empty() { + sess.err(&format!("missing alignment for `{}` in \"data-layout\"", cause)); + } + let abi = parse_bits(s[0], "alignment", cause); + let pref = s.get(1).map_or(abi, |pref| parse_bits(pref, "alignment", cause)); + Align::from_bits(abi, pref).unwrap_or_else(|err| { + sess.err(&format!("invalid alignment for `{}` in \"data-layout\": {}", + cause, err)); + Align::from_bits(8, 8).unwrap() + }) + }; + + let mut dl = TargetDataLayout::default(); + for spec in sess.target.target.data_layout.split("-") { + match &spec.split(":").collect::>()[..] { + ["e"] => dl.endian = Endian::Little, + ["E"] => dl.endian = Endian::Big, + ["a", a..] => dl.aggregate_align = align(a, "a"), + ["f32", a..] => dl.f32_align = align(a, "f32"), + ["f64", a..] => dl.f64_align = align(a, "f64"), + [p @ "p", s, a..] | [p @ "p0", s, a..] => { + dl.pointer_size = size(s, p); + dl.pointer_align = align(a, p); + } + [s, a..] if s.starts_with("i") => { + let ty_align = match s[1..].parse::() { + Ok(1) => &mut dl.i8_align, + Ok(8) => &mut dl.i8_align, + Ok(16) => &mut dl.i16_align, + Ok(32) => &mut dl.i32_align, + Ok(64) => &mut dl.i64_align, + Ok(_) => continue, + Err(_) => { + size(&s[1..], "i"); // For the user error. + continue; + } + }; + *ty_align = align(a, s); + } + [s, a..] if s.starts_with("v") => { + let v_size = size(&s[1..], "v"); + let a = align(a, s); + if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) { + v.1 = a; + continue; + } + // No existing entry, add a new one. + dl.vector_align.push((v_size, a)); + } + _ => {} // Ignore everything else. + } + } + + // Sort vector alignments by size. + dl.vector_align.sort_by_key(|&(s, _)| s); + + // Perform consistency checks against the Target information. + let endian_str = match dl.endian { + Endian::Little => "little", + Endian::Big => "big" + }; + if endian_str != sess.target.target.target_endian { + sess.err(&format!("inconsistent target specification: \"data-layout\" claims \ + architecture is {}-endian, while \"target-endian\" is `{}`", + endian_str, sess.target.target.target_endian)); + } + + if dl.pointer_size.bits().to_string() != sess.target.target.target_pointer_width { + sess.err(&format!("inconsistent target specification: \"data-layout\" claims \ + pointers are {}-bit, while \"target-pointer-width\" is `{}`", + dl.pointer_size.bits(), sess.target.target.target_pointer_width)); + } + + dl + } +} + +/// Endianness of the target, which must match cfg(target-endian). +#[derive(Copy, Clone)] +pub enum Endian { + Little, + Big +} + +/// Size of a type in bytes. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Size { + raw: u64 +} + +impl Size { + pub fn from_bits(bits: u64) -> Size { + Size::from_bytes((bits + 7) / 8) + } + + pub fn from_bytes(bytes: u64) -> Size { + if bytes >= (1 << 61) { + bug!("Size::from_bytes: {} bytes in bits doesn't fit in u64", bytes) + } + Size { + raw: bytes + } + } + + pub fn bytes(self) -> u64 { + self.raw + } + + pub fn bits(self) -> u64 { + self.bytes() * 8 + } +} + +/// Alignment of a type in bytes, both ABI-mandated and preferred. +/// Since alignments are always powers of 2, we can pack both in one byte, +/// giving each a nibble (4 bits) for a maximum alignment of 2^15 = 32768. +#[derive(Copy, Clone)] +pub struct Align { + raw: u8 +} + +impl Align { + pub fn from_bits(abi: u64, pref: u64) -> Result { + Align::from_bytes((abi + 7) / 8, (pref + 7) / 8) + } + + pub fn from_bytes(abi: u64, pref: u64) -> Result { + let pack = |align: u64| { + // Treat an alignment of 0 bytes like 1-byte alignment. + if align == 0 { + return Ok(0); + } + + let mut bytes = align; + let mut pow: u8 = 0; + while (bytes & 1) == 0 { + pow += 1; + bytes >>= 1; + } + if bytes != 1 { + Err(format!("`{}` is not a power of 2", align)) + } else if pow > 0x0f { + Err(format!("`{}` is too large", align)) + } else { + Ok(pow) + } + }; + + Ok(Align { + raw: pack(abi)? | (pack(pref)? << 4) + }) + } + + pub fn abi(self) -> u64 { + 1 << (self.raw & 0xf) + } + + pub fn pref(self) -> u64 { + 1 << (self.raw >> 4) + } + + pub fn min(self, other: Align) -> Align { + let abi = cmp::min(self.raw & 0x0f, other.raw & 0x0f); + let pref = cmp::min(self.raw & 0xf0, other.raw & 0xf0); + Align { + raw: abi | pref + } + } + + pub fn max(self, other: Align) -> Align { + let abi = cmp::max(self.raw & 0x0f, other.raw & 0x0f); + let pref = cmp::max(self.raw & 0xf0, other.raw & 0xf0); + Align { + raw: abi | pref + } + } +} diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index dda5f699bae..ec117f998cd 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -84,6 +84,7 @@ pub mod error; pub mod fast_reject; pub mod fold; pub mod item_path; +pub mod layout; pub mod _match; pub mod maps; pub mod outlives;