Auto merge of #112157 - erikdesjardins:align, r=nikic

Resurrect: rustc_target: Add alignment to indirectly-passed by-value types, correcting the alignment of byval on x86 in the process.

Same as #111551, which I [accidentally closed](https://github.com/rust-lang/rust/pull/111551#issuecomment-1571222612) :/

---

This resurrects PR #103830, which has sat idle for a while.

Beyond #103830, this also:
- fixes byval alignment for types containing vectors on Darwin (see `tests/codegen/align-byval-vector.rs`)
- fixes byval alignment for overaligned types on x86 Windows (see `tests/codegen/align-byval.rs`)
- fixes ABI for types with 128bit requested alignment on ARM64 Linux (see `tests/codegen/aarch64-struct-align-128.rs`)

r? `@nikic`

---

`@pcwalton's` original PR description is reproduced below:

Commit 88e4d2c from five years ago removed
support for alignment on indirectly-passed arguments because of problems with
the `i686-pc-windows-msvc` target. Unfortunately, the `memcpy` optimizations I
recently added to LLVM 16 depend on this to forward `memcpy`s. This commit
attempts to fix the problems with `byval` parameters on that target and now
correctly adds the `align` attribute.

The problem is summarized in [this comment] by `@eddyb.` Briefly, 32-bit x86 has
special alignment rules for `byval` parameters: for the most part, their
alignment is forced to 4. This is not well-documented anywhere but in the Clang
source. I looked at the logic in Clang `TargetInfo.cpp` and tried to replicate
it here. The relevant methods in that file are
`X86_32ABIInfo::getIndirectResult()` and
`X86_32ABIInfo::getTypeStackAlignInBytes()`. The `align` parameter attribute
for `byval` parameters in LLVM must match the platform ABI, or miscompilations
will occur. Note that this doesn't use the approach suggested by eddyb, because
I felt it was overkill to store the alignment in `on_stack` when special
handling is really only needed for 32-bit x86.

As a side effect, this should fix #80127, because it will make the `align`
parameter attribute for `byval` parameters match the platform ABI on LLVM
x86-64.

[this comment]: #80822 (comment)
This commit is contained in:
bors 2023-07-15 15:39:53 +00:00
commit 7a17f577b3
32 changed files with 1251 additions and 93 deletions

View File

