about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorEduard Burtescu <edy.burt@gmail.com>2016-04-18 16:03:16 +0300
committerEduard Burtescu <edy.burt@gmail.com>2016-04-19 16:08:45 +0300
commitefd0ea5b20a72a0967f80ea3a63f6dc4d1434ce0 (patch)
tree18d1dadcfde43f39a2b108b0416d93d99a8a1077 /src
parent0776399eac62f8cf5cb02761014e8ed6c51204aa (diff)
downloadrust-efd0ea5b20a72a0967f80ea3a63f6dc4d1434ce0.tar.gz
rust-efd0ea5b20a72a0967f80ea3a63f6dc4d1434ce0.zip
Parse data-layout specifications.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/ty/context.rs6
-rw-r--r--src/librustc/ty/layout.rs248
-rw-r--r--src/librustc/ty/mod.rs1
3 files changed, 255 insertions, 0 deletions
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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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::<u64>().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::<Vec<_>>()[..] {
+                ["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::<u64>() {
+                        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, String> {
+        Align::from_bytes((abi + 7) / 8, (pref + 7) / 8)
+    }
+
+    pub fn from_bytes(abi: u64, pref: u64) -> Result<Align, String> {
+        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;