This is the mail archive of the
gdb@sourceware.cygnus.com
mailing list for the GDB project.
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