OSDN Git Service

* config/pdp11/pdp11-protos.h (output_move_double,
authorpkoning <pkoning@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 10 Dec 2010 01:31:08 +0000 (01:31 +0000)
committerpkoning <pkoning@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 10 Dec 2010 01:31:08 +0000 (01:31 +0000)
output_move_quad): Delete.
(output_move_multiple, pdp11_expand_operands): New functions.
(pdp11_action, pdp11_partorder): New enums.
* config/pdp11/pdp11.md (movdi, movsi, movdf, movsf): Use
output_move_multiple.
(adddi3, subdi3, negdi2): New patterns.
(addsi3, subsi3, negsi2): Use pdp11_expand_operands.
(abshi2): Delete.
(neghi2, negqi2): Use PDPint iterator.
* config/pdp11/pdp11.c (find_addr_reg, output_move_double,
output_move_quad): Delete.
(pdp11_expand_operands, output_move_multiple): New functions.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@167676 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/config/pdp11/pdp11-protos.h
gcc/config/pdp11/pdp11.c
gcc/config/pdp11/pdp11.md

index 196fcbd..988dd2e 100644 (file)
@@ -1,3 +1,19 @@
+2010-12-09  Paul Koning  <ni1d@arrl.net>
+
+       * config/pdp11/pdp11-protos.h (output_move_double,
+       output_move_quad): Delete.
+       (output_move_multiple, pdp11_expand_operands): New functions.
+       (pdp11_action, pdp11_partorder): New enums.
+       * config/pdp11/pdp11.md (movdi, movsi, movdf, movsf): Use
+       output_move_multiple.
+       (adddi3, subdi3, negdi2): New patterns.
+       (addsi3, subsi3, negsi2): Use pdp11_expand_operands.
+       (abshi2): Delete.
+       (neghi2, negqi2): Use PDPint iterator.
+       * config/pdp11/pdp11.c (find_addr_reg, output_move_double,
+       output_move_quad): Delete. 
+       (pdp11_expand_operands, output_move_multiple): New functions.
+       
 2010-12-09  Joseph Myers  <joseph@codesourcery.com>
 
        * config/vax/linux.h (WCHAR_TYPE, WCHAR_TYPE_SIZE): Define.
index 97f0589..09ba7b2 100644 (file)
@@ -26,8 +26,7 @@ extern int simple_memory_operand (rtx, enum machine_mode);
 extern int legitimate_const_double_p (rtx);
 extern void notice_update_cc_on_set (rtx, rtx);
 extern void output_addr_const_pdp11 (FILE *, rtx);
-extern const char *output_move_double (rtx *);
-extern const char *output_move_quad (rtx *);
+extern const char *output_move_multiple (rtx *);
 extern const char *output_block_move (rtx *);
 extern const char *output_jump (enum rtx_code, int, int);
 extern void print_operand_address (FILE *, rtx);
@@ -35,6 +34,10 @@ extern bool pdp11_cannot_change_mode_class (enum machine_mode,
                                             enum machine_mode, enum reg_class);
 extern bool pdp11_secondary_memory_needed (reg_class_t, reg_class_t, 
                                           enum machine_mode);
+typedef enum { no_action, dec_before, inc_after } pdp11_action;
+typedef enum { little, either, big } pdp11_partorder;
+extern bool pdp11_expand_operands (rtx *, rtx [][2], int, 
+                                  pdp11_action *, pdp11_partorder);
 extern int pdp11_initial_elimination_offset (int, int);
 extern enum reg_class pdp11_regno_reg_class (int);
 
index bb220f0..fedb22a 100644 (file)
@@ -140,7 +140,6 @@ decode_pdp11_d (const struct real_format *fmt ATTRIBUTE_UNUSED,
 
 static bool pdp11_handle_option (size_t, const char *, int);
 static void pdp11_option_init_struct (struct gcc_options *);
-static rtx find_addr_reg (rtx); 
 static const char *singlemove_string (rtx *);
 static bool pdp11_assemble_integer (rtx, unsigned int, int);
 static void pdp11_output_function_prologue (FILE *, HOST_WIDE_INT);
@@ -236,7 +235,6 @@ static const struct default_options pdp11_option_optimization_table[] =
 
 #undef  TARGET_ASM_FUNCTION_SECTION
 #define TARGET_ASM_FUNCTION_SECTION pdp11_function_section
-
 \f
 /* Implement TARGET_HANDLE_OPTION.  */
 
@@ -483,408 +481,216 @@ singlemove_string (rtx *operands)
 }
 
 \f
