1 #undef _GNU_SOURCE
2 #define _GNU_SOURCE 1
3 #undef __USE_GNU
4 #define __USE_GNU 1
5 #include <unistd.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <signal.h>
10 #include <sys/types.h>
11 #include <sys/select.h>
12 #include <sys/time.h>
13 #include <sys/wait.h>
14 #include <fenv.h>
15 
16 enum {
17 	CF = 1 << 0,
18 	PF = 1 << 2,
19 	ZF = 1 << 6,
20 	ARITH = CF | PF | ZF,
21 };
22 
23 long res_fcomi_pi_1;
24 long res_fcomi_1_pi;
25 long res_fcomi_1_1;
26 long res_fcomi_nan_1;
27 /* sNaN is s|111 1111 1|1xx xxxx xxxx xxxx xxxx xxxx */
28 /* qNaN is s|111 1111 1|0xx xxxx xxxx xxxx xxxx xxxx (some x must be nonzero) */
29 int snan = 0x7fc11111;
30 int qnan = 0x7f811111;
31 unsigned short snan1[5];
32 /* sNaN80 is s|111 1111 1111 1111 |10xx xx...xx (some x must be nonzero) */
33 unsigned short snan80[5] = { 0x1111, 0x1111, 0x1111, 0x8111, 0x7fff };
34 
35 int test(long flags)
36 {
37 	feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
38 
39 	asm ("\n"
40 
41 	"	push	%0""\n"
42 	"	popf""\n"
43 	"	fld1""\n"
44 	"	fldpi""\n"
45 	"	fcomi	%%st(1), %%st" "\n"
46 	"	ffree	%%st(0)" "\n"
47 	"	ffree	%%st(1)" "\n"
48 	"	pushf""\n"
49 	"	pop	res_fcomi_1_pi""\n"
50 
51 	"	push	%0""\n"
52 	"	popf""\n"
53 	"	fldpi""\n"
54 	"	fld1""\n"
55 	"	fcomi	%%st(1), %%st" "\n"
56 	"	ffree	%%st(0)" "\n"
57 	"	ffree	%%st(1)" "\n"
58 	"	pushf""\n"
59 	"	pop	res_fcomi_pi_1""\n"
60 
61 	"	push	%0""\n"
62 	"	popf""\n"
63 	"	fld1""\n"
64 	"	fld1""\n"
65 	"	fcomi	%%st(1), %%st" "\n"
66 	"	ffree	%%st(0)" "\n"
67 	"	ffree	%%st(1)" "\n"
68 	"	pushf""\n"
69 	"	pop	res_fcomi_1_1""\n"
70 	:
71 	: "r" (flags)
72 	);
73 	if ((res_fcomi_1_pi & ARITH) != (0)) {
74 		printf("[BAD]\tfcomi_1_pi with flags:%lx\n", flags);
75 		return 1;
76 	}
77 	if ((res_fcomi_pi_1 & ARITH) != (CF)) {
78 		printf("[BAD]\tfcomi_pi_1 with flags:%lx->%lx\n", flags, res_fcomi_pi_1 & ARITH);
79 		return 1;
80 	}
81 	if ((res_fcomi_1_1 & ARITH) != (ZF)) {
82 		printf("[BAD]\tfcomi_1_1 with flags:%lx\n", flags);
83 		return 1;
84 	}
85 	if (fetestexcept(FE_INVALID) != 0) {
86 		printf("[BAD]\tFE_INVALID is set in %s\n", __func__);
87 		return 1;
88 	}
89 	return 0;
90 }
91 
92 int test_qnan(long flags)
93 {
94 	feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
95 
96 	asm ("\n"
97 	"	push	%0""\n"
98 	"	popf""\n"
99 	"	flds	qnan""\n"
100 	"	fld1""\n"
101 	"	fnclex""\n"		// fld of a qnan raised FE_INVALID, clear it
102 	"	fcomi	%%st(1), %%st" "\n"
103 	"	ffree	%%st(0)" "\n"
104 	"	ffree	%%st(1)" "\n"
105 	"	pushf""\n"
106 	"	pop	res_fcomi_nan_1""\n"
107 	:
108 	: "r" (flags)
109 	);
110 	if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) {
111 		printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags);
112 		return 1;
113 	}
114 	if (fetestexcept(FE_INVALID) != FE_INVALID) {
115 		printf("[BAD]\tFE_INVALID is not set in %s\n", __func__);
116 		return 1;
117 	}
118 	return 0;
119 }
120 
121 int testu_qnan(long flags)
122 {
123 	feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
124 
125 	asm ("\n"
126 	"	push	%0""\n"
127 	"	popf""\n"
128 	"	flds	qnan""\n"
129 	"	fld1""\n"
130 	"	fnclex""\n"		// fld of a qnan raised FE_INVALID, clear it
131 	"	fucomi	%%st(1), %%st" "\n"
132 	"	ffree	%%st(0)" "\n"
133 	"	ffree	%%st(1)" "\n"
134 	"	pushf""\n"
135 	"	pop	res_fcomi_nan_1""\n"
136 	:
137 	: "r" (flags)
138 	);
139 	if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) {
140 		printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags);
141 		return 1;
142 	}
143 	if (fetestexcept(FE_INVALID) != 0) {
144 		printf("[BAD]\tFE_INVALID is set in %s\n", __func__);
145 		return 1;
146 	}
147 	return 0;
148 }
149 
150 int testu_snan(long flags)
151 {
152 	feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
153 
154 	asm ("\n"
155 	"	push	%0""\n"
156 	"	popf""\n"
157 //	"	flds	snan""\n"	// WRONG, this will convert 32-bit fp snan to a *qnan* in 80-bit fp register!
158 //	"	fstpt	snan1""\n"	// if uncommented, it prints "snan1:7fff c111 1100 0000 0000" - c111, not 8111!
159 //	"	fnclex""\n"		// flds of a snan raised FE_INVALID, clear it
160 	"	fldt	snan80""\n"	// fldt never raise FE_INVALID
161 	"	fld1""\n"
162 	"	fucomi	%%st(1), %%st" "\n"
163 	"	ffree	%%st(0)" "\n"
164 	"	ffree	%%st(1)" "\n"
165 	"	pushf""\n"
166 	"	pop	res_fcomi_nan_1""\n"
167 	:
168 	: "r" (flags)
169 	);
170 	if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) {
171 		printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags);
172 		return 1;
173 	}
174 //	printf("snan:%x snan1:%04x %04x %04x %04x %04x\n", snan, snan1[4], snan1[3], snan1[2], snan1[1], snan1[0]);
175 	if (fetestexcept(FE_INVALID) != FE_INVALID) {
176 		printf("[BAD]\tFE_INVALID is not set in %s\n", __func__);
177 		return 1;
178 	}
179 	return 0;
180 }
181 
182 int testp(long flags)
183 {
184 	feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
185 
186 	asm ("\n"
187 
188 	"	push	%0""\n"
189 	"	popf""\n"
190 	"	fld1""\n"
191 	"	fldpi""\n"
192 	"	fcomip	%%st(1), %%st" "\n"
193 	"	ffree	%%st(0)" "\n"
194 	"	pushf""\n"
195 	"	pop	res_fcomi_1_pi""\n"
196 
197 	"	push	%0""\n"
198 	"	popf""\n"
199 	"	fldpi""\n"
200 	"	fld1""\n"
201 	"	fcomip	%%st(1), %%st" "\n"
202 	"	ffree	%%st(0)" "\n"
203 	"	pushf""\n"
204 	"	pop	res_fcomi_pi_1""\n"
205 
206 	"	push	%0""\n"
207 	"	popf""\n"
208 	"	fld1""\n"
209 	"	fld1""\n"
210 	"	fcomip	%%st(1), %%st" "\n"
211 	"	ffree	%%st(0)" "\n"
212 	"	pushf""\n"
213 	"	pop	res_fcomi_1_1""\n"
214 	:
215 	: "r" (flags)
216 	);
217 	if ((res_fcomi_1_pi & ARITH) != (0)) {
218 		printf("[BAD]\tfcomi_1_pi with flags:%lx\n", flags);
219 		return 1;
220 	}
221 	if ((res_fcomi_pi_1 & ARITH) != (CF)) {
222 		printf("[BAD]\tfcomi_pi_1 with flags:%lx->%lx\n", flags, res_fcomi_pi_1 & ARITH);
223 		return 1;
224 	}
225 	if ((res_fcomi_1_1 & ARITH) != (ZF)) {
226 		printf("[BAD]\tfcomi_1_1 with flags:%lx\n", flags);
227 		return 1;
228 	}
229 	if (fetestexcept(FE_INVALID) != 0) {
230 		printf("[BAD]\tFE_INVALID is set in %s\n", __func__);
231 		return 1;
232 	}
233 	return 0;
234 }
235 
236 int testp_qnan(long flags)
237 {
238 	feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
239 
240 	asm ("\n"
241 	"	push	%0""\n"
242 	"	popf""\n"
243 	"	flds	qnan""\n"
244 	"	fld1""\n"
245 	"	fnclex""\n"		// fld of a qnan raised FE_INVALID, clear it
246 	"	fcomip	%%st(1), %%st" "\n"
247 	"	ffree	%%st(0)" "\n"
248 	"	pushf""\n"
249 	"	pop	res_fcomi_nan_1""\n"
250 	:
251 	: "r" (flags)
252 	);
253 	if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) {
254 		printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags);
255 		return 1;
256 	}
257 	if (fetestexcept(FE_INVALID) != FE_INVALID) {
258 		printf("[BAD]\tFE_INVALID is not set in %s\n", __func__);
259 		return 1;
260 	}
261 	return 0;
262 }
263 
264 int testup_qnan(long flags)
265 {
266 	feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
267 
268 	asm ("\n"
269 	"	push	%0""\n"
270 	"	popf""\n"
271 	"	flds	qnan""\n"
272 	"	fld1""\n"
273 	"	fnclex""\n"		// fld of a qnan raised FE_INVALID, clear it
274 	"	fucomip	%%st(1), %%st" "\n"
275 	"	ffree	%%st(0)" "\n"
276 	"	pushf""\n"
277 	"	pop	res_fcomi_nan_1""\n"
278 	:
279 	: "r" (flags)
280 	);
281 	if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) {
282 		printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags);
283 		return 1;
284 	}
285 	if (fetestexcept(FE_INVALID) != 0) {
286 		printf("[BAD]\tFE_INVALID is set in %s\n", __func__);
287 		return 1;
288 	}
289 	return 0;
290 }
291 
292 void sighandler(int sig)
293 {
294 	printf("[FAIL]\tGot signal %d, exiting\n", sig);
295 	exit(1);
296 }
297 
298 int main(int argc, char **argv, char **envp)
299 {
300 	int err = 0;
301 
302 	/* SIGILL triggers on 32-bit kernels w/o fcomi emulation
303 	 * when run with "no387 nofxsr". Other signals are caught
304 	 * just in case.
305 	 */
306 	signal(SIGILL, sighandler);
307 	signal(SIGFPE, sighandler);
308 	signal(SIGSEGV, sighandler);
309 
310 	printf("[RUN]\tTesting f[u]comi[p] instructions\n");
311 	err |= test(0);
312 	err |= test_qnan(0);
313 	err |= testu_qnan(0);
314 	err |= testu_snan(0);
315 	err |= test(CF|ZF|PF);
316 	err |= test_qnan(CF|ZF|PF);
317 	err |= testu_qnan(CF|ZF|PF);
318 	err |= testu_snan(CF|ZF|PF);
319 	err |= testp(0);
320 	err |= testp_qnan(0);
321 	err |= testup_qnan(0);
322 	err |= testp(CF|ZF|PF);
323 	err |= testp_qnan(CF|ZF|PF);
324 	err |= testup_qnan(CF|ZF|PF);
325 	if (!err)
326 		printf("[OK]\tf[u]comi[p]\n");
327 	else
328 		printf("[FAIL]\tf[u]comi[p] errors: %d\n", err);
329 
330 	return err;
331 }
332