This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
x86 fp fix
- To: gcc-patches at gcc dot gnu dot org
- Subject: x86 fp fix
- From: Alan Modra <alan at linuxcare dot com dot au>
- Date: Sat, 25 Mar 2000 14:57:29 +1100 (EST)
- cc: rth at cygnus dot com, robertlipe at usa dot net
Intel mode fp assembly output was wrong, mainly due to a wrong
assumption about how the Unixware assembler is broken. Ian Taylor and I,
with help from Robert Lipe, thrashed out exactly how the Unixware
assembler was broken quite a while ago (so that gas would be compatible).
Please check this patch over carefully. I'm fairly sure it's right, and
it doesn't change AT&T mode output, but this function is mighty
confusing. Which is why I've added some comments. Ah, the "smells"
comment might be wrong.
Note: Until recently, intel mode gas was also wrong in this area.
--
Linuxcare. Support for the Revolution.
gcc/ChangeLog
2000-03-25 Alan Modra <alan@linuxcare.com>
* config/i386/i386.c (output_387_binary_op): Correct intel
mode assembly output. Correct Unixware assembler comment.
Document input constraints. Comment fp operations.
(UNIXWARE_COMPAT): Define. Add !UNIXWARE_COMPAT code.
--- egcs/gcc/config/i386/i386.c~ Mon Mar 20 23:35:06 2000
+++ egcs/gcc/config/i386/i386.c Sat Mar 25 11:03:24 2000
@@ -3434,6 +3434,15 @@ split_di (operands, num, lo_half, hi_hal
There is no guarantee that the operands are the same mode, as they
might be within FLOAT or FLOAT_EXTEND expressions. */
+#ifndef UNIXWARE_COMPAT
+/* Set to 1 for compatibility with brain-damaged assemblers. No-one
+ wants to fix the assemblers because that causes incompatibility
+ with gcc. No-one wants to fix gcc because that causes
+ incompatibility with assemblers... You can use the option of
+ !UNIXWARE_COMPAT if you recompile both gcc and gas this way. */
+#define UNIXWARE_COMPAT 1
+#endif
+
const char *
output_387_binary_op (insn, operands)
rtx insn;
@@ -3443,6 +3452,22 @@ output_387_binary_op (insn, operands)
rtx temp;
const char *p;
+#ifdef FP_PARANOIA
+ /* Even if we do not want to check the inputs, this documents input
+ constraints. Which helps in understanding the following code. */
+ if (STACK_REG_P (operands[0])
+ && ((REG_P (operands[1])
+ && REGNO (operands[0]) == REGNO (operands[1])
+ && (STACK_REG_P (operands[2]) || GET_CODE (operands[2]) == MEM))
+ || (REG_P (operands[2])
+ && REGNO (operands[0]) == REGNO (operands[2])
+ && (STACK_REG_P (operands[1]) || GET_CODE (operands[1]) == MEM)))
+ && (STACK_TOP_P (operands[1]) || STACK_TOP_P (operands[2])))
+ ; /* ok */
+ else
+ abort ();
+#endif
+
switch (GET_CODE (operands[3]))
{
case PLUS:
@@ -3494,6 +3519,8 @@ output_387_binary_op (insn, operands)
operands[1] = temp;
}
+ /* know operands[0] == operands[1] */
+
if (GET_CODE (operands[2]) == MEM)
{
p = "%z2\t%2";
@@ -3503,16 +3530,21 @@ output_387_binary_op (insn, operands)
if (find_regno_note (insn, REG_DEAD, REGNO (operands[2])))
{
if (STACK_TOP_P (operands[0]))
- p = "p\t{%0,%2|%2, %0}";
+ /* How is it that we are storing to a dead operand[2]?
+ This smells like we're covering for a problem
+ elsewhere. Well, maybe operands[1] is dead too.
+ gcc <= 2.8.1 didn't have this check and generated
+ assembly code that the Unixware assembler rejected. */
+ p = "p\t{%0,%2|%2, %0}"; /* st(1) = st(0) op st(1); pop */
else
- p = "p\t{%2,%0|%0, %2}";
+ p = "p\t{%2,%0|%0, %2}"; /* st(r1) = st(r1) op st(0); pop */
break;
}
if (STACK_TOP_P (operands[0]))
- p = "\t{%y2,%0|%0, %y2}";
+ p = "\t{%y2,%0|%0, %y2}"; /* st(0) = st(0) op st(r2) */
else
- p = "\t{%2,%0|%0, %2}";
+ p = "\t{%2,%0|%0, %2}"; /* st(r1) = st(r1) op st(0) */
break;
case MINUS:
@@ -3529,42 +3561,68 @@ output_387_binary_op (insn, operands)
break;
}
- if (! STACK_REG_P (operands[1]) || ! STACK_REG_P (operands[2]))
- abort ();
-
- /* Note that the Unixware assembler, and the AT&T assembler before
- that, are confusingly not reversed from Intel syntax in this
- area. */
if (find_regno_note (insn, REG_DEAD, REGNO (operands[2])))
{
+#if UNIXWARE_COMPAT
+ /* The Unixware assembler, and the AT&T assembler before
+ that, confusingly reverse the direction of the operation
+ for fsub{r} and fdiv{r} when the destination register is
+ not st(0). The Intel assembler doesn't have this brain
+ damage. Read !UNIXWARE_COMPAT to figure out what the
+ hardware really does. */
+ if (STACK_TOP_P (operands[0]))
+ p = "{p\t%0,%2|rp\t%2, %0}";
+ else
+ p = "{rp\t%2,%0|p\t%0, %2}";
+#else
if (STACK_TOP_P (operands[0]))
- p = "p\t%0,%2";
+ p = "rp\t{%0,%2|%2, %0}"; /* st(1) = st(0) op st(1); pop */
else
- p = "rp\t%2,%0";
+ p = "p\t{%2,%0|%0, %2}"; /* st(r1) = st(r1) op st(0); pop */
+#endif
break;
}
if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
{
+#if UNIXWARE_COMPAT
if (STACK_TOP_P (operands[0]))
- p = "rp\t%0,%1";
+ p = "{rp\t%0,%1|p\t%1, %0}";
else
- p = "p\t%1,%0";
+ p = "{p\t%1,%0|rp\t%0, %1}";
+#else
+ if (STACK_TOP_P (operands[0]))
+ p = "p\t{%0,%1|%1, %0}"; /* st(1) = st(1) op st(0); pop */
+ else
+ p = "rp\t{%1,%0|%0, %1}"; /* st(r2) = st(0) op st(r2); pop */
+#endif
break;
}
if (STACK_TOP_P (operands[0]))
{
if (STACK_TOP_P (operands[1]))
- p = "\t%y2,%0";
+ p = "\t{%y2,%0|%0, %y2}"; /* st(0) = st(0) op st(r2) */
else
- p = "r\t%y1,%0";
+ p = "r\t{%y1,%0|%0, %y1}"; /* st(0) = st(r1) op st(0) */
break;
}
else if (STACK_TOP_P (operands[1]))
- p = "\t%1,%0";
+ {
+#if UNIXWARE_COMPAT
+ p = "{\t%1,%0|r\t%0, %1}";
+#else
+ p = "r\t{%1,%0|%0, %1}"; /* st(r2) = st(0) op st(r2) */
+#endif
+ }
else
- p = "r\t%2,%0";
+ {
+#if UNIXWARE_COMPAT
+ p = "{r\t%2,%0|\t%0, %2}";
+#else
+ p = "\t{%2,%0|%0, %2}"; /* st(r1) = st(r1) op st(0) */
+#endif
+ }
break;
default: