[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.
This commit is contained in:
parent
e77366b57b
commit
f71741bac4
@ -238,6 +238,7 @@ fn adjust_for_rust_scalar<'tcx>(
|
|||||||
layout: TyAndLayout<'tcx>,
|
layout: TyAndLayout<'tcx>,
|
||||||
offset: Size,
|
offset: Size,
|
||||||
is_return: bool,
|
is_return: bool,
|
||||||
|
is_drop_target: bool,
|
||||||
) {
|
) {
|
||||||
// Booleans are always a noundef i1 that needs to be zero-extended.
|
// Booleans are always a noundef i1 that needs to be zero-extended.
|
||||||
if scalar.is_bool() {
|
if scalar.is_bool() {
|
||||||
@ -307,6 +308,25 @@ fn adjust_for_rust_scalar<'tcx>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is the argument to `drop_in_place`, the contents of which we fully control as the
|
||||||
|
// compiler, then we may be able to mark that argument `noalias`. Currently, we're conservative
|
||||||
|
// and do so only if `drop_in_place` results in a direct call to the programmer's `drop` method.
|
||||||
|
// The `drop` method requires `&mut self`, so we're effectively just propagating the `noalias`
|
||||||
|
// guarantee from `drop` upward to `drop_in_place` in this case.
|
||||||
|
if is_drop_target {
|
||||||
|
match *layout.ty.kind() {
|
||||||
|
ty::RawPtr(inner) => {
|
||||||
|
if let ty::Adt(adt_def, _) = inner.ty.kind() {
|
||||||
|
if adt_def.destructor(cx.tcx()).is_some() {
|
||||||
|
debug!("marking drop_in_place argument as noalias");
|
||||||
|
attrs.set(ArgAttribute::NoAlias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => bug!("drop target isn't a raw pointer"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(eddyb) perhaps group the signature/type-containing (or all of them?)
|
// FIXME(eddyb) perhaps group the signature/type-containing (or all of them?)
|
||||||
@ -362,10 +382,16 @@ fn fn_abi_new_uncached<'tcx>(
|
|||||||
use SpecAbi::*;
|
use SpecAbi::*;
|
||||||
let rust_abi = matches!(sig.abi, RustIntrinsic | PlatformIntrinsic | Rust | RustCall);
|
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<usize>| -> Result<_, FnAbiError<'tcx>> {
|
let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, FnAbiError<'tcx>> {
|
||||||
let span = tracing::debug_span!("arg_of");
|
let span = tracing::debug_span!("arg_of");
|
||||||
let _entered = span.enter();
|
let _entered = span.enter();
|
||||||
let is_return = arg_idx.is_none();
|
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 = cx.layout_of(ty)?;
|
||||||
let layout = if force_thin_self_ptr && arg_idx == Some(0) {
|
let layout = if force_thin_self_ptr && arg_idx == Some(0) {
|
||||||
@ -379,7 +405,15 @@ fn fn_abi_new_uncached<'tcx>(
|
|||||||
|
|
||||||
let mut arg = ArgAbi::new(cx, layout, |layout, scalar, offset| {
|
let mut arg = ArgAbi::new(cx, layout, |layout, scalar, offset| {
|
||||||
let mut attrs = ArgAttributes::new();
|
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
|
attrs
|
||||||
});
|
});
|
||||||
|
|
||||||
|
34
tests/codegen/drop-in-place-noalias.rs
Normal file
34
tests/codegen/drop-in-place-noalias.rs
Normal file
@ -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{{.*}}drop_in_place{{.*}}Foo{{.*}}({{.*}}noalias{{.*}})
|
||||||
|
|
||||||
|
#[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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user