Don't treat ref. fields with non-null niches as dereferenceable_or_null
This commit is contained in:
parent
4fb039ed6c
commit
403f34b599
4 changed files with 37 additions and 25 deletions
|
@ -339,7 +339,8 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
|
||||||
return pointee;
|
return pointee;
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset);
|
let assume_valid_ptr = true;
|
||||||
|
let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset, assume_valid_ptr);
|
||||||
|
|
||||||
cx.pointee_infos.borrow_mut().insert((self.ty, offset), result);
|
cx.pointee_infos.borrow_mut().insert((self.ty, offset), result);
|
||||||
result
|
result
|
||||||
|
|
|
@ -411,8 +411,8 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
|
||||||
if let Some(&pointee) = cx.pointee_infos.borrow().get(&(self.ty, offset)) {
|
if let Some(&pointee) = cx.pointee_infos.borrow().get(&(self.ty, offset)) {
|
||||||
return pointee;
|
return pointee;
|
||||||
}
|
}
|
||||||
|
let assume_valid_ptr = true;
|
||||||
let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset);
|
let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset, assume_valid_ptr);
|
||||||
|
|
||||||
cx.pointee_infos.borrow_mut().insert((self.ty, offset), result);
|
cx.pointee_infos.borrow_mut().insert((self.ty, offset), result);
|
||||||
result
|
result
|
||||||
|
|
|
@ -1036,6 +1036,9 @@ where
|
||||||
this: TyAndLayout<'tcx>,
|
this: TyAndLayout<'tcx>,
|
||||||
cx: &C,
|
cx: &C,
|
||||||
offset: Size,
|
offset: Size,
|
||||||
|
// If true, assume that pointers are either null or valid (according to their type),
|
||||||
|
// enabling extra optimizations.
|
||||||
|
mut assume_valid_ptr: bool,
|
||||||
) -> Option<PointeeInfo> {
|
) -> Option<PointeeInfo> {
|
||||||
let tcx = cx.tcx();
|
let tcx = cx.tcx();
|
||||||
let param_env = cx.param_env();
|
let param_env = cx.param_env();
|
||||||
|
@ -1058,19 +1061,19 @@ where
|
||||||
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
|
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
|
||||||
// attributes in LLVM have compile-time cost even in unoptimized builds).
|
// attributes in LLVM have compile-time cost even in unoptimized builds).
|
||||||
let optimize = tcx.sess.opts.optimize != OptLevel::No;
|
let optimize = tcx.sess.opts.optimize != OptLevel::No;
|
||||||
let kind = match mt {
|
let safe = match (assume_valid_ptr, mt) {
|
||||||
hir::Mutability::Not => PointerKind::SharedRef {
|
(true, hir::Mutability::Not) => Some(PointerKind::SharedRef {
|
||||||
frozen: optimize && ty.is_freeze(tcx, cx.param_env()),
|
frozen: optimize && ty.is_freeze(tcx, cx.param_env()),
|
||||||
},
|
}),
|
||||||
hir::Mutability::Mut => PointerKind::MutableRef {
|
(true, hir::Mutability::Mut) => Some(PointerKind::MutableRef {
|
||||||
unpin: optimize && ty.is_unpin(tcx, cx.param_env()),
|
unpin: optimize && ty.is_unpin(tcx, cx.param_env()),
|
||||||
},
|
}),
|
||||||
|
(false, _) => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
|
tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
|
||||||
size: layout.size,
|
size: layout.size,
|
||||||
align: layout.align.abi,
|
align: layout.align.abi,
|
||||||
safe: Some(kind),
|
safe,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1079,20 +1082,21 @@ where
|
||||||
// Within the discriminant field, only the niche itself is
|
// Within the discriminant field, only the niche itself is
|
||||||
// always initialized, so we only check for a pointer at its
|
// always initialized, so we only check for a pointer at its
|
||||||
// offset.
|
// 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).
|
|
||||||
// FIXME(reference_niches): well, the day has come...
|
|
||||||
Variants::Multiple {
|
Variants::Multiple {
|
||||||
tag_encoding: TagEncoding::Niche { untagged_variant, .. },
|
tag_encoding:
|
||||||
|
TagEncoding::Niche {
|
||||||
|
untagged_variant,
|
||||||
|
niche_variants: ref variants,
|
||||||
|
niche_start,
|
||||||
|
},
|
||||||
tag_field,
|
tag_field,
|
||||||
..
|
..
|
||||||
} if this.fields.offset(tag_field) == offset => {
|
} if this.fields.offset(tag_field) == offset => {
|
||||||
|
// We can only continue assuming pointer validity if the only possible
|
||||||
|
// discriminant value is null. The null special-case is permitted by LLVM's
|
||||||
|
// `dereferenceable_or_null`, and allow types like `Option<&T>` to benefit
|
||||||
|
// from optimizations.
|
||||||
|
assume_valid_ptr &= niche_start == 0 && variants.start() == variants.end();
|
||||||
Some(this.for_variant(cx, untagged_variant))
|
Some(this.for_variant(cx, untagged_variant))
|
||||||
}
|
}
|
||||||
_ => Some(this),
|
_ => Some(this),
|
||||||
|
@ -1118,9 +1122,12 @@ where
|
||||||
result = field.to_result().ok().and_then(|field| {
|
result = field.to_result().ok().and_then(|field| {
|
||||||
if ptr_end <= field_start + field.size {
|
if ptr_end <= field_start + field.size {
|
||||||
// We found the right field, look inside it.
|
// We found the right field, look inside it.
|
||||||
let field_info =
|
Self::ty_and_layout_pointee_info_at(
|
||||||
field.pointee_info_at(cx, offset - field_start);
|
field,
|
||||||
field_info
|
cx,
|
||||||
|
offset - field_start,
|
||||||
|
assume_valid_ptr,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -1135,7 +1142,7 @@ where
|
||||||
// FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
|
// FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
|
||||||
if let Some(ref mut pointee) = result {
|
if let Some(ref mut pointee) = result {
|
||||||
if let ty::Adt(def, _) = this.ty.kind() {
|
if let ty::Adt(def, _) = this.ty.kind() {
|
||||||
if def.is_box() && offset.bytes() == 0 {
|
if assume_valid_ptr && def.is_box() && offset.bytes() == 0 {
|
||||||
let optimize = tcx.sess.opts.optimize != OptLevel::No;
|
let optimize = tcx.sess.opts.optimize != OptLevel::No;
|
||||||
pointee.safe = Some(PointerKind::Box {
|
pointee.safe = Some(PointerKind::Box {
|
||||||
unpin: optimize && this.ty.boxed_ty().is_unpin(tcx, cx.param_env()),
|
unpin: optimize && this.ty.boxed_ty().is_unpin(tcx, cx.param_env()),
|
||||||
|
|
|
@ -50,6 +50,9 @@ pub trait TyAbiInterface<'a, C>: Sized {
|
||||||
this: TyAndLayout<'a, Self>,
|
this: TyAndLayout<'a, Self>,
|
||||||
cx: &C,
|
cx: &C,
|
||||||
offset: Size,
|
offset: Size,
|
||||||
|
// If true, assume that pointers are either null or valid (according to their type),
|
||||||
|
// enabling extra optimizations.
|
||||||
|
assume_valid_ptr: bool,
|
||||||
) -> Option<PointeeInfo>;
|
) -> Option<PointeeInfo>;
|
||||||
fn is_adt(this: TyAndLayout<'a, Self>) -> bool;
|
fn is_adt(this: TyAndLayout<'a, Self>) -> bool;
|
||||||
fn is_never(this: TyAndLayout<'a, Self>) -> bool;
|
fn is_never(this: TyAndLayout<'a, Self>) -> bool;
|
||||||
|
@ -76,7 +79,8 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
||||||
where
|
where
|
||||||
Ty: TyAbiInterface<'a, C>,
|
Ty: TyAbiInterface<'a, C>,
|
||||||
{
|
{
|
||||||
Ty::ty_and_layout_pointee_info_at(self, cx, offset)
|
let assume_valid_ptr = true;
|
||||||
|
Ty::ty_and_layout_pointee_info_at(self, cx, offset, assume_valid_ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_single_fp_element<C>(self, cx: &C) -> bool
|
pub fn is_single_fp_element<C>(self, cx: &C) -> bool
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue