test_dfp4.c revision 663860b1408516d02ebfcb3a9999a134e6cfb223
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
29typedef union stuff {
30   _Decimal64  dec_val;
31   _Decimal128  dec_val128;
32   unsigned long long u64_val;
33   struct {
34      unsigned long long valu;
35      unsigned long long vall;
36   } u128;
37} dfp_val_t;
38
39
40typedef unsigned char Bool;
41#define True 1
42#define False 0
43
44
45#define ALLCR "cr0","cr1","cr2","cr3","cr4","cr5","cr6","cr7"
46
47#define SET_CR(_arg) \
48      __asm__ __volatile__ ("mtcr  %0" : : "b"(_arg) : ALLCR );
49
50#define SET_XER(_arg) \
51      __asm__ __volatile__ ("mtxer %0" : : "b"(_arg) : "xer" );
52
53#define GET_CR(_lval) \
54      __asm__ __volatile__ ("mfcr %0"  : "=b"(_lval) )
55
56#define GET_XER(_lval) \
57      __asm__ __volatile__ ("mfxer %0" : "=b"(_lval) )
58
59#define GET_CR_XER(_lval_cr,_lval_xer) \
60   do { GET_CR(_lval_cr); GET_XER(_lval_xer); } while (0)
61
62#define SET_CR_ZERO \
63      SET_CR(0)
64
65#define SET_XER_ZERO \
66      SET_XER(0)
67
68#define SET_CR_XER_ZERO \
69   do { SET_CR_ZERO; SET_XER_ZERO; } while (0)
70
71#define SET_FPSCR_ZERO \
72   do { double _d = 0.0; \
73        __asm__ __volatile__ ("mtfsf 0xFF, %0" : : "f"(_d) ); \
74   } while (0)
75
76#define GET_FPSCR(_arg) \
77    __asm__ __volatile__ ("mffs %0"  : "=f"(_arg) )
78
79#define SET_FPSCR_DRN \
80    __asm__ __volatile__ ("mtfsf  1, %0, 0, 1" :  : "f"(f14) )
81
82
83// The assembly-level instructions being tested
84
85/* In _test_dtstdc[q], DCM can be one of 6 possible data classes, numbered 0-5.
86 * In reality, DCM is a 6-bit mask field.  We just test the individual values
87 * and assume that masking multiple values would work OK.
88 * BF is the condition register bit field which can range from 0-7.  But for
89 * testing purposes, we only use BF values of '0' and '5'.
90 */
91static void _test_dtstdc(int BF, int DCM, dfp_val_t val1, dfp_val_t x1 __attribute__((unused)))
92{
93   _Decimal64 f14 = val1.dec_val;
94   if (DCM < 0 || DCM > 5 || !(BF == 0 || BF == 5)) {
95      fprintf(stderr, "Invalid inputs to asm test: a=%d, b=%d\n", BF, DCM);
96      return;
97   }
98   switch (DCM) {
99      case 0:
100         if (BF)
101            __asm__ __volatile__ ("dtstdc 5, %0, 1" : : "f" (f14));
102         else
103            __asm__ __volatile__ ("dtstdc 0, %0, 1" : : "f" (f14));
104         break;
105      case 1:
106         if (BF)
107            __asm__ __volatile__ ("dtstdc 5, %0, 2" : : "f" (f14));
108         else
109            __asm__ __volatile__ ("dtstdc 0, %0, 2" : : "f" (f14));
110         break;
111      case 2:
112         if (BF)
113            __asm__ __volatile__ ("dtstdc 5, %0, 4" : : "f" (f14));
114         else
115            __asm__ __volatile__ ("dtstdc 0, %0, 4" : : "f" (f14));
116         break;
117      case 3:
118         if (BF)
119            __asm__ __volatile__ ("dtstdc 5, %0, 8" : : "f" (f14));
120         else
121            __asm__ __volatile__ ("dtstdc 0, %0, 8" : : "f" (f14));
122         break;
123      case 4:
124         if (BF)
125            __asm__ __volatile__ ("dtstdc 5, %0, 16" : : "f" (f14));
126         else
127            __asm__ __volatile__ ("dtstdc 0, %0, 16" : : "f" (f14));
128         break;
129      case 5:
130         if (BF)
131            __asm__ __volatile__ ("dtstdc 5, %0, 32" : : "f" (f14));
132         else
133            __asm__ __volatile__ ("dtstdc 0, %0, 32" : : "f" (f14));
134         break;
135      default:
136         break;
137   }
138}
139
140static void _test_dtstdcq(int BF, int DCM, dfp_val_t val1, dfp_val_t x1 __attribute__((unused)))
141{
142   _Decimal128 f14 = val1.dec_val128;
143   if (DCM < 0 || DCM > 5 || !(BF == 0 || BF == 5)) {
144      fprintf(stderr, "Invalid inputs to asm test: a=%d, b=%d\n", BF, DCM);
145      return;
146   }
147   switch (DCM) {
148      case 0:
149         if (BF)
150            __asm__ __volatile__ ("dtstdcq 5, %0, 1" : : "f" (f14));
151         else
152            __asm__ __volatile__ ("dtstdcq 0, %0, 1" : : "f" (f14));
153         break;
154      case 1:
155         if (BF)
156            __asm__ __volatile__ ("dtstdcq 5, %0, 2" : : "f" (f14));
157         else
158            __asm__ __volatile__ ("dtstdcq 0, %0, 2" : : "f" (f14));
159         break;
160      case 2:
161         if (BF)
162            __asm__ __volatile__ ("dtstdcq 5, %0, 4" : : "f" (f14));
163         else
164            __asm__ __volatile__ ("dtstdcq 0, %0, 4" : : "f" (f14));
165         break;
166      case 3:
167         if (BF)
168            __asm__ __volatile__ ("dtstdcq 5, %0, 8" : : "f" (f14));
169         else
170            __asm__ __volatile__ ("dtstdcq 0, %0, 8" : : "f" (f14));
171         break;
172      case 4:
173         if (BF)
174            __asm__ __volatile__ ("dtstdcq 5, %0, 16" : : "f" (f14));
175         else
176            __asm__ __volatile__ ("dtstdcq 0, %0, 16" : : "f" (f14));
177         break;
178      case 5:
179         if (BF)
180            __asm__ __volatile__ ("dtstdcq 5, %0, 32" : : "f" (f14));
181         else
182            __asm__ __volatile__ ("dtstdcq 0, %0, 32" : : "f" (f14));
183         break;
184      default:
185         break;
186   }
187}
188
189/* In _test_dtstdg[q], DGM can be one of 6 possible data groups, numbered 0-5.
190 * In reality, DGM is a 6-bit mask field.  We just test the individual values
191 * and assume that masking multiple values would work OK.
192 * BF is the condition register bit field which can range from 0-7.  But for
193 * testing purposes, we only use BF values of '0' and '5'.
194 */
195static void _test_dtstdg(int BF, int DGM, dfp_val_t val1, dfp_val_t x1 __attribute__((unused)))
196{
197   _Decimal64 f14 = val1.dec_val;
198   if (DGM < 0 || DGM > 5 || !(BF == 0 || BF == 5)) {
199      fprintf(stderr, "Invalid inputs to asm test: a=%d, b=%d\n", BF, DGM);
200      return;
201   }
202   switch (DGM) {
203      case 0:
204         if (BF)
205            __asm__ __volatile__ ("dtstdg 5, %0, 1" : : "f" (f14));
206         else
207            __asm__ __volatile__ ("dtstdg 0, %0, 1" : : "f" (f14));
208         break;
209      case 1:
210         if (BF)
211            __asm__ __volatile__ ("dtstdg 5, %0, 2" : : "f" (f14));
212         else
213            __asm__ __volatile__ ("dtstdg 0, %0, 2" : : "f" (f14));
214         break;
215      case 2:
216         if (BF)
217            __asm__ __volatile__ ("dtstdg 5, %0, 4" : : "f" (f14));
218         else
219            __asm__ __volatile__ ("dtstdg 0, %0, 4" : : "f" (f14));
220         break;
221      case 3:
222         if (BF)
223            __asm__ __volatile__ ("dtstdg 5, %0, 8" : : "f" (f14));
224         else
225            __asm__ __volatile__ ("dtstdg 0, %0, 8" : : "f" (f14));
226         break;
227      case 4:
228         if (BF)
229            __asm__ __volatile__ ("dtstdg 5, %0, 16" : : "f" (f14));
230         else
231            __asm__ __volatile__ ("dtstdg 0, %0, 16" : : "f" (f14));
232         break;
233      case 5:
234         if (BF)
235            __asm__ __volatile__ ("dtstdg 5, %0, 32" : : "f" (f14));
236         else
237            __asm__ __volatile__ ("dtstdg 0, %0, 32" : : "f" (f14));
238         break;
239      default:
240         break;
241   }
242}
243
244static void _test_dtstdgq(int BF, int DGM, dfp_val_t val1, dfp_val_t x1 __attribute__((unused)))
245{
246   _Decimal128 f14 = val1.dec_val128;
247   if (DGM < 0 || DGM > 5 || !(BF == 0 || BF == 5)) {
248      fprintf(stderr, "Invalid inputs to asm test: a=%d, b=%d\n", BF, DGM);
249      return;
250   }
251   switch (DGM) {
252      case 0:
253         if (BF)
254            __asm__ __volatile__ ("dtstdgq 5, %0, 1" : : "f" (f14));
255         else
256            __asm__ __volatile__ ("dtstdgq 0, %0, 1" : : "f" (f14));
257         break;
258      case 1:
259         if (BF)
260            __asm__ __volatile__ ("dtstdgq 5, %0, 2" : : "f" (f14));
261         else
262            __asm__ __volatile__ ("dtstdgq 0, %0, 2" : : "f" (f14));
263         break;
264      case 2:
265         if (BF)
266            __asm__ __volatile__ ("dtstdgq 5, %0, 4" : : "f" (f14));
267         else
268            __asm__ __volatile__ ("dtstdgq 0, %0, 4" : : "f" (f14));
269         break;
270      case 3:
271         if (BF)
272            __asm__ __volatile__ ("dtstdgq 5, %0, 8" : : "f" (f14));
273         else
274            __asm__ __volatile__ ("dtstdgq 0, %0, 8" : : "f" (f14));
275         break;
276      case 4:
277         if (BF)
278            __asm__ __volatile__ ("dtstdgq 5, %0, 16" : : "f" (f14));
279         else
280            __asm__ __volatile__ ("dtstdgq 0, %0, 16" : : "f" (f14));
281         break;
282      case 5:
283         if (BF)
284            __asm__ __volatile__ ("dtstdgq 5, %0, 32" : : "f" (f14));
285         else
286            __asm__ __volatile__ ("dtstdgq 0, %0, 32" : : "f" (f14));
287         break;
288      default:
289         break;
290   }
291}
292
293/* In _test_dtstex[q], BF is the condition register bit field indicating the
294 * CR field in which the result of the test should be placed.  BF can range
295 * from 0-7, but for testing purposes, we only use BF values of '4' and '7'.
296 */
297static void
298_test_dtstex(int BF, int x __attribute__((unused)), dfp_val_t val1, dfp_val_t val2)
299{
300   _Decimal64 f14 = val1.dec_val;
301   _Decimal64 f16 = val2.dec_val;
302   if (!(BF == 4 || BF == 7)) {
303      fprintf(stderr, "Invalid input to asm test: a=%d\n", BF);
304      return;
305   }
306   switch (BF) {
307      case 4:
308         __asm__ __volatile__ ("dtstex  4, %0, %1" :  : "f" (f14),"f" (f16));
309         break;
310      case 7:
311         __asm__ __volatile__ ("dtstex  7, %0, %1" :  : "f" (f14),"f" (f16));
312         break;
313      default:
314         break;
315   }
316}
317
318static void _test_dtstexq(int BF, int x __attribute__((unused)), dfp_val_t val1, dfp_val_t val2)
319{
320   _Decimal128 f14 = val1.dec_val128;
321   _Decimal128 f16 = val2.dec_val128;
322   if (!(BF == 4 || BF == 7)) {
323      fprintf(stderr, "Invalid input to asm test: a=%d\n", BF);
324      return;
325   }
326   switch (BF) {
327      case 4:
328         __asm__ __volatile__ ("dtstexq  4, %0, %1" :  : "f" (f14),"f" (f16));
329         break;
330      case 7:
331         __asm__ __volatile__ ("dtstexq  7, %0, %1" :  : "f" (f14),"f" (f16));
332         break;
333      default:
334         break;
335   }
336}
337
338
339
340typedef void (*test_func_t)(int a, int b,  dfp_val_t val1,  dfp_val_t val2);
341typedef void (*test_driver_func_t)(void);
342typedef struct test_table
343{
344   test_driver_func_t test_category;
345   char * name;
346} test_table_t;
347
348/*
349 *  345.0DD (0x2207c00000000000 0xe50)
350 *  1.2300e+5DD (0x2207c00000000000 0x14c000)
351 *  -16.0DD (0xa207c00000000000 0xe0)
352 *  0.00189DD (0x2206c00000000000 0xcf)
353 *  -4.1235DD (0xa205c00000000000 0x10a395bcf)
354 *  9.8399e+20DD (0x2209400000000000 0x253f1f534acdd4)
355 *  0DD (0x2208000000000000 0x0)
356 *  0DD (0x2208000000000000 0x0)
357 *  infDD (0x7800000000000000 0x0)
358 *  nanDD (0x7c00000000000000 0x0
359 */
360static unsigned long long dfp128_vals[] = {
361                                    // Some finite numbers
362                                    0x2207c00000000000ULL, 0x0000000000000e50ULL,
363                                    0x2207c00000000000ULL, 0x000000000014c000ULL,
364                                    0xa207c00000000000ULL, 0x00000000000000e0ULL,
365                                    0x2206c00000000000ULL, 0x00000000000000cfULL,
366                                    0xa205c00000000000ULL, 0x000000010a395bcfULL,
367                                    0x6209400000fd0000ULL, 0x00253f1f534acdd4ULL, // huge number
368                                    0x000400000089b000ULL, 0x0a6000d000000049ULL, // very small number
369                                    // flavors of zero
370                                    0x2208000000000000ULL, 0x0000000000000000ULL,
371                                    0xa208000000000000ULL, 0x0000000000000000ULL, // negative
372                                    0xa248000000000000ULL, 0x0000000000000000ULL,
373                                    // flavors of NAN
374                                    0x7c00000000000000ULL, 0x0000000000000000ULL, // quiet
375                                    0xfc00000000000000ULL, 0xc00100035b007700ULL,
376                                    0x7e00000000000000ULL, 0xfe000000d0e0a0d0ULL, // signaling
377                                    // flavors of Infinity
378                                    0x7800000000000000ULL, 0x0000000000000000ULL,
379                                    0xf800000000000000ULL, 0x0000000000000000ULL, // negative
380                                    0xf900000000000000ULL, 0x0000000000000000ULL
381};
382
383static unsigned long long dfp64_vals[] = {
384                                 // various finite numbers
385                                 0x2234000000000e50ULL,
386                                 0x223400000014c000ULL,
387                                 0xa2340000000000e0ULL,// negative
388                                 0x22240000000000cfULL,
389                                 0xa21400010a395bcfULL,// negative
390                                 0x6e4d3f1f534acdd4ULL,// huge number
391                                 0x000400000089b000ULL,// very small number
392                                 // flavors of zero
393                                 0x2238000000000000ULL,
394                                 0xa238000000000000ULL,
395                                 0x4248000000000000ULL,
396                                 // flavors of NAN
397                                 0x7e34000000000111ULL,
398                                 0xfe000000d0e0a0d0ULL,//signaling
399                                 0xfc00000000000000ULL,//quiet
400                                 // flavors of Infinity
401                                 0x7800000000000000ULL,
402                                 0xf800000000000000ULL,//negative
403                                 0x7a34000000000000ULL,
404};
405
406// Both Long and Quad arrays of DFP values should have the same length, so it
407// doesn't matter which array I use for calculating the following #define.
408#define NUM_DFP_VALS (sizeof(dfp64_vals)/8)
409
410typedef struct dfp_test_args {
411   int fra_idx;
412   int frb_idx;
413} dfp_test_args_t;
414
415
416// Index pairs from dfp64_vals array to be used with dfp_two_arg_tests
417static dfp_test_args_t dfp_2args_x1[] = {
418                                    {0, 1},
419                                    {2, 1},
420                                    {4, 3},
421                                    {6, 0},
422                                    {2, 4},
423                                    {5, 1},
424                                    {5, 2},
425                                    {7, 1},
426                                    {7, 2},
427                                    {8, 0},
428                                    {8, 1},
429                                    {8, 2},
430                                    {7, 8},
431                                    {12, 14},
432                                    {12, 1},
433                                    {12, 13},
434                                    {12, 12},
435                                    {12, 11},
436                                    {11, 14},
437                                    {11, 0},
438                                    {11, 13},
439                                    {11, 11},
440                                    {14, 14},
441                                    {14, 3},
442                                    {14, 15},
443};
444
445typedef enum {
446   LONG_TEST,
447   QUAD_TEST
448} precision_type_t;
449
450typedef struct dfp_test
451{
452   test_func_t test_func;
453   const char * name;
454   dfp_test_args_t * targs;
455   int num_tests;
456   precision_type_t precision;
457   const char * op;
458} dfp_test_t;
459
460typedef struct dfp_one_arg_test
461{
462   test_func_t test_func;
463   const char * name;
464   precision_type_t precision;
465   const char * op;
466} dfp_one_arg_test_t;
467
468
469
470static dfp_one_arg_test_t
471dfp_ClassAndGroupTest_tests[] = {
472                                 { &_test_dtstdc,  "dtstdc", LONG_TEST, "[tCls]"},
473                                 { &_test_dtstdcq, "dtstdcq", QUAD_TEST, "[tCls]"},
474                                 { &_test_dtstdg,  "dtstdg", LONG_TEST, "[tGrp]"},
475                                 { &_test_dtstdgq, "dtstdgq", QUAD_TEST, "[tGrp]"},
476                                 { NULL, NULL, 0, NULL}
477};
478
479static void test_dfp_ClassAndGroupTest_ops(void)
480{
481   test_func_t func;
482   dfp_val_t test_val, dummy;
483
484   int k = 0;
485
486   while ((func = dfp_ClassAndGroupTest_tests[k].test_func)) {
487      int i;
488      dfp_one_arg_test_t test_def = dfp_ClassAndGroupTest_tests[k];
489
490      for (i = 0; i < NUM_DFP_VALS; i++) {
491         int data_class_OR_group, BF = 0;
492         Bool repeat = True;
493
494         if (test_def.precision == LONG_TEST) {
495            test_val.u64_val = dfp64_vals[i];
496         } else {
497            test_val.u128.valu = dfp128_vals[i * 2];
498            test_val.u64_val = test_val.u128.valu;
499            test_val.u128.vall = dfp128_vals[(i * 2) + 1];
500         }
501
502again:
503         for (data_class_OR_group = 0; data_class_OR_group < 6; data_class_OR_group++) {
504            unsigned int condreg;
505            unsigned int flags;
506            SET_FPSCR_ZERO;
507            SET_CR_XER_ZERO;
508            (*func)(BF, data_class_OR_group, test_val, dummy);
509            GET_CR(flags);
510
511            condreg = ((flags >> (4 * (7-BF)))) & 0xf;
512            printf("%s (DC/DG=%d) %s%016llx", test_def.name, data_class_OR_group,
513                   test_def.op, test_val.u64_val);
514            if (test_def.precision == QUAD_TEST) {
515               printf(" %016llx", test_val.u128.vall);
516            }
517            printf(" => %x (BF=%d)\n", condreg, BF);
518         }
519         if (repeat) {
520            repeat = False;
521            BF = 5;
522            goto again;
523         }
524      }
525      k++;
526      printf( "\n" );
527   }
528}
529
530
531static dfp_test_t
532dfp_ExpTest_tests[] = {
533                   { &_test_dtstex, "dtstex", dfp_2args_x1, 25, LONG_TEST, "[tExp]"},
534                   { &_test_dtstexq, "dtstexq", dfp_2args_x1, 25, QUAD_TEST, "[tExp]"},
535                   { NULL, NULL, NULL, 0, 0, NULL}
536};
537
538
539static void test_dfp_ExpTest_ops(void)
540{
541   dfp_val_t test_val1, test_val2;
542   test_func_t func;
543   int k = 0;
544
545   while ((func = dfp_ExpTest_tests[k].test_func)) {
546      /* BF is a 3-bit instruction field that indicates the CR field in which the
547       * result of the test should be placed.  We won't iterate through all
548       * 8 possible BF values since storing compare results to a given field is
549       * a well-tested mechanism in VEX.  But we will test two BF values, just as
550       * a sniff-test.
551       */
552      int i, repeat = 1, BF = 4;
553      dfp_test_t test_def = dfp_ExpTest_tests[k];
554
555again:
556      for (i = 0; i < test_def.num_tests; i++) {
557         unsigned int condreg;
558         unsigned int flags;
559
560         if (test_def.precision == LONG_TEST) {
561            test_val1.u64_val = dfp64_vals[test_def.targs[i].fra_idx];
562            test_val2.u64_val  = dfp64_vals[test_def.targs[i].frb_idx];
563         } else {
564            test_val1.u128.valu = dfp128_vals[test_def.targs[i].fra_idx * 2];
565            test_val1.u64_val = test_val1.u128.valu;
566            test_val1.u128.vall = dfp128_vals[(test_def.targs[i].fra_idx * 2) + 1];
567            test_val2.u128.valu = dfp128_vals[test_def.targs[i].frb_idx * 2];
568            test_val2.u64_val = test_val2.u128.valu;
569            test_val2.u128.vall = dfp128_vals[(test_def.targs[i].frb_idx * 2) + 1];
570         }
571
572         SET_FPSCR_ZERO;
573         SET_CR_XER_ZERO;
574         (*func)(BF, 0, test_val1, test_val2);
575         GET_CR(flags);
576
577         condreg = ((flags >> (4 * (7-BF)))) & 0xf;
578         printf("%s %016llx", test_def.name, test_val1.u64_val);
579         if (test_def.precision == LONG_TEST) {
580            printf(" %s %016llx ",
581                   test_def.op, test_val2.u64_val);
582         } else {
583            printf(" %016llx %s %016llx %016llx ",
584                   test_val1.u128.vall, test_def.op, test_val2.u128.valu, test_val2.u128.vall);
585         }
586         printf(" => %x (BF=%d)\n", condreg, BF);
587      }
588      if (repeat) {
589         repeat = 0;
590         BF = 7;
591         goto again;
592      }
593      k++;
594      printf( "\n" );
595   }
596}
597
598
599static test_table_t
600         all_tests[] =
601{
602                    { &test_dfp_ExpTest_ops,
603                      "Test DFP exponent test instructions"},
604                    { &test_dfp_ClassAndGroupTest_ops,
605                      "Test DFP class and group test instructions"},
606                    { NULL, NULL }
607};
608#endif // HAS_DFP
609
610int main() {
611#if defined(HAS_DFP)
612
613   test_table_t aTest;
614   test_driver_func_t func;
615   int i = 0;
616
617   while ((func = all_tests[i].test_category)) {
618      aTest = all_tests[i];
619      printf( "%s\n", aTest.name );
620      (*func)();
621      i++;
622   }
623
624#endif // HAS_DFP
625   return 0;
626}
627