Restructuring i386_extract_return_value
Mark Kettenis
kettenis@wins.uva.nl
Thu Mar 9 14:42:00 GMT 2000
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)
- ®buf[REGISTER_BYTE (FPDATA_REGNUM)],
-#else /* !FPDATA_REGNUM */
- ®buf[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, ®buf[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,
+ ®buf[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, ®buf[REGISTER_BYTE (LOW_RETURN_REGNUM)], len);
else if (len <= (low_size + high_size))
{
memcpy (valbuf,
- regbuf + REGISTER_BYTE (LOW_RETURN_REGNUM),
- low_size);
+ ®buf[REGISTER_BYTE (LOW_RETURN_REGNUM)], low_size);
memcpy (valbuf + low_size,
- regbuf + REGISTER_BYTE (HIGH_RETURN_REGNUM),
- len - low_size);
+ ®buf[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);
}
}
More information about the Gdb
mailing list