Skip to content

Commit ecfc338

Browse files
authored
Rollup merge of #122781 - nikic:ppc-abi-fix, r=cuviper
Fix argument ABI for overaligned structs on ppc64le When passing a 16 (or higher) aligned struct by value on ppc64le, it needs to be passed as an array of `i128` rather than an array of `i64`. This will force the use of an even starting doubleword. For the case of a 16 byte struct with alignment 16 it is important that `[1 x i128]` is used instead of `i128` -- apparently, the latter will get treated similarly to `[2 x i64]`, not exhibiting the correct ABI. Add a `force_array` flag to `Uniform` to support this. The relevant clang code can be found here: https://github.com/llvm/llvm-project/blob/fe2119a7b08b6e468b2a67768904ea85b1bf0a45/clang/lib/CodeGen/Targets/PPC.cpp#L878-L884 https://github.com/llvm/llvm-project/blob/fe2119a7b08b6e468b2a67768904ea85b1bf0a45/clang/lib/CodeGen/Targets/PPC.cpp#L780-L784 I think the corresponding psABI wording is this: > Fixed size aggregates and unions passed by value are mapped to as > many doublewords of the parameter save area as the value uses in > memory. Aggregrates and unions are aligned according to their > alignment requirements. This may result in doublewords being > skipped for alignment. In particular the last sentence. Though I didn't find any wording for Clang's behavior of clamping the alignment to 16. Fixes #122767. r? `@cuviper`
2 parents 537aab7 + 1b7342b commit ecfc338

File tree

15 files changed

+156
-41
lines changed

15 files changed

+156
-41
lines changed

compiler/rustc_codegen_llvm/src/abi.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,10 @@ impl LlvmType for CastTarget {
150150
// Simplify to a single unit or an array if there's no prefix.
151151
// This produces the same layout, but using a simpler type.
152152
if self.prefix.iter().all(|x| x.is_none()) {
153-
if rest_count == 1 {
153+
// We can't do this if is_consecutive is set and the unit would get
154+
// split on the target. Currently, this is only relevant for i128
155+
// registers.
156+
if rest_count == 1 && (!self.rest.is_consecutive || self.rest.unit != Reg::i128()) {
154157
return rest_ll_unit;
155158
}
156159

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ where
3131
RegKind::Vector => size.bits() == 64 || size.bits() == 128,
3232
};
3333

34-
valid_unit.then_some(Uniform { unit, total: size })
34+
valid_unit.then_some(Uniform::consecutive(unit, size))
3535
})
3636
}
3737

@@ -60,7 +60,7 @@ where
6060
let size = ret.layout.size;
6161
let bits = size.bits();
6262
if bits <= 128 {
63-
ret.cast_to(Uniform { unit: Reg::i64(), total: size });
63+
ret.cast_to(Uniform::new(Reg::i64(), size));
6464
return;
6565
}
6666
ret.make_indirect();
@@ -100,9 +100,9 @@ where
100100
};
101101
if size.bits() <= 128 {
102102
if align.bits() == 128 {
103-
arg.cast_to(Uniform { unit: Reg::i128(), total: size });
103+
arg.cast_to(Uniform::new(Reg::i128(), size));
104104
} else {
105-
arg.cast_to(Uniform { unit: Reg::i64(), total: size });
105+
arg.cast_to(Uniform::new(Reg::i64(), size));
106106
}
107107
return;
108108
}

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ where
2121
RegKind::Vector => size.bits() == 64 || size.bits() == 128,
2222
};
2323

24-
valid_unit.then_some(Uniform { unit, total: size })
24+
valid_unit.then_some(Uniform::consecutive(unit, size))
2525
})
2626
}
2727

@@ -49,7 +49,7 @@ where
4949
let size = ret.layout.size;
5050
let bits = size.bits();
5151
if bits <= 32 {
52-
ret.cast_to(Uniform { unit: Reg::i32(), total: size });
52+
ret.cast_to(Uniform::new(Reg::i32(), size));
5353
return;
5454
}
5555
ret.make_indirect();
@@ -78,7 +78,7 @@ where
7878

7979
let align = arg.layout.align.abi.bytes();
8080
let total = arg.layout.size;
81-
arg.cast_to(Uniform { unit: if align <= 4 { Reg::i32() } else { Reg::i64() }, total });
81+
arg.cast_to(Uniform::consecutive(if align <= 4 { Reg::i32() } else { Reg::i64() }, total));
8282
}
8383

