rustc_trans: generate LLVM pointee types based on alignment.

This commit is contained in:
Eduard-Mihai Burtescu 2017-10-05 02:21:10 +03:00
parent f8d5d0c30c
commit ac60872077
4 changed files with 191 additions and 73 deletions

View File

@ -32,13 +32,11 @@ use cabi_nvptx64;
use cabi_hexagon;
use mir::lvalue::LvalueRef;
use type_::Type;
use type_of::LayoutLlvmExt;
use type_of::{LayoutLlvmExt, PointerKind};
use rustc::hir;
use rustc::ty::{self, Ty};
use rustc::ty::layout::{self, Align, Size, TyLayout};
use rustc::ty::layout::{HasDataLayout, LayoutOf};
use rustc_back::PanicStrategy;
use libc::c_uint;
use std::{cmp, iter};
@ -514,22 +512,6 @@ impl<'a, 'tcx> ArgType<'tcx> {
self.kind = ArgKind::Ignore;
}
fn safe_pointee(&mut self, layout: TyLayout) {
match self.layout.abi {
layout::Abi::Scalar(layout::Scalar {
value: layout::Pointer,
ref valid_range
}) => {
if valid_range.start > 0 {
self.attrs.set(ArgAttribute::NonNull);
}
self.attrs.pointee_size = layout.size;
self.attrs.pointee_align = Some(layout.align);
}
_ => bug!("ArgType::safe_pointee({:#?}): not a pointer", self.layout)
}
}
pub fn extend_integer_width_to(&mut self, bits: u64) {
// Only integers have signedness
if let layout::Abi::Scalar(ref scalar) = self.layout.abi {
@ -754,42 +736,30 @@ impl<'a, 'tcx> FnType<'tcx> {
// Handle safe Rust thin and fat pointers.
let adjust_for_rust_type = |arg: &mut ArgType<'tcx>, is_return: bool| {
// We only handle thin pointers here.
match arg.layout.abi {
layout::Abi::Scalar(layout::Scalar { value: layout::Pointer, .. }) => {}
_ => return
}
let mut ty = arg.layout.ty;
// FIXME(eddyb) detect more nested cases than `Option<&T>` here.
match arg.layout.variants {
layout::Variants::NicheFilling { dataful_variant, .. } => {
let variant = arg.layout.for_variant(ccx, dataful_variant);
for i in 0..variant.fields.count() {
let field = variant.field(ccx, i);
match field.abi {
layout::Abi::Scalar(layout::Scalar { value: layout::Pointer, .. }) => {
// We found the pointer field, use its type.
ty = field.ty;
break;
}
_ => {}
}
layout::Abi::Scalar(layout::Scalar {
value: layout::Pointer,
ref valid_range
}) => {
if valid_range.start > 0 && valid_range.start < valid_range.end {
arg.attrs.set(ArgAttribute::NonNull);
}
}
_ => {}
}
match ty.sty {
// `Box` pointer parameters never alias because ownership is transferred
ty::TyAdt(def, _) if def.is_box() => {
arg.attrs.set(ArgAttribute::NoAlias);
if let Some(pointee) = arg.layout.pointee_info(ccx) {
if let Some(kind) = pointee.safe {
arg.attrs.pointee_size = pointee.size;
arg.attrs.pointee_align = Some(pointee.align);
arg.safe_pointee(ccx.layout_of(ty.boxed_ty()));
}
// HACK(eddyb) LLVM inserts `llvm.assume` calls when inlining functions
// with align attributes, and those calls later block optimizations.
if !is_return {
arg.attrs.pointee_align = None;
}
ty::TyRef(_, mt) => {
// `Box` pointer parameters never alias because ownership is transferred
// `&mut` pointer parameters never alias other parameters,
// or mutable global data
//
@ -797,35 +767,19 @@ impl<'a, 'tcx> FnType<'tcx> {
// and can be marked as both `readonly` and `noalias`, as
// LLVM's definition of `noalias` is based solely on memory
// dependencies rather than pointer equality
let is_freeze = ccx.shared().type_is_freeze(mt.ty);
let no_alias_is_safe =
if ccx.shared().tcx().sess.opts.debugging_opts.mutable_noalias ||
ccx.shared().tcx().sess.panic_strategy() == PanicStrategy::Abort {
// Mutable refrences or immutable shared references
mt.mutbl == hir::MutMutable || is_freeze
} else {
// Only immutable shared references
mt.mutbl != hir::MutMutable && is_freeze
};
if no_alias_is_safe {
let no_alias = match kind {
PointerKind::Shared => false,
PointerKind::Frozen | PointerKind::UniqueOwned => true,
PointerKind::UniqueBorrowed => !is_return
};
if no_alias {
arg.attrs.set(ArgAttribute::NoAlias);
}
if mt.mutbl == hir::MutImmutable && is_freeze && !is_return {
if kind == PointerKind::Frozen && !is_return {
arg.attrs.set(ArgAttribute::ReadOnly);
}
arg.safe_pointee(ccx.layout_of(mt.ty));
}
_ => {}
}
// HACK(eddyb) LLVM inserts `llvm.assume` calls when inlining functions
// with align attributes, and those calls later block optimizations.
if !is_return {
arg.attrs.pointee_align = None;
}
};

View File

@ -24,6 +24,7 @@ use monomorphize::Instance;
use partitioning::CodegenUnit;
use type_::Type;
use type_of::PointeeInfo;
use rustc_data_structures::base_n;
use rustc::middle::trans::Stats;
@ -102,6 +103,7 @@ pub struct LocalCrateContext<'a, 'tcx: 'a> {
lltypes: RefCell<FxHashMap<(Ty<'tcx>, Option<usize>), Type>>,
scalar_lltypes: RefCell<FxHashMap<Ty<'tcx>, Type>>,
pointee_infos: RefCell<FxHashMap<Ty<'tcx>, Option<PointeeInfo>>>,
isize_ty: Type,
dbg_cx: Option<debuginfo::CrateDebugContext<'tcx>>,
@ -378,6 +380,7 @@ impl<'a, 'tcx> LocalCrateContext<'a, 'tcx> {
used_statics: RefCell::new(Vec::new()),
lltypes: RefCell::new(FxHashMap()),
scalar_lltypes: RefCell::new(FxHashMap()),
pointee_infos: RefCell::new(FxHashMap()),
isize_ty: Type::from_ref(ptr::null_mut()),
dbg_cx,
eh_personality: Cell::new(None),
@ -513,6 +516,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
&self.local().scalar_lltypes
}
pub fn pointee_infos<'a>(&'a self) -> &'a RefCell<FxHashMap<Ty<'tcx>, Option<PointeeInfo>>> {
&self.local().pointee_infos
}
pub fn stats<'a>(&'a self) -> &'a RefCell<Stats> {
&self.local().stats
}

View File

@ -17,7 +17,7 @@ use llvm::{Float, Double, X86_FP80, PPC_FP128, FP128};
use context::CrateContext;
use syntax::ast;
use rustc::ty::layout;
use rustc::ty::layout::{self, Align};
use std::ffi::CString;
use std::fmt;
@ -275,4 +275,15 @@ impl Type {
I128 => Type::i128(cx),
}
}
/// Return a LLVM type that has at most the required alignment,
/// as a conservative approximation for unknown pointee types.
pub fn pointee_for_abi_align(ccx: &CrateContext, align: Align) -> Type {
if let Some(ity) = layout::Integer::for_abi_align(ccx, align) {
Type::from_integer(ccx, ity)
} else {
// FIXME(eddyb) We could find a better approximation here.
Type::i8(ccx)
}
}
}

View File

@ -10,8 +10,10 @@
use abi::FnType;
use common::*;
use rustc::hir;
use rustc::ty::{self, Ty, TypeFoldable};
use rustc::ty::layout::{self, HasDataLayout, Align, LayoutOf, Size, TyLayout};
use rustc::ty::layout::{self, Align, LayoutOf, Size, TyLayout};
use rustc_back::PanicStrategy;
use trans_item::DefPathBasedNames;
use type_::Type;
@ -148,12 +150,35 @@ impl<'a, 'tcx> CrateContext<'a, 'tcx> {
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum PointerKind {
/// Most general case, we know no restrictions to tell LLVM.
Shared,
/// `&T` where `T` contains no `UnsafeCell`, is `noalias` and `readonly`.
Frozen,
/// `&mut T`, when we know `noalias` is safe for LLVM.
UniqueBorrowed,
/// `Box<T>`, unlike `UniqueBorrowed`, it also has `noalias` on returns.
UniqueOwned
}
#[derive(Copy, Clone)]
pub struct PointeeInfo {
pub size: Size,
pub align: Align,
pub safe: Option<PointerKind>,
}
pub trait LayoutLlvmExt<'tcx> {
fn is_llvm_immediate(&self) -> bool;
fn llvm_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Type;
fn immediate_llvm_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Type;
fn over_align(&self) -> Option<Align>;
fn llvm_field_index(&self, index: usize) -> u64;
fn pointee_info<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Option<PointeeInfo>;
}
impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> {
@ -202,7 +227,14 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> {
let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&sig);
FnType::new(ccx, sig, &[]).llvm_type(ccx)
}
_ => Type::i8(ccx)
_ => {
// If we know the alignment, pick something better than i8.
if let Some(pointee) = self.pointee_info(ccx) {
Type::pointee_for_abi_align(ccx, pointee.align)
} else {
Type::i8(ccx)
}
}
};
pointee.ptr_to()
}
@ -285,4 +317,118 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> {
}
}
}
fn pointee_info<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Option<PointeeInfo> {
// We only handle thin pointers here.
match self.abi {
layout::Abi::Scalar(layout::Scalar { value: layout::Pointer, .. }) => {}
_ => return None
}
if let Some(&pointee) = ccx.pointee_infos().borrow().get(&self.ty) {
return pointee;
}
let mut result = None;
match self.ty.sty {
ty::TyRawPtr(mt) => {
let (size, align) = ccx.size_and_align_of(mt.ty);
result = Some(PointeeInfo {
size,
align,
safe: None
});
}
ty::TyRef(_, mt) => {
let (size, align) = ccx.size_and_align_of(mt.ty);
let kind = match mt.mutbl {
hir::MutImmutable => if ccx.shared().type_is_freeze(mt.ty) {
PointerKind::Frozen
} else {
PointerKind::Shared
},
hir::MutMutable => {
if ccx.shared().tcx().sess.opts.debugging_opts.mutable_noalias ||
ccx.shared().tcx().sess.panic_strategy() == PanicStrategy::Abort {
PointerKind::UniqueBorrowed
} else {
PointerKind::Shared
}
}
};
result = Some(PointeeInfo {
size,
align,
safe: Some(kind)
});
}
ty::TyAdt(def, _) if def.is_box() => {
let (size, align) = ccx.size_and_align_of(self.ty.boxed_ty());
result = Some(PointeeInfo {
size,
align,
safe: Some(PointerKind::UniqueOwned)
});
}
_ => {
let mut data_variant = match self.variants {
layout::Variants::NicheFilling { dataful_variant, .. } => {
// Only the niche itself is always initialized,
// so only check for a pointer at its offset.
//
// If the niche is a pointer, it's either valid
// (according to its type), or null (which the
// niche field's scalar validity range encodes).
// This allows using `dereferenceable_or_null`
// for e.g. `Option<&T>`, and this will continue
// to work as long as we don't start using more
// niches than just null (e.g. the first page
// of the address space, or unaligned pointers).
if self.fields.offset(0).bytes() == 0 {
Some(self.for_variant(ccx, dataful_variant))
} else {
None
}
}
_ => Some(*self)
};
if let Some(variant) = data_variant {
// We're not interested in any unions.
if let layout::FieldPlacement::Union(_) = variant.fields {
data_variant = None;
}
}
if let Some(variant) = data_variant {
for i in 0..variant.fields.count() {
let field = variant.field(ccx, i);
if field.size == self.size {
// We found the pointer field, use its information.
result = field.pointee_info(ccx);
break;
}
}
}
if let ty::TyAdt(def, _) = self.ty.sty {
if Some(def.did) == ccx.tcx().lang_items().non_zero() {
// FIXME(eddyb) Don't treat NonZero<*T> as
// as containing &T in ty::layout.
if let Some(ref mut pointee) = result {
pointee.safe = None;
}
}
}
}
}
ccx.pointee_infos().borrow_mut().insert(self.ty, result);
result
}
}