*** empty log message ***
authorJim Wilson <wilson@gcc.gnu.org>
Tue, 19 May 1992 23:57:13 +0000 (16:57 -0700)
committerJim Wilson <wilson@gcc.gnu.org>
Tue, 19 May 1992 23:57:13 +0000 (16:57 -0700)
From-SVN: r1021

gcc/config/sparc/sparc.c
gcc/config/sparc/sparc.h
gcc/config/sparc/sparc.md

index f9eeef8c00cb2820d67b7ca159b095eb5247bf5d..a875dba5e3c2a828dcc81d3597802af22d4b6be7 100644 (file)
@@ -955,35 +955,137 @@ singlemove_string (operands)
   return "mov %1,%0";
 }
 \f
+/* Return non-zero if it is OK to assume that the given memory operand is
+   aligned at least to a 8-byte boundary.  This should only be called
+   for memory accesses whose size is 8 bytes or larger.  */
+
+static int
+mem_aligned_8 (mem)
+     register rtx mem;
+{
+  register rtx addr;
+  register rtx base;
+  register rtx offset;
+
+  if (GET_CODE (mem) != MEM)
+    abort ();  /* It's gotta be a MEM! */
+
+  addr = XEXP (mem, 0);
+
+#if 1
+  /* Now that all misaligned double parms are copied on function entry,
+     we can assume any 64-bit object is 64-bit aligned.  */
+
+  /* See what register we use in the address.  */
+  base = 0;
+  if (GET_CODE (addr) == PLUS)
+    {
+      if (GET_CODE (XEXP (addr, 0)) == REG
+         && GET_CODE (XEXP (addr, 1)) == CONST_INT)
+       {
+         base = XEXP (addr, 0);
+         offset = XEXP (addr, 1);
+       }
+    }
+  else if (GET_CODE (addr) == REG)
+    {
+      base = addr;
+      offset = const0_rtx;
+    }
+
+  /* If it's the stack or frame pointer, check offset alignment.
+     We can have improper aligment in the function entry code.  */
+  if (base
+      && (REGNO (base) == FRAME_POINTER_REGNUM
+         || REGNO (base) == STACK_POINTER_REGNUM))
+    {
+      if ((INTVAL (offset) & 0x7) == 0)
+       return 1;
+    }
+  else
+    /* Anything else, we know is properly aligned.  */
+    return 1;
+#else
+  /* If the operand is known to have been allocated in static storage, then
+     it must be aligned.  */
+
+  if (CONSTANT_P (addr) || GET_CODE (addr) == LO_SUM)
+    return 1;
+
+  base = 0;
+  if (GET_CODE (addr) == PLUS)
+    {
+      if (GET_CODE (XEXP (addr, 0)) == REG
+          && GET_CODE (XEXP (addr, 1)) == CONST_INT)
+        {
+          base = XEXP (addr, 0);
+          offset = XEXP (addr, 1);
+        }
+    }
+  else if (GET_CODE (addr) == REG)
+    {
+      base = addr;
+      offset = const0_rtx;
+    }
+
+  /* Trust round enough offsets from the stack or frame pointer.
+     If TARGET_HOPE_ALIGN, trust round enough offset from any register.
+     If it is obviously unaligned, don't ever return true.  */
+  if (base
+      && (REGNO (base) == FRAME_POINTER_REGNUM
+          || REGNO (base) == STACK_POINTER_REGNUM
+         || TARGET_HOPE_ALIGN))
+    {
+      if ((INTVAL (offset) & 0x7) == 0)
+       return 1;
+    }
+  /* Otherwise, we can assume that an access is aligned if it is to an
+     aggregate.  Also, if TARGET_HOPE_ALIGN, then assume everything that isn't
+     obviously unaligned is aligned.  */
+  else if (MEM_IN_STRUCT_P (mem) || TARGET_HOPE_ALIGN)
+    return 1;
+#endif
+
+  /* An obviously unaligned address.  */
+  return 0;
+}
+
+enum optype { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP };
+
 /* Output assembler code to perform a doubleword move insn
-   with operands OPERANDS.  */
+   with operands OPERANDS.  This is very similar to the following
+   output_move_quad function.  */
 
 char *
 output_move_double (operands)
      rtx *operands;
 {
-  enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
+  register rtx op0 = operands[0];
+  register rtx op1 = operands[1];
+  register enum optype optype0;
+  register enum optype optype1;
   rtx latehalf[2];
-  rtx addreg0 = 0, addreg1 = 0;
+  rtx addreg0 = 0;
+  rtx addreg1 = 0;
 
   /* First classify both operands.  */
 
-  if (REG_P (operands[0]))
+  if (REG_P (op0))
     optype0 = REGOP;
-  else if (offsettable_memref_p (operands[0]))
+  else if (offsettable_memref_p (op0))
     optype0 = OFFSOP;
-  else if (GET_CODE (operands[0]) == MEM)
+  else if (GET_CODE (op0) == MEM)
     optype0 = MEMOP;
   else
     optype0 = RNDOP;
 
-  if (REG_P (operands[1]))
+  if (REG_P (op1))
     optype1 = REGOP;
-  else if (CONSTANT_P (operands[1]))
+  else if (CONSTANT_P (op1))
     optype1 = CNSTOP;
-  else if (offsettable_memref_p (operands[1]))
+  else if (offsettable_memref_p (op1))
     optype1 = OFFSOP;
-  else if (GET_CODE (operands[1]) == MEM)
+  else if (GET_CODE (op1) == MEM)
     optype1 = MEMOP;
   else
     optype1 = RNDOP;
@@ -992,180 +1094,76 @@ output_move_double (operands)
      supposed to allow to happen.  Abort if we get one,
      because generating code for these cases is painful.  */
 
-  if (optype0 == RNDOP || optype1 == RNDOP)
+  if (optype0 == RNDOP || optype1 == RNDOP
+      || (optype0 == MEM && optype1 == MEM))
     abort ();
 
   /* If an operand is an unoffsettable memory ref, find a register
      we can increment temporarily to make it refer to the second word.  */
 
   if (optype0 == MEMOP)
-    addreg0 = find_addr_reg (XEXP (operands[0], 0));
+    addreg0 = find_addr_reg (XEXP (op0, 0));
 
   if (optype1 == MEMOP)
-    addreg1 = find_addr_reg (XEXP (operands[1], 0));
+    addreg1 = find_addr_reg (XEXP (op1, 0));
 
   /* Ok, we can do one word at a time.
-     Normally we do the low-numbered word first,
-     but if either operand is autodecrementing then we
-     do the high-numbered word first.
-
-     In either case, set up in LATEHALF the operands to use for the
+     Set up in LATEHALF the operands to use for the
      high-numbered (least significant) word and in some cases alter the
      operands in OPERANDS to be suitable for the low-numbered word.  */
 
   if (optype0 == REGOP)
-    latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
+    latehalf[0] = gen_rtx (REG, SImode, REGNO (op0) + 1);
   else if (optype0 == OFFSOP)
-    latehalf[0] = adj_offsettable_operand (operands[0], 4);
+    latehalf[0] = adj_offsettable_operand (op0, 4);
   else
-    latehalf[0] = operands[0];
+    latehalf[0] = op0;
 
   if (optype1 == REGOP)
-    latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
+    latehalf[1] = gen_rtx (REG, SImode, REGNO (op1) + 1);
   else if (optype1 == OFFSOP)
-    latehalf[1] = adj_offsettable_operand (operands[1], 4);
+    latehalf[1] = adj_offsettable_operand (op1, 4);
   else if (optype1 == CNSTOP)
-    split_double (operands[1], &operands[1], &latehalf[1]);
+    split_double (op1, &operands[1], &latehalf[1]);
   else
-    latehalf[1] = operands[1];
-
-  /* If the first move would clobber the source of the second one,
-     do them in the other order.
-
-     RMS says "This happens only for registers;
-     such overlap can't happen in memory unless the user explicitly
-     sets it up, and that is an undefined circumstance."
+    latehalf[1] = op1;
 
-     but it happens on the sparc when loading parameter registers,
-     so I am going to define that circumstance, and make it work
-     as expected.  */
-
-  /* Easy case: try moving both words at once.  */
-  /* First check for moving between an even/odd register pair
-     and a memory location.  */
+  /* Easy case: try moving both words at once.  Check for moving between
+     an even/odd register pair and a memory location.  */
   if ((optype0 == REGOP && optype1 != REGOP && optype1 != CNSTOP
-       && (REGNO (operands[0]) & 1) == 0)
+       && (REGNO (op0) & 1) == 0)
       || (optype0 != REGOP && optype0 != CNSTOP && optype1 == REGOP
-         && (REGNO (operands[1]) & 1) == 0))
+         && (REGNO (op1) & 1) == 0))
     {
-      /* Now that all misaligned double parms are copied
-        on function entry, we can assume any 64-bit object
-        is 64-bit aligned.  */
-#if 1
-      rtx addr;
-      rtx base, offset;
+      register rtx mem;
 
       if (optype0 == REGOP)
-       addr = operands[1];
+       mem = op1;
       else
-       addr = operands[0];
+       mem = op0;
 
-      /* See what register we use in the address.  */
-      base = 0;
-      if (GET_CODE (XEXP (addr, 0)) == PLUS)
-       {
-         rtx temp = XEXP (addr, 0);
-         if (GET_CODE (XEXP (temp, 0)) == REG
-             && GET_CODE (XEXP (temp, 1)) == CONST_INT)
-           base = XEXP (temp, 0), offset = XEXP (temp, 1);
-       }
-      else if (GET_CODE (XEXP (addr, 0)) == REG)
-       base = XEXP (addr, 0), offset = const0_rtx;
-
-      /* If it's the  stack or frame pointer, check offset alignment.
-        We can have improper aligment in the function entry code.  */
-      if (base
-         && (REGNO (base) == FRAME_POINTER_REGNUM
-             || REGNO (base) == STACK_POINTER_REGNUM))
-       {
-         if ((INTVAL (offset) & 0x7) == 0)
-           return (addr == operands[1] ? "ldd %1,%0" : "std %1,%0");
-       }
-      else
-       /* Anything else, we know is properly aligned.  */
-       return (addr == operands[1] ? "ldd %1,%0" : "std %1,%0");
-#else
-      /* This old code is preserved in case we ever need
-        it for Fortran.  It won't be complete right;
-        In Fortran, doubles can be just 32-bit aligned
-        even in global variables and arrays.  */
-      rtx addr;
-      rtx base, offset;
-
-      if (optype0 == REGOP)
-       addr = operands[1];
-      else
-       addr = operands[0];
-
-      /* Now see if we can trust the address to be 8-byte aligned.
-        Trust double-precision floats in global variables.  */
-
-      if (GET_CODE (XEXP (addr, 0)) == LO_SUM && GET_MODE (addr) == DFmode)
-       return (addr == operands[1] ? "ldd %1,%0" : "std %1,%0");
-
-      base = 0;
-      if (GET_CODE (XEXP (addr, 0)) == PLUS)
-       {
-         rtx temp = XEXP (addr, 0);
-         if (GET_CODE (XEXP (temp, 0)) == REG
-             && GET_CODE (XEXP (temp, 1)) == CONST_INT)
-           base = XEXP (temp, 0), offset = XEXP (temp, 1);
-       }
-      else if (GET_CODE (XEXP (addr, 0)) == REG)
-       base = XEXP (addr, 0), offset = const0_rtx;
-
-      /* Trust round enough offsets from the stack or frame pointer.
-        If TARGET_HOPE_ALIGN, trust round enough offset from any register
-        for DFmode loads.  If it is obviously unaligned, don't ever
-        generate ldd or std.  */
-      if (base
-         && (REGNO (base) == FRAME_POINTER_REGNUM
-             || REGNO (base) == STACK_POINTER_REGNUM
-             || (TARGET_HOPE_ALIGN && GET_MODE (addr) == DFmode)))
-       {
-         if ((INTVAL (offset) & 0x7) == 0)
-           return (addr == operands[1] ? "ldd %1,%0" : "std %1,%0");
-       }
-      /* We know structs not on the stack are properly aligned.  Since a
-        double asks for 8-byte alignment, we know it must have got that
-        if it is in a struct.  But a DImode need not be 8-byte aligned,
-        because it could be a struct containing two ints or pointers.
-        Hence, a constant DFmode address will always be 8-byte aligned.
-        Any DFmode access inside a struct will always be aligned.
-        If TARGET_HOPE_ALIGN, then assume all doubles are aligned even if this
-        is not a constant address.  */
-      else if (GET_MODE (addr) == DFmode
-              && (CONSTANT_P (XEXP (addr, 0))
-                  || MEM_IN_STRUCT_P (addr)
-                  || TARGET_HOPE_ALIGN))
-       return (addr == operands[1] ? "ldd %1,%0" : "std %1,%0");
-#endif /* 0 */
+      if (mem_aligned_8 (mem))
+       return (mem == op1 ? "ldd %1,%0" : "std %1,%0");
     }
 
