also do not add noalias on not-Unpin Box
This commit is contained in:
parent
ea541bc2ee
commit
1ef16874b5
6 changed files with 183 additions and 119 deletions
|
@ -1443,8 +1443,8 @@ pub enum PointerKind {
|
||||||
SharedRef { frozen: bool },
|
SharedRef { frozen: bool },
|
||||||
/// Mutable reference. `unpin` indicates the absence of any pinned data.
|
/// Mutable reference. `unpin` indicates the absence of any pinned data.
|
||||||
MutableRef { unpin: bool },
|
MutableRef { unpin: bool },
|
||||||
/// Box.
|
/// Box. `unpin` indicates the absence of any pinned data.
|
||||||
Box,
|
Box { unpin: bool },
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Note that this information is advisory only, and backends are free to ignore it.
|
/// Note that this information is advisory only, and backends are free to ignore it.
|
||||||
|
|
|
@ -818,110 +818,114 @@ where
|
||||||
let tcx = cx.tcx();
|
let tcx = cx.tcx();
|
||||||
let param_env = cx.param_env();
|
let param_env = cx.param_env();
|
||||||
|
|
||||||
let pointee_info =
|
let pointee_info = match *this.ty.kind() {
|
||||||
match *this.ty.kind() {
|
ty::RawPtr(mt) if offset.bytes() == 0 => {
|
||||||
ty::RawPtr(mt) if offset.bytes() == 0 => {
|
tcx.layout_of(param_env.and(mt.ty)).ok().map(|layout| PointeeInfo {
|
||||||
tcx.layout_of(param_env.and(mt.ty)).ok().map(|layout| PointeeInfo {
|
size: layout.size,
|
||||||
size: layout.size,
|
align: layout.align.abi,
|
||||||
align: layout.align.abi,
|
safe: None,
|
||||||
safe: None,
|
})
|
||||||
})
|
}
|
||||||
}
|
ty::FnPtr(fn_sig) if offset.bytes() == 0 => {
|
||||||
ty::FnPtr(fn_sig) if offset.bytes() == 0 => {
|
tcx.layout_of(param_env.and(tcx.mk_fn_ptr(fn_sig))).ok().map(|layout| PointeeInfo {
|
||||||
tcx.layout_of(param_env.and(tcx.mk_fn_ptr(fn_sig))).ok().map(|layout| {
|
size: layout.size,
|
||||||
PointeeInfo { size: layout.size, align: layout.align.abi, safe: None }
|
align: layout.align.abi,
|
||||||
})
|
safe: None,
|
||||||
}
|
})
|
||||||
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
|
}
|
||||||
// Use conservative pointer kind if not optimizing. This saves us the
|
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
|
||||||
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
|
// Use conservative pointer kind if not optimizing. This saves us the
|
||||||
// attributes in LLVM have compile-time cost even in unoptimized builds).
|
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
|
||||||
let optimize = tcx.sess.opts.optimize != OptLevel::No;
|
// attributes in LLVM have compile-time cost even in unoptimized builds).
|
||||||
let kind = match mt {
|
let optimize = tcx.sess.opts.optimize != OptLevel::No;
|
||||||
hir::Mutability::Not => PointerKind::SharedRef {
|
let kind = match mt {
|
||||||
frozen: optimize && ty.is_freeze(tcx, cx.param_env()),
|
hir::Mutability::Not => PointerKind::SharedRef {
|
||||||
},
|
frozen: optimize && ty.is_freeze(tcx, cx.param_env()),
|
||||||
hir::Mutability::Mut => PointerKind::MutableRef {
|
},
|
||||||
unpin: optimize && ty.is_unpin(tcx, cx.param_env()),
|
hir::Mutability::Mut => PointerKind::MutableRef {
|
||||||
},
|
unpin: optimize && ty.is_unpin(tcx, cx.param_env()),
|
||||||
};
|
},
|
||||||
|
};
|
||||||
|
|
||||||
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: Some(kind),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
let mut data_variant = match this.variants {
|
let mut data_variant = match this.variants {
|
||||||
// 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
|
// If the niche is a pointer, it's either valid (according
|
||||||
// to its type), or null (which the niche field's scalar
|
// to its type), or null (which the niche field's scalar
|
||||||
// validity range encodes). This allows using
|
// validity range encodes). This allows using
|
||||||
// `dereferenceable_or_null` for e.g., `Option<&T>`, and
|
// `dereferenceable_or_null` for e.g., `Option<&T>`, and
|
||||||
// this will continue to work as long as we don't start
|
// this will continue to work as long as we don't start
|
||||||
// using more niches than just null (e.g., the first page of
|
// using more niches than just null (e.g., the first page of
|
||||||
// the address space, or unaligned pointers).
|
// the address space, or unaligned pointers).
|
||||||
Variants::Multiple {
|
Variants::Multiple {
|
||||||
tag_encoding: TagEncoding::Niche { untagged_variant, .. },
|
tag_encoding: TagEncoding::Niche { untagged_variant, .. },
|
||||||
tag_field,
|
tag_field,
|
||||||
..
|
..
|
||||||
} if this.fields.offset(tag_field) == offset => {
|
} if this.fields.offset(tag_field) == offset => {
|
||||||
Some(this.for_variant(cx, untagged_variant))
|
Some(this.for_variant(cx, untagged_variant))
|
||||||
}
|
|
||||||
_ => Some(this),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(variant) = data_variant {
|
|
||||||
// We're not interested in any unions.
|
|
||||||
if let FieldsShape::Union(_) = variant.fields {
|
|
||||||
data_variant = None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
_ => Some(this),
|
||||||
|
};
|
||||||
|
|
||||||
let mut result = None;
|
if let Some(variant) = data_variant {
|
||||||
|
// We're not interested in any unions.
|
||||||
|
if let FieldsShape::Union(_) = variant.fields {
|
||||||
|
data_variant = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(variant) = data_variant {
|
let mut result = None;
|
||||||
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
|
|
||||||
// (requires passing in the expected address space from the caller)
|
if let Some(variant) = data_variant {
|
||||||
let ptr_end = offset + Pointer(AddressSpace::DATA).size(cx);
|
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
|
||||||
for i in 0..variant.fields.count() {
|
// (requires passing in the expected address space from the caller)
|
||||||
let field_start = variant.fields.offset(i);
|
let ptr_end = offset + Pointer(AddressSpace::DATA).size(cx);
|
||||||
if field_start <= offset {
|
for i in 0..variant.fields.count() {
|
||||||
let field = variant.field(cx, i);
|
let field_start = variant.fields.offset(i);
|
||||||
result = field.to_result().ok().and_then(|field| {
|
if field_start <= offset {
|
||||||
if ptr_end <= field_start + field.size {
|
let field = variant.field(cx, i);
|
||||||
// We found the right field, look inside it.
|
result = field.to_result().ok().and_then(|field| {
|
||||||
let field_info =
|
if ptr_end <= field_start + field.size {
|
||||||
field.pointee_info_at(cx, offset - field_start);
|
// We found the right field, look inside it.
|
||||||
field_info
|
let field_info =
|
||||||
} else {
|
field.pointee_info_at(cx, offset - field_start);
|
||||||
None
|
field_info
|
||||||
}
|
} else {
|
||||||
});
|
None
|
||||||
if result.is_some() {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
if result.is_some() {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
|
|
||||||
if let Some(ref mut pointee) = result {
|
|
||||||
if let ty::Adt(def, _) = this.ty.kind() {
|
|
||||||
if def.is_box() && offset.bytes() == 0 {
|
|
||||||
pointee.safe = Some(PointerKind::Box);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
// FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
|
||||||
|
if let Some(ref mut pointee) = result {
|
||||||
|
if let ty::Adt(def, _) = this.ty.kind() {
|
||||||
|
if def.is_box() && offset.bytes() == 0 {
|
||||||
|
let optimize = tcx.sess.opts.optimize != OptLevel::No;
|
||||||
|
pointee.safe = Some(PointerKind::Box {
|
||||||
|
unpin: optimize && this.ty.boxed_ty().is_unpin(tcx, cx.param_env()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"pointee_info_at (offset={:?}, type kind: {:?}) => {:?}",
|
"pointee_info_at (offset={:?}, type kind: {:?}) => {:?}",
|
||||||
|
|
|
@ -261,7 +261,7 @@ fn adjust_for_rust_scalar<'tcx>(
|
||||||
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>). If LLVM had a way
|
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>). If LLVM had a way
|
||||||
// to say "dereferenceable on entry" we could use it here.
|
// to say "dereferenceable on entry" we could use it here.
|
||||||
attrs.pointee_size = match kind {
|
attrs.pointee_size = match kind {
|
||||||
PointerKind::Box
|
PointerKind::Box { .. }
|
||||||
| PointerKind::SharedRef { frozen: false }
|
| PointerKind::SharedRef { frozen: false }
|
||||||
| PointerKind::MutableRef { unpin: false } => Size::ZERO,
|
| PointerKind::MutableRef { unpin: false } => Size::ZERO,
|
||||||
PointerKind::SharedRef { frozen: true }
|
PointerKind::SharedRef { frozen: true }
|
||||||
|
@ -278,17 +278,16 @@ fn adjust_for_rust_scalar<'tcx>(
|
||||||
// versions at all anymore. We still support turning it off using -Zmutable-noalias.
|
// versions at all anymore. We still support turning it off using -Zmutable-noalias.
|
||||||
let noalias_mut_ref = cx.tcx.sess.opts.unstable_opts.mutable_noalias;
|
let noalias_mut_ref = cx.tcx.sess.opts.unstable_opts.mutable_noalias;
|
||||||
|
|
||||||
// `&mut` pointer parameters never alias other parameters,
|
|
||||||
// or mutable global data
|
|
||||||
//
|
|
||||||
// `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as both
|
// `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as both
|
||||||
// `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely on memory
|
// `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely on memory
|
||||||
// dependencies rather than pointer equality. However this only applies to arguments,
|
// dependencies rather than pointer equality. However this only applies to arguments,
|
||||||
// not return values.
|
// not return values.
|
||||||
|
//
|
||||||
|
// `&mut T` and `Box<T>` where `T: Unpin` are unique and hence `noalias`.
|
||||||
let no_alias = match kind {
|
let no_alias = match kind {
|
||||||
PointerKind::SharedRef { frozen } => frozen,
|
PointerKind::SharedRef { frozen } => frozen,
|
||||||
PointerKind::MutableRef { unpin } => unpin && noalias_mut_ref,
|
PointerKind::MutableRef { unpin } => unpin && noalias_mut_ref,
|
||||||
PointerKind::Box => noalias_for_box,
|
PointerKind::Box { unpin } => unpin && noalias_for_box,
|
||||||
};
|
};
|
||||||
// We can never add `noalias` in return position; that LLVM attribute has some very surprising semantics
|
// We can never add `noalias` in return position; that LLVM attribute has some very surprising semantics
|
||||||
// (see <https://github.com/rust-lang/unsafe-code-guidelines/issues/385#issuecomment-1368055745>).
|
// (see <https://github.com/rust-lang/unsafe-code-guidelines/issues/385#issuecomment-1368055745>).
|
||||||
|
|
|
@ -135,6 +135,32 @@ impl NewPermission {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn from_box_ty<'tcx>(
|
||||||
|
ty: Ty<'tcx>,
|
||||||
|
kind: RetagKind,
|
||||||
|
cx: &crate::MiriInterpCx<'_, 'tcx>,
|
||||||
|
) -> Self {
|
||||||
|
// `ty` is not the `Box` but the field of the Box with this pointer (due to allocator handling).
|
||||||
|
let pointee = ty.builtin_deref(true).unwrap().ty;
|
||||||
|
if pointee.is_unpin(*cx.tcx, cx.param_env()) {
|
||||||
|
// A regular box. On `FnEntry` this is `noalias`, but not `dereferenceable` (hence only
|
||||||
|
// a weak protector).
|
||||||
|
NewPermission::Uniform {
|
||||||
|
perm: Permission::Unique,
|
||||||
|
access: Some(AccessKind::Write),
|
||||||
|
protector: (kind == RetagKind::FnEntry)
|
||||||
|
.then_some(ProtectorKind::WeakProtector),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// `!Unpin` boxes do not get `noalias` nor `dereferenceable`.
|
||||||
|
NewPermission::Uniform {
|
||||||
|
perm: Permission::SharedReadWrite,
|
||||||
|
access: None,
|
||||||
|
protector: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn protector(&self) -> Option<ProtectorKind> {
|
fn protector(&self) -> Option<ProtectorKind> {
|
||||||
match self {
|
match self {
|
||||||
NewPermission::Uniform { protector, .. } => *protector,
|
NewPermission::Uniform { protector, .. } => *protector,
|
||||||
|
@ -914,12 +940,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
|
|
||||||
fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
|
fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
|
||||||
// Boxes get a weak protectors, since they may be deallocated.
|
// Boxes get a weak protectors, since they may be deallocated.
|
||||||
let new_perm = NewPermission::Uniform {
|
let new_perm = NewPermission::from_box_ty(place.layout.ty, self.kind, self.ecx);
|
||||||
perm: Permission::Unique,
|
|
||||||
access: Some(AccessKind::Write),
|
|
||||||
protector: (self.kind == RetagKind::FnEntry)
|
|
||||||
.then_some(ProtectorKind::WeakProtector),
|
|
||||||
};
|
|
||||||
self.retag_ptr_inplace(place, new_perm, self.retag_cause)
|
self.retag_ptr_inplace(place, new_perm, self.retag_cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,19 @@ impl Future for Delay {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mk_waker() -> Waker {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
struct MyWaker;
|
||||||
|
impl Wake for MyWaker {
|
||||||
|
fn wake(self: Arc<Self>) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Waker::from(Arc::new(MyWaker))
|
||||||
|
}
|
||||||
|
|
||||||
async fn do_stuff() {
|
async fn do_stuff() {
|
||||||
(&mut Delay::new(1)).await;
|
(&mut Delay::new(1)).await;
|
||||||
}
|
}
|
||||||
|
@ -73,16 +86,7 @@ impl Future for DoStuff {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_fut<T>(fut: impl Future<Output = T>) -> T {
|
fn run_fut<T>(fut: impl Future<Output = T>) -> T {
|
||||||
use std::sync::Arc;
|
let waker = mk_waker();
|
||||||
|
|
||||||
struct MyWaker;
|
|
||||||
impl Wake for MyWaker {
|
|
||||||
fn wake(self: Arc<Self>) {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let waker = Waker::from(Arc::new(MyWaker));
|
|
||||||
let mut context = Context::from_waker(&waker);
|
let mut context = Context::from_waker(&waker);
|
||||||
|
|
||||||
let mut pinned = pin!(fut);
|
let mut pinned = pin!(fut);
|
||||||
|
@ -94,7 +98,37 @@ fn run_fut<T>(fut: impl Future<Output = T>) -> T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn self_referential_box() {
|
||||||
|
let waker = mk_waker();
|
||||||
|
let cx = &mut Context::from_waker(&waker);
|
||||||
|
|
||||||
|
async fn my_fut() -> i32 {
|
||||||
|
let val = 10;
|
||||||
|
let val_ref = &val;
|
||||||
|
|
||||||
|
let _ = Delay::new(1).await;
|
||||||
|
|
||||||
|
*val_ref
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_poll<F: Future>(
|
||||||
|
mut f: Pin<Box<F>>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
) -> (Pin<Box<F>>, Poll<F::Output>) {
|
||||||
|
let p = f.as_mut().poll(cx);
|
||||||
|
(f, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
let my_fut = Box::pin(my_fut());
|
||||||
|
let (my_fut, p1) = box_poll(my_fut, cx);
|
||||||
|
assert!(p1.is_pending());
|
||||||
|
let (my_fut, p2) = box_poll(my_fut, cx);
|
||||||
|
assert!(p2.is_ready());
|
||||||
|
drop(my_fut);
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
run_fut(do_stuff());
|
run_fut(do_stuff());
|
||||||
run_fut(DoStuff::new());
|
run_fut(DoStuff::new());
|
||||||
|
self_referential_box();
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,6 +181,12 @@ pub fn _box(x: Box<i32>) -> Box<i32> {
|
||||||
x
|
x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CHECK: noundef nonnull align 4 {{i32\*|ptr}} @notunpin_box({{i32\*|ptr}} noundef nonnull align 4 %x)
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn notunpin_box(x: Box<NotUnpin>) -> Box<NotUnpin> {
|
||||||
|
x
|
||||||
|
}
|
||||||
|
|
||||||
// CHECK: @struct_return({{%S\*|ptr}} noalias nocapture noundef sret(%S) dereferenceable(32){{( %0)?}})
|
// CHECK: @struct_return({{%S\*|ptr}} noalias nocapture noundef sret(%S) dereferenceable(32){{( %0)?}})
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn struct_return() -> S {
|
pub fn struct_return() -> S {
|
||||||
|
@ -247,12 +253,12 @@ pub fn trait_raw(_: *const dyn Drop) {
|
||||||
|
|
||||||
// CHECK: @trait_box({{\{\}\*|ptr}} noalias noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
|
// CHECK: @trait_box({{\{\}\*|ptr}} noalias noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn trait_box(_: Box<dyn Drop>) {
|
pub fn trait_box(_: Box<dyn Drop + Unpin>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECK: { {{i8\*|ptr}}, {{i8\*|ptr}} } @trait_option({{i8\*|ptr}} noalias noundef align 1 %x.0, {{i8\*|ptr}} %x.1)
|
// CHECK: { {{i8\*|ptr}}, {{i8\*|ptr}} } @trait_option({{i8\*|ptr}} noalias noundef align 1 %x.0, {{i8\*|ptr}} %x.1)
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn trait_option(x: Option<Box<dyn Drop>>) -> Option<Box<dyn Drop>> {
|
pub fn trait_option(x: Option<Box<dyn Drop + Unpin>>) -> Option<Box<dyn Drop + Unpin>> {
|
||||||
x
|
x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue