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