14132431fSAlex Bennée /*
24132431fSAlex Bennée * x86 FPREM test - executes the FPREM and FPREM1 instructions with corner case
34132431fSAlex Bennée * operands and prints the operands, result and FPU status word.
44132431fSAlex Bennée *
54132431fSAlex Bennée * Run this on real hardware, then under QEMU, and diff the outputs, to compare
64132431fSAlex Bennée * QEMU's implementation to your hardware. The 'run-test-i386-fprem' make
74132431fSAlex Bennée * target does this.
84132431fSAlex Bennée *
94132431fSAlex Bennée * Copyright (c) 2003 Fabrice Bellard
104132431fSAlex Bennée * Copyright (c) 2012 Catalin Patulea
114132431fSAlex Bennée *
124132431fSAlex Bennée * This program is free software; you can redistribute it and/or modify
134132431fSAlex Bennée * it under the terms of the GNU General Public License as published by
144132431fSAlex Bennée * the Free Software Foundation; either version 2 of the License, or
154132431fSAlex Bennée * (at your option) any later version.
164132431fSAlex Bennée *
174132431fSAlex Bennée * This program is distributed in the hope that it will be useful,
184132431fSAlex Bennée * but WITHOUT ANY WARRANTY; without even the implied warranty of
194132431fSAlex Bennée * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
204132431fSAlex Bennée * GNU General Public License for more details.
214132431fSAlex Bennée *
224132431fSAlex Bennée * You should have received a copy of the GNU General Public License
234132431fSAlex Bennée * along with this program; if not, see <http://www.gnu.org/licenses/>.
244132431fSAlex Bennée */
254132431fSAlex Bennée
26*25f9e7e8SAlex Bennée #include <stdio.h>
27*25f9e7e8SAlex Bennée #include <stdint.h>
28*25f9e7e8SAlex Bennée
29*25f9e7e8SAlex Bennée #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
304132431fSAlex Bennée
314132431fSAlex Bennée /*
324132431fSAlex Bennée * Inspired by <ieee754.h>'s union ieee854_long_double, but with single
334132431fSAlex Bennée * long long mantissa fields and assuming little-endianness for simplicity.
344132431fSAlex Bennée */
354132431fSAlex Bennée union float80u {
364132431fSAlex Bennée long double d;
374132431fSAlex Bennée
384132431fSAlex Bennée /* This is the IEEE 854 double-extended-precision format. */
394132431fSAlex Bennée struct {
404132431fSAlex Bennée unsigned long long mantissa:63;
414132431fSAlex Bennée unsigned int one:1;
424132431fSAlex Bennée unsigned int exponent:15;
434132431fSAlex Bennée unsigned int negative:1;
444132431fSAlex Bennée unsigned int empty:16;
45*25f9e7e8SAlex Bennée } __attribute__((packed)) ieee;
464132431fSAlex Bennée
474132431fSAlex Bennée /* This is for NaNs in the IEEE 854 double-extended-precision format. */
484132431fSAlex Bennée struct {
494132431fSAlex Bennée unsigned long long mantissa:62;
504132431fSAlex Bennée unsigned int quiet_nan:1;
514132431fSAlex Bennée unsigned int one:1;
524132431fSAlex Bennée unsigned int exponent:15;
534132431fSAlex Bennée unsigned int negative:1;
544132431fSAlex Bennée unsigned int empty:16;
55*25f9e7e8SAlex Bennée } __attribute__((packed)) ieee_nan;
564132431fSAlex Bennée };
574132431fSAlex Bennée
584132431fSAlex Bennée #define IEEE854_LONG_DOUBLE_BIAS 0x3fff
594132431fSAlex Bennée
604132431fSAlex Bennée static const union float80u q_nan = {
614132431fSAlex Bennée .ieee_nan.negative = 0, /* X */
624132431fSAlex Bennée .ieee_nan.exponent = 0x7fff,
634132431fSAlex Bennée .ieee_nan.one = 1,
644132431fSAlex Bennée .ieee_nan.quiet_nan = 1,
654132431fSAlex Bennée .ieee_nan.mantissa = 0,
664132431fSAlex Bennée };
674132431fSAlex Bennée
684132431fSAlex Bennée static const union float80u s_nan = {
694132431fSAlex Bennée .ieee_nan.negative = 0, /* X */
704132431fSAlex Bennée .ieee_nan.exponent = 0x7fff,
714132431fSAlex Bennée .ieee_nan.one = 1,
724132431fSAlex Bennée .ieee_nan.quiet_nan = 0,
734132431fSAlex Bennée .ieee_nan.mantissa = 1, /* nonzero */
744132431fSAlex Bennée };
754132431fSAlex Bennée
764132431fSAlex Bennée static const union float80u pos_inf = {
774132431fSAlex Bennée .ieee.negative = 0,
784132431fSAlex Bennée .ieee.exponent = 0x7fff,
794132431fSAlex Bennée .ieee.one = 1,
804132431fSAlex Bennée .ieee.mantissa = 0,
814132431fSAlex Bennée };
824132431fSAlex Bennée
834132431fSAlex Bennée static const union float80u pseudo_pos_inf = { /* "unsupported" */
844132431fSAlex Bennée .ieee.negative = 0,
854132431fSAlex Bennée .ieee.exponent = 0x7fff,
864132431fSAlex Bennée .ieee.one = 0,
874132431fSAlex Bennée .ieee.mantissa = 0,
884132431fSAlex Bennée };
894132431fSAlex Bennée
904132431fSAlex Bennée static const union float80u pos_denorm = {
914132431fSAlex Bennée .ieee.negative = 0,
924132431fSAlex Bennée .ieee.exponent = 0,
934132431fSAlex Bennée .ieee.one = 0,
944132431fSAlex Bennée .ieee.mantissa = 1,
954132431fSAlex Bennée };
964132431fSAlex Bennée
974132431fSAlex Bennée static const union float80u smallest_positive_norm = {
984132431fSAlex Bennée .ieee.negative = 0,
994132431fSAlex Bennée .ieee.exponent = 1,
1004132431fSAlex Bennée .ieee.one = 1,
1014132431fSAlex Bennée .ieee.mantissa = 0,
1024132431fSAlex Bennée };
1034132431fSAlex Bennée
fninit()1044132431fSAlex Bennée static void fninit()
1054132431fSAlex Bennée {
1064132431fSAlex Bennée asm volatile ("fninit\n");
1074132431fSAlex Bennée }
1084132431fSAlex Bennée
fprem(long double a,long double b,uint16_t * sw)1094132431fSAlex Bennée static long double fprem(long double a, long double b, uint16_t *sw)
1104132431fSAlex Bennée {
1114132431fSAlex Bennée long double result;
1124132431fSAlex Bennée asm volatile ("fprem\n"
1134132431fSAlex Bennée "fnstsw %1\n"
1144132431fSAlex Bennée : "=t" (result), "=m" (*sw)
1154132431fSAlex Bennée : "0" (a), "u" (b)
1164132431fSAlex Bennée : "st(1)");
1174132431fSAlex Bennée return result;
1184132431fSAlex Bennée }
1194132431fSAlex Bennée
fprem1(long double a,long double b,uint16_t * sw)1204132431fSAlex Bennée static long double fprem1(long double a, long double b, uint16_t *sw)
1214132431fSAlex Bennée {
1224132431fSAlex Bennée long double result;
1234132431fSAlex Bennée asm volatile ("fprem1\n"
1244132431fSAlex Bennée "fnstsw %1\n"
1254132431fSAlex Bennée : "=t" (result), "=m" (*sw)
1264132431fSAlex Bennée : "0" (a), "u" (b)
1274132431fSAlex Bennée : "st(1)");
1284132431fSAlex Bennée return result;
1294132431fSAlex Bennée }
1304132431fSAlex Bennée
1314132431fSAlex Bennée #define FPUS_IE (1 << 0)
1324132431fSAlex Bennée #define FPUS_DE (1 << 1)
1334132431fSAlex Bennée #define FPUS_ZE (1 << 2)
1344132431fSAlex Bennée #define FPUS_OE (1 << 3)
1354132431fSAlex Bennée #define FPUS_UE (1 << 4)
1364132431fSAlex Bennée #define FPUS_PE (1 << 5)
1374132431fSAlex Bennée #define FPUS_SF (1 << 6)
1384132431fSAlex Bennée #define FPUS_SE (1 << 7)
1394132431fSAlex Bennée #define FPUS_C0 (1 << 8)
1404132431fSAlex Bennée #define FPUS_C1 (1 << 9)
1414132431fSAlex Bennée #define FPUS_C2 (1 << 10)
1424132431fSAlex Bennée #define FPUS_TOP 0x3800
1434132431fSAlex Bennée #define FPUS_C3 (1 << 14)
1444132431fSAlex Bennée #define FPUS_B (1 << 15)
1454132431fSAlex Bennée
1464132431fSAlex Bennée #define FPUS_EMASK 0x007f
1474132431fSAlex Bennée
1484132431fSAlex Bennée #define FPUC_EM 0x3f
1494132431fSAlex Bennée
psw(uint16_t sw)1504132431fSAlex Bennée static void psw(uint16_t sw)
1514132431fSAlex Bennée {
1524132431fSAlex Bennée printf("SW: C3 TopC2C1C0\n");
1534132431fSAlex Bennée printf("SW: %c %d %3d %d %d %d %c %c %c %c %c %c %c %c\n",
1544132431fSAlex Bennée sw & FPUS_B ? 'B' : 'b',
1554132431fSAlex Bennée !!(sw & FPUS_C3),
1564132431fSAlex Bennée (sw & FPUS_TOP) >> 11,
1574132431fSAlex Bennée !!(sw & FPUS_C2),
1584132431fSAlex Bennée !!(sw & FPUS_C1),
1594132431fSAlex Bennée !!(sw & FPUS_C0),
1604132431fSAlex Bennée (sw & FPUS_SE) ? 'S' : 's',
1614132431fSAlex Bennée (sw & FPUS_SF) ? 'F' : 'f',
1624132431fSAlex Bennée (sw & FPUS_PE) ? 'P' : 'p',
1634132431fSAlex Bennée (sw & FPUS_UE) ? 'U' : 'u',
1644132431fSAlex Bennée (sw & FPUS_OE) ? 'O' : 'o',
1654132431fSAlex Bennée (sw & FPUS_ZE) ? 'Z' : 'z',
1664132431fSAlex Bennée (sw & FPUS_DE) ? 'D' : 'd',
1674132431fSAlex Bennée (sw & FPUS_IE) ? 'I' : 'i');
1684132431fSAlex Bennée }
1694132431fSAlex Bennée
do_fprem(long double a,long double b)1704132431fSAlex Bennée static void do_fprem(long double a, long double b)
1714132431fSAlex Bennée {
1724132431fSAlex Bennée const union float80u au = {.d = a};
1734132431fSAlex Bennée const union float80u bu = {.d = b};
1744132431fSAlex Bennée union float80u ru;
1754132431fSAlex Bennée uint16_t sw;
1764132431fSAlex Bennée
1774132431fSAlex Bennée printf("A: S=%d Exp=%04x Int=%d (QNaN=%d) Sig=%016llx (%.06Le)\n",
1784132431fSAlex Bennée au.ieee.negative, au.ieee.exponent, au.ieee.one,
1794132431fSAlex Bennée au.ieee_nan.quiet_nan, (unsigned long long)au.ieee.mantissa,
1804132431fSAlex Bennée a);
1814132431fSAlex Bennée printf("B: S=%d Exp=%04x Int=%d (QNaN=%d) Sig=%016llx (%.06Le)\n",
1824132431fSAlex Bennée bu.ieee.negative, bu.ieee.exponent, bu.ieee.one,
1834132431fSAlex Bennée bu.ieee_nan.quiet_nan, (unsigned long long)bu.ieee.mantissa,
1844132431fSAlex Bennée b);
1854132431fSAlex Bennée fflush(stdout);
1864132431fSAlex Bennée
1874132431fSAlex Bennée fninit();
1884132431fSAlex Bennée ru.d = fprem(a, b, &sw);
1894132431fSAlex Bennée psw(sw);
1904132431fSAlex Bennée
1914132431fSAlex Bennée printf("R : S=%d Exp=%04x Int=%d (QNaN=%d) Sig=%016llx (%.06Le)\n",
1924132431fSAlex Bennée ru.ieee.negative, ru.ieee.exponent, ru.ieee.one,
1934132431fSAlex Bennée ru.ieee_nan.quiet_nan, (unsigned long long)ru.ieee.mantissa,
1944132431fSAlex Bennée ru.d);
1954132431fSAlex Bennée
1964132431fSAlex Bennée fninit();
1974132431fSAlex Bennée ru.d = fprem1(a, b, &sw);
1984132431fSAlex Bennée psw(sw);
1994132431fSAlex Bennée
2004132431fSAlex Bennée printf("R1: S=%d Exp=%04x Int=%d (QNaN=%d) Sig=%016llx (%.06Le)\n",
2014132431fSAlex Bennée ru.ieee.negative, ru.ieee.exponent, ru.ieee.one,
2024132431fSAlex Bennée ru.ieee_nan.quiet_nan, (unsigned long long)ru.ieee.mantissa,
2034132431fSAlex Bennée ru.d);
2044132431fSAlex Bennée
2054132431fSAlex Bennée printf("\n");
2064132431fSAlex Bennée }
2074132431fSAlex Bennée
do_fprem_stack_underflow(void)2084132431fSAlex Bennée static void do_fprem_stack_underflow(void)
2094132431fSAlex Bennée {
2104132431fSAlex Bennée const long double a = 1.0;
2114132431fSAlex Bennée union float80u ru;
2124132431fSAlex Bennée uint16_t sw;
2134132431fSAlex Bennée
2144132431fSAlex Bennée fninit();
2154132431fSAlex Bennée asm volatile ("fprem\n"
2164132431fSAlex Bennée "fnstsw %1\n"
2174132431fSAlex Bennée : "=t" (ru.d), "=m" (sw)
2184132431fSAlex Bennée : "0" (a)
2194132431fSAlex Bennée : "st(1)");
2204132431fSAlex Bennée psw(sw);
2214132431fSAlex Bennée
2224132431fSAlex Bennée printf("R: S=%d Exp=%04x Int=%d (QNaN=%d) Sig=%016llx (%.06Le)\n",
2234132431fSAlex Bennée ru.ieee.negative, ru.ieee.exponent, ru.ieee.one,
2244132431fSAlex Bennée ru.ieee_nan.quiet_nan, (unsigned long long)ru.ieee.mantissa,
2254132431fSAlex Bennée ru.d);
2264132431fSAlex Bennée printf("\n");
2274132431fSAlex Bennée }
2284132431fSAlex Bennée
test_fprem_cases(void)2294132431fSAlex Bennée static void test_fprem_cases(void)
2304132431fSAlex Bennée {
2314132431fSAlex Bennée printf("= stack underflow =\n");
2324132431fSAlex Bennée do_fprem_stack_underflow();
2334132431fSAlex Bennée
2344132431fSAlex Bennée printf("= invalid operation =\n");
235*25f9e7e8SAlex Bennée do_fprem(q_nan.d, 1.0);
2364132431fSAlex Bennée do_fprem(s_nan.d, 1.0);
2374132431fSAlex Bennée do_fprem(1.0, 0.0);
2384132431fSAlex Bennée do_fprem(pos_inf.d, 1.0);
2394132431fSAlex Bennée do_fprem(pseudo_pos_inf.d, 1.0);
2404132431fSAlex Bennée
2414132431fSAlex Bennée printf("= denormal =\n");
2424132431fSAlex Bennée do_fprem(pos_denorm.d, 1.0);
2434132431fSAlex Bennée do_fprem(1.0, pos_denorm.d);
2444132431fSAlex Bennée
245*25f9e7e8SAlex Bennée do_fprem(smallest_positive_norm.d, smallest_positive_norm.d);
246*25f9e7e8SAlex Bennée
2474132431fSAlex Bennée /* printf("= underflow =\n"); */
2484132431fSAlex Bennée /* TODO: Is there a case where FPREM raises underflow? */
2494132431fSAlex Bennée }
2504132431fSAlex Bennée
test_fprem_pairs(void)2514132431fSAlex Bennée static void test_fprem_pairs(void)
2524132431fSAlex Bennée {
2534132431fSAlex Bennée unsigned long long count;
2544132431fSAlex Bennée
2554132431fSAlex Bennée unsigned int negative_index_a = 0;
2564132431fSAlex Bennée unsigned int negative_index_b = 0;
2574132431fSAlex Bennée static const unsigned int negative_values[] = {
2584132431fSAlex Bennée 0,
2594132431fSAlex Bennée 1,
2604132431fSAlex Bennée };
2614132431fSAlex Bennée
2624132431fSAlex Bennée unsigned int exponent_index_a = 0;
2634132431fSAlex Bennée unsigned int exponent_index_b = 0;
2644132431fSAlex Bennée static const unsigned int exponent_values[] = {
2654132431fSAlex Bennée 0,
2664132431fSAlex Bennée 1,
2674132431fSAlex Bennée 2,
2684132431fSAlex Bennée IEEE854_LONG_DOUBLE_BIAS - 1,
2694132431fSAlex Bennée IEEE854_LONG_DOUBLE_BIAS,
2704132431fSAlex Bennée IEEE854_LONG_DOUBLE_BIAS + 1,
2714132431fSAlex Bennée 0x7ffd,
2724132431fSAlex Bennée 0x7ffe,
2734132431fSAlex Bennée 0x7fff,
2744132431fSAlex Bennée };
2754132431fSAlex Bennée
2764132431fSAlex Bennée unsigned int one_index_a = 0;
2774132431fSAlex Bennée unsigned int one_index_b = 0;
2784132431fSAlex Bennée static const unsigned int one_values[] = {
2794132431fSAlex Bennée 0,
2804132431fSAlex Bennée 1,
2814132431fSAlex Bennée };
2824132431fSAlex Bennée
2834132431fSAlex Bennée unsigned int quiet_nan_index_a = 0;
2844132431fSAlex Bennée unsigned int quiet_nan_index_b = 0;
2854132431fSAlex Bennée static const unsigned int quiet_nan_values[] = {
2864132431fSAlex Bennée 0,
2874132431fSAlex Bennée 1,
2884132431fSAlex Bennée };
2894132431fSAlex Bennée
2904132431fSAlex Bennée unsigned int mantissa_index_a = 0;
2914132431fSAlex Bennée unsigned int mantissa_index_b = 0;
2924132431fSAlex Bennée static const unsigned long long mantissa_values[] = {
2934132431fSAlex Bennée 0,
2944132431fSAlex Bennée 1,
2954132431fSAlex Bennée 2,
2964132431fSAlex Bennée 0x3ffffffffffffffdULL,
2974132431fSAlex Bennée 0x3ffffffffffffffeULL,
2984132431fSAlex Bennée 0x3fffffffffffffffULL,
2994132431fSAlex Bennée };
3004132431fSAlex Bennée
3014132431fSAlex Bennée for (count = 0; ; ++count) {
3024132431fSAlex Bennée #define INIT_FIELD(var, field) \
3034132431fSAlex Bennée .ieee_nan.field = field##_values[field##_index_##var]
3044132431fSAlex Bennée const union float80u a = {
3054132431fSAlex Bennée INIT_FIELD(a, negative),
3064132431fSAlex Bennée INIT_FIELD(a, exponent),
3074132431fSAlex Bennée INIT_FIELD(a, one),
3084132431fSAlex Bennée INIT_FIELD(a, quiet_nan),
3094132431fSAlex Bennée INIT_FIELD(a, mantissa),
3104132431fSAlex Bennée };
3114132431fSAlex Bennée const union float80u b = {
3124132431fSAlex Bennée INIT_FIELD(b, negative),
3134132431fSAlex Bennée INIT_FIELD(b, exponent),
3144132431fSAlex Bennée INIT_FIELD(b, one),
3154132431fSAlex Bennée INIT_FIELD(b, quiet_nan),
3164132431fSAlex Bennée INIT_FIELD(b, mantissa),
3174132431fSAlex Bennée };
3184132431fSAlex Bennée #undef INIT_FIELD
3194132431fSAlex Bennée
3204132431fSAlex Bennée do_fprem(a.d, b.d);
3214132431fSAlex Bennée
3224132431fSAlex Bennée int carry = 1;
3234132431fSAlex Bennée #define CARRY_INTO(var, field) do { \
3244132431fSAlex Bennée if (carry) { \
3254132431fSAlex Bennée if (++field##_index_##var == ARRAY_SIZE(field##_values)) { \
3264132431fSAlex Bennée field##_index_##var = 0; \
3274132431fSAlex Bennée } else { \
3284132431fSAlex Bennée carry = 0; \
3294132431fSAlex Bennée } \
3304132431fSAlex Bennée } \
3314132431fSAlex Bennée } while (0)
3324132431fSAlex Bennée CARRY_INTO(b, mantissa);
3334132431fSAlex Bennée CARRY_INTO(b, quiet_nan);
3344132431fSAlex Bennée CARRY_INTO(b, one);
3354132431fSAlex Bennée CARRY_INTO(b, exponent);
3364132431fSAlex Bennée CARRY_INTO(b, negative);
3374132431fSAlex Bennée CARRY_INTO(a, mantissa);
3384132431fSAlex Bennée CARRY_INTO(a, quiet_nan);
3394132431fSAlex Bennée CARRY_INTO(a, one);
3404132431fSAlex Bennée CARRY_INTO(a, exponent);
3414132431fSAlex Bennée CARRY_INTO(a, negative);
3424132431fSAlex Bennée #undef CARRY_INTO
3434132431fSAlex Bennée
3444132431fSAlex Bennée if (carry) {
3454132431fSAlex Bennée break;
3464132431fSAlex Bennée }
3474132431fSAlex Bennée }
3484132431fSAlex Bennée
3494132431fSAlex Bennée fprintf(stderr, "test-i386-fprem: tested %llu cases\n", count);
3504132431fSAlex Bennée }
3514132431fSAlex Bennée
main(int argc,char ** argv)3524132431fSAlex Bennée int main(int argc, char **argv)
3534132431fSAlex Bennée {
3544132431fSAlex Bennée test_fprem_cases();
3554132431fSAlex Bennée test_fprem_pairs();
3564132431fSAlex Bennée return 0;
3574132431fSAlex Bennée }
358