This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Re: FWD: FLOATING-POINT CONSISTENCY, -FFLOAT-STORE, AND X86
- To: hjstein at bfr dot co dot il
- Subject: Re: FWD: FLOATING-POINT CONSISTENCY, -FFLOAT-STORE, AND X86
- From: Craig Burley <burley at gnu dot org>
- Date: Wed, 16 Dec 1998 13:34:48 -0500 (EST)
- Cc: bosch at gnat dot com, hjstein at bfr dot co dot il, moshier at mediaone dot net, egcs at cygnus dot com, tprince at cat dot e-mail dot com
- Cc: burley at gnu dot org
>For example, suppose I have code like:
>
> x = a*b;
> y = c*d;
> z = x+y;
>
>I've been under the (worst case) assumption that any combination of a,
>b, c & d might be in FP registers, that the multiplies might be done
>using register/register or memory/register multiplies, and that x and
>y might be gotten either from memory or from registers.
>
>Is this the case?
Yes, conceptually. I don't know whether gcc does this currently,
but any current or future CPU chip might mandate that maximum
optimization is achieved by being able to make such decisions on
arbitrary bases -- including replicated that code sequence into
multiple ones, each with different sets of decisions, with the
choice of entry depending on which code path preceded and/or followed
the snippet, to get minimal cache re-loading.
In other words, the first time the above code is executed, it puts
everything in registers; the second time, some things spill to memory;
and so on.
In a sufficiently complicated CPU, it might even be profitable to
*simultaneously* execute multiple instances of the above snippet
implementing different decisions, and choose which results to use
at the last moment depending on preceding or subsequent decisions
made by the code, again, for optimization. (If anyone doesn't get
or believe this, don't worry -- it's quite unlikely we'll ever see
such a CPU that also has wider FP registers than normal FP operations
like the x86. But I might have to eat my words someday.)
>Or is it the case that values always go into FP registers first, and
>are always manipulated from FP registers except if we run out, in
>which case a spill is done?
I think this relates to -fforce-mem. Basically it's up to the compiler.
Some issues might be more general than the x86 (the x86 might mandate
some choices due to instruction set, others due to optimization
requirements), and my proposal relates to the general issue of
completely spilling all registers.
>In particular, is it never the case that
>something would get stored back into memory (freeing up an FP
>register), and then later loaded back into an FP. For example, in the
>above (after adding enough computations), could x get computed, stored
>back into &x and then later loaded from &x to compute z?
Assuming &x doesn't have the C meaning, but refers to a temporary
copy of x, then, yes, in fact gcc does this now. It does it trivially
for function return values (causing f(x) < f(y) to not imply f(y) > f(x)),
with more difficulty for straight code (I have an example I made up).
>If my original assumption is correct, then I think my objections still
>hold - spilling in extended precision will help a little but not
>completely.
It'll at least get rid of a big, obvious problem, but we might well
find problems remain. I'd rather we study the problem up front so
we understand and document the remaining problems, and have some
idea of what solutions to propose, if not offer, but I don't think
we should ask people to *not* fix gcc as I propose until we have
accomplished this (as long as we have an option to get the current
behavior).
>If not - if it's really the case that everything always stays in FP
>registers except for spills, then I agree that doing 80 bit spills
>will largely prevent weird numerical values. It would effectively
>make 80 bitness contagious, which should be sufficient even for
>comparisons to act reasonably (assuming constants are also computed in
>80 bits, to prevent 1.0/3.0 from not equalling x/y after x=1.0;
>y=3.0).
My impression is that, because -fno-force-mem is now the default, most
*computations* are 80-bit, even if the operands start out as 64-bit.
So, I think gcc is closer to this latter case of yours than the former,
although I'm uncomfortable saying that "everything always stays in
FP registers except for spills", not having studied the issues.
At least, for the sample program I tried to write (and compiled without
optimization), it was *hard* to get the compiler to do *anything*
outside of FP registers, AFAICT, except of course the loads/stores
of the variables, and the final store of the result. Only by making
this straight-line code very complicated could I convince it to
spill any intermediate results.
Therefore, I think the stuff we'll tend to "fix" by adopting my
proposal will be function return values, offhand, not by what
we think of as "normal" spills -- because it seems that normal
spills in straight-line code are pretty rare.
That's also why I don't think we'll see a big performance hit on
most code, and, of the code that *does* see it, I would't be
surprised if it turned out that a substantial portion of it was
previously producing inadequately correct results in its "fast" mode.
(This seems to be a recurring theme throughout the industry. I remember
back when I went through, then "inflicted" on others, various iterations
of the "we will now default to rejecting implicit declarations" paradigm,
e.g. IMPLICIT NONE in Fortran. Each time, the opposing arguments were
things like "but that'll slow me down", "I already know what I'm doing",
"what about all the existing, working code", but once the decision was
executed, the results slowly convinced most everyone that the bugs found
in existing production code were worth finding, even at the additional
costs. The mind-set that claims partial spills are an okay default is
the same that claimed, 15 years ago, that header (#include) files and
prototypes were a waste of time and energy, suitable only for the
newbie programmer. Not entirely false, but missing the big, and growing,
picture, IMO.)
tq vm, (burley)