SourceForge.net Logo

[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).