This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Re: optimization problem with 1.0.3
- To: itlee at leland dot Stanford dot EDU
- Subject: Re: optimization problem with 1.0.3
- From: Craig Burley <burley at gnu dot org>
- Date: Sun, 6 Dec 1998 22:51:33 -0500 (EST)
- Cc: egcs at egcs dot cygnus dot com
- Cc: burley at gnu dot org
>Shouldn't the code generated by gcc prevent this kind of thing from
>happening?
Ideally, yes, and, in this case, it does seem to be the result of
comparing an 80-bit result to the 64-bit-chopped version of an
identical 80-bit result.
I don't know if my proposal really would address this, minimally or
otherwise.
But, if my proposal is interpreted and implemented to treat the return
value from a function like `f' in your example as an 80-bit value, and
the fstp/fld pair as a "spill", then the result would be that they'd
become fstpt/fldt instructions (80-bit) instead of fstpl/fldl (64-bit)
ones.
When I hand-modify the generated assembly code accordingly, including
changing the temporary from -20(%ebp) to -28(%ebp) and adjusting the
prologue accordingly, then assemble and run the result, the program
prints no output, meaning the numerics work as it expected.
I think that's an improvement, which is why I think my proposal would
be, perhaps, worth implementing.
However, if it doesn't address *all* such cases, which it might not
be able to, it might be more trouble to implement it now and then
cope with subsequent problems later, than to just punt until we
get a clearer picture of what the industry expects from FP implementations
in the dominant languages. (And I'm not even sure how important
the industry will see differences among languages like Java, Fortran,
C, and C++, or among processors like SPARC, Intel x86, Alpha, and Cray.
It's easy to be an FP "pedant" when the machine you work on doesn't
run much slower implementing your wishes, for example. But whatever
consensus emerges, is likely to at least try and take into account
the fact that it'll have an effect on design of FP units in new
languages, architectures, and processors for the next 20 years or
so -- which might mean the x86 will get less weight than otherwise, and
expressibility, predictability, and accuracy more weight.)
An example of a case my proposal wouldn't address is hinted at by
modifying your example to store the result in a `double' variable
before returning that variable. In that case, a flag like
`-ffloat-store', and the optimizations related to it, takes
precedence.
With `-ffloat-store', it'd work more predictably on the x86, with much
less performance.
Without it, it might work most of the time. But, if that particular
routine was inlined one place and not in another, or if it was
complicated enough that return paths differed, the compiler chose
to avoid the store/load of the variable in just one path but "spill"
it to the `double' variable in the others, or similar, it might
no longer work.
That's because, in such cases, while the compiler wouldn't be chopping
down the result *returned* by `f' when it had to spill it, that result
*itself* might change from run to run depending on whether a run happened
to execute code that had to store the computed return result through
the declared `double' variable, thus chopping it (or a value contributing
to its computation later on) anyway.
For example, change the body of `f' to something like:
double res;
res = x/3.0;
if (clock_ticks++ & 1) printf ("another second call to `f'\n");
return res;
Now, on every other call, `f' might chop `res' down to 64 bits before
restoring it to 80 bits and returning, while on the other calls, the
computed 80-bit result would get returned. (I don't know whether
egcs would make this distinction in this case -- it might save/restore
the value around the entire test, for example -- but it's an example
of the sorts of things compilers do.)
Here, at least the programmer has some insight into why the
chopping happens: he wrote a call into the midst of FP code.
We can explain that, somewhat, in documentation.
But with implicit calls, such as to run-time library routines (to
do things like complex divides, or maybe profiling), the visibility
goes down, and the surprise of having the "same" value returned
be different from call to call goes up.
And, in a language like C++, even the most innocent-looking code
can involve lots of implicit calls. I'd guess that's true for Java
as well, and it's probably true for Fortran 90 and beyond, for
most character operations in FORTRAN 77, and so on.
Yet, compiled without optimization, one would expect an 80-bit
result to *not* be computed at all, at least for a sufficiently
complicated routine. In such a case, the every-odd-invocation-is-
different behavior wouldn't be observed. It's a bit surprising,
perhaps, that this isn't already the case -- even with no optimization
turned on, egcs is optimizing the program a bit by keeping the
variable on the stack. Though that's not abnormal for egcs/gcc
technology, it would be for some other compiler technologies,
which assume everything is in memory, and registers are the
special, optimized case (egcs/gcc being the other way 'round, in a
sense).
In fact, compiling your original example *with* optimization level
6 (probably 2 or 3 is sufficient, I forget where the effective
cutoff is offhand), it runs just fine, because egcs decides it
won't print anything and just emits a null main program (though
it still emits a shortened `f', naturally, since it's a global).
Of course, that seems kind of bizarre. Compiled with less than
a given level of optimization, egcs runs the program, and it prints
a message; compiled with more optimization, it analyzes the program,
concludes it *won't* print a message, and thus optimizes the program
away entirely. But its analysis is presumably in the more "sane"
world of non-x86 FP arithmetic, and similar inconsistencies could
probably be observed without using FP at all (though I can't offhand
guess how).
Floating-point processing is a pain in the butt. It would be much
easier if the universe were a whole lot smaller, such that everything
worth counting in it fit into a 64-bit integer.
tq vm, (burley)