Skip to content

Commit 20e17c0

Browse files
committed
Auto merge of #152786 - scottmcm:layout_of_val, r=<try>
Add a `layout_of_val` intrinsic for when you want both `size_of_val`+`align_of_val`
2 parents c043085 + 3ecb0bc commit 20e17c0

18 files changed

Lines changed: 271 additions & 466 deletions

compiler/rustc_codegen_llvm/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,12 @@ impl CodegenBackend for LlvmCodegenBackend {
354354
}
355355

356356
fn replaced_intrinsics(&self) -> Vec<Symbol> {
357-
let mut will_not_use_fallback =
358-
vec![sym::unchecked_funnel_shl, sym::unchecked_funnel_shr, sym::carrying_mul_add];
357+
let mut will_not_use_fallback = vec![
358+
sym::unchecked_funnel_shl,
359+
sym::unchecked_funnel_shr,
360+
sym::carrying_mul_add,
361+
sym::layout_of_val,
362+
];
359363

360364
if llvm_util::get_version() >= (22, 0, 0) {
361365
will_not_use_fallback.push(sym::carryless_mul);

compiler/rustc_codegen_ssa/src/mir/intrinsic.rs

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_abi::WrappingRange;
1+
use rustc_abi::{FieldIdx, WrappingRange};
22
use rustc_middle::mir::SourceInfo;
33
use rustc_middle::ty::{self, Ty, TyCtxt};
44
use rustc_middle::{bug, span_bug};
@@ -7,7 +7,7 @@ use rustc_span::sym;
77
use rustc_target::spec::Arch;
88

99
use super::FunctionCx;
10-
use super::operand::OperandRef;
10+
use super::operand::{OperandRef, OperandRefBuilder};
1111
use super::place::PlaceRef;
1212
use crate::common::{AtomicRmwBinOp, SynchronizationScope};
1313
use crate::errors::InvalidMonomorphization;
@@ -149,17 +149,26 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
149149

150150
sym::va_start => bx.va_start(args[0].immediate()),
151151
sym::va_end => bx.va_end(args[0].immediate()),
152-
sym::size_of_val => {
152+
sym::size_of_val | sym::align_of_val | sym::layout_of_val => {
153153
let tp_ty = fn_args.type_at(0);
154154
let (_, meta) = args[0].val.pointer_parts();
155-
let (llsize, _) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta);
156-
llsize
157-
}
158-
sym::align_of_val => {
159-
let tp_ty = fn_args.type_at(0);
160-
let (_, meta) = args[0].val.pointer_parts();
161-
let (_, llalign) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta);
162-
llalign
155+
let (llsize, llalign) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta);
156+
match name {
157+
sym::size_of_val => llsize,
158+
sym::align_of_val => llalign,
159+
sym::layout_of_val => {
160+
// Use the builder so we're insulated from the in-memory field order
161+
let mut builder = OperandRefBuilder::<'_, Bx::Value>::new(result.layout);
162+
builder.insert_imm(FieldIdx::from_u32(0), llsize);
163+
builder.insert_imm(FieldIdx::from_u32(1), llalign);
164+
let val = builder.build(bx.cx()).val;
165+
// the match can only return a single `Bx::Value`,
166+
// so we need to do the store and return.
167+
val.store(bx, result);
168+
return Ok(());
169+
}
170+
_ => bug!(),
171+
}
163172
}
164173
sym::vtable_size | sym::vtable_align => {
165174
let vtable = args[0].immediate();
@@ -179,9 +188,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
179188
let size_bound = bx.data_layout().ptr_sized_integer().signed_max() as u128;
180189
bx.range_metadata(value, WrappingRange { start: 0, end: size_bound });
181190
}
182-
// Alignment is always nonzero.
191+
// Alignment is always a power of two, thus 1..=0x800…000.
183192
sym::vtable_align => {
184-
bx.range_metadata(value, WrappingRange { start: 1, end: !0 })
193+
let align_bound = bx.data_layout().ptr_sized_integer().signed_min() as u128;
194+
bx.range_metadata(value, WrappingRange { start: 1, end: align_bound })
185195
}
186196
_ => {}
187197
}