@ -40,6 +40,8 @@ pub trait LayoutCalculator {
largest_niche,
align,
size,
max_repr_align: None,
unadjusted_abi_align: align.abi,
}
}
@ -122,6 +124,8 @@ pub trait LayoutCalculator {
largest_niche: None,
align: dl.i8_align,
size: Size::ZERO,
max_repr_align: None,
unadjusted_abi_align: dl.i8_align.abi,
}
}
@ -289,6 +293,9 @@ pub trait LayoutCalculator {
}
let mut align = dl.aggregate_align;
let mut max_repr_align = repr.align;
let mut unadjusted_abi_align = align.abi;
let mut variant_layouts = variants
.iter_enumerated()
.map(|(j, v)| {
@ -296,6 +303,8 @@ pub trait LayoutCalculator {
st.variants = Variants::Single { index: j };
align = align.max(st.align);
max_repr_align = max_repr_align.max(st.max_repr_align);
unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align);
Some(st)
})
@ -422,6 +431,8 @@ pub trait LayoutCalculator {
largest_niche,
size,
align,
max_repr_align,
unadjusted_abi_align,
};
Some(TmpLayout { layout, variants: variant_layouts })
@ -456,6 +467,9 @@ pub trait LayoutCalculator {
let (min_ity, signed) = discr_range_of_repr(min, max); //Integer::repr_discr(tcx, ty, &repr, min, max);
let mut align = dl.aggregate_align;
let mut max_repr_align = repr.align;
let mut unadjusted_abi_align = align.abi;
let mut size = Size::ZERO;
// We're interested in the smallest alignment, so start large.
@ -498,6 +512,8 @@ pub trait LayoutCalculator {
}
size = cmp::max(size, st.size);
align = align.max(st.align);
max_repr_align = max_repr_align.max(st.max_repr_align);
unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align);
Some(st)
})
.collect::<Option<IndexVec<VariantIdx, _>>>()?;
@ -691,6 +707,8 @@ pub trait LayoutCalculator {
abi,
align,
size,
max_repr_align,
unadjusted_abi_align,
};
let tagged_layout = TmpLayout { layout: tagged_layout, variants: layout_variants };
@ -730,10 +748,7 @@ pub trait LayoutCalculator {
let dl = self.current_data_layout();
let dl = dl.borrow();
let mut align = if repr.pack.is_some() { dl.i8_align } else { dl.aggregate_align };
if let Some(repr_align) = repr.align {
align = align.max(AbiAndPrefAlign::new(repr_align));
}
let mut max_repr_align = repr.align;
// If all the non-ZST fields have the same ABI and union ABI optimizations aren't
// disabled, we can use that common ABI for the union as a whole.
@ -751,6 +766,7 @@ pub trait LayoutCalculator {
assert!(field.0.is_sized());
align = align.max(field.align());
max_repr_align = max_repr_align.max(field.max_repr_align());
size = cmp::max(size, field.size());
if field.0.is_zst() {
@ -787,6 +803,14 @@ pub trait LayoutCalculator {
if let Some(pack) = repr.pack {
align = align.min(AbiAndPrefAlign::new(pack));
}
// The unadjusted ABI alignment does not include repr(align), but does include repr(pack).
// See documentation on `LayoutS::unadjusted_abi_align`.
let unadjusted_abi_align = align.abi;
if let Some(repr_align) = repr.align {
align = align.max(AbiAndPrefAlign::new(repr_align));
}
// `align` must not be modified after this, or `unadjusted_abi_align` could be inaccurate.
let align = align;
// If all non-ZST fields have the same ABI, we may forward that ABI
// for the union as a whole, unless otherwise inhibited.
@ -809,6 +833,8 @@ pub trait LayoutCalculator {
largest_niche: None,
align,
size: size.align_to(align.abi),
max_repr_align,
unadjusted_abi_align,
})
}
}
@ -829,6 +855,7 @@ fn univariant(
) -> Option<LayoutS> {
let pack = repr.pack;
let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align };
let mut max_repr_align = repr.align;
let mut inverse_memory_index: IndexVec<u32, FieldIdx> = fields.indices().collect();
let optimize = !repr.inhibit_struct_field_reordering_opt();
if optimize && fields.len() > 1 {
@ -997,6 +1024,7 @@ fn univariant(
};
offset = offset.align_to(field_align.abi);
align = align.max(field_align);
max_repr_align = max_repr_align.max(field.max_repr_align());
debug!("univariant offset: {:?} field: {:#?}", offset, field);
offsets[i] = offset;
@ -1018,9 +1046,16 @@ fn univariant(
offset = offset.checked_add(field.size(), dl)?;
}
// The unadjusted ABI alignment does not include repr(align), but does include repr(pack).
// See documentation on `LayoutS::unadjusted_abi_align`.
let unadjusted_abi_align = align.abi;
if let Some(repr_align) = repr.align {
align = align.max(AbiAndPrefAlign::new(repr_align));
}
// `align` must not be modified after this point, or `unadjusted_abi_align` could be inaccurate.
let align = align;
debug!("univariant min_size: {:?}", offset);
let min_size = offset;
// As stated above, inverse_memory_index holds field indices by increasing offset.
@ -1036,6 +1071,7 @@ fn univariant(
inverse_memory_index.into_iter().map(FieldIdx::as_u32).collect()
};
let size = min_size.align_to(align.abi);
let mut layout_of_single_non_zst_field = None;
let mut abi = Abi::Aggregate { sized };
// Unpack newtype ABIs and find scalar pairs.
if sized && size.bytes() > 0 {
@ -1045,6 +1081,8 @@ fn univariant(
match (non_zst_fields.next(), non_zst_fields.next(), non_zst_fields.next()) {
// We have exactly one non-ZST field.
(Some((i, field)), None, None) => {
layout_of_single_non_zst_field = Some(field);
// Field fills the struct and it has a scalar or scalar pair ABI.
if offsets[i].bytes() == 0 && align.abi == field.align().abi && size == field.size()
{
@ -1102,6 +1140,19 @@ fn univariant(
if fields.iter().any(|f| f.abi().is_uninhabited()) {
abi = Abi::Uninhabited;
}
let unadjusted_abi_align = if repr.transparent() {
match layout_of_single_non_zst_field {
Some(l) => l.unadjusted_abi_align(),
None => {
// `repr(transparent)` with all ZST fields.
align.abi
}
}
} else {
unadjusted_abi_align
};
Some(LayoutS {
variants: Variants::Single { index: FIRST_VARIANT },
fields: FieldsShape::Arbitrary { offsets, memory_index },
@ -1109,6 +1160,8 @@ fn univariant(
largest_niche,
align,
size,
max_repr_align,
unadjusted_abi_align,
})
}

View File

@ -1531,6 +1531,16 @@ pub struct LayoutS {
pub align: AbiAndPrefAlign,
pub size: Size,
/// The largest alignment explicitly requested with `repr(align)` on this type or any field.
/// Only used on i686-windows, where the argument passing ABI is different when alignment is
/// requested, even if the requested alignment is equal to the natural alignment.
pub max_repr_align: Option<Align>,
/// The alignment the type would have, ignoring any `repr(align)` but including `repr(packed)`.
/// Only used on aarch64-linux, where the argument passing ABI ignores the requested alignment
/// in some cases.
pub unadjusted_abi_align: Align,
}
impl LayoutS {
@ -1545,6 +1555,8 @@ impl LayoutS {
largest_niche,
size,
align,
max_repr_align: None,
unadjusted_abi_align: align.abi,
}
}
}
@ -1554,7 +1566,16 @@ impl fmt::Debug for LayoutS {
// This is how `Layout` used to print before it become
// `Interned<LayoutS>`. We print it like this to avoid having to update
// expected output in a lot of tests.
let LayoutS { size, align, abi, fields, largest_niche, variants } = self;
let LayoutS {
size,
align,
abi,
fields,
largest_niche,
variants,
max_repr_align,
unadjusted_abi_align,
} = self;
f.debug_struct("Layout")
.field("size", size)
.field("align", align)
@ -1562,6 +1583,8 @@ impl fmt::Debug for LayoutS {
.field("fields", fields)
.field("largest_niche", largest_niche)
.field("variants", variants)
.field("max_repr_align", max_repr_align)
.field("unadjusted_abi_align", unadjusted_abi_align)
.finish()
}
}
@ -1602,6 +1625,14 @@ impl<'a> Layout<'a> {
self.0.0.size
}
pub fn max_repr_align(self) -> Option<Align> {
self.0.0.max_repr_align
}
pub fn unadjusted_abi_align(self) -> Align {
self.0.0.unadjusted_abi_align
}
/// Whether the layout is from a type that implements [`std::marker::PointerLike`].
///
/// Currently, that means that the type is pointer-sized, pointer-aligned,

View File

@ -80,14 +80,7 @@ pub(super) fn add_local_place_comments<'tcx>(
return;
}
let TyAndLayout { ty, layout } = place.layout();
let rustc_target::abi::LayoutS {
size,
align,
abi: _,
variants: _,
fields: _,
largest_niche: _,
} = layout.0.0;
let rustc_target::abi::LayoutS { size, align, .. } = layout.0.0;
let (kind, extra) = place.debug_comment();

View File

@ -23,6 +23,8 @@ use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode, Reg};
use rustc_target::abi::{self, HasDataLayout, WrappingRange};
use rustc_target::spec::abi::Abi;
use std::cmp;
// Indicates if we are in the middle of merging a BB's successor into it. This
// can happen when BB jumps directly to its successor and the successor has no
// other predecessors.
@ -1360,36 +1362,58 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// Force by-ref if we have to load through a cast pointer.
let (mut llval, align, by_ref) = match op.val {
Immediate(_) | Pair(..) => match arg.mode {
PassMode::Indirect { .. } | PassMode::Cast(..) => {
PassMode::Indirect { attrs, .. } => {
// Indirect argument may have higher alignment requirements than the type's alignment.
// This can happen, e.g. when passing types with <4 byte alignment on the stack on x86.
let required_align = match attrs.pointee_align {
Some(pointee_align) => cmp::max(pointee_align, arg.layout.align.abi),
None => arg.layout.align.abi,
};
let scratch = PlaceRef::alloca_aligned(bx, arg.layout, required_align);
op.val.store(bx, scratch);
(scratch.llval, scratch.align, true)
}
PassMode::Cast(..) => {
let scratch = PlaceRef::alloca(bx, arg.layout);
op.val.store(bx, scratch);
(scratch.llval, scratch.align, true)
}
_ => (op.immediate_or_packed_pair(bx), arg.layout.align.abi, false),
},
Ref(llval, _, align) => {
if arg.is_indirect() && align < arg.layout.align.abi {
// `foo(packed.large_field)`. We can't pass the (unaligned) field directly. I
// think that ATM (Rust 1.16) we only pass temporaries, but we shouldn't
// have scary latent bugs around.
let scratch = PlaceRef::alloca(bx, arg.layout);
base::memcpy_ty(
bx,
scratch.llval,
scratch.align,
llval,
align,
op.layout,
MemFlags::empty(),
);
(scratch.llval, scratch.align, true)
} else {
(llval, align, true)
Ref(llval, _, align) => match arg.mode {
PassMode::Indirect { attrs, .. } => {
let required_align = match attrs.pointee_align {
Some(pointee_align) => cmp::max(pointee_align, arg.layout.align.abi),
None => arg.layout.align.abi,
};
if align < required_align {
// For `foo(packed.large_field)`, and types with <4 byte alignment on x86,
// alignment requirements may be higher than the type's alignment, so copy
// to a higher-aligned alloca.
let scratch = PlaceRef::alloca_aligned(bx, arg.layout, required_align);
base::memcpy_ty(
bx,
scratch.llval,
scratch.align,
llval,
align,
op.layout,
MemFlags::empty(),
);
(scratch.llval, scratch.align, true)
} else {
(llval, align, true)
}
}
}
_ => (llval, align, true),
},
ZeroSized => match arg.mode {
PassMode::Indirect { .. } => {
PassMode::Indirect { on_stack, .. } => {
if on_stack {
// It doesn't seem like any target can have `byval` ZSTs, so this assert
// is here to replace a would-be untested codepath.
bug!("ZST {op:?} passed on stack with abi {arg:?}");
}
// Though `extern "Rust"` doesn't pass ZSTs, some ABIs pass
// a pointer for `repr(C)` structs even when empty, so get
// one from an `alloca` (which can be left uninitialized).

View File

@ -47,10 +47,18 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
pub fn alloca<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
bx: &mut Bx,
layout: TyAndLayout<'tcx>,
) -> Self {
Self::alloca_aligned(bx, layout, layout.align.abi)
}
pub fn alloca_aligned<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
bx: &mut Bx,
layout: TyAndLayout<'tcx>,
align: Align,
) -> Self {
assert!(layout.is_sized(), "tried to statically allocate unsized place");
let tmp = bx.alloca(bx.cx().backend_type(layout), layout.align.abi);
Self::new_sized(tmp, layout)
let tmp = bx.alloca(bx.cx().backend_type(layout), align);
Self::new_sized_aligned(tmp, layout, align)
}
/// Returns a place for an indirect reference to an unsized place.

View File

@ -755,6 +755,8 @@ where
largest_niche: None,
align: tcx.data_layout.i8_align,
size: Size::ZERO,
max_repr_align: None,
unadjusted_abi_align: tcx.data_layout.i8_align.abi,
})
}

View File

@ -1,25 +1,15 @@
use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform};
use crate::abi::{HasDataLayout, TyAbiInterface};
/// Given integer-types M and register width N (e.g. M=u16 and N=32 bits), the
/// `ParamExtension` policy specifies how a uM value should be treated when
/// passed via register or stack-slot of width N. See also rust-lang/rust#97463.
/// Indicates the variant of the AArch64 ABI we are compiling for.
/// Used to accommodate Apple and Microsoft's deviations from the usual AAPCS ABI.
///
/// Corresponds to Clang's `AArch64ABIInfo::ABIKind`.
#[derive(Copy, Clone, PartialEq)]
pub enum ParamExtension {
/// Indicates that when passing an i8/i16, either as a function argument or
/// as a return value, it must be sign-extended to 32 bits, and likewise a
/// u8/u16 must be zero-extended to 32-bits. (This variant is here to
/// accommodate Apple's deviation from the usual AArch64 ABI as defined by
/// ARM.)
///
/// See also: <https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Pass-Arguments-to-Functions-Correctly>
ExtendTo32Bits,
/// Indicates that no sign- nor zero-extension is performed: if a value of
/// type with bitwidth M is passed as function argument or return value,
/// then M bits are copied into the least significant M bits, and the
/// remaining bits of the register (or word of memory) are untouched.
NoExtension,
pub enum AbiKind {
AAPCS,
DarwinPCS,
Win64,
}
fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option<Uniform>
@ -45,15 +35,17 @@ where
})
}
fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, param_policy: ParamExtension)
fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, kind: AbiKind)
where
Ty: TyAbiInterface<'a, C> + Copy,
C: HasDataLayout,
{
if !ret.layout.is_aggregate() {
match param_policy {
ParamExtension::ExtendTo32Bits => ret.extend_integer_width_to(32),
ParamExtension::NoExtension => {}
if kind == AbiKind::DarwinPCS {
// On Darwin, when returning an i8/i16, it must be sign-extended to 32 bits,
// and likewise a u8/u16 must be zero-extended to 32-bits.
// See also: <https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Pass-Arguments-to-Functions-Correctly>
ret.extend_integer_width_to(32)
}
return;
}
@ -70,15 +62,17 @@ where
ret.make_indirect();
}
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, param_policy: ParamExtension)
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, kind: AbiKind)
where
Ty: TyAbiInterface<'a, C> + Copy,
C: HasDataLayout,
{
if !arg.layout.is_aggregate() {
match param_policy {
ParamExtension::ExtendTo32Bits => arg.extend_integer_width_to(32),
ParamExtension::NoExtension => {}
if kind == AbiKind::DarwinPCS {
// On Darwin, when passing an i8/i16, it must be sign-extended to 32 bits,
// and likewise a u8/u16 must be zero-extended to 32-bits.
// See also: <https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Pass-Arguments-to-Functions-Correctly>
arg.extend_integer_width_to(32);
}
return;
}
@ -87,27 +81,39 @@ where
return;
}
let size = arg.layout.size;
let bits = size.bits();
if bits <= 128 {
arg.cast_to(Uniform { unit: Reg::i64(), total: size });
let align = if kind == AbiKind::AAPCS {
// When passing small aggregates by value, the AAPCS ABI mandates using the unadjusted
// alignment of the type (not including `repr(align)`).
// This matches behavior of `AArch64ABIInfo::classifyArgumentType` in Clang.
// See: <https://github.com/llvm/llvm-project/blob/5e691a1c9b0ad22689d4a434ddf4fed940e58dec/clang/lib/CodeGen/TargetInfo.cpp#L5816-L5823>
arg.layout.unadjusted_abi_align
} else {
arg.layout.align.abi
};
if size.bits() <= 128 {
if align.bits() == 128 {
arg.cast_to(Uniform { unit: Reg::i128(), total: size });
} else {
arg.cast_to(Uniform { unit: Reg::i64(), total: size });
}
return;
}
arg.make_indirect();
}
pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, param_policy: ParamExtension)
pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, kind: AbiKind)
where
Ty: TyAbiInterface<'a, C> + Copy,
C: HasDataLayout,
{
if !fn_abi.ret.is_ignore() {
classify_ret(cx, &mut fn_abi.ret, param_policy);
classify_ret(cx, &mut fn_abi.ret, kind);
}
for arg in fn_abi.args.iter_mut() {
if arg.is_ignore() {
continue;
}
classify_arg(cx, arg, param_policy);
classify_arg(cx, arg, kind);
}
}

