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]

Re: Unifying the x86 FPU register sets


   From: Jim Blandy <jimb@cygnus.com>
   Date: 21 Oct 1999 18:51:25 -0500

   I agree that the floating-point stuff in tm-linux.h is not the way it
   should be.  Yes, the way LD_I387 and the conversion stuff are defined
   isn't correct.  But for now, we should focus on getting tm-i386.h the
   way we all want it.  If we do that right, then tm-linux.h will be
   straightforward to fix.

Indeed.  As my patch shows almost all FP definitions can be removed :-).

   I'm having a hard time following a lot of the discussion about
   floating-point formats.  Here is my present understanding of
   how things work; please correct me if I say something wrong.

You're not the only one.  Took me a little while to figure it out and
it looks as if almost no maintainer of a specific port has managed to
figure it out.  I hope my comments here will help though.

   There are three forms floating-point values can take on as they pass
   through GDB:
   - the register file form
   - the `struct value' form
   - the host DOUBLEST form

There also is the host `double' which comes into play if the host
HAS_LONG_DOUBLE, but not PRINTF_HAS_LONG_DOUBLE.  Such a host will
convert a DOUBLEST to `double' before printing.

   The register file form should be bit-for-bit identical with the way
   the processor stores them, because the register file's role is to be a
   direct reflection of the processor's state.  The REGISTER_RAW_SIZE
   macro gives the number of bytes a floating-point value occupies in
   this form.  GDB has no macro describing the types of registers in the
   register file.

Indeed.  REGISTER_RAW_SIZE is all that matters and should be 10 for
the FP data registers on all x86 targets.

   GDB uses the `struct value' form for variable values, intermediate
   values in expression evaluation, and so on.  REGISTER_VIRTUAL_TYPE
   gives the type of this form; REGISTER_CONVERTIBLE,
   REGISTER_CONVERT_TO_VIRTUAL, and REGISTER_CONVERT_TO_RAW control the
   conversion between the register file form and this form, done by
   value_of_register, value_from_register, and value_assign.

   GDB converts a `struct value' to the host DOUBLEST form whenever it
   actually wants to operate on a floating-point value.  This conversion
   is controlled by {TARGET,HOST}_LONG_DOUBLE_FORMAT and
   TARGET_{EXTRACT,STORE}_FLOATING, and carried out by functions like
   value_as_double, unpack_double, extract_floating, and
   store_floating.

TARGET_{EXTRACT,STORE}_FLOATING were introduced as part of the
premature Linux/x86 floating point merge.  They are only used by the Linux
code, for which they are unnecessary, and I believe they are the wrong
approach.  Once the Linux/x86 port has been fixed we should remove all
traces of these macros.

There are some other quantities that are relevant:
TARGET_LONG_DOUBLE_BIT and `sizeof (long double)'.  Basically GDB
assumes that it can do an accurate conversion if
TARGET_LONG_DOUBLE_BIT matches `sizeof (long double)'.  If in addition
TARGET_LONG_DOUBLE_FORMAT == HOST_LONG_DOUBLE_FORMAT, it simply copies
bytes.  Otherwise the function you mention are used.

   (This last conversion is controversial, since it loses information;
   ideally, GDB would perform the operations in the target's format,
   using some software implementation of IEEE floating point arithmetic.)

I believe that GDB will simply fail if DOUBLEST is not equivalent with
the target `long double'.  The only lossy conversion seems to happen
when the host cannot print its own `long double'.

   It's worth noting that the first conversions, to and from the `struct
   value' form, never need to be lossy, because `struct value' can hold
   the value in raw target format.  REGISTER_VIRTUAL_TYPE just needs to
   correctly describe the registers.

   So, here's my understanding of folks' suggestions about tm-i386.h:

   - REGISTER_VIRTUAL_TYPE should be some type from tm-i387.c, dedicated
     to describing FPU registers.  That way, it's not dependent on today's
     compiler's interpretation of `long double'.

Although introducing a new type looks like a good idea, there are some
problems.  The way extract_floating and store_floating work right now,
only the size of the type use used to sitinguish floating point
types.  If the size of our new type matches `sizeof (DOUBLEST)' only
conversion from/to TARGET_LONG_DOUBLE_FORMAT is tried which might not
be the right thing.

   - REGISTER_VIRTUAL_TYPE should be 12 bytes long, and
     REGISTER_CONVERT_TO_{VIRTUAL,RAW} should simply account for the
     position of the 10-byte value within the 12-byte space.

This is what my patch does, except that if the host doesn't have an
adequate `long double' type I convert to TARGET_DOUBLE_FORMAT.  I
think this is useful since `long double' isn't widely used and for
ordinary `double' arithmetic on the x86 this conversion is exactly
what happens if you pop a value from the FP stack.

     It makes me a little uncomfortable to have REGISTER_VIRTUAL_TYPE
     specify a 12-byte size, while floatformat_i387_ext is a ten-byte
     format, but I think it should work fine.

I think you could define floatformat_i387_ext as a 12-byte format.
The padding is at the end, and never touched by the floatformat
functions.  You would probably end up with floatformat_i960_ext (I
didn't verify this).

   - {TARGET,HOST}_LONG_DOUBLE_{FORMAT,BITS} should be defined as
     appropriate in the right tm-*.h and xm-*.h files, not in tm-i386.h.
     The latter doesn't know what compiler you're using, and so can't say
     how long its types are.

You never know what the target compiler is.  You can only hope that
all compilers on the target have the same idea of a `long double'.  I
looked at GCC and it seems that on all x86 ports `long double' is the
i387 extended floating point format and `sizeof (long double)' is 12,
with the exception of OSF/1 (where `long double' is the same as
`double').  That's why I put TARGET_LONG_DOUBLE_{BIT,FORMAT} into
tm-i386.h.

There is no HOST_LONG_DOUBLE_BIT(S).  Defining HOST_LONG_DOUBLE_FORMAT
where appropriate is probably a good idea, but AFAICT it is only used
to bypass conversion.

   The implications carry on to Linux as follows:

   - Once those definitions are corrected in tm-linux.h, the
     TARGET_{EXTRACT,STORE}_FLOATING macros will never be used, because
     the earlier clauses in extract_floating and store_floating will
     apply, so we can delete the TARGET_{EXTRACT,STORE}_FLOATING
     definitions from tm-linux.h.

   - But tm-linux.h is the only target in GDB that defines them, so we
     can remove all references to them from GDB completely.

   It looks like Mark's patch has already done this.  I'll take a look.

It doesn't remove the traces of TARGET_{EXTRACT,STORE}_FLOATING from
the generic GDB code yet.

Mark

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