1 /* Copyright (C) 2012 IBM 2 3 Author: Maynard Johnson <maynardj (at) 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; 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 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 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 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 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 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 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 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 147 static void _test_mffs (void) 148 { 149 __asm__ __volatile__ ("mffs %0" : "=f"(f14)); 150 } 151 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 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 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 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 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