Skip to content

Commit 99105d5

Browse files
committed
Emit nofree attribute
Treat the semantics of pointee.size as "dereferenceable-at-point" and always specify the size. Instead, use a separate NoFree attribute to determine whether dereferenceability extends to the whole function. Then in the LLVM backend, only actually emit dereferenceable if nofree is also set, as dereferenceable currently implies nofree. In addition, explicitly emit the nofree attribute, which will help when LLVM switches dereferenceable to be at-point.
1 parent 0e5924a commit 99105d5

22 files changed

Lines changed: 92 additions & 92 deletions

compiler/rustc_codegen_llvm/src/abi.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,13 @@ trait ArgAttributesExt {
3838
const ABI_AFFECTING_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 1] =
3939
[(ArgAttribute::InReg, llvm::AttributeKind::InReg)];
4040

41-
const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 5] = [
41+
const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 6] = [
4242
(ArgAttribute::NoAlias, llvm::AttributeKind::NoAlias),
4343
(ArgAttribute::NonNull, llvm::AttributeKind::NonNull),
4444
(ArgAttribute::ReadOnly, llvm::AttributeKind::ReadOnly),
4545
(ArgAttribute::NoUndef, llvm::AttributeKind::NoUndef),
4646
(ArgAttribute::Writable, llvm::AttributeKind::Writable),
47+
(ArgAttribute::NoFree, llvm::AttributeKind::NoFree),
4748
];
4849

4950
const CAPTURES_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 3] = [
@@ -75,7 +76,9 @@ fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'
7576
// Only apply remaining attributes when optimizing
7677
if cx.sess().opts.optimize != config::OptLevel::No {
7778
let deref = this.pointee_size.bytes();
78-
if deref != 0 {
79+
// dereferenceable in LLVM currently implies nofree, so only emit dereferenceable if nofree
80+
// is also set.
81+
if deref != 0 && regular.contains(ArgAttribute::NoFree) {
7982
if regular.contains(ArgAttribute::NonNull) {
8083
attrs.push(llvm::CreateDereferenceableAttr(cx.llcx, deref));
8184
} else {

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ pub(crate) enum AttributeKind {
294294
SanitizeRealtimeNonblocking = 47,
295295
SanitizeRealtimeBlocking = 48,
296296
Convergent = 49,
297+
NoFree = 50,
297298
}
298299

299300
/// LLVMIntPredicate

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ enum class LLVMRustAttributeKind {
383383
SanitizeRealtimeNonblocking = 47,
384384
SanitizeRealtimeBlocking = 48,
385385
Convergent = 49,
386+
NoFree = 50,
386387
};
387388

388389
static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
@@ -481,6 +482,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
481482
return Attribute::SanitizeRealtimeBlocking;
482483
case LLVMRustAttributeKind::Convergent:
483484
return Attribute::Convergent;
485+
case LLVMRustAttributeKind::NoFree:
486+
return Attribute::NoFree;
484487
}
485488
report_fatal_error("bad LLVMRustAttributeKind");
486489
}

compiler/rustc_middle/src/ty/layout.rs

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -979,33 +979,19 @@ where
979979
}
980980
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
981981
tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| {
982-
let (size, kind);
983-
match mt {
982+
let kind = match mt {
984983
hir::Mutability::Not => {
985984
let frozen = optimize && ty.is_freeze(tcx, typing_env);
986-
987-
// Non-frozen shared references are not necessarily dereferenceable for the entire duration of the function
988-
// (see <https://github.com/rust-lang/rust/pull/98017>)
989-
// (if we had "dereferenceable on entry", we could support this)
990-
size = if frozen { layout.size } else { Size::ZERO };
991-
992-
kind = PointerKind::SharedRef { frozen };
985+
PointerKind::SharedRef { frozen }
993986
}
994987
hir::Mutability::Mut => {
995988
let unpin = optimize
996989
&& ty.is_unpin(tcx, typing_env)
997990
&& ty.is_unsafe_unpin(tcx, typing_env);
998-
999-
// Mutable references to potentially self-referential types are not
1000-
// necessarily dereferenceable for the entire duration of the function
1001-
// (see <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>)
1002-
// (if we had "dereferenceable on entry", we could support this)
1003-
size = if unpin { layout.size } else { Size::ZERO };
1004-
1005-
kind = PointerKind::MutableRef { unpin };
991+
PointerKind::MutableRef { unpin }
1006992
}
1007993
};
1008-
PointeeInfo { safe: Some(kind), size, align: layout.align.abi }
994+
PointeeInfo { safe: Some(kind), size: layout.size, align: layout.align.abi }
1009995
})
1010996
}
1011997

@@ -1021,12 +1007,7 @@ where
10211007
&& pointee.is_unsafe_unpin(tcx, typing_env),
10221008
global: this.ty.is_box_global(tcx),
10231009
}),
1024-
1025-
// `Box` are not necessarily dereferenceable for the entire duration of the function as
1026-
// they can be deallocated at any time.
1027-
// (if we had "dereferenceable on entry", we could support this)
1028-
size: Size::ZERO,
1029-
1010+
size: layout.size,
10301011
align: layout.align.abi,
10311012
})
10321013
}

