Skip to content

Commit 4bb89c4

Browse files
authored
Unrolled build for #145382
Rollup merge of #145382 - winstonallo:reg-struct-return-asm-test, r=tgross35 Add assembly test for `-Zreg-struct-return` option r? `@tgross35` As discussed in #145309 with `@tgross35` and `@ojeda,` I added assembly tests for the `-Zreg-struct-return` option verifying that it changes the ABI from hidden pointer to register-return on x86_32. The test covers: - Direct struct construction, showing register return vs hidden pointer - External function calls returning structs, showing ABI mismatch handling Different memory layouts affect ABI mismatch handling, but register returns use the same register allocation regardless of struct field layout (apart from the fact that they use smaller registers for smaller structs, of course). [Here](https://godbolt.org/z/dcW6rnMG3) is a compiler explorer with 2 examples. Let me know if there is anything more I could add. Since register returns only happen for structs up to the size of 2 registers, I figured testing the pivot value (8 bytes) would be most critical.
2 parents d36f964 + 5f39612 commit 4bb89c4

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)