Skip to content

Commit d1e764c

Browse files
aarch64-linux: properly handle 128bit aligned aggregates
1 parent 7e933b4 commit d1e764c

File tree

15 files changed

+370
-48
lines changed

15 files changed

+370
-48
lines changed

compiler/rustc_abi/src/layout.rs

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ pub trait LayoutCalculator {
4141
align,
4242
size,
4343
repr_align: None,
44+
unadjusted_abi_align: align.abi,
4445
}
4546
}
4647

@@ -124,6 +125,7 @@ pub trait LayoutCalculator {
124125
align: dl.i8_align,
125126
size: Size::ZERO,
126127
repr_align: None,
128+
unadjusted_abi_align: dl.i8_align.abi,
127129
}
128130
}
129131

@@ -291,13 +293,16 @@ pub trait LayoutCalculator {
291293
}
292294

293295
let mut align = dl.aggregate_align;
296+
let mut unadjusted_abi_align = align.abi;
297+
294298
let mut variant_layouts = variants
295299
.iter_enumerated()
296300
.map(|(j, v)| {
297301
let mut st = self.univariant(dl, v, repr, StructKind::AlwaysSized)?;
298302
st.variants = Variants::Single { index: j };
299303

300304
align = align.max(st.align);
305+
unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align);
301306

302307
Some(st)
303308
})
@@ -425,6 +430,7 @@ pub trait LayoutCalculator {
425430
size,
426431
align,
427432
repr_align: repr.align,
433+
unadjusted_abi_align,
428434
};
429435

430436
Some(TmpLayout { layout, variants: variant_layouts })
@@ -459,6 +465,8 @@ pub trait LayoutCalculator {
459465
let (min_ity, signed) = discr_range_of_repr(min, max); //Integer::repr_discr(tcx, ty, &repr, min, max);
460466

461467
let mut align = dl.aggregate_align;
468+
let mut unadjusted_abi_align = align.abi;
469+
462470
let mut size = Size::ZERO;
463471

464472
// We're interested in the smallest alignment, so start large.
@@ -501,6 +509,7 @@ pub trait LayoutCalculator {
501509
}
502510
size = cmp::max(size, st.size);
503511
align = align.max(st.align);
512+
unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align);
504513
Some(st)
505514
})
506515
.collect::<Option<IndexVec<VariantIdx, _>>>()?;
@@ -695,6 +704,7 @@ pub trait LayoutCalculator {
695704
align,
696705
size,
697706
repr_align: repr.align,
707+
unadjusted_abi_align,
698708
};
699709

700710
let tagged_layout = TmpLayout { layout: tagged_layout, variants: layout_variants };
@@ -735,10 +745,6 @@ pub trait LayoutCalculator {
735745
let dl = dl.borrow();
736746
let mut align = if repr.pack.is_some() { dl.i8_align } else { dl.aggregate_align };
737747

738-
if let Some(repr_align) = repr.align {
739-
align = align.max(AbiAndPrefAlign::new(repr_align));
740-
}
741-
742748
// If all the non-ZST fields have the same ABI and union ABI optimizations aren't
743749
// disabled, we can use that common ABI for the union as a whole.
744750
struct AbiMismatch;
@@ -791,6 +797,14 @@ pub trait LayoutCalculator {
791797
if let Some(pack) = repr.pack {
792798
align = align.min(AbiAndPrefAlign::new(pack));
793799
}
800+
// The unadjusted ABI alignment does not include repr(align), but does include repr(pack).
801+
// See documentation on `LayoutS::unadjusted_abi_align`.
802+
let unadjusted_abi_align = align.abi;
803+
if let Some(repr_align) = repr.align {
804+
align = align.max(AbiAndPrefAlign::new(repr_align));
805+
}
806+
// `align` must not be modified after this, or `unadjusted_abi_align` could be inaccurate.
807+
let align = align;
794808

795809
// If all non-ZST fields have the same ABI, we may forward that ABI
796810
// for the union as a whole, unless otherwise inhibited.
@@ -814,6 +828,7 @@ pub trait LayoutCalculator {
814828
align,
815829
size: size.align_to(align.abi),
816830
repr_align: repr.align,
831+
unadjusted_abi_align,
817832
})
818833
}
819834
}
@@ -1023,9 +1038,16 @@ fn univariant(
10231038

10241039
offset = offset.checked_add(field.size(), dl)?;
10251040
}
1041+
1042+
// The unadjusted ABI alignment does not include repr(align), but does include repr(pack).
1043+
// See documentation on `LayoutS::unadjusted_abi_align`.
1044+
let unadjusted_abi_align = align.abi;
10261045
if let Some(repr_align) = repr.align {
10271046
align = align.max(AbiAndPrefAlign::new(repr_align));
10281047
}
1048+
// `align` must not be modified after this point, or `unadjusted_abi_align` could be inaccurate.
1049+
let align = align;
1050+
10291051
debug!("univariant min_size: {:?}", offset);
10301052
let min_size = offset;
10311053
// As stated above, inverse_memory_index holds field indices by increasing offset.
@@ -1111,9 +1133,29 @@ fn univariant(
11111133
abi = Abi::Uninhabited;
11121134
}
11131135

