Skip to content

Commit f3d597b

Browse files
committed
Auto merge of rust-lang#111807 - erikdesjardins:noalias, r=oli-obk
[rustc_ty_utils] Treat `drop_in_place`'s *mut argument like &mut when adding LLVM attributes This resurrects PR rust-lang#103614, which has sat idle for a while. This could probably use a new perf run, since we're on a new LLVM version now. r? `@oli-obk` cc `@RalfJung` --- LLVM can make use of the `noalias` parameter attribute on the parameter to `drop_in_place` in areas like argument promotion. Because the Rust compiler fully controls the code for `drop_in_place`, it can soundly deduce parameter attributes on it. In rust-lang#103957, Miri was changed to retag `drop_in_place`'s argument as if it was `&mut`, matching this change.
2 parents cda5bec + fb7f1d2 commit f3d597b

File tree

4 files changed

+84
-10
lines changed

4 files changed

+84
-10
lines changed

compiler/rustc_ty_utils/src/abi.rs

+31-4
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ fn adjust_for_rust_scalar<'tcx>(
238238
layout: TyAndLayout<'tcx>,
239239
offset: Size,
240240
is_return: bool,
241+
drop_target_pointee: Option<Ty<'tcx>>,
241242
) {
242243
// Booleans are always a noundef i1 that needs to be zero-extended.
243244
if scalar.is_bool() {
@@ -251,14 +252,24 @@ fn adjust_for_rust_scalar<'tcx>(
251252
}
252253

253254
// Only pointer types handled below.
254-
let Scalar::Initialized { value: Pointer(_), valid_range} = scalar else { return };
255+
let Scalar::Initialized { value: Pointer(_), valid_range } = scalar else { return };
255256

256-
if !valid_range.contains(0) {
257+
// Set `nonnull` if the validity range excludes zero, or for the argument to `drop_in_place`,
258+
// which must be nonnull per its documented safety requirements.
259+
if !valid_range.contains(0) || drop_target_pointee.is_some() {
257260
attrs.set(ArgAttribute::NonNull);
258261
}
259262

260263
if let Some(pointee) = layout.pointee_info_at(&cx, offset) {
261-
if let Some(kind) = pointee.safe {
264+
let kind = if let Some(kind) = pointee.safe {
265+
Some(kind)
266+
} else if let Some(pointee) = drop_target_pointee {
267+
// The argument to `drop_in_place` is semantically equivalent to a mutable reference.
268+
Some(PointerKind::MutableRef { unpin: pointee.is_unpin(cx.tcx, cx.param_env()) })
269+
} else {
270+
None
271+
};
272+
if let Some(kind) = kind {
262273
attrs.pointee_align = Some(pointee.align);
263274

264275
// `Box` are not necessarily dereferenceable for the entire duration of the function as
@@ -362,10 +373,18 @@ fn fn_abi_new_uncached<'tcx>(
362373
use SpecAbi::*;
363374
let rust_abi = matches!(sig.abi, RustIntrinsic | PlatformIntrinsic | Rust | RustCall);
364375

376+
let is_drop_in_place =
377+
fn_def_id.is_some() && fn_def_id == cx.tcx.lang_items().drop_in_place_fn();
378+
365379
let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, FnAbiError<'tcx>> {
366380
let span = tracing::debug_span!("arg_of");
367381
let _entered = span.enter();
368382
let is_return = arg_idx.is_none();
383+
let is_drop_target = is_drop_in_place && arg_idx == Some(0);
384+
let drop_target_pointee = is_drop_target.then(|| match ty.kind() {
385+
ty::RawPtr(ty::TypeAndMut { ty, .. }) => *ty,
386+
_ => bug!("argument to drop_in_place is not a raw ptr: {:?}", ty),
387+
});
369388

370389
let layout = cx.layout_of(ty)?;
371390
let layout = if force_thin_self_ptr && arg_idx == Some(0) {
@@ -379,7 +398,15 @@ fn fn_abi_new_uncached<'tcx>(
379398

380399
let mut arg = ArgAbi::new(cx, layout, |layout, scalar, offset| {
381400
let mut attrs = ArgAttributes::new();
382-
adjust_for_rust_scalar(*cx, &mut attrs, scalar, *layout, offset, is_return);
401+
adjust_for_rust_scalar(
402+
*cx,
403+
&mut attrs,
404+
scalar,
405+
*layout,
406+
offset,
407+
is_return,
408+
drop_target_pointee,
409+
);
383410
attrs
384411
});
385412

library/core/src/ptr/mod.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -441,19 +441,25 @@ mod mut_ptr;
441441
///
442442
/// * `to_drop` must be [valid] for both reads and writes.
443443
///
444-
/// * `to_drop` must be properly aligned.
444+
/// * `to_drop` must be properly aligned, even if `T` has size 0.
445445
///
446-
/// * The value `to_drop` points to must be valid for dropping, which may mean it must uphold
447-
/// additional invariants - this is type-dependent.
446+
/// * `to_drop` must be nonnull, even if `T` has size 0.
447+
///
448+
/// * The value `to_drop` points to must be valid for dropping, which may mean
449+
/// it must uphold additional invariants. These invariants depend on the type
450+
/// of the value being dropped. For instance, when dropping a Box, the box's
451+
/// pointer to the heap must be valid.
452+
///
453+
/// * While `drop_in_place` is executing, the only way to access parts of
454+
/// `to_drop` is through the `&mut self` references supplied to the
455+
/// `Drop::drop` methods that `drop_in_place` invokes.
448456
///
449457
/// Additionally, if `T` is not [`Copy`], using the pointed-to value after
450458
/// calling `drop_in_place` can cause undefined behavior. Note that `*to_drop =
451459
/// foo` counts as a use because it will cause the value to be dropped
452460
/// again. [`write()`] can be used to overwrite data without causing it to be
453461
/// dropped.
454462
///
455-
/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned.
456-
///
457463
/// [valid]: self#safety
458464
///
459465
/// # Examples
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// compile-flags: -O -C no-prepopulate-passes
2+
3+
// Tests that the compiler can apply `noalias` and other &mut attributes to `drop_in_place`.
4+
// Note that non-Unpin types should not get `noalias`, matching &mut behavior.
5+
6+
#![crate_type="lib"]
7+
8+
use std::marker::PhantomPinned;
9+
10+
// CHECK: define internal void @{{.*}}core{{.*}}ptr{{.*}}drop_in_place{{.*}}StructUnpin{{.*}}({{.*\*|ptr}} noalias noundef align 4 dereferenceable(12) %{{.+}})
11+
12+
// CHECK: define internal void @{{.*}}core{{.*}}ptr{{.*}}drop_in_place{{.*}}StructNotUnpin{{.*}}({{.*\*|ptr}} noundef nonnull align 4 %{{.+}})
13+
14+
pub struct StructUnpin {
15+
a: i32,
16+
b: i32,
17+
c: i32,
18+
}
19+
20+
impl Drop for StructUnpin {
21+
fn drop(&mut self) {}
22+
}
23+
24+
pub struct StructNotUnpin {
25+
a: i32,
26+
b: i32,
27+
c: i32,
28+
p: PhantomPinned,
29+
}
30+
31+
impl Drop for StructNotUnpin {
32+
fn drop(&mut self) {}
33+
}
34+
35+
pub unsafe fn main(x: StructUnpin, y: StructNotUnpin) {
36+
drop(x);
37+
drop(y);
38+
}

tests/codegen/noalias-box-off.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@
44

55
// CHECK-LABEL: @box_should_not_have_noalias_if_disabled(
66
// CHECK-NOT: noalias
7+
// CHECK-SAME: %foo)
78
#[no_mangle]
8-
pub fn box_should_not_have_noalias_if_disabled(_b: Box<u8>) {}
9+
pub fn box_should_not_have_noalias_if_disabled(foo: Box<u8>) {
10+
drop(foo);
11+
}

0 commit comments

Comments
 (0)