1 /*  Copyright (C) 2012 IBM
2 
3  Author: Maynard Johnson <maynardj@us.ibm.com>
4 
5  This program is free software; you can redistribute it and/or
6  modify it under the terms of the GNU General Public License as
7  published by the Free Software Foundation; either version 2 of the
8  License, or (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful, but
11  WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; if not, write to the Free Software
17  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18  02111-1307, USA.
19 
20  The GNU General Public License is contained in the file COPYING.
21  */
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdint.h>
26 
27 #if defined(HAS_DFP)
28 
29 register double f14 __asm__ ("fr14");
30 register double f15 __asm__ ("fr15");
31 register double f16 __asm__ ("fr16");
32 register double f17 __asm__ ("fr17");
33 register double f18 __asm__ ("fr18");
34 register double f19 __asm__ ("fr19");
35 
36 
37 typedef unsigned char Bool;
38 #define True 1
39 #define False 0
40 
41 
42 #define ALLCR "cr0","cr1","cr2","cr3","cr4","cr5","cr6","cr7"
43 
44 #define SET_CR(_arg) \
45       __asm__ __volatile__ ("mtcr  %0" : : "b"(_arg) : ALLCR );
46 
47 #define SET_XER(_arg) \
48       __asm__ __volatile__ ("mtxer %0" : : "b"(_arg) : "xer" );
49 
50 #define GET_CR(_lval) \
51       __asm__ __volatile__ ("mfcr %0"  : "=b"(_lval) )
52 
53 #define GET_XER(_lval) \
54       __asm__ __volatile__ ("mfxer %0" : "=b"(_lval) )
55 
56 #define GET_CR_XER(_lval_cr,_lval_xer) \
57    do { GET_CR(_lval_cr); GET_XER(_lval_xer); } while (0)
58 
59 #define SET_CR_ZERO \
60       SET_CR(0)
61 
62 #define SET_XER_ZERO \
63       SET_XER(0)
64 
65 #define SET_CR_XER_ZERO \
66    do { SET_CR_ZERO; SET_XER_ZERO; } while (0)
67 
68 #define SET_FPSCR_ZERO \
69    do { double _d = 0.0; \
70         __asm__ __volatile__ ("mtfsf 0xFF, %0" : : "f"(_d) ); \
71    } while (0)
72 
73 #define GET_FPSCR(_arg) \
74     __asm__ __volatile__ ("mffs %0"  : "=f"(_arg) )
75 
76 #define SET_FPSCR_DRN \
77     __asm__ __volatile__ ("mtfsf  1, %0, 0, 1" :  : "f"(f14) )
78 
79 
80 // The assembly-level instructions being tested
81 static Bool do_dot;
_test_dadd(void)82 static void _test_dadd (void)
83 {
84    if (do_dot)
85       __asm__ __volatile__ ("dadd. %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
86    else
87       __asm__ __volatile__ ("dadd %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
88 }
89 
_test_dsub(void)90 static void _test_dsub (void)
91 {
92    if (do_dot)
93       __asm__ __volatile__ ("dsub. %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
94    else
95       __asm__ __volatile__ ("dsub %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
96 }
97 
_test_dmul(void)98 static void _test_dmul (void)
99 {
100    if (do_dot)
101       __asm__ __volatile__ ("dmul. %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
102    else
103       __asm__ __volatile__ ("dmul %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
104 }
105 
_test_ddiv(void)106 static void _test_ddiv (void)
107 {
108    if (do_dot)
109       __asm__ __volatile__ ("ddiv. %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
110    else
111       __asm__ __volatile__ ("ddiv %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
112 }
113 
114 // Quad DFP arith instructions
_test_daddq(void)115 static void _test_daddq (void)
116 {
117    if (do_dot)
118       __asm__ __volatile__ ("daddq. %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
119    else
120       __asm__ __volatile__ ("daddq %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
121 }
122 
_test_dsubq(void)123 static void _test_dsubq (void)
124 {
125    if (do_dot)
126       __asm__ __volatile__ ("dsubq. %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
127    else
128       __asm__ __volatile__ ("dsubq %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
129 }
130 
_test_dmulq(void)131 static void _test_dmulq (void)
132 {
133    if (do_dot)
134       __asm__ __volatile__ ("dmulq. %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
135    else
136       __asm__ __volatile__ ("dmulq %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
137 }
138 
_test_ddivq(void)139 static void _test_ddivq (void)
140 {
141    if (do_dot)
142       __asm__ __volatile__ ("ddivq. %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
143    else
144       __asm__ __volatile__ ("ddivq %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
145 }
146 
_test_mffs(void)147 static void _test_mffs (void)
148 {
149    __asm__ __volatile__ ("mffs %0"  : "=f"(f14));
150 }
151 
_test_mtfsf(int upper)152 static void _test_mtfsf (int upper)
153 {
154    if (upper)
155       __asm__ __volatile__ ("mtfsf  1, %0, 0, 1" :  : "f"(f14) );
156    else
157       __asm__ __volatile__ ("mtfsf  1, %0, 0, 0" :  : "f"(f14) );
158 }
159 
160 typedef void (*test_func_t)(void);
161 typedef struct test_table
162 {
163    test_func_t test_category;
164    char * name;
165 } test_table_t;
166 
167 /*
168  *  345.0DD (0x2207c00000000000 0xe50)
169  *  1.2300e+5DD (0x2207c00000000000 0x14c000)
170  *  -16.0DD (0xa207c00000000000 0xe0)
171  *  0.00189DD (0x2206c00000000000 0xcf)
172  *  -4.1235DD (0xa205c00000000000 0x10a395bcf)
173  *  9.8399e+20DD (0x2209400000000000 0x253f1f534acdd4)
174  *  0DD (0x2208000000000000 0x0)
175  *  0DD (0x2208000000000000 0x0)
176  *  infDD (0x7800000000000000 0x0)
177  *  nanDD (0x7c00000000000000 0x0
178  */
179 static unsigned long long dfp128_vals[] = {
180                                     // Some finite numbers
181                                     0x2207c00000000000ULL, 0x0000000000000e50ULL,
182                                     0x2207c00000000000ULL, 0x000000000014c000ULL,
183                                     0xa207c00000000000ULL, 0x00000000000000e0ULL,
184                                     0x2206c00000000000ULL, 0x00000000000000cfULL,
185                                     0xa205c00000000000ULL, 0x000000010a395bcfULL,
186                                     0x6209400000fd0000ULL, 0x00253f1f534acdd4ULL, // huge number
187                                     0x000400000089b000ULL, 0x0a6000d000000049ULL, // very small number
188                                     // flavors of zero
189                                     0x2208000000000000ULL, 0x0000000000000000ULL,
190                                     0xa208000000000000ULL, 0x0000000000000000ULL, // negative
191                                     0xa248000000000000ULL, 0x0000000000000000ULL,
192                                     // flavors of NAN
193                                     0x7c00000000000000ULL, 0x0000000000000000ULL, // quiet
194                                     0xfc00000000000000ULL, 0xc00100035b007700ULL,
195                                     0x7e00000000000000ULL, 0xfe000000d0e0a0d0ULL, // signaling
196                                     // flavors of Infinity
197                                     0x7800000000000000ULL, 0x0000000000000000ULL,
198                                     0xf800000000000000ULL, 0x0000000000000000ULL, // negative
199                                     0xf900000000000000ULL, 0x0000000000000000ULL
200 };
201 
202 static unsigned long long dfp64_vals[] = {
203                                  // various finite numbers
204                                  0x2234000000000e50ULL,
205                                  0x223400000014c000ULL,
206                                  0xa2340000000000e0ULL,// negative
207                                  0x22240000000000cfULL,
208                                  0xa21400010a395bcfULL,// negative
209                                  0x6e4d3f1f534acdd4ULL,// huge number
210                                  0x000400000089b000ULL,// very small number
211                                  // flavors of zero
212                                  0x2238000000000000ULL,
213                                  0xa238000000000000ULL,
214                                  0x4248000000000000ULL,
215                                  // flavors of NAN
216                                  0x7e34000000000111ULL,
217                                  0xfe000000d0e0a0d0ULL,//signaling
218                                  0xfc00000000000000ULL,//quiet
219                                  // flavors of Infinity
220                                  0x7800000000000000ULL,
221                                  0xf800000000000000ULL,//negative
222                                  0x7a34000000000000ULL,
223 };
224 
225 
226 typedef struct dfp_test_args {
227    int fra_idx;
228    int frb_idx;
229 } dfp_test_args_t;
230 
231 
232 // Index pairs from dfp64_vals or dfp128_vals array to be used with dfp_two_arg_tests
233 static dfp_test_args_t dfp_2args_x2[] = {
234                                   {0, 1},
235                                   {2, 1},
236                                   {3, 4},
237                                   {0, 6},
238                                   {2, 4},
239                                   {5, 1},
240                                   {5, 2},
241                                   {7, 8},
242                                   {7, 1},
243                                   {9, 15},
244                                   {8, 12},
245                                   {7, 11},
246                                   {13, 2},
247                                   {13, 14},
248                                   {15, 12},
249                                   {14, 11},
250                                   {12, 12},
251                                   {12, 11},
252                                   {11, 11}
253 };
254 
255 // Index pairs from dfp64_vals array to be used with dfp_two_arg_tests
256 static dfp_test_args_t dfp_2args_x1[] = {
257                                     {0, 1},
258                                     {2, 1},
259                                     {3, 4},
260                                     {0, 6},
261                                     {2, 4},
262                                     {5, 1},
263                                     {5, 2},
264                                     {7, 1},
265                                     {7, 2},
266                                     {8, 0},
267                                     {8, 1},
268                                     {8, 2},
269                                     {7, 8},
270                                     {12, 14},
271                                     {12, 1},
272                                     {12, 13},
273                                     {12, 12},
274                                     {12, 11},
275                                     {11, 14},
276                                     {11, 0},
277                                     {11, 13},
278                                     {11, 11},
279                                     {14, 14},
280                                     {14, 3},
281                                     {14, 15},
282 };
283 
284 typedef enum {
285    LONG_TEST,
286    QUAD_TEST
287 } precision_type_t;
288 
289 typedef struct dfp_test
290 {
291    test_func_t test_func;
292    const char * name;
293    dfp_test_args_t * targs;
294    int num_tests;
295    precision_type_t precision;
296    const char * op;
297    Bool cr_supported;
298 } dfp_test_t;
299 
300 
301 static dfp_test_t
302 dfp_two_arg_tests[] = {
303                      { &_test_dadd, "dadd", dfp_2args_x1, 25, LONG_TEST, "+", False},
304                      { &_test_dsub, "dsub", dfp_2args_x1, 25, LONG_TEST, "-", False},
305                      { &_test_dmul, "dmul", dfp_2args_x2, 19, LONG_TEST, "*", False},
306                      { &_test_ddiv, "ddiv", dfp_2args_x2, 19, LONG_TEST, "/", False},
307                      { &_test_daddq, "daddq", dfp_2args_x1, 25, QUAD_TEST, "+", False},
308                      { &_test_dsubq, "dsubq", dfp_2args_x1, 25, QUAD_TEST, "-", False},
309                      { &_test_dmulq, "dmulq", dfp_2args_x2, 19, QUAD_TEST, "*", False},
310                      { &_test_ddivq, "ddivq", dfp_2args_x2, 19, QUAD_TEST, "/", False},
311                      { NULL, NULL, NULL, 0, 0, NULL}
312 };
313 
test_dfp_two_arg_ops(void)314 static void test_dfp_two_arg_ops(void)
315 {
316    test_func_t func;
317    unsigned long long u0, u0x, u1, u1x;
318    double res, d0, d1, *d0p, *d1p;
319    double d0x, d1x, *d0xp, *d1xp;
320    int k = 0;
321    u0x = u1x = 0;
322    d0p = &d0;
323    d0xp = &d0x;
324    d1p = &d1;
325    d1xp = &d1x;
326 
327    while ((func = dfp_two_arg_tests[k].test_func)) {
328       int i, repeat = 1;
329       dfp_test_t test_group = dfp_two_arg_tests[k];
330       do_dot = False;
331 
332 again:
333       for (i = 0; i < test_group.num_tests; i++) {
334          unsigned int condreg;
335          unsigned int flags;
336 
337          if (test_group.precision == LONG_TEST) {
338             u0 = dfp64_vals[test_group.targs[i].fra_idx];
339             u1 = dfp64_vals[test_group.targs[i].frb_idx];
340          } else {
341             u0 = dfp128_vals[test_group.targs[i].fra_idx * 2];
342             u0x = dfp128_vals[(test_group.targs[i].fra_idx * 2) + 1];
343             u1 = dfp128_vals[test_group.targs[i].frb_idx * 2];
344             u1x = dfp128_vals[(test_group.targs[i].frb_idx * 2) + 1];
345          }
346          *(unsigned long long *)d0p = u0;
347          *(unsigned long long *)d1p = u1;
348          f14 = d0;
349          f16 = d1;
350          if (test_group.precision == QUAD_TEST) {
351             *(unsigned long long *)d0xp = u0x;
352             *(unsigned long long *)d1xp = u1x;
353             f15 = d0x;
354             f17 = d1x;
355          }
356 
357          SET_FPSCR_ZERO;
358          SET_CR_XER_ZERO;
359          (*func)();
360          GET_CR(flags);
361          res = f18;
362 
363          condreg = (flags & 0x000000f0) >> 4;
364          printf("%s%s %016llx", test_group.name, do_dot? "." : "", u0);
365          if (test_group.precision == LONG_TEST) {
366             printf(" %s %016llx => %016llx",
367                    test_group.op, u1, *((unsigned long long *)(&res)));
368          } else {
369             double resx = f19;
370             printf(" %016llx %s %016llx %016llx ==> %016llx %016llx",
371                    u0x, test_group.op, u1, u1x,
372                    *((unsigned long long *)(&res)), *((unsigned long long *)(&resx)));
373          }
374          if (test_group.cr_supported)
375             printf(" (cr = %08x)\n", condreg);
376          else
377             printf("\n");
378 
379       }
380       printf("\n");
381       if (repeat) {
382          repeat = 0;
383          do_dot = True;
384          goto again;
385       }
386       k++;
387       printf( "\n" );
388    }
389 }
390 
test_move_toFrom_fpscr(void)391 void test_move_toFrom_fpscr(void)
392 {
393 #define BFP_MAX_RM 3
394    int shift = 0;
395    unsigned long long i, max_rm, expected_val;
396    double fpscr_in, fpscr_out;
397    unsigned long long * hex_fpscr_in = (unsigned long long *)&fpscr_in;
398    unsigned long long * hex_fpscr_out = (unsigned long long *)&fpscr_out;
399 
400 
401    max_rm = 4;
402 again:
403    /* NOTE: The first time through this loop is for setting the binary
404     * floating point rounding mode (bits 62:63 of FPSCR).  The second time
405     * through is for setting the decimal floating point rounding mode
406     * (bits 29:31 of FPSCR).  In the second time through this loop, the value
407     * returned should include the final binary FP rounding mode, along with
408     * the decimal FP rounding modes.
409     */
410    for (i = 0; i < max_rm; i++) {
411       *hex_fpscr_in = (i << shift);
412       f14 = fpscr_in;
413       _test_mtfsf(max_rm/8);
414       *hex_fpscr_in = 0ULL;
415       f14= fpscr_in;
416       _test_mffs();
417       fpscr_out = f14;
418       if (max_rm == 4) {
419          *hex_fpscr_out &= (max_rm - 1) << shift;
420          expected_val = i << shift;
421       } else {
422          *hex_fpscr_out &= BFP_MAX_RM | ((max_rm - 1) << shift);
423          expected_val = (i << shift) | BFP_MAX_RM;
424       }
425 
426       printf("FPSCR %s floating point rounding mode %016llx == %016llx? %s\n",
427              (max_rm == 8) ? "decimal" : "binary",
428              *hex_fpscr_out, expected_val,
429              (expected_val == *hex_fpscr_out) ? "yes" : "no");
430    }
431    if (max_rm == 4) {
432       max_rm = 8;
433       shift = 32;
434       goto again;
435    }
436 }
437 
test_rounding_modes(void)438 void test_rounding_modes(void)
439 {
440    int j;
441    unsigned long long u0, u1, rm_idx;
442    double res, d0, d1, *d0p, *d1p, fpscr;
443    unsigned long long * hex_fpscr = (unsigned long long *)&fpscr;
444    u0 = 0x26cc3f1f534acdd4ULL;
445    u1 = 0x27feff197a42ba06ULL;
446    d0p = &d0;
447    d1p = &d1;
448 
449    for (j = 0; j < 12; j++) {
450       for (rm_idx = 0; rm_idx < 8; rm_idx++) {
451          *hex_fpscr = 0ULL;
452          __asm__ __volatile__ ("mffs %0"  : "=f"(f14));
453          fpscr = f14;
454          *hex_fpscr &= 0xFFFFFFF0FFFFFFFFULL;
455          *hex_fpscr |= (rm_idx << 32);
456          f14 = fpscr;
457          SET_FPSCR_DRN;
458          *(unsigned long long *)d0p = u0;
459          *(unsigned long long *)d1p = u1;
460          f14 = d0;
461          f16 = d1;
462          _test_dmul();
463          res = f18;
464          printf("test #%d: dmul with rounding mode %d: %016llx * %016llx => %016llx\n",
465                 j, (int)rm_idx, u0, u1, *((unsigned long long *)(&res)));
466          printf("\n");
467       }
468       // Changing the least significant bit of one of the dmul arguments give us more
469       // opportunities for different rounding modes to yield different results which
470       // can then be validated.
471       u0++;
472    }
473 }
474 
475 static test_table_t
476          all_tests[] =
477 {
478                     { &test_dfp_two_arg_ops,
479                       "Test DFP arithmetic instructions"},
480                     { &test_rounding_modes,
481                       "Test DFP rounding modes"},
482                     { &test_move_toFrom_fpscr,
483                     "Test move to/from FPSCR"},
484                     { NULL, NULL }
485 };
486 #endif // HAS_DFP
487 
main()488 int main() {
489 #if defined(HAS_DFP)
490 
491    test_table_t aTest;
492    test_func_t func;
493    int i = 0;
494 
495    while ((func = all_tests[i].test_category)) {
496       aTest = all_tests[i];
497       printf( "%s\n", aTest.name );
498       (*func)();
499       i++;
500    }
501 
502 #endif // HAS_DFP
503    return 0;
504 }
505