-/* Output assembler code to perform a doubleword move insn
-   with operands OPERANDS.  */
-
-const char *
-output_move_double (rtx *operands)
+/* Expand multi-word operands (SImode or DImode) into the 2 or 4
+   corresponding HImode operands.  The number of operands is given
+   as the third argument, and the required order of the parts as
+   the fourth argument.  */
+bool
+pdp11_expand_operands (rtx *operands, rtx exops[][2], int opcount, 
+                      pdp11_action *action, pdp11_partorder order)
 {
-  enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
-  rtx latehalf[2];
-  rtx addreg0 = 0, addreg1 = 0;
-
-  /* First classify both operands.  */
-
-  if (REG_P (operands[0]))
-    optype0 = REGOP;
-  else if (offsettable_memref_p (operands[0]))
-    optype0 = OFFSOP;
-  else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
-    optype0 = POPOP;
-  else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
-    optype0 = PUSHOP;
-  else if (GET_CODE (operands[0]) == MEM)
-    optype0 = MEMOP;
-  else
-    optype0 = RNDOP;
-
-  if (REG_P (operands[1]))
-    optype1 = REGOP;
-  else if (CONSTANT_P (operands[1])
-#if 0
-          || GET_CODE (operands[1]) == CONST_DOUBLE
-#endif
-          )
-    optype1 = CNSTOP;
-  else if (offsettable_memref_p (operands[1]))
-    optype1 = OFFSOP;
-  else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
-    optype1 = POPOP;
-  else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
-    optype1 = PUSHOP;
-  else if (GET_CODE (operands[1]) == 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.  */
-
-  gcc_assert (optype0 != RNDOP && optype1 != RNDOP);
-
-  /* If one operand is decrementing and one is incrementing
-     decrement the former register explicitly
-     and change that operand into ordinary indexing.  */
-
-  if (optype0 == PUSHOP && optype1 == POPOP)
-    {
-      operands[0] = XEXP (XEXP (operands[0], 0), 0);
-      output_asm_insn ("sub $4,%0", operands);
-      operands[0] = gen_rtx_MEM (SImode, operands[0]);
-      optype0 = OFFSOP;
-    }
-  if (optype0 == POPOP && optype1 == PUSHOP)
+  int words, op, w, i, sh;
+  pdp11_partorder useorder;
+  bool sameoff = false;
+  enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype;
+  REAL_VALUE_TYPE r;
+  long sval[2];
+  
+  words = GET_MODE_BITSIZE (GET_MODE (operands[0])) / 16;
+  
+  /* If either piece order is accepted and one is pre-decrement
+     while the other is post-increment, set order to be high order
+     word first.  That will force the pre-decrement to be turned
+     into a pointer adjust, then offset addressing.
+     Otherwise, if either operand uses pre-decrement, that means
+     the order is low order first. 
+     Otherwise, if both operands are registers and destination is
+     higher than source and they overlap, do low order word (highest
+     register number) first.  */
+  useorder = either;
+  if (opcount == 2)
     {
-      operands[1] = XEXP (XEXP (operands[1], 0), 0);
-      output_asm_insn ("sub $4,%1", operands);
-      operands[1] = gen_rtx_MEM (SImode, operands[1]);
-      optype1 = OFFSOP;
+      if (!REG_P (operands[0]) && !REG_P (operands[1]) &&
+         !(CONSTANT_P (operands[1]) || 
+           GET_CODE (operands[1]) == CONST_DOUBLE) &&
+         ((GET_CODE (XEXP (operands[0], 0)) == POST_INC &&
+           GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) ||
+          (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC &&
+           GET_CODE (XEXP (operands[1], 0)) == POST_INC)))
+           useorder = big;
+      else if ((!REG_P (operands[0]) &&
+               GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) ||
+              (!REG_P (operands[1]) &&
+               !(CONSTANT_P (operands[1]) || 
+                 GET_CODE (operands[1]) == CONST_DOUBLE) &&
+               GET_CODE (XEXP (operands[1], 0)) == PRE_DEC))
+       useorder = little;
+      else if (REG_P (operands[0]) && REG_P (operands[1]) &&
+              REGNO (operands[0]) > REGNO (operands[1]) &&
+              REGNO (operands[0]) < REGNO (operands[1]) + words)
+           useorder = little;
+
+      /* Check for source == offset from register and dest == push of
+        the same register.  In that case, we have to use the same
+        offset (the one for the low order word) for all words, because
+        the push increases the offset to each source word.
+        In theory there are other cases like this, for example dest == pop,
+        but those don't occur in real life so ignore those.  */
+      if (GET_CODE (operands[0]) ==  MEM 
+         && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC
+         && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
+         && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
+       sameoff = true;
     }
 
-  /* 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));
-
-  if (optype1 == MEMOP)
-    addreg1 = find_addr_reg (XEXP (operands[1], 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 high-numbered 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 (HImode, REGNO (operands[0]) + 1);
-  else if (optype0 == OFFSOP)
-    latehalf[0] = adjust_address (operands[0], HImode, 2);
+  /* If the caller didn't specify order, use the one we computed,
+     or high word first if we don't care either.  If the caller did
+     specify, verify we don't have a problem with that order.
+     (If it matters to the caller, constraints need to be used to
+     ensure this case doesn't occur).  */
+  if (order == either)
+    order = (useorder == either) ? big : useorder;
   else
-    latehalf[0] = operands[0];
+    gcc_assert (useorder == either || useorder == order);
 
-  if (optype1 == REGOP)
-    latehalf[1] = gen_rtx_REG (HImode, REGNO (operands[1]) + 1);
-  else if (optype1 == OFFSOP)
-    latehalf[1] = adjust_address (operands[1], HImode, 2);
-  else if (optype1 == CNSTOP)
+  
+  for (op = 0; op < opcount; op++)
     {
-       if (CONSTANT_P (operands[1]))
+      /* First classify the operand.  */
+      if (REG_P (operands[op]))
+       optype = REGOP;
+      else if (CONSTANT_P (operands[op])
+              || GET_CODE (operands[op]) == CONST_DOUBLE)
+       optype = CNSTOP;
+      else if (GET_CODE (XEXP (operands[op], 0)) == POST_INC)
+       optype = POPOP;
+      else if (GET_CODE (XEXP (operands[op], 0)) == PRE_DEC)
+       optype = PUSHOP;
+      else if (!reload_in_progress || offsettable_memref_p (operands[op]))
+       optype = OFFSOP;
+      else if (GET_CODE (operands[op]) == MEM)
+       optype = MEMOP;
+      else
+       optype = RNDOP;
+
+      /* Check for the cases that the operand constraints are not
+        supposed to allow to happen. Return failure for such cases.  */
+      if (optype == RNDOP)
+       return false;
+      
+      if (action != NULL)
+       action[op] = no_action;
+      
+      /* If the operand uses pre-decrement addressing but we
+        want to get the parts high order first,
+        decrement the former register explicitly
+        and change the operand into ordinary indexing.  */
+      if (optype == PUSHOP && order == big)
+       {
+         gcc_assert (action != NULL);
+         action[op] = dec_before;
+         operands[op] = gen_rtx_MEM (GET_MODE (operands[op]),
+                                     XEXP (XEXP (operands[op], 0), 0));
+         optype = OFFSOP;
+       }
+      /* If the operand uses post-increment mode but we want 
+        to get the parts low order first, change the operand
+        into ordinary indexing and remember to increment
+        the register explicitly when we're done.  */
+      else if (optype == POPOP && order == little)
        {
-           /* now the mess begins, high word is in lower word??? 
+         gcc_assert (action != NULL);
+         action[op] = inc_after;
+         operands[op] = gen_rtx_MEM (GET_MODE (operands[op]),
+                                     XEXP (XEXP (operands[op], 0), 0));
+         optype = OFFSOP;
+       }
 
-              that's what ashc makes me think, but I don't remember :-( */
-           latehalf[1] = GEN_INT (INTVAL(operands[1]) >> 16);
-           operands[1] = GEN_INT (INTVAL(operands[1]) & 0xff);
+      if (GET_CODE (operands[op]) == CONST_DOUBLE)
+       {
+         REAL_VALUE_FROM_CONST_DOUBLE (r, operands[op]);
+         REAL_VALUE_TO_TARGET_DOUBLE (r, sval);
+       }
+      
+      for (i = 0; i < words; i++)
+       {
+         if (order == big)
+           w = i;
+         else if (sameoff)
+           w = words - 1;
+         else
+           w = words - 1 - i;
+
+         /* Set the output operand to be word "w" of the input.  */
+         if (optype == REGOP)
+           exops[i][op] = gen_rtx_REG (HImode, REGNO (operands[op]) + w);
+         else if (optype == OFFSOP)
+           exops[i][op] = adjust_address (operands[op], HImode, w * 2);
+         else if (optype == CNSTOP)
+           {
+             if (GET_CODE (operands[op]) == CONST_DOUBLE)
+               {
+                 sh = 16 - (w & 1) * 16;
+                 exops[i][op] = gen_rtx_CONST_INT (HImode, (sval[w / 2] >> sh) & 0xffff);
+               }
+             else
+               {
+                 sh = ((words - 1 - w) * 16);
+                 exops[i][op] = gen_rtx_CONST_INT (HImode, trunc_int_for_mode (INTVAL(operands[op]) >> sh, HImode));
+               }
+           }
+         else
+           exops[i][op] = operands[op];
        }
-       else
-         /* immediate 32-bit values not allowed */
-         gcc_assert (GET_CODE (operands[1]) != CONST_DOUBLE);
-    }
-  else
-    latehalf[1] = operands[1];
-
-  /* If insn is effectively movd N(sp),-(sp) then we will do the
-     high word first.  We should use the adjusted operand 1 (which is N+4(sp))
-     for the low word as well, to compensate for the first decrement of sp.  */
-  if (optype0 == PUSHOP
-      && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
-      && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
-    operands[1] = latehalf[1];
-
-  /* If one or both operands autodecrementing,
-     do the two words, high-numbered first.  */
-
-  /* Likewise,  the first move would clobber the source of the second one,
-     do them in the other order.  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.  */
-
-  if (optype0 == PUSHOP || optype1 == PUSHOP
-      || (optype0 == REGOP && optype1 == REGOP
-         && REGNO (operands[0]) == REGNO (latehalf[1])))
-    {
-      /* Make any unoffsettable addresses point at high-numbered word.  */
-      if (addreg0)
-       output_asm_insn ("add $2,%0", &addreg0);
-      if (addreg1)
-       output_asm_insn ("add $2,%0", &addreg1);
-
-      /* Do that word.  */
-      output_asm_insn (singlemove_string (latehalf), latehalf);
-
-      /* Undo the adds we just did.  */
-      if (addreg0)
-       output_asm_insn ("sub $2,%0", &addreg0);
-      if (addreg1)
-       output_asm_insn ("sub $2,%0", &addreg1);
-
-      /* Do low-numbered word.  */
-      return singlemove_string (operands);
     }
-
-  /* Normal case: do the two words, low-numbered first.  */
-
-  output_asm_insn (singlemove_string (operands), operands);
-
-  /* Make any unoffsettable addresses point at high-numbered word.  */
-  if (addreg0)
-    output_asm_insn ("add $2,%0", &addreg0);
-  if (addreg1)
-    output_asm_insn ("add $2,%0", &addreg1);
-
-  /* Do that word.  */
-  output_asm_insn (singlemove_string (latehalf), latehalf);
-
-  /* Undo the adds we just did.  */
-  if (addreg0)
-    output_asm_insn ("sub $2,%0", &addreg0);
-  if (addreg1)
-    output_asm_insn ("sub $2,%0", &addreg1);
-
-  return "";
+  return true;
 }
-/* Output assembler code to perform a quadword move insn
-   with operands OPERANDS.  */
+
+/* Output assembler code to perform a multiple-word move insn
+   with operands OPERANDS.  This moves 2 or 4 words depending
+   on the machine mode of the operands.  */
 
 const char *
-output_move_quad (rtx *operands)
+output_move_multiple (rtx *operands)
 {
-  enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
-  rtx latehalf[2];
-  rtx addreg0 = 0, addreg1 = 0;
-
-  output_asm_insn(";/* movdi/df: %1 -> %0 */", operands);
+  rtx exops[4][2];
+  pdp11_action action[2];
+  int i, words;
   
-  if (REG_P (operands[0]))
-    optype0 = REGOP;
-  else if (offsettable_memref_p (operands[0]))
-    optype0 = OFFSOP;
-  else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
-    optype0 = POPOP;
-  else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
-    optype0 = PUSHOP;
-  else if (GET_CODE (operands[0]) == MEM)
-    optype0 = MEMOP;
-  else
-    optype0 = RNDOP;
-
-  if (REG_P (operands[1]))
-    optype1 = REGOP;
-  else if (CONSTANT_P (operands[1])
-          || GET_CODE (operands[1]) == CONST_DOUBLE)
-    optype1 = CNSTOP;
-  else if (offsettable_memref_p (operands[1]))
-    optype1 = OFFSOP;
-  else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
-    optype1 = POPOP;
-  else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
-    optype1 = PUSHOP;
-  else if (GET_CODE (operands[1]) == 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.  */
+  words = GET_MODE_BITSIZE (GET_MODE (operands[0])) / 16;
 
-  gcc_assert (optype0 != RNDOP && optype1 != RNDOP);
+  pdp11_expand_operands (operands, exops, 2, action, either);
   
-  if (optype0 == REGOP || optype1 == REGOP)
-  {
-      /* check for use of clrd???? 
-         if you ever allow ac4 and ac5 (now we require secondary load) 
-        you must check whether 
-        you want to load into them or store from them - 
-        then dump ac0 into $help$ movce ac4/5 to ac0, do the 
-        store from ac0, and restore ac0 - if you can find 
-        an unused ac[0-3], use that and you save a store and a load!*/
-
-      if (FPU_REG_P(REGNO(operands[0])))
-      {
-         if (GET_CODE(operands[1]) == CONST_DOUBLE)
-         {
-             REAL_VALUE_TYPE r;
-             REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
-
-             if (REAL_VALUES_EQUAL (r, dconst0))
-                 return "{clrd|clrf} %0";
-         }
-             
-         return "{ldd|movf} %1, %0";
-      }
-      
-      if (FPU_REG_P(REGNO(operands[1])))
-         return "{std|movf} %1, %0";
-  }
-      
-  /* If one operand is decrementing and one is incrementing
-     decrement the former register explicitly
-     and change that operand into ordinary indexing.  */
-
-  if (optype0 == PUSHOP && optype1 == POPOP)
+  /* Check for explicit decrement before.  */
+  if (action[0] == dec_before)
     {
-      operands[0] = XEXP (XEXP (operands[0], 0), 0);
-      output_asm_insn ("sub $8,%0", operands);
-      operands[0] = gen_rtx_MEM (DImode, operands[0]);
-      optype0 = OFFSOP;
+      operands[0] = XEXP (operands[0], 0);
+      output_asm_insn ("sub $4,%0", operands);
     }
-  if (optype0 == POPOP && optype1 == PUSHOP)
+  if (action[1] == dec_before)
     {
-      operands[1] = XEXP (XEXP (operands[1], 0), 0);
-      output_asm_insn ("sub $8,%1", operands);
-      operands[1] = gen_rtx_MEM (SImode, operands[1]);
-      optype1 = OFFSOP;
+      operands[1] = XEXP (operands[1], 0);
+      output_asm_insn ("sub $4,%1", operands);
     }
 
-  /* 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));
-
-  if (optype1 == MEMOP)
-    addreg1 = find_addr_reg (XEXP (operands[1], 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 high-numbered word and in some cases alter the
-     operands in OPERANDS to be suitable for the low-numbered word.  */
+  /* Do the words.  */
+  for (i = 0; i < words; i++)
+    output_asm_insn (singlemove_string (exops[i]), exops[i]);
 
-  if (optype0 == REGOP)
-    latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 2);
-  else if (optype0 == OFFSOP)
-    latehalf[0] = adjust_address (operands[0], SImode, 4);
-  else
-    latehalf[0] = operands[0];
-
-  if (optype1 == REGOP)
-    latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 2);
-  else if (optype1 == OFFSOP)
-    latehalf[1] = adjust_address (operands[1], SImode, 4);
-  else if (optype1 == CNSTOP)
+  /* Check for increment after.  */
+  if (action[0] == inc_after)
     {
-      if (GET_CODE (operands[1]) == CONST_DOUBLE)
-       {
-         REAL_VALUE_TYPE r;
-         long dval[2];
-         REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
-         REAL_VALUE_TO_TARGET_DOUBLE (r, dval);
-         latehalf[1] = GEN_INT (dval[1]);
-         operands[1] = GEN_INT (dval[0]);
-       }
-      else if (GET_CODE(operands[1]) == CONST_INT)
-       {
-         latehalf[1] = const0_rtx;
-       }
-      else
-       gcc_unreachable ();
+      operands[0] = XEXP (operands[0], 0);
+      output_asm_insn ("add $4,%0", operands);
     }
-  else
-    latehalf[1] = operands[1];
-
-  /* If insn is effectively movd N(sp),-(sp) then we will do the
-     high word first.  We should use the adjusted operand 1 (which is N+4(sp))
-     for the low word as well, to compensate for the first decrement of sp.  */
-  if (optype0 == PUSHOP
-      && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
-      && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
-    operands[1] = latehalf[1];
-
-  /* If one or both operands autodecrementing,
-     do the two words, high-numbered first.  */
-
-  /* Likewise,  the first move would clobber the source of the second one,
-     do them in the other order.  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.  */
-
-  if (optype0 == PUSHOP || optype1 == PUSHOP
-      || (optype0 == REGOP && optype1 == REGOP
-         && REGNO (operands[0]) == REGNO (latehalf[1])))
+  if (action[1] == inc_after)
     {
-      /* Make any unoffsettable addresses point at high-numbered word.  */
-      if (addreg0)
-       output_asm_insn ("add $4,%0", &addreg0);
-      if (addreg1)
-       output_asm_insn ("add $4,%0", &addreg1);
-
-      /* Do that word.  */
-      output_asm_insn(output_move_double(latehalf), latehalf);
-
-      /* Undo the adds we just did.  */
-      if (addreg0)
-       output_asm_insn ("sub $4,%0", &addreg0);
-      if (addreg1)
-       output_asm_insn ("sub $4,%0", &addreg1);
-
-      /* Do low-numbered word.  */
-      return output_move_double (operands);
+      operands[1] = XEXP (operands[1], 0);
+      output_asm_insn ("add $4,%1", operands);
     }
 
-  /* Normal case: do the two words, low-numbered first.  */
-
-  output_asm_insn (output_move_double (operands), operands);
-
-  /* Make any unoffsettable addresses point at high-numbered word.  */
-  if (addreg0)
-    output_asm_insn ("add $4,%0", &addreg0);
-  if (addreg1)
-    output_asm_insn ("add $4,%0", &addreg1);
-
-  /* Do that word.  */
-  output_asm_insn (output_move_double (latehalf), latehalf);
-
-  /* Undo the adds we just did.  */
-  if (addreg0)
-    output_asm_insn ("sub $4,%0", &addreg0);
-  if (addreg1)
-    output_asm_insn ("sub $4,%0", &addreg1);
-
   return "";
 }
-
-\f
-/* Return a REG that occurs in ADDR with coefficient 1.
-   ADDR can be effectively incremented by incrementing REG.  */
-
-static rtx
-find_addr_reg (rtx addr)
-{
-  while (GET_CODE (addr) == PLUS)
-    {
-      if (GET_CODE (XEXP (addr, 0)) == REG)
-       addr = XEXP (addr, 0);
-      if (GET_CODE (XEXP (addr, 1)) == REG)
-       addr = XEXP (addr, 1);
-      if (CONSTANT_P (XEXP (addr, 0)))
-       addr = XEXP (addr, 1);
-      if (CONSTANT_P (XEXP (addr, 1)))
-       addr = XEXP (addr, 0);
-    }
-  if (GET_CODE (addr) == REG)
-    return addr;
-  return 0;
-}
 \f
 /* Output an ascii string.  */
 void
@@ -1806,6 +1612,7 @@ pdp11_legitimate_address_p (enum machine_mode mode,
        return false;
       }
 }
+
 /* Return the class number of the smallest class containing
    reg number REGNO.  */
 enum reg_class
index 2d4eb8a..a070c37 100644 (file)
 ;; Move instructions
 
 (define_insn "movdi"
-  [(set (match_operand:DI 0 "nonimmediate_operand" "=g,rm,o")
-       (match_operand:DI 1 "general_operand" "m,r,a"))]
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,g")
+       (match_operand:DI 1 "general_operand" "rN,g"))]
   ""
-  "* return output_move_quad (operands);"
+  "* return output_move_multiple (operands);"
 ;; what's the mose expensive code - say twice movsi = 16
-  [(set_attr "length" "32,32,32")])
+  [(set_attr "length" "16,32")])
 
 (define_insn "movsi"
-  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r,rm,m")
-       (match_operand:SI 1 "general_operand" "rN,IJ,K,m,r"))]
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,g,g")
+       (match_operand:SI 1 "general_operand" "rN,IJ,IJ,g"))]
   ""
