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:
commit
7a17f577b3
32 changed files with 1251 additions and 93 deletions
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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)
|
||||
|
|
150
tests/codegen/aarch64-struct-align-128.rs
Normal file
150
tests/codegen/aarch64-struct-align-128.rs
Normal 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);
|
||||
}
|
|
@ -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();
|
||||
|
|
58
tests/codegen/align-byval-vector.rs
Normal file
58
tests/codegen/align-byval-vector.rs
Normal 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 },
|
||||
})
|
||||
}
|
||||
}
|
342
tests/codegen/align-byval.rs
Normal file
342
tests/codegen/align-byval.rs
Normal 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);
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
6
tests/run-make/extern-fn-explicit-align/Makefile
Normal file
6
tests/run-make/extern-fn-explicit-align/Makefile
Normal file
|
@ -0,0 +1,6 @@
|
|||
# ignore-cross-compile
|
||||
include ../tools.mk
|
||||
|
||||
all: $(call NATIVE_STATICLIB,test)
|
||||
$(RUSTC) test.rs
|
||||
$(call RUN,test) || exit 1
|
93
tests/run-make/extern-fn-explicit-align/test.c
Normal file
93
tests/run-make/extern-fn-explicit-align/test.c
Normal 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;
|
||||
}
|
89
tests/run-make/extern-fn-explicit-align/test.rs
Normal file
89
tests/run-make/extern-fn-explicit-align/test.rs
Normal 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(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
||||
|
|
|
@ -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
|
||||
|
|
||||
|
|
|
@ -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
|
||||
|
|
||||
|
|
|
@ -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
|
||||
|
|
||||
|
|
|
@ -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
|
||||
|
|
||||
|
|
|
@ -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
|
||||
|
|
||||
|
|
Loading…
Add table
Reference in a new issue