1114-
let repr_align = repr.align.or_else(|| {
1115-
if repr.transparent() { layout_of_single_non_zst_field?.repr_align() } else { None }
1116-
});
1136+
let (repr_align, unadjusted_abi_align) = if repr.transparent() {
1137+
match layout_of_single_non_zst_field {
1138+
Some(l) => (l.repr_align(), l.unadjusted_abi_align()),
1139+
None => {
1140+
// `repr(transparent)` with all ZST fields.
1141+
//
1142+
// Using `None` for `repr_align` here is technically incorrect, since one of
1143+
// the ZSTs could have `repr(align(1))`. It's an interesting question, if you have
1144+
// `#{repr(transparent)] struct Foo((), ZstWithReprAlign1)`, which of those ZSTs'
1145+
// ABIs is forwarded by `repr(transparent)`? The answer to that question determines
1146+
// whether we should use `None` or `Some(align 1)` here. Thanksfully, two things
1147+
// together mean this doesn't matter:
1148+
// - You're not allowed to have a `repr(transparent)` struct that contains
1149+
// `repr(align)` > 1 ZSTs. See error E0691.
1150+
// - MSVC never treats requested align 1 differently from natural align 1.
1151+
// (And the `repr_align` field is only used on i686-windows, see `LayoutS` docs.)
1152+
// So just use `None` for now.
1153+
(None, align.abi)
1154+
}
1155+
}
1156+
} else {
1157+
(repr.align, unadjusted_abi_align)
1158+
};
11171159

11181160
Some(LayoutS {
11191161
variants: Variants::Single { index: FIRST_VARIANT },
@@ -1123,6 +1165,7 @@ fn univariant(
11231165
align,
11241166
size,
11251167
repr_align,
1168+
unadjusted_abi_align,
11261169
})
11271170
}
11281171

compiler/rustc_abi/src/lib.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1536,6 +1536,11 @@ pub struct LayoutS {
15361536
/// Only used on i686-windows, where the argument passing ABI is different when alignment is
15371537
/// requested, even if the requested alignment is equal to the natural alignment.
15381538
pub repr_align: Option<Align>,
1539+
1540+
/// The alignment the type would have, ignoring any `repr(align)` but including `repr(packed)`.
1541+
/// Only used on aarch64-linux, where the argument passing ABI ignores the requested alignment
1542+
/// in some cases.
1543+
pub unadjusted_abi_align: Align,
15391544
}
15401545