compiler/rustc_target/src/callconv/mod.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ mod attr_impl {
122122
const InReg = 1 << 6;
123123
const NoUndef = 1 << 7;
124124
const Writable = 1 << 8;
125+
const NoFree = 1 << 9;
125126
}
126127
}
127128
rustc_data_structures::external_bitflags_debug! { ArgAttribute }
@@ -143,9 +144,8 @@ pub enum ArgExtension {
143144
pub struct ArgAttributes {
144145
pub regular: ArgAttribute,
145146
pub arg_ext: ArgExtension,
146-
/// The minimum size of the pointee, guaranteed to be valid for the duration of the whole call
147-
/// (corresponding to LLVM's dereferenceable_or_null attributes, i.e., it is okay for this to be
148-
/// set on a null pointer, but all non-null pointers must be dereferenceable).
147+
/// If the pointer is not null, the minimum dereferenceable size of the pointee, at the time of
148+
/// function entry (for arguments) or function return (for return values).
149149
pub pointee_size: Size,
150150
/// The minimum alignment of the pointee, if any.
151151
pub pointee_align: Option<Align>,
@@ -408,7 +408,8 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
408408
.set(ArgAttribute::NoAlias)
409409
.set(ArgAttribute::CapturesAddress)
410410
.set(ArgAttribute::NonNull)
411-
.set(ArgAttribute::NoUndef);
411+
.set(ArgAttribute::NoUndef)
412+
.set(ArgAttribute::NoFree);
412413
attrs.pointee_size = layout.size;
413414
attrs.pointee_align = Some(layout.align.abi);
414415

compiler/rustc_ty_utils/src/abi.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -356,13 +356,7 @@ fn arg_attrs_for_rust_scalar<'tcx>(
356356
Some(pointee.align.min(cx.tcx().sess.target.max_reliable_alignment()));
357357
}
358358

359-
// LLVM dereferenceable attribute has unclear semantics on the return type,
360-
// they seem to be "dereferenceable until the end of the program", which is
361-
// generally, not valid for references. See
362-
// <https://rust-lang.zulipchat.com/#narrow/channel/136281-t-opsem/topic/LLVM.20dereferenceable.20on.20return.20type/with/563001493>
363-
if !is_return {
364-
attrs.pointee_size = pointee.size;
365-
};
359+
attrs.pointee_size = pointee.size;
366360

367361
if let Some(kind) = pointee.safe {
368362
// The aliasing rules for `Box<T>` are still not decided, but currently we emit
@@ -407,6 +401,23 @@ fn arg_attrs_for_rust_scalar<'tcx>(
407401
}
408402
}
409403