View File

@ -10,7 +10,7 @@ fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
if arg.layout.is_aggregate() {
arg.make_indirect_byval();
arg.make_indirect_byval(None);
} else {
arg.extend_integer_width_to(32);
}

View File

@ -494,9 +494,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
.set(ArgAttribute::NonNull)
.set(ArgAttribute::NoUndef);
attrs.pointee_size = layout.size;
// FIXME(eddyb) We should be doing this, but at least on
// i686-pc-windows-msvc, it results in wrong stack offsets.
// attrs.pointee_align = Some(layout.align.abi);
attrs.pointee_align = Some(layout.align.abi);
let extra_attrs = layout.is_unsized().then_some(ArgAttributes::new());
@ -513,11 +511,19 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
self.mode = Self::indirect_pass_mode(&self.layout);
}
pub fn make_indirect_byval(&mut self) {
pub fn make_indirect_byval(&mut self, byval_align: Option<Align>) {
self.make_indirect();
match self.mode {
PassMode::Indirect { attrs: _, extra_attrs: _, ref mut on_stack } => {
PassMode::Indirect { ref mut attrs, extra_attrs: _, ref mut on_stack } => {
*on_stack = true;
// Some platforms, like 32-bit x86, change the alignment of the type when passing
// `byval`. Account for that.
if let Some(byval_align) = byval_align {
// On all targets with byval align this is currently true, so let's assert it.
debug_assert!(byval_align >= Align::from_bytes(4).unwrap());
attrs.pointee_align = Some(byval_align);
}
}
_ => unreachable!(),
}
@ -644,7 +650,8 @@ impl<'a, Ty> FnAbi<'a, Ty> {
{
if abi == spec::abi::Abi::X86Interrupt {
if let Some(arg) = self.args.first_mut() {
arg.make_indirect_byval();
// FIXME(pcwalton): This probably should use the x86 `byval` ABI...
arg.make_indirect_byval(None);
}
return Ok(());
}
@ -672,12 +679,14 @@ impl<'a, Ty> FnAbi<'a, Ty> {
}
},
"aarch64" => {
let param_policy = if cx.target_spec().is_like_osx {
aarch64::ParamExtension::ExtendTo32Bits
let kind = if cx.target_spec().is_like_osx {
aarch64::AbiKind::DarwinPCS
} else if cx.target_spec().is_like_windows {
aarch64::AbiKind::Win64
} else {
aarch64::ParamExtension::NoExtension
aarch64::AbiKind::AAPCS
};
aarch64::compute_abi_info(cx, self, param_policy)
aarch64::compute_abi_info(cx, self, kind)
}
"amdgpu" => amdgpu::compute_abi_info(cx, self),
"arm" => arm::compute_abi_info(cx, self),

View File

@ -36,7 +36,7 @@ where
{
arg.extend_integer_width_to(32);
if arg.layout.is_aggregate() && !unwrap_trivial_aggregate(cx, arg) {
arg.make_indirect_byval();
arg.make_indirect_byval(None);
}
}

View File

@ -1,5 +1,5 @@
use crate::abi::call::{ArgAttribute, FnAbi, PassMode, Reg, RegKind};
use crate::abi::{HasDataLayout, TyAbiInterface};
use crate::abi::{Abi, Align, HasDataLayout, TyAbiInterface, TyAndLayout};
use crate::spec::HasTargetSpec;
#[derive(PartialEq)]
@ -53,8 +53,75 @@ where
if arg.is_ignore() {
continue;
}
if arg.layout.is_aggregate() {
arg.make_indirect_byval();
// FIXME: MSVC 2015+ will pass the first 3 vector arguments in [XYZ]MM0-2
// See https://reviews.llvm.org/D72114 for Clang behavior
let t = cx.target_spec();
let align_4 = Align::from_bytes(4).unwrap();
let align_16 = Align::from_bytes(16).unwrap();
if t.is_like_msvc
&& arg.layout.is_adt()
&& let Some(max_repr_align) = arg.layout.max_repr_align
&& max_repr_align > align_4
{
// MSVC has special rules for overaligned arguments: https://reviews.llvm.org/D72114.
// Summarized here:
// - Arguments with _requested_ alignment > 4 are passed indirectly.
// - For backwards compatibility, arguments with natural alignment > 4 are still passed
// on stack (via `byval`). For example, this includes `double`, `int64_t`,
// and structs containing them, provided they lack an explicit alignment attribute.
assert!(arg.layout.align.abi >= max_repr_align,
"abi alignment {:?} less than requested alignment {max_repr_align:?}",
arg.layout.align.abi,
);
arg.make_indirect();
} else if arg.layout.is_aggregate() {
// We need to compute the alignment of the `byval` argument. The rules can be found in
// `X86_32ABIInfo::getTypeStackAlignInBytes` in Clang's `TargetInfo.cpp`. Summarized
// here, they are:
//
// 1. If the natural alignment of the type is <= 4, the alignment is 4.
//
// 2. Otherwise, on Linux, the alignment of any vector type is the natural alignment.
// This doesn't matter here because we only pass aggregates via `byval`, not vectors.
//
// 3. Otherwise, on Apple platforms, the alignment of anything that contains a vector
// type is 16.
//
// 4. If none of these conditions are true, the alignment is 4.
fn contains_vector<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool
where
Ty: TyAbiInterface<'a, C> + Copy,
{
match layout.abi {
Abi::Uninhabited | Abi::Scalar(_) | Abi::ScalarPair(..) => false,
Abi::Vector { .. } => true,
Abi::Aggregate { .. } => {
for i in 0..layout.fields.count() {
if contains_vector(cx, layout.field(cx, i)) {
return true;
}
}
false
}
}
}
let byval_align = if arg.layout.align.abi < align_4 {
// (1.)
align_4
} else if t.is_like_osx && contains_vector(cx, arg.layout) {
// (3.)
align_16
} else {
// (4.)
align_4
};
arg.make_indirect_byval(Some(byval_align));
} else {
arg.extend_integer_width_to(32);
}

View File

@ -213,7 +213,7 @@ where
match cls_or_mem {
Err(Memory) => {
if is_arg {
arg.make_indirect_byval();
arg.make_indirect_byval(None);
} else {
// `sret` parameter thus one less integer register available
arg.make_indirect();

View File

@ -12,6 +12,7 @@
#![feature(associated_type_bounds)]
#![feature(exhaustive_patterns)]
#![feature(iter_intersperse)]
#![feature(let_chains)]
#![feature(min_specialization)]
#![feature(never_type)]
#![feature(rustc_attrs)]

View File

@ -258,6 +258,8 @@ fn layout_of_uncached<'tcx>(
largest_niche,
align: element.align,
size,
max_repr_align: None,
unadjusted_abi_align: element.align.abi,
})
}
ty::Slice(element) => {
@ -269,6 +271,8 @@ fn layout_of_uncached<'tcx>(
largest_niche: None,
align: element.align,
size: Size::ZERO,
max_repr_align: None,
unadjusted_abi_align: element.align.abi,
})
}
ty::Str => tcx.mk_layout(LayoutS {
@ -278,6 +282,8 @@ fn layout_of_uncached<'tcx>(
largest_niche: None,
align: dl.i8_align,
size: Size::ZERO,
max_repr_align: None,
unadjusted_abi_align: dl.i8_align.abi,
}),
// Odd unit types.
@ -431,6 +437,8 @@ fn layout_of_uncached<'tcx>(
largest_niche: e_ly.largest_niche,
size,
align,
max_repr_align: None,
unadjusted_abi_align: align.abi,
})
}
@ -884,6 +892,8 @@ fn generator_layout<'tcx>(
largest_niche: prefix.largest_niche,
size,
align,
max_repr_align: None,
unadjusted_abi_align: align.abi,
});
debug!("generator layout ({:?}): {:#?}", ty, layout);
Ok(layout)

