#include "defs.h"
#include "doublest.h"
#include "floatformat.h"
#include "gdb_assert.h"
#include "gdb_string.h"
#include "gdbtypes.h"
#include <math.h>
#define FLOATFORMAT_CHAR_BIT 8
#define FLOATFORMAT_LARGEST_BYTES 16
static unsigned long
get_field (const bfd_byte *data, enum floatformat_byteorders order,
unsigned int total_len, unsigned int start, unsigned int len)
{
unsigned long result;
unsigned int cur_byte;
int cur_bitshift;
gdb_assert (order == floatformat_little || order == floatformat_big);
if (order == floatformat_little)
{
int excess = FLOATFORMAT_CHAR_BIT - (total_len % FLOATFORMAT_CHAR_BIT);
cur_byte = (total_len / FLOATFORMAT_CHAR_BIT)
- ((start + len + excess) / FLOATFORMAT_CHAR_BIT);
cur_bitshift = ((start + len + excess) % FLOATFORMAT_CHAR_BIT)
- FLOATFORMAT_CHAR_BIT;
}
else
{
cur_byte = (start + len) / FLOATFORMAT_CHAR_BIT;
cur_bitshift =
((start + len) % FLOATFORMAT_CHAR_BIT) - FLOATFORMAT_CHAR_BIT;
}
if (cur_bitshift > -FLOATFORMAT_CHAR_BIT)
result = *(data + cur_byte) >> (-cur_bitshift);
else
result = 0;
cur_bitshift += FLOATFORMAT_CHAR_BIT;
if (order == floatformat_little)
++cur_byte;
else
--cur_byte;
while (cur_bitshift < len)
{
result |= (unsigned long)*(data + cur_byte) << cur_bitshift;
cur_bitshift += FLOATFORMAT_CHAR_BIT;
switch (order)
{
case floatformat_little:
++cur_byte;
break;
case floatformat_big:
--cur_byte;
break;
}
}
if (len < sizeof(result) * FLOATFORMAT_CHAR_BIT)
result &= ((1UL << len) - 1);
return result;
}
static enum floatformat_byteorders
floatformat_normalize_byteorder (const struct floatformat *fmt,
const void *from, void *to)
{
const unsigned char *swapin;
unsigned char *swapout;
int words;
if (fmt->byteorder == floatformat_little
|| fmt->byteorder == floatformat_big)
return fmt->byteorder;
gdb_assert (fmt->byteorder == floatformat_littlebyte_bigword);
words = fmt->totalsize / FLOATFORMAT_CHAR_BIT;
words >>= 2;
swapout = (unsigned char *)to;
swapin = (const unsigned char *)from;
while (words-- > 0)
{
*swapout++ = swapin[3];
*swapout++ = swapin[2];
*swapout++ = swapin[1];
*swapout++ = swapin[0];
swapin += 4;
}
return floatformat_big;
}
static void
convert_floatformat_to_doublest (const struct floatformat *fmt,
const void *from,
DOUBLEST *to)
{
unsigned char *ufrom = (unsigned char *) from;
DOUBLEST dto;
long exponent;
unsigned long mant;
unsigned int mant_bits, mant_off;
int mant_bits_left;
int special_exponent;
enum floatformat_byteorders order;
unsigned char newfrom[FLOATFORMAT_LARGEST_BYTES];
gdb_assert (fmt->totalsize
<= FLOATFORMAT_LARGEST_BYTES * FLOATFORMAT_CHAR_BIT);
order = floatformat_normalize_byteorder (fmt, ufrom, newfrom);
if (order != fmt->byteorder)
ufrom = newfrom;
exponent = get_field (ufrom, order, fmt->totalsize, fmt->exp_start,
fmt->exp_len);
mant_bits_left = fmt->man_len;
mant_off = fmt->man_start;
dto = 0.0;
special_exponent = exponent == 0 || exponent == fmt->exp_nan;
if (!special_exponent)
exponent -= fmt->exp_bias;
else if (exponent == 0)
exponent = 1 - fmt->exp_bias;
if (!special_exponent)
{
if (fmt->intbit == floatformat_intbit_no)
dto = ldexp (1.0, exponent);
else
exponent++;
}
while (mant_bits_left > 0)
{
mant_bits = min (mant_bits_left, 32);
mant = get_field (ufrom, order, fmt->totalsize, mant_off, mant_bits);
dto += ldexp ((double) mant, exponent - mant_bits);
exponent -= mant_bits;
mant_off += mant_bits;
mant_bits_left -= mant_bits;
}
if (get_field (ufrom, order, fmt->totalsize, fmt->sign_start, 1))
dto = -dto;
*to = dto;
}
static void put_field (unsigned char *, enum floatformat_byteorders,
unsigned int,
unsigned int, unsigned int, unsigned long);
static void
put_field (unsigned char *data, enum floatformat_byteorders order,
unsigned int total_len, unsigned int start, unsigned int len,
unsigned long stuff_to_put)
{
unsigned int cur_byte;
int cur_bitshift;
gdb_assert (order == floatformat_little || order == floatformat_big);
if (order == floatformat_little)
{
int excess = FLOATFORMAT_CHAR_BIT - (total_len % FLOATFORMAT_CHAR_BIT);
cur_byte = (total_len / FLOATFORMAT_CHAR_BIT)
- ((start + len + excess) / FLOATFORMAT_CHAR_BIT);
cur_bitshift = ((start + len + excess) % FLOATFORMAT_CHAR_BIT)
- FLOATFORMAT_CHAR_BIT;
}
else
{
cur_byte = (start + len) / FLOATFORMAT_CHAR_BIT;
cur_bitshift =
((start + len) % FLOATFORMAT_CHAR_BIT) - FLOATFORMAT_CHAR_BIT;
}
if (cur_bitshift > -FLOATFORMAT_CHAR_BIT)
{
*(data + cur_byte) &=
~(((1 << ((start + len) % FLOATFORMAT_CHAR_BIT)) - 1)
<< (-cur_bitshift));
*(data + cur_byte) |=
(stuff_to_put & ((1 << FLOATFORMAT_CHAR_BIT) - 1)) << (-cur_bitshift);
}
cur_bitshift += FLOATFORMAT_CHAR_BIT;
if (order == floatformat_little)
++cur_byte;
else
--cur_byte;
while (cur_bitshift < len)
{
if (len - cur_bitshift < FLOATFORMAT_CHAR_BIT)
{
*(data + cur_byte) &=
~((1 << (len - cur_bitshift)) - 1);
*(data + cur_byte) |= (stuff_to_put >> cur_bitshift);
}
else
*(data + cur_byte) = ((stuff_to_put >> cur_bitshift)
& ((1 << FLOATFORMAT_CHAR_BIT) - 1));
cur_bitshift += FLOATFORMAT_CHAR_BIT;
if (order == floatformat_little)
++cur_byte;
else
--cur_byte;
}
}
#ifdef HAVE_LONG_DOUBLE
static long double ldfrexp (long double value, int *eptr);
static long double
ldfrexp (long double value, int *eptr)
{
long double tmp;
int exp;
if (value < 0.0l)
value = -value;
tmp = 1.0l;
exp = 0;
if (value >= tmp)
while (value >= tmp)
{
tmp *= 2.0l;
exp++;
}
else if (value != 0.0l)
{
while (value < tmp)
{
tmp /= 2.0l;
exp--;
}
tmp *= 2.0l;
exp++;
}
*eptr = exp;
return value / tmp;
}
#endif
static void
convert_doublest_to_floatformat (CONST struct floatformat *fmt,
const DOUBLEST *from,
void *to)
{
DOUBLEST dfrom;
int exponent;
DOUBLEST mant;
unsigned int mant_bits, mant_off;
int mant_bits_left;
unsigned char *uto = (unsigned char *) to;
enum floatformat_byteorders order = fmt->byteorder;
if (order == floatformat_littlebyte_bigword)
order = floatformat_big;
memcpy (&dfrom, from, sizeof (dfrom));
memset (uto, 0, (fmt->totalsize + FLOATFORMAT_CHAR_BIT - 1)
/ FLOATFORMAT_CHAR_BIT);
if (dfrom == 0)
return;
if (dfrom != dfrom)
{
put_field (uto, order, fmt->totalsize, fmt->exp_start,
fmt->exp_len, fmt->exp_nan);
put_field (uto, order, fmt->totalsize, fmt->man_start,
32, 1);
goto finalize_byteorder;
}
if (dfrom < 0)
{
put_field (uto, order, fmt->totalsize, fmt->sign_start, 1, 1);
dfrom = -dfrom;
}
if (dfrom + dfrom == dfrom && dfrom != 0.0)
{
put_field (uto, order, fmt->totalsize, fmt->exp_start,
fmt->exp_len, fmt->exp_nan);
put_field (uto, order, fmt->totalsize, fmt->man_start,
fmt->man_len, 0);
goto finalize_byteorder;
}
#ifdef HAVE_LONG_DOUBLE
mant = ldfrexp (dfrom, &exponent);
#else
mant = frexp (dfrom, &exponent);
#endif
put_field (uto, order, fmt->totalsize, fmt->exp_start, fmt->exp_len,
exponent + fmt->exp_bias - 1);
mant_bits_left = fmt->man_len;
mant_off = fmt->man_start;
while (mant_bits_left > 0)
{
unsigned long mant_long;
mant_bits = mant_bits_left < 32 ? mant_bits_left : 32;
mant *= 4294967296.0;
mant_long = ((unsigned long) mant) & 0xffffffffL;
mant -= mant_long;
if (mant_bits_left == fmt->man_len
&& fmt->intbit == floatformat_intbit_no)
{
mant_long <<= 1;
mant_long &= 0xffffffffL;
if (mant_bits == 32)
mant_bits -= 1;
}
if (mant_bits < 32)
{
mant_long >>= 32 - mant_bits;
}
put_field (uto, order, fmt->totalsize,
mant_off, mant_bits, mant_long);
mant_off += mant_bits;
mant_bits_left -= mant_bits;
}
finalize_byteorder:
if (order != fmt->byteorder)
{
int words;
unsigned char *curword = uto;
unsigned char tmp;
words = fmt->totalsize / FLOATFORMAT_CHAR_BIT;
words >>= 2;
while (words-- > 0)
{
tmp = curword[0];
curword[0] = curword[3];
curword[3] = tmp;
tmp = curword[1];
curword[1] = curword[2];
curword[2] = tmp;
curword += 4;
}
}
}
int
floatformat_is_negative (const struct floatformat *fmt,
const bfd_byte *uval)
{
enum floatformat_byteorders order;
unsigned char newfrom[FLOATFORMAT_LARGEST_BYTES];
gdb_assert (fmt != NULL);
gdb_assert (fmt->totalsize
<= FLOATFORMAT_LARGEST_BYTES * FLOATFORMAT_CHAR_BIT);
order = floatformat_normalize_byteorder (fmt, uval, newfrom);
if (order != fmt->byteorder)
uval = newfrom;
return get_field (uval, order, fmt->totalsize, fmt->sign_start, 1);
}
int
floatformat_is_nan (const struct floatformat *fmt,
const bfd_byte *uval)
{
long exponent;
unsigned long mant;
unsigned int mant_bits, mant_off;
int mant_bits_left;
enum floatformat_byteorders order;
unsigned char newfrom[FLOATFORMAT_LARGEST_BYTES];
gdb_assert (fmt != NULL);
gdb_assert (fmt->totalsize
<= FLOATFORMAT_LARGEST_BYTES * FLOATFORMAT_CHAR_BIT);
order = floatformat_normalize_byteorder (fmt, uval, newfrom);
if (order != fmt->byteorder)
uval = newfrom;
if (! fmt->exp_nan)
return 0;
exponent = get_field (uval, order, fmt->totalsize, fmt->exp_start,
fmt->exp_len);
if (exponent != fmt->exp_nan)
return 0;
mant_bits_left = fmt->man_len;
mant_off = fmt->man_start;
while (mant_bits_left > 0)
{
mant_bits = min (mant_bits_left, 32);
mant = get_field (uval, order, fmt->totalsize, mant_off, mant_bits);
if (mant_off == fmt->man_start
&& fmt->intbit == floatformat_intbit_yes)
mant &= ~(1 << (mant_bits - 1));
if (mant)
return 1;
mant_off += mant_bits;
mant_bits_left -= mant_bits;
}
return 0;
}
const char *
floatformat_mantissa (const struct floatformat *fmt,
const bfd_byte *val)
{
unsigned char *uval = (unsigned char *) val;
unsigned long mant;
unsigned int mant_bits, mant_off;
int mant_bits_left;
static char res[50];
char buf[9];
enum floatformat_byteorders order;
unsigned char newfrom[FLOATFORMAT_LARGEST_BYTES];
gdb_assert (fmt != NULL);
gdb_assert (fmt->totalsize
<= FLOATFORMAT_LARGEST_BYTES * FLOATFORMAT_CHAR_BIT);
order = floatformat_normalize_byteorder (fmt, uval, newfrom);
if (order != fmt->byteorder)
uval = newfrom;
if (! fmt->exp_nan)
return 0;
gdb_assert (sizeof res > ((fmt->man_len + 7) / 8) * 2);
mant_off = fmt->man_start;
mant_bits_left = fmt->man_len;
mant_bits = (mant_bits_left % 32) > 0 ? mant_bits_left % 32 : 32;
mant = get_field (uval, order, fmt->totalsize, mant_off, mant_bits);
sprintf (res, "%lx", mant);
mant_off += mant_bits;
mant_bits_left -= mant_bits;
while (mant_bits_left > 0)
{
mant = get_field (uval, order, fmt->totalsize, mant_off, 32);
sprintf (buf, "%08lx", mant);
strcat (res, buf);
mant_off += 32;
mant_bits_left -= 32;
}
return res;
}
static int
floatformat_byteorders_are_reversed (enum floatformat_byteorders a,
enum floatformat_byteorders b)
{
if (a == floatformat_little && b == floatformat_big)
return 1;
if (a == floatformat_big && b == floatformat_little)
return 1;
return 0;
}
static int
floatformats_same_except_for_byteorder (const struct floatformat *a,
const struct floatformat *b)
{
if (a->totalsize != b->totalsize)
return 0;
if (a->sign_start != b->sign_start)
return 0;
if (a->exp_start != b->exp_start)
return 0;
if (a->exp_len != b->exp_len)
return 0;
if (a->exp_bias != b->exp_bias)
return 0;
if (a->exp_nan != b->exp_nan)
return 0;
if (a->man_start != b->man_start)
return 0;
if (a->man_len != b->man_len)
return 0;
if (a->intbit != b->intbit)
return 0;
return 1;
}
static void
swap_bytes (gdb_byte *from, gdb_byte *to, int len)
{
int i;
for (i = 0; i < len; i++)
to[i] = from[len - i - 1];
}
static const struct floatformat *host_float_format = GDB_HOST_FLOAT_FORMAT;
static const struct floatformat *host_double_format = GDB_HOST_DOUBLE_FORMAT;
static const struct floatformat *host_long_double_format = GDB_HOST_LONG_DOUBLE_FORMAT;
void
floatformat_to_doublest (const struct floatformat *fmt,
const void *in, DOUBLEST *out)
{
gdb_assert (fmt != NULL);
if (fmt == host_float_format)
{
float val;
memcpy (&val, in, sizeof (val));
*out = val;
}
else if (fmt == host_double_format)
{
double val;
memcpy (&val, in, sizeof (val));
*out = val;
}
else if (fmt == host_long_double_format)
{
long double val;
memcpy (&val, in, sizeof (val));
*out = val;
}
else if (floatformats_same_except_for_byteorder (fmt, host_float_format)
&& floatformat_byteorders_are_reversed (fmt->byteorder,
host_float_format->byteorder))
{
float val;
swap_bytes ((gdb_byte *) in, (gdb_byte *) &val, sizeof (val));
*out = val;
}
else if (floatformats_same_except_for_byteorder (fmt, host_double_format)
&& floatformat_byteorders_are_reversed (fmt->byteorder,
host_double_format->byteorder))
{
double val;
swap_bytes ((gdb_byte *) in, (gdb_byte *) &val, sizeof (val));
*out = val;
}
else if (floatformats_same_except_for_byteorder (fmt, host_long_double_format)
&& floatformat_byteorders_are_reversed (fmt->byteorder,
host_long_double_format->byteorder))
{
long double val;
swap_bytes ((gdb_byte *) in, (gdb_byte *) &val, sizeof (val));
*out = val;
}
else
convert_floatformat_to_doublest (fmt, in, out);
}
void
floatformat_from_doublest (const struct floatformat *fmt,
const DOUBLEST *in, void *out)
{
gdb_assert (fmt != NULL);
if (fmt == host_float_format)
{
float val = *in;
memcpy (out, &val, sizeof (val));
}
else if (fmt == host_double_format)
{
double val = *in;
memcpy (out, &val, sizeof (val));
}
else if (fmt == host_long_double_format)
{
long double val = *in;
memcpy (out, &val, sizeof (val));
}
else
convert_doublest_to_floatformat (fmt, in, out);
}
static const struct floatformat *
floatformat_from_length (int len)
{
const struct floatformat *format;
if (len * TARGET_CHAR_BIT == TARGET_FLOAT_BIT)
format = TARGET_FLOAT_FORMAT;
else if (len * TARGET_CHAR_BIT == TARGET_DOUBLE_BIT)
format = TARGET_DOUBLE_FORMAT;
else if (len * TARGET_CHAR_BIT == TARGET_LONG_DOUBLE_BIT)
format = TARGET_LONG_DOUBLE_FORMAT;
else if ((TARGET_LONG_DOUBLE_FORMAT != NULL)
&& (len * TARGET_CHAR_BIT ==
TARGET_LONG_DOUBLE_FORMAT->totalsize))
format = TARGET_LONG_DOUBLE_FORMAT;
else
format = NULL;
if (format == NULL)
error (_("Unrecognized %d-bit floating-point type."),
len * TARGET_CHAR_BIT);
return format;
}
const struct floatformat *
floatformat_from_type (const struct type *type)
{
gdb_assert (TYPE_CODE (type) == TYPE_CODE_FLT);
if (TYPE_FLOATFORMAT (type) != NULL)
return TYPE_FLOATFORMAT (type);
else
return floatformat_from_length (TYPE_LENGTH (type));
}
#undef NAN
#define NAN 0.0
static DOUBLEST
extract_floating_by_length (const void *addr, int len)
{
const struct floatformat *fmt = floatformat_from_length (len);
DOUBLEST val;
floatformat_to_doublest (fmt, addr, &val);
return val;
}
DOUBLEST
deprecated_extract_floating (const void *addr, int len)
{
return extract_floating_by_length (addr, len);
}
static void
store_floating_by_length (void *addr, int len, DOUBLEST val)
{
const struct floatformat *fmt = floatformat_from_length (len);
floatformat_from_doublest (fmt, &val, addr);
}
void
deprecated_store_floating (void *addr, int len, DOUBLEST val)
{
store_floating_by_length (addr, len, val);
}
DOUBLEST
extract_typed_floating (const void *addr, const struct type *type)
{
DOUBLEST retval;
gdb_assert (TYPE_CODE (type) == TYPE_CODE_FLT);
if (TYPE_FLOATFORMAT (type) == NULL)
return extract_floating_by_length (addr, TYPE_LENGTH (type));
floatformat_to_doublest (TYPE_FLOATFORMAT (type), addr, &retval);
return retval;
}
void
store_typed_floating (void *addr, const struct type *type, DOUBLEST val)
{
gdb_assert (TYPE_CODE (type) == TYPE_CODE_FLT);
memset (addr, 0, TYPE_LENGTH (type));
if (TYPE_FLOATFORMAT (type) == NULL)
store_floating_by_length (addr, TYPE_LENGTH (type), val);
else
floatformat_from_doublest (TYPE_FLOATFORMAT (type), &val, addr);
}
void
convert_typed_floating (const void *from, const struct type *from_type,
void *to, const struct type *to_type)
{
const struct floatformat *from_fmt = floatformat_from_type (from_type);
const struct floatformat *to_fmt = floatformat_from_type (to_type);
gdb_assert (TYPE_CODE (from_type) == TYPE_CODE_FLT);
gdb_assert (TYPE_CODE (to_type) == TYPE_CODE_FLT);
if (from_fmt == NULL || to_fmt == NULL)
{
warning (_("Can't convert floating-point number to desired type."));
memset (to, 0, TYPE_LENGTH (to_type));
}
else if (from_fmt == to_fmt)
{
memset (to, 0, TYPE_LENGTH (to_type));
memcpy (to, from, min (TYPE_LENGTH (from_type), TYPE_LENGTH (to_type)));
}
else
{
DOUBLEST d;
floatformat_to_doublest (from_fmt, from, &d);
floatformat_from_doublest (to_fmt, &d, to);
}
}
const struct floatformat *floatformat_ieee_single[BFD_ENDIAN_UNKNOWN];
const struct floatformat *floatformat_ieee_double[BFD_ENDIAN_UNKNOWN];
const struct floatformat *floatformat_ieee_quad[BFD_ENDIAN_UNKNOWN];
const struct floatformat *floatformat_arm_ext[BFD_ENDIAN_UNKNOWN];
const struct floatformat *floatformat_ia64_spill[BFD_ENDIAN_UNKNOWN];
extern void _initialize_doublest (void);
extern void
_initialize_doublest (void)
{
floatformat_ieee_single[BFD_ENDIAN_LITTLE] = &floatformat_ieee_single_little;
floatformat_ieee_single[BFD_ENDIAN_BIG] = &floatformat_ieee_single_big;
floatformat_ieee_double[BFD_ENDIAN_LITTLE] = &floatformat_ieee_double_little;
floatformat_ieee_double[BFD_ENDIAN_BIG] = &floatformat_ieee_double_big;
floatformat_arm_ext[BFD_ENDIAN_LITTLE] = &floatformat_arm_ext_littlebyte_bigword;
floatformat_arm_ext[BFD_ENDIAN_BIG] = &floatformat_arm_ext_big;
floatformat_ia64_spill[BFD_ENDIAN_LITTLE] = &floatformat_ia64_spill_little;
floatformat_ia64_spill[BFD_ENDIAN_BIG] = &floatformat_ia64_spill_big;
floatformat_ieee_quad[BFD_ENDIAN_LITTLE] = &floatformat_ia64_quad_little;
floatformat_ieee_quad[BFD_ENDIAN_BIG] = &floatformat_ia64_quad_big;
}