Skip to content

Commit 8d5fd94

Browse files
add tests for PassMode::Cast fixes
Tests added in cast-target-abi.rs, covering the single element, array, and prefix cases in `CastTarget::llvm_type`, and the Rust-is-larger/smaller cases in the Rust<->ABI copying code. ffi-out-of-bounds-loads.rs was overhauled to be runnable on any platform. Its alignment also increases due to the removal of a `min` in the previous commit; this was probably an insufficient workaround for this issue or similar. The higher alignment is fine, since the alloca is actually aligned to 8 bytes, as the test checks now confirm.
1 parent 8841315 commit 8d5fd94

File tree

2 files changed

+291
-6
lines changed

2 files changed

+291
-6
lines changed

tests/codegen/cast-target-abi.rs

+269
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
// ignore-tidy-linelength
2+
//@ revisions:aarch64 loongarch64 powerpc64 sparc64
3+
//@ compile-flags: -O -C no-prepopulate-passes
4+
5+
//@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu
6+
//@[aarch64] needs-llvm-components: arm
7+
//@[loongarch64] compile-flags: --target loongarch64-unknown-linux-gnu
8+
//@[loongarch64] needs-llvm-components: loongarch
9+
//@[powerpc64] compile-flags: --target powerpc64-unknown-linux-gnu
10+
//@[powerpc64] needs-llvm-components: powerpc
11+
//@[sparc64] compile-flags: --target sparc64-unknown-linux-gnu
12+
//@[sparc64] needs-llvm-components: sparc
13+
14+
// Tests that arguments with `PassMode::Cast` are handled correctly.
15+
16+
#![feature(no_core, lang_items)]
17+
#![crate_type = "lib"]
18+
#![no_std]
19+
#![no_core]
20+
21+
#[lang="sized"] trait Sized { }
22+
#[lang="freeze"] trait Freeze { }
23+
#[lang="copy"] trait Copy { }
24+
25+
// This struct will be passed as a single `i64` or `i32`.
26+
// This may be (if `i64)) larger than the Rust layout, which is just `{ i16, i16 }`.
27+
#[repr(C)]
28+
pub struct TwoU16s {
29+
a: u16,
30+
b: u16,
31+
}
32+
33+
// This struct will be passed as `[2 x i64]`.
34+
// This is larger than the Rust layout.
35+
#[repr(C)]
36+
pub struct FiveU16s {
37+
a: u16,
38+
b: u16,
39+
c: u16,
40+
d: u16,
41+
e: u16,
42+
}
43+
44+
// This struct will be passed as `[2 x double]`.
45+
// This is the same as the Rust layout.
46+
#[repr(C)]
47+
pub struct DoubleDouble {
48+
f: f64,
49+
g: f64,
50+
}
51+
52+
// On loongarch, this struct will be passed as `{ double, float }`.
53+
// This is smaller than the Rust layout, which has trailing padding (`{ f64, f32, <f32 padding> }`)
54+
#[repr(C)]
55+
pub struct DoubleFloat {
56+
f: f64,
57+
g: f32,
58+
}
59+
60+
extern "C" {
61+
fn receives_twou16s(x: TwoU16s);
62+
fn returns_twou16s() -> TwoU16s;
63+
64+
fn receives_fiveu16s(x: FiveU16s);
65+
fn returns_fiveu16s() -> FiveU16s;
66+
67+
fn receives_doubledouble(x: DoubleDouble);
68+
fn returns_doubledouble() -> DoubleDouble;
69+
70+
fn receives_doublefloat(x: DoubleFloat);
71+
fn returns_doublefloat() -> DoubleFloat;
72+
}
73+
74+
// CHECK-LABEL: @call_twou16s
75+
#[no_mangle]
76+
pub unsafe fn call_twou16s() {
77+
// aarch64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:i64]], align [[ABI_ALIGN:8]]
78+
// loongarch64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:i64]], align [[ABI_ALIGN:8]]
79+
// powerpc64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:i32]], align [[ABI_ALIGN:4]]
80+
// sparc64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:i64]], align [[ABI_ALIGN:8]]
81+
82+
// CHECK: [[RUST_ALLOCA:%.+]] = alloca %TwoU16s, align [[RUST_ALIGN:2]]
83+
84+
// CHECK: call void @llvm.memcpy.{{.+}}(ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], i64 4, i1 false)
85+
// CHECK: [[ABI_VALUE:%.+]] = load [[ABI_TYPE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
86+
// CHECK: call void @receives_twou16s([[ABI_TYPE]] [[ABI_VALUE]])
87+
let x = TwoU16s { a: 1, b: 2 };
88+
receives_twou16s(x);
89+
}
90+
91+
// CHECK-LABEL: @return_twou16s
92+
#[no_mangle]
93+
pub unsafe fn return_twou16s() -> TwoU16s {
94+
// powerpc returns this struct via sret pointer, it doesn't use the cast ABI.
95+
96+
// powerpc64: [[RETVAL:%.+]] = alloca %TwoU16s, align 2
97+
// powerpc64: call void @returns_twou16s(ptr {{.+}} [[RETVAL]])
98+
99+
100+
// The other targets copy the cast ABI type to an alloca.
101+
102+
// aarch64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:i64]], align [[ABI_ALIGN:8]]
103+
// loongarch64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:i64]], align [[ABI_ALIGN:8]]
104+
// sparc64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:i64]], align [[ABI_ALIGN:8]]
105+
106+
// aarch64: [[RUST_ALLOCA:%.+]] = alloca %TwoU16s, align [[RUST_ALIGN:2]]
107+
// loongarch64: [[RUST_ALLOCA:%.+]] = alloca %TwoU16s, align [[RUST_ALIGN:2]]
108+
// sparc64: [[RUST_ALLOCA:%.+]] = alloca %TwoU16s, align [[RUST_ALIGN:2]]
109+
110+
// aarch64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE]] @returns_twou16s()
111+
// loongarch64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE]] @returns_twou16s()
112+
// sparc64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE]] @returns_twou16s()
113+
114+
// aarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
115+
// loongarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
116+
// sparc64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
117+
118+
// aarch64: call void @llvm.memcpy.{{.+}}(ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], i64 4, i1 false)
119+
// loongarch64: call void @llvm.memcpy.{{.+}}(ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], i64 4, i1 false)
120+
// sparc64: call void @llvm.memcpy.{{.+}}(ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], i64 4, i1 false)
121+
returns_twou16s()
122+
}
123+
124+
// CHECK-LABEL: @call_fiveu16s
125+
#[no_mangle]
126+
pub unsafe fn call_fiveu16s() {
127+
// CHECK: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:\[2 x i64\]]], align [[ABI_ALIGN:8]]
128+
129+
// CHECK: [[RUST_ALLOCA:%.+]] = alloca %FiveU16s, align 2
130+
131+
// CHECK: call void @llvm.memcpy.{{.+}}(ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], i64 10, i1 false)
132+
// CHECK: [[ABI_VALUE:%.+]] = load [[ABI_TYPE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
133+
// CHECK: call void @receives_fiveu16s([[ABI_TYPE]] [[ABI_VALUE]])
134+
let x = FiveU16s { a: 1, b: 2, c: 3, d: 4, e: 5 };
135+
receives_fiveu16s(x);
136+
}
137+
138+
// CHECK-LABEL: @return_fiveu16s
139+
// CHECK-SAME: (ptr {{.+}} sret([10 x i8]) align [[RUST_ALIGN:2]] dereferenceable(10) [[RET_PTR:%.+]])
140+
#[no_mangle]
141+
pub unsafe fn return_fiveu16s() -> FiveU16s {
142+
// powerpc returns this struct via sret pointer, it doesn't use the cast ABI.
143+
144+
// powerpc64: call void @returns_fiveu16s(ptr {{.+}} [[RET_PTR]])
145+
146+
147+
// The other targets copy the cast ABI type to the sret pointer.
148+
149+
// aarch64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:\[2 x i64\]]], align [[ABI_ALIGN:8]]
150+
// loongarch64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:\[2 x i64\]]], align [[ABI_ALIGN:8]]
151+
// sparc64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:\[2 x i64\]]], align [[ABI_ALIGN:8]]
152+
153+
// aarch64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE]] @returns_fiveu16s()
154+
// loongarch64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE]] @returns_fiveu16s()
155+
// sparc64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE]] @returns_fiveu16s()
156+
157+
// aarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
158+
// loongarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
159+
// sparc64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
160+
161+
// aarch64: call void @llvm.memcpy.{{.+}}(ptr align [[RUST_ALIGN]] [[RET_PTR]], ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], i64 10, i1 false)
162+
// loongarch64: call void @llvm.memcpy.{{.+}}(ptr align [[RUST_ALIGN]] [[RET_PTR]], ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], i64 10, i1 false)
163+
// sparc64: call void @llvm.memcpy.{{.+}}(ptr align [[RUST_ALIGN]] [[RET_PTR]], ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], i64 10, i1 false)
164+
returns_fiveu16s()
165+
}
166+
167+
// CHECK-LABEL: @call_doubledouble
168+
#[no_mangle]
169+
pub unsafe fn call_doubledouble() {
170+
// aarch64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:\[2 x double\]]], align [[ABI_ALIGN:8]]
171+
// loongarch64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:{ double, double }]], align [[ABI_ALIGN:8]]
172+
// powerpc64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:\[2 x i64\]]], align [[ABI_ALIGN:8]]
173+
// sparc64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:{ double, double }]], align [[ABI_ALIGN:8]]
174+
175+
// CHECK: [[RUST_ALLOCA:%.+]] = alloca %DoubleDouble, align [[RUST_ALIGN:8]]
176+
177+
// CHECK: call void @llvm.memcpy.{{.+}}(ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], i64 16, i1 false)
178+
// CHECK: [[ABI_VALUE:%.+]] = load [[ABI_TYPE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
179+
// CHECK: call void @receives_doubledouble([[ABI_TYPE]] [[ABI_VALUE]])
180+
let x = DoubleDouble { f: 1., g: 2. };
181+
receives_doubledouble(x);
182+
}
183+
184+
// CHECK-LABEL: @return_doubledouble
185+
#[no_mangle]
186+
pub unsafe fn return_doubledouble() -> DoubleDouble {
187+
// powerpc returns this struct via sret pointer, it doesn't use the cast ABI.
188+
189+
// powerpc64: [[RETVAL:%.+]] = alloca %DoubleDouble, align 8
190+
// powerpc64: call void @returns_doubledouble(ptr {{.+}} [[RETVAL]])
191+
192+
193+
// The other targets copy the cast ABI type to an alloca.
194+
195+
// aarch64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:\[2 x double\]]], align [[ABI_ALIGN:8]]
196+
// loongarch64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:{ double, double }]], align [[ABI_ALIGN:8]]
197+
// sparc64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:{ double, double }]], align [[ABI_ALIGN:8]]
198+
199+
// aarch64: [[RUST_ALLOCA:%.+]] = alloca %DoubleDouble, align [[RUST_ALIGN:8]]
200+
// loongarch64: [[RUST_ALLOCA:%.+]] = alloca %DoubleDouble, align [[RUST_ALIGN:8]]
201+
// sparc64: [[RUST_ALLOCA:%.+]] = alloca %DoubleDouble, align [[RUST_ALIGN:8]]
202+
203+
// aarch64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE]] @returns_doubledouble()
204+
// loongarch64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE]] @returns_doubledouble()
205+
// sparc64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE]] @returns_doubledouble()
206+
207+
// aarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
208+
// loongarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
209+
// sparc64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
210+
211+
// aarch64: call void @llvm.memcpy.{{.+}}(ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], i64 16, i1 false)
212+
// loongarch64: call void @llvm.memcpy.{{.+}}(ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], i64 16, i1 false)
213+
// sparc64: call void @llvm.memcpy.{{.+}}(ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], i64 16, i1 false)
214+
returns_doubledouble()
215+
}
216+
217+
// CHECK-LABEL: @call_doublefloat
218+
#[no_mangle]
219+
pub unsafe fn call_doublefloat() {
220+
// aarch64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:\[2 x i64\]]], align [[ABI_ALIGN:8]]
221+
// loongarch64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:{ double, float }]], align [[ABI_ALIGN:8]]
222+
// powerpc64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:\[2 x i64\]]], align [[ABI_ALIGN:8]]
223+
// sparc64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:{ double, float, i32, i64 }]], align [[ABI_ALIGN:8]]
224+
225+
// CHECK: [[RUST_ALLOCA:%.+]] = alloca %DoubleFloat, align [[RUST_ALIGN:8]]
226+
227+
// aarch64: call void @llvm.memcpy.{{.+}}(ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], i64 16, i1 false)
228+
// loongarch64: call void @llvm.memcpy.{{.+}}(ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], i64 12, i1 false)
229+
// powerpc64: call void @llvm.memcpy.{{.+}}(ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], i64 16, i1 false)
230+
// sparc64: call void @llvm.memcpy.{{.+}}(ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], i64 16, i1 false)
231+
232+
// CHECK: [[ABI_VALUE:%.+]] = load [[ABI_TYPE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
233+
// CHECK: call void @receives_doublefloat([[ABI_TYPE]] {{(inreg )?}}[[ABI_VALUE]])
234+
let x = DoubleFloat { f: 1., g: 2. };
235+
receives_doublefloat(x);
236+
}
237+
238+
// CHECK-LABEL: @return_doublefloat
239+
#[no_mangle]
240+
pub unsafe fn return_doublefloat() -> DoubleFloat {
241+
// powerpc returns this struct via sret pointer, it doesn't use the cast ABI.
242+
243+
// powerpc64: [[RETVAL:%.+]] = alloca %DoubleFloat, align 8
244+
// powerpc64: call void @returns_doublefloat(ptr {{.+}} [[RETVAL]])
245+
246+
247+
// The other targets copy the cast ABI type to an alloca.
248+
249+
// aarch64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:\[2 x i64\]]], align [[ABI_ALIGN:8]]
250+
// loongarch64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:{ double, float }]], align [[ABI_ALIGN:8]]
251+
// sparc64: [[ABI_ALLOCA:%.+]] = alloca [[ABI_TYPE:{ double, float, i32, i64 }]], align [[ABI_ALIGN:8]]
252+
253+
// aarch64: [[RUST_ALLOCA:%.+]] = alloca %DoubleFloat, align [[RUST_ALIGN:8]]
254+
// loongarch64: [[RUST_ALLOCA:%.+]] = alloca %DoubleFloat, align [[RUST_ALIGN:8]]
255+
// sparc64: [[RUST_ALLOCA:%.+]] = alloca %DoubleFloat, align [[RUST_ALIGN:8]]
256+
257+
// aarch64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE]] @returns_doublefloat()
258+
// loongarch64: [[ABI_VALUE:%.+]] = call [[ABI_TYPE]] @returns_doublefloat()
259+
// sparc64: [[ABI_VALUE:%.+]] = call inreg [[ABI_TYPE]] @returns_doublefloat()
260+
261+
// aarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
262+
// loongarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
263+
// sparc64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
264+
265+
// aarch64: call void @llvm.memcpy.{{.+}}(ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], i64 16, i1 false)
266+
// loongarch64: call void @llvm.memcpy.{{.+}}(ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], i64 12, i1 false)
267+
// sparc64: call void @llvm.memcpy.{{.+}}(ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], i64 16, i1 false)
268+
returns_doublefloat()
269+
}
+22-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
1+
//@ revisions: linux apple
2+
//@ compile-flags: -C opt-level=0 -C no-prepopulate-passes
3+
4+
//@[linux] compile-flags: --target x86_64-unknown-linux-gnu
5+
//@[linux] needs-llvm-components: x86
6+
//@[apple] compile-flags: --target x86_64-apple-darwin
7+
//@[apple] needs-llvm-components: x86
8+
19
// Regression test for #29988
210

