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(flags: 0); |
313 | err |= test_qnan(flags: 0); |
314 | err |= testu_qnan(flags: 0); |
315 | err |= testu_snan(flags: 0); |
316 | err |= test(flags: CF|ZF|PF); |
317 | err |= test_qnan(flags: CF|ZF|PF); |
318 | err |= testu_qnan(flags: CF|ZF|PF); |
319 | err |= testu_snan(flags: CF|ZF|PF); |
320 | err |= testp(flags: 0); |
321 | err |= testp_qnan(flags: 0); |
322 | err |= testup_qnan(flags: 0); |
323 | err |= testp(flags: CF|ZF|PF); |
324 | err |= testp_qnan(flags: CF|ZF|PF); |
325 | err |= testup_qnan(flags: 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 | |