+  /* If the first move would clobber the source of the second one,
+     do them in the other order.  */
+
+  /* Overlapping registers.  */
   if (optype0 == REGOP && optype1 == REGOP
-      && REGNO (operands[0]) == REGNO (latehalf[1]))
+      && REGNO (op0) == REGNO (latehalf[1]))
     {
-      /* Make any unoffsettable addresses point at high-numbered word.  */
-      if (addreg0)
-       output_asm_insn ("add %0,0x4,%0", &addreg0);
-      if (addreg1)
-       output_asm_insn ("add %0,0x4,%0", &addreg1);
-
       /* Do that word.  */
       output_asm_insn (singlemove_string (latehalf), latehalf);
-
-      /* Undo the adds we just did.  */
-      if (addreg0)
-       output_asm_insn ("add %0,-0x4,%0", &addreg0);
-      if (addreg1)
-       output_asm_insn ("add %0,-0x4,%0", &addreg1);
-
       /* Do low-numbered word.  */
       return singlemove_string (operands);
     }
+  /* Loading into a register which overlaps a register used in the address.  */
   else if (optype0 == REGOP && optype1 != REGOP
-          && reg_overlap_mentioned_p (operands[0], operands[1]))
+          && reg_overlap_mentioned_p (op0, op1))
     {
+      /* ??? This fails if the address is a double register address, each
+        of which is clobbered by operand 0.  */
       /* Do the late half first.  */
       output_asm_insn (singlemove_string (latehalf), latehalf);
       /* Then clobber.  */
@@ -1193,8 +1191,217 @@ output_move_double (operands)
 
   return "";
 }
