*** empty log message ***
[deliverable/binutils-gdb.git] / gdb / doublest.c
index e2dd3e896dc00345836c223134484b13d1f3e5fb..3f283e13ffa55842eba72b08a4515b52fe0ca38e 100644 (file)
@@ -1,7 +1,8 @@
 /* Floating point routines for GDB, the GNU debugger.
-   Copyright 1986, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996,
-   1997, 1998, 1999, 2000, 2001
-   Free Software Foundation, Inc.
+
+   Copyright 1986, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,
+   1996, 1997, 1998, 1999, 2000, 2001, 2003 Free Software Foundation,
+   Inc.
 
    This file is part of GDB.
 
@@ -172,8 +173,10 @@ convert_floatformat_to_doublest (const struct floatformat *fmt,
 
   special_exponent = exponent == 0 || exponent == fmt->exp_nan;
 
-/* Don't bias NaNs. Use minimum exponent for denorms. For simplicity,
-   we don't check for zero as the exponent doesn't matter. */
+  /* Don't bias NaNs. Use minimum exponent for denorms. For simplicity,
+     we don't check for zero as the exponent doesn't matter.  Note the cast
+     to int; exp_bias is unsigned, so it's important to make sure the
+     operation is done in signed arithmetic.  */
   if (!special_exponent)
     exponent -= fmt->exp_bias;
   else if (exponent == 0)
@@ -401,7 +404,15 @@ convert_doublest_to_floatformat (CONST struct floatformat *fmt,
        {
          mant_long <<= 1;
          mant_long &= 0xffffffffL;
-         mant_bits -= 1;
+          /* If we are processing the top 32 mantissa bits of a doublest
+             so as to convert to a float value with implied integer bit,
+             we will only be putting 31 of those 32 bits into the
+             final value due to the discarding of the top bit.  In the 
+             case of a small float value where the number of mantissa 
+             bits is less than 32, discarding the top bit does not alter
+             the number of bits we will be adding to the result.  */
+          if (mant_bits == 32)
+            mant_bits -= 1;
        }
 
       if (mant_bits < 32)
@@ -439,7 +450,7 @@ int
 floatformat_is_negative (const struct floatformat *fmt, char *val)
 {
   unsigned char *uval = (unsigned char *) val;
-
+  gdb_assert (fmt != NULL);
   return get_field (uval, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1);
 }
 
@@ -454,6 +465,8 @@ floatformat_is_nan (const struct floatformat *fmt, char *val)
   unsigned int mant_bits, mant_off;
   int mant_bits_left;
 
+  gdb_assert (fmt != NULL);
+
   if (! fmt->exp_nan)
     return 0;
 
@@ -503,6 +516,7 @@ floatformat_mantissa (const struct floatformat *fmt, char *val)
   char buf[9];
 
   /* Make sure we have enough room to store the mantissa.  */
+  gdb_assert (fmt != NULL);
   gdb_assert (sizeof res > ((fmt->man_len + 7) / 8) * 2);
 
   mant_off = fmt->man_start;
@@ -532,7 +546,6 @@ floatformat_mantissa (const struct floatformat *fmt, char *val)
   return res;
 }
 
