Skip to content

Commit 4703125

Browse files
authored
Rollup merge of rust-lang#137256 - workingjubilee:untangle-vector-abi-assumptions, r=bjorn3,RalfJung
compiler: untangle SIMD alignment assumptions There were a number of puzzling assumptions being made about SIMD types and their layout that I have corrected in this diff. These are mostly no-op edits in actual fact, but they do subtly alter a pair of checks in our invariant-checking and union layout computation that rested on those peculiar assumptions. Those unfortunately stand in the way of any further actual fixes. I submit this for review, even though it's not clearly motivated without its followups, because it should still be possible to independently conclude whether this is correct.
2 parents f8061c5 + 46f7a7d commit 4703125

File tree

6 files changed

+108
-86
lines changed

6 files changed

+108
-86
lines changed

compiler/rustc_abi/src/callconv/reg.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ impl Reg {
5757
128 => dl.f128_align.abi,
5858
_ => panic!("unsupported float: {self:?}"),
5959
},
60-
RegKind::Vector => dl.vector_align(self.size).abi,
60+
RegKind::Vector => dl.llvmlike_vector_align(self.size).abi,
6161
}
6262
}
6363
}

compiler/rustc_abi/src/layout.rs

+23-14
Original file line numberDiff line numberDiff line change
@@ -310,10 +310,10 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
310310
let mut align = if repr.pack.is_some() { dl.i8_align } else { dl.aggregate_align };
311311
let mut max_repr_align = repr.align;
312312