+
+/* Output assembler code to perform a quadword move insn
+   with operands OPERANDS.  This is very similar to the preceeding
+   output_move_double function.  */
+
+char *
+output_move_quad (operands)
+     rtx *operands;
+{
+  register rtx op0 = operands[0];
+  register rtx op1 = operands[1];
+  register enum optype optype0;
+  register enum optype optype1;
+  rtx wordpart[4][2];
+  rtx addreg0 = 0;
+  rtx addreg1 = 0;
+
+  /* First classify both operands.  */
+
+  if (REG_P (op0))
+    optype0 = REGOP;
+  else if (offsettable_memref_p (op0))
+    optype0 = OFFSOP;
+  else if (GET_CODE (op0) == MEM)
+    optype0 = MEMOP;
+  else
+    optype0 = RNDOP;
+
+  if (REG_P (op1))
+    optype1 = REGOP;
+  else if (CONSTANT_P (op1))
+    optype1 = CNSTOP;
+  else if (offsettable_memref_p (op1))
+    optype1 = OFFSOP;
+  else if (GET_CODE (op1) == MEM)
+    optype1 = MEMOP;
+  else
+    optype1 = RNDOP;
+
+  /* Check for the cases that the operand constraints are not
+     supposed to allow to happen.  Abort if we get one,
+     because generating code for these cases is painful.  */
+
+  if (optype0 == RNDOP || optype1 == RNDOP
+      || (optype0 == MEM && optype1 == MEM))
+    abort ();
+
+  /* If an operand is an unoffsettable memory ref, find a register
+     we can increment temporarily to make it refer to the later words.  */
+
+  if (optype0 == MEMOP)
+    addreg0 = find_addr_reg (XEXP (op0, 0));
+
+  if (optype1 == MEMOP)
+    addreg1 = find_addr_reg (XEXP (op1, 0));
+
+  /* Ok, we can do one word at a time.
+     Set up in wordpart the operands to use for each word of the arguments.  */
+
+  if (optype0 == REGOP)
+    {
+      wordpart[0][0] = gen_rtx (REG, SImode, REGNO (op0) + 0);
+      wordpart[1][0] = gen_rtx (REG, SImode, REGNO (op0) + 1);
+      wordpart[2][0] = gen_rtx (REG, SImode, REGNO (op0) + 2);
+      wordpart[3][0] = gen_rtx (REG, SImode, REGNO (op0) + 3);
+    }
+  else if (optype0 == OFFSOP)
+    {
+      wordpart[0][0] = adj_offsettable_operand (op0, 0);
+      wordpart[1][0] = adj_offsettable_operand (op0, 4);
+      wordpart[2][0] = adj_offsettable_operand (op0, 8);
+      wordpart[3][0] = adj_offsettable_operand (op0, 12);
+    }
+  else
+    {
+      wordpart[0][0] = op0;
+      wordpart[1][0] = op0;
+      wordpart[2][0] = op0;
+      wordpart[3][0] = op0;
+    }
+
+  if (optype1 == REGOP)
+    {
+      wordpart[0][1] = gen_rtx (REG, SImode, REGNO (op1) + 0);
+      wordpart[1][1] = gen_rtx (REG, SImode, REGNO (op1) + 1);
+      wordpart[2][1] = gen_rtx (REG, SImode, REGNO (op1) + 2);
+      wordpart[3][1] = gen_rtx (REG, SImode, REGNO (op1) + 3);
+    }
+  else if (optype1 == OFFSOP)
+    {
+      wordpart[0][1] = adj_offsettable_operand (op1, 0);
+      wordpart[1][1] = adj_offsettable_operand (op1, 4);
+      wordpart[2][1] = adj_offsettable_operand (op1, 8);
+      wordpart[3][1] = adj_offsettable_operand (op1, 12);
+    }
+  else if (optype1 == CNSTOP)
+    {
+      /* This case isn't implemented yet, because there is no internal
+        representation for quad-word constants, and there is no split_quad
+        function.  */
+#if 0
+      split_quad (op1, &wordpart[0][1], &wordpart[1][1],
+                 &wordpart[2][1], &wordpart[3][1]);
+#else
+      abort ();
+#endif
+    }
+  else
+    {
+      wordpart[0][1] = op1;
+      wordpart[1][1] = op1;
+      wordpart[2][1] = op1;
+      wordpart[3][1] = op1;
+    }
+
+  /* Easy case: try moving the quad as two pairs.  Check for moving between
+     an even/odd register pair and a memory location.  */
+  /* ??? Should also handle the case of non-offsettable addresses here.
+     We can at least do the first pair as a ldd/std, and then do the third
+     and fourth words individually.  */
+  if ((optype0 == REGOP && optype1 == OFFSOP && (REGNO (op0) & 1) == 0)
+      || (optype0 == OFFSOP && optype1 == REGOP && (REGNO (op1) & 1) == 0))
+    {
+      rtx mem;
+
+      if (optype0 == REGOP)
+       mem = op1;
+      else
+       mem = op0;
+
+      if (mem_aligned_8 (mem))
+       {
+         operands[2] = adj_offsettable_operand (mem, 8);
+         if (mem == op1)
+           return "ldd %1,%0;ldd %2,%S0";
+         else
+           return "std %1,%0;std %S1,%2";
+       }
+    }
+
+  /* If the first move would clobber the source of the second one,
+     do them in the other order.  */
+
+  /* Overlapping registers.  */
+  if (optype0 == REGOP && optype1 == REGOP
+      && (REGNO (op0) == REGNO (wordpart[1][3])
+         || REGNO (op0) == REGNO (wordpart[1][2])
+         || REGNO (op0) == REGNO (wordpart[1][1])))
+    {
+      /* Do fourth word.  */
+      output_asm_insn (singlemove_string (wordpart[3]), wordpart[3]);
+      /* Do the third word.  */
+      output_asm_insn (singlemove_string (wordpart[2]), wordpart[2]);
+      /* Do the second word.  */
+      output_asm_insn (singlemove_string (wordpart[1]), wordpart[1]);
+      /* Do lowest-numbered word.  */
+      return singlemove_string (wordpart[0]);
+    }
+  /* Loading into a register which overlaps a register used in the address.  */
+  if (optype0 == REGOP && optype1 != REGOP
+      && reg_overlap_mentioned_p (op0, op1))
+    {
+      /* ??? Not implemented yet.  This is a bit complicated, because we
+        must load which ever part overlaps the address last.  If the address
+        is a double-reg address, then there are two parts which need to
+        be done last, which is impossible.  We would need a scratch register
+        in that case.  */
+      abort ();
+    }
+
+  /* Normal case: move the four words in lowest to higest address order.  */
+
+  output_asm_insn (singlemove_string (wordpart[0]), wordpart[0]);
+
+  /* Make any unoffsettable addresses point at the second word.  */
+  if (addreg0)
+    output_asm_insn ("add %0,0x4,%0", &addreg0);
+  if (addreg1)
+    output_asm_insn ("add %0,0x4,%0", &addreg1);
+
+  /* Do the second word.  */
+  output_asm_insn (singlemove_string (wordpart[1]), wordpart[1]);
+
+  /* Make any unoffsettable addresses point at the third word.  */
+  if (addreg0)
+    output_asm_insn ("add %0,0x4,%0", &addreg0);
+  if (addreg1)
+    output_asm_insn ("add %0,0x4,%0", &addreg1);
+
+  /* Do the third word.  */
+  output_asm_insn (singlemove_string (wordpart[2]), wordpart[2]);
+
+  /* Make any unoffsettable addresses point at the fourth word.  */
+  if (addreg0)
+    output_asm_insn ("add %0,0x4,%0", &addreg0);
+  if (addreg1)
+    output_asm_insn ("add %0,0x4,%0", &addreg1);
+
+  /* Do the fourth word.  */
+  output_asm_insn (singlemove_string (wordpart[3]), wordpart[3]);
+
+  /* Undo the adds we just did.  */
+  if (addreg0)
+    output_asm_insn ("add %0,-0xc,%0", &addreg0);
+  if (addreg1)
+    output_asm_insn ("add %0,-0xc,%0", &addreg1);
+
+  return "";
+}
 \f
