1
Fork 0

also do not add noalias on not-Unpin Box

This commit is contained in:
Ralf Jung 2023-01-02 14:09:01 +01:00
parent ea541bc2ee
commit 1ef16874b5
6 changed files with 183 additions and 119 deletions

View file

@ -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.

View file

@ -818,8 +818,7 @@ 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,
@ -828,8 +827,10 @@ where
}) })
} }
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| { tcx.layout_of(param_env.and(tcx.mk_fn_ptr(fn_sig))).ok().map(|layout| PointeeInfo {
PointeeInfo { size: layout.size, align: layout.align.abi, safe: None } size: layout.size,
align: layout.align.abi,
safe: None,
}) })
} }
ty::Ref(_, ty, mt) if offset.bytes() == 0 => { ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
@ -914,7 +915,10 @@ where
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 def.is_box() && offset.bytes() == 0 {
pointee.safe = Some(PointerKind::Box); 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()),
});
} }
} }
} }

View file

@ -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>).

View file

@ -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)
} }

View file

@ -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();
} }

View file

@ -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
} }