Skip to content

Commit ddf6fe3

Browse files
committed
Implement layout_of_val in cg_ssa
It was already computing both anyway with `size_of_val::size_and_align_of_dst`, so we just need to return them both instead of ignoring one.
1 parent 74b8cf7 commit ddf6fe3

5 files changed

Lines changed: 116 additions & 19 deletions

File tree

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.

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}}}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes -Z inline-mir
2+
//@ only-64bit (so I don't need to worry about usize)
3+
//@ needs-deterministic-layouts
4+
5+
// Note that the layout algorithm currently puts the align before the size,
6+
// because the *type* for the size doesn't have a niche. This test may need
7+
// to be updated if the in-memory field order of `Layout` ever changes.
8+
9+
#![crate_type = "lib"]
10+
#![feature(core_intrinsics)]
11+
12+
use std::alloc::Layout;
13+
use std::intrinsics::layout_of_val;
14+
15+
// CHECK-LABEL: @thin_metadata(
16+
#[no_mangle]
17+
pub unsafe fn thin_metadata(ptr: *const [u32; 2]) -> Layout {
18+
// CHECK: [[LAYOUT:%.+]] = alloca [16 x i8], align 8
19+
// CHECK-NOT: load
20+
// CHECK-NOT: store
21+
// CHECK: store i64 4, ptr [[LAYOUT]], align 8
22+
// CHECK-NEXT: [[SIZEP:%.+]] = getelementptr inbounds i8, ptr [[LAYOUT]], i64 8
23+
// CHECK-NEXT: store i64 8, ptr [[SIZEP]], align 8
24+
// CHECK-NOT: store
25+
layout_of_val(ptr)
26+
}
27+
28+
// CHECK-LABEL: @slice_metadata(ptr noundef %ptr.0, i64 noundef %ptr.1)
29+
#[no_mangle]
30+
pub unsafe fn slice_metadata(ptr: *const [u32]) -> Layout {
31+
// CHECK: [[LAYOUT:%.+]] = alloca [16 x i8], align 8
32+
// CHECK-NOT: load
33+
// CHECK-NOT: store
34+
// CHECK: [[BYTES:%.+]] = mul nuw nsw i64 %ptr.1, 4
35+
// CHECK-NEXT: store i64 4, ptr [[LAYOUT]], align 8
36+
// CHECK-NEXT: [[SIZEP:%.+]] = getelementptr inbounds i8, ptr [[LAYOUT]], i64 8
37+
// CHECK-NEXT: store i64 [[BYTES]], ptr [[SIZEP]], align 8
38+
// CHECK-NOT: store
39+
layout_of_val(ptr)
40+
}
41+
42+
pub struct WithTail<T: ?Sized>([u32; 3], T);
43+
44+
// CHECK-LABEL: @dst_metadata
45+
// CHECK-SAME: (ptr noundef %ptr.0, ptr{{.+}}%ptr.1)
46+
#[no_mangle]
47+
pub unsafe fn dst_metadata(ptr: *const WithTail<dyn std::fmt::Debug>) -> Layout {
48+
// CHECK: [[LAYOUT:%.+]] = alloca [16 x i8], align 8
49+
// CHECK-NOT: load
50+
// CHECK-NOT: store
51+
// CHECK: [[DST_SIZEP:%.+]] = getelementptr inbounds i8, ptr %ptr.1, i64 8
52+
// CHECK-NEXT: [[DST_SIZE:%.+]] = load i64, ptr [[DST_SIZEP]], align 8,
53+
// CHECK-SAME: !range [[SIZE_RANGE:.+]], !invariant.load
54+
// CHECK-NEXT: [[DST_ALIGNP:%.+]] = getelementptr inbounds i8, ptr %ptr.1, i64 16
55+
// CHECK-NEXT: [[DST_ALIGN:%.+]] = load i64, ptr [[DST_ALIGNP]], align 8,
56+
// CHECK-SAME: !range [[ALIGN_RANGE:!.+]], !invariant.load
57+
58+
// CHECK-NEXT: [[STRUCT_MORE:%.+]] = icmp ugt i64 4, [[DST_ALIGN]]
59+
// CHECK-NEXT: [[ALIGN:%.+]] = select i1 [[STRUCT_MORE]], i64 4, i64 [[DST_ALIGN]]
60+
61+
// CHECK-NEXT: [[MINSIZE:%.+]] = add nuw nsw i64 12, [[DST_SIZE]]
62+
// CHECK-NEXT: [[ALIGN_M1:%.+]] = sub i64 [[ALIGN]], 1
63+
// CHECK-NEXT: [[MAXSIZE:%.+]] = add i64 [[MINSIZE]], [[ALIGN_M1]]
64+
// CHECK-NEXT: [[ALIGN_NEG:%.+]] = sub i64 0, [[ALIGN]]
65+
// CHECK-NEXT: [[SIZE:%.+]] = and i64 [[MAXSIZE]], [[ALIGN_NEG]]
66+
67+
// CHECK-NEXT: store i64 [[ALIGN]], ptr [[LAYOUT]], align 8
68+
// CHECK-NEXT: [[LAYOUT_SIZEP:%.+]] = getelementptr inbounds i8, ptr [[LAYOUT]], i64 8
69+
// CHECK-NEXT: store i64 [[SIZE]], ptr [[LAYOUT_SIZEP]], align 8
70+
// CHECK-NOT: store
71+
layout_of_val(ptr)
72+
}
73+
74+
// CHECK-LABEL: declare
75+
76+
// CHECK: [[ALIGN_RANGE]] = !{i64 1, i64 -[[#0x7FFFFFFFFFFFFFFF]]
77+
// CHECK: [[SIZE_RANGE]] = !{i64 0, i64 -[[#0x8000000000000000]]

0 commit comments

Comments
 (0)