-/* Output assembler code to perform a doubleword move insn with perands
+/* Output assembler code to perform a doubleword move insn with operands
    OPERANDS, one of which must be a floating point register.  */
 
 char *
@@ -1231,6 +1438,47 @@ output_fp_move_double (operands)
     }
   else abort ();
 }
+
+/* Output assembler code to perform a quadword move insn with operands
+   OPERANDS, one of which must be a floating point register.  */
+
+char *
+output_fp_move_quad (operands)
+     rtx *operands;
+{
+  register rtx op0 = operands[0];
+  register rtx op1 = operands[1];
+  register rtx addr;
+
+  if (FP_REG_P (op0))
+    {
+      if (FP_REG_P (op1))
+       return "fmovs %1,%0\n\tfmovs %R1,%R0\n\tfmovs %S1,%S0\n\tfmovs %T1,%T0";
+      if (GET_CODE (op1) == REG)
+       {
+         if ((REGNO (op1) & 1) == 0)
+           return "std %1,[%@-8]\n\tldd [%@-8],%0\n\tstd %S1,[%@-8]\n\tldd [%@-8],%S0";
+         else
+           return "st %R1,[%@-4]\n\tst %1,[%@-8]\n\tldd [%@-8],%0\n\tst %T1,[%@-4]\n\tst %S1,[%@-8]\n\tldd [%@-8],%S0";
+       }
+      else
+       return output_move_quad (operands);
+    }
+  else if (FP_REG_P (op1))
+    {
+      if (GET_CODE (op0) == REG)
+       {
+         if ((REGNO (op0) & 1) == 0)
+           return "std %1,[%@-8]\n\tldd [%@-8],%0\n\tstd %S1,[%@-8]\n\tldd [%@-8],%S0";
+         else
+           return "std %S1,[%@-8]\n\tld [%@-4],%T0\n\tld [%@-8],%S0\n\tstd %1,[%@-8]\n\tld [%@-4],%R0\n\tld [%@-8],%0";
+       }
+      else
+       return output_move_quad (operands);
+    }
+  else
+    abort ();
+}
 \f
 /* Return a REG that occurs in ADDR with coefficient 1.
    ADDR can be effectively incremented by incrementing REG.  */
