Skip to content

Commit 5ce1a00

Browse files
committed
[rustc_ty_utils] Add the LLVM noalias parameter attribute to drop_in_place in certain cases.
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 the case of a value that has a programmer-defined Drop implementation, we know that the first thing `drop_in_place` will do is pass a pointer to the object to `Drop::drop`. `Drop::drop` takes `&mut`, so it must be guaranteed that there are no pointers to the object upon entering that function. Therefore, it should be safe to mark `noalias` there. With this patch, we mark `noalias` only when the type is a value with a programmer-defined Drop implementation. This is possibly overly conservative, but I thought that proceeding cautiously was best in this instance.
1 parent b6097f2 commit 5ce1a00

File tree

2 files changed

+69
-1
lines changed

2 files changed

+69
-1
lines changed

compiler/rustc_ty_utils/src/abi.rs

+35-1
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ fn adjust_for_rust_scalar<'tcx>(
199199
layout: TyAndLayout<'tcx>,
200200
offset: Size,
201201
is_return: bool,
202+
is_drop_target: bool,
202203
) {
203204
// Booleans are always a noundef i1 that needs to be zero-extended.
204205
if scalar.is_bool() {
@@ -276,6 +277,25 @@ fn adjust_for_rust_scalar<'tcx>(
276277
}
277278
}
278279
}
280+
281+
// If this is the argument to `drop_in_place`, the contents of which we fully control as the
282+
// compiler, then we may be able to mark that argument `noalias`. Currently, we're conservative
283+
// and do so only if `drop_in_place` results in a direct call to the programmer's `drop` method.
284+
// The `drop` method requires `&mut self`, so we're effectively just propagating the `noalias`
285+
// guarantee from `drop` upward to `drop_in_place` in this case.
286+
if is_drop_target {
287+
match *layout.ty.kind() {
288+
ty::RawPtr(inner) => {
289+
if let ty::Adt(adt_def, _) = inner.ty.kind() {
290+
if adt_def.destructor(cx.tcx()).is_some() {
291+
debug!("marking drop_in_place argument as noalias");
292+
attrs.set(ArgAttribute::NoAlias);
293+
}
294+
}
295+
}
296+
_ => bug!("drop target isn't a raw pointer"),
297+
}
298+
}
279299
}
280300

281301
// FIXME(eddyb) perhaps group the signature/type-containing (or all of them?)
@@ -331,10 +351,16 @@ fn fn_abi_new_uncached<'tcx>(
331351
use SpecAbi::*;
332352
let rust_abi = matches!(sig.abi, RustIntrinsic | PlatformIntrinsic | Rust | RustCall);
333353

354+
let is_drop_in_place = match (cx.tcx.lang_items().drop_in_place_fn(), fn_def_id) {
355+
(Some(drop_in_place_fn), Some(fn_def_id)) => drop_in_place_fn == fn_def_id,
356+
_ => false,
357+
};
358+
334359
let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, FnAbiError<'tcx>> {
335360
let span = tracing::debug_span!("arg_of");
336361
let _entered = span.enter();
337362
let is_return = arg_idx.is_none();
363+
let is_drop_target = is_drop_in_place && arg_idx == Some(0);
338364

339365
let layout = cx.layout_of(ty)?;
340366
let layout = if force_thin_self_ptr && arg_idx == Some(0) {
@@ -348,7 +374,15 @@ fn fn_abi_new_uncached<'tcx>(
348374

349375
let mut arg = ArgAbi::new(cx, layout, |layout, scalar, offset| {
350376
let mut attrs = ArgAttributes::new();
351-
adjust_for_rust_scalar(*cx, &mut attrs, scalar, *layout, offset, is_return);
377+
adjust_for_rust_scalar(
378+
*cx,
379+
&mut attrs,
380+
scalar,
381+
*layout,
382+
offset,
383+
is_return,
384+
is_drop_target,
385+
);
352386
attrs
353387
});
354388

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Tests that the compiler can mark `drop_in_place` as `noalias` when safe to do so.
2+
3+
#![crate_type="lib"]
4+
5+
use std::hint::black_box;
6+
7+
// CHECK: define{{.*}}drop_in_place{{.*}}Foo{{.*}}({{.*}}noalias{{.*}})
8+
9+
#[repr(C)]
10+
pub struct Foo {
11+
a: i32,
12+
b: i32,
13+
c: i32,
14+
}
15+
16+
impl Drop for Foo {
17+
#[inline(never)]
18+
fn drop(&mut self) {
19+
black_box(self.a);
20+
}
21+
}
22+
23+
extern {
24+
fn bar();
25+
fn baz(foo: Foo);
26+
}
27+
28+
pub fn haha() {
29+
let foo = Foo { a: 1, b: 2, c: 3 };
30+
unsafe {
31+
bar();
32+
baz(foo);
33+
}
34+
}

0 commit comments

Comments
 (0)