8484
pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ fn classify_ret<Ty>(arg: &mut ArgAbi<'_, Ty>) {
1818
if total.bits() > 64 {
1919
arg.make_indirect();
2020
} else if total.bits() > 32 {
21-
arg.cast_to(Uniform { unit: Reg::i32(), total });
21+
arg.cast_to(Uniform::new(Reg::i32(), total));
2222
} else {
2323
arg.cast_to(Reg::i32());
2424
}
@@ -38,7 +38,7 @@ fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
3838
if arg.layout.is_aggregate() {
3939
let total = arg.layout.size;
4040
if total.bits() > 32 {
41-
arg.cast_to(Uniform { unit: Reg::i32(), total });
41+
arg.cast_to(Uniform::new(Reg::i32(), total));
4242
} else {
4343
arg.cast_to(Reg::i32());
4444
}

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

+5-5
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ where
195195
if total.bits() <= xlen {
196196
arg.cast_to(xlen_reg);
197197
} else {
198-
arg.cast_to(Uniform { unit: xlen_reg, total: Size::from_bits(xlen * 2) });
198+
arg.cast_to(Uniform::new(xlen_reg, Size::from_bits(xlen * 2)));
199199
}
200200
return false;
201201
}
@@ -278,10 +278,10 @@ fn classify_arg<'a, Ty, C>(
278278
if total.bits() > xlen {
279279
let align_regs = align > xlen;
280280
if is_loongarch_aggregate(arg) {
281-
arg.cast_to(Uniform {
282-
unit: if align_regs { double_xlen_reg } else { xlen_reg },
283-
total: Size::from_bits(xlen * 2),
284-
});
281+
arg.cast_to(Uniform::new(
282+
if align_regs { double_xlen_reg } else { xlen_reg },
283+
Size::from_bits(xlen * 2),
284+
));
285285
}
286286
if align_regs && is_vararg {
287287
*avail_gprs -= *avail_gprs % 2;

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ where
2727

2828
if arg.layout.is_aggregate() {
2929
let pad_i32 = !offset.is_aligned(align);
30-
arg.cast_to_and_pad_i32(Uniform { unit: Reg::i32(), total: size }, pad_i32);
30+
arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32);
3131
} else {
3232
arg.extend_integer_width_to(32);
3333
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ where
6868
}
6969

7070
// Cast to a uniform int structure
71-
ret.cast_to(Uniform { unit: Reg::i64(), total: size });
71+
ret.cast_to(Uniform::new(Reg::i64(), size));
7272
} else {
7373
ret.make_indirect();
7474
}
@@ -139,7 +139,7 @@ where
139139
let rest_size = size - Size::from_bytes(8) * prefix_index as u64;
140140
arg.cast_to(CastTarget {
141141
prefix,
142-
rest: Uniform { unit: Reg::i64(), total: rest_size },
142+
rest: Uniform::new(Reg::i64(), rest_size),
143143
attrs: ArgAttributes {
144144
regular: ArgAttribute::default(),
145145
arg_ext: ArgExtension::None,

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

+18-1
Original file line numberDiff line numberDiff line change
@@ -255,18 +255,35 @@ pub struct Uniform {
255255
/// for 64-bit integers with a total size of 20 bytes. When the argument is actually passed,
256256
/// this size will be rounded up to the nearest multiple of `unit.size`.
257257
pub total: Size,
258+
259+
/// Indicate that the argument is consecutive, in the sense that either all values need to be
260+
/// passed in register, or all on the stack. If they are passed on the stack, there should be
261+
/// no additional padding between elements.
262+
pub is_consecutive: bool,
258263
}
259264

260265
impl From<Reg> for Uniform {
261266
fn from(unit: Reg) -> Uniform {
262-
Uniform { unit, total: unit.size }
267+
Uniform { unit, total: unit.size, is_consecutive: false }
263268
}
264269
}
265270

266271
impl Uniform {
267272
pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align {
268273
self.unit.align(cx)
269274
}
275+
276+
/// Pass using one or more values of the given type, without requiring them to be consecutive.
277+
/// That is, some values may be passed in register and some on the stack.
278+
pub fn new(unit: Reg, total: Size) -> Self {
279+
Uniform { unit, total, is_consecutive: false }
280+
}
281+
282+
/// Pass using one or more consecutive values of the given type. Either all values will be
283+
/// passed in registers, or all on the stack.
284+
pub fn consecutive(unit: Reg, total: Size) -> Self {
285+
Uniform { unit, total, is_consecutive: true }
286+
}
270287
}
271288

272289
/// Describes the type used for `PassMode::Cast`.

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ where
3535
16 => Reg::i128(),
3636
_ => unreachable!("Align is given as power of 2 no larger than 16 bytes"),
3737
};
38-
arg.cast_to(Uniform { unit, total: Size::from_bytes(2 * align_bytes) });
38+
arg.cast_to(Uniform::new(unit, Size::from_bytes(2 * align_bytes)));
3939
} else {
4040
// FIXME: find a better way to do this. See https://github.com/rust-lang/rust/issues/117271.
4141
arg.make_direct_deprecated();

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

+13-11
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Alignment of 128 bit types is not currently handled, this will
33
// need to be fixed when PowerPC vector support is added.
44

5-
use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform};
5+
use crate::abi::call::{Align, ArgAbi, FnAbi, Reg, RegKind, Uniform};
66
use crate::abi::{Endian, HasDataLayout, TyAbiInterface};
77
use crate::spec::HasTargetSpec;
88

@@ -37,7 +37,7 @@ where
3737
RegKind::Vector => arg.layout.size.bits() == 128,
3838
};
3939

40-
valid_unit.then_some(Uniform { unit, total: arg.layout.size })
40+
valid_unit.then_some(Uniform::consecutive(unit, arg.layout.size))
4141
})
4242
}
4343

