diff mbox

[RFC,v2,4/9] Target FP: Add binop and compare routines to target-float.{c,h}

Message ID 20171025161647.089A3D807F1@oc3748833570.ibm.com
State New
Headers show

Commit Message

Ulrich Weigand Oct. 25, 2017, 4:16 p.m. UTC
[RFC v2][4/9] Target FP: Add binop and compare routines to target-float.{c,h}

This patch adds the following target floating-point routines:
 - target_float_binop
 - target_float_compare
which call the equivalent decimal_ routines to handle decimal FP,
and call helper routines that currently still go via DOUBLEST to
handle binary FP (derived from current valarith.c code).

These routines are used to handle both binary and decimal FP types
in scalar_binop, value_equal, and value_less, mostly following the
method currently used for decimal FP.  The existing value_args_as_decimal
helper is renamed to value_args_as_target_float and extended to handle
both binary and decimal types.

The unary operations value_pos and value_neg are also simplified,
the former by using a simple copy for all scalar types, the latter
by using value_binop (... BINOP_SUB) to implement negation as
subtraction from zero.

Bye,
Ulrich


ChangeLog:

	* target-float.c: Include <math.h>.
	(floatformat_binop): New function.
	(floatformat_compare): Likewise.
	(target_float_binop): Likewise.
	(target_float_compare): Likewise.
	* target-float.h: Include "expression.h".
	(target_float_binop): Add prototype.
	(target_float_compare): Likewise.

	* valarith.c: Do not include "doublest.h" and "dfp.h".
	Include "common/byte-vector.h".
	(value_args_as_decimal): Remove, replace by ...
	(value_args_as_target_float): ... this function.  Handle both
	binary and decimal target floating-point formats.
	(scalar_binop): Handle both binary and decimal FP using
	value_args_as_target_float and target_float_binop.
	(value_equal): Handle both binary and decimal FP using
	value_args_as_target_float and target_float_compare.
	(value_less): Likewise.
	(value_pos): Handle all scalar types as simple copy.
	(value_neg): Handle all scalar types via BINOP_SUB from 0.
	* dfp.c (decimal_binop): Throw error instead of internal_error
	when called with an unsupported operation code.
diff mbox

Patch

Index: binutils-gdb/gdb/target-float.c
===================================================================
--- binutils-gdb.orig/gdb/target-float.c
+++ binutils-gdb/gdb/target-float.c
@@ -27,6 +27,8 @@ 
 
 /* Helper routines operating on binary floating-point data.  */
 
+#include <math.h>
+
 /* Convert the byte-stream ADDR, interpreted as floating-point format FMT,
    to an integer value (rounding towards zero).  */
 static LONGEST
@@ -82,6 +84,82 @@  floatformat_convert (const gdb_byte *fro
     }
 }
 
