Skip to content

Commit 42ba6e2

Browse files
committed
Auto merge of #152867 - 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 ef70767 + fb5b025 commit 42ba6e2

25 files changed

Lines changed: 435 additions & 359 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: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_abi::WrappingRange;
1+
use rustc_abi::{Align, FIRST_VARIANT, 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,39 @@ 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+
// The builder insulates us from in-memory order, but double-check declared order
161+
debug_assert!({
162+
let layout_adt = result.layout.ty.ty_adt_def().unwrap();
163+
let layout_fields = layout_adt.variant(FIRST_VARIANT).fields.as_slice();
164+
if let [size, align] = &layout_fields.raw
165+
&& size.name == sym::size
166+
&& align.name == sym::align
167+
{
168+
true
169+
} else {
170+
false
171+
}
172+
});
173+
174+
let mut builder = OperandRefBuilder::<'_, Bx::Value>::new(result.layout);
175+
builder.insert_imm(FieldIdx::from_u32(0), llsize);
176+
builder.insert_imm(FieldIdx::from_u32(1), llalign);
177+
let val = builder.build(bx.cx()).val;
178+
// the match can only return a single `Bx::Value`,
179+
// so we need to do the store and return.
180+
val.store(bx, result);
181+
return Ok(());
182+
}
183+
_ => bug!(),
184+
}
163185
}
164186
sym::vtable_size | sym::vtable_align => {
165187
let vtable = args[0].immediate();
@@ -179,9 +201,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
179201
let size_bound = bx.data_layout().ptr_sized_integer().signed_max() as u128;
180202
bx.range_metadata(value, WrappingRange { start: 0, end: size_bound });
181203
}
182-
// Alignment is always nonzero.
204+
// Alignment is always a power of two, thus 1..=0x800…000,
205+
// but also bounded by the maximum we support in type layout.
183206
sym::vtable_align => {
184-
bx.range_metadata(value, WrappingRange { start: 1, end: !0 })
207+
let align_bound = u128::min(
208+
bx.data_layout().ptr_sized_integer().signed_min() as u128,
209+
Align::MAX.bytes().into(),
210+
);
211+
bx.range_metadata(value, WrappingRange { start: 1, end: align_bound })
185212
}
186213
_ => {}
187214
}

compiler/rustc_codegen_ssa/src/size_of_val.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Computing the size and alignment of a value.
22
3-
use rustc_abi::WrappingRange;
3+
use rustc_abi::{Align, WrappingRange};
44
use rustc_hir::LangItem;
55
use rustc_middle::bug;
66
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
@@ -36,8 +36,10 @@ 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+
// but also bounded by the maximum we support in type layout.
41+
let align_bound = u128::min(size_bound + 1, Align::MAX.bytes().into());
42+
bx.range_metadata(align, WrappingRange { start: 1, end: align_bound });
4143

4244
(size, align)
4345
}
@@ -157,7 +159,12 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
157159
// Furthermore, `align >= unsized_align`, and therefore we only need to do:
158160
// let full_size = (unsized_offset_unadjusted + unsized_size).align_to(full_align);
159161

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

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

compiler/rustc_hir/src/lang_items.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ language_item_table! {
158158
Unsize, sym::unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1);
159159
AlignOf, sym::mem_align_const, align_const, Target::AssocConst, GenericRequirement::Exact(0);
160160
SizeOf, sym::mem_size_const, size_const, Target::AssocConst, GenericRequirement::Exact(0);
161+
LayoutOf, sym::mem_layout_const, layout_const, Target::AssocConst, GenericRequirement::Exact(0);
161162
OffsetOf, sym::offset_of, offset_of, Target::Fn, GenericRequirement::Exact(1);
162163
/// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ").
163164
StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None;

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_mir_transform/src/instsimplify.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,8 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
273273
LangItem::SizeOf
274274
} else if self.tcx.is_intrinsic(fn_def_id, sym::align_of_val) {
275275
LangItem::AlignOf
276+
} else if self.tcx.is_intrinsic(fn_def_id, sym::layout_of_val) {
277+
LangItem::LayoutOf
276278
} else {
277279
return;
278280
};

