linux/tools/testing/selftests/x86/test_FCOMI.c
<<
>>
Prefs
   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
  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 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
 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 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
 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 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
 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 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
 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 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
 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 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