+/* Perform the binary operation indicated by OPCODE, using as operands the
+   target byte streams X and Y, interpreted as floating-point numbers of
+   formats FMT_X and FMT_Y, respectively.  Convert the result to format
+   FMT_RES and store it into the byte-stream RES.  */
+static void
+floatformat_binop (enum exp_opcode op,
+		   const struct floatformat *fmt_x, const gdb_byte *x,
+		   const struct floatformat *fmt_y, const gdb_byte *y,
+		   const struct floatformat *fmt_result, gdb_byte *result)
+{
+  DOUBLEST v1, v2, v = 0;
+
+  floatformat_to_doublest (fmt_x, x, &v1);
+  floatformat_to_doublest (fmt_y, y, &v2);
+
+  switch (op)
+    {
+      case BINOP_ADD:
+	v = v1 + v2;
+	break;
+
+      case BINOP_SUB:
+	v = v1 - v2;
+	break;
+
+      case BINOP_MUL:
+	v = v1 * v2;
+	break;
+
+      case BINOP_DIV:
+	v = v1 / v2;
+	break;
+
+      case BINOP_EXP:
+	errno = 0;
+	v = pow (v1, v2);
+	if (errno)
+	  error (_("Cannot perform exponentiation: %s"),
+		 safe_strerror (errno));
+	break;
+
+      case BINOP_MIN:
+	v = v1 < v2 ? v1 : v2;
+	break;
+
+      case BINOP_MAX:
+	v = v1 > v2 ? v1 : v2;
+	break;
+
+      default:
+	error (_("Integer-only operation on floating point number."));
+	break;
+    }
+
+  floatformat_from_doublest (fmt_result, &v, result);
+}
+
+/* Compare the two target byte streams X and Y, interpreted as floating-point
+   numbers of formats FMT_X and FMT_Y, respectively.  Return zero if X and Y
+   are equal, -1 if X is less than Y, and 1 otherwise.  */
+static int
+floatformat_compare (const struct floatformat *fmt_x, const gdb_byte *x,
+		     const struct floatformat *fmt_y, const gdb_byte *y)
+{
+  DOUBLEST v1, v2;
+
+  floatformat_to_doublest (fmt_x, x, &v1);
+  floatformat_to_doublest (fmt_y, y, &v2);
+
+  if (v1 == v2)
+    return 0;
+  if (v1 < v2)
+    return -1;
+  return 1;
+}
+
 
 /* Typed floating-point routines.  These routines operate on floating-point
    values in target format, represented by a byte buffer interpreted as a
@@ -266,3 +344,77 @@  target_float_convert (const gdb_byte *fr
 
   gdb_assert_not_reached ("unexpected type code");
 }
+
+/* Perform the binary operation indicated by OPCODE, using as operands the
+   target byte streams X and Y, interpreted as floating-point numbers of
+   types TYPE_X and TYPE_Y, respectively.  Convert the result to type
+   TYPE_RES and store it into the byte-stream RES.
+
+   The three types must either be all binary floating-point types, or else
+   all decimal floating-point types.  Binary and decimal floating-point
+   types cannot be mixed within a single operation.  */
+void
+target_float_binop (enum exp_opcode opcode,
+		    const gdb_byte *x, const struct type *type_x,
+		    const gdb_byte *y, const struct type *type_y,
+		    gdb_byte *res, const struct type *type_res)
+{
+  /* Ensure possible padding bytes in the target buffer are zeroed out.  */
+  memset (res, 0, TYPE_LENGTH (type_res));
+
+  if (TYPE_CODE (type_res) == TYPE_CODE_FLT)
+    {
+      gdb_assert (TYPE_CODE (type_x) == TYPE_CODE_FLT);
+      gdb_assert (TYPE_CODE (type_y) == TYPE_CODE_FLT);
+      return floatformat_binop (opcode,
+				floatformat_from_type (type_x), x,
+				floatformat_from_type (type_y), y,
+				floatformat_from_type (type_res), res);
+    }
+
+  if (TYPE_CODE (type_res) == TYPE_CODE_DECFLOAT)
+    {
+      gdb_assert (TYPE_CODE (type_x) == TYPE_CODE_DECFLOAT);
+      gdb_assert (TYPE_CODE (type_y) == TYPE_CODE_DECFLOAT);
+      return decimal_binop (opcode,
+			    x, TYPE_LENGTH (type_x),
+			    gdbarch_byte_order (get_type_arch (type_x)),
+			    y, TYPE_LENGTH (type_y),
+			    gdbarch_byte_order (get_type_arch (type_y)),
+			    res, TYPE_LENGTH (type_res),
+			    gdbarch_byte_order (get_type_arch (type_res)));
+    }
+
+  gdb_assert_not_reached ("unexpected type code");
+}
+
+/* Compare the two target byte streams X and Y, interpreted as floating-point
+   numbers of types TYPE_X and TYPE_Y, respectively.  Return zero if X and Y
+   are equal, -1 if X is less than Y, and 1 otherwise.
+
+   The two types must either both be binary floating-point types, or else
+   both be decimal floating-point types.  Binary and decimal floating-point
+   types cannot compared directly against each other.  */
+int
+target_float_compare (const gdb_byte *x, const struct type *type_x,
+		      const gdb_byte *y, const struct type *type_y)
+{
+  if (TYPE_CODE (type_x) == TYPE_CODE_FLT)
+    {
+      gdb_assert (TYPE_CODE (type_y) == TYPE_CODE_FLT);
+      return floatformat_compare (floatformat_from_type (type_x), x,
+				  floatformat_from_type (type_y), y);
+    }
+
+  if (TYPE_CODE (type_x) == TYPE_CODE_DECFLOAT)
+    {
+      gdb_assert (TYPE_CODE (type_y) == TYPE_CODE_DECFLOAT);
+      return decimal_compare (x, TYPE_LENGTH (type_x),
+			      gdbarch_byte_order (get_type_arch (type_x)),
+			      y, TYPE_LENGTH (type_y),
+			      gdbarch_byte_order (get_type_arch (type_y)));
+    }
+
+  gdb_assert_not_reached ("unexpected type code");
+}
+
Index: binutils-gdb/gdb/target-float.h
===================================================================
--- binutils-gdb.orig/gdb/target-float.h
+++ binutils-gdb/gdb/target-float.h
@@ -20,6 +20,8 @@ 
 #ifndef TYPED_FLOAT_H
 #define TYPED_FLOAT_H
 
