Skip to content

Commit ac60872

Browse files
committed
rustc_trans: generate LLVM pointee types based on alignment.
1 parent f8d5d0c commit ac60872

File tree

4 files changed

+191
-73
lines changed

4 files changed

+191
-73
lines changed

src/librustc_trans/abi.rs

Lines changed: 24 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,11 @@ use cabi_nvptx64;
3232
use cabi_hexagon;
3333
use mir::lvalue::LvalueRef;
3434
use type_::Type;
35-
use type_of::LayoutLlvmExt;
35+
use type_of::{LayoutLlvmExt, PointerKind};
3636

37-
use rustc::hir;
3837
use rustc::ty::{self, Ty};
3938
use rustc::ty::layout::{self, Align, Size, TyLayout};
4039
use rustc::ty::layout::{HasDataLayout, LayoutOf};
41-
use rustc_back::PanicStrategy;
4240

4341
use libc::c_uint;
4442
use std::{cmp, iter};
@@ -514,22 +512,6 @@ impl<'a, 'tcx> ArgType<'tcx> {
514512
self.kind = ArgKind::Ignore;
515513
}
516514

517-
fn safe_pointee(&mut self, layout: TyLayout) {
518-
match self.layout.abi {
519-
layout::Abi::Scalar(layout::Scalar {
520-
value: layout::Pointer,
521-
ref valid_range
522-
}) => {
523-
if valid_range.start > 0 {
524-
self.attrs.set(ArgAttribute::NonNull);
525-
}
526-
self.attrs.pointee_size = layout.size;
527-
self.attrs.pointee_align = Some(layout.align);
528-
}
529-
_ => bug!("ArgType::safe_pointee({:#?}): not a pointer", self.layout)
530-
}
531-
}
532-
533515
pub fn extend_integer_width_to(&mut self, bits: u64) {
534516
// Only integers have signedness
535517
if let layout::Abi::Scalar(ref scalar) = self.layout.abi {
@@ -754,78 +736,50 @@ impl<'a, 'tcx> FnType<'tcx> {
754736

755737
// Handle safe Rust thin and fat pointers.
756738
let adjust_for_rust_type = |arg: &mut ArgType<'tcx>, is_return: bool| {
757-
// We only handle thin pointers here.
758739
match arg.layout.abi {
759-
layout::Abi::Scalar(layout::Scalar { value: layout::Pointer, .. }) => {}
760-
_ => return
761-
}
762-
763-
let mut ty = arg.layout.ty;
764-
765-
// FIXME(eddyb) detect more nested cases than `Option<&T>` here.
766-
match arg.layout.variants {
767-
layout::Variants::NicheFilling { dataful_variant, .. } => {
768-
let variant = arg.layout.for_variant(ccx, dataful_variant);
769-
for i in 0..variant.fields.count() {
770-
let field = variant.field(ccx, i);
771-
match field.abi {
772-
layout::Abi::Scalar(layout::Scalar { value: layout::Pointer, .. }) => {
773-
// We found the pointer field, use its type.
774-
ty = field.ty;
775-
break;
776-
}
777-
_ => {}
778-
}
740+
layout::Abi::Scalar(layout::Scalar {
741+
value: layout::Pointer,
742+
ref valid_range
743+
}) => {
744+
if valid_range.start > 0 && valid_range.start < valid_range.end {
745+
arg.attrs.set(ArgAttribute::NonNull);
779746
}
780747
}
781748
_ => {}
782749
}
783750

784-
match ty.sty {
785-
// `Box` pointer parameters never alias because ownership is transferred
786-
ty::TyAdt(def, _) if def.is_box() => {
787-
arg.attrs.set(ArgAttribute::NoAlias);
751+
if let Some(pointee) = arg.layout.pointee_info(ccx) {
752+
if let Some(kind) = pointee.safe {
753+
arg.attrs.pointee_size = pointee.size;
754+
arg.attrs.pointee_align = Some(pointee.align);
788755

789-
arg.safe_pointee(ccx.layout_of(ty.boxed_ty()));
790-
}
756+
// HACK(eddyb) LLVM inserts `llvm.assume` calls when inlining functions
757+
// with align attributes, and those calls later block optimizations.
758+
if !is_return {
759+
arg.attrs.pointee_align = None;
760+
}
791761

792-
ty::TyRef(_, mt) => {
762+
// `Box` pointer parameters never alias because ownership is transferred
793763
// `&mut` pointer parameters never alias other parameters,
794764
// or mutable global data
795765
//
796766
// `&T` where `T` contains no `UnsafeCell<U>` is immutable,
797767
// and can be marked as both `readonly` and `noalias`, as
798768
// LLVM's definition of `noalias` is based solely on memory
799769
// dependencies rather than pointer equality
800-
let is_freeze = ccx.shared().type_is_freeze(mt.ty);
801-
802-
let no_alias_is_safe =
803-
if ccx.shared().tcx().sess.opts.debugging_opts.mutable_noalias ||
804-
ccx.shared().tcx().sess.panic_strategy() == PanicStrategy::Abort {
805-
// Mutable refrences or immutable shared references
806-
mt.mutbl == hir::MutMutable || is_freeze
807-
} else {
808-
// Only immutable shared references
809-
mt.mutbl != hir::MutMutable && is_freeze
810-
};
811-
812-
if no_alias_is_safe {
770+
let no_alias = match kind {
771+
PointerKind::Shared => false,
772+
PointerKind::Frozen | PointerKind::UniqueOwned => true,
773+
PointerKind::UniqueBorrowed => !is_return
774+
};
775+
if no_alias {
813776
arg.attrs.set(ArgAttribute::NoAlias);
814777
}
815778

816-
if mt.mutbl == hir::MutImmutable && is_freeze && !is_return {
779+
if kind == PointerKind::Frozen && !is_return {
817780
arg.attrs.set(ArgAttribute::ReadOnly);
818781
}
819-
820-
arg.safe_pointee(ccx.layout_of(mt.ty));
821782
}
822-
_ => {}
823-
}
824-
825-
// HACK(eddyb) LLVM inserts `llvm.assume` calls when inlining functions
826-
// with align attributes, and those calls later block optimizations.
827-
if !is_return {
828-
arg.attrs.pointee_align = None;
829783
}
830784
};
831785

src/librustc_trans/context.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use monomorphize::Instance;
2424

2525
use partitioning::CodegenUnit;
2626
use type_::Type;
27+
use type_of::PointeeInfo;
2728

2829
use rustc_data_structures::base_n;
2930
use rustc::middle::trans::Stats;
@@ -102,6 +103,7 @@ pub struct LocalCrateContext<'a, 'tcx: 'a> {
102103

103104
lltypes: RefCell<FxHashMap<(Ty<'tcx>, Option<usize>), Type>>,
104105
scalar_lltypes: RefCell<FxHashMap<Ty<'tcx>, Type>>,
106+
pointee_infos: RefCell<FxHashMap<Ty<'tcx>, Option<PointeeInfo>>>,
105107
isize_ty: Type,
106108

107109
dbg_cx: Option<debuginfo::CrateDebugContext<'tcx>>,
@@ -378,6 +380,7 @@ impl<'a, 'tcx> LocalCrateContext<'a, 'tcx> {
378380
used_statics: RefCell::new(Vec::new()),
379381
lltypes: RefCell::new(FxHashMap()),
380382
scalar_lltypes: RefCell::new(FxHashMap()),
383+
pointee_infos: RefCell::new(FxHashMap()),
381384
isize_ty: Type::from_ref(ptr::null_mut()),
382385
dbg_cx,
383386
eh_personality: Cell::new(None),
@@ -513,6 +516,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
513516
&self.local().scalar_lltypes
514517
}
515518

519+
pub fn pointee_infos<'a>(&'a self) -> &'a RefCell<FxHashMap<Ty<'tcx>, Option<PointeeInfo>>> {
520+
&self.local().pointee_infos
521+
}
522+
516523
pub fn stats<'a>(&'a self) -> &'a RefCell<Stats> {
517524
&self.local().stats
518525
}

src/librustc_trans/type_.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use llvm::{Float, Double, X86_FP80, PPC_FP128, FP128};
1717
use context::CrateContext;
1818

1919
use syntax::ast;
20-
use rustc::ty::layout;
20+
use rustc::ty::layout::{self, Align};
2121

2222
use std::ffi::CString;
2323
use std::fmt;
@@ -275,4 +275,15 @@ impl Type {
275275
I128 => Type::i128(cx),
276276
}
277277
}
278+
279+
/// Return a LLVM type that has at most the required alignment,
280+
/// as a conservative approximation for unknown pointee types.
281+
pub fn pointee_for_abi_align(ccx: &CrateContext, align: Align) -> Type {
282+
if let Some(ity) = layout::Integer::for_abi_align(ccx, align) {
283+
Type::from_integer(ccx, ity)
284+
} else {
285+
// FIXME(eddyb) We could find a better approximation here.
286+
Type::i8(ccx)
287+
}
288+
}
278289
}

src/librustc_trans/type_of.rs

Lines changed: 148 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010

1111
use abi::FnType;
1212
use common::*;
13+
use rustc::hir;
1314
use rustc::ty::{self, Ty, TypeFoldable};
14-
use rustc::ty::layout::{self, HasDataLayout, Align, LayoutOf, Size, TyLayout};
15+
use rustc::ty::layout::{self, Align, LayoutOf, Size, TyLayout};
16+
use rustc_back::PanicStrategy;
1517
use trans_item::DefPathBasedNames;
1618
use type_::Type;
1719

@@ -148,12 +150,35 @@ impl<'a, 'tcx> CrateContext<'a, 'tcx> {
148150
}
149151
}
150152

153+
#[derive(Copy, Clone, PartialEq, Eq)]
154+
pub enum PointerKind {
155+
/// Most general case, we know no restrictions to tell LLVM.
156+
Shared,
157+
158+
/// `&T` where `T` contains no `UnsafeCell`, is `noalias` and `readonly`.
159+
Frozen,
160+
161+
/// `&mut T`, when we know `noalias` is safe for LLVM.
162+
UniqueBorrowed,
163+
164+
/// `Box<T>`, unlike `UniqueBorrowed`, it also has `noalias` on returns.
165+
UniqueOwned
166+
}
167+
168+
#[derive(Copy, Clone)]
169+
pub struct PointeeInfo {
170+
pub size: Size,
171+
pub align: Align,
172+
pub safe: Option<PointerKind>,
173+
}
174+
151175
pub trait LayoutLlvmExt<'tcx> {
152176
fn is_llvm_immediate(&self) -> bool;
153177
fn llvm_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Type;
154178
fn immediate_llvm_type<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Type;
155179
fn over_align(&self) -> Option<Align>;
156180
fn llvm_field_index(&self, index: usize) -> u64;
181+
fn pointee_info<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Option<PointeeInfo>;
157182
}
158183

159184
impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> {
@@ -202,7 +227,14 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> {
202227
let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&sig);
203228
FnType::new(ccx, sig, &[]).llvm_type(ccx)
204229
}
205-
_ => Type::i8(ccx)
230+
_ => {
231+
// If we know the alignment, pick something better than i8.
232+
if let Some(pointee) = self.pointee_info(ccx) {
233+
Type::pointee_for_abi_align(ccx, pointee.align)
234+
} else {
235+
Type::i8(ccx)
236+
}
237+
}
206238
};
207239
pointee.ptr_to()
208240
}
@@ -285,4 +317,118 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> {
285317
}
286318
}
287319
}
320+
321+
fn pointee_info<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> Option<PointeeInfo> {
322+
// We only handle thin pointers here.
323+
match self.abi {
324+
layout::Abi::Scalar(layout::Scalar { value: layout::Pointer, .. }) => {}
325+
_ => return None
326+
}
327+
328+
if let Some(&pointee) = ccx.pointee_infos().borrow().get(&self.ty) {
329+
return pointee;
330+
}
331+
332+
let mut result = None;
333+
match self.ty.sty {
334+
ty::TyRawPtr(mt) => {
335+
let (size, align) = ccx.size_and_align_of(mt.ty);
336+
result = Some(PointeeInfo {
337+
size,
338+
align,
339+
safe: None
340+
});
341+
}
342+
343+
ty::TyRef(_, mt) => {
344+
let (size, align) = ccx.size_and_align_of(mt.ty);
345+
346+
let kind = match mt.mutbl {
347+
hir::MutImmutable => if ccx.shared().type_is_freeze(mt.ty) {
348+
PointerKind::Frozen
349+
} else {
350+
PointerKind::Shared
351+
},
352+
hir::MutMutable => {
353+
if ccx.shared().tcx().sess.opts.debugging_opts.mutable_noalias ||
354+
ccx.shared().tcx().sess.panic_strategy() == PanicStrategy::Abort {
355+
PointerKind::UniqueBorrowed
356+
} else {
357+
PointerKind::Shared
358+
}
359+
}
360+
};
361+
362+
result = Some(PointeeInfo {
363+
size,
364+
align,
365+
safe: Some(kind)
366+
});
367+
}
368+
369+
ty::TyAdt(def, _) if def.is_box() => {
370+
let (size, align) = ccx.size_and_align_of(self.ty.boxed_ty());
371+
result = Some(PointeeInfo {
372+
size,
373+
align,
374+
safe: Some(PointerKind::UniqueOwned)
375+
});
376+
}
377+
378+
_ => {
379+
let mut data_variant = match self.variants {
380+
layout::Variants::NicheFilling { dataful_variant, .. } => {
381+
// Only the niche itself is always initialized,
382+
// so only check for a pointer at its offset.
383+
//
384+
// If the niche is a pointer, it's either valid
385+
// (according to its type), or null (which the
386+
// niche field's scalar validity range encodes).
387+
// This allows using `dereferenceable_or_null`
388+
// for e.g. `Option<&T>`, and this will continue
389+
// to work as long as we don't start using more
390+
// niches than just null (e.g. the first page
391+
// of the address space, or unaligned pointers).
392+
if self.fields.offset(0).bytes() == 0 {
393+
Some(self.for_variant(ccx, dataful_variant))
394+
} else {
395+
None
396+
}
397+
}
398+
_ => Some(*self)
399+
};
400+
401+
if let Some(variant) = data_variant {
402+
// We're not interested in any unions.
403+
if let layout::FieldPlacement::Union(_) = variant.fields {
404+
data_variant = None;
405+
}
406+
}
407+
408+
if let Some(variant) = data_variant {
409+
for i in 0..variant.fields.count() {
410+
let field = variant.field(ccx, i);
411+
if field.size == self.size {
412+
// We found the pointer field, use its information.
413+
result = field.pointee_info(ccx);
414+
break;
415+
}
416+
}
417+
}
418+
419+
if let ty::TyAdt(def, _) = self.ty.sty {
420+
if Some(def.did) == ccx.tcx().lang_items().non_zero() {
421+
// FIXME(eddyb) Don't treat NonZero<*T> as
422+
// as containing &T in ty::layout.
423+
if let Some(ref mut pointee) = result {
424+
pointee.safe = None;
425+
}
426+
}
427+
}
428+
}
429+
}
430+
431+
ccx.pointee_infos().borrow_mut().insert(self.ty, result);
432+
result
433+
}
288434
}

0 commit comments

Comments
 (0)