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