+#include "expression.h"
+
 extern bool target_float_is_valid (const gdb_byte *addr,
 				   const struct type *type);
 extern bool target_float_is_zero (const gdb_byte *addr,
@@ -44,4 +46,11 @@  extern void target_float_convert (const
 				  const struct type *from_type,
 				  gdb_byte *to, const struct type *to_type);
 
+extern void target_float_binop (enum exp_opcode opcode,
+				const gdb_byte *x, const struct type *type_x,
+				const gdb_byte *y, const struct type *type_y,
+				gdb_byte *res, const struct type *type_res);
+extern int target_float_compare (const gdb_byte *x, const struct type *type_x,
+				 const gdb_byte *y, const struct type *type_y);
+
 #endif
Index: binutils-gdb/gdb/dfp.c
===================================================================
--- binutils-gdb.orig/gdb/dfp.c
+++ binutils-gdb/gdb/dfp.c
@@ -330,9 +330,8 @@  decimal_binop (enum exp_opcode op,
       case BINOP_EXP:
 	decNumberPower (&number3, &number1, &number2, &set);
 	break;
-      default:
-	internal_error (__FILE__, __LINE__,
-			_("Unknown decimal floating point operation."));
+     default:
+	error (_("Operation not valid for decimal floating point number."));
 	break;
     }
 
Index: binutils-gdb/gdb/valarith.c
===================================================================
--- binutils-gdb.orig/gdb/valarith.c
+++ binutils-gdb/gdb/valarith.c
@@ -24,11 +24,9 @@ 
 #include "expression.h"
 #include "target.h"
 #include "language.h"
-#include "doublest.h"
-#include "dfp.h"
 #include "target-float.h"
-#include <math.h>
 #include "infcall.h"
+#include "common/byte-vector.h"
 
 /* Define whether or not the C operator '/' truncates towards zero for
    differently signed operands (truncation direction is undefined in C).  */
@@ -845,69 +843,62 @@  uinteger_pow (ULONGEST v1, LONGEST v2)
     }
 }
 
-/* Obtain decimal value of arguments for binary operation, converting from
-   other types if one of them is not decimal floating point.  */
+/* Obtain argument values for binary operation, converting from
+   other types if one of them is not floating point.  */
 static void