compiler/rustc_codegen_ssa/src/size_of_val.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
3636
// Size is always <= isize::MAX.
3737
let size_bound = bx.data_layout().ptr_sized_integer().signed_max() as u128;
3838
bx.range_metadata(size, WrappingRange { start: 0, end: size_bound });
39-
// Alignment is always nonzero.
40-
bx.range_metadata(align, WrappingRange { start: 1, end: !0 });
39+
// Alignment is always a power of two, thus 1..=0x800…000.
40+
let align_bound = size_bound + 1;
41+
bx.range_metadata(align, WrappingRange { start: 1, end: align_bound });
4142

4243
(size, align)
4344
}
@@ -157,7 +158,12 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
157158
// Furthermore, `align >= unsized_align`, and therefore we only need to do:
158159
// let full_size = (unsized_offset_unadjusted + unsized_size).align_to(full_align);
159160

160-
let full_size = bx.add(unsized_offset_unadjusted, unsized_size);
161+
// This is the size *before* rounding up, which cannot exceed the size *after*
162+
// rounding up, which itself cannot exceed `isize::MAX`. Thus the addition
163+
// itself cannot overflow `isize::MAX`, let alone `usize::MAX`.
164+
// (The range attribute from loading the size from the vtable is enough to prove
165+
// `nuw`, but not `nsw`, which we only know from Rust's layout rules.)
166+
let full_size = bx.unchecked_suadd(unsized_offset_unadjusted, unsized_size);
161167