@@ -2140,6 +2388,8 @@ output_cbranch (op, label, reversed, annul, noop)
   return string;
 }
 
+/* Output assembler code to return from a function.  */
+
 char *
 output_return (operands)
      rtx *operands;
@@ -2199,6 +2449,8 @@ output_return (operands)
     }
 }
 
+/* Output assembler code for a SImode to SFmode conversion.  */
+
 char *
 output_floatsisf2 (operands)
      rtx *operands;
@@ -2210,6 +2462,8 @@ output_floatsisf2 (operands)
   return "st %r1,[%%fp-4]\n\tld [%%fp-4],%0\n\tfitos %0,%0";
 }
 
+/* Output assembler code for a SImode to DFmode conversion.  */
+
 char *
 output_floatsidf2 (operands)
      rtx *operands;
@@ -2220,6 +2474,19 @@ output_floatsidf2 (operands)
     return "fitod %1,%0";
   return "st %r1,[%%fp-4]\n\tld [%%fp-4],%0\n\tfitod %0,%0";
 }
+
+/* Output assembler code for a SImode to TFmode conversion.  */
+
+char *
+output_floatsitf2 (operands)
+     rtx *operands;
+{
+  if (GET_CODE (operands[1]) == MEM)
+    return "ld %1,%0\n\tfitoq %0,%0";
+  else if (FP_REG_P (operands[1]))
+    return "fitoq %1,%0";
+  return "st %r1,[%%fp-4]\n\tld [%%fp-4],%0\n\tfitoq %0,%0";
+}
 \f
 /* Leaf functions and non-leaf functions have different needs.  */
 
