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 typedef 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
40 typedef 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 */
_test_dtstdc(int BF,int DCM,dfp_val_t val1,dfp_val_t x1)91 static 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
_test_dtstdcq(int BF,int DCM,dfp_val_t val1,dfp_val_t x1)140 static 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 */
_test_dtstdg(int BF,int DGM,dfp_val_t val1,dfp_val_t x1)195 static 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
_test_dtstdgq(int BF,int DGM,dfp_val_t val1,dfp_val_t x1)244 static 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 */
297 static void
_test_dtstex(int BF,int x,dfp_val_t val1,dfp_val_t val2)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
_test_dtstexq(int BF,int x,dfp_val_t val1,dfp_val_t val2)318 static 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
340 typedef void (*test_func_t)(int a, int b, dfp_val_t val1, dfp_val_t val2);
341 typedef void (*test_driver_func_t)(void);
342 typedef 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 */
360 static 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
383 static 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
410 typedef 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
417 static 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
445 typedef enum {
446 LONG_TEST,
447 QUAD_TEST
448 } precision_type_t;
449
450 typedef 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
460 typedef 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
470 static dfp_one_arg_test_t
471 dfp_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
test_dfp_ClassAndGroupTest_ops(void)479 static 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
502 again:
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
531 static dfp_test_t
532 dfp_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
test_dfp_ExpTest_ops(void)539 static 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
555 again:
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
599 static 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
main()610 int 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