162168
// Issue #27023: must add any necessary padding to `size`
163169
// (to make it a multiple of `align`) before returning it.

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,9 @@ pub(crate) fn check_intrinsic_type(
296296
sym::size_of_val | sym::align_of_val => {
297297
(1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize)
298298
}
299+
sym::layout_of_val => {
300+
(1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.ty_alloc_layout(span))
301+
}
299302
sym::offset_of => (1, 0, vec![tcx.types.u32, tcx.types.u32], tcx.types.usize),
300303
sym::rustc_peek => (1, 0, vec![param(0)], param(0)),
301304
sym::caller_location => (0, 0, vec![], tcx.caller_location_ty()),

compiler/rustc_middle/src/ty/context.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,6 +1079,12 @@ impl<'tcx> TyCtxt<'tcx> {
10791079
self.type_of(ordering_enum).no_bound_vars().unwrap()
10801080
}
10811081

1082+
/// Gets a `Ty` representing the [`LangItem::AllocLayout`]
1083+
pub fn ty_alloc_layout(self, span: Span) -> Ty<'tcx> {
1084+
let layout_did = self.require_lang_item(hir::LangItem::AllocLayout, span);
1085+
self.type_of(layout_did).no_bound_vars().unwrap()
1086+
}
1087+
10821088
/// Obtain the given diagnostic item's `DefId`. Use `is_diagnostic_item` if you just want to
10831089
/// compare against another `DefId`, since `is_diagnostic_item` is cheaper.
10841090
pub fn get_diagnostic_item(self, name: Symbol) -> Option<DefId> {

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,6 +1347,7 @@ symbols! {
13471347
large_assignments,
13481348
last,
13491349
lateout,
1350+
layout_of_val,
13501351
lazy_normalization_consts,
13511352
lazy_type_alias,
13521353
le,

library/alloc/src/boxed.rs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1923,12 +1923,27 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Box<T, A> {
19231923
fn drop(&mut self) {
19241924
// the T in the Box is dropped by the compiler before the destructor is run
19251925

1926-
let ptr = self.0;
1927-
1926+
let ptr = Box::as_ptr(self);
1927+
// SAFETY: By type invariants, this is the allocator and layout for the pointer.
19281928
unsafe {
1929-
let layout = Layout::for_value_raw(ptr.as_ptr());
1930-
if layout.size() != 0 {
1931-
self.1.deallocate(From::from(ptr.cast()), layout);
1929+
box_drop_inner(&self.1, ptr as *const u8, Layout::for_value_raw(ptr));
1930+
}
1931+
1932+
/// # Safety
1933+
/// If `layout` is non-ZST, then `ptr` must have been allocated
1934+
/// with compatible `layout` by `a`.
1935+
/// (or `layout` is a ZST)
1936+
// This is intentionally polymorphized, like `box_new_uninit`, so we
1937+
// keep it from inlining in MIR, and leave it up to the backend to
1938+
// decide whether it's actually worth inlining.
1939+
#[rustc_no_mir_inline]
1940+
#[inline]
1941+
unsafe fn box_drop_inner(a: &impl Allocator, ptr: *const u8, layout: Layout) {
1942+
// SAFETY: Obligations propagated to the caller
1943+
unsafe {
1944+
if layout.size() != 0 {
1945+
a.deallocate(NonNull::new_unchecked(ptr.cast_mut()), layout);
1946+
}
19321947
}
19331948
}
19341949
}

library/core/src/alloc/layout.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// Your performance intuition is useless. Run perf.
66

77
use crate::error::Error;
8-
use crate::intrinsics::{unchecked_add, unchecked_mul, unchecked_sub};
8+
use crate::intrinsics::{self, unchecked_add, unchecked_mul, unchecked_sub};
99
use crate::mem::SizedTypeProperties;
1010
use crate::ptr::{Alignment, NonNull};
1111
use crate::{assert_unsafe_precondition, fmt, mem};
@@ -250,11 +250,9 @@ impl Layout {
250250
#[unstable(feature = "layout_for_ptr", issue = "69835")]
251251
#[must_use]
252252
#[inline]
253-
pub const unsafe fn for_value_raw<T: ?Sized>(t: *const T) -> Self {
253+
pub const unsafe fn for_value_raw<T: ?Sized>(ptr: *const T) -> Self {
254254
// SAFETY: we pass along the prerequisites of these functions to the caller
255-
let (size, alignment) = unsafe { (mem::size_of_val_raw(t), Alignment::of_val_raw(t)) };
256-
// SAFETY: see rationale in `new` for why this is using the unsafe variant
257-
unsafe { Layout::from_size_alignment_unchecked(size, alignment) }
255+
unsafe { intrinsics::layout_of_val(ptr) }
258256
}
259257

260258
/// Creates a `NonNull` that is dangling, but well-aligned for this Layout.

library/core/src/intrinsics/mod.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
issue = "none"
5454
)]
5555

56+
use crate::alloc::Layout;
5657
use crate::ffi::va_list::{VaArgSafe, VaList};
5758
use crate::marker::{ConstParamTy, DiscriminantKind, PointeeSized, Tuple};
5859
use crate::{mem, ptr};
@@ -2864,6 +2865,26 @@ pub const unsafe fn size_of_val<T: ?Sized>(ptr: *const T) -> usize;
28642865
#[rustc_intrinsic_const_stable_indirect]
28652866
pub const unsafe fn align_of_val<T: ?Sized>(ptr: *const T) -> usize;
28662867

2868+
/// The size and alignment of the referenced value in bytes.
2869+
///
2870+
/// The stabilized version of this intrinsic is [`Layout::for_value_raw`].
2871+
///
2872+
/// # Safety
2873+
///
2874+
/// See [`Layout::for_value_raw`] for safety conditions.
2875+
#[rustc_nounwind]
2876+
#[unstable(feature = "core_intrinsics", issue = "none")]
2877+
#[rustc_intrinsic]
2878+
// This adds no semantics or UB atop just calling `size_of_val`+`align_of_val`.
2879+
#[miri::intrinsic_fallback_is_spec]
2880+
pub const unsafe fn layout_of_val<T: ?Sized>(ptr: *const T) -> Layout {
2881+
// SAFETY: we pass along the prerequisites of these functions to the caller
2882+
let (size, align) = unsafe { (size_of_val(ptr), align_of_val(ptr)) };
2883+
// SAFETY: The size and alignment of a valid allocation (or type)
2884+
// always meet the requirements of `Layout`.
2885+
unsafe { Layout::from_size_align_unchecked(size, align) }
2886+
}
2887+
28672888
/// Compute the type information of a concrete type.
28682889
/// It can only be called at compile time, the backends do
28692890
/// not implement it.

tests/codegen-llvm/dst-vtable-align-nonzero.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,4 @@ pub unsafe fn align_load_from_vtable_align_intrinsic(x: &dyn Trait) -> usize {
6464
core::intrinsics::vtable_align(vtable)
6565
}
6666

67-
// CHECK: [[RANGE_META]] = !{[[USIZE]] 1, [[USIZE]] 0}
67+
// CHECK: [[RANGE_META]] = !{[[USIZE]] 1, [[USIZE]] {{-2147483647|-9223372036854775807}}}

0 commit comments

Comments
 (0)