
[Release b]
<t4m_bug_fdivfsub>
Mon Mar 29 12:03:01 WEST 2004
First, remember that there is a traditional bug in AT&T-style assemblers that
affects all the non-commutative binary floating-point operations whose source
operand is the top of the stack (register st). Each mnemonic gets assembled to
its 'reverse' version. For example,
fsub %st, %st(1)
gets assembled to
fsubr %st, %st(1)
and so it performs the operation "st(1) := st - st(1)" (the general rule
for 80x86 arithmetic operations is to perform "dest := dest <op> src", but
reverse operations compute "dest := src <op> dest"). The ACK assembler does not
have this bug, so the equivalent operation would be
fsubr st(1), st
(recall that AT&T-style instructions follow the syntax "opcode src, dest",
while ACK assembler has the format "opcode dest, src").
The following table illustrates these differences between AT&T-style assemblers
and the ACK assembler.
AT&T ACK
----------------------------------------------------------
.text .sect .text
fdiv %st(1), %st fdiv st, st(1) ! d8 f1
fdivr %st(1), %st fdivr st, st(1) ! d8 f9
fsub %st(1), %st fsub st, st(1) ! d8 e1
fsubr %st(1), %st fsubr st, st(1) ! d8 e9
fdiv %st, %st(1) fdivr st(1), st ! dc f1
fdivr %st, %st(1) fdiv st(1), st ! dc f9
fsub %st, %st(1) fsubr st(1), st ! dc e1
fsubr %st, %st(1) fsub st(1), st ! dc e9
fdivp %st, %st(1) fdivrp st(1), st ! de f1
fdivrp %st, %st(1) fdivp st(1), st ! de f9
fsubp %st, %st(1) fsubrp st(1), st ! de e1
fsubrp %st, %st(1) fsubp st(1), st ! de e9
----------------------------------------------------------
Well, in the Release A of TenDRA4Minix, I thought *all* binary non-commutative
floating-point operations (regardless of its source operand) had this bug, and
so I complemented the value of the flag 'rev' inside the function fopr in file
src/installers/80x86/minix/instr386.c. This is, of course, wrong [1]: the flag
must be complemented only for the above-mentioned instructions. Now I have
decided to restore the original code of function fopr and invert the flag 'rev'
at the invocations of fopr inside function fl_binop.
[1] The program fdivcase3.C triggers one of these wrong cases (tst = 3).
I have gone through a whole revision of function fl_binop. Below, numbers
in square brackets indicate line numbers. The word OK means the flag 'rev'
needn't be reversed with respect to src/installers/80x86/common/instr386.c,
and the word COMPLEMENT means that the flag 'rev' has been reversed in the
Minix version of file instr386.c (for the Release B of TenDRA4Minix).
We will use division for the purposes of illustration. The syntax used in the
example instructions is that of the ACK assembler. Note that the calls to
fl_binop from function codec (in file src/installers/80x86/common/codec.c)
have their arguments reversed,
arg1 = mw(bro(son(e)), 0) (divisor)
arg2 = mw(son(e), 0) (dividend)
(see src/installers/common/construct/tags.h), and so function fl_binop has to
emit code to compute arg2/arg1.
[6411] arg1 = st(1), arg2 = st, "fdivrp st(1), st"
st(1) := st / st(1) := arg2 / arg1. COMPLEMENT.
[6432] tst = (6|7), (arg1, arg2, dest) = (mem, mem, reg|mem)
arg1 = mem, arg2 = st, fopm (sha, op, 0, arg1) = "fdiv arg1"
st := st / arg1 = arg2 / arg1. OK.
[6439] tst = 4, (arg1, arg2, dest) = (mem, reg, reg)
[6443] arg1 = mem, arg2 = dest = st, fopm (sha, op, 0, arg1) = "fdiv arg1"
st := st / arg1 = arg2 / arg1. OK.
[6448] move (sha, arg1, flstack);
arg1 = st, arg2 = dest = st(i)
fopr (op, 0, flstack, dest, 1) = "fdivp st(i), st"
st(i) := st(i) / st = arg2 / arg1. COMPLEMENT.
[6453] tst = 5, (arg1, arg2, dest) = (mem, reg, mem) | tst = 4 && arg2 != dest
[6456] arg1 = mem, arg2 = st, fopm (sha, op, 0, arg1) = "fdiv arg1"
st := st / arg1 = arg2 / arg1. OK.
[6463] move (sha, arg1, flstack);
arg1 = st, arg2 = st(i) != dest
fopr (op, 1, arg2, flstack, 0) = "fdivr st, st(i)"
st := st(i) / st = arg2 / arg1. OK.
[6467] tst = 2, (arg1, arg2, dest) = (reg, mem, reg)
[6471] arg1 = dest = st, arg2 = mem, fopm (sha, op, 1, arg2) = "fdivr arg2"
st := arg2 / st = arg2 / arg1. OK.
[6476] move (sha, arg2, flstack);
arg1 = dest = st(i), arg2 = st
fopr (op, 1, flstack, dest, 1) = "fdivrp st(i), st"
st(i) := st / st(i) = arg2 / arg1. COMPLEMENT.
[6481] tst = 3, (arg1, arg2, dest) = (reg, mem, mem) | tst = 2 && arg1 != dest
[6483] arg1 = st, arg2 = mem, fopm (sha, op, 1, arg2) = "fdivr arg2"
st := arg2 / st = arg2 / arg1. OK.
[6490] move (sha, arg2, flstack);
arg1 = st(i) != dest, arg2 = st
fopr (op, 0, arg1, flstack, 0) = "fdiv st, st(i)"
st := st / st(i) = arg2 / arg1. OK.
[6494] tst = (0|1), (arg1, arg2, dest) = (reg, reg, reg|mem)
[6507] arg1 = st, arg2 = dest = st(i)
fopr(op, 0, flstack, arg2, 1) = "fdivp st(i), st"
st(i) := st(i) / st = arg2 / arg1. COMPLEMENT.
[6511] arg1 = dest = st(i), arg2 = st
fopr(op, 1, flstack, arg1, 1) = "fdivrp st(i), st"
st(i) := st / st(i) = arg2 / arg1. COMPLEMENT.
[6522] arg1 = st, arg2 = st(i)
fopr (op, 1, arg2, flstack, 0) = "fdivr st, st(i)"
st := st(i) / st = arg2 / arg1. OK.
[6528] arg1 = st, arg2 = st(i)
fopr (op, 0, flstack, arg2, 1) = "fdivp st(i), st"
st(i) := st(i) / st = arg2 / arg1. COMPLEMENT.
[6540] arg1 = st(i), arg2 = st
fopr (op, 0, arg1, flstack, 0) = "fdiv st, st(i)"
st := st / st(i) = arg2 / arg1. OK.
[6546] arg1 = st(i), arg2 = st
fopr (op, 1, flstack, arg1, 1) = "fdivrp st(i), st"
st(i) := st / st(i) = arg2 / arg1. COMPLEMENT.
[6551] arg1 = st(i), arg2 = st
fopr(op, 0, arg1, flstack, 0) = "fdiv st, st(i)"
st := st / st(i) = arg2 / arg1. OK.
[6561] arg1 = st(i), arg2 = st
fopr (op, 0, arg1, flstack, 0) = "fdiv st, st(i)"
st := st / st(i) = arg2 / arg1. OK.
Note: there is a call to fl_binop in the same file (instr386.c), in function
fl_multop, but fl_multop is only invoked from function codec with the opcodes
fplus_tag, fmult_tag (commutative operations).