x86 fp fix

Alan Modra alan@linuxcare.com.au
Fri Mar 24 19:57:00 GMT 2000


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:






More information about the Gcc-patches mailing list