1b0ccb4d09a74c94a712b2edf9894b408f270493asewardj/*  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
29register double f14 __asm__ ("fr14");
30register double f15 __asm__ ("fr15");
31register double f16 __asm__ ("fr16");
32register double f17 __asm__ ("fr17");
33register double f18 __asm__ ("fr18");
34register double f19 __asm__ ("fr19");
35
36
37typedef 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
81static Bool do_dot;
82static 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
90static 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
98static 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
106static 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
115static 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
123static 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
131static 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
139static 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
147static void _test_mffs (void)
148{
149   __asm__ __volatile__ ("mffs %0"  : "=f"(f14));
150}
151
152static 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
160typedef void (*test_func_t)(void);
161typedef 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 */
179static 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
202static 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
226typedef 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
233static 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
256static 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
284typedef enum {
285   LONG_TEST,
286   QUAD_TEST
287} precision_type_t;
288
289typedef 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
301static dfp_test_t
302dfp_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
314static 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
332again:
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
391void 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;
402again:
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
438void 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
475static 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
488int 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