Skip to content

Commit fef7d19

Browse files
committed
Adjust the alignment when passing a niche as a pointer
1 parent 88f3114 commit fef7d19

File tree

3 files changed

+153
-6
lines changed

3 files changed

+153
-6
lines changed

compiler/rustc_abi/src/lib.rs

+23
Original file line numberDiff line numberDiff line change
@@ -1489,6 +1489,29 @@ pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> {
14891489
},
14901490
}
14911491

1492+
impl<FieldIdx: Idx, VariantIdx: Idx> Variants<FieldIdx, VariantIdx> {
1493+
// Returns niches for the discriminants.
1494+
pub fn niches(&self) -> Option<impl Iterator<Item = u128> + '_> {
1495+
match self {
1496+
&Variants::Multiple {
1497+
tag_encoding:
1498+
TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start },
1499+
ref variants,
1500+
..
1501+
} => Some(variants.iter_enumerated().filter_map(move |(variant_idx, variant)| {
1502+
if untagged_variant == variant_idx || variant.abi.is_uninhabited() {
1503+
None
1504+
} else {
1505+
let niche = ((variant_idx.index() - niche_variants.start().index()) as u128)
1506+
.wrapping_add(niche_start);
1507+
Some(niche)
1508+
}
1509+
})),
1510+
_ => None,
1511+
}
1512+
}
1513+
}
1514+
14921515
// NOTE: This struct is generic over the VariantIdx for rust-analyzer usage.
14931516
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
14941517
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]

compiler/rustc_middle/src/ty/layout.rs

+17-6
Original file line numberDiff line numberDiff line change
@@ -999,7 +999,7 @@ where
999999
}
10001000

10011001
_ => {
1002-
let mut data_variant = match this.variants {
1002+
let (mut data_variant, niche_align) = match this.variants {
10031003
// Within the discriminant field, only the niche itself is
10041004
// always initialized, so we only check for a pointer at its
10051005
// offset.
@@ -1016,9 +1016,16 @@ where
10161016
tag_field,
10171017
..
10181018
} if this.fields.offset(tag_field) == offset => {
1019-
Some(this.for_variant(cx, untagged_variant))
1019+
// When a non-null niche is passed in, we might pass an unaligned value.
1020+
// Calculates a maximum alignment that matches all niches.
1021+
let niche_align = this.variants.niches().map(|niches| {
1022+
niches.fold(Align::MAX, |align, niche| {
1023+
align.restrict_for_offset(Size::from_bytes(niche))
1024+
})
1025+
});
1026+
(Some(this.for_variant(cx, untagged_variant)), niche_align)
10201027
}
1021-
_ => Some(this),
1028+
_ => (Some(this), None),
10221029
};
10231030

10241031
if let Some(variant) = data_variant {
@@ -1055,20 +1062,24 @@ where
10551062
}
10561063
}
10571064

1058-
// Fixup info for the first field of a `Box`. Recursive traversal will have found
1059-
// the raw pointer, so size and align are set to the boxed type, but `pointee.safe`
1060-
// will still be `None`.
10611065
if let Some(ref mut pointee) = result {
10621066
if offset.bytes() == 0
10631067
&& let Some(boxed_ty) = this.ty.boxed_ty()
10641068
{
1069+
// Fixup info for the first field of a `Box`. Recursive traversal will have found
1070+
// the raw pointer, so size and align are set to the boxed type, but `pointee.safe`
1071+
// will still be `None`.
10651072
debug_assert!(pointee.safe.is_none());
10661073
let optimize = tcx.sess.opts.optimize != OptLevel::No;
10671074
pointee.safe = Some(PointerKind::Box {
10681075
unpin: optimize && boxed_ty.is_unpin(tcx, cx.param_env()),
10691076
global: this.ty.is_box_global(tcx),
10701077
});
10711078
}
1079+
if let Some(align) = niche_align {
1080+
// Takes the minimum alignment of the pointer and niches to ensure that the alignment is legal.
1081+
pointee.align = pointee.align.min(align);
1082+
}
10721083
}
10731084

