diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 73c7eb6992f07..6c5be946392d3 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -199,6 +199,7 @@ fn adjust_for_rust_scalar<'tcx>( layout: TyAndLayout<'tcx>, offset: Size, is_return: bool, + is_drop_target: bool, ) { // Booleans are always a noundef i1 that needs to be zero-extended. if scalar.is_bool() { @@ -275,6 +276,18 @@ fn adjust_for_rust_scalar<'tcx>( attrs.set(ArgAttribute::NoAliasMutRef); } } + + // If this is the argument to `drop_in_place`, the contents of which we fully control as the + // compiler, then we mark this argument as `noalias`, aligned, and dereferenceable. (The + // standard library documents the necessary requirements to uphold these attributes for code + // that calls this method directly.) This can enable better optimizations, such as argument + // promotion. + if is_drop_target { + attrs.set(ArgAttribute::NoAlias); + attrs.set(ArgAttribute::NonNull); + attrs.pointee_size = pointee.size; + attrs.pointee_align = Some(pointee.align); + } } } @@ -331,10 +344,16 @@ fn fn_abi_new_uncached<'tcx>( use SpecAbi::*; let rust_abi = matches!(sig.abi, RustIntrinsic | PlatformIntrinsic | Rust | RustCall); + let is_drop_in_place = match (cx.tcx.lang_items().drop_in_place_fn(), fn_def_id) { + (Some(drop_in_place_fn), Some(fn_def_id)) => drop_in_place_fn == fn_def_id, + _ => false, + }; + let arg_of = |ty: Ty<'tcx>, arg_idx: Option| -> Result<_, FnAbiError<'tcx>> { let span = tracing::debug_span!("arg_of"); let _entered = span.enter(); let is_return = arg_idx.is_none(); + let is_drop_target = is_drop_in_place && arg_idx == Some(0); let layout = cx.layout_of(ty)?; let layout = if force_thin_self_ptr && arg_idx == Some(0) { @@ -348,7 +367,15 @@ fn fn_abi_new_uncached<'tcx>( let mut arg = ArgAbi::new(cx, layout, |layout, scalar, offset| { let mut attrs = ArgAttributes::new(); - adjust_for_rust_scalar(*cx, &mut attrs, scalar, *layout, offset, is_return); + adjust_for_rust_scalar( + *cx, + &mut attrs, + scalar, + *layout, + offset, + is_return, + is_drop_target, + ); attrs }); diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 9d4bae3eaa5f7..afa4f5ef6ce74 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -436,14 +436,34 @@ mod mut_ptr; /// /// # Safety /// -/// Behavior is undefined if any of the following conditions are violated: +/// Immediately upon executing, `drop_in_place` takes out a mutable borrow on the +/// pointed-to-value. Effectively, this function is implemented like so: +/// +/// ``` +/// # struct Foo { x: i32 } +/// unsafe fn drop_in_place(to_drop: *mut Foo) { +/// let mut value = &mut *to_drop; +/// // ... drop the fields of `value` ... +/// } +/// ``` +/// +/// This implies that the behavior is undefined if any of the following +/// conditions are violated: /// /// * `to_drop` must be [valid] for both reads and writes. /// -/// * `to_drop` must be properly aligned. +/// * `to_drop` must be properly aligned, even if T has size 0. +/// +/// * `to_drop` must be nonnull, even if T has size 0. +/// +/// * The value `to_drop` points to must be valid for dropping, which may mean +/// it must uphold additional invariants. These invariants depend on the type +/// of the value being dropped. For instance, when dropping a Box, the box's +/// pointer to the heap must be valid. /// -/// * The value `to_drop` points to must be valid for dropping, which may mean it must uphold -/// additional invariants - this is type-dependent. +/// * While `drop_in_place` is executing, the only way to access parts of +/// `to_drop` is through the `&mut self` references supplied to the +/// `Drop::drop` methods that `drop_in_place` invokes. /// /// Additionally, if `T` is not [`Copy`], using the pointed-to value after /// calling `drop_in_place` can cause undefined behavior. Note that `*to_drop = @@ -451,8 +471,6 @@ mod mut_ptr; /// again. [`write()`] can be used to overwrite data without causing it to be /// dropped. /// -/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned. -/// /// [valid]: self#safety /// /// # Examples diff --git a/src/test/codegen/drop-in-place-noalias.rs b/src/test/codegen/drop-in-place-noalias.rs new file mode 100644 index 0000000000000..64ac8760456cf --- /dev/null +++ b/src/test/codegen/drop-in-place-noalias.rs @@ -0,0 +1,34 @@ +// Tests that the compiler can mark `drop_in_place` as `noalias` when safe to do so. + +#![crate_type="lib"] + +use std::hint::black_box; + +// CHECK: define{{.*}}core{{.*}}ptr{{.*}}drop_in_place{{.*}}Foo{{.*}}({{.*}}noalias {{.*}} align 4 dereferenceable(12){{.*}}) + +#[repr(C)] +pub struct Foo { + a: i32, + b: i32, + c: i32, +} + +impl Drop for Foo { + #[inline(never)] + fn drop(&mut self) { + black_box(self.a); + } +} + +extern { + fn bar(); + fn baz(foo: Foo); +} + +pub fn haha() { + let foo = Foo { a: 1, b: 2, c: 3 }; + unsafe { + bar(); + baz(foo); + } +} diff --git a/src/test/codegen/noalias-box-off.rs b/src/test/codegen/noalias-box-off.rs index afd17c7c16074..aef3fd73994f7 100644 --- a/src/test/codegen/noalias-box-off.rs +++ b/src/test/codegen/noalias-box-off.rs @@ -3,6 +3,6 @@ #![crate_type = "lib"] // CHECK-LABEL: @box_should_not_have_noalias_if_disabled( -// CHECK-NOT: noalias +// CHECK-NOT: noalias{{.*}}% #[no_mangle] pub fn box_should_not_have_noalias_if_disabled(_b: Box) {}