OSDN Git Service

2000-12-05 Richard Sandiford <r.sandiford@redhat.com>
[pf3gnuchains/gcc-fork.git] / gcc / config / mn10300 / mn10300.c
index 3a5454c..bc6b1fc 100644 (file)
@@ -361,6 +361,45 @@ print_operand_address (file, addr)
     }
 }
 
+/* Print a set of registers in the format required by "movm" and "ret".
+   Register K is saved if bit K of MASK is set.  The data and address
+   registers can be stored individually, but the extended registers cannot.
+   We assume that the mask alread takes that into account.  For instance,
+   bits 14 to 17 must have the same value. */
+
+void
+mn10300_print_reg_list (file, mask)
+     FILE *file;
+     int mask;
+{
+  int need_comma;
+  int i;
+
+  need_comma = 0;
+  fputc ('[', file);
+
+  for (i = 0; i < FIRST_EXTENDED_REGNUM; i++)
+    if ((mask & (1 << i)) != 0)
+      {
+       if (need_comma)
+         fputc (',', file);
+       fputs (reg_names [i], file);
+       need_comma = 1;
+      }
+
+  if ((mask & 0x3c000) != 0)
+    {
+      if ((mask & 0x3c000) != 0x3c000)
+       abort();
+      if (need_comma)
+       fputc (',', file);
+      fputs ("exreg1", file);
+      need_comma = 1;
+    }
+
+  fputc (']', file);
+}
+
 int
 can_use_return_insn ()
 {
@@ -383,6 +422,94 @@ can_use_return_insn ()
          && !frame_pointer_needed);
 }
 
+/* Returns the set of live, callee-saved registers as a bitmask.  The
+   callee-saved extended registers cannot be stored individually, so
+   all of them will be included in the mask if any one of them is used. */
+
+int
+mn10300_get_live_callee_saved_regs ()
+{
+  int mask;
+  int i;
+
+  mask = 0;
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    if (regs_ever_live[i] && ! call_used_regs[i])
+      mask |= (1 << i);
+  if ((mask & 0x3c000) != 0)
+    mask |= 0x3c000;
+
+  return mask;
+}
+
+/* Generate an instruction that pushes several registers onto the stack.
+   Register K will be saved if bit K in MASK is set.  The function does
+   nothing if MASK is zero.
+
+   To be compatible with the "movm" instruction, the lowest-numbered
+   register must be stored in the lowest slot.  If MASK is the set
+   { R1,...,RN }, where R1...RN are ordered least first, the generated
+   instruction will have the form:
+
+       (parallel
+         (set (reg:SI 9) (plus:SI (reg:SI 9) (const_int -N*4)))
+        (set (mem:SI (plus:SI (reg:SI 9)
+                              (const_int -1*4)))
+             (reg:SI RN))
+        ...
+        (set (mem:SI (plus:SI (reg:SI 9)
+                              (const_int -N*4)))
+             (reg:SI R1))) */
+
+void
+mn10300_gen_multiple_store (mask)
+     int mask;
+{
+  if (mask != 0)
+    {
+      int i;
+      int count;
+      rtx par;
+      int pari;
+
+      /* Count how many registers need to be saved. */
+      count = 0;
+      for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+       if ((mask & (1 << i)) != 0)
+         count += 1;
+
+      /* We need one PARALLEL element to update the stack pointer and
+        an additional element for each register that is stored. */
+      par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count + 1));
+
+      /* Create the instruction that updates the stack pointer. */
+      XVECEXP (par, 0, 0)
+       = gen_rtx_SET (SImode,
+                      stack_pointer_rtx,
+                      gen_rtx_PLUS (SImode,
+                                    stack_pointer_rtx,
+                                    GEN_INT (-count * 4)));
+
+      /* Create each store. */
+      pari = 1;
+      for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
+       if ((mask & (1 << i)) != 0)
+         {
+           rtx address = gen_rtx_PLUS (SImode,
+                                       stack_pointer_rtx,
+                                       GEN_INT (-pari * 4));
+           XVECEXP(par, 0, pari)
+             = gen_rtx_SET (VOIDmode,
+                            gen_rtx_MEM (SImode, address),
+                            gen_rtx_REG (SImode, i));
+           pari += 1;
+         }
+
+      par = emit_insn (par);
+      RTX_FRAME_RELATED_P (par) = 1;
+    }
+}
+
 void
 expand_prologue ()
 {
@@ -404,14 +531,8 @@ expand_prologue ()
                      gen_rtx_REG (SImode, 1));
     }
 