-  "* return output_move_double (operands);"
+  "* return output_move_multiple (operands);"
 ;; what's the most expensive code ? - I think 8!
 ;; we could split it up and make several sub-cases...
-  [(set_attr "length" "4,6,8,16,16")])
+  [(set_attr "length" "4,6,8,16")])
 
 (define_insn "mov<mode>"
   [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q")
      else if (which_alternative == 1 || which_alternative == 3)
        return \"std %1, %0\";
      else 
-       return output_move_quad (operands); "
+       return output_move_multiple (operands); "
 ;; just a guess..
   [(set_attr "length" "2,2,10,10,32")])
 
      else if (which_alternative == 1 || which_alternative == 3)
        return \"{stcdf|movfo} %1, %0\";
      else 
-       return output_move_double (operands); "
+       return output_move_multiple (operands); "
 ;; just a guess..
   [(set_attr "length" "2,2,10,10,16")])
 
   "{addd|addf} %2, %0"
   [(set_attr "length" "2,4,10")])
 
-(define_insn "addsi3"
-  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,o,o,r,r,r,o,o,o")
-       (plus:SI (match_operand:SI 1 "general_operand" "%0,0,0,0,0,0,0,0,0,0")
-                (match_operand:SI 2 "general_operand" "r,o,r,o,I,J,K,I,J,K")))]
+(define_insn "adddi3"
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,r,o,o")
+       (plus:DI (match_operand:DI 1 "general_operand" "%0,0,0,0")
+                (match_operand:DI 2 "general_operand" "r,on,r,on")))]
   ""
   "*
