This is the mail archive of the gdb@sourceware.cygnus.com mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Restructuring i386_extract_return_value


Hi,

Here are some other changes that I'm planning to make to the generic
ix86 code.  Since this affect several of the i386 targets I'd like to
give the people involved with those targets the opportunity to object
:-).  The benefits are clear: support for `long long' return values on
all targets that use this, and support for all floating-point types
(including `long double') on all targets where GDB can access the FPU.

Looking at the GCC sources and the Intel documentation, I believe that
the attached patch is the right thing to do.  Please refer to the
detailed description below.

I have verified that all targets that use i386_extract_return_value
share the same register layout for the %eax and %edx registers.

In addition to the attached patch some cleanup of the various tm-*.h
files will be needed (remove definitions of {LOW,HIGH}_RETURN_REGNUM,
get rid of the I386_*_TARGET defines where possible), I also think
that the redefinition of EXTRACT_RETURN_VALUE in tm-i386v.h can go.

Comments are welcome!

Mark


Return values
-------------

There are three issues related to function return values: (1)
floating-point return values, (2) 64-bit integer return values and (3)
`struct' return values.

1. The majority of i386 targets in GCC return floating-point values by
   default on the FPU stack.  In fact the only exception is NeXT.
   There is a switch `-mno-fp-ret-in-387' to force GCC to return
   floating-point values in the ordinary CPU registers.  I don't think
   this can be determined from the debugging information.  Is it worth
   adding a i386 target specific option to enable people to debug
   this code?  Propably not.

   It seems reasonable to make i386-tdep.c:i386_extract_return_value()
   look for floating-point return values on the FPU stack if
   NUM_FREGS > 0, and issue a warning otherwise.

   Targets that return floating-point values in the CPU registers
   should provide their own function to extract return values, and
   override EXTRACT_RETURN_VALUE.  Since NeXT isn't supported by GDB
   this isn't an issue right now.

2. As far as I can tell, all i386 targets return `long long' values in
   eax (low word) and edx (high word).  This makes sense since this is
   the convention used by the machine instructions themselves.  The
   relevant GCC code suggests that in addition to eax and edx, ecx
   might also be used for 12-byte values (then edx is the "middle"
   word, and ecx the high word).  But since GCC doesn't have a 12-byte
   integer type (would that make sense?) this should never occur.
   However, the full 12-byte range is used for returning extended
   floating-point numbers (`long double').  Right now this is only
   implemented for AIX, Linux and DJGPP, but this should be made more
   general.

