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 } 333