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