-value_args_as_decimal (struct value *arg1, struct value *arg2,
-		       gdb_byte *x, int *len_x, enum bfd_endian *byte_order_x,
-		       gdb_byte *y, int *len_y, enum bfd_endian *byte_order_y)
+value_args_as_target_float (struct value *arg1, struct value *arg2,
+			    gdb_byte *x, struct type **eff_type_x,
+			    gdb_byte *y, struct type **eff_type_y)
 {
   struct type *type1, *type2;
 
   type1 = check_typedef (value_type (arg1));
   type2 = check_typedef (value_type (arg2));
 
-  /* At least one of the arguments must be of decimal float type.  */
-  gdb_assert (TYPE_CODE (type1) == TYPE_CODE_DECFLOAT
-	      || TYPE_CODE (type2) == TYPE_CODE_DECFLOAT);
+  /* At least one of the arguments must be of floating-point type.  */
+  gdb_assert (is_floating_type (type1) || is_floating_type (type2));
 
-  if (TYPE_CODE (type1) == TYPE_CODE_FLT
-      || TYPE_CODE (type2) == TYPE_CODE_FLT)
+  if (is_floating_type (type1) && is_floating_type (type2)
+      && TYPE_CODE (type1) != TYPE_CODE (type2))
     /* The DFP extension to the C language does not allow mixing of
      * decimal float types with other float types in expressions
      * (see WDTR 24732, page 12).  */
     error (_("Mixing decimal floating types with "
 	     "other floating types is not allowed."));
 
-  /* Obtain decimal value of arg1, converting from other types
-     if necessary.  */
+  /* Obtain value of arg1, converting from other types if necessary.  */
 
-  if (TYPE_CODE (type1) == TYPE_CODE_DECFLOAT)
+  if (is_floating_type (type1))
     {
-      *byte_order_x = gdbarch_byte_order (get_type_arch (type1));
-      *len_x = TYPE_LENGTH (type1);
-      memcpy (x, value_contents (arg1), *len_x);
+      *eff_type_x = type1;
+      memcpy (x, value_contents (arg1), TYPE_LENGTH (type1));
     }
   else if (is_integral_type (type1))
     {
-      *byte_order_x = gdbarch_byte_order (get_type_arch (type2));
-      *len_x = TYPE_LENGTH (type2);
+      *eff_type_x = type2;
       if (TYPE_UNSIGNED (type1))
-	decimal_from_ulongest (value_as_long (arg1), x, *len_x, *byte_order_x);
+	target_float_from_ulongest (x, *eff_type_x, value_as_long (arg1));
       else
-	decimal_from_longest (value_as_long (arg1), x, *len_x, *byte_order_x);
+	target_float_from_longest (x, *eff_type_x, value_as_long (arg1));
     }
   else
     error (_("Don't know how to convert from %s to %s."), TYPE_NAME (type1),
 	     TYPE_NAME (type2));
 
-  /* Obtain decimal value of arg2, converting from other types
-     if necessary.  */
+  /* Obtain value of arg2, converting from other types if necessary.  */
 
-  if (TYPE_CODE (type2) == TYPE_CODE_DECFLOAT)
+  if (is_floating_type (type2))
     {
-      *byte_order_y = gdbarch_byte_order (get_type_arch (type2));
-      *len_y = TYPE_LENGTH (type2);
-      memcpy (y, value_contents (arg2), *len_y);
+      *eff_type_y = type2;
+      memcpy (y, value_contents (arg2), TYPE_LENGTH (type2));
     }
   else if (is_integral_type (type2))
     {
-      *byte_order_y = gdbarch_byte_order (get_type_arch (type1));
-      *len_y = TYPE_LENGTH (type1);
+      *eff_type_y = type1;
       if (TYPE_UNSIGNED (type2))
-	decimal_from_ulongest (value_as_long (arg2), y, *len_y, *byte_order_y);
+	target_float_from_ulongest (y, *eff_type_y, value_as_long (arg2));
       else
-	decimal_from_longest (value_as_long (arg2), y, *len_y, *byte_order_y);
+	target_float_from_longest (y, *eff_type_y, value_as_long (arg2));
     }
   else
     error (_("Don't know how to convert from %s to %s."), TYPE_NAME (type1),
@@ -932,111 +923,17 @@  scalar_binop (struct value *arg1, struct
   type1 = check_typedef (value_type (arg1));
   type2 = check_typedef (value_type (arg2));
 
-  if ((TYPE_CODE (type1) != TYPE_CODE_FLT
-       && TYPE_CODE (type1) != TYPE_CODE_DECFLOAT
-       && !is_integral_type (type1))
-      || (TYPE_CODE (type2) != TYPE_CODE_FLT
-	  && TYPE_CODE (type2) != TYPE_CODE_DECFLOAT
-	  && !is_integral_type (type2)))
+  if ((!is_floating_value (arg1) && !is_integral_type (type1))
+      || (!is_floating_value (arg2) && !is_integral_type (type2)))
     error (_("Argument to arithmetic operation not a number or boolean."));
 
-  if (TYPE_CODE (type1) == TYPE_CODE_DECFLOAT
-      || TYPE_CODE (type2) == TYPE_CODE_DECFLOAT)
+  if (is_floating_type (type1) || is_floating_type (type2))
     {
-      int len_v1, len_v2, len_v;
-      enum bfd_endian byte_order_v1, byte_order_v2, byte_order_v;
-      gdb_byte v1[16], v2[16];
-      gdb_byte v[16];
-
-      /* If only one type is decimal float, use its type.
+      /* If only one type is floating-point, use its type.
 	 Otherwise use the bigger type.  */
-      if (TYPE_CODE (type1) != TYPE_CODE_DECFLOAT)
+      if (!is_floating_type (type1))
 	result_type = type2;
-      else if (TYPE_CODE (type2) != TYPE_CODE_DECFLOAT)
-	result_type = type1;
-      else if (TYPE_LENGTH (type2) > TYPE_LENGTH (type1))
-	result_type = type2;
-      else
-	result_type = type1;
-
-      len_v = TYPE_LENGTH (result_type);
-      byte_order_v = gdbarch_byte_order (get_type_arch (result_type));
-
-      value_args_as_decimal (arg1, arg2, v1, &len_v1, &byte_order_v1,
-					 v2, &len_v2, &byte_order_v2);
-
-      switch (op)
-	{
-	case BINOP_ADD:
-	case BINOP_SUB:
-	case BINOP_MUL:
-	case BINOP_DIV:
-	case BINOP_EXP:
-	  decimal_binop (op, v1, len_v1, byte_order_v1,
-			     v2, len_v2, byte_order_v2,
-			     v, len_v, byte_order_v);
-	  break;
-
-	default:
-	  error (_("Operation not valid for decimal floating point number."));
-	}
-
-      val = value_from_decfloat (result_type, v);
-    }
-  else if (TYPE_CODE (type1) == TYPE_CODE_FLT
-	   || TYPE_CODE (type2) == TYPE_CODE_FLT)
-    {
-      /* FIXME-if-picky-about-floating-accuracy: Should be doing this
-         in target format.  real.c in GCC probably has the necessary
-         code.  */
-      DOUBLEST v1, v2, v = 0;
-
-      v1 = value_as_double (arg1);
-      v2 = value_as_double (arg2);
-
-      switch (op)
-	{
-	case BINOP_ADD:
-	  v = v1 + v2;
-	  break;
-
-	case BINOP_SUB:
-	  v = v1 - v2;
-	  break;
-
-	case BINOP_MUL:
-	  v = v1 * v2;
-	  break;
-
-	case BINOP_DIV:
-	  v = v1 / v2;
-	  break;
-
-	case BINOP_EXP:
-	  errno = 0;
-	  v = pow (v1, v2);
-	  if (errno)
-	    error (_("Cannot perform exponentiation: %s"),
-		   safe_strerror (errno));
-	  break;
-
-	case BINOP_MIN:
-	  v = v1 < v2 ? v1 : v2;
-	  break;
-	      
-	case BINOP_MAX:
-	  v = v1 > v2 ? v1 : v2;
-	  break;
-
-	default:
-	  error (_("Integer-only operation on floating point number."));
-	}
-
-      /* If only one type is float, use its type.
-	 Otherwise use the bigger type.  */
-      if (TYPE_CODE (type1) != TYPE_CODE_FLT)
-	result_type = type2;
-      else if (TYPE_CODE (type2) != TYPE_CODE_FLT)
+      else if (!is_floating_type (type2))
 	result_type = type1;
       else if (TYPE_LENGTH (type2) > TYPE_LENGTH (type1))
 	result_type = type2;
@@ -1044,7 +941,18 @@  scalar_binop (struct value *arg1, struct
 	result_type = type1;
 
       val = allocate_value (result_type);
-      store_typed_floating (value_contents_raw (val), value_type (val), v);
+
+      struct type *eff_type_v1, *eff_type_v2;
+      gdb::byte_vector v1, v2;
+      v1.resize (TYPE_LENGTH (result_type));
+      v2.resize (TYPE_LENGTH (result_type));
+
+      value_args_as_target_float (arg1, arg2,
+				  v1.data (), &eff_type_v1,
+				  v2.data (), &eff_type_v2);
+      target_float_binop (op, v1.data (), eff_type_v1,
+			      v2.data (), eff_type_v2,
+			      value_contents_raw (val), result_type);
     }
   else if (TYPE_CODE (type1) == TYPE_CODE_BOOL
 	   || TYPE_CODE (type2) == TYPE_CODE_BOOL)
@@ -1587,27 +1495,20 @@  value_equal (struct value *arg1, struct
   if (is_int1 && is_int2)
     return longest_to_int (value_as_long (value_binop (arg1, arg2,
 						       BINOP_EQUAL)));
-  else if ((code1 == TYPE_CODE_FLT || is_int1)
-	   && (code2 == TYPE_CODE_FLT || is_int2))
+  else if ((is_floating_value (arg1) || is_int1)
+	   && (is_floating_value (arg2) || is_int2))
     {
-      /* NOTE: kettenis/20050816: Avoid compiler bug on systems where
-	 `long double' values are returned in static storage (m68k).  */
-      DOUBLEST d = value_as_double (arg1);
+      struct type *eff_type_v1, *eff_type_v2;
+      gdb::byte_vector v1, v2;
+      v1.resize (std::max (TYPE_LENGTH (type1), TYPE_LENGTH (type2)));
+      v2.resize (std::max (TYPE_LENGTH (type1), TYPE_LENGTH (type2)));
+
+      value_args_as_target_float (arg1, arg2,
+				  v1.data (), &eff_type_v1,
+				  v2.data (), &eff_type_v2);
 
-      return d == value_as_double (arg2);
-    }
-  else if ((code1 == TYPE_CODE_DECFLOAT || is_int1)
-	   && (code2 == TYPE_CODE_DECFLOAT || is_int2))
-    {
-      gdb_byte v1[16], v2[16];
-      int len_v1, len_v2;
-      enum bfd_endian byte_order_v1, byte_order_v2;
-
-      value_args_as_decimal (arg1, arg2, v1, &len_v1, &byte_order_v1,
-					 v2, &len_v2, &byte_order_v2);
-
-      return decimal_compare (v1, len_v1, byte_order_v1,
-			      v2, len_v2, byte_order_v2) == 0;
+      return target_float_compare (v1.data (), eff_type_v1,
+				   v2.data (), eff_type_v2) == 0;
     }
 
   /* FIXME: Need to promote to either CORE_ADDR or LONGEST, whichever
@@ -1683,27 +1584,20 @@  value_less (struct value *arg1, struct v
   if (is_int1 && is_int2)
     return longest_to_int (value_as_long (value_binop (arg1, arg2,
 						       BINOP_LESS)));
-  else if ((code1 == TYPE_CODE_FLT || is_int1)
-	   && (code2 == TYPE_CODE_FLT || is_int2))
+  else if ((is_floating_value (arg1) || is_int1)
+	   && (is_floating_value (arg2) || is_int2))
     {
-      /* NOTE: kettenis/20050816: Avoid compiler bug on systems where
-	 `long double' values are returned in static storage (m68k).  */
-      DOUBLEST d = value_as_double (arg1);
+      struct type *eff_type_v1, *eff_type_v2;
+      gdb::byte_vector v1, v2;
+      v1.resize (std::max (TYPE_LENGTH (type1), TYPE_LENGTH (type2)));
+      v2.resize (std::max (TYPE_LENGTH (type1), TYPE_LENGTH (type2)));
+
+      value_args_as_target_float (arg1, arg2,
+				  v1.data (), &eff_type_v1,
+				  v2.data (), &eff_type_v2);
 
-      return d < value_as_double (arg2);
-    }
-  else if ((code1 == TYPE_CODE_DECFLOAT || is_int1)
-	   && (code2 == TYPE_CODE_DECFLOAT || is_int2))
-    {
-      gdb_byte v1[16], v2[16];
-      int len_v1, len_v2;
-      enum bfd_endian byte_order_v1, byte_order_v2;
-
-      value_args_as_decimal (arg1, arg2, v1, &len_v1, &byte_order_v1,
-					 v2, &len_v2, &byte_order_v2);
-
-      return decimal_compare (v1, len_v1, byte_order_v1,
-			      v2, len_v2, byte_order_v2) == -1;
+      return target_float_compare (v1.data (), eff_type_v1,
+				   v2.data (), eff_type_v2) == -1;
     }
   else if (code1 == TYPE_CODE_PTR && code2 == TYPE_CODE_PTR)
     return value_as_address (arg1) < value_as_address (arg2);
@@ -1733,22 +1627,9 @@  value_pos (struct value *arg1)
   arg1 = coerce_ref (arg1);
   type = check_typedef (value_type (arg1));
 
-  if (TYPE_CODE (type) == TYPE_CODE_FLT)
-    return value_from_double (type, value_as_double (arg1));
-  else if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
-    return value_from_decfloat (type, value_contents (arg1));
-  else if (is_integral_type (type))
-    {
-      return value_from_longest (type, value_as_long (arg1));
-    }
-  else if (TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type))
-    {
-      struct value *val = allocate_value (type);
-
-      memcpy (value_contents_raw (val), value_contents (arg1),
-              TYPE_LENGTH (type));
-      return val;
-    }
+  if (is_integral_type (type) || is_floating_value (arg1)
+      || (TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type)))
+    return value_from_contents (type, value_contents (arg1));
   else
     {
       error (_("Argument to positive operation not a number."));
@@ -1764,28 +1645,8 @@  value_neg (struct value *arg1)
   arg1 = coerce_ref (arg1);
   type = check_typedef (value_type (arg1));
 
-  if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
-    {
-      struct value *val = allocate_value (type);
-      int len = TYPE_LENGTH (type);
-      gdb_byte decbytes[16];  /* a decfloat is at most 128 bits long.  */
-
-      memcpy (decbytes, value_contents (arg1), len);
-
-      if (gdbarch_byte_order (get_type_arch (type)) == BFD_ENDIAN_LITTLE)
-	decbytes[len-1] = decbytes[len - 1] | 0x80;
-      else
-	decbytes[0] = decbytes[0] | 0x80;
-
-      memcpy (value_contents_raw (val), decbytes, len);
-      return val;
-    }
-  else if (TYPE_CODE (type) == TYPE_CODE_FLT)
-    return value_from_double (type, -value_as_double (arg1));
-  else if (is_integral_type (type))
-    {
-      return value_from_longest (type, -value_as_long (arg1));
-    }
+  if (is_integral_type (type) || is_floating_type (type))
+    return value_binop (value_from_longest (type, 0), arg1, BINOP_SUB);
   else if (TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type))
     {
       struct value *tmp, *val = allocate_value (type);