View File

@ -0,0 +1,150 @@
// Test that structs aligned to 128 bits are passed with the correct ABI on aarch64.
// revisions:linux darwin windows
//[linux] compile-flags: --target aarch64-unknown-linux-gnu
//[darwin] compile-flags: --target aarch64-apple-darwin
//[windows] compile-flags: --target aarch64-pc-windows-msvc
//[linux] needs-llvm-components: aarch64
//[darwin] needs-llvm-components: aarch64
//[windows] needs-llvm-components: aarch64
#![feature(no_core, lang_items)]
#![crate_type = "lib"]
#![no_core]
#[lang="sized"]
trait Sized { }
#[lang="freeze"]
trait Freeze { }
#[lang="copy"]
trait Copy { }
// Passed as `[i64 x 2]`, since it's an aggregate with size <= 128 bits, align < 128 bits.
#[repr(C)]
pub struct Align8 {
pub a: u64,
pub b: u64,
}
// repr(transparent), so same as above.
#[repr(transparent)]
pub struct Transparent8 {
a: Align8
}
// Passed as `[i64 x 2]`, since it's an aggregate with size <= 128 bits, align < 128 bits.
#[repr(C)]
pub struct Wrapped8 {
a: Align8,
}
extern "C" {
// linux: declare void @test_8([2 x i64], [2 x i64], [2 x i64])
// darwin: declare void @test_8([2 x i64], [2 x i64], [2 x i64])
// windows: declare void @test_8([2 x i64], [2 x i64], [2 x i64])
fn test_8(a: Align8, b: Transparent8, c: Wrapped8);
}
// Passed as `i128`, since it's an aggregate with size <= 128 bits, align = 128 bits.
// EXCEPT on Linux, where there's a special case to use its unadjusted alignment,
// making it the same as `Align8`, so it's be passed as `[i64 x 2]`.
#[repr(C)]
#[repr(align(16))]
pub struct Align16 {
pub a: u64,
pub b: u64,
}
// repr(transparent), so same as above.
#[repr(transparent)]
pub struct Transparent16 {
a: Align16
}
// Passed as `i128`, since it's an aggregate with size <= 128 bits, align = 128 bits.
// On Linux, the "unadjustedness" doesn't recurse into fields, so this is passed as `i128`.
#[repr(C)]
pub struct Wrapped16 {
pub a: Align16,
}
extern "C" {
// linux: declare void @test_16([2 x i64], [2 x i64], i128)
// darwin: declare void @test_16(i128, i128, i128)
// windows: declare void @test_16(i128, i128, i128)
fn test_16(a: Align16, b: Transparent16, c: Wrapped16);
}
// Passed as `i128`, since it's an aggregate with size <= 128 bits, align = 128 bits.
#[repr(C)]
pub struct I128 {
pub a: i128,
}
// repr(transparent), so same as above.
#[repr(transparent)]
pub struct TransparentI128 {
a: I128
}
// Passed as `i128`, since it's an aggregate with size <= 128 bits, align = 128 bits.
#[repr(C)]
pub struct WrappedI128 {
pub a: I128
}
extern "C" {
// linux: declare void @test_i128(i128, i128, i128)
// darwin: declare void @test_i128(i128, i128, i128)
// windows: declare void @test_i128(i128, i128, i128)
fn test_i128(a: I128, b: TransparentI128, c: WrappedI128);
}
// Passed as `[2 x i64]`, since it's an aggregate with size <= 128 bits, align < 128 bits.
// Note that the Linux special case does not apply, because packing is not considered "adjustment".
#[repr(C)]
#[repr(packed)]
pub struct Packed {
pub a: i128,
}
// repr(transparent), so same as above.
#[repr(transparent)]
pub struct TransparentPacked {
a: Packed
}
// Passed as `[2 x i64]`, since it's an aggregate with size <= 128 bits, align < 128 bits.
#[repr(C)]
pub struct WrappedPacked {
pub a: Packed
}
extern "C" {
// linux: declare void @test_packed([2 x i64], [2 x i64], [2 x i64])
// darwin: declare void @test_packed([2 x i64], [2 x i64], [2 x i64])
// windows: declare void @test_packed([2 x i64], [2 x i64], [2 x i64])
fn test_packed(a: Packed, b: TransparentPacked, c: WrappedPacked);
}
pub unsafe fn main(
a1: Align8, a2: Transparent8, a3: Wrapped8,
b1: Align16, b2: Transparent16, b3: Wrapped16,
c1: I128, c2: TransparentI128, c3: WrappedI128,
d1: Packed, d2: TransparentPacked, d3: WrappedPacked,
) {
test_8(a1, a2, a3);
test_16(b1, b2, b3);
test_i128(c1, c2, c3);
test_packed(d1, d2, d3);
}

View File

