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