3-
//@ compile-flags: -C no-prepopulate-passes
4-
//@ only-x86_64
5-
//@ ignore-windows
11+
#![feature(no_core, lang_items)]
12+
#![crate_type = "lib"]
13+
#![no_std]
14+
#![no_core]
15+
16+
#[lang="sized"] trait Sized { }
17+
#[lang="freeze"] trait Freeze { }
18+
#[lang="copy"] trait Copy { }
619

720
#[repr(C)]
821
struct S {
@@ -15,11 +28,14 @@ extern "C" {
1528
fn foo(s: S);
1629
}
1730

18-
fn main() {
31+
// CHECK-LABEL: @test
32+
#[no_mangle]
33+
pub fn test() {
1934
let s = S { f1: 1, f2: 2, f3: 3 };
2035
unsafe {
21-
// CHECK: load { i64, i32 }, {{.*}}, align 4
22-
// CHECK: call void @foo({ i64, i32 } {{.*}})
36+
// CHECK: [[ALLOCA:%.+]] = alloca { i64, i32 }, align 8
37+
// CHECK: [[LOAD:%.+]] = load { i64, i32 }, ptr [[ALLOCA]], align 8
38+
// CHECK: call void @foo({ i64, i32 } [[LOAD]])
2339
foo(s);
2440
}
2541
}

0 commit comments

Comments
 (0)