rustc_trans: generate LLVM pointee types based on alignment.
This commit is contained in:
parent
f8d5d0c30c
commit
ac60872077
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user