15411546
impl LayoutS {
@@ -1551,6 +1556,7 @@ impl LayoutS {
15511556
size,
15521557
align,
15531558
repr_align: None,
1559+
unadjusted_abi_align: align.abi,
15541560
}
15551561
}
15561562
}
@@ -1560,7 +1566,16 @@ impl fmt::Debug for LayoutS {
15601566
// This is how `Layout` used to print before it become
15611567
// `Interned<LayoutS>`. We print it like this to avoid having to update
15621568
// expected output in a lot of tests.
1563-
let LayoutS { size, align, abi, fields, largest_niche, variants, repr_align } = self;
1569+
let LayoutS {
1570+
size,
1571+
align,
1572+
abi,
1573+
fields,
1574+
largest_niche,
1575+
variants,
1576+
repr_align,
1577+
unadjusted_abi_align,
1578+
} = self;
15641579
f.debug_struct("Layout")
15651580
.field("size", size)
15661581
.field("align", align)
@@ -1569,6 +1584,7 @@ impl fmt::Debug for LayoutS {
15691584
.field("largest_niche", largest_niche)
15701585
.field("variants", variants)
15711586
.field("repr_align", repr_align)
1587+
.field("unadjusted_abi_align", unadjusted_abi_align)
15721588
.finish()
15731589
}
15741590
}
@@ -1613,6 +1629,10 @@ impl<'a> Layout<'a> {
16131629
self.0.0.repr_align
16141630
}
16151631

1632+
pub fn unadjusted_abi_align(self) -> Align {
1633+
self.0.0.unadjusted_abi_align
1634+
}
1635+
16161636
/// Whether the layout is from a type that implements [`std::marker::PointerLike`].
16171637
///
16181638
/// Currently, that means that the type is pointer-sized, pointer-aligned,

compiler/rustc_middle/src/ty/layout.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,7 @@ where
756756
align: tcx.data_layout.i8_align,
757757
size: Size::ZERO,
758758
repr_align: None,
759+
unadjusted_abi_align: tcx.data_layout.i8_align.abi,
759760
})
760761
}
761762

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,15 @@
11
use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform};
22
use crate::abi::{HasDataLayout, TyAbiInterface};
33

4-
/// Given integer-types M and register width N (e.g. M=u16 and N=32 bits), the
5-
/// `ParamExtension` policy specifies how a uM value should be treated when
6-
/// passed via register or stack-slot of width N. See also rust-lang/rust#97463.
4+
/// Indicates the variant of the AArch64 ABI we are compiling for.
5+
/// Used to accommodate Apple and Microsoft's deviations from the usual AAPCS ABI.
6+
///
7+
/// Corresponds to Clang's `AArch64ABIInfo::ABIKind`.
78
#[derive(Copy, Clone, PartialEq)]
8-
pub enum ParamExtension {
9-
/// Indicates that when passing an i8/i16, either as a function argument or
10-
/// as a return value, it must be sign-extended to 32 bits, and likewise a
11-
/// u8/u16 must be zero-extended to 32-bits. (This variant is here to
12-
/// accommodate Apple's deviation from the usual AArch64 ABI as defined by
13-
/// ARM.)
14-
///
15-
/// See also: <https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Pass-Arguments-to-Functions-Correctly>
16-
ExtendTo32Bits,
17-
18-
/// Indicates that no sign- nor zero-extension is performed: if a value of
19-
/// type with bitwidth M is passed as function argument or return value,
20-
/// then M bits are copied into the least significant M bits, and the
21-
/// remaining bits of the register (or word of memory) are untouched.
22-
NoExtension,
9+
pub enum AbiKind {
10+
AAPCS,
11+
DarwinPCS,
12+
Win64,
2313
}
2414

2515
fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option<Uniform>
@@ -45,15 +35,17 @@ where
4535
})
4636
}
4737

