mirror of
https://github.com/ioacademy-jikim/debugging
synced 2025-06-18 13:26:14 +00:00
505 lines
16 KiB
C
505 lines
16 KiB
C
/* Copyright (C) 2012 IBM
|
|
|
|
Author: Maynard Johnson <maynardj@us.ibm.com>
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
02111-1307, USA.
|
|
|
|
The GNU General Public License is contained in the file COPYING.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
|
|
#if defined(HAS_DFP)
|
|
|
|
register double f14 __asm__ ("fr14");
|
|
register double f15 __asm__ ("fr15");
|
|
register double f16 __asm__ ("fr16");
|
|
register double f17 __asm__ ("fr17");
|
|
register double f18 __asm__ ("fr18");
|
|
register double f19 __asm__ ("fr19");
|
|
|
|
|
|
typedef unsigned char Bool;
|
|
#define True 1
|
|
#define False 0
|
|
|
|
|
|
#define ALLCR "cr0","cr1","cr2","cr3","cr4","cr5","cr6","cr7"
|
|
|
|
#define SET_CR(_arg) \
|
|
__asm__ __volatile__ ("mtcr %0" : : "b"(_arg) : ALLCR );
|
|
|
|
#define SET_XER(_arg) \
|
|
__asm__ __volatile__ ("mtxer %0" : : "b"(_arg) : "xer" );
|
|
|
|
#define GET_CR(_lval) \
|
|
__asm__ __volatile__ ("mfcr %0" : "=b"(_lval) )
|
|
|
|
#define GET_XER(_lval) \
|
|
__asm__ __volatile__ ("mfxer %0" : "=b"(_lval) )
|
|
|
|
#define GET_CR_XER(_lval_cr,_lval_xer) \
|
|
do { GET_CR(_lval_cr); GET_XER(_lval_xer); } while (0)
|
|
|
|
#define SET_CR_ZERO \
|
|
SET_CR(0)
|
|
|
|
#define SET_XER_ZERO \
|
|
SET_XER(0)
|
|
|
|
#define SET_CR_XER_ZERO \
|
|
do { SET_CR_ZERO; SET_XER_ZERO; } while (0)
|
|
|
|
#define SET_FPSCR_ZERO \
|
|
do { double _d = 0.0; \
|
|
__asm__ __volatile__ ("mtfsf 0xFF, %0" : : "f"(_d) ); \
|
|
} while (0)
|
|
|
|
#define GET_FPSCR(_arg) \
|
|
__asm__ __volatile__ ("mffs %0" : "=f"(_arg) )
|
|
|
|
#define SET_FPSCR_DRN \
|
|
__asm__ __volatile__ ("mtfsf 1, %0, 0, 1" : : "f"(f14) )
|
|
|
|
|
|
// The assembly-level instructions being tested
|
|
static Bool do_dot;
|
|
static void _test_dadd (void)
|
|
{
|
|
if (do_dot)
|
|
__asm__ __volatile__ ("dadd. %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
|
|
else
|
|
__asm__ __volatile__ ("dadd %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
|
|
}
|
|
|
|
static void _test_dsub (void)
|
|
{
|
|
if (do_dot)
|
|
__asm__ __volatile__ ("dsub. %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
|
|
else
|
|
__asm__ __volatile__ ("dsub %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
|
|
}
|
|
|
|
static void _test_dmul (void)
|
|
{
|
|
if (do_dot)
|
|
__asm__ __volatile__ ("dmul. %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
|
|
else
|
|
__asm__ __volatile__ ("dmul %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
|
|
}
|
|
|
|
static void _test_ddiv (void)
|
|
{
|
|
if (do_dot)
|
|
__asm__ __volatile__ ("ddiv. %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
|
|
else
|
|
__asm__ __volatile__ ("ddiv %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
|
|
}
|
|
|
|
// Quad DFP arith instructions
|
|
static void _test_daddq (void)
|
|
{
|
|
if (do_dot)
|
|
__asm__ __volatile__ ("daddq. %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
|
|
else
|
|
__asm__ __volatile__ ("daddq %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
|
|
}
|
|
|
|
static void _test_dsubq (void)
|
|
{
|
|
if (do_dot)
|
|
__asm__ __volatile__ ("dsubq. %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
|
|
else
|
|
__asm__ __volatile__ ("dsubq %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
|
|
}
|
|
|
|
static void _test_dmulq (void)
|
|
{
|
|
if (do_dot)
|
|
__asm__ __volatile__ ("dmulq. %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
|
|
else
|
|
__asm__ __volatile__ ("dmulq %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
|
|
}
|
|
|
|
static void _test_ddivq (void)
|
|
{
|
|
if (do_dot)
|
|
__asm__ __volatile__ ("ddivq. %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
|
|
else
|
|
__asm__ __volatile__ ("ddivq %0, %1, %2" : "=f" (f18) : "f" (f14),"f" (f16));
|
|
}
|
|
|
|
static void _test_mffs (void)
|
|
{
|
|
__asm__ __volatile__ ("mffs %0" : "=f"(f14));
|
|
}
|
|
|
|
static void _test_mtfsf (int upper)
|
|
{
|
|
if (upper)
|
|
__asm__ __volatile__ ("mtfsf 1, %0, 0, 1" : : "f"(f14) );
|
|
else
|
|
__asm__ __volatile__ ("mtfsf 1, %0, 0, 0" : : "f"(f14) );
|
|
}
|
|
|
|
typedef void (*test_func_t)(void);
|
|
typedef struct test_table
|
|
{
|
|
test_func_t test_category;
|
|
char * name;
|
|
} test_table_t;
|
|
|
|
/*
|
|
* 345.0DD (0x2207c00000000000 0xe50)
|
|
* 1.2300e+5DD (0x2207c00000000000 0x14c000)
|
|
* -16.0DD (0xa207c00000000000 0xe0)
|
|
* 0.00189DD (0x2206c00000000000 0xcf)
|
|
* -4.1235DD (0xa205c00000000000 0x10a395bcf)
|
|
* 9.8399e+20DD (0x2209400000000000 0x253f1f534acdd4)
|
|
* 0DD (0x2208000000000000 0x0)
|
|
* 0DD (0x2208000000000000 0x0)
|
|
* infDD (0x7800000000000000 0x0)
|
|
* nanDD (0x7c00000000000000 0x0
|
|
*/
|
|
static unsigned long long dfp128_vals[] = {
|
|
// Some finite numbers
|
|
0x2207c00000000000ULL, 0x0000000000000e50ULL,
|
|
0x2207c00000000000ULL, 0x000000000014c000ULL,
|
|
0xa207c00000000000ULL, 0x00000000000000e0ULL,
|
|
0x2206c00000000000ULL, 0x00000000000000cfULL,
|
|
0xa205c00000000000ULL, 0x000000010a395bcfULL,
|
|
0x6209400000fd0000ULL, 0x00253f1f534acdd4ULL, // huge number
|
|
0x000400000089b000ULL, 0x0a6000d000000049ULL, // very small number
|
|
// flavors of zero
|
|
0x2208000000000000ULL, 0x0000000000000000ULL,
|
|
0xa208000000000000ULL, 0x0000000000000000ULL, // negative
|
|
0xa248000000000000ULL, 0x0000000000000000ULL,
|
|
// flavors of NAN
|
|
0x7c00000000000000ULL, 0x0000000000000000ULL, // quiet
|
|
0xfc00000000000000ULL, 0xc00100035b007700ULL,
|
|
0x7e00000000000000ULL, 0xfe000000d0e0a0d0ULL, // signaling
|
|
// flavors of Infinity
|
|
0x7800000000000000ULL, 0x0000000000000000ULL,
|
|
0xf800000000000000ULL, 0x0000000000000000ULL, // negative
|
|
0xf900000000000000ULL, 0x0000000000000000ULL
|
|
};
|
|
|
|
static unsigned long long dfp64_vals[] = {
|
|
// various finite numbers
|
|
0x2234000000000e50ULL,
|
|
0x223400000014c000ULL,
|
|
0xa2340000000000e0ULL,// negative
|
|
0x22240000000000cfULL,
|
|
0xa21400010a395bcfULL,// negative
|
|
0x6e4d3f1f534acdd4ULL,// huge number
|
|
0x000400000089b000ULL,// very small number
|
|
// flavors of zero
|
|
0x2238000000000000ULL,
|
|
0xa238000000000000ULL,
|
|
0x4248000000000000ULL,
|
|
// flavors of NAN
|
|
0x7e34000000000111ULL,
|
|
0xfe000000d0e0a0d0ULL,//signaling
|
|
0xfc00000000000000ULL,//quiet
|
|
// flavors of Infinity
|
|
0x7800000000000000ULL,
|
|
0xf800000000000000ULL,//negative
|
|
0x7a34000000000000ULL,
|
|
};
|
|
|
|
|
|
typedef struct dfp_test_args {
|
|
int fra_idx;
|
|
int frb_idx;
|
|
} dfp_test_args_t;
|
|
|
|
|
|
// Index pairs from dfp64_vals or dfp128_vals array to be used with dfp_two_arg_tests
|
|
static dfp_test_args_t dfp_2args_x2[] = {
|
|
{0, 1},
|
|
{2, 1},
|
|
{3, 4},
|
|
{0, 6},
|
|
{2, 4},
|
|
{5, 1},
|
|
{5, 2},
|
|
{7, 8},
|
|
{7, 1},
|
|
{9, 15},
|
|
{8, 12},
|
|
{7, 11},
|
|
{13, 2},
|
|
{13, 14},
|
|
{15, 12},
|
|
{14, 11},
|
|
{12, 12},
|
|
{12, 11},
|
|
{11, 11}
|
|
};
|
|
|
|
// Index pairs from dfp64_vals array to be used with dfp_two_arg_tests
|
|
static dfp_test_args_t dfp_2args_x1[] = {
|
|
{0, 1},
|
|
{2, 1},
|
|
{3, 4},
|
|
{0, 6},
|
|
{2, 4},
|
|
{5, 1},
|
|
{5, 2},
|
|
{7, 1},
|
|
{7, 2},
|
|
{8, 0},
|
|
{8, 1},
|
|
{8, 2},
|
|
{7, 8},
|
|
{12, 14},
|
|
{12, 1},
|
|
{12, 13},
|
|
{12, 12},
|
|
{12, 11},
|
|
{11, 14},
|
|
{11, 0},
|
|
{11, 13},
|
|
{11, 11},
|
|
{14, 14},
|
|
{14, 3},
|
|
{14, 15},
|
|
};
|
|
|
|
typedef enum {
|
|
LONG_TEST,
|
|
QUAD_TEST
|
|
} precision_type_t;
|
|
|
|
typedef struct dfp_test
|
|
{
|
|
test_func_t test_func;
|
|
const char * name;
|
|
dfp_test_args_t * targs;
|
|
int num_tests;
|
|
precision_type_t precision;
|
|
const char * op;
|
|
Bool cr_supported;
|
|
} dfp_test_t;
|
|
|
|
|
|
static dfp_test_t
|
|
dfp_two_arg_tests[] = {
|
|
{ &_test_dadd, "dadd", dfp_2args_x1, 25, LONG_TEST, "+", False},
|
|
{ &_test_dsub, "dsub", dfp_2args_x1, 25, LONG_TEST, "-", False},
|
|
{ &_test_dmul, "dmul", dfp_2args_x2, 19, LONG_TEST, "*", False},
|
|
{ &_test_ddiv, "ddiv", dfp_2args_x2, 19, LONG_TEST, "/", False},
|
|
{ &_test_daddq, "daddq", dfp_2args_x1, 25, QUAD_TEST, "+", False},
|
|
{ &_test_dsubq, "dsubq", dfp_2args_x1, 25, QUAD_TEST, "-", False},
|
|
{ &_test_dmulq, "dmulq", dfp_2args_x2, 19, QUAD_TEST, "*", False},
|
|
{ &_test_ddivq, "ddivq", dfp_2args_x2, 19, QUAD_TEST, "/", False},
|
|
{ NULL, NULL, NULL, 0, 0, NULL}
|
|
};
|
|
|
|
static void test_dfp_two_arg_ops(void)
|
|
{
|
|
test_func_t func;
|
|
unsigned long long u0, u0x, u1, u1x;
|
|
double res, d0, d1, *d0p, *d1p;
|
|
double d0x, d1x, *d0xp, *d1xp;
|
|
int k = 0;
|
|
u0x = u1x = 0;
|
|
d0p = &d0;
|
|
d0xp = &d0x;
|
|
d1p = &d1;
|
|
d1xp = &d1x;
|
|
|
|
while ((func = dfp_two_arg_tests[k].test_func)) {
|
|
int i, repeat = 1;
|
|
dfp_test_t test_group = dfp_two_arg_tests[k];
|
|
do_dot = False;
|
|
|
|
again:
|
|
for (i = 0; i < test_group.num_tests; i++) {
|
|
unsigned int condreg;
|
|
unsigned int flags;
|
|
|
|
if (test_group.precision == LONG_TEST) {
|
|
u0 = dfp64_vals[test_group.targs[i].fra_idx];
|
|
u1 = dfp64_vals[test_group.targs[i].frb_idx];
|
|
} else {
|
|
u0 = dfp128_vals[test_group.targs[i].fra_idx * 2];
|
|
u0x = dfp128_vals[(test_group.targs[i].fra_idx * 2) + 1];
|
|
u1 = dfp128_vals[test_group.targs[i].frb_idx * 2];
|
|
u1x = dfp128_vals[(test_group.targs[i].frb_idx * 2) + 1];
|
|
}
|
|
*(unsigned long long *)d0p = u0;
|
|
*(unsigned long long *)d1p = u1;
|
|
f14 = d0;
|
|
f16 = d1;
|
|
if (test_group.precision == QUAD_TEST) {
|
|
*(unsigned long long *)d0xp = u0x;
|
|
*(unsigned long long *)d1xp = u1x;
|
|
f15 = d0x;
|
|
f17 = d1x;
|
|
}
|
|
|
|
SET_FPSCR_ZERO;
|
|
SET_CR_XER_ZERO;
|
|
(*func)();
|
|
GET_CR(flags);
|
|
res = f18;
|
|
|
|
condreg = (flags & 0x000000f0) >> 4;
|
|
printf("%s%s %016llx", test_group.name, do_dot? "." : "", u0);
|
|
if (test_group.precision == LONG_TEST) {
|
|
printf(" %s %016llx => %016llx",
|
|
test_group.op, u1, *((unsigned long long *)(&res)));
|
|
} else {
|
|
double resx = f19;
|
|
printf(" %016llx %s %016llx %016llx ==> %016llx %016llx",
|
|
u0x, test_group.op, u1, u1x,
|
|
*((unsigned long long *)(&res)), *((unsigned long long *)(&resx)));
|
|
}
|
|
if (test_group.cr_supported)
|
|
printf(" (cr = %08x)\n", condreg);
|
|
else
|
|
printf("\n");
|
|
|
|
}
|
|
printf("\n");
|
|
if (repeat) {
|
|
repeat = 0;
|
|
do_dot = True;
|
|
goto again;
|
|
}
|
|
k++;
|
|
printf( "\n" );
|
|
}
|
|
}
|
|
|
|
void test_move_toFrom_fpscr(void)
|
|
{
|
|
#define BFP_MAX_RM 3
|
|
int shift = 0;
|
|
unsigned long long i, max_rm, expected_val;
|
|
double fpscr_in, fpscr_out;
|
|
unsigned long long * hex_fpscr_in = (unsigned long long *)&fpscr_in;
|
|
unsigned long long * hex_fpscr_out = (unsigned long long *)&fpscr_out;
|
|
|
|
|
|
max_rm = 4;
|
|
again:
|
|
/* NOTE: The first time through this loop is for setting the binary
|
|
* floating point rounding mode (bits 62:63 of FPSCR). The second time
|
|
* through is for setting the decimal floating point rounding mode
|
|
* (bits 29:31 of FPSCR). In the second time through this loop, the value
|
|
* returned should include the final binary FP rounding mode, along with
|
|
* the decimal FP rounding modes.
|
|
*/
|
|
for (i = 0; i < max_rm; i++) {
|
|
*hex_fpscr_in = (i << shift);
|
|
f14 = fpscr_in;
|
|
_test_mtfsf(max_rm/8);
|
|
*hex_fpscr_in = 0ULL;
|
|
f14= fpscr_in;
|
|
_test_mffs();
|
|
fpscr_out = f14;
|
|
if (max_rm == 4) {
|
|
*hex_fpscr_out &= (max_rm - 1) << shift;
|
|
expected_val = i << shift;
|
|
} else {
|
|
*hex_fpscr_out &= BFP_MAX_RM | ((max_rm - 1) << shift);
|
|
expected_val = (i << shift) | BFP_MAX_RM;
|
|
}
|
|
|
|
printf("FPSCR %s floating point rounding mode %016llx == %016llx? %s\n",
|
|
(max_rm == 8) ? "decimal" : "binary",
|
|
*hex_fpscr_out, expected_val,
|
|
(expected_val == *hex_fpscr_out) ? "yes" : "no");
|
|
}
|
|
if (max_rm == 4) {
|
|
max_rm = 8;
|
|
shift = 32;
|
|
goto again;
|
|
}
|
|
}
|
|
|
|
void test_rounding_modes(void)
|
|
{
|
|
int j;
|
|
unsigned long long u0, u1, rm_idx;
|
|
double res, d0, d1, *d0p, *d1p, fpscr;
|
|
unsigned long long * hex_fpscr = (unsigned long long *)&fpscr;
|
|
u0 = 0x26cc3f1f534acdd4ULL;
|
|
u1 = 0x27feff197a42ba06ULL;
|
|
d0p = &d0;
|
|
d1p = &d1;
|
|
|
|
for (j = 0; j < 12; j++) {
|
|
for (rm_idx = 0; rm_idx < 8; rm_idx++) {
|
|
*hex_fpscr = 0ULL;
|
|
__asm__ __volatile__ ("mffs %0" : "=f"(f14));
|
|
fpscr = f14;
|
|
*hex_fpscr &= 0xFFFFFFF0FFFFFFFFULL;
|
|
*hex_fpscr |= (rm_idx << 32);
|
|
f14 = fpscr;
|
|
SET_FPSCR_DRN;
|
|
*(unsigned long long *)d0p = u0;
|
|
*(unsigned long long *)d1p = u1;
|
|
f14 = d0;
|
|
f16 = d1;
|
|
_test_dmul();
|
|
res = f18;
|
|
printf("test #%d: dmul with rounding mode %d: %016llx * %016llx => %016llx\n",
|
|
j, (int)rm_idx, u0, u1, *((unsigned long long *)(&res)));
|
|
printf("\n");
|
|
}
|
|
// Changing the least significant bit of one of the dmul arguments give us more
|
|
// opportunities for different rounding modes to yield different results which
|
|
// can then be validated.
|
|
u0++;
|
|
}
|
|
}
|
|
|
|
static test_table_t
|
|
all_tests[] =
|
|
{
|
|
{ &test_dfp_two_arg_ops,
|
|
"Test DFP arithmetic instructions"},
|
|
{ &test_rounding_modes,
|
|
"Test DFP rounding modes"},
|
|
{ &test_move_toFrom_fpscr,
|
|
"Test move to/from FPSCR"},
|
|
{ NULL, NULL }
|
|
};
|
|
#endif // HAS_DFP
|
|
|
|
int main() {
|
|
#if defined(HAS_DFP)
|
|
|
|
test_table_t aTest;
|
|
test_func_t func;
|
|
int i = 0;
|
|
|
|
while ((func = all_tests[i].test_category)) {
|
|
aTest = all_tests[i];
|
|
printf( "%s\n", aTest.name );
|
|
(*func)();
|
|
i++;
|
|
}
|
|
|
|
#endif // HAS_DFP
|
|
return 0;
|
|
}
|