BSD C floating point bug affecting code everywhere (Really awful)
Todd Brunhoff
toddb at tekcrl.UUCP
Thu Nov 21 05:57:37 AEST 1985
The following program seems innocuous enough:
int tst[1],i;
main()
{
tst[0] = 0x20000020;
i = 0;
tst[i] += 0.0 * i;
}
However, the code generated for the "tst[i] += 0.0 * i;" is incorrect
and even causes a core dump, which is how we came to notice this. The
problem is based on the fact that if a destination operand for a
D-format floating point operation is a register, say n, then the vax
cpu uses register n and register n+1 for the result. Here's the code
generated:
...
L18:
.double 0d0.00000000000000000000e+00
.text
cvtld _i,r0 /* convert i to D-format floating point,
* putting result in r0 and r1. I believe it
* is only an accident that something is not
* in r1 to get clobbered.
*/
muld3 r0,L18,r2 /* put "0.0 * i" into r2 ... and r3 as well */
movl _i,r0
cvtld _tst[r0],r1 /* convert long to D-format. The result
* goes into r1 AND r2 thus clobbering the
* current value of r2 which was half of
* the expression "0.0 * i".
*/
addd2 r2,r1 /* Here's the worst. We are actually adding
* the top half of the previous result to its
* bottom half (although the intent was
* different). And with the particular
* constant 0x20000020, the result of the
* previous instruction put a 0x000080000
* in r2. It just happens that this is a
* reserved operand (sign bit 15 = 1, exponent
* bits 14-7 = 0), and so we got a reserved
* operand fault.
*/
We never would have known about this except for the reserved operand fault
caused by this particular constant, which means that vaxes running 4.2BSD unix
everywhere are just doing calculations flat wrong and no one knows it.
But if they get them right, it is purely by accident. The correct code
should be:
.double 0d0.00000000000000000000e+00
.text
cvtld _i,r0 /* convert i to D-format (r0,r1) */
muld3 r0,L18,r3 /* put "0.0 * i" into r3 and r4 */
movl _i,r0
cvtld _tst[r0],r1 /* convert long to D-format. The result
* goes into r1 AND r2.
*/
addd2 r3,r1 /* add D-format (r3-r4) + (r1-r2) */
I haven't looked at the code generation modules, so it may be that only
the conversion operations forget about allocating register pairs. How
about system V and Tartan's C-compiler? It is not a well-known problem
here; is it known elsewhere?
This is awful! Blech!
More information about the Net.bugs
mailing list