@@ -2436,10 +2703,20 @@ print_operand (file, x, code)
       fputs (frame_base_name, file);
       return;
     case 'R':
-      /* Print out the second register name of a register pair.
+      /* Print out the second register name of a register pair or quad.
         I.e., R (%o0) => %o1.  */
       fputs (reg_names[REGNO (x)+1], file);
       return;
+    case 'S':
+      /* Print out the third register name of a register quad.
+        I.e., S (%o0) => %o2.  */
+      fputs (reg_names[REGNO (x)+2], file);
+      return;
+    case 'T':
+      /* Print out the fourth register name of a register quad.
+        I.e., T (%o0) => %o3.  */
+      fputs (reg_names[REGNO (x)+3], file);
+      return;
     case 'm':
       /* Print the operand's address only.  */
       output_address (XEXP (x, 0));
index 5be9cdbf56bf45fc75445f165503af7873c0fb81..00f58583d6b0368131c105e48ae0852ab0afe812 100644 (file)
@@ -1655,13 +1655,16 @@ do {                                                                    \
 
 extern char *singlemove_string ();
 extern char *output_move_double ();
+extern char *output_move_quad ();
 extern char *output_fp_move_double ();
+extern char *output_fp_move_quad ();
 extern char *output_block_move ();
 extern char *output_scc_insn ();
 extern char *output_cbranch ();
 extern char *output_return ();
 extern char *output_floatsisf2 ();
 extern char *output_floatsidf2 ();
+extern char *output_floatsitf2 ();
 
 /* Defined in flags.h, but insn-emit.c does not include flags.h.  */
 
index 43ec22a45ddfedd0e4776d503bda6b24dbf275f9..4f8dc221383b1dbe2d147da08ca968fffdc20e18 100644 (file)
   DONE;
 }")
 
+(define_expand "cmptf"
+  [(set (reg:CCFP 0)
+       (compare:CCFP (match_operand:TF 0 "register_operand" "")
+                     (match_operand:TF 1 "register_operand" "")))]
+  ""
+  "
+{
+  sparc_compare_op0 = operands[0];
+  sparc_compare_op1 = operands[1];
+  DONE;
+}")
+
 ;; Next come the scc insns.  For seq, sne, sgeu, and sltu, we can do this
 ;; without jumps using the addx/subx instructions.  For the rest, we do
 ;; branches.  Seq_special and sne_special clobber the CC reg, because they
   "fcmpes %0,%1"
   [(set_attr "type" "fpcmp")])
 
+(define_insn ""
+  [(set (reg:CCFP 0)
+       (compare:CCFP (match_operand:TF 0 "register_operand" "f")
+                     (match_operand:TF 1 "register_operand" "f")))]
+  ""
+  "fcmpeq %0,%1"
+  [(set_attr "type" "fpcmp")])
+
 ;; The SEQ and SNE patterns are special because they can be done
 ;; without any branching and do not involve a COMPARE.
 
 \f
 ;; Floating point move insns
 
