| // Copyright 2013 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. |
| |
| //! # Representation of Algebraic Data Types |
| //! |
| //! This module determines how to represent enums, structs, and tuples |
| //! based on their monomorphized types; it is responsible both for |
| //! choosing a representation and translating basic operations on |
| //! values of those types. (Note: exporting the representations for |
| //! debuggers is handled in debuginfo.rs, not here.) |
| //! |
| //! Note that the interface treats everything as a general case of an |
| //! enum, so structs/tuples/etc. have one pseudo-variant with |
| //! discriminant 0; i.e., as if they were a univariant enum. |
| //! |
| //! Having everything in one place will enable improvements to data |
| //! structure representation; possibilities include: |
| //! |
| //! - User-specified alignment (e.g., cacheline-aligning parts of |
| //! concurrently accessed data structures); LLVM can't represent this |
| //! directly, so we'd have to insert padding fields in any structure |
| //! that might contain one and adjust GEP indices accordingly. See |
| //! issue #4578. |
| //! |
| //! - Store nested enums' discriminants in the same word. Rather, if |
| //! some variants start with enums, and those enums representations |
| //! have unused alignment padding between discriminant and body, the |
| //! outer enum's discriminant can be stored there and those variants |
| //! can start at offset 0. Kind of fancy, and might need work to |
| //! make copies of the inner enum type cooperate, but it could help |
| //! with `Option` or `Result` wrapped around another enum. |
| //! |
| //! - Tagged pointers would be neat, but given that any type can be |
| //! used unboxed and any field can have pointers (including mutable) |
| //! taken to it, implementing them for Rust seems difficult. |
| |
| use rustc::ty::{self, Ty}; |
| use rustc::ty::layout::{self, HasDataLayout, LayoutOf, Size, FullLayout}; |
| |
| use context::CrateContext; |
| use type_::Type; |
| |
| /// LLVM-level types are a little complicated. |
| /// |
| /// C-like enums need to be actual ints, not wrapped in a struct, |
| /// because that changes the ABI on some platforms (see issue #10308). |
| /// |
| /// For nominal types, in some cases, we need to use LLVM named structs |
| /// and fill in the actual contents in a second pass to prevent |
| /// unbounded recursion; see also the comments in `trans::type_of`. |
| pub fn type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type { |
| generic_type_of(cx, t, None) |
| } |
| |
| pub fn incomplete_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, |
| t: Ty<'tcx>, name: &str) -> Type { |
| generic_type_of(cx, t, Some(name)) |
| } |
| |
| pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, |
| t: Ty<'tcx>, llty: &mut Type) { |
| let l = cx.layout_of(t); |
| debug!("finish_type_of: {} with layout {:#?}", t, l); |
| if let layout::Abi::Scalar(_) = l.abi { |
| return; |
| } |
| match *l.layout { |
| layout::Univariant(_) => { |
| let is_enum = if let ty::TyAdt(def, _) = t.sty { |
| def.is_enum() |
| } else { |
| false |
| }; |
| let variant_layout = if is_enum { |
| l.for_variant(0) |
| } else { |
| l |
| }; |
| llty.set_struct_body(&struct_llfields(cx, variant_layout), |
| variant_layout.is_packed()) |
| } |
| |
| _ => {} |
| } |
| } |
| |
| fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, |
| t: Ty<'tcx>, |
| name: Option<&str>) -> Type { |
| let l = cx.layout_of(t); |
| debug!("adt::generic_type_of {:#?} name: {:?}", l, name); |
| if let layout::Abi::Scalar(value) = l.abi { |
| return cx.llvm_type_of(value.to_ty(cx.tcx())); |
| } |
| match *l.layout { |
| layout::Univariant(_) => { |
| match name { |
| None => { |
| Type::struct_(cx, &struct_llfields(cx, l), l.is_packed()) |
| } |
| Some(name) => { |
| Type::named_struct(cx, name) |
| } |
| } |
| } |
| _ => { |
| let align = l.align(cx); |
| let abi_align = align.abi(); |
| let elem_ty = if let Some(ity) = layout::Integer::for_abi_align(cx, align) { |
| Type::from_integer(cx, ity) |
| } else { |
| let vec_align = cx.data_layout().vector_align(Size::from_bytes(abi_align)); |
| assert_eq!(vec_align.abi(), abi_align); |
| Type::vector(&Type::i32(cx), abi_align / 4) |
| }; |
| |
| let size = l.size(cx).bytes(); |
| assert_eq!(size % abi_align, 0); |
| let fill = Type::array(&elem_ty, size / abi_align); |
| match name { |
| None => { |
| Type::struct_(cx, &[fill], l.is_packed()) |
| } |
| Some(name) => { |
| let mut llty = Type::named_struct(cx, name); |
| llty.set_struct_body(&[fill], l.is_packed()); |
| llty |
| } |
| } |
| } |
| } |
| } |
| |
| /// Double an index and add 1 to account for padding. |
| pub fn memory_index_to_gep(index: u64) -> u64 { |
| 1 + index * 2 |
| } |
| |
| pub fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, |
| layout: FullLayout<'tcx>) -> Vec<Type> { |
| debug!("struct_llfields: {:#?}", layout); |
| let align = layout.align(cx); |
| let size = layout.size(cx); |
| let field_count = layout.fields.count(); |
| |
| let mut offset = Size::from_bytes(0); |
| let mut result: Vec<Type> = Vec::with_capacity(1 + field_count * 2); |
| let field_index_by_increasing_offset = match *layout.layout { |
| layout::Univariant(ref variant) => variant.field_index_by_increasing_offset(), |
| _ => bug!("unexpected {:#?}", layout) |
| }; |
| for i in field_index_by_increasing_offset { |
| let field = layout.field(cx, i); |
| let target_offset = layout.fields.offset(i as usize); |
| debug!("struct_llfields: {}: {:?} offset: {:?} target_offset: {:?}", |
| i, field, offset, target_offset); |
| assert!(target_offset >= offset); |
| let padding = target_offset - offset; |
| result.push(Type::array(&Type::i8(cx), padding.bytes())); |
| debug!(" padding before: {:?}", padding); |
| |
| let llty = cx.llvm_type_of(field.ty); |
| result.push(llty); |
| |
| if layout.is_packed() { |
| assert_eq!(padding.bytes(), 0); |
| } else { |
| let field_align = field.align(cx); |
| assert!(field_align.abi() <= align.abi(), |
| "non-packed type has field with larger align ({}): {:#?}", |
| field_align.abi(), layout); |
| } |
| |
| offset = target_offset + field.size(cx); |
| } |
| if !layout.is_unsized() && field_count > 0 { |
| if offset > size { |
| bug!("layout: {:#?} stride: {:?} offset: {:?}", |
| layout, size, offset); |
| } |
| let padding = size - offset; |
| debug!("struct_llfields: pad_bytes: {:?} offset: {:?} stride: {:?}", |
| padding, offset, size); |
| result.push(Type::array(&Type::i8(cx), padding.bytes())); |
| assert!(result.len() == 1 + field_count * 2); |
| } else { |
| debug!("struct_llfields: offset: {:?} stride: {:?}", |
| offset, size); |
| } |
| |
| result |
| } |
| |