@ -6,7 +6,7 @@
// Test for the absence of `readonly` on the argument when it is mutated via `&raw const`.
// See <https://github.com/rust-lang/rust/issues/111502>.
// CHECK: i8 @foo(ptr noalias nocapture noundef dereferenceable(128) %x)
// CHECK: i8 @foo(ptr noalias nocapture noundef align 1 dereferenceable(128) %x)
#[no_mangle]
pub fn foo(x: [u8; 128]) -> u8 {
let ptr = core::ptr::addr_of!(x).cast_mut();
@ -16,7 +16,7 @@ pub fn foo(x: [u8; 128]) -> u8 {
x[0]
}
// CHECK: i1 @second(ptr noalias nocapture noundef dereferenceable({{[0-9]+}}) %a_ptr_and_b)
// CHECK: i1 @second(ptr noalias nocapture noundef align {{[0-9]+}} dereferenceable({{[0-9]+}}) %a_ptr_and_b)
#[no_mangle]
pub unsafe fn second(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool {
let b_bool_ptr = core::ptr::addr_of!(a_ptr_and_b.1.1).cast_mut();
@ -25,7 +25,7 @@ pub unsafe fn second(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool {
}
// If going through a deref (and there are no other mutating accesses), then `readonly` is fine.
// CHECK: i1 @third(ptr noalias nocapture noundef readonly dereferenceable({{[0-9]+}}) %a_ptr_and_b)
// CHECK: i1 @third(ptr noalias nocapture noundef readonly align {{[0-9]+}} dereferenceable({{[0-9]+}}) %a_ptr_and_b)
#[no_mangle]
pub unsafe fn third(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool {
let b_bool_ptr = core::ptr::addr_of!((*a_ptr_and_b.0).1).cast_mut();

View File

@ -0,0 +1,58 @@
// revisions:x86-linux x86-darwin
//[x86-linux] compile-flags: --target i686-unknown-linux-gnu
//[x86-linux] needs-llvm-components: x86
//[x86-darwin] compile-flags: --target i686-apple-darwin
//[x86-darwin] needs-llvm-components: x86
// Tests that aggregates containing vector types get their alignment increased to 16 on Darwin.
#![feature(no_core, lang_items, repr_simd, simd_ffi)]
#![crate_type = "lib"]
#![no_std]
#![no_core]
#![allow(non_camel_case_types)]
#[lang = "sized"]
trait Sized {}
#[lang = "freeze"]
trait Freeze {}
#[lang = "copy"]
trait Copy {}
#[repr(simd)]
pub struct i32x4(i32, i32, i32, i32);
#[repr(C)]
pub struct Foo {
a: i32x4,
b: i8,
}
// This tests that we recursively check for vector types, not just at the top level.
#[repr(C)]
pub struct DoubleFoo {
one: Foo,
two: Foo,
}
extern "C" {
// x86-linux: declare void @f({{.*}}byval(%Foo) align 4{{.*}})
// x86-darwin: declare void @f({{.*}}byval(%Foo) align 16{{.*}})
fn f(foo: Foo);
// x86-linux: declare void @g({{.*}}byval(%DoubleFoo) align 4{{.*}})
// x86-darwin: declare void @g({{.*}}byval(%DoubleFoo) align 16{{.*}})
fn g(foo: DoubleFoo);
}
pub fn main() {
unsafe { f(Foo { a: i32x4(1, 2, 3, 4), b: 0 }) }
unsafe {
g(DoubleFoo {
one: Foo { a: i32x4(1, 2, 3, 4), b: 0 },
two: Foo { a: i32x4(1, 2, 3, 4), b: 0 },
})
}
}

View File

@ -0,0 +1,342 @@
// ignore-tidy-linelength
// revisions:m68k wasm x86_64-linux x86_64-windows i686-linux i686-windows
//[m68k] compile-flags: --target m68k-unknown-linux-gnu
//[m68k] needs-llvm-components: m68k
//[wasm] compile-flags: --target wasm32-unknown-emscripten
//[wasm] needs-llvm-components: webassembly
//[x86_64-linux] compile-flags: --target x86_64-unknown-linux-gnu
//[x86_64-linux] needs-llvm-components: x86
//[x86_64-windows] compile-flags: --target x86_64-pc-windows-msvc
//[x86_64-windows] needs-llvm-components: x86
//[i686-linux] compile-flags: --target i686-unknown-linux-gnu
//[i686-linux] needs-llvm-components: x86
//[i686-windows] compile-flags: --target i686-pc-windows-msvc
//[i686-windows] needs-llvm-components: x86
// Tests that `byval` alignment is properly specified (#80127).
// The only targets that use `byval` are m68k, wasm, x86-64, and x86.
// Note also that Windows mandates a by-ref ABI here, so it does not use byval.
#![feature(no_core, lang_items)]
#![crate_type = "lib"]
#![no_std]
#![no_core]
#[lang="sized"] trait Sized { }
#[lang="freeze"] trait Freeze { }
#[lang="copy"] trait Copy { }
impl Copy for i32 {}
impl Copy for i64 {}
// This struct can be represented as a pair, so it exercises the OperandValue::Pair
// codepath in `codegen_argument`.
#[repr(C)]
pub struct NaturalAlign1 {
a: i8,
b: i8,
}
// This struct cannot be represented as an immediate, so it exercises the OperandValue::Ref
// codepath in `codegen_argument`.
#[repr(C)]
pub struct NaturalAlign2 {
a: [i16; 16],
b: i16,
}
#[repr(C)]
#[repr(align(4))]
pub struct ForceAlign4 {
a: [i8; 16],
b: i8,
}
// On i686-windows, this is passed on stack using `byval`
#[repr(C)]
pub struct NaturalAlign8 {
a: i64,
b: i64,
c: i64
}
// On i686-windows, this is passed by reference (because alignment is >4 and requested/forced),
// even though it has the exact same layout as `NaturalAlign8`!
#[repr(C)]
#[repr(align(8))]
pub struct ForceAlign8 {
a: i64,
b: i64,
c: i64
}
// On i686-windows, this is passed on stack, because requested alignment is <=4.
#[repr(C)]
#[repr(align(4))]
pub struct LowerFA8 {
a: i64,
b: i64,
c: i64
}
// On i686-windows, this is passed by reference, because it contains a field with
// requested/forced alignment.
#[repr(C)]
pub struct WrappedFA8 {
a: ForceAlign8
}
// On i686-windows, this has the same ABI as ForceAlign8, i.e. passed by reference.
#[repr(transparent)]
pub struct TransparentFA8 {
_0: (),
a: ForceAlign8
}
#[repr(C)]
#[repr(align(16))]
pub struct ForceAlign16 {
a: [i32; 16],
b: i8
}
// CHECK-LABEL: @call_na1
#[no_mangle]
pub unsafe fn call_na1(x: NaturalAlign1) {
// CHECK: start:
// m68k: [[ALLOCA:%[a-z0-9+]]] = alloca { i8, i8 }, align 1
// m68k: call void @natural_align_1({{.*}}byval({ i8, i8 }) align 1{{.*}} [[ALLOCA]])
// wasm: [[ALLOCA:%[a-z0-9+]]] = alloca { i8, i8 }, align 1
// wasm: call void @natural_align_1({{.*}}byval({ i8, i8 }) align 1{{.*}} [[ALLOCA]])
// x86_64-linux: call void @natural_align_1(i16
// x86_64-windows: call void @natural_align_1(i16
// i686-linux: [[ALLOCA:%[a-z0-9+]]] = alloca { i8, i8 }, align 4
// i686-linux: call void @natural_align_1({{.*}}byval({ i8, i8 }) align 4{{.*}} [[ALLOCA]])
// i686-windows: [[ALLOCA:%[a-z0-9+]]] = alloca { i8, i8 }, align 4
// i686-windows: call void @natural_align_1({{.*}}byval({ i8, i8 }) align 4{{.*}} [[ALLOCA]])
natural_align_1(x);
}
// CHECK-LABEL: @call_na2
#[no_mangle]
pub unsafe fn call_na2(x: NaturalAlign2) {
// CHECK: start:
// m68k-NEXT: call void @natural_align_2
// wasm-NEXT: call void @natural_align_2
// x86_64-linux-NEXT: call void @natural_align_2
// x86_64-windows-NEXT: call void @natural_align_2
// i686-linux: [[ALLOCA:%[0-9]+]] = alloca %NaturalAlign2, align 4
// i686-linux: call void @natural_align_2({{.*}}byval(%NaturalAlign2) align 4{{.*}} [[ALLOCA]])
// i686-windows: [[ALLOCA:%[0-9]+]] = alloca %NaturalAlign2, align 4
// i686-windows: call void @natural_align_2({{.*}}byval(%NaturalAlign2) align 4{{.*}} [[ALLOCA]])
natural_align_2(x);
}
// CHECK-LABEL: @call_fa4
#[no_mangle]
pub unsafe fn call_fa4(x: ForceAlign4) {
// CHECK: start:
// CHECK-NEXT: call void @force_align_4
force_align_4(x);
}
// CHECK-LABEL: @call_na8
#[no_mangle]
pub unsafe fn call_na8(x: NaturalAlign8) {
// CHECK: start:
// CHECK-NEXT: call void @natural_align_8
natural_align_8(x);
}
// CHECK-LABEL: @call_fa8
#[no_mangle]
pub unsafe fn call_fa8(x: ForceAlign8) {
// CHECK: start:
// CHECK-NEXT: call void @force_align_8
force_align_8(x);
}
// CHECK-LABEL: @call_lfa8
#[no_mangle]
pub unsafe fn call_lfa8(x: LowerFA8) {
// CHECK: start:
// CHECK-NEXT: call void @lower_fa8
lower_fa8(x);
}
// CHECK-LABEL: @call_wfa8
#[no_mangle]
pub unsafe fn call_wfa8(x: WrappedFA8) {
// CHECK: start:
// CHECK-NEXT: call void @wrapped_fa8
wrapped_fa8(x);
}
// CHECK-LABEL: @call_tfa8
#[no_mangle]
pub unsafe fn call_tfa8(x: TransparentFA8) {
// CHECK: start:
// CHECK-NEXT: call void @transparent_fa8
transparent_fa8(x);
}
// CHECK-LABEL: @call_fa16
#[no_mangle]
pub unsafe fn call_fa16(x: ForceAlign16) {
// CHECK: start:
// CHECK-NEXT: call void @force_align_16
force_align_16(x);
}
extern "C" {
// m68k: declare void @natural_align_1({{.*}}byval({ i8, i8 }) align 1{{.*}})
// wasm: declare void @natural_align_1({{.*}}byval({ i8, i8 }) align 1{{.*}})
// x86_64-linux: declare void @natural_align_1(i16)
// x86_64-windows: declare void @natural_align_1(i16)
// i686-linux: declare void @natural_align_1({{.*}}byval({ i8, i8 }) align 4{{.*}})
// i686-windows: declare void @natural_align_1({{.*}}byval({ i8, i8 }) align 4{{.*}})
fn natural_align_1(x: NaturalAlign1);
// m68k: declare void @natural_align_2({{.*}}byval(%NaturalAlign2) align 2{{.*}})
// wasm: declare void @natural_align_2({{.*}}byval(%NaturalAlign2) align 2{{.*}})
// x86_64-linux: declare void @natural_align_2({{.*}}byval(%NaturalAlign2) align 2{{.*}})
// x86_64-windows: declare void @natural_align_2(
// x86_64-windows-NOT: byval
// x86_64-windows-SAME: align 2{{.*}})
// i686-linux: declare void @natural_align_2({{.*}}byval(%NaturalAlign2) align 4{{.*}})
// i686-windows: declare void @natural_align_2({{.*}}byval(%NaturalAlign2) align 4{{.*}})
fn natural_align_2(x: NaturalAlign2);
// m68k: declare void @force_align_4({{.*}}byval(%ForceAlign4) align 4{{.*}})
// wasm: declare void @force_align_4({{.*}}byval(%ForceAlign4) align 4{{.*}})
// x86_64-linux: declare void @force_align_4({{.*}}byval(%ForceAlign4) align 4{{.*}})
// x86_64-windows: declare void @force_align_4(
// x86_64-windows-NOT: byval
// x86_64-windows-SAME: align 4{{.*}})
// i686-linux: declare void @force_align_4({{.*}}byval(%ForceAlign4) align 4{{.*}})
// i686-windows: declare void @force_align_4({{.*}}byval(%ForceAlign4) align 4{{.*}})
fn force_align_4(x: ForceAlign4);
// m68k: declare void @natural_align_8({{.*}}byval(%NaturalAlign8) align 4{{.*}})
// wasm: declare void @natural_align_8({{.*}}byval(%NaturalAlign8) align 8{{.*}})
// x86_64-linux: declare void @natural_align_8({{.*}}byval(%NaturalAlign8) align 8{{.*}})
// x86_64-windows: declare void @natural_align_8(
// x86_64-windows-NOT: byval
// x86_64-windows-SAME: align 8{{.*}})
// i686-linux: declare void @natural_align_8({{.*}}byval(%NaturalAlign8) align 4{{.*}})
// i686-windows: declare void @natural_align_8({{.*}}byval(%NaturalAlign8) align 4{{.*}})
fn natural_align_8(x: NaturalAlign8);
// m68k: declare void @force_align_8({{.*}}byval(%ForceAlign8) align 8{{.*}})
// wasm: declare void @force_align_8({{.*}}byval(%ForceAlign8) align 8{{.*}})
// x86_64-linux: declare void @force_align_8({{.*}}byval(%ForceAlign8) align 8{{.*}})
// x86_64-windows: declare void @force_align_8(
// x86_64-windows-NOT: byval
// x86_64-windows-SAME: align 8{{.*}})
// i686-linux: declare void @force_align_8({{.*}}byval(%ForceAlign8) align 4{{.*}})
// i686-windows: declare void @force_align_8(
// i686-windows-NOT: byval
// i686-windows-SAME: align 8{{.*}})
fn force_align_8(x: ForceAlign8);
// m68k: declare void @lower_fa8({{.*}}byval(%LowerFA8) align 4{{.*}})
// wasm: declare void @lower_fa8({{.*}}byval(%LowerFA8) align 8{{.*}})
// x86_64-linux: declare void @lower_fa8({{.*}}byval(%LowerFA8) align 8{{.*}})
// x86_64-windows: declare void @lower_fa8(
// x86_64-windows-NOT: byval
// x86_64-windows-SAME: align 8{{.*}})
// i686-linux: declare void @lower_fa8({{.*}}byval(%LowerFA8) align 4{{.*}})
// i686-windows: declare void @lower_fa8({{.*}}byval(%LowerFA8) align 4{{.*}})
fn lower_fa8(x: LowerFA8);
// m68k: declare void @wrapped_fa8({{.*}}byval(%WrappedFA8) align 8{{.*}})
// wasm: declare void @wrapped_fa8({{.*}}byval(%WrappedFA8) align 8{{.*}})
// x86_64-linux: declare void @wrapped_fa8({{.*}}byval(%WrappedFA8) align 8{{.*}})
// x86_64-windows: declare void @wrapped_fa8(
// x86_64-windows-NOT: byval
// x86_64-windows-SAME: align 8{{.*}})
// i686-linux: declare void @wrapped_fa8({{.*}}byval(%WrappedFA8) align 4{{.*}})
// i686-windows: declare void @wrapped_fa8(
// i686-windows-NOT: byval
// i686-windows-SAME: align 8{{.*}})
fn wrapped_fa8(x: WrappedFA8);
// m68k: declare void @transparent_fa8({{.*}}byval(%TransparentFA8) align 8{{.*}})
// wasm: declare void @transparent_fa8({{.*}}byval(%TransparentFA8) align 8{{.*}})
// x86_64-linux: declare void @transparent_fa8({{.*}}byval(%TransparentFA8) align 8{{.*}})
// x86_64-windows: declare void @transparent_fa8(
// x86_64-windows-NOT: byval
// x86_64-windows-SAME: align 8{{.*}})
// i686-linux: declare void @transparent_fa8({{.*}}byval(%TransparentFA8) align 4{{.*}})
// i686-windows: declare void @transparent_fa8(
// i686-windows-NOT: byval
// i686-windows-SAME: align 8{{.*}})
fn transparent_fa8(x: TransparentFA8);
// m68k: declare void @force_align_16({{.*}}byval(%ForceAlign16) align 16{{.*}})
// wasm: declare void @force_align_16({{.*}}byval(%ForceAlign16) align 16{{.*}})
// x86_64-linux: declare void @force_align_16({{.*}}byval(%ForceAlign16) align 16{{.*}})
// x86_64-windows: declare void @force_align_16(
// x86_64-windows-NOT: byval
// x86_64-windows-SAME: align 16{{.*}})
// i686-linux: declare void @force_align_16({{.*}}byval(%ForceAlign16) align 4{{.*}})
// i686-windows: declare void @force_align_16(
// i686-windows-NOT: byval
// i686-windows-SAME: align 16{{.*}})
fn force_align_16(x: ForceAlign16);
}

View File

@ -30,7 +30,6 @@ pub fn short_integer_map(x: [u32; 8]) -> [u32; 8] {
pub fn long_integer_map(x: [u32; 512]) -> [u32; 512] {
// CHECK: start:
// CHECK-NEXT: alloca [512 x i32]
// CHECK-NEXT: alloca %"core::mem::manually_drop::ManuallyDrop<[u32; 512]>"
// CHECK-NOT: alloca
// CHECK: mul <{{[0-9]+}} x i32>
// CHECK: add <{{[0-9]+}} x i32>

View File

@ -42,7 +42,7 @@ pub fn borrow_call(x: &i32, f: fn(&i32) -> &i32) -> &i32 {
f(x)
}
// CHECK: void @struct_({{%S\*|ptr}} sret(%S){{( %_0)?}}, {{%S\*|ptr}} %x)
// CHECK: void @struct_({{%S\*|ptr}} sret(%S) align 4{{( %_0)?}}, {{%S\*|ptr}} align 4 %x)
#[no_mangle]
pub fn struct_(x: S) -> S {
x
@ -51,7 +51,7 @@ pub fn struct_(x: S) -> S {
// CHECK-LABEL: @struct_call
#[no_mangle]
pub fn struct_call(x: S, f: fn(S) -> S) -> S {
// CHECK: call void %f({{%S\*|ptr}} sret(%S){{( %_0)?}}, {{%S\*|ptr}} %{{.+}})
// CHECK: call void %f({{%S\*|ptr}} sret(%S) align 4{{( %_0)?}}, {{%S\*|ptr}} align 4 %{{.+}})
f(x)
}

View File

@ -142,7 +142,7 @@ pub fn mutable_notunpin_borrow(_: &mut NotUnpin) {
pub fn notunpin_borrow(_: &NotUnpin) {
}
// CHECK: @indirect_struct({{%S\*|ptr}} noalias nocapture noundef readonly dereferenceable(32) %_1)
// CHECK: @indirect_struct({{%S\*|ptr}} noalias nocapture noundef readonly align 4 dereferenceable(32) %_1)
#[no_mangle]
pub fn indirect_struct(_: S) {
}
@ -188,7 +188,7 @@ pub fn notunpin_box(x: Box<NotUnpin>) -> Box<NotUnpin> {
x
}
// CHECK: @struct_return({{%S\*|ptr}} noalias nocapture noundef sret(%S) dereferenceable(32){{( %_0)?}})
// CHECK: @struct_return({{%S\*|ptr}} noalias nocapture noundef sret(%S) align 4 dereferenceable(32){{( %_0)?}})
#[no_mangle]
pub fn struct_return() -> S {
S {

View File

@ -0,0 +1,6 @@
# ignore-cross-compile
include ../tools.mk
all: $(call NATIVE_STATICLIB,test)
$(RUSTC) test.rs
$(call RUN,test) || exit 1

View File

@ -0,0 +1,93 @@
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
struct BoolAndU32
{
bool a;
uint32_t b;
};
#ifdef _MSC_VER
__declspec(align(16))
struct TwoU64s
{
uint64_t a;
uint64_t b;
};
#else
struct __attribute__((aligned(16))) TwoU64s
{
uint64_t a;
uint64_t b;
};
#endif
struct WrappedU64s
{
struct TwoU64s a;
};
#ifdef _MSC_VER
__declspec(align(1))
struct LowerAlign
{
uint64_t a;
uint64_t b;
};
#else
struct __attribute__((aligned(1))) LowerAlign
{
uint64_t a;
uint64_t b;
};
#endif
#pragma pack(push, 1)
struct Packed
{
uint64_t a;
uint64_t b;
};
#pragma pack(pop)
int32_t many_args(
void *a,
void *b,
const char *c,
uint64_t d,
bool e,
struct BoolAndU32 f,
void *g,
struct TwoU64s h,
void *i,
struct WrappedU64s j,
void *k,
struct LowerAlign l,
void *m,
struct Packed n,
const char *o)
{
assert(!a);
assert(!b);
assert(!c);
assert(d == 42);
assert(e);
assert(f.a);
assert(f.b == 1337);
assert(!g);
assert(h.a == 1);
assert(h.b == 2);
assert(!i);
assert(j.a.a == 3);
assert(j.a.b == 4);
assert(!k);
assert(l.a == 5);
assert(l.b == 6);
assert(!m);
assert(n.a == 7);
assert(n.b == 8);
assert(strcmp(o, "Hello world") == 0);
return 0;
}

View File

@ -0,0 +1,89 @@
// Issue #80127: Passing structs via FFI should work with explicit alignment.
use std::ffi::{CStr, c_char};
use std::ptr::null_mut;
#[repr(C)]
pub struct BoolAndU32 {
pub a: bool,
pub b: u32,
}
#[repr(C)]
#[repr(align(16))]
pub struct TwoU64s {
pub a: u64,
pub b: u64,
}
#[repr(C)]
pub struct WrappedU64s {
pub a: TwoU64s
}
#[repr(C)]
// Even though requesting align 1 can never change the alignment, it still affects the ABI
// on some platforms like i686-windows.
#[repr(align(1))]
pub struct LowerAlign {
pub a: u64,
pub b: u64,
}
#[repr(C)]
#[repr(packed)]
pub struct Packed {
pub a: u64,
pub b: u64,
}
#[link(name = "test", kind = "static")]
extern "C" {
fn many_args(
a: *mut (),
b: *mut (),
c: *const c_char,
d: u64,
e: bool,
f: BoolAndU32,
g: *mut (),
h: TwoU64s,
i: *mut (),
j: WrappedU64s,
k: *mut (),
l: LowerAlign,
m: *mut (),
n: Packed,
o: *const c_char,
) -> i32;
}
const STRING: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"Hello world\0") };
fn main() {
let bool_and_u32 = BoolAndU32 { a: true, b: 1337 };
let two_u64s = TwoU64s { a: 1, b: 2 };
let wrapped = WrappedU64s { a: TwoU64s { a: 3, b: 4 } };
let lower = LowerAlign { a: 5, b: 6 };
let packed = Packed { a: 7, b: 8 };
let string = STRING;
unsafe {
many_args(
null_mut(),
null_mut(),
null_mut(),
42,
true,
bool_and_u32,
null_mut(),
two_u64s,
null_mut(),
wrapped,
null_mut(),
lower,
null_mut(),
packed,
string.as_ptr(),
);
}
}

View File

@ -28,6 +28,14 @@ struct Huge {
int32_t e;
};
struct Huge64 {
int64_t a;
int64_t b;
int64_t c;
int64_t d;
int64_t e;
};
struct FloatPoint {
double x;
double y;
@ -152,6 +160,21 @@ void byval_rect_with_many_huge(struct Huge a, struct Huge b, struct Huge c,
assert(g.d == 420);
}
// System V x86_64 ABI:
// a, b, d, e, f should be byval pointer (on the stack)
// g passed via register (fixes #41375)
//
// i686-windows ABI:
// a, b, d, e, f, g should be byval pointer
void byval_rect_with_many_huge64(struct Huge64 a, struct Huge64 b, struct Huge64 c,
struct Huge64 d, struct Huge64 e, struct Huge64 f,
struct Rect g) {
assert(g.a == 1234);
assert(g.b == 4567);
assert(g.c == 7890);
assert(g.d == 4209);
}
// System V x86_64 & Win64 ABI:
// a, b should be in registers
// s should be split across 2 integer registers
@ -279,6 +302,19 @@ struct Huge huge_struct(struct Huge s) {
return s;
}
// System V x86_64 & i686-windows ABI:
// s should be byval pointer
// return should in a hidden sret pointer
struct Huge64 huge64_struct(struct Huge64 s) {
assert(s.a == 1234);
assert(s.b == 1335);
assert(s.c == 1436);
assert(s.d == 1537);
assert(s.e == 1638);
return s;
}
// System V x86_64 ABI:
// p should be in registers
// return should be in registers

View File

@ -36,6 +36,16 @@ struct Huge {
e: i32,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
struct Huge64 {
a: i64,
b: i64,
c: i64,
d: i64,
e: i64,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
struct FloatPoint {
@ -79,6 +89,12 @@ extern "C" {
fn byval_rect_with_many_huge(a: Huge, b: Huge, c: Huge, d: Huge, e: Huge, f: Huge, g: Rect);
fn byval_rect_with_many_huge64(
a: Huge64, b: Huge64, c: Huge64,
d: Huge64, e: Huge64, f: Huge64,
g: Rect,
);
fn split_rect(a: i32, b: i32, s: Rect);
fn split_rect_floats(a: f32, b: f32, s: FloatRect);
@ -95,6 +111,8 @@ extern "C" {
fn huge_struct(s: Huge) -> Huge;
fn huge64_struct(s: Huge64) -> Huge64;
fn float_point(p: FloatPoint) -> FloatPoint;
fn float_one(f: FloatOne) -> FloatOne;
@ -107,6 +125,7 @@ fn main() {
let t = BiggerRect { s: s, a: 27834, b: 7657 };
let u = FloatRect { a: 3489, b: 3490, c: 8. };
let v = Huge { a: 5647, b: 5648, c: 5649, d: 5650, e: 5651 };
let w = Huge64 { a: 1234, b: 1335, c: 1436, d: 1537, e: 1638 };
let p = FloatPoint { x: 5., y: -3. };
let f1 = FloatOne { x: 7. };
let i = IntOdd { a: 1, b: 2, c: 3 };
@ -117,12 +136,14 @@ fn main() {
byval_rect_floats(1., 2., 3., 4., 5., 6., 7., s, u);
byval_rect_with_float(1, 2, 3.0, 4, 5, 6, s);
byval_rect_with_many_huge(v, v, v, v, v, v, Rect { a: 123, b: 456, c: 789, d: 420 });
byval_rect_with_many_huge64(w, w, w, w, w, w, Rect { a: 1234, b: 4567, c: 7890, d: 4209 });
split_rect(1, 2, s);
split_rect_floats(1., 2., u);
split_rect_with_floats(1, 2, 3.0, 4, 5.0, 6, s);
split_and_byval_rect(1, 2, 3, s, s);
split_rect(1, 2, s);
assert_eq!(huge_struct(v), v);
assert_eq!(huge64_struct(w), w);
assert_eq!(split_ret_byval_struct(1, 2, s), s);
assert_eq!(sret_byval_struct(1, 2, 3, 4, s), t);
assert_eq!(sret_split_struct(1, 2, s), t);

View File

@ -53,6 +53,8 @@ error: layout_of(E) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
Layout {
size: Size(12 bytes),
@ -77,9 +79,13 @@ error: layout_of(E) = Layout {
variants: Single {
index: 1,
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
}
--> $DIR/debug.rs:7:1
|
@ -124,6 +130,8 @@ error: layout_of(S) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
}
--> $DIR/debug.rs:10:1
|
@ -146,6 +154,8 @@ error: layout_of(U) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
}
--> $DIR/debug.rs:13:1
|
@ -237,6 +247,8 @@ error: layout_of(std::result::Result<i32, i32>) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
},
Layout {
size: Size(8 bytes),
@ -272,9 +284,13 @@ error: layout_of(std::result::Result<i32, i32>) = Layout {
variants: Single {
index: 1,
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
}
--> $DIR/debug.rs:16:1
|
@ -301,6 +317,8 @@ error: layout_of(i32) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
}
--> $DIR/debug.rs:19:1
|
@ -323,6 +341,8 @@ error: layout_of(V) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(2 bytes),
}
--> $DIR/debug.rs:22:1
|
@ -345,6 +365,8 @@ error: layout_of(W) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(2 bytes),
}
--> $DIR/debug.rs:28:1
|
@ -367,6 +389,8 @@ error: layout_of(Y) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(2 bytes),
}
--> $DIR/debug.rs:34:1
|
@ -389,6 +413,8 @@ error: layout_of(P1) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
}
--> $DIR/debug.rs:41:1
|
@ -411,6 +437,8 @@ error: layout_of(P2) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
}
--> $DIR/debug.rs:45:1
|
@ -433,6 +461,8 @@ error: layout_of(P3) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
}
--> $DIR/debug.rs:53:1
|
@ -455,6 +485,8 @@ error: layout_of(P4) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
}
--> $DIR/debug.rs:57:1
|
@ -482,6 +514,8 @@ error: layout_of(P5) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
}
--> $DIR/debug.rs:61:1
|
@ -509,6 +543,8 @@ error: layout_of(std::mem::MaybeUninit<u8>) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
}
--> $DIR/debug.rs:64:1
|

View File

@ -59,9 +59,13 @@ error: layout_of(A) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
}
--> $DIR/hexagon-enum.rs:16:1
|
@ -129,9 +133,13 @@ error: layout_of(B) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
}
--> $DIR/hexagon-enum.rs:20:1
|
@ -199,9 +207,13 @@ error: layout_of(C) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(2 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(2 bytes),
}
--> $DIR/hexagon-enum.rs:24:1
|
@ -269,9 +281,13 @@ error: layout_of(P) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
}
--> $DIR/hexagon-enum.rs:28:1
|
@ -339,9 +355,13 @@ error: layout_of(T) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
}
--> $DIR/hexagon-enum.rs:34:1
|

View File

@ -81,6 +81,8 @@ error: layout_of(MissingPayloadField) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
Layout {
size: Size(1 bytes),
@ -99,9 +101,13 @@ error: layout_of(MissingPayloadField) = Layout {
variants: Single {
index: 1,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
}
--> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:16:1
|
@ -193,6 +199,8 @@ error: layout_of(CommonPayloadField) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
Layout {
size: Size(2 bytes),
@ -228,9 +236,13 @@ error: layout_of(CommonPayloadField) = Layout {
variants: Single {
index: 1,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
}
--> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:25:1
|
@ -320,6 +332,8 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
Layout {
size: Size(2 bytes),
@ -354,9 +368,13 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout {
variants: Single {
index: 1,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
}
--> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:33:1
|
@ -462,6 +480,8 @@ error: layout_of(NicheFirst) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
Layout {
size: Size(0 bytes),
@ -480,6 +500,8 @@ error: layout_of(NicheFirst) = Layout {
variants: Single {
index: 1,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
Layout {
size: Size(0 bytes),
@ -498,9 +520,13 @@ error: layout_of(NicheFirst) = Layout {
variants: Single {
index: 2,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
}
--> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:41:1
|
@ -606,6 +632,8 @@ error: layout_of(NicheSecond) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
Layout {
size: Size(0 bytes),
@ -624,6 +652,8 @@ error: layout_of(NicheSecond) = Layout {
variants: Single {
index: 1,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
Layout {
size: Size(0 bytes),
@ -642,9 +672,13 @@ error: layout_of(NicheSecond) = Layout {
variants: Single {
index: 2,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
}
--> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:50:1
|

View File

@ -53,6 +53,10 @@ error: layout_of(Aligned1) = Layout {
variants: Single {
index: 0,
},
max_repr_align: Some(
Align(8 bytes),
),
unadjusted_abi_align: Align(1 bytes),
},
Layout {
size: Size(8 bytes),
@ -71,9 +75,17 @@ error: layout_of(Aligned1) = Layout {
variants: Single {
index: 1,
},
max_repr_align: Some(
Align(8 bytes),
),
unadjusted_abi_align: Align(1 bytes),
},
],
},
max_repr_align: Some(
Align(8 bytes),
),
unadjusted_abi_align: Align(1 bytes),
}
--> $DIR/issue-96185-overaligned-enum.rs:8:1
|
@ -141,6 +153,10 @@ error: layout_of(Aligned2) = Layout {
variants: Single {
index: 0,
},
max_repr_align: Some(
Align(1 bytes),
),
unadjusted_abi_align: Align(1 bytes),
},
Layout {
size: Size(1 bytes),
@ -159,9 +175,17 @@ error: layout_of(Aligned2) = Layout {
variants: Single {
index: 1,
},
max_repr_align: Some(
Align(1 bytes),
),
unadjusted_abi_align: Align(1 bytes),
},
],
},
max_repr_align: Some(
Align(1 bytes),
),
unadjusted_abi_align: Align(1 bytes),
}
--> $DIR/issue-96185-overaligned-enum.rs:16:1
|

View File

@ -59,9 +59,13 @@ error: layout_of(A) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
}
--> $DIR/thumb-enum.rs:16:1
|
@ -129,9 +133,13 @@ error: layout_of(B) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
}
--> $DIR/thumb-enum.rs:20:1
|
@ -199,9 +207,13 @@ error: layout_of(C) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(2 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(2 bytes),
}
--> $DIR/thumb-enum.rs:24:1
|
@ -269,9 +281,13 @@ error: layout_of(P) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
}
--> $DIR/thumb-enum.rs:28:1
|
@ -339,9 +355,13 @@ error: layout_of(T) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
}
--> $DIR/thumb-enum.rs:34:1
|

View File

@ -57,6 +57,8 @@ error: layout_of(std::result::Result<[u32; 0], bool>) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
},
Layout {
size: Size(2 bytes),
@ -88,9 +90,13 @@ error: layout_of(std::result::Result<[u32; 0], bool>) = Layout {
variants: Single {
index: 1,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
}
--> $DIR/zero-sized-array-enum-niche.rs:13:1
|
@ -156,6 +162,8 @@ error: layout_of(MultipleAlignments) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(2 bytes),
},
Layout {
size: Size(4 bytes),
@ -178,6 +186,8 @@ error: layout_of(MultipleAlignments) = Layout {
variants: Single {
index: 1,
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
},
Layout {
size: Size(2 bytes),
@ -209,9 +219,13 @@ error: layout_of(MultipleAlignments) = Layout {
variants: Single {
index: 2,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
}
--> $DIR/zero-sized-array-enum-niche.rs:21:1
|
@ -277,6 +291,8 @@ error: layout_of(std::result::Result<[u32; 0], Packed<std::num::NonZeroU16>>) =
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
},
Layout {
size: Size(3 bytes),
@ -308,9 +324,13 @@ error: layout_of(std::result::Result<[u32; 0], Packed<std::num::NonZeroU16>>) =
variants: Single {
index: 1,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
}
--> $DIR/zero-sized-array-enum-niche.rs:37:1
|
@ -380,6 +400,8 @@ error: layout_of(std::result::Result<[u32; 0], Packed<U16IsZero>>) = Layout {
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
},
Layout {
size: Size(2 bytes),
@ -411,9 +433,13 @@ error: layout_of(std::result::Result<[u32; 0], Packed<U16IsZero>>) = Layout {
variants: Single {
index: 1,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
},
],
},
max_repr_align: None,
unadjusted_abi_align: Align(4 bytes),
}
--> $DIR/zero-sized-array-enum-niche.rs:44:1
|