3. GCC can return `struct' and `union' values in registers when their
   size and alignment match some integer type.  This is the default on
   several platforms where GCC is the only compiler (*BSD), and
   specified by the Win32 ABI (which is also used by Cygwin).
   Platforms that use the System V ABI (such as Linux) do not do this
   by default.  Again there are switches (`-fpcc-struct-return' and
   `-freg-struct-return') that override the default.  The behaviour is
   governed by USE_STRUCT_CONVENTION, and the default seems to be not
   to expect "small" structs to be returned in the registers unless
   GCC 1.x was used.  This seems to be wrong for systems where
   -freg-struct-return is the default.


2000-03-09  Mark Kettenis  <kettenis@gnu.org>

	* i386-tdep.c (LOW_RETURN_REGNUM, HIGH_RETURN_REGNUM): New defines.
	(i386_extract_return_value): Rewritten.  Correctly support all
	floating-point types and large integer types on targets that use
	the standard i386 GDB register layout and return floating-point
	values in the FPU.


Index: i386-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/i386-tdep.c,v
retrieving revision 1.6
diff -u -p -r1.6 i386-tdep.c
--- i386-tdep.c	2000/03/08 22:34:18	1.6
+++ i386-tdep.c	2000/03/09 22:03:54
@@ -698,56 +698,66 @@ get_longjmp_target (pc)
 
 #endif /* GET_LONGJMP_TARGET */
 
+/* These registers are used for returning integers (and on some
+   targets also for returning `struct' and `union' values when their
+   size and alignment match an integer type.  */
+#define LOW_RETURN_REGNUM 0	/* %eax */
+#define HIGH_RETURN_REGNUM 2	/* %edx */
+
+/* Extract from an array REGBUF containing the (raw) register state, a
+   function return value of TYPE, and copy that, in virtual format,
+   into VALBUF.  */
+
 void
-i386_extract_return_value (type, regbuf, valbuf)
-     struct type *type;
-     char regbuf[REGISTER_BYTES];
-     char *valbuf;
+i386_extract_return_value (struct type *type, char *regbuf, char *valbuf)
 {
-  /* On AIX, i386 GNU/Linux and DJGPP, floating point values are
-     returned in floating point registers.  */
-  /* FIXME: cagney/2000-02-29: This function needs to be rewritten
-     using multi-arch. Please don't keep adding to this #ifdef
-     spaghetti. */
-#if defined(I386_AIX_TARGET) || defined(I386_GNULINUX_TARGET) || defined(I386_DJGPP_TARGET)
+  int len = TYPE_LENGTH (type);
+
   if (TYPE_CODE_FLT == TYPE_CODE (type))
     {
-      double d;
-      /* 387 %st(0), gcc uses this */
-      floatformat_to_double (&floatformat_i387_ext,
-#if defined(FPDATA_REGNUM)
-			     &regbuf[REGISTER_BYTE (FPDATA_REGNUM)],
-#else /* !FPDATA_REGNUM */
-			     &regbuf[REGISTER_BYTE (FP0_REGNUM)],
-#endif /* FPDATA_REGNUM */
+      if (NUM_FREGS == 0)
+	{
+	  warning ("Cannot find floating-point return value.");
+	  memset (valbuf, 0, len);
+	}
 
-			     &d);
-      store_floating (valbuf, TYPE_LENGTH (type), d);
+      /* Floating-point return values can be found in %st(0).  */
+      if (len == TARGET_LONG_DOUBLE_BIT / TARGET_CHAR_BIT
+	  && TARGET_LONG_DOUBLE_FORMAT == &floatformat_i387_ext)
+	{
+	  /* Copy straight over, but take care of the padding.  */
+	  memcpy (valbuf, &regbuf[REGISTER_BYTE (FP0_REGNUM)],
+		  FPU_REG_RAW_SIZE);
+	  memset (valbuf + FPU_REG_RAW_SIZE, 0, len - FPU_REG_RAW_SIZE);
+	}
+      else
+	{
+	  /* Convert the extended floating-point number found in
+             %st(0) to the desired type.  This is probably not exactly
+             how it would happen on the target itself, but it is the
+             best we can do.  */
+	  DOUBLEST val;
+	  floatformat_to_doublest (&floatformat_i387_ext,
+				   &regbuf[REGISTER_BYTE (FP0_REGNUM)], &val);
+	  store_floating (valbuf, TYPE_LENGTH (type), val);
+	}
     }
   else
-#endif /* I386_AIX_TARGET || I386_GNULINUX_TARGET || I386_DJGPP_TARGET */
     {
-#if defined(LOW_RETURN_REGNUM)
-      int len = TYPE_LENGTH (type);
       int low_size = REGISTER_RAW_SIZE (LOW_RETURN_REGNUM);
       int high_size = REGISTER_RAW_SIZE (HIGH_RETURN_REGNUM);
 
       if (len <= low_size)
-	memcpy (valbuf, regbuf + REGISTER_BYTE (LOW_RETURN_REGNUM), len);
+	memcpy (valbuf, &regbuf[REGISTER_BYTE (LOW_RETURN_REGNUM)], len);
       else if (len <= (low_size + high_size))
 	{
 	  memcpy (valbuf,
-		  regbuf + REGISTER_BYTE (LOW_RETURN_REGNUM),
-		  low_size);
+		  &regbuf[REGISTER_BYTE (LOW_RETURN_REGNUM)], low_size);
 	  memcpy (valbuf + low_size,
-		  regbuf + REGISTER_BYTE (HIGH_RETURN_REGNUM),
-		  len - low_size);
+		  &regbuf[REGISTER_BYTE (HIGH_RETURN_REGNUM)], len - low_size);
 	}
       else
-	error ("GDB bug: i386-tdep.c (i386_extract_return_value): Don't know how to find a return value %d bytes long", len);
-#else /* !LOW_RETURN_REGNUM */
-      memcpy (valbuf, regbuf, TYPE_LENGTH (type));
-#endif /* LOW_RETURN_REGNUM */
+	internal_error ("Cannot extract return value of %d bytes long.", len);
     }
 }
 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]