404+
let no_free = !is_return
405+
&& match kind {
406+
// Non-frozen shared references are not necessarily dereferenceable for the
407+
// entire duration of the function
408+
// (see <https://github.com/rust-lang/rust/pull/98017>).
409+
PointerKind::SharedRef { frozen } => frozen,
410+
// Mutable references to potentially self-referential types are not necessarily
411+
// dereferenceable for the entire duration of the function
412+
// (see <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>).
413+
PointerKind::MutableRef { unpin } => unpin,
414+
// Box may be deallocated during execution of the function.
415+
PointerKind::Box { .. } => false,
416+
};
417+
if no_free {
418+
attrs.set(ArgAttribute::NoFree);
419+
}
420+
410421
if matches!(kind, PointerKind::SharedRef { frozen: true }) && !is_return {
411422
attrs.set(ArgAttribute::ReadOnly);
412423
attrs.set(ArgAttribute::CapturesReadOnly);

tests/codegen-llvm/addr-of-mutate.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// Test for the absence of `readonly` on the argument when it is mutated via `&raw const`.
66
// See <https://github.com/rust-lang/rust/issues/111502>.
77

8-
// CHECK: i8 @foo(ptr{{( dead_on_return)?}} noalias noundef align 1{{( captures\(address\))?}}{{( dead_on_return)?}} dereferenceable(128) %x)
8+
// CHECK: i8 @foo(ptr{{( dead_on_return)?}} noalias nofree noundef align 1{{( captures\(address\))?}}{{( dead_on_return)?}} dereferenceable(128) %x)
99
#[no_mangle]
1010
pub fn foo(x: [u8; 128]) -> u8 {
1111
let ptr = core::ptr::addr_of!(x).cast_mut();
@@ -15,7 +15,7 @@ pub fn foo(x: [u8; 128]) -> u8 {
1515
x[0]
1616
}
1717

18-
// CHECK: i1 @second(ptr{{( dead_on_return)?}} noalias noundef align {{[0-9]+}}{{( captures\(address\))?}}{{( dead_on_return)?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b)
18+
// CHECK: i1 @second(ptr{{( dead_on_return)?}} noalias nofree noundef align {{[0-9]+}}{{( captures\(address\))?}}{{( dead_on_return)?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b)
1919
#[no_mangle]
2020
pub unsafe fn second(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool {
2121
let b_bool_ptr = core::ptr::addr_of!(a_ptr_and_b.1.1).cast_mut();
@@ -24,7 +24,7 @@ pub unsafe fn second(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool {
2424
}
2525

2626
// If going through a deref (and there are no other mutating accesses), then `readonly` is fine.
27-
// CHECK: i1 @third(ptr{{( dead_on_return)?}} noalias noundef readonly align {{[0-9]+}}{{( captures\(none\))?}}{{( dead_on_return)?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b)
27+
// CHECK: i1 @third(ptr{{( dead_on_return)?}} noalias nofree noundef readonly align {{[0-9]+}}{{( captures\(none\))?}}{{( dead_on_return)?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b)
2828
#[no_mangle]
2929
pub unsafe fn third(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool {
3030
let b_bool_ptr = core::ptr::addr_of!((*a_ptr_and_b.0).1).cast_mut();

tests/codegen-llvm/drop-in-place-noalias.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
use std::marker::PhantomPinned;
99

10-
// CHECK: define internal void @{{.*}}core{{.*}}ptr{{.*}}drop_in_place{{.*}}StructUnpin{{.*}}(ptr noalias noundef align 4 dereferenceable(12) %{{.+}})
10+
// CHECK: define internal void @{{.*}}core{{.*}}ptr{{.*}}drop_in_place{{.*}}StructUnpin{{.*}}(ptr noalias nofree noundef align 4 dereferenceable(12) %{{.+}})
1111

1212
// CHECK: define internal void @{{.*}}core{{.*}}ptr{{.*}}drop_in_place{{.*}}StructNotUnpin{{.*}}(ptr noundef nonnull align 4 %{{.+}})
1313

0 commit comments

Comments
 (0)