1
Fork 0

rustc_trans: compute LLVM types from type layouts, not Rust types.

This commit is contained in:
Eduard-Mihai Burtescu 2017-09-19 23:43:55 +03:00
parent fad99542c8
commit d0ab6e8644
7 changed files with 190 additions and 386 deletions

View file

@ -1541,10 +1541,10 @@ impl<'a, 'tcx> Layout {
discr_range: (min as u64)..=(max as u64),
variants
},
fields: FieldPlacement::Arbitrary {
offsets: vec![Size::from_bytes(0)],
memory_index: vec![0]
},
// FIXME(eddyb): using `FieldPlacement::Arbitrary` here results
// in lost optimizations, specifically around allocations, see
// `test/codegen/{alloc-optimisation,vec-optimizes-away}.rs`.
fields: FieldPlacement::Union(1),
abi: if discr.size(dl) == size {
Abi::Scalar(discr)
} else {

View file

@ -1,196 +0,0 @@
// 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);
for i in layout.fields.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
}

View file

@ -104,7 +104,6 @@ pub mod back {
}
mod abi;
mod adt;
mod allocator;
mod asm;
mod assert_module_sources;

View file

@ -77,7 +77,7 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
}
// Not in the cache. Build it.
let nullptr = C_null(Type::nil(ccx).ptr_to());
let nullptr = C_null(Type::i8p(ccx));
let (size, align) = ccx.size_and_align_of(ty);
let mut components: Vec<_> = [

View file

@ -10,17 +10,16 @@
use llvm::{self, ValueRef};
use rustc::ty::{self, Ty, TypeFoldable};
use rustc::ty::layout::{self, Align, FullLayout, Layout, LayoutOf};
use rustc::ty::layout::{self, Align, FullLayout, LayoutOf};
use rustc::mir;
use rustc::mir::tcx::LvalueTy;
use rustc_data_structures::indexed_vec::Idx;
use abi;
use adt;
use base;
use builder::Builder;
use common::{self, CrateContext, C_usize, C_u8, C_u32, C_uint, C_int, C_null, val_ty};
use consts;
use type_of::LayoutLlvmExt;
use type_of::{self, LayoutLlvmExt};
use type_::Type;
use value::Value;
use glue;
@ -206,52 +205,26 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
let alignment = self.alignment | Alignment::from(l);
// Unions and newtypes only use an offset of 0.
match *l.layout {
// FIXME(eddyb) The fields of a fat pointer aren't correct, especially
// to unsized structs, we can't represent their pointee types in `Ty`.
Layout::FatPointer { .. } => {}
_ if offset == 0 => {
let ty = ccx.llvm_type_of(field.ty);
return LvalueRef {
llval: bcx.pointercast(self.llval, ty.ptr_to()),
llextra: if field.is_unsized() {
self.llextra
} else {
ptr::null_mut()
},
ty: LvalueTy::from_ty(field.ty),
alignment,
};
let has_llvm_fields = match *l.fields {
layout::FieldPlacement::Union(_) => false,
layout::FieldPlacement::Array { .. } => true,
layout::FieldPlacement::Arbitrary { .. } => {
match l.abi {
layout::Abi::Scalar(_) | layout::Abi::Vector { .. } => false,
layout::Abi::Aggregate { .. } => true
}
}
_ => {}
}
// Discriminant field of enums.
if let layout::NullablePointer { .. } = *l.layout {
let ty = ccx.llvm_type_of(field.ty);
let size = field.size(ccx).bytes();
// If the discriminant is not on a multiple of the primitive's size,
// we need to go through i8*. Also assume the worst alignment.
if offset % size != 0 {
let byte_ptr = bcx.pointercast(self.llval, Type::i8p(ccx));
let byte_ptr = bcx.inbounds_gep(byte_ptr, &[C_usize(ccx, offset)]);
let byte_align = Alignment::Packed(Align::from_bytes(1, 1).unwrap());
return LvalueRef::new_sized(
bcx.pointercast(byte_ptr, ty.ptr_to()), field.ty, byte_align);
}
let discr_ptr = bcx.pointercast(self.llval, ty.ptr_to());
return LvalueRef::new_sized(
bcx.inbounds_gep(discr_ptr, &[C_usize(ccx, offset / size)]),
field.ty, alignment);
}
};
let simple = || {
LvalueRef {
llval: bcx.struct_gep(self.llval, l.llvm_field_index(ix)),
llval: if has_llvm_fields {
bcx.struct_gep(self.llval, l.llvm_field_index(ix))
} else {
assert_eq!(offset, 0);
let ty = ccx.llvm_type_of(field.ty);
bcx.pointercast(self.llval, ty.ptr_to())
},
llextra: if ccx.shared().type_has_metadata(field.ty) {
self.llextra
} else {
@ -460,7 +433,7 @@ impl<'a, 'tcx> LvalueRef<'tcx> {
layout::General { .. } => {
let variant_layout = layout.for_variant(variant_index);
let variant_ty = Type::struct_(bcx.ccx,
&adt::struct_llfields(bcx.ccx, variant_layout),
&type_of::struct_llfields(bcx.ccx, variant_layout),
variant_layout.is_packed());
downcast.llval = bcx.pointercast(downcast.llval, variant_ty.ptr_to());
}

View file

@ -66,10 +66,6 @@ impl Type {
ty!(llvm::LLVMVoidTypeInContext(ccx.llcx()))
}
pub fn nil(ccx: &CrateContext) -> Type {
Type::empty_struct(ccx)
}
pub fn metadata(ccx: &CrateContext) -> Type {
ty!(llvm::LLVMRustMetadataTypeInContext(ccx.llcx()))
}
@ -202,9 +198,6 @@ impl Type {
ty!(llvm::LLVMStructCreateNamed(ccx.llcx(), name.as_ptr()))
}
pub fn empty_struct(ccx: &CrateContext) -> Type {
Type::struct_(ccx, &[], false)
}
pub fn array(ty: &Type, len: u64) -> Type {
ty!(llvm::LLVMRustArrayType(ty.to_ref(), len))

View file

@ -9,10 +9,9 @@
// except according to those terms.
use abi::FnType;
use adt;
use common::*;
use rustc::ty::{self, Ty, TypeFoldable};
use rustc::ty::layout::{self, Align, Layout, LayoutOf, Size, FullLayout};
use rustc::ty::layout::{self, HasDataLayout, Align, LayoutOf, Size, FullLayout};
use trans_item::DefPathBasedNames;
use type_::Type;
@ -43,30 +42,10 @@ pub fn unsized_info_ty<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) ->
}
}
fn compute_llvm_type<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type {
// Check the cache.
if let Some(&llty) = cx.lltypes().borrow().get(&t) {
return llty;
}
debug!("type_of {:?}", t);
assert!(!t.has_escaping_regions(), "{:?} has escaping regions", t);
// Replace any typedef'd types with their equivalent non-typedef
// type. This ensures that all LLVM nominal types that contain
// Rust types are defined as the same LLVM types. If we don't do
// this then, e.g. `Option<{myfield: bool}>` would be a different
// type than `Option<myrec>`.
let t_norm = cx.tcx().erase_regions(&t);
if t != t_norm {
let llty = cx.llvm_type_of(t_norm);
debug!("--> normalized {:?} to {:?} llty={:?}", t, t_norm, llty);
cx.lltypes().borrow_mut().insert(t, llty);
return llty;
}
fn uncached_llvm_type<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
ty: Ty<'tcx>,
defer: &mut Option<(Type, FullLayout<'tcx>)>)
-> Type {
let ptr_ty = |ty: Ty<'tcx>| {
if cx.shared().type_has_metadata(ty) {
if let ty::TyStr = ty.sty {
@ -88,97 +67,130 @@ fn compute_llvm_type<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type
cx.llvm_type_of(ty).ptr_to()
}
};
let mut llty = match t.sty {
ty::TyBool => Type::bool(cx),
ty::TyChar => Type::char(cx),
ty::TyInt(t) => Type::int_from_ty(cx, t),
ty::TyUint(t) => Type::uint_from_ty(cx, t),
ty::TyFloat(t) => Type::float_from_ty(cx, t),
ty::TyNever => Type::nil(cx),
ty::TyClosure(..) => {
// Only create the named struct, but don't fill it in. We
// fill it in *after* placing it into the type cache.
adt::incomplete_type_of(cx, t, "closure")
}
ty::TyGenerator(..) => {
// Only create the named struct, but don't fill it in. We
// fill it in *after* placing it into the type cache.
adt::incomplete_type_of(cx, t, "generator")
}
ty::TyRef(_, ty::TypeAndMut{ty, ..}) |
ty::TyRawPtr(ty::TypeAndMut{ty, ..}) => {
ptr_ty(ty)
}
ty::TyAdt(def, _) if def.is_box() => {
ptr_ty(t.boxed_ty())
}
ty::TyArray(ty, size) => {
let llty = cx.llvm_type_of(ty);
let size = size.val.to_const_int().unwrap().to_u64().unwrap();
Type::array(&llty, size)
}
ty::TySlice(ty) => {
Type::array(&cx.llvm_type_of(ty), 0)
}
ty::TyStr => {
Type::array(&Type::i8(cx), 0)
}
ty::TyDynamic(..) |
ty::TyForeign(..) => adt::type_of(cx, t),
ty::TyFnDef(..) => Type::nil(cx),
ty::TyFnPtr(sig) => {
let sig = cx.tcx().erase_late_bound_regions_and_normalize(&sig);
FnType::new(cx, sig, &[]).llvm_type(cx).ptr_to()
}
ty::TyTuple(ref tys, _) if tys.is_empty() => Type::nil(cx),
ty::TyTuple(..) => {
adt::type_of(cx, t)
}
ty::TyAdt(..) if t.is_simd() => {
let e = t.simd_type(cx.tcx());
if !e.is_machine() {
cx.sess().fatal(&format!("monomorphising SIMD type `{}` with \
a non-machine element type `{}`",
t, e))
}
let llet = cx.llvm_type_of(e);
let n = t.simd_size(cx.tcx()) as u64;
Type::vector(&llet, n)
}
ty::TyAdt(..) => {
// Only create the named struct, but don't fill it in. We
// fill it in *after* placing it into the type cache. This
// avoids creating more than one copy of the enum when one
// of the enum's variants refers to the enum itself.
let name = llvm_type_name(cx, t);
adt::incomplete_type_of(cx, t, &name[..])
}
ty::TyInfer(..) |
ty::TyProjection(..) |
ty::TyParam(..) |
ty::TyAnon(..) |
ty::TyError => bug!("type_of with {:?}", t),
};
debug!("--> mapped t={:?} to llty={:?}", t, llty);
cx.lltypes().borrow_mut().insert(t, llty);
// If this was an enum or struct, fill in the type now.
match t.sty {
ty::TyAdt(..) | ty::TyClosure(..) | ty::TyGenerator(..) if !t.is_simd() && !t.is_box() => {
adt::finish_type_of(cx, t, &mut llty);
match ty.sty {
ty::TyRef(_, ty::TypeAndMut{ty, ..}) |
ty::TyRawPtr(ty::TypeAndMut{ty, ..}) => {
return ptr_ty(ty);
}
_ => ()
ty::TyAdt(def, _) if def.is_box() => {
return ptr_ty(ty.boxed_ty());
}
ty::TyFnPtr(sig) => {
let sig = cx.tcx().erase_late_bound_regions_and_normalize(&sig);
return FnType::new(cx, sig, &[]).llvm_type(cx).ptr_to();
}
_ => {}
}
llty
let layout = cx.layout_of(ty);
if let layout::Abi::Scalar(value) = layout.abi {
let llty = match value {
layout::Int(layout::I1, _) => Type::i8(cx),
layout::Int(i, _) => Type::from_integer(cx, i),
layout::F32 => Type::f32(cx),
layout::F64 => Type::f64(cx),
layout::Pointer => cx.llvm_type_of(layout::Pointer.to_ty(cx.tcx()))
};
return llty;
}
if let layout::Abi::Vector { .. } = layout.abi {
return Type::vector(&cx.llvm_type_of(layout.field(cx, 0).ty),
layout.fields.count() as u64);
}
let name = match ty.sty {
ty::TyClosure(..) | ty::TyGenerator(..) | ty::TyAdt(..) => {
let mut name = String::with_capacity(32);
let printer = DefPathBasedNames::new(cx.tcx(), true, true);
printer.push_type_name(ty, &mut name);
Some(name)
}
_ => None
};
match *layout.fields {
layout::FieldPlacement::Union(_) => {
let size = layout.size(cx).bytes();
let fill = Type::array(&Type::i8(cx), size);
match name {
None => {
Type::struct_(cx, &[fill], layout.is_packed())
}
Some(ref name) => {
let mut llty = Type::named_struct(cx, name);
llty.set_struct_body(&[fill], layout.is_packed());
llty
}
}
}
layout::FieldPlacement::Array { count, .. } => {
Type::array(&cx.llvm_type_of(layout.field(cx, 0).ty), count)
}
layout::FieldPlacement::Arbitrary { .. } => {
match name {
None => {
Type::struct_(cx, &struct_llfields(cx, layout), layout.is_packed())
}
Some(ref name) => {
let llty = Type::named_struct(cx, name);
*defer = Some((llty, layout));
llty
}
}
}
}
}
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);
for i in layout.fields.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
}
impl<'a, 'tcx> CrateContext<'a, 'tcx> {
@ -219,7 +231,38 @@ impl<'a, 'tcx> CrateContext<'a, 'tcx> {
/// of that field's type - this is useful for taking the address of
/// that field and ensuring the struct has the right alignment.
pub fn llvm_type_of(&self, ty: Ty<'tcx>) -> Type {
compute_llvm_type(self, ty)
// Check the cache.
if let Some(&llty) = self.lltypes().borrow().get(&ty) {
return llty;
}
debug!("type_of {:?}", ty);
assert!(!ty.has_escaping_regions(), "{:?} has escaping regions", ty);
// Make sure lifetimes are erased, to avoid generating distinct LLVM
// types for Rust types that only differ in the choice of lifetimes.
let normal_ty = self.tcx().erase_regions(&ty);
if ty != normal_ty {
let llty = self.llvm_type_of(normal_ty);
debug!("--> normalized {:?} to {:?} llty={:?}", ty, normal_ty, llty);
self.lltypes().borrow_mut().insert(ty, llty);
return llty;
}
let mut defer = None;
let llty = uncached_llvm_type(self, ty, &mut defer);
debug!("--> mapped ty={:?} to llty={:?}", ty, llty);
self.lltypes().borrow_mut().insert(ty, llty);
if let Some((mut llty, layout)) = defer {
llty.set_struct_body(&struct_llfields(self, layout), layout.is_packed())
}
llty
}
pub fn immediate_llvm_type_of(&self, ty: Ty<'tcx>) -> Type {
@ -240,26 +283,18 @@ impl<'tcx> LayoutLlvmExt for FullLayout<'tcx> {
if let layout::Abi::Scalar(_) = self.abi {
bug!("FullLayout::llvm_field_index({:?}): not applicable", self);
}
let index = self.fields.memory_index(index);
match *self.layout {
Layout::Vector | Layout::Array => {
match *self.fields {
layout::FieldPlacement::Union(_) => {
bug!("FullLayout::llvm_field_index({:?}): not applicable", self)
}
layout::FieldPlacement::Array { .. } => {
index as u64
}
Layout::FatPointer | Layout::Univariant => {
adt::memory_index_to_gep(index as u64)
}
_ => {
bug!("FullLayout::llvm_field_index({:?}): not applicable", self)
layout::FieldPlacement::Arbitrary { .. } => {
1 + (self.fields.memory_index(index) as u64) * 2
}
}
}
}
fn llvm_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> String {
let mut name = String::with_capacity(32);
let printer = DefPathBasedNames::new(cx.tcx(), true, true);
printer.push_type_name(ty, &mut name);
name
}