Skip to content

Commit 5f39612

Browse files
committed
Add assembly tests verifying the functionality of -Zreg-struct-return for structs of different sizes.
This test covers: * The callee side, making sure that the structs are correctly loaded into registers when `-Zreg-struct-return` is enabled * The caller side, making sure that callers do receive returned structs in registers when `-Zreg-struct-return` is enabled Structs of the size of up to 2 registers (8 bytes) can be returned in registers in x86_32. Therefore, the tests are done with 3 different struct sizes: * 2 bytes (register returns should happen) * 8 bytes (last value where register returns should happen) * 12 bytes (register returns should not happen even when `-Zreg-struct-return` is enabled)
1 parent 350d0ef commit 5f39612

File tree

1 file changed

+143
-0
lines changed

1 file changed

+143
-0
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
//! Tests that -Zreg-struct-return changes ABI for small struct returns
2+
//! from hidden-pointer convention to register-return on x86_32.
3+
//! This test covers:
4+
//! * Callee side, verifying that the structs are correctly loaded into registers when
5+
//! `-Zreg-struct-return` is activated
6+
//! * Caller side, verifying callers do receive returned structs in registers when
7+
//! `-Zreg-struct-return` is activated
8+
//@ add-core-stubs
9+
//@ assembly-output: emit-asm
10+
//@ compile-flags: -O --target=i686-unknown-linux-gnu -Crelocation-model=static
11+
//@ revisions: WITH WITHOUT
12+
//@[WITH] compile-flags: -Zreg-struct-return
13+
//@ needs-llvm-components: x86
14+
15+
#![feature(no_core)]
16+
#![no_std]
17+
#![no_core]
18+
#![crate_type = "lib"]
19+
20+
extern crate minicore;
21+
use minicore::*;
22+
23+
// Verifies ABI changes for small structs, where both fields fit into one register.
24+
// WITH is expected to use register return, WITHOUT should use hidden pointer.
25+
mod Small {
26+
struct SmallStruct {
27+
a: i8,
28+
b: i8,
29+
}
30+
31+
unsafe extern "C" {
32+
fn small() -> SmallStruct;
33+
}
34+
35+
#[unsafe(no_mangle)]
36+
pub unsafe extern "C" fn small_callee() -> SmallStruct {
37+
// (42 << 8) | 42 = 10794
38+
39+
// WITH-LABEL: small_callee
40+
// WITH: movw $10794, %ax
41+
// WITH: retl
42+
43+
// WITHOUT-LABEL: small_callee
44+
// WITHOUT: movl 4(%esp), %e{{.*}}
45+
// WITHOUT: movw $10794, (%e{{.*}})
46+
// WITHOUT: retl $4
47+
SmallStruct { a: 42, b: 42 }
48+
}
49+
50+
#[unsafe(no_mangle)]
51+
pub unsafe extern "C" fn small_caller(dst: &mut SmallStruct) {
52+
// WITH-LABEL: small_caller
53+
// WITH: calll small
54+
// WITH: movw %ax, (%e{{.*}})
55+
56+
// WITHOUT-LABEL: small_caller
57+
// WITHOUT: calll small
58+
// WITHOUT: movzwl {{.*}}(%esp), %e[[TMP:..]]
59+
// WITHOUT: movw %[[TMP]], (%e{{..}})
60+
*dst = small();
61+
}
62+
}
63+
64+
// Verifies ABI changes for a struct of size 8, which is the maximum size
65+
// for reg-struct-return.
66+
// WITH is expected to still use register return, WITHOUT should use hidden
67+
// pointer.
68+
mod Pivot {
69+
struct PivotStruct {
70+
a: i32,
71+
b: i32,
72+
}
73+
74+
unsafe extern "C" {
75+
fn pivot() -> PivotStruct;
76+
}
77+
78+
#[unsafe(no_mangle)]
79+
pub unsafe extern "C" fn pivot_callee() -> PivotStruct {
80+
// WITH-LABEL: pivot_callee
81+
// WITH: movl $42, %e{{.*}}
82+
// WITH: movl $42, %e{{.*}}
83+
// WITH: retl
84+
85+
// WITHOUT-LABEL: pivot_callee
86+
// WITHOUT: movl 4(%esp), %e{{.*}}
87+
// WITHOUT-DAG: movl $42, (%e{{.*}})
88+
// WITHOUT-DAG: movl $42, 4(%e{{.*}})
89+
// WITHOUT: retl $4
90+
PivotStruct { a: 42, b: 42 }
91+
}
92+
93+
#[unsafe(no_mangle)]
94+
pub unsafe extern "C" fn pivot_caller(dst: &mut PivotStruct) {
95+
// WITH-LABEL: pivot_caller
96+
// WITH: calll pivot
97+
// WITH-DAG: movl %e{{.*}}, 4(%e{{.*}})
98+
// WITH-DAG: movl %e{{.*}}, (%e{{.*}})
99+
100+
// WITHOUT-LABEL: pivot_caller
101+
// WITHOUT: calll pivot
102+
// WITHOUT: movsd {{.*}}(%esp), %[[TMP:xmm.]]
103+
// WITHOUT: movsd %[[TMP]], (%e{{..}})
104+
*dst = pivot();
105+
}
106+
}
107+
108+
// Verifies ABI changes for a struct of size 12, which is larger than the
109+
// maximum size for reg-struct-return (8 bytes).
110+
// Here, the hidden pointer convention should be used even when `-Zreg-struct-return` is set.
111+
mod Large {
112+
struct LargeStruct {
113+
a: i32,
114+
b: i32,
115+
c: i32,
116+
}
117+
118+
unsafe extern "C" {
119+
fn large() -> LargeStruct;
120+
}
121+
122+
#[unsafe(no_mangle)]
123+
pub unsafe extern "C" fn large_callee() -> LargeStruct {
124+
// CHECK-LABEL: large_callee
125+
// CHECK: movl 4(%esp), %e{{.*}}
126+
// CHECK-DAG: movl $42, (%e{{.*}})
127+
// CHECK-DAG: movl $42, 4(%e{{.*}})
128+
// CHECK-DAG: movl $42, 8(%e{{.*}})
129+
// CHECK: retl $4
130+
LargeStruct { a: 42, b: 42, c: 42 }
131+
}
132+
133+
#[unsafe(no_mangle)]
134+
pub unsafe extern "C" fn large_caller(dst: &mut LargeStruct) {
135+
// CHECK-LABEL: large_caller
136+
// CHECK: calll large
137+
// CHECK-DAG: movl {{.*}}(%esp), %[[TMP1:e..]]
138+
// CHECK-DAG: movl %[[TMP1]], {{.*}}(%e{{..}})
139+
// CHECK-DAG: movsd {{.*}}(%esp), %[[TMP2:xmm.]]
140+
// CHECK-DAG: movsd %[[TMP2]], {{.*}}(%e{{..}})
141+
*dst = large();
142+
}
143+
}

0 commit comments

Comments
 (0)