313-
// If all the non-ZST fields have the same ABI and union ABI optimizations aren't
314-
// disabled, we can use that common ABI for the union as a whole.
313+
// If all the non-ZST fields have the same repr and union repr optimizations aren't
314+
// disabled, we can use that common repr for the union as a whole.
315315
struct AbiMismatch;
316-
let mut common_non_zst_abi_and_align = if repr.inhibits_union_abi_opt() {
316+
let mut common_non_zst_repr_and_align = if repr.inhibits_union_abi_opt() {
317317
// Can't optimize
318318
Err(AbiMismatch)
319319
} else {
@@ -337,14 +337,14 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
337337
continue;
338338
}
339339

340-
if let Ok(common) = common_non_zst_abi_and_align {
340+
if let Ok(common) = common_non_zst_repr_and_align {
341341
// Discard valid range information and allow undef
342342
let field_abi = field.backend_repr.to_union();
343343

344344
if let Some((common_abi, common_align)) = common {
345345
if common_abi != field_abi {
346346
// Different fields have different ABI: disable opt
347-
common_non_zst_abi_and_align = Err(AbiMismatch);
347+
common_non_zst_repr_and_align = Err(AbiMismatch);
348348
} else {
349349
// Fields with the same non-Aggregate ABI should also
350350
// have the same alignment
@@ -357,7 +357,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
357357
}
358358
} else {
359359
// First non-ZST field: record its ABI and alignment
360-
common_non_zst_abi_and_align = Ok(Some((field_abi, field.align.abi)));
360+
common_non_zst_repr_and_align = Ok(Some((field_abi, field.align.abi)));
361361
}
362362
}
363363
}
@@ -376,16 +376,25 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
376376

377377
// If all non-ZST fields have the same ABI, we may forward that ABI
378378
// for the union as a whole, unless otherwise inhibited.
379-
let abi = match common_non_zst_abi_and_align {
379+
let backend_repr = match common_non_zst_repr_and_align {
380380
Err(AbiMismatch) | Ok(None) => BackendRepr::Memory { sized: true },
381-
Ok(Some((abi, _))) => {
382-
if abi.inherent_align(dl).map(|a| a.abi) != Some(align.abi) {
383-
// Mismatched alignment (e.g. union is #[repr(packed)]): disable opt
381+
Ok(Some((repr, _))) => match repr {
382+
// Mismatched alignment (e.g. union is #[repr(packed)]): disable opt
383+
BackendRepr::Scalar(_) | BackendRepr::ScalarPair(_, _)
384+
if repr.scalar_align(dl).unwrap() != align.abi =>
385+
{
384386
BackendRepr::Memory { sized: true }
385-
} else {
386-
abi
387387
}
388-
}
388+
// Vectors require at least element alignment, else disable the opt
389+
BackendRepr::Vector { element, count: _ } if element.align(dl).abi > align.abi => {
390+
BackendRepr::Memory { sized: true }
391+
}
392+
// the alignment tests passed and we can use this
393+
BackendRepr::Scalar(..)
394+
| BackendRepr::ScalarPair(..)
395+
| BackendRepr::Vector { .. }
396+
| BackendRepr::Memory { .. } => repr,
397+
},
389398
};
390399

391400
let Some(union_field_count) = NonZeroUsize::new(only_variant.len()) else {
@@ -400,7 +409,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
400409
Ok(LayoutData {
401410
variants: Variants::Single { index: only_variant_idx },
402411
fields: FieldsShape::Union(union_field_count),
403-
backend_repr: abi,
412+
backend_repr,
404413
largest_niche: None,
405414
uninhabited: false,
406415
align,

compiler/rustc_abi/src/lib.rs

+49-44
Original file line numberDiff line numberDiff line change
@@ -408,16 +408,21 @@ impl TargetDataLayout {
408408
}
409409
}
410410

411+
/// psABI-mandated alignment for a vector type, if any
411412
#[inline]
412-
pub fn vector_align(&self, vec_size: Size) -> AbiAndPrefAlign {
413-
for &(size, align) in &self.vector_align {
414-
if size == vec_size {
415-
return align;
416-
}
417-
}
418-
// Default to natural alignment, which is what LLVM does.
419-
// That is, use the size, rounded up to a power of 2.
420-
AbiAndPrefAlign::new(Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap())
413+
fn cabi_vector_align(&self, vec_size: Size) -> Option<AbiAndPrefAlign> {
414+
self.vector_align
415+
.iter()
416+
.find(|(size, _align)| *size == vec_size)
417+
.map(|(_size, align)| *align)
418+
}
419+
420+
/// an alignment resembling the one LLVM would pick for a vector
421+
#[inline]
422+
pub fn llvmlike_vector_align(&self, vec_size: Size) -> AbiAndPrefAlign {
423+
self.cabi_vector_align(vec_size).unwrap_or(AbiAndPrefAlign::new(
424+
Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap(),
425+
))
421426
}
422427
}
423428

@@ -810,20 +815,19 @@ impl Align {
810815
self.bits().try_into().unwrap()
811816
}
812817

813-
/// Computes the best alignment possible for the given offset
814-
/// (the largest power of two that the offset is a multiple of).
818+
/// Obtain the greatest factor of `size` that is an alignment
819+
/// (the largest power of two the Size is a multiple of).
815820
///
816-
/// N.B., for an offset of `0`, this happens to return `2^64`.
821+
/// Note that all numbers are factors of 0
817822
#[inline]
818-
pub fn max_for_offset(offset: Size) -> Align {
819-
Align { pow2: offset.bytes().trailing_zeros() as u8 }
823+
pub fn max_aligned_factor(size: Size) -> Align {
824+
Align { pow2: size.bytes().trailing_zeros() as u8 }
820825
}
821826

822-
/// Lower the alignment, if necessary, such that the given offset
823-
/// is aligned to it (the offset is a multiple of the alignment).
827+
/// Reduces Align to an aligned factor of `size`.
824828
#[inline]
825-
pub fn restrict_for_offset(self, offset: Size) -> Align {
826-
self.min(Align::max_for_offset(offset))
829+
pub fn restrict_for_offset(self, size: Size) -> Align {
830+
self.min(Align::max_aligned_factor(size))
827831
}
828832
}
829833

@@ -1455,37 +1459,38 @@ impl BackendRepr {
14551459
matches!(*self, BackendRepr::Scalar(s) if s.is_bool())
14561460
}
14571461

1458-
/// Returns the fixed alignment of this ABI, if any is mandated.
1459-
pub fn inherent_align<C: HasDataLayout>(&self, cx: &C) -> Option<AbiAndPrefAlign> {
1460-
Some(match *self {
1461-
BackendRepr::Scalar(s) => s.align(cx),
1462-
BackendRepr::ScalarPair(s1, s2) => s1.align(cx).max(s2.align(cx)),
1463-
BackendRepr::Vector { element, count } => {
1464-
cx.data_layout().vector_align(element.size(cx) * count)
1465-
}
1466-
BackendRepr::Memory { .. } => return None,
1467-
})
1462+
/// The psABI alignment for a `Scalar` or `ScalarPair`
1463+
///
1464+
/// `None` for other variants.
1465+
pub fn scalar_align<C: HasDataLayout>(&self, cx: &C) -> Option<Align> {
1466+
match *self {
1467+
BackendRepr::Scalar(s) => Some(s.align(cx).abi),
1468+
BackendRepr::ScalarPair(s1, s2) => Some(s1.align(cx).max(s2.align(cx)).abi),
1469+
// The align of a Vector can vary in surprising ways
1470+
BackendRepr::Vector { .. } | BackendRepr::Memory { .. } => None,
1471+
}
14681472
}
14691473

1470-
/// Returns the fixed size of this ABI, if any is mandated.
1471-
pub fn inherent_size<C: HasDataLayout>(&self, cx: &C) -> Option<Size> {
1472-
Some(match *self {
1473-
BackendRepr::Scalar(s) => {
1474-
// No padding in scalars.
1475-
s.size(cx)
1476-
}
1474+
/// The psABI size for a `Scalar` or `ScalarPair`
1475+
///
1476+
/// `None` for other variants
1477+
pub fn scalar_size<C: HasDataLayout>(&self, cx: &C) -> Option<Size> {
1478+
match *self {
1479+
// No padding in scalars.
1480+
BackendRepr::Scalar(s) => Some(s.size(cx)),
1481+
// May have some padding between the pair.
14771482
BackendRepr::ScalarPair(s1, s2) => {
1478-
// May have some padding between the pair.
14791483
let field2_offset = s1.size(cx).align_to(s2.align(cx).abi);
1480-
(field2_offset + s2.size(cx)).align_to(self.inherent_align(cx)?.abi)
1484+
let size = (field2_offset + s2.size(cx)).align_to(
1485+
self.scalar_align(cx)
1486+
// We absolutely must have an answer here or everything is FUBAR.
1487+
.unwrap(),
1488+
);
1489+
Some(size)
14811490
}
1482-
BackendRepr::Vector { element, count } => {
1483-
// No padding in vectors, except possibly for trailing padding
1484-
// to make the size a multiple of align (e.g. for vectors of size 3).
1485-
(element.size(cx) * count).align_to(self.inherent_align(cx)?.abi)
1486-
}
1487-
BackendRepr::Memory { .. } => return None,
1488-
})
1491+
// The size of a Vector can vary in surprising ways
1492+
BackendRepr::Vector { .. } | BackendRepr::Memory { .. } => None,
1493+
}
14891494
}
14901495

14911496
/// Discard validity range information and allow undef.

compiler/rustc_ty_utils/src/layout.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -547,12 +547,15 @@ fn layout_of_uncached<'tcx>(
547547
(
548548
BackendRepr::Memory { sized: true },
549549
AbiAndPrefAlign {
550-
abi: Align::max_for_offset(size),
551-
pref: dl.vector_align(size).pref,
550+
abi: Align::max_aligned_factor(size),
551+
pref: dl.llvmlike_vector_align(size).pref,
552552
},
553553
)
554554
} else {
555-
(BackendRepr::Vector { element: e_abi, count: e_len }, dl.vector_align(size))
555+
(
556+
BackendRepr::Vector { element: e_abi, count: e_len },
557+
dl.llvmlike_vector_align(size),
558+
)
556559
};
557560
let size = size.align_to(align.abi);
558561

compiler/rustc_ty_utils/src/layout/invariant.rs

+28-23
Original file line numberDiff line numberDiff line change
@@ -69,31 +69,30 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou
6969
}
7070

7171
fn check_layout_abi<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) {
72-
// Verify the ABI mandated alignment and size.
73-
let align = layout.backend_repr.inherent_align(cx).map(|align| align.abi);
74-
let size = layout.backend_repr.inherent_size(cx);
75-
let Some((align, size)) = align.zip(size) else {
76-
assert_matches!(
77-
layout.layout.backend_repr(),
78-
BackendRepr::Memory { .. },
79-
"ABI unexpectedly missing alignment and/or size in {layout:#?}"
72+
// Verify the ABI-mandated alignment and size for scalars.
73+
let align = layout.backend_repr.scalar_align(cx);
74+
let size = layout.backend_repr.scalar_size(cx);
75+
if let Some(align) = align {
76+
assert_eq!(
77+
layout.layout.align().abi,
78+
align,
79+
"alignment mismatch between ABI and layout in {layout:#?}"
8080
);
81-
return;
82-
};
83-
assert_eq!(
84-
layout.layout.align().abi,
85-
align,
86-
"alignment mismatch between ABI and layout in {layout:#?}"
87-
);
88-
assert_eq!(
89-
layout.layout.size(),
90-
size,
91-
"size mismatch between ABI and layout in {layout:#?}"
92-
);
81+
}
82+
if let Some(size) = size {
83+
assert_eq!(
84+
layout.layout.size(),
85+
size,
86+
"size mismatch between ABI and layout in {layout:#?}"
87+
);
88+
}
9389

9490
// Verify per-ABI invariants
9591
match layout.layout.backend_repr() {
9692
BackendRepr::Scalar(_) => {
93+
// These must always be present for `Scalar` types.
94+
let align = align.unwrap();
95+
let size = size.unwrap();
9796
// Check that this matches the underlying field.
9897
let inner = skip_newtypes(cx, layout);
9998
assert!(
@@ -235,9 +234,15 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou
235234
"`ScalarPair` second field with bad ABI in {inner:#?}",
236235
);
237236
}
238-
BackendRepr::Vector { element, .. } => {
239-
assert!(align >= element.align(cx).abi); // just sanity-checking `vector_align`.
240-
// FIXME: Do some kind of check of the inner type, like for Scalar and ScalarPair.
237+
BackendRepr::Vector { element, count } => {
238+
let align = layout.align.abi;
239+
let size = layout.size;
240+
let element_align = element.align(cx).abi;
241+
let element_size = element.size(cx);
242+
// Currently, vectors must always be aligned to at least their elements:
243+
assert!(align >= element_align);
244+
// And the size has to be element * count plus alignment padding, of course
245+
assert!(size == (element_size * count).align_to(align));
241246
}
242247
BackendRepr::Memory { .. } => {} // Nothing to check.
243248
}

src/tools/rust-analyzer/crates/hir-ty/src/layout.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ fn layout_of_simd_ty(
179179
.size
180180
.checked_mul(e_len, dl)
181181
.ok_or(LayoutError::BadCalc(LayoutCalculatorError::SizeOverflow))?;
182-
let align = dl.vector_align(size);
182+
let align = dl.llvmlike_vector_align(size);
183183
let size = size.align_to(align.abi);
184184

185185
// Compute the placement of the vector fields:

0 commit comments

Comments
 (0)