On 09/16/2015 09:48 PM, Denys Vlasenko wrote: > Run-tested by booting with "no387 nofxsr" and running test programs: > > # ./test_FCMOV > [RUN] Testing fcmovCC instructions > [OK] fcmovCC > # ./test_FCOMI > [RUN] Testing f[u]comi[p] instructions > [OK] f[u]comi[p]
The sources for these test programs are in this email. Ingo, let me know if you want a patch which adds them to tools/testing/selftests/x86/*
#undef _GNU_SOURCE #define _GNU_SOURCE 1 #undef __USE_GNU #define __USE_GNU 1 #include <unistd.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <signal.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/select.h> #include <sys/time.h> //#include <sys/ptrace.h> #include <sys/wait.h> #define TEST(insn) \ long double __attribute__((noinline)) insn(long flags) \ { \ long double out; \ asm ("\n" \ " push %1""\n" \ " popf""\n" \ " fldpi""\n" \ " fld1""\n" \ " " #insn " %%st(1), %%st" "\n" \ " ffree %%st(1)" "\n" \ : "=t" (out) \ : "r" (flags) \ ); \ return out; \ } TEST(fcmovb) TEST(fcmove) TEST(fcmovbe) TEST(fcmovu) TEST(fcmovnb) TEST(fcmovne) TEST(fcmovnbe) TEST(fcmovnu) enum { CF = 1 << 0, PF = 1 << 2, ZF = 1 << 6, }; int main(int argc, char **argv, char **envp) { int err = 0; printf("[RUN]\tTesting fcmovCC instructions\n"); /* If fcmovCC() returns 1.0, the move wasn't done */ err |= !(fcmovb(0) == 1.0); err |= !(fcmovnb(0) != 1.0); err |= !(fcmove(0) == 1.0); err |= !(fcmovne(0) != 1.0); err |= !(fcmovbe(0) == 1.0); err |= !(fcmovnbe(0) != 1.0); err |= !(fcmovu(0) == 1.0); err |= !(fcmovnu(0) != 1.0); err |= !(fcmovb(CF) != 1.0); err |= !(fcmovnb(CF) == 1.0); err |= !(fcmove(CF) == 1.0); err |= !(fcmovne(CF) != 1.0); err |= !(fcmovbe(CF) != 1.0); err |= !(fcmovnbe(CF) == 1.0); err |= !(fcmovu(CF) == 1.0); err |= !(fcmovnu(CF) != 1.0); err |= !(fcmovb(ZF) == 1.0); err |= !(fcmovnb(ZF) != 1.0); err |= !(fcmove(ZF) != 1.0); err |= !(fcmovne(ZF) == 1.0); err |= !(fcmovbe(ZF) != 1.0); err |= !(fcmovnbe(ZF) == 1.0); err |= !(fcmovu(ZF) == 1.0); err |= !(fcmovnu(ZF) != 1.0); err |= !(fcmovb(PF) == 1.0); err |= !(fcmovnb(PF) != 1.0); err |= !(fcmove(PF) == 1.0); err |= !(fcmovne(PF) != 1.0); err |= !(fcmovbe(PF) == 1.0); err |= !(fcmovnbe(PF) != 1.0); err |= !(fcmovu(PF) != 1.0); err |= !(fcmovnu(PF) == 1.0); if (!err) printf("[OK]\tfcmovCC\n"); else printf("[FAIL]\tfcmovCC errors: %d\n", err); return err; }
#undef _GNU_SOURCE #define _GNU_SOURCE 1 #undef __USE_GNU #define __USE_GNU 1 #include <unistd.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <signal.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/select.h> #include <sys/time.h> //#include <sys/ptrace.h> #include <sys/wait.h> #include <fenv.h> enum { CF = 1 << 0, PF = 1 << 2, ZF = 1 << 6, ARITH = CF | PF | ZF, }; long res_fcomi_pi_1; long res_fcomi_1_pi; long res_fcomi_1_1; long res_fcomi_nan_1; /* sNaN is s|111 1111 1|1xx xxxx xxxx xxxx xxxx xxxx */ /* qNaN is s|111 1111 1|0xx xxxx xxxx xxxx xxxx xxxx (some x must be nonzero) */ int snan = 0x7fc11111; int qnan = 0x7f811111; unsigned short snan1[5]; /* sNaN80 is s|111 1111 1111 1111 |10xx xx...xx (some x must be nonzero) */ unsigned short snan80[5] = { 0x1111, 0x1111, 0x1111, 0x8111, 0x7fff }; int test(long flags) { feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); asm ("\n" " push %0""\n" " popf""\n" " fld1""\n" " fldpi""\n" " fcomi %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " ffree %%st(1)" "\n" " pushf""\n" " pop res_fcomi_1_pi""\n" " push %0""\n" " popf""\n" " fldpi""\n" " fld1""\n" " fcomi %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " ffree %%st(1)" "\n" " pushf""\n" " pop res_fcomi_pi_1""\n" " push %0""\n" " popf""\n" " fld1""\n" " fld1""\n" " fcomi %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " ffree %%st(1)" "\n" " pushf""\n" " pop res_fcomi_1_1""\n" : : "r" (flags) ); if ((res_fcomi_1_pi & ARITH) != (0)) { printf("[BAD]\tfcomi_1_pi with flags:%lx\n", flags); return 1; } if ((res_fcomi_pi_1 & ARITH) != (CF)) { printf("[BAD]\tfcomi_pi_1 with flags:%lx->%lx\n", flags, res_fcomi_pi_1 & ARITH); return 1; } if ((res_fcomi_1_1 & ARITH) != (ZF)) { printf("[BAD]\tfcomi_1_1 with flags:%lx\n", flags); return 1; } if (fetestexcept(FE_INVALID) != 0) { printf("[BAD]\tFE_INVALID is set in %s\n", __func__); return 1; } return 0; } int test_qnan(long flags) { feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); asm ("\n" " push %0""\n" " popf""\n" " flds qnan""\n" " fld1""\n" " fnclex""\n" // fld of a qnan raised FE_INVALID, clear it " fcomi %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " ffree %%st(1)" "\n" " pushf""\n" " pop res_fcomi_nan_1""\n" : : "r" (flags) ); if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) { printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags); return 1; } if (fetestexcept(FE_INVALID) != FE_INVALID) { printf("[BAD]\tFE_INVALID is not set in %s\n", __func__); return 1; } return 0; } int testu_qnan(long flags) { feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); asm ("\n" " push %0""\n" " popf""\n" " flds qnan""\n" " fld1""\n" " fnclex""\n" // fld of a qnan raised FE_INVALID, clear it " fucomi %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " ffree %%st(1)" "\n" " pushf""\n" " pop res_fcomi_nan_1""\n" : : "r" (flags) ); if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) { printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags); return 1; } if (fetestexcept(FE_INVALID) != 0) { printf("[BAD]\tFE_INVALID is set in %s\n", __func__); return 1; } return 0; } int testu_snan(long flags) { feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); asm ("\n" " push %0""\n" " popf""\n" // " flds snan""\n" // WRONG, this will convert 32-bit fp snan to a *qnan* in 80-bit fp register! // " fstpt snan1""\n" // if uncommented, it prints "snan1:7fff c111 1100 0000 0000" - c111, not 8111! // " fnclex""\n" // flds of a snan raised FE_INVALID, clear it " fldt snan80""\n" // fldt never raise FE_INVALID " fld1""\n" " fucomi %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " ffree %%st(1)" "\n" " pushf""\n" " pop res_fcomi_nan_1""\n" : : "r" (flags) ); if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) { printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags); return 1; } // printf("snan:%x snan1:%04x %04x %04x %04x %04x\n", snan, snan1[4], snan1[3], snan1[2], snan1[1], snan1[0]); if (fetestexcept(FE_INVALID) != FE_INVALID) { printf("[BAD]\tFE_INVALID is not set in %s\n", __func__); return 1; } return 0; } int testp(long flags) { feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); asm ("\n" " push %0""\n" " popf""\n" " fld1""\n" " fldpi""\n" " fcomip %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " pushf""\n" " pop res_fcomi_1_pi""\n" " push %0""\n" " popf""\n" " fldpi""\n" " fld1""\n" " fcomip %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " pushf""\n" " pop res_fcomi_pi_1""\n" " push %0""\n" " popf""\n" " fld1""\n" " fld1""\n" " fcomip %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " pushf""\n" " pop res_fcomi_1_1""\n" : : "r" (flags) ); if ((res_fcomi_1_pi & ARITH) != (0)) { printf("[BAD]\tfcomi_1_pi with flags:%lx\n", flags); return 1; } if ((res_fcomi_pi_1 & ARITH) != (CF)) { printf("[BAD]\tfcomi_pi_1 with flags:%lx->%lx\n", flags, res_fcomi_pi_1 & ARITH); return 1; } if ((res_fcomi_1_1 & ARITH) != (ZF)) { printf("[BAD]\tfcomi_1_1 with flags:%lx\n", flags); return 1; } if (fetestexcept(FE_INVALID) != 0) { printf("[BAD]\tFE_INVALID is set in %s\n", __func__); return 1; } return 0; } int testp_qnan(long flags) { feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); asm ("\n" " push %0""\n" " popf""\n" " flds qnan""\n" " fld1""\n" " fnclex""\n" // fld of a qnan raised FE_INVALID, clear it " fcomip %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " pushf""\n" " pop res_fcomi_nan_1""\n" : : "r" (flags) ); if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) { printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags); return 1; } if (fetestexcept(FE_INVALID) != FE_INVALID) { printf("[BAD]\tFE_INVALID is not set in %s\n", __func__); return 1; } return 0; } int testup_qnan(long flags) { feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); asm ("\n" " push %0""\n" " popf""\n" " flds qnan""\n" " fld1""\n" " fnclex""\n" // fld of a qnan raised FE_INVALID, clear it " fucomip %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " pushf""\n" " pop res_fcomi_nan_1""\n" : : "r" (flags) ); if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) { printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags); return 1; } if (fetestexcept(FE_INVALID) != 0) { printf("[BAD]\tFE_INVALID is set in %s\n", __func__); return 1; } return 0; } int main(int argc, char **argv, char **envp) { int err = 0; printf("[RUN]\tTesting f[u]comi[p] instructions\n"); err |= test(0); err |= test_qnan(0); err |= testu_qnan(0); err |= testu_snan(0); err |= test(CF|ZF|PF); err |= test_qnan(CF|ZF|PF); err |= testu_qnan(CF|ZF|PF); err |= testu_snan(CF|ZF|PF); err |= testp(0); err |= testp_qnan(0); err |= testup_qnan(0); err |= testp(CF|ZF|PF); err |= testp_qnan(CF|ZF|PF); err |= testup_qnan(CF|ZF|PF); if (!err) printf("[OK]\tf[u]comi[p]\n"); else printf("[FAIL]\tf[u]comi[p] errors: %d\n", err); return err; }