#include "floatformat.h"
#include "gdb_assert.h"
#include "gdb_string.h"
+#include "gdbtypes.h"
#include <math.h> /* ldexp */
/* The odds that CHAR_BIT will be anything but 8 are low enough that I'm not
FROM is the address of the extended float.
Store the DOUBLEST in *TO. */
-void
-floatformat_to_doublest (const struct floatformat *fmt,
- const void *from,
- DOUBLEST *to)
+static void
+convert_floatformat_to_doublest (const struct floatformat *fmt,
+ const void *from,
+ DOUBLEST *to)
{
unsigned char *ufrom = (unsigned char *) from;
DOUBLEST dto;
and store where TO points. Neither FROM nor TO have any alignment
restrictions. */
-void
-floatformat_from_doublest (CONST struct floatformat *fmt,
- const DOUBLEST *from,
- void *to)
+static void
+convert_doublest_to_floatformat (CONST struct floatformat *fmt,
+ const DOUBLEST *from,
+ void *to)
{
DOUBLEST dfrom;
int exponent;
return res;
}
-
\f
-/* Extract a floating-point number from a target-order byte-stream at ADDR.
- Returns the value as type DOUBLEST.
+/* Convert TO/FROM target to the hosts DOUBLEST floating-point format.
- If the host and target formats agree, we just copy the raw data into the
- appropriate type of variable and return, letting the host increase precision
- as necessary. Otherwise, we call the conversion routine and let it do the
- dirty work. */
+ If the host and target formats agree, we just copy the raw data
+ into the appropriate type of variable and return, letting the host
+ increase precision as necessary. Otherwise, we call the conversion
+ routine and let it do the dirty work. */
-DOUBLEST
-extract_floating (const void *addr, int len)
-{
- DOUBLEST dretval;
+#ifndef HOST_FLOAT_FORMAT
+#define HOST_FLOAT_FORMAT 0
+#endif
+#ifndef HOST_DOUBLE_FORMAT
+#define HOST_DOUBLE_FORMAT 0
+#endif
+#ifndef HOST_LONG_DOUBLE_FORMAT
+#define HOST_LONG_DOUBLE_FORMAT 0
+#endif
- if (len * TARGET_CHAR_BIT == TARGET_FLOAT_BIT)
- {
- if (HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT)
- {
- float retval;
+static const struct floatformat *host_float_format = HOST_FLOAT_FORMAT;
+static const struct floatformat *host_double_format = HOST_DOUBLE_FORMAT;
+static const struct floatformat *host_long_double_format = HOST_LONG_DOUBLE_FORMAT;
- memcpy (&retval, addr, sizeof (retval));
- return retval;
- }
- else
- floatformat_to_doublest (TARGET_FLOAT_FORMAT, addr, &dretval);
+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 (len * TARGET_CHAR_BIT == TARGET_DOUBLE_BIT)
+ else if (fmt == host_double_format)
{
- if (HOST_DOUBLE_FORMAT == TARGET_DOUBLE_FORMAT)
- {
- double retval;
-
- memcpy (&retval, addr, sizeof (retval));
- return retval;
- }
- else
- floatformat_to_doublest (TARGET_DOUBLE_FORMAT, addr, &dretval);
+ double val;
+ memcpy (&val, in, sizeof (val));
+ *out = val;
}
- else if (len * TARGET_CHAR_BIT == TARGET_LONG_DOUBLE_BIT)
+ else if (fmt == host_long_double_format)
{
- if (HOST_LONG_DOUBLE_FORMAT == TARGET_LONG_DOUBLE_FORMAT)
- {
- DOUBLEST retval;
+ long double val;
+ memcpy (&val, in, sizeof (val));
+ *out = val;
+ }
+ else
+ convert_floatformat_to_doublest (fmt, in, out);
+}
- memcpy (&retval, addr, sizeof (retval));
- return retval;
- }
- else
- floatformat_to_doublest (TARGET_LONG_DOUBLE_FORMAT, addr, &dretval);
+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);
+}
+
+\f
+/* Return a floating-point format for a floating-point variable of
+ length LEN. Return NULL, if no suitable floating-point format
+ could be found.
+
+ We need this functionality since information about the
+ floating-point format of a type is not always available to GDB; the
+ debug information typically only tells us the size of a
+ floating-point type.
+
+ FIXME: kettenis/2001-10-28: In many places, particularly in
+ target-dependent code, the format of floating-point types is known,
+ but not passed on by GDB. This should be fixed. */
+
+static const struct floatformat *
+floatformat_from_length (int len)
+{
+ if (len * TARGET_CHAR_BIT == TARGET_FLOAT_BIT)
+ return TARGET_FLOAT_FORMAT;
+ else if (len * TARGET_CHAR_BIT == TARGET_DOUBLE_BIT)
+ return TARGET_DOUBLE_FORMAT;
+ else if (len * TARGET_CHAR_BIT == TARGET_LONG_DOUBLE_BIT)
+ return TARGET_LONG_DOUBLE_FORMAT;
+
+ return NULL;
+}
+
+/* If the host doesn't define NAN, use zero instead. */
+#ifndef NAN
+#define NAN 0.0
+#endif
+
+/* Extract a floating-point number of length LEN from a target-order
+ byte-stream at ADDR. Returns the value as type DOUBLEST. */
+
+DOUBLEST
+extract_floating (const void *addr, int len)
+{
+ const struct floatformat *fmt = floatformat_from_length (len);
+ DOUBLEST val;
+
+ if (fmt == NULL)
{
- error ("Can't deal with a floating point number of %d bytes.", len);
+ warning ("Can't store a floating-point number of %d bytes.", len);
+ return NAN;
}
- return dretval;
+ floatformat_to_doublest (fmt, addr, &val);
+ return val;
}
+/* Store VAL as a floating-point number of length LEN to a
+ target-order byte-stream at ADDR. */
+
void
store_floating (void *addr, int len, DOUBLEST val)
{
- if (len * TARGET_CHAR_BIT == TARGET_FLOAT_BIT)
- {
- if (HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT)
- {
- float floatval = val;
+ const struct floatformat *fmt = floatformat_from_length (len);
- memcpy (addr, &floatval, sizeof (floatval));
- }
- else
- floatformat_from_doublest (TARGET_FLOAT_FORMAT, &val, addr);
- }
- else if (len * TARGET_CHAR_BIT == TARGET_DOUBLE_BIT)
+ if (fmt == NULL)
{
- if (HOST_DOUBLE_FORMAT == TARGET_DOUBLE_FORMAT)
- {
- double doubleval = val;
+ warning ("Can't store a floating-point number of %d bytes.", len);
+ memset (addr, 0, len);
+ }
- memcpy (addr, &doubleval, sizeof (doubleval));
- }
- else
- floatformat_from_doublest (TARGET_DOUBLE_FORMAT, &val, addr);
+ floatformat_from_doublest (fmt, &val, addr);
+}
+
+/* Extract a floating-point number of type TYPE from a target-order
+ byte-stream at ADDR. Returns the value as type DOUBLEST. */
+
+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 (addr, TYPE_LENGTH (type));
+
+ floatformat_to_doublest (TYPE_FLOATFORMAT (type), addr, &retval);
+ return retval;
+}
+
+/* Store VAL as a floating-point number of type TYPE to a target-order
+ byte-stream at ADDR. */
+
+void
+store_typed_floating (void *addr, const struct type *type, DOUBLEST val)
+{
+ gdb_assert (TYPE_CODE (type) == TYPE_CODE_FLT);
+
+ /* FIXME: kettenis/2001-10-28: It is debatable whether we should
+ zero out any remaining bytes in the target buffer when TYPE is
+ longer than the actual underlying floating-point format. Perhaps
+ we should store a fixed bitpattern in those remaining bytes,
+ instead of zero, or perhaps we shouldn't touch those remaining
+ bytes at all.
+
+ NOTE: cagney/2001-10-28: With the way things currently work, it
+ isn't a good idea to leave the end bits undefined. This is
+ because GDB writes out the entire sizeof(<floating>) bits of the
+ floating-point type even though the value might only be stored
+ in, and the target processor may only refer to, the first N <
+ TYPE_LENGTH (type) bits. If the end of the buffer wasn't
+ initialized, GDB would write undefined data to the target. An
+ errant program, refering to that undefined data, would then
+ become non-deterministic.
+
+ See also the function convert_typed_floating below. */
+ memset (addr, 0, TYPE_LENGTH (type));
+
+ if (TYPE_FLOATFORMAT (type) == NULL)
+ return store_floating (addr, TYPE_LENGTH (type), val);
+
+ floatformat_from_doublest (TYPE_FLOATFORMAT (type), &val, addr);
+}
+
+/* Convert a floating-point number of type FROM_TYPE from a
+ target-order byte-stream at FROM to a floating-point number of type
+ TO_TYPE, and store it to a target-order byte-stream at TO. */
+
+void
+convert_typed_floating (const void *from, const struct type *from_type,
+ void *to, const struct type *to_type)
+{
+ const struct floatformat *from_fmt = TYPE_FLOATFORMAT (from_type);
+ const struct floatformat *to_fmt = TYPE_FLOATFORMAT (to_type);
+
+ gdb_assert (TYPE_CODE (from_type) == TYPE_CODE_FLT);
+ gdb_assert (TYPE_CODE (to_type) == TYPE_CODE_FLT);
+
+ /* If the floating-point format of FROM_TYPE or TO_TYPE isn't known,
+ try to guess it from the type's length. */
+ if (from_fmt == NULL)
+ from_fmt = floatformat_from_length (TYPE_LENGTH (from_type));
+ if (to_fmt == NULL)
+ to_fmt = floatformat_from_length (TYPE_LENGTH (to_type));
+
+ if (from_fmt == NULL || to_fmt == NULL)
+ {
+ /* If we don't know the floating-point format of FROM_TYPE or
+ TO_TYPE, there's not much we can do. We might make the
+ assumption that if the length of FROM_TYPE and TO_TYPE match,
+ their floating-point format would match too, but that
+ assumption might be wrong on targets that support
+ floating-point types that only differ in endianness for
+ example. So we warn instead, and zero out the target buffer. */
+ warning ("Can't convert floating-point number to desired type.");
+ memset (to, 0, TYPE_LENGTH (to_type));
}
- else if (len * TARGET_CHAR_BIT == TARGET_LONG_DOUBLE_BIT)
+ else if (from_fmt == to_fmt)
{
- if (HOST_LONG_DOUBLE_FORMAT == TARGET_LONG_DOUBLE_FORMAT)
- memcpy (addr, &val, sizeof (val));
- else
- floatformat_from_doublest (TARGET_LONG_DOUBLE_FORMAT, &val, addr);
+ /* We're in business. The floating-point format of FROM_TYPE
+ and TO_TYPE match. However, even though the floating-point
+ format matches, the length of the type might still be
+ different. Make sure we don't overrun any buffers. See
+ comment in store_typed_floating for a discussion about
+ zeroing out remaining bytes in the target buffer. */
+ memset (to, 0, TYPE_LENGTH (to_type));
+ memcpy (to, from, min (TYPE_LENGTH (from_type), TYPE_LENGTH (to_type)));
}
else
{
- error ("Can't deal with a floating point number of %d bytes.", len);
+ /* The floating-point types don't match. The best we can do
+ (aport from simulating the target FPU) is converting to the
+ widest floating-point type supported by the host, and then
+ again to the desired type. */
+ DOUBLEST d;
+
+ floatformat_to_doublest (from_fmt, from, &d);
+ floatformat_from_doublest (to_fmt, &d, to);
}
}