@@ -81,7 +81,7 @@ where
8181
Reg::i64()
8282
};
8383

84-
ret.cast_to(Uniform { unit, total: size });
84+
ret.cast_to(Uniform::new(unit, size));
8585
return;
8686
}
8787

@@ -108,18 +108,20 @@ where
108108
}
109109

110110
let size = arg.layout.size;
111-
let (unit, total) = if size.bits() <= 64 {
111+
if size.bits() <= 64 {
112112
// Aggregates smaller than a doubleword should appear in
113113
// the least-significant bits of the parameter doubleword.
114-
(Reg { kind: RegKind::Integer, size }, size)
114+
arg.cast_to(Reg { kind: RegKind::Integer, size })
115115
} else {
116-
// Aggregates larger than a doubleword should be padded
117-
// at the tail to fill out a whole number of doublewords.
118-
let reg_i64 = Reg::i64();
119-
(reg_i64, size.align_to(reg_i64.align(cx)))
116+
// Aggregates larger than i64 should be padded at the tail to fill out a whole number
117+
// of i64s or i128s, depending on the aggregate alignment. Always use an array for
118+
// this, even if there is only a single element.
119+
let reg = if arg.layout.align.abi.bytes() > 8 { Reg::i128() } else { Reg::i64() };
120+
arg.cast_to(Uniform::consecutive(
121+
reg,
122+
size.align_to(Align::from_bytes(reg.size.bytes()).unwrap()),
123+
))
120124
};
121-
122-
arg.cast_to(Uniform { unit, total });
123125
}
124126

125127
pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)

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