+;; This pattern forces (set (reg:TF ...) (const_double ...))
+;; to be reloaded by putting the constant into memory.
+;; It must come before the more general movtf pattern.
+(define_insn ""
+  [(set (match_operand:TF 0 "general_operand" "=?r,f,o")
+       (match_operand:TF 1 "" "?E,m,G"))]
+  "GET_CODE (operands[1]) == CONST_DOUBLE"
+  "*
+{
+  switch (which_alternative)
+    {
+    case 0:
+      return output_move_quad (operands);
+    case 1:
+      return output_fp_move_quad (operands);
+    case 2:
+      operands[1] = adj_offsettable_operand (operands[0], 4);
+      operands[2] = adj_offsettable_operand (operands[0], 8);
+      operands[3] = adj_offsettable_operand (operands[0], 12);
+      return \"st %%g0,%0\;st %%g0,%1\;st %%g0,%2\;st %%g0,%3\";
+    }
+}"
+  [(set_attr "type" "load,fpload,store")
+   (set_attr "length" "5,5,5")])
+
+(define_expand "movtf"
+  [(set (match_operand:TF 0 "general_operand" "")
+       (match_operand:TF 1 "general_operand" ""))]
+  ""
+  "
+{
+  if (emit_move_sequence (operands, TFmode, 0))
+    DONE;
+}")
+
+(define_insn ""
+  [(set (match_operand:TF 0 "reg_or_nonsymb_mem_operand" "=f,r,Q,Q,f,&r,?f,?r")
+       (match_operand:TF 1 "reg_or_nonsymb_mem_operand" "f,r,f,r,Q,Q,r,f"))]
+  "register_operand (operands[0], TFmode)
+   || register_operand (operands[1], TFmode)"
+  "*
+{
+  if (FP_REG_P (operands[0]) || FP_REG_P (operands[1]))
+    return output_fp_move_quad (operands);
+  return output_move_quad (operands);
+}"
+  [(set_attr "type" "fp,move,fpstore,store,fpload,load,multi,multi")
+   (set_attr "length" "4,4,5,5,5,5,5,5")])
+
+(define_insn ""
+  [(set (mem:TF (match_operand:SI 0 "symbolic_operand" "i,i"))
+       (match_operand:TF 1 "reg_or_0_operand" "rf,G"))
+   (clobber (match_scratch:SI 2 "=&r,&r"))]
+  ""
+  "*
+{
+  output_asm_insn (\"sethi %%hi(%a0),%2\", operands);
+  if (which_alternative == 0)
+    return \"std %1,[%2+%%lo(%a0)]\;std %S1,[%2+%%lo(%a0+8)]\";
+  else
+    return \"st %%g0,[%2+%%lo(%a0)]\;st %%g0,[%2+%%lo(%a0+4)]\; st %%g0,[%2+%%lo(%a0+8)]\;st %%g0,[%2+%%lo(%a0+12)]\";
+}"
+  [(set_attr "type" "store")
+   (set_attr "length" "5")])
+\f
 ;; This pattern forces (set (reg:DF ...) (const_double ...))
 ;; to be reloaded by putting the constant into memory.
 ;; It must come before the more general movdf pattern.
   return \"andcc %0,%1,%%g0\";
 }")
 \f
-;; Conversions between float and double.
+;; Conversions between float, double and long double.
 
 (define_insn "extendsfdf2"
   [(set (match_operand:DF 0 "register_operand" "=f")
   "fstod %1,%0"
   [(set_attr "type" "fp")])
 
+(define_insn "extendsftf2"
+  [(set (match_operand:TF 0 "register_operand" "=f")
+       (float_extend:TF
+        (match_operand:SF 1 "register_operand" "f")))]
+  ""
+  "fstoq %1,%0"
+  [(set_attr "type" "fp")])
+
+(define_insn "extenddftf2"
+  [(set (match_operand:TF 0 "register_operand" "=f")
+       (float_extend:TF
+        (match_operand:DF 1 "register_operand" "f")))]
+  ""
+  "fdtoq %1,%0"
+  [(set_attr "type" "fp")])
+
 (define_insn "truncdfsf2"
   [(set (match_operand:SF 0 "register_operand" "=f")
        (float_truncate:SF
   ""
   "fdtos %1,%0"
   [(set_attr "type" "fp")])
+
+(define_insn "trunctfsf2"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (float_truncate:SF
+        (match_operand:TF 1 "register_operand" "f")))]
+  ""
+  "fqtos %1,%0"
+  [(set_attr "type" "fp")])
+
+(define_insn "trunctfdf2"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (float_truncate:DF
+        (match_operand:TF 1 "register_operand" "f")))]
+  ""
+  "fqtod %1,%0"
+  [(set_attr "type" "fp")])
 \f
 ;; Conversion between fixed point and floating point.
 
   [(set_attr "type" "fp")
    (set_attr "length" "3")])
 
+(define_insn "floatsitf2"
+  [(set (match_operand:TF 0 "general_operand" "=f")
+       (float:TF (match_operand:SI 1 "nonimmediate_operand" "rfm")))]
+  ""
+  "* return output_floatsitf2 (operands);"
+  [(set_attr "type" "fp")
+   (set_attr "length" "3")])
+
 ;; Convert a float to an actual integer.
 ;; Truncation is performed as part of the conversion.
 
   [(set_attr "type" "fp")
    (set_attr "length" "3")])
 