compiler/rustc_span/src/symbol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1352,6 +1352,7 @@ symbols! {
13521352
large_assignments,
13531353
last,
13541354
lateout,
1355+
layout_of_val,
13551356
lazy_normalization_consts,
13561357
lazy_type_alias,
13571358
le,
@@ -1453,6 +1454,7 @@ symbols! {
14531454
mem_discriminant,
14541455
mem_drop,
14551456
mem_forget,
1457+
mem_layout_const,
14561458
mem_replace,
14571459
mem_size_const,
14581460
mem_size_of,

library/core/src/alloc/layout.rs

Lines changed: 42 additions & 8 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};
@@ -26,6 +26,9 @@ use crate::{assert_unsafe_precondition, fmt, mem};
2626
/// requirements, or use the more lenient `Allocator` interface.)
2727
#[stable(feature = "alloc_layout", since = "1.28.0")]
2828
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
29+
// BEWARE! The implementation of the `layout_of_val` intrinsic is coupled to the
30+
// declared order of these fields. As a reminder, you'll also get a (debug-only)
31+
// ICE if you change their names, though you can easily update that expectation.
2932
#[lang = "alloc_layout"]
3033
pub struct Layout {
3134
// size of the requested block of memory, measured in bytes.
@@ -210,14 +213,23 @@ impl Layout {
210213
/// Produces layout describing a record that could be used to
211214
/// allocate backing structure for `T` (which could be a trait
212215
/// or other unsized type like a slice).
216+
///
217+
/// # Examples
218+
///
219+
/// ```
220+
/// use std::alloc::Layout;
221+
///
222+
/// let array = [1_u8, 2, 3];
223+
/// assert_eq!(Layout::for_value::<[u8]>(&array), Layout::from_size_align(3, 1).unwrap());
224+
/// ```
213225
#[stable(feature = "alloc_layout", since = "1.28.0")]
214226
#[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")]
215227
#[must_use]
216228
#[inline]
217229
pub const fn for_value<T: ?Sized>(t: &T) -> Self {
218-
let (size, alignment) = (size_of_val(t), Alignment::of_val(t));
219-
// SAFETY: see rationale in `new` for why this is using the unsafe variant
220-
unsafe { Layout::from_size_alignment_unchecked(size, alignment) }
230+
// SAFETY: val is a reference, so if it's to a DST it has valid metadata.
231+
// (And if `T` is sized there's no requirements on the pointer.)
232+
unsafe { Layout::for_value_raw(t) }
221233
}
222234

223235
/// Produces layout describing a record that could be used to
@@ -247,14 +259,36 @@ impl Layout {
247259
///
248260
/// [trait object]: ../../book/ch17-02-trait-objects.html
249261
/// [extern type]: ../../unstable-book/language-features/extern-types.html
262+
///
263+
/// # Examples
264+
///
265+
/// ```
266+
/// #![feature(layout_for_ptr)]
267+
///
268+
/// use std::alloc::Layout;
269+
/// use std::ptr;
270+
///
271+
/// let arbitrary = ptr::without_provenance::<[u16; 3]>(123456);
272+
/// assert_eq!(
273+
/// // SAFETY: for a sized pointee, the function is always sound.
274+
/// unsafe { Layout::for_value_raw(arbitrary) },
275+
/// Layout::from_size_align(6, 2).unwrap(),
276+
/// );
277+
///
278+
/// let slice = ptr::slice_from_raw_parts(arbitrary, 789);
279+
/// assert_eq!(
280+
/// // SAFETY: with a slice pointee, this is sound because the length
281+
/// // is short enough that size in bytes doesn't overflow isize::MAX.
282+
/// unsafe { Layout::for_value_raw(slice) },
283+
/// Layout::from_size_align(6 * 789, 2).unwrap(),
284+
/// );
285+
/// ```
250286
#[unstable(feature = "layout_for_ptr", issue = "69835")]
251287
#[must_use]
252288
#[inline]
253-
pub const unsafe fn for_value_raw<T: ?Sized>(t: *const T) -> Self {
289+
pub const unsafe fn for_value_raw<T: ?Sized>(ptr: *const T) -> Self {
254290
// 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) }
291+
unsafe { intrinsics::layout_of_val(ptr) }
258292
}
259293

260294
/// 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.

0 commit comments

Comments
 (0)