48-
fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, param_policy: ParamExtension)
38+
fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, kind: AbiKind)
4939
where
5040
Ty: TyAbiInterface<'a, C> + Copy,
5141
C: HasDataLayout,
5242
{
5343
if !ret.layout.is_aggregate() {
54-
match param_policy {
55-
ParamExtension::ExtendTo32Bits => ret.extend_integer_width_to(32),
56-
ParamExtension::NoExtension => {}
44+
if kind == AbiKind::DarwinPCS {
45+
// On Darwin, when returning an i8/i16, it must be sign-extended to 32 bits,
46+
// and likewise a u8/u16 must be zero-extended to 32-bits.
47+
// See also: <https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Pass-Arguments-to-Functions-Correctly>
48+
ret.extend_integer_width_to(32)
5749
}
5850
return;
5951
}
@@ -70,15 +62,17 @@ where
7062
ret.make_indirect();
7163
}
7264

73-
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, param_policy: ParamExtension)
65+
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, kind: AbiKind)
7466
where
7567
Ty: TyAbiInterface<'a, C> + Copy,
7668
C: HasDataLayout,
7769
{
7870
if !arg.layout.is_aggregate() {
79-
match param_policy {
80-
ParamExtension::ExtendTo32Bits => arg.extend_integer_width_to(32),
81-
ParamExtension::NoExtension => {}
71+
if kind == AbiKind::DarwinPCS {
72+
// On Darwin, when passing an i8/i16, it must be sign-extended to 32 bits,
73+
// and likewise a u8/u16 must be zero-extended to 32-bits.
74+
// See also: <https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Pass-Arguments-to-Functions-Correctly>
75+
arg.extend_integer_width_to(32);
8276
}
8377
return;
8478
}
@@ -87,27 +81,39 @@ where
8781
return;
8882
}
8983
let size = arg.layout.size;
90-
let bits = size.bits();
91-
if bits <= 128 {
92-
arg.cast_to(Uniform { unit: Reg::i64(), total: size });
84+
let align = if kind == AbiKind::AAPCS {
85+
// When passing small aggregates by value, the AAPCS ABI mandates using the unadjusted
86+
// alignment of the type (not including `repr(align)`).
87+
// This matches behavior of `AArch64ABIInfo::classifyArgumentType` in Clang.
88+
// See: <https://github.com/llvm/llvm-project/blob/5e691a1c9b0ad22689d4a434ddf4fed940e58dec/clang/lib/CodeGen/TargetInfo.cpp#L5816-L5823>
89+
arg.layout.unadjusted_abi_align
90+
} else {
91+
arg.layout.align.abi
92+
};
93+
if size.bits() <= 128 {
94+
if align.bits() == 128 {
95+
arg.cast_to(Uniform { unit: Reg::i128(), total: size });
96+
} else {
97+
arg.cast_to(Uniform { unit: Reg::i64(), total: size });
98+
}
9399
return;
94100
}
95101
arg.make_indirect();
96102
}
97103

98-
pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, param_policy: ParamExtension)
104+
pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, kind: AbiKind)
99105
where
100106
Ty: TyAbiInterface<'a, C> + Copy,
101107
C: HasDataLayout,
102108
{
103109
if !fn_abi.ret.is_ignore() {
104-
classify_ret(cx, &mut fn_abi.ret, param_policy);
110+
classify_ret(cx, &mut fn_abi.ret, kind);
105111
}
106112

107113
for arg in fn_abi.args.iter_mut() {
108114
if arg.is_ignore() {
109115
continue;
110116
}
111-
classify_arg(cx, arg, param_policy);
117+
classify_arg(cx, arg, kind);
112118
}
113119
}

compiler/rustc_target/src/abi/call/mod.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -679,12 +679,14 @@ impl<'a, Ty> FnAbi<'a, Ty> {
679679
}
680680
},
681681
"aarch64" => {
682-
let param_policy = if cx.target_spec().is_like_osx {
683-
aarch64::ParamExtension::ExtendTo32Bits
682+
let kind = if cx.target_spec().is_like_osx {
683+
aarch64::AbiKind::DarwinPCS
684+
} else if cx.target_spec().is_like_windows {
685+
aarch64::AbiKind::Win64
684686
} else {
685-
aarch64::ParamExtension::NoExtension
687+
aarch64::AbiKind::AAPCS
686688
};
687-
aarch64::compute_abi_info(cx, self, param_policy)
689+
aarch64::compute_abi_info(cx, self, kind)
688690
}
689691
"amdgpu" => amdgpu::compute_abi_info(cx, self),
690692
"arm" => arm::compute_abi_info(cx, self),

0 commit comments

Comments
 (0)