+5-5
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ where
201201
if total.bits() <= xlen {
202202
arg.cast_to(xlen_reg);
203203
} else {
204-
arg.cast_to(Uniform { unit: xlen_reg, total: Size::from_bits(xlen * 2) });
204+
arg.cast_to(Uniform::new(xlen_reg, Size::from_bits(xlen * 2)));
205205
}
206206
return false;
207207
}
@@ -284,10 +284,10 @@ fn classify_arg<'a, Ty, C>(
284284
if total.bits() > xlen {
285285
let align_regs = align > xlen;
286286
if is_riscv_aggregate(arg) {
287-
arg.cast_to(Uniform {
288-
unit: if align_regs { double_xlen_reg } else { xlen_reg },
289-
total: Size::from_bits(xlen * 2),
290-
});
287+
arg.cast_to(Uniform::new(
288+
if align_regs { double_xlen_reg } else { xlen_reg },
289+
Size::from_bits(xlen * 2),
290+
));
291291
}
292292
if align_regs && is_vararg {
293293
*avail_gprs -= *avail_gprs % 2;

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ where
2727

2828
if arg.layout.is_aggregate() {
2929
let pad_i32 = !offset.is_aligned(align);
30-
arg.cast_to_and_pad_i32(Uniform { unit: Reg::i32(), total: size }, pad_i32);
30+
arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32);
3131
} else {
3232
arg.extend_integer_width_to(32);
3333
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ where
192192

193193
arg.cast_to(CastTarget {
194194
prefix: data.prefix,
195-
rest: Uniform { unit: Reg::i64(), total: rest_size },
195+
rest: Uniform::new(Reg::i64(), rest_size),
196196
attrs: ArgAttributes {
197197
regular: data.arg_attribute,
198198
arg_ext: ArgExtension::None,
@@ -205,7 +205,7 @@ where
205205
}
206206
}
207207

208-
arg.cast_to(Uniform { unit: Reg::i64(), total });
208+
arg.cast_to(Uniform::new(Reg::i64(), total));
209209
}
210210

211211
pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::abi::call::{ArgAbi, FnAbi, Uniform};
1+
use crate::abi::call::{ArgAbi, FnAbi};
22
use crate::abi::{HasDataLayout, TyAbiInterface};
33

44
fn unwrap_trivial_aggregate<'a, Ty, C>(cx: &C, val: &mut ArgAbi<'a, Ty>) -> bool
@@ -10,7 +10,7 @@ where
1010
if let Some(unit) = val.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()) {
1111
let size = val.layout.size;
1212
if unit.size == size {
13-
val.cast_to(Uniform { unit, total: size });
13+
val.cast_to(unit);
1414
return true;
1515
}
1616
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Test that structs aligned to 128 bits are passed with the correct ABI on powerpc64le.
2+
// This is similar to aarch64-struct-align-128.rs, but for ppc.
3+
4+
//@ compile-flags: --target powerpc64le-unknown-linux-gnu
5+
//@ needs-llvm-components: powerpc
6+
7+
#![feature(no_core, lang_items)]
8+
#![crate_type = "lib"]
9+
#![no_core]
10+
11+
#[lang="sized"]
12+
trait Sized { }
13+
#[lang="freeze"]
14+
trait Freeze { }
15+
#[lang="copy"]
16+
trait Copy { }
17+
18+
#[repr(C)]
19+
pub struct Align8 {
20+
pub a: u64,
21+
pub b: u64,
22+
}
23+
24+
#[repr(transparent)]
25+
pub struct Transparent8 {
26+
a: Align8
27+
}
28+
29+
#[repr(C)]
30+
pub struct Wrapped8 {
31+
a: Align8,
32+
}
33+
34+
extern "C" {
35+
// CHECK: declare void @test_8([2 x i64], [2 x i64], [2 x i64])
36+
fn test_8(a: Align8, b: Transparent8, c: Wrapped8);
37+
}
38+
39+
#[repr(C)]
40+
#[repr(align(16))]
41+
pub struct Align16 {
42+
pub a: u64,
43+
pub b: u64,
44+
}
45+
46+
#[repr(transparent)]
47+
pub struct Transparent16 {
48+
a: Align16
49+
}
50+
51+
#[repr(C)]
52+
pub struct Wrapped16 {
53+
pub a: Align16,
54+
}
55+
56+
extern "C" {
57+
// It's important that this produces [1 x i128] rather than just i128!
58+
// CHECK: declare void @test_16([1 x i128], [1 x i128], [1 x i128])
59+
fn test_16(a: Align16, b: Transparent16, c: Wrapped16);
60+
}
61+
62+
#[repr(C)]
63+
#[repr(align(32))]
64+
pub struct Align32 {
65+
pub a: u64,
66+
pub b: u64,
67+
pub c: u64,
68+
}
69+
70+
#[repr(transparent)]
71+
pub struct Transparent32 {
72+
a: Align32
73+
}
74+
75+
#[repr(C)]
76+
pub struct Wrapped32 {
77+
pub a: Align32,
78+
}
79+
80+
extern "C" {
81+
// CHECK: declare void @test_32([2 x i128], [2 x i128], [2 x i128])
82+
fn test_32(a: Align32, b: Transparent32, c: Wrapped32);
83+
}
84+
85+
pub unsafe fn main(
86+
a1: Align8, a2: Transparent8, a3: Wrapped8,
87+
b1: Align16, b2: Transparent16, b3: Wrapped16,
88+
c1: Align32, c2: Transparent32, c3: Wrapped32,
89+
) {
90+
test_8(a1, a2, a3);
91+
test_16(b1, b2, b3);
92+
test_32(c1, c2, c3);
93+
}

0 commit comments

Comments
 (0)