+(define_insn "fix_trunctfsi2"
+  [(set (match_operand:SI 0 "general_operand" "=rm")
+       (fix:SI (fix:TF (match_operand:TF 1 "general_operand" "fm"))))
+   (clobber (match_scratch:DF 2 "=&f"))]
+  ""
+  "*
+{
+  if (FP_REG_P (operands[1]))
+    output_asm_insn (\"fqtoi %1,%2\", operands);
+  else
+    {
+      rtx xoperands[3];
+      xoperands[0] = operands[2];
+      xoperands[1] = operands[1];
+      output_asm_insn (output_fp_move_quad (xoperands), xoperands);
+      output_asm_insn (\"fqtoi %2,%2\", operands);
+    }
+  if (GET_CODE (operands[0]) == MEM)
+    return \"st %2,%0\";
+  else
+    return \"st %2,[%%fp-4]\;ld [%%fp-4],%0\";
+}"
+  [(set_attr "type" "fp")
+   (set_attr "length" "3")])
+
+;; Allow combiner to combine a fix_trunctfsi2 with a floatsitf2
+;; This eliminates 2 useless instructions.
+;; The first one matches if the fixed result is needed.  The second one
+;; matches if the fixed result is not needed.
+
+(define_insn ""
+  [(set (match_operand:TF 0 "general_operand" "=f")
+       (float:TF (fix:SI (fix:TF (match_operand:TF 1 "general_operand" "fm")))))
+   (set (match_operand:SI 2 "general_operand" "=rm")
+       (fix:SI (fix:TF (match_dup 1))))]
+  ""
+  "*
+{
+  if (FP_REG_P (operands[1]))
+    output_asm_insn (\"fqtoi %1,%0\", operands);
+  else
+    {
+      output_asm_insn (output_fp_move_quad (operands), operands);
+      output_asm_insn (\"fqtoi %0,%0\", operands);
+    }
+  if (GET_CODE (operands[2]) == MEM)
+    return \"st %0,%2\;fitoq %0,%0\";
+  else
+    return \"st %0,[%%fp-4]\;fitoq %0,%0\;ld [%%fp-4],%2\";
+}"
+  [(set_attr "type" "fp")
+   (set_attr "length" "5")])
+
+(define_insn ""
+  [(set (match_operand:TF 0 "general_operand" "=f")
+       (float:TF (fix:SI (fix:TF (match_operand:TF 1 "general_operand" "fm")))))]
+  ""
+  "*
+{
+  if (FP_REG_P (operands[1]))
+    output_asm_insn (\"fqtoi %1,%0\", operands);
+  else
+    {
+      output_asm_insn (output_fp_move_quad (operands), operands);
+      output_asm_insn (\"fqtoi %0,%0\", operands);
+    }
+  return \"fitoq %0,%0\";
+}"
+  [(set_attr "type" "fp")
+   (set_attr "length" "3")])
+
 ;; Allow combiner to combine a fix_truncdfsi2 with a floatsidf2
 ;; This eliminates 2 useless instructions.
 ;; The first one matches if the fixed result is needed.  The second one
 \f
 ;; Floating point arithmetic instructions.
 
+(define_insn "addtf3"
+  [(set (match_operand:TF 0 "register_operand" "=f")
+       (plus:TF (match_operand:TF 1 "register_operand" "f")
+                (match_operand:TF 2 "register_operand" "f")))]
+  ""
+  "faddq %1,%2,%0"
+  [(set_attr "type" "fp")])
+
 (define_insn "adddf3"
   [(set (match_operand:DF 0 "register_operand" "=f")
        (plus:DF (match_operand:DF 1 "register_operand" "f")
   "fadds %1,%2,%0"
   [(set_attr "type" "fp")])
 
+(define_insn "subtf3"
+  [(set (match_operand:TF 0 "register_operand" "=f")
+       (minus:TF (match_operand:TF 1 "register_operand" "f")
+                 (match_operand:TF 2 "register_operand" "f")))]
+  ""
+  "fsubq %1,%2,%0"
+  [(set_attr "type" "fp")])
+
 (define_insn "subdf3"
   [(set (match_operand:DF 0 "register_operand" "=f")
        (minus:DF (match_operand:DF 1 "register_operand" "f")
   "fsubs %1,%2,%0"
   [(set_attr "type" "fp")])
 
+(define_insn "multf3"
+  [(set (match_operand:TF 0 "register_operand" "=f")
+       (mult:TF (match_operand:TF 1 "register_operand" "f")
+                (match_operand:TF 2 "register_operand" "f")))]
+  ""
+  "fmulq %1,%2,%0"
+  [(set_attr "type" "fpmul")])
+
 (define_insn "muldf3"
   [(set (match_operand:DF 0 "register_operand" "=f")
        (mult:DF (match_operand:DF 1 "register_operand" "f")
   "fmuls %1,%2,%0"
   [(set_attr "type" "fpmul")])
 
+(define_insn "divtf3"
+  [(set (match_operand:TF 0 "register_operand" "=f")
+       (div:TF (match_operand:TF 1 "register_operand" "f")
+               (match_operand:TF 2 "register_operand" "f")))]
+  ""
+  "fdivq %1,%2,%0"
+  [(set_attr "type" "fpdiv")])
+
 (define_insn "divdf3"
   [(set (match_operand:DF 0 "register_operand" "=f")
        (div:DF (match_operand:DF 1 "register_operand" "f")
   "fdivs %1,%2,%0"
   [(set_attr "type" "fpdiv")])
 
+(define_insn "negtf2"
+  [(set (match_operand:TF 0 "register_operand" "=f,f")
+       (neg:TF (match_operand:TF 1 "register_operand" "0,f")))]
+  ""
+  "@
+   fnegs %0,%0
+   fnegs %1,%0\;fmovs %R1,%R0\;fmovs %S1,%S0\;fmovs %T1,%T0"
+  [(set_attr "type" "fp")
+   (set_attr "length" "1,4")])
+
 (define_insn "negdf2"
   [(set (match_operand:DF 0 "register_operand" "=f,f")
        (neg:DF (match_operand:DF 1 "register_operand" "0,f")))]
   [(set_attr "type" "fp")
    (set_attr "length" "1,2")])
 
-
 (define_insn "negsf2"
   [(set (match_operand:SF 0 "register_operand" "=f")
        (neg:SF (match_operand:SF 1 "register_operand" "f")))]
   "fnegs %1,%0"
   [(set_attr "type" "fp")])
 
+(define_insn "abstf2"
+  [(set (match_operand:TF 0 "register_operand" "=f,f")
+       (abs:TF (match_operand:TF 1 "register_operand" "0,f")))]
+  ""
+  "@
+   fabss %0,%0
+   fabss %1,%0\;fmovs %R1,%R0\;fmovs %S1,%S0\;fmovs %T1,%T0"
+  [(set_attr "type" "fp")
+   (set_attr "length" "1,4")])
+
 (define_insn "absdf2"
   [(set (match_operand:DF 0 "register_operand" "=f,f")
        (abs:DF (match_operand:DF 1 "register_operand" "0,f")))]
   "fabss %1,%0"
   [(set_attr "type" "fp")])
 
+(define_insn "sqrttf2"
+  [(set (match_operand:TF 0 "register_operand" "=f")
+       (sqrt:TF (match_operand:TF 1 "register_operand" "f")))]
+  ""
+  "fsqrtq %1,%0"
+  [(set_attr "type" "fpsqrt")])
+
 (define_insn "sqrtdf2"
   [(set (match_operand:DF 0 "register_operand" "=f")
        (sqrt:DF (match_operand:DF 1 "register_operand" "f")))]