1 |
328 |
jeremybenn |
/* This tests returning of structures. */
|
2 |
|
|
|
3 |
|
|
#include <stdio.h>
|
4 |
|
|
#include "defines.h"
|
5 |
|
|
#include "macros.h"
|
6 |
|
|
#include "args.h"
|
7 |
|
|
|
8 |
|
|
struct IntegerRegisters iregs;
|
9 |
|
|
struct FloatRegisters fregs;
|
10 |
|
|
unsigned int num_iregs, num_fregs;
|
11 |
|
|
|
12 |
|
|
int current_test;
|
13 |
|
|
int num_failed = 0;
|
14 |
|
|
|
15 |
|
|
#undef assert
|
16 |
|
|
#define assert(test) do { if (!(test)) {fprintf (stderr, "failed in test %d\n", current_test); num_failed++; } } while (0)
|
17 |
|
|
|
18 |
|
|
#define xmm0f xmm_regs[0]._float
|
19 |
|
|
#define xmm0d xmm_regs[0]._double
|
20 |
|
|
#define xmm1f xmm_regs[1]._float
|
21 |
|
|
#define xmm1d xmm_regs[1]._double
|
22 |
|
|
|
23 |
|
|
typedef enum {
|
24 |
|
|
INT = 0,
|
25 |
|
|
SSE_F,
|
26 |
|
|
SSE_D,
|
27 |
|
|
X87,
|
28 |
|
|
MEM,
|
29 |
|
|
INT_SSE,
|
30 |
|
|
SSE_INT,
|
31 |
|
|
SSE_F_V
|
32 |
|
|
} Type;
|
33 |
|
|
|
34 |
|
|
/* Structures which should be returned in INTEGER. */
|
35 |
|
|
#define D(I,MEMBERS,B) struct S_ ## I { MEMBERS ; }; Type class_ ## I = INT; \
|
36 |
|
|
struct S_ ## I f_ ## I (void) { struct S_ ## I s; memset (&s, 0, sizeof(s)); B; return s; }
|
37 |
|
|
|
38 |
|
|
D(1,char m1, s.m1=42)
|
39 |
|
|
D(2,short m1, s.m1=42)
|
40 |
|
|
D(3,int m1, s.m1=42)
|
41 |
|
|
D(4,long m1, s.m1=42)
|
42 |
|
|
D(5,long long m1, s.m1=42)
|
43 |
|
|
D(6,char m1;short s, s.m1=42)
|
44 |
|
|
D(7,char m1;int i, s.m1=42)
|
45 |
|
|
D(8,char m1; long l, s.m1=42)
|
46 |
|
|
D(9,char m1; long long l, s.m1=42)
|
47 |
|
|
D(10,char m1[16], s.m1[0]=42)
|
48 |
|
|
D(11,short m1[8], s.m1[0]=42)
|
49 |
|
|
D(12,int m1[4], s.m1[0]=42)
|
50 |
|
|
D(13,long m1[2], s.m1[0]=42)
|
51 |
|
|
D(14,long long m1[2], s.m1[0]=42)
|
52 |
|
|
|
53 |
|
|
#undef D
|
54 |
|
|
|
55 |
|
|
/* Structures which should be returned in SSE. */
|
56 |
|
|
#define D(I,MEMBERS,C,B) struct S_ ## I { MEMBERS ; }; Type class_ ## I = C; \
|
57 |
|
|
struct S_ ## I f_ ## I (void) { struct S_ ## I s; memset (&s, 0, sizeof(s)); B; return s; }
|
58 |
|
|
|
59 |
|
|
D(100,float f,SSE_F, s.f=42)
|
60 |
|
|
D(101,double d,SSE_D, s.d=42)
|
61 |
|
|
D(102,float f;float f2,SSE_F, s.f=42)
|
62 |
|
|
D(103,float f;double d,SSE_F, s.f=42)
|
63 |
|
|
D(104,double d; float f,SSE_D, s.d=42)
|
64 |
|
|
D(105,double d; double d2,SSE_D, s.d=42)
|
65 |
|
|
D(106,float f[2],SSE_F, s.f[0]=42)
|
66 |
|
|
D(107,float f[3],SSE_F, s.f[0]=42)
|
67 |
|
|
D(108,float f[4],SSE_F, s.f[0]=42)
|
68 |
|
|
D(109,double d[2],SSE_D, s.d[0]=42)
|
69 |
|
|
D(110,float f[2]; double d,SSE_F, s.f[0]=42)
|
70 |
|
|
D(111,double d;float f[2],SSE_D, s.d=42)
|
71 |
|
|
|
72 |
|
|
#undef D
|
73 |
|
|
|
74 |
|
|
/* Structures which should be returned on x87 stack. */
|
75 |
|
|
#define D(I,MEMBERS) struct S_ ## I { MEMBERS ; }; Type class_ ## I = X87; \
|
76 |
|
|
struct S_ ## I f_ ## I (void) { struct S_ ## I s = { 42 }; return s; }
|
77 |
|
|
|
78 |
|
|
/* The only struct containing a long double, which is returned in
|
79 |
|
|
registers at all, is the singleton struct. All others are too large.
|
80 |
|
|
This includes a struct containing complex long double, which is passed
|
81 |
|
|
in memory, although a complex long double type itself is returned in
|
82 |
|
|
two registers. */
|
83 |
|
|
D(200,long double ld)
|
84 |
|
|
|
85 |
|
|
#undef D
|
86 |
|
|
|
87 |
|
|
/* Structures which should be returned in INT (low) and SSE (high). */
|
88 |
|
|
#define D(I,MEMBERS) struct S_ ## I { MEMBERS ; }; Type class_ ## I = INT_SSE; \
|
89 |
|
|
struct S_ ## I f_ ## I (void) { struct S_ ## I s = { 42,43 }; return s; }
|
90 |
|
|
|
91 |
|
|
D(300,char m1; float m2)
|
92 |
|
|
D(301,char m1; double m2)
|
93 |
|
|
D(302,short m1; float m2)
|
94 |
|
|
D(303,short m1; double m2)
|
95 |
|
|
D(304,int m1; float m2)
|
96 |
|
|
D(305,int m1; double m2)
|
97 |
|
|
D(306,long m1; float m2)
|
98 |
|
|
D(307,long m1; double m2)
|
99 |
|
|
|
100 |
|
|
#undef D
|
101 |
|
|
|
102 |
|
|
void check_300 (void)
|
103 |
|
|
{
|
104 |
|
|
XMM_T x;
|
105 |
|
|
x._ulong[0] = rax;
|
106 |
|
|
switch (current_test) {
|
107 |
|
|
case 300: assert ((rax & 0xff) == 42 && x._float[1] == 43); break;
|
108 |
|
|
case 301: assert ((rax & 0xff) == 42 && xmm0d[0] == 43); break;
|
109 |
|
|
case 302: assert ((rax & 0xffff) == 42 && x._float[1] == 43); break;
|
110 |
|
|
case 303: assert ((rax & 0xffff) == 42 && xmm0d[0] == 43); break;
|
111 |
|
|
case 304: assert ((rax & 0xffffffff) == 42 && x._float[1] == 43); break;
|
112 |
|
|
case 305: assert ((rax & 0xffffffff) == 42 && xmm0d[0] == 43); break;
|
113 |
|
|
case 306: assert (rax == 42 && xmm0f[0] == 43); break;
|
114 |
|
|
case 307: assert (rax == 42 && xmm0d[0] == 43); break;
|
115 |
|
|
default: assert (0); break;
|
116 |
|
|
}
|
117 |
|
|
}
|
118 |
|
|
|
119 |
|
|
/* Structures which should be returned in SSE (low) and INT (high). */
|
120 |
|
|
#define D(I,MEMBERS,B) struct S_ ## I { MEMBERS ; }; Type class_ ## I = SSE_INT; \
|
121 |
|
|
struct S_ ## I f_ ## I (void) { struct S_ ## I s; memset (&s, 0, sizeof(s)); B; return s; }
|
122 |
|
|
|
123 |
|
|
D(400,float f[2];char c, s.f[0]=42; s.c=43)
|
124 |
|
|
D(401,double d;char c, s.d=42; s.c=43)
|
125 |
|
|
|
126 |
|
|
#undef D
|
127 |
|
|
|
128 |
|
|
void check_400 (void)
|
129 |
|
|
{
|
130 |
|
|
switch (current_test) {
|
131 |
|
|
case 400: assert (xmm0f[0] == 42 && (rax & 0xff) == 43); break;
|
132 |
|
|
case 401: assert (xmm0d[0] == 42 && (rax & 0xff) == 43); break;
|
133 |
|
|
default: assert (0); break;
|
134 |
|
|
}
|
135 |
|
|
}
|
136 |
|
|
|
137 |
|
|
/* Structures which should be returned in MEM. */
|
138 |
|
|
void *struct_addr;
|
139 |
|
|
#define D(I,MEMBERS) struct S_ ## I { MEMBERS ; }; Type class_ ## I = MEM; \
|
140 |
|
|
struct S_ ## I f_ ## I (void) { union {unsigned char c; struct S_ ## I s;} u; memset (&u.s, 0, sizeof(u.s)); u.c = 42; return u.s; }
|
141 |
|
|
|
142 |
|
|
/* Too large. */
|
143 |
|
|
D(500,char m1[17])
|
144 |
|
|
D(501,short m1[9])
|
145 |
|
|
D(502,int m1[5])
|
146 |
|
|
D(503,long m1[3])
|
147 |
|
|
D(504,short m1[8];char c)
|
148 |
|
|
D(505,char m1[1];int i[4])
|
149 |
|
|
D(506,float m1[5])
|
150 |
|
|
D(507,double m1[3])
|
151 |
|
|
D(508,char m1[1];float f[4])
|
152 |
|
|
D(509,char m1[1];double d[2])
|
153 |
|
|
D(510,__complex long double m1[1])
|
154 |
|
|
|
155 |
|
|
/* Too large due to padding. */
|
156 |
|
|
D(520,char m1[1];int i;char c2; int i2; char c3)
|
157 |
|
|
|
158 |
|
|
/* Unnaturally aligned members. */
|
159 |
|
|
D(530,short m1[1];int i PACKED)
|
160 |
|
|
|
161 |
|
|
#undef D
|
162 |
|
|
|
163 |
|
|
|
164 |
|
|
/* Special tests. */
|
165 |
|
|
#define D(I,MEMBERS,C,B) struct S_ ## I { MEMBERS ; }; Type class_ ## I = C; \
|
166 |
|
|
struct S_ ## I f_ ## I (void) { struct S_ ## I s; B; return s; }
|
167 |
|
|
D(600,float f[4], SSE_F_V, s.f[0] = s.f[1] = s.f[2] = s.f[3] = 42)
|
168 |
|
|
#undef D
|
169 |
|
|
|
170 |
|
|
void clear_all (void)
|
171 |
|
|
{
|
172 |
|
|
clear_int_registers;
|
173 |
|
|
clear_float_registers;
|
174 |
|
|
clear_x87_registers;
|
175 |
|
|
}
|
176 |
|
|
|
177 |
|
|
void check_all (Type class, unsigned long size)
|
178 |
|
|
{
|
179 |
|
|
switch (class) {
|
180 |
|
|
case INT: if (size < 8) rax &= ~0UL >> (64-8*size); assert (rax == 42); break;
|
181 |
|
|
case SSE_F: assert (xmm0f[0] == 42); break;
|
182 |
|
|
case SSE_D: assert (xmm0d[0] == 42); break;
|
183 |
|
|
case SSE_F_V: assert (xmm0f[0] == 42 && xmm0f[1]==42 && xmm1f[0] == 42 && xmm1f[1] == 42); break;
|
184 |
|
|
case X87: assert (x87_regs[0]._ldouble == 42); break;
|
185 |
|
|
case INT_SSE: check_300(); break;
|
186 |
|
|
case SSE_INT: check_400(); break;
|
187 |
|
|
/* Ideally we would like to check that rax == struct_addr.
|
188 |
|
|
Unfortunately the address of the target struct escapes (for setting
|
189 |
|
|
struct_addr), so the return struct is a temporary one whose address
|
190 |
|
|
is given to the f_* functions, otherwise a conforming program
|
191 |
|
|
could notice the struct changing already before the function returns.
|
192 |
|
|
This temporary struct could be anywhere. For GCC it will be on
|
193 |
|
|
stack, but noone is forbidding that it could be a static variable
|
194 |
|
|
if there's no threading or proper locking. Nobody in his right mind
|
195 |
|
|
will not use the stack for that. */
|
196 |
|
|
case MEM: assert (*(unsigned char*)struct_addr == 42 && rdi == rax); break;
|
197 |
|
|
}
|
198 |
|
|
}
|
199 |
|
|
|
200 |
|
|
#define D(I) { struct S_ ## I s; current_test = I; struct_addr = (void*)&s; \
|
201 |
|
|
clear_all(); \
|
202 |
|
|
s = WRAP_RET(f_ ## I) (); \
|
203 |
|
|
check_all(class_ ## I, sizeof(s)); \
|
204 |
|
|
}
|
205 |
|
|
|
206 |
|
|
int
|
207 |
|
|
main (void)
|
208 |
|
|
{
|
209 |
|
|
D(1) D(2) D(3) D(4) D(5) D(6) D(7) D(8) D(9) D(10) D(11) D(12) D(13) D(14)
|
210 |
|
|
|
211 |
|
|
D(100) D(101) D(102) D(103) D(104) D(105) D(106) D(107) D(108) D(109) D(110)
|
212 |
|
|
D(111)
|
213 |
|
|
|
214 |
|
|
D(200)
|
215 |
|
|
|
216 |
|
|
D(300) D(301) D(302) D(303) D(304) D(305) D(306) D(307)
|
217 |
|
|
|
218 |
|
|
D(400) D(401)
|
219 |
|
|
|
220 |
|
|
D(500) D(501) D(502) D(503) D(504) D(505) D(506) D(507) D(508) D(509)
|
221 |
|
|
D(520)
|
222 |
|
|
D(530)
|
223 |
|
|
|
224 |
|
|
D(600)
|
225 |
|
|
if (num_failed)
|
226 |
|
|
abort ();
|
227 |
|
|
|
228 |
|
|
return 0;
|
229 |
|
|
}
|
230 |
|
|
#undef D
|