10741085
result
+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//@ compile-flags: -Cno-prepopulate-passes -O
2+
//@ only-64bit (I don't care about alignment under different bits)
3+
4+
// Testing different niches updates to the corresponding alignment.
5+
6+
#![crate_type = "lib"]
7+
#![feature(rustc_attrs)]
8+
#![feature(never_type)]
9+
10+
#[rustc_layout_scalar_valid_range_start(0)]
11+
#[rustc_layout_scalar_valid_range_end(0x7fff)]
12+
struct RestrictedAddress_0_0x7fff(&'static i64);
13+
14+
#[rustc_layout_scalar_valid_range_start(1)]
15+
#[rustc_layout_scalar_valid_range_end(0x7fff)]
16+
struct RestrictedAddress_1_0x7fff(&'static i64);
17+
18+
#[rustc_layout_scalar_valid_range_start(0)]
19+
#[rustc_layout_scalar_valid_range_end(0xf000)]
20+
struct RestrictedAddress_0_0xf000(&'static i64);
21+
22+
enum MultipleAlign8 {
23+
Untag(RestrictedAddress_1_0x7fff),
24+
Niche_32768,
25+
Uninhabited_1(!),
26+
Uninhabited_2(!),
27+
Uninhabited_3(!),
28+
Uninhabited_4(!),
29+
Uninhabited_5(!),
30+
Uninhabited_6(!),
31+
Uninhabited_7(!),
32+
Niche_32776,
33+
}
34+
35+
// CHECK-LABEL: @multiple_niches_align_8
36+
// CHECK-SAME: align 8 {{.*}}%a)
37+
#[no_mangle]
38+
#[inline(never)]
39+
fn multiple_niches_align_8(a: MultipleAlign8) {}
40+
41+
// CHECK-LABEL: @call_multiple_niches_align_8
42+
#[no_mangle]
43+
fn call_multiple_niches_align_8() {
44+
// CHECK: call void @multiple_niches_align_8(ptr {{.*}}align 8 {{.*}}(i64 32768 to ptr)
45+
multiple_niches_align_8(MultipleAlign8::Niche_32768);
46+
// CHECK: call void @multiple_niches_align_8(ptr {{.*}}align 8 {{.*}}(i64 32776 to ptr)
47+
multiple_niches_align_8(MultipleAlign8::Niche_32776);
48+
}
49+
50+
enum MultipleAlign2 {
51+
Untag(RestrictedAddress_1_0x7fff),
52+
Niche_32768,
53+
Uninhabited_1(!),
54+
Niche_32770,
55+
}
56+
57+
// CHECK-LABEL: @multiple_niches_align_2
58+
// CHECK-SAME: align 2 {{.*}}%a)
59+
#[no_mangle]
60+
#[inline(never)]
61+
fn multiple_niches_align_2(a: MultipleAlign2) {}
62+
63+
// CHECK-LABEL: @call_multiple_niches_align_2
64+
#[no_mangle]
65+
fn call_multiple_niches_align_2() {
66+
// CHECK: call void @multiple_niches_align_2(ptr {{.*}}align 2 {{.*}}(i64 32768 to ptr)
67+
multiple_niches_align_2(MultipleAlign2::Niche_32768);
68+
// CHECK: call void @multiple_niches_align_2(ptr {{.*}}align 2 {{.*}}(i64 32770 to ptr)
69+
multiple_niches_align_2(MultipleAlign2::Niche_32770);
70+
}
71+
72+
enum SingleAlign8 {
73+
Untag(RestrictedAddress_0_0x7fff),
74+
Niche_32768,
75+
}
76+
77+
// CHECK-LABEL: @single_niche_align_8
78+
// CHECK-SAME: align 8 {{.*}}%a)
79+
#[no_mangle]
80+
#[inline(never)]
81+
fn single_niche_align_8(a: SingleAlign8) {}
82+
83+
// CHECK-LABEL: @call_single_niche_align_8
84+
#[no_mangle]
85+
fn call_single_niche_align_8() {
86+
// CHECK: call void @single_niche_align_8(ptr {{.*}}align 8 {{.*}}(i64 32768 to ptr)
87+
single_niche_align_8(SingleAlign8::Niche_32768);
88+
}
89+
90+
enum SingleAlign1 {
91+
Untag(RestrictedAddress_0_0xf000),
92+
Niche_61441,
93+
}
94+
95+
// CHECK-LABEL: @single_niche_align_1
96+
// CHECK-SAME: align 1 {{.*}}%a)
97+
#[no_mangle]
98+
#[inline(never)]
99+
fn single_niche_align_1(a: SingleAlign1) {}
100+
101+
// CHECK-LABEL: @call_single_niche_align_1
102+
#[no_mangle]
103+
fn call_single_niche_align_1() {
104+
// CHECK: call void @single_niche_align_1(ptr {{.*}}align 1 {{.*}}(i64 61441 to ptr)
105+
single_niche_align_1(SingleAlign1::Niche_61441);
106+
}
107+
108+
// Check that we only apply the new alignment on enum.
109+
110+
// CHECK-LABEL: @restricted_address
111+
// CHECK-SAME: align 8 {{.*}}%a)
112+
#[no_mangle]
113+
fn restricted_address(a: RestrictedAddress_0_0x7fff) {}

0 commit comments

Comments
 (0)