-
 \f
 /* Convert TO/FROM target to the hosts DOUBLEST floating-point format.
 
@@ -607,84 +620,205 @@ floatformat_from_doublest (const struct floatformat *fmt,
 }
 
 \f
-/* Extract/store a target floating-point number from byte-stream at
-   ADDR to/from a DOUBLEST.  The LEN is used to select between the
-   pre-defined target type FLOAT, DOUBLE or LONG_DOUBLE.  These
-   functions are used when extract/store typed floating() find that
-   the ``struct type'' did not include a FLOATFORMAT (e.g. some symbol
-   table readers and XXX-language support modules).  */
+/* Return a floating-point format for a floating-point variable of
+   length LEN.  Return NULL, if no suitable floating-point format
+   could be found.
 
-DOUBLEST
-extract_floating (const void *addr, int len)
+   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)
 {
-  DOUBLEST dretval;
   if (len * TARGET_CHAR_BIT == TARGET_FLOAT_BIT)
-    {
-      floatformat_to_doublest (TARGET_FLOAT_FORMAT, addr, &dretval);
-    }
+    return TARGET_FLOAT_FORMAT;
   else if (len * TARGET_CHAR_BIT == TARGET_DOUBLE_BIT)
-    {
-      floatformat_to_doublest (TARGET_DOUBLE_FORMAT, addr, &dretval);
-    }
+    return TARGET_DOUBLE_FORMAT;
   else if (len * TARGET_CHAR_BIT == TARGET_LONG_DOUBLE_BIT)
-    {
-      floatformat_to_doublest (TARGET_LONG_DOUBLE_FORMAT, addr, &dretval);
-    }
+    return TARGET_LONG_DOUBLE_FORMAT;
+  /* On i386 the 'long double' type takes 96 bits,
+     while the real number of used bits is only 80,
+     both in processor and in memory.  
+     The code below accepts the real bit size.  */ 
+  else if ((TARGET_LONG_DOUBLE_FORMAT != NULL) 
+          && (len * TARGET_CHAR_BIT ==
+               TARGET_LONG_DOUBLE_FORMAT->totalsize))
+    return TARGET_LONG_DOUBLE_FORMAT;
+
+  return NULL;
+}
+
+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
-    {
-      error ("Can't deal with a floating point number of %d bytes.", len);
-    }
-  return dretval;
+    return floatformat_from_length (TYPE_LENGTH (type));
 }
 
-void
-store_floating (void *addr, int len, DOUBLEST val)
+/* 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.  */
+
+static DOUBLEST
+extract_floating_by_length (const void *addr, int len)
 {
-  if (len * TARGET_CHAR_BIT == TARGET_FLOAT_BIT)
-    {
-      floatformat_from_doublest (TARGET_FLOAT_FORMAT, &val, addr);
-    }
-  else if (len * TARGET_CHAR_BIT == TARGET_DOUBLE_BIT)
-    {
-      floatformat_from_doublest (TARGET_DOUBLE_FORMAT, &val, addr);
-    }
-  else if (len * TARGET_CHAR_BIT == TARGET_LONG_DOUBLE_BIT)
+  const struct floatformat *fmt = floatformat_from_length (len);
+  DOUBLEST val;
+
+  if (fmt == NULL)
     {
-      floatformat_from_doublest (TARGET_LONG_DOUBLE_FORMAT, &val, addr);
+      warning ("Can't extract a floating-point number of %d bytes.", len);
+      return NAN;
     }
-  else
+
+  floatformat_to_doublest (fmt, addr, &val);
+  return val;
+}
+
+DOUBLEST
+deprecated_extract_floating (const void *addr, int len)
+{
+  return extract_floating_by_length (addr, len);
+}
+
+/* Store VAL as a floating-point number of length LEN to a
+   target-order byte-stream at ADDR.  */
+
+static void
+store_floating_by_length (void *addr, int len, DOUBLEST val)
+{
+  const struct floatformat *fmt = floatformat_from_length (len);
+
+  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);
+      memset (addr, 0, len);
+      return;
     }
+
+  floatformat_from_doublest (fmt, &val, addr);
+}
+
+void
+deprecated_store_floating (void *addr, int len, DOUBLEST val)
+{
+  store_floating_by_length (addr, len, val);
 }
 
-/* Extract/store a floating-point number of format TYPE from a
-   target-ordered byte-stream at ADDR to/from an internal DOUBLEST
-   accroding to its TYPE_FORMAT().  When GDB reads in debug
-   information, it is sometimes only provided with the type name, its
-   length and the fact that it is a float (TYPE_FORMAT() is not set).
-   For such types, the old extract/store floating() functions are
-   used. */
+/* 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)
-    retval = extract_floating (addr, TYPE_LENGTH (type));
-  else
-    floatformat_to_doublest (TYPE_FLOATFORMAT (type), addr, &retval);
+    /* Not all code remembers to set the FLOATFORMAT (language
+       specific code? stabs?) so handle that here as a special case.  */
+    return extract_floating_by_length (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)
-    store_floating (addr, TYPE_LENGTH (type), val);
+    /* Not all code remembers to set the FLOATFORMAT (language
+       specific code? stabs?) so handle that here as a special case.  */
+    store_floating_by_length (addr, TYPE_LENGTH (type), val);
   else
     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 = 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)
+    {
+      /* 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 (from_fmt == to_fmt)
+    {
+      /* 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
+    {
+      /* 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);
+    }
+}
This page took 0.030954 seconds and 4 git commands to generate.