GNU Linux-libre 4.14.290-gnu1
[releases.git] / tools / testing / selftests / x86 / test_FCOMI.c
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
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
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
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
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
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
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
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
293 void sighandler(int sig)
294 {
295         printf("[FAIL]\tGot signal %d, exiting\n", sig);
296         exit(1);
297 }
298
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 }