-  /* And now store all the registers onto the stack with a
-     single two byte instruction.  */
-  if (regs_ever_live[2] || regs_ever_live[3]
-      || regs_ever_live[6] || regs_ever_live[7]
-      || regs_ever_live[14] || regs_ever_live[15]
-      || regs_ever_live[16] || regs_ever_live[17]
-      || frame_pointer_needed)
-    emit_insn (gen_store_movm ());
+  /* If we use any of the callee-saved registers, save them now. */
+  mn10300_gen_multiple_store (mn10300_get_live_callee_saved_regs ());
 
   /* Now put the frame pointer into the frame pointer register.  */
   if (frame_pointer_needed)
@@ -532,6 +653,87 @@ notice_update_cc (body, insn)
     }
 }
 
+/* Recognise the PARALLEL rtx generated by mn10300_gen_multiple_store().
+   This function is for MATCH_PARALLEL and so assumes OP is known to be
+   parallel.  If OP is a multiple store, return a mask indicating which
+   registers it saves.  Return 0 otherwise.  */
+
+int
+store_multiple_operation (op, mode)
+     rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  int count;
+  int mask;
+  int i;
+  unsigned int last;
+  rtx elt;
+
+  count = XVECLEN (op, 0);
+  if (count < 2)
+    return 0;
+
+  /* Check that first instruction has the form (set (sp) (plus A B)) */
+  elt = XVECEXP (op, 0, 0);
+  if (GET_CODE (elt) != SET
+      || GET_CODE (SET_DEST (elt)) != REG
+      || REGNO (SET_DEST (elt)) != STACK_POINTER_REGNUM
+      || GET_CODE (SET_SRC (elt)) != PLUS)
+    return 0;
+
+  /* Check that A is the stack pointer and B is the expected stack size.
+     For OP to match, each subsequent instruction should push a word onto
+     the stack.  We therefore expect the first instruction to create
+     COUNT-1 stack slots. */
+  elt = SET_SRC (elt);
+  if (GET_CODE (XEXP (elt, 0)) != REG
+      || REGNO (XEXP (elt, 0)) != STACK_POINTER_REGNUM
+      || GET_CODE (XEXP (elt, 1)) != CONST_INT
+      || INTVAL (XEXP (elt, 1)) != -(count - 1) * 4)
+    return 0;
+
+  /* Now go through the rest of the vector elements.  They must be
+     ordered so that the first instruction stores the highest-numbered
+     register to the highest stack slot and that subsequent instructions
+     store a lower-numbered register to the slot below.
+
+     LAST keeps track of the smallest-numbered register stored so far.
+     MASK is the set of stored registers. */
+  last = FIRST_PSEUDO_REGISTER;
+  mask = 0;
+  for (i = 1; i < count; i++)
+    {
+      /* Check that element i is a (set (mem M) R) and that R is valid. */
+      elt = XVECEXP (op, 0, i);
+      if (GET_CODE (elt) != SET
+         || GET_CODE (SET_DEST (elt)) != MEM
+         || GET_CODE (SET_SRC (elt)) != REG
+         || REGNO (SET_SRC (elt)) >= last)
+       return 0;
+
+      /* R was OK, so provisionally add it to MASK.  We return 0 in any
+        case if the rest of the instruction has a flaw. */
+      last = REGNO (SET_SRC (elt));
+      mask |= (1 << last);
+
+      /* Check that M has the form (plus (sp) (const_int -I*4)) */
+      elt = XEXP (SET_DEST (elt), 0);
+      if (GET_CODE (elt) != PLUS
+         || GET_CODE (XEXP (elt, 0)) != REG
+         || REGNO (XEXP (elt, 0)) != STACK_POINTER_REGNUM
+         || GET_CODE (XEXP (elt, 1)) != CONST_INT
+         || INTVAL (XEXP (elt, 1)) != -i * 4)
+       return 0;
+    }
+
+  /* All or none of the callee-saved extended registers must be in the set. */
+  if ((mask & 0x3c000) != 0
+      && (mask & 0x3c000) != 0x3c000)
+    return 0;
+
+  return mask;
+}
+
 /* Return true if OP is a valid call operand.  */
 
 int