-{ /* Here we trust that operands don't overlap 
-
-     or is lateoperands the low word?? - looks like it! */
-
-  rtx lateoperands[3];
+{
+  rtx inops[2];
+  rtx exops[4][2];
   
-  lateoperands[0] = operands[0];
-
-  if (REG_P (operands[0]))
-    operands[0] = gen_rtx_REG (HImode, REGNO (operands[0]) + 1);
-  else
-    operands[0] = adjust_address (operands[0], HImode, 2);
+  inops[0] = operands[0];
+  inops[1] = operands[2];
+  pdp11_expand_operands (inops, exops, 2, NULL, either);
   
-  if (! CONSTANT_P(operands[2]))
+  if (!CONSTANT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
+    output_asm_insn (\"add %1, %0\", exops[0]);
+  if (!CONSTANT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
   {
-    lateoperands[2] = operands[2];
-
-    if (REG_P (operands[2]))
-      operands[2] = gen_rtx_REG (HImode, REGNO (operands[2]) + 1);
-    else
-      operands[2] = adjust_address (operands[2], HImode, 2);
-
-    output_asm_insn (\"add %2, %0\", operands);
-    output_asm_insn (\"adc %0\", lateoperands);
-    output_asm_insn (\"add %2, %0\", lateoperands);
-    return \"\";
+    output_asm_insn (\"add %1, %0\", exops[1]);
+    output_asm_insn (\"adc %0\", exops[0]);
+  }
+  if (!CONSTANT_P (exops[2][1]) || INTVAL (exops[2][1]) != 0)
+  {
+    output_asm_insn (\"add %1, %0\", exops[2]);
+    output_asm_insn (\"adc %0\", exops[1]);
+    output_asm_insn (\"adc %0\", exops[0]);
+  }
+  if (!CONSTANT_P (exops[3][1]) || INTVAL (exops[3][1]) != 0)
+  {
+    output_asm_insn (\"add %1, %0\", exops[3]);
+    output_asm_insn (\"adc %0\", exops[2]);
+    output_asm_insn (\"adc %0\", exops[1]);
+    output_asm_insn (\"adc %0\", exops[0]);
   }
 
-  lateoperands[2] = GEN_INT ((INTVAL (operands[2]) >> 16) & 0xffff);
-  operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);
+  return \"\";
+}"
+  [(set_attr "length" "20,28,40,48")])
+
+;; Note that the register operand is not marked earlyclobber.
+;; The reason is that SI values go in register pairs, so they
+;; can't partially overlap.  They can be either disjoint, or
+;; source and destination can be equal.  The latter case is 
+;; handled properly because of the ordering of the individual
+;; instructions used.  Specifically, carry from the low to the
+;; high word is added at the end, so the adding of the high parts
+;; will always used the original high part and not a high part
+;; modified by carry (which would amount to double carry).
+(define_insn "addsi3"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,o,o")
+       (plus:SI (match_operand:SI 1 "general_operand" "%0,0,0,0")
+                (match_operand:SI 2 "general_operand" "r,on,r,on")))]
+  ""
+  "*
+{
+  rtx inops[2];
+  rtx exops[2][2];
   
-  if (INTVAL(operands[2]))
-  { 
-    output_asm_insn (\"add %2, %0\", operands);
-    output_asm_insn (\"adc %0\", lateoperands);
+  inops[0] = operands[0];
+  inops[1] = operands[2];
+  pdp11_expand_operands (inops, exops, 2, NULL, either);
+  
+  if (!CONSTANT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
+    output_asm_insn (\"add %1, %0\", exops[0]);
+  if (!CONSTANT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
+  {
+    output_asm_insn (\"add %1, %0\", exops[1]);
+    output_asm_insn (\"adc %0\", exops[0]);
   }
 
-  if (INTVAL(lateoperands[2]))
-    output_asm_insn (\"add %2, %0\", lateoperands);
-
   return \"\";
 }"
-  [(set_attr "length" "6,10,12,16,6,2,10,10,6,16")])
+  [(set_attr "length" "6,10,12,16")])
 
 (define_insn "addhi3"
   [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,rR,Q,Q")
   "{subd|subf} %2, %0"
   [(set_attr "length" "2,4")])
 
-(define_insn "subsi3"
-  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,o,o")
-        (minus:SI (match_operand:SI 1 "general_operand" "0,0,0,0")
-                  (match_operand:SI 2 "general_operand" "r,o,r,o")))]
+(define_insn "subdi3"
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,r,o,o")
+       (minus:DI (match_operand:DI 1 "general_operand" "0,0,0,0")
+                (match_operand:DI 2 "general_operand" "r,on,r,on")))]
   ""
   "*
-{ /* Here we trust that operands don't overlap 
+{
+  rtx inops[2];
+  rtx exops[4][2];
+  
+  inops[0] = operands[0];
+  inops[1] = operands[2];
+  pdp11_expand_operands (inops, exops, 2, NULL, either);
+  
+  if (!CONSTANT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
+    output_asm_insn (\"sub %1, %0\", exops[0]);
+  if (!CONSTANT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
+  {
+    output_asm_insn (\"sub %1, %0\", exops[1]);
+    output_asm_insn (\"sbc %0\", exops[0]);
+  }
+  if (!CONSTANT_P (exops[2][1]) || INTVAL (exops[2][1]) != 0)
+  {
+    output_asm_insn (\"sub %1, %0\", exops[2]);
+    output_asm_insn (\"sbc %0\", exops[1]);
+    output_asm_insn (\"sbc %0\", exops[0]);
+  }
+  if (!CONSTANT_P (exops[3][1]) || INTVAL (exops[3][1]) != 0)
+  {
+    output_asm_insn (\"sub %1, %0\", exops[3]);
+    output_asm_insn (\"sbc %0\", exops[2]);
+    output_asm_insn (\"sbc %0\", exops[1]);
+    output_asm_insn (\"sbc %0\", exops[0]);
+  }
 
-     or is lateoperands the low word?? - looks like it! */
+  return \"\";
+}"
+  [(set_attr "length" "20,28,40,48")])
 
-  rtx lateoperands[3];
+(define_insn "subsi3"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,o,o")
+       (minus:SI (match_operand:SI 1 "general_operand" "0,0,0,0")
+                (match_operand:SI 2 "general_operand" "r,on,r,on")))]
+  ""
+  "*
+{
+  rtx inops[2];
+  rtx exops[2][2];
   
-  lateoperands[0] = operands[0];
-
-  if (REG_P (operands[0]))
-    operands[0] = gen_rtx_REG (HImode, REGNO (operands[0]) + 1);
-  else
-    operands[0] = adjust_address (operands[0], HImode, 2);
+  inops[0] = operands[0];
+  inops[1] = operands[2];
+  pdp11_expand_operands (inops, exops, 2, NULL, either);
   
-  lateoperands[2] = operands[2];
-
-  if (REG_P (operands[2]))
-    operands[2] = gen_rtx_REG (HImode, REGNO (operands[2]) + 1);
-  else
-    operands[2] = adjust_address (operands[2], HImode, 2);
+  if (!CONSTANT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
+    output_asm_insn (\"sub %1, %0\", exops[0]);
+  if (!CONSTANT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
+  {
+    output_asm_insn (\"sub %1, %0\", exops[1]);
+    output_asm_insn (\"sbc %0\", exops[0]);
+  }
 
-  output_asm_insn (\"sub %2, %0\", operands);
-  output_asm_insn (\"sbc %0\", lateoperands);
-  output_asm_insn (\"sub %2, %0\", lateoperands);
   return \"\";
 }"
-;; offsettable memory addresses always are expensive!!!
   [(set_attr "length" "6,10,12,16")])
 
 (define_insn "subhi3"
   "{absd|absf} %0"
   [(set_attr "length" "2,4")])
 
-(define_insn "abshi2"
-  [(set (match_operand:HI 0 "nonimmediate_operand" "=r,o")
-       (abs:HI (match_operand:HI 1 "general_operand" "0,0")))]
-  ""
-  "*
-{
-  static int count = 0;
-  char buf[200];
-       
-  output_asm_insn(\"tst %0\", operands);
-  sprintf(buf, \"bge abshi%d\", count);
-  output_asm_insn(buf, NULL);
-  output_asm_insn(\"neg %0\", operands);
-  sprintf(buf, \"\\nabshi%d:\", count++);
-  output_asm_insn(buf, NULL);
-
-  return \"\";
-}"
-  [(set_attr "length" "6,10")])
-
-
-;; define expand abshi - is much better !!! - but
-;; will it be optimized into an abshi2 ?
-;; it will leave better code, because the tsthi might be 
-;; optimized away!!
-; -- just a thought - don't have time to check 
-;
-;(define_expand "abshi2"
-;  [(match_operand:HI 0 "nonimmediate_operand" "")
-;   (match_operand:HI 1 "general_operand" "")]
-;  ""
-;  "
-;{
-;  rtx label = gen_label_rtx ();
-;
-;  /* do I need this? */
-;  do_pending_stack_adjust ();
-;
-;  emit_move_insn (operands[0], operands[1]);
-;
-;  emit_insn (gen_tsthi (operands[0]));
-;  emit_insn (gen_bge (label1));
-;
-;  emit_insn (gen_neghi(operands[0], operands[0])
-;  
-;  emit_barrier ();
-;
-;  emit_label (label);
-;
-;  /* allow REG_NOTES to be set on last insn (labels don't have enough
-;     fields, and can't be used for REG_NOTES anyway).  */
-;  emit_use (stack_pointer_rtx);
-;  DONE;
-;}")
 
 ;; negate insns
 
   "{negd|negf} %0"
   [(set_attr "length" "2,4")])
 
-(define_insn "negsi2"
-  [(set (match_operand:SI 0 "register_operand" "=r")
-       (neg:SI (match_operand:SI 1 "general_operand" "0")))]
+(define_insn "negdi2"
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=r,o")
+       (neg:DI (match_operand:DI 1 "general_operand" "0,0")))]
   ""
 {
+  rtx exops[4][2];
+  
+  pdp11_expand_operands (operands, exops, 1, NULL, either);
 
-  rtx lateoperands[2];
+  output_asm_insn (\"com %0\", exops[3]);
+  output_asm_insn (\"com %0\", exops[2]);
+  output_asm_insn (\"com %0\", exops[1]);
+  output_asm_insn (\"com %0\", exops[0]);
+  output_asm_insn (\"add $1, %0\", exops[3]);
+  output_asm_insn (\"adc %0\", exops[2]);
+  output_asm_insn (\"adc %0\", exops[1]);
+  output_asm_insn (\"adc %0\", exops[0]);
 
-  lateoperands[0] = operands[0];
-  operands[0] = gen_rtx_REG (HImode, REGNO (operands[0]) + 1);
+  return \"\";
+}
+[(set_attr "length" "18,34")])
 
-  lateoperands[1] = operands[1];
-  operands[1] = gen_rtx_REG (HImode, REGNO (operands[1]) + 1);
+(define_insn "negsi2"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,o")
+       (neg:SI (match_operand:SI 1 "general_operand" "0,0")))]
+  ""
+{
+  rtx exops[2][2];
+  
+  pdp11_expand_operands (operands, exops, 1, NULL, either);
 
-  output_asm_insn (\"com %0\", lateoperands);
-  output_asm_insn (\"com %0\", operands);
-  output_asm_insn (\"add $1, %0\", operands);
-  output_asm_insn (\"adc %0\", lateoperands);
+  output_asm_insn (\"com %0\", exops[1]);
+  output_asm_insn (\"com %0\", exops[0]);
+  output_asm_insn (\"add $1, %0\", exops[1]);
+  output_asm_insn (\"adc %0\", exops[0]);
 
   return \"\";
 }
-  [(set_attr "length" "14")])
+[(set_attr "length" "12,20")])
 
-(define_insn "neghi2"
-  [(set (match_operand:HI 0 "nonimmediate_operand" "=rR,Q")
-       (neg:HI (match_operand:HI 1 "general_operand" "0,0")))]
-  ""
-  "neg %0"
-  [(set_attr "length" "2,4")])
-
-(define_insn "negqi2"
-  [(set (match_operand:QI 0 "nonimmediate_operand" "=rR,Q")
-       (neg:QI (match_operand:QI 1 "general_operand" "0,0")))]
+(define_insn "neg<mode>2"
+  [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,Q")
+       (neg:PDPint (match_operand:PDPint 1 "general_operand" "0,0")))]
   ""
-  "negb %0"
+  "neg<isfx> %0"
   [(set_attr "length" "2,4")])