333 lines · c
1// SPDX-License-Identifier: GPL-2.02#undef _GNU_SOURCE3#define _GNU_SOURCE 14#undef __USE_GNU5#define __USE_GNU 16#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 17enum {18 CF = 1 << 0,19 PF = 1 << 2,20 ZF = 1 << 6,21 ARITH = CF | PF | ZF,22};23 24long res_fcomi_pi_1;25long res_fcomi_1_pi;26long res_fcomi_1_1;27long 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) */30int snan = 0x7fc11111;31int qnan = 0x7f811111;32unsigned short snan1[5];33/* sNaN80 is s|111 1111 1111 1111 |10xx xx...xx (some x must be nonzero) */34unsigned short snan80[5] = { 0x1111, 0x1111, 0x1111, 0x8111, 0x7fff };35 36int 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 93int 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 it103 " 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 122int 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 it132 " 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 151int 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 it161 " fldt snan80""\n" // fldt never raise FE_INVALID162 " 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 183int 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 237int 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 it247 " 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 265int 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 it275 " 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 293void sighandler(int sig)294{295 printf("[FAIL]\tGot signal %d, exiting\n", sig);296 exit(1);297}298 299int main(int argc, char **argv, char **envp)300{301 int err = 0;302 303 /* SIGILL triggers on 32-bit kernels w/o fcomi emulation304 * when run with "no387 nofxsr". Other signals are caught305 * 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 else329 printf("[FAIL]\tf[u]comi[p] errors: %d\n", err);330 331 return err;332}333