* config/ms1/ms1.md (UNSPEC_LOOP): New constant.
authornathan <nathan@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 8 Dec 2005 13:46:18 +0000 (13:46 +0000)
committernathan <nathan@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 8 Dec 2005 13:46:18 +0000 (13:46 +0000)
(loop_end, loop_init, doloop_end): New insns.
* config/ms1/ms1.h (LOOP_FIRST, LOOP_LAST): New.
(SPECIAL_REG_FIRST, FIRST_PSEUDO_REGISTER): Adjust.
(FIXED_REGISTERS, CALL_USED_REGISTERS): Adjust.
(REG_CLASS_CONTENTS, REGISTER_NAMES): Adjust.
* config/ms1/ms1.c: #include basic-block.h
(struct machine_function): Add has_loops field.
(ms1_add_loop): New.
(MAX_LOOP_DEPTH, MAX_LOO_LENGTH): New.
(struct loop_info, struct loop_work): New.
(ms1_loop_nesting, ms1_block_length, ms1_scan_loop): New workers.
(ms1_reorg_loops): New loop optimization.
(ms1_machine_reorg): Call it.
* config/ms1/ms1-protos.h (ms1_add_loop): Declare.

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

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

index 83df5d9..90e9b84 100644 (file)
@@ -1,3 +1,21 @@
+2005-12-08  Nathan Sidwell  <nathan@codesourcery.com>
+
+       * config/ms1/ms1.md (UNSPEC_LOOP): New constant.
+       (loop_end, loop_init, doloop_end): New insns.
+       * config/ms1/ms1.h (LOOP_FIRST, LOOP_LAST): New.
+       (SPECIAL_REG_FIRST, FIRST_PSEUDO_REGISTER): Adjust.
+       (FIXED_REGISTERS, CALL_USED_REGISTERS): Adjust.
+       (REG_CLASS_CONTENTS, REGISTER_NAMES): Adjust.
+       * config/ms1/ms1.c: #include basic-block.h
+       (struct machine_function): Add has_loops field.
+       (ms1_add_loop): New.
+       (MAX_LOOP_DEPTH, MAX_LOO_LENGTH): New.
+       (struct loop_info, struct loop_work): New.
+       (ms1_loop_nesting, ms1_block_length, ms1_scan_loop): New workers.
+       (ms1_reorg_loops): New loop optimization.
+       (ms1_machine_reorg): Call it.
+       * config/ms1/ms1-protos.h (ms1_add_loop): Declare.
+
 2005-12-08  Zdenek Dvorak  <dvorakz@suse.cz>
 
        PR tree-optimization/25248
index 412b42d..c370482 100644 (file)
@@ -26,6 +26,7 @@ extern void       ms1_override_options (void);
 extern int         ms1_initial_elimination_offset (int, int);
 extern const char * ms1_asm_output_opcode (FILE *, const char *);
 extern int          ms1_epilogue_uses   (int);
+extern void        ms1_add_loop         (void);
 
 #ifdef TREE_CODE
 extern const char * ms1_cannot_inline_p         (tree);
index 9bc5138..3695f0f 100644 (file)
@@ -47,6 +47,7 @@
 #include "except.h"
 #include "target.h"
 #include "target-def.h"
+#include "basic-block.h"
 
 /* Frame pointer register mask.  */
 #define FP_MASK                         (1 << (GPR_FP))
@@ -68,6 +69,7 @@ struct machine_function GTY(())
   int ra_needs_full_frame;
   struct rtx_def * eh_stack_adjust;
   int interrupt_handler;
+  int has_loops;
 };
 
 /* Define the information needed to generate branch and scc insns.
@@ -811,7 +813,7 @@ ms1_override_options (void)
        error ("bad value (%s) for -march= switch", ms1_cpu_string);
     }
   else
-    ms1_cpu = PROCESSOR_MS1_64_001;
+    ms1_cpu = PROCESSOR_MS2;
 
   if (flag_exceptions)
     {
@@ -1648,6 +1650,510 @@ ms1_pass_in_stack (enum machine_mode mode ATTRIBUTE_UNUSED, tree type)
               || TREE_ADDRESSABLE (type))));
 }
 \f
+/* Increment the counter for the number of loop instructions in the
+   current function.  */
+
+void ms1_add_loop (void)
+{
+  cfun->machine->has_loops++;
+}
+
+
+/* Maxium loop nesting depth.  */
+#define MAX_LOOP_DEPTH 4
+/* Maxium size of a loop (allows some headroom for delayed branch slot
+   filling.  */
+#define MAX_LOOP_LENGTH (200 * 4)
+
+/* We need to keep a vector of basic blocks */
+DEF_VEC_P (basic_block);
+DEF_VEC_ALLOC_P (basic_block,heap);
+
+/* And a vector of loops */
+typedef struct loop_info *loop_info;
+DEF_VEC_P (loop_info);
+DEF_VEC_ALLOC_P (loop_info,heap);
+
+/* Information about a loop we have found (or are in the process of
+   finding).  */
+struct loop_info GTY (())
+{
+  /* loop number, for dumps */
+  int loop_no;
+  
+  /* Predecessor block of the loop.   This is the one that falls into
+     the loop and contains the initialization instruction.  */
+  basic_block predecessor;
+
+  /* First block in the loop.  This is the one branched to by the dbnz
+     insn.  */
+  basic_block head;
+  
+  /* Last block in the loop (the one with the dbnz insn */
+  basic_block tail;
+
+  /* The successor block of the loop.  This is the one the dbnz insn
+     falls into.  */
+  basic_block successor;
+
+  /* The dbnz insn.  */
+  rtx dbnz;
+
+  /* The initialization insn.  */
+  rtx init;
+
+  /* The new initialization instruction.  */
+  rtx loop_init;
+
+  /* The new ending instruction. */
+  rtx loop_end;
+
+  /* The new label placed at the end of the loop. */
+  rtx end_label;
+
+  /* The nesting depth of the loop.  Set to -1 for a bad loop.  */
+  int depth;
+
+  /* The length of the loop.  */
+  int length;
+
+  /* Next loop in the graph. */
+  struct loop_info *next;
+
+  /* Vector of blocks only within the loop, (excluding those within
+     inner loops).  */
+  VEC (basic_block,heap) *blocks;
+
+  /* Vector of inner loops within this loop  */
+  VEC (loop_info,heap) *loops;
+};
+
+/* Information used during loop detection.  */
+typedef struct loop_work GTY(())
+{
+  /* Basic block to be scanned.  */
+  basic_block block;
+
+  /* Loop it will be within.  */
+  loop_info loop;
+} loop_work;
+
+/* Work list.  */
+DEF_VEC_O (loop_work);
+DEF_VEC_ALLOC_O (loop_work,heap);
+
+/* Determine the nesting and length of LOOP.  Return false if the loop
+   is bad.  */
+
+static bool
+ms1_loop_nesting (loop_info loop)
+{
+  loop_info inner;
+  unsigned ix;
+  int inner_depth = 0;
+  
+  if (!loop->depth)
+    {
+      /* Make sure we only have one entry point.  */
+      if (EDGE_COUNT (loop->head->preds) == 2)
+       {
+         loop->predecessor = EDGE_PRED (loop->head, 0)->src;
+         if (loop->predecessor == loop->tail)
+           /* We wanted the other predecessor.  */
+           loop->predecessor = EDGE_PRED (loop->head, 1)->src;
+         
+         /* We can only place a loop insn on a fall through edge of a
+            single exit block.  */
+         if (EDGE_COUNT (loop->predecessor->succs) != 1
+             || !(EDGE_SUCC (loop->predecessor, 0)->flags & EDGE_FALLTHRU))
+           loop->predecessor = NULL;
+       }
+
+      /* Mark this loop as bad for now.  */
+      loop->depth = -1;
+      if (loop->predecessor)
+       {
+         for (ix = 0; VEC_iterate (loop_info, loop->loops, ix++, inner);)
+           {
+             if (!inner->depth)
+               ms1_loop_nesting (inner);
+             
+             if (inner->depth < 0)
+               {
+                 inner_depth = -1;
+                 break;
+               }
+             
+             if (inner_depth < inner->depth)
+               inner_depth = inner->depth;
+             loop->length += inner->length;
+           }
+         
+         /* Set the proper loop depth, if it was good. */
+         if (inner_depth >= 0)
+           loop->depth = inner_depth + 1;
+       }
+    }
+  return (loop->depth > 0
+         && loop->predecessor
+         && loop->depth < MAX_LOOP_DEPTH
+         && loop->length < MAX_LOOP_LENGTH);
+}
+
+/* Determine the length of block BB.  */
+
+static int
+ms1_block_length (basic_block bb)
+{
+  int length = 0;
+  rtx insn;
+
+  for (insn = BB_HEAD (bb);
+       insn != NEXT_INSN (BB_END (bb));
+       insn = NEXT_INSN (insn))
+    {
+      if (!INSN_P (insn))
+       continue;
+      if (CALL_P (insn))
+       {
+         /* Calls are not allowed in loops.  */
+         length = MAX_LOOP_LENGTH + 1;
+         break;
+       }
+      
+      length += get_attr_length (insn);
+    }
+  return length;
+}
+
+/* Scan the blocks of LOOP (and its inferiors) looking for uses of
+   REG.  Return true, if we find any.  Don't count the loop's dbnz
+   insn if it matches DBNZ.  */
+
+static bool
+ms1_scan_loop (loop_info loop, rtx reg, rtx dbnz)
+{
+  unsigned ix;
+  loop_info inner;
+  basic_block bb;
+  
+  for (ix = 0; VEC_iterate (basic_block, loop->blocks, ix, bb); ix++)
+    {
+      rtx insn;
+
+      for (insn = BB_HEAD (bb);
+          insn != NEXT_INSN (BB_END (bb));
+          insn = NEXT_INSN (insn))
+       {
+         if (!INSN_P (insn))
+           continue;
+         if (insn == dbnz)
+           continue;
+         if (reg_mentioned_p (reg, PATTERN (insn)))
+           return true;
+       }
+    }
+  for (ix = 0; VEC_iterate (loop_info, loop->loops, ix, inner); ix++)
+    if (ms1_scan_loop (inner, reg, NULL_RTX))
+      return true;
+  
+  return false;
+}
+
+/* MS2 has a loop instruction which needs to be placed just before the
+   loop.  It indicates the end of the loop and specifies the number of
+   loop iterations.  It can be nested with an automatically maintained
+   stack of counter and end address registers.  It's an ideal
+   candidate for doloop.  Unfortunately, gcc presumes that loops
+   always end with an explicit instriction, and the doloop_begin
+   instruction is not a flow control instruction so it can be
+   scheduled earlier than just before the start of the loop.  To make
+   matters worse, the optimization pipeline can duplicate loop exit
+   and entrance blocks and fails to track abnormally exiting loops.
+   Thus we cannot simply use doloop.
+
+   What we do is emit a dbnz pattern for the doloop optimization, and
+   let that be optimized as normal.  Then in machine dependent reorg
+   we have to repeat the loop searching algorithm.  We use the
+   flow graph to find closed loops ending in a dbnz insn.  We then try
+   and convert it to use the loop instruction.  The conditions are,
+
+   * the loop has no abnormal exits, duplicated end conditions or
+   duplicated entrance blocks
+
+   * the loop counter register is only used in the dbnz instruction
+   within the loop
+   
+   * we can find the instruction setting the initial value of the loop
+   counter
+
+   * the loop is not executed more than 65535 times. (This might be
+   changed to 2^32-1, and would therefore allow variable initializers.)
+
+   * the loop is not nested more than 4 deep 5) there are no
+   subroutine calls in the loop.  */
+
+static void
+ms1_reorg_loops (FILE *dump_file)
+{
+  basic_block bb;
+  loop_info loops = NULL;
+  loop_info loop;
+  int nloops = 0;
+  unsigned dwork = 0;
+  VEC (loop_work,heap) *works = VEC_alloc (loop_work,heap,20);
+  loop_work *work;
+  edge e;
+  edge_iterator ei;
+  bool replaced = false;
+
+  /* Find all the possible loop tails.  This means searching for every
+     dbnz instruction.  For each one found, create a loop_info
+     structure and add the head block to the work list. */
+  FOR_EACH_BB (bb)
+    {
+      rtx tail = BB_END (bb);
+
+      while (GET_CODE (tail) == NOTE)
+       tail = PREV_INSN (tail);
+      
+      bb->aux = NULL;
+      if (recog_memoized (tail) == CODE_FOR_decrement_and_branch_until_zero)
+       {
+         /* A possible loop end */
+
+         loop = XNEW (struct loop_info);
+         loop->next = loops;
+         loops = loop;
+         loop->tail = bb;
+         loop->head = BRANCH_EDGE (bb)->dest;
+         loop->successor = FALLTHRU_EDGE (bb)->dest;
+         loop->predecessor = NULL;
+         loop->dbnz = tail;
+         loop->depth = 0;
+         loop->length = ms1_block_length (bb);
+         loop->blocks = VEC_alloc (basic_block, heap, 20);
+         VEC_quick_push (basic_block, loop->blocks, bb);
+         loop->loops = NULL;
+         loop->loop_no = nloops++;
+         
+         loop->init = loop->end_label = NULL_RTX;
+         loop->loop_init = loop->loop_end = NULL_RTX;
+         
+         work = VEC_safe_push (loop_work, heap, works, NULL);
+         work->block = loop->head;
+         work->loop = loop;
+
+         bb->aux = loop;
+
+         if (dump_file)
+           {
+             fprintf (dump_file, ";; potential loop %d ending at\n",
+                      loop->loop_no);
+             print_rtl_single (dump_file, tail);
+           }
+       }
+    }
+
+  /*  Now find all the closed loops.
+      until work list empty,
+       if block's auxptr is set
+         if != loop slot
+           if block's loop's start != block
+            mark loop as bad
+          else
+             append block's loop's fallthrough block to worklist
+            increment this loop's depth
+       else if block is exit block
+         mark loop as bad
+       else
+         set auxptr
+         for each target of block
+           add to worklist */
+  while (VEC_iterate (loop_work, works, dwork++, work))
+    {
+      loop = work->loop;
+      bb = work->block;
+      if (bb == EXIT_BLOCK_PTR)
+       /* We've reached the exit block.  The loop must be bad. */
+       loop->depth = -1;
+      else if (!bb->aux)
+       {
+         /* We've not seen this block before.  Add it to the loop's
+            list and then add each successor to the work list.  */
+         bb->aux = loop;
+         loop->length += ms1_block_length (bb);
+         VEC_safe_push (basic_block, heap, loop->blocks, bb);
+         FOR_EACH_EDGE (e, ei, bb->succs)
+           {
+             if (!VEC_space (loop_work, works, 1))
+               {
+                 if (dwork)
+                   {
+                     VEC_block_remove (loop_work, works, 0, dwork);
+                     dwork = 0;
+                   }
+                 else
+                   VEC_reserve (loop_work, heap, works, 1);
+               }
+             work = VEC_quick_push (loop_work, works, NULL);
+             work->block = EDGE_SUCC (bb, ei.index)->dest;
+             work->loop = loop;
+           }
+       }
+      else if (bb->aux != loop)
+       {
+         /* We've seen this block in a different loop.  If it's not
+            the other loop's head, then this loop must be bad.
+            Otherwise, the other loop might be a nested loop, so
+            continue from that loop's successor.  */
+         loop_info other = bb->aux;
+         
+         if (other->head != bb)
+           loop->depth = -1;
+         else
+           {
+             VEC_safe_push (loop_info, heap, loop->loops, other);
+             work = VEC_safe_push (loop_work, heap, works, NULL);
+             work->loop = loop;
+             work->block = other->successor;
+           }
+       }
+    }
+  VEC_free (loop_work, heap, works);
+
+  /* Now optimize the loops.  */
+  for (loop = loops; loop; loop = loop->next)
+    {
+      rtx iter_reg, insn, init_insn;
+      rtx init_val, loop_end, loop_init, end_label, head_label;
+
+      if (!ms1_loop_nesting (loop))
+       {
+         if (dump_file)
+           fprintf (dump_file, ";; loop %d is bad\n", loop->loop_no);
+         continue;
+       }
+
+      /* Get the loop iteration register.  */
+      iter_reg = SET_DEST (XVECEXP (PATTERN (loop->dbnz), 0, 1));
+      
+      if (!REG_P (iter_reg))
+       {
+         /* Spilled */
+         if (dump_file)
+           fprintf (dump_file, ";; loop %d has spilled iteration count\n",
+                    loop->loop_no);
+         continue;
+       }
+
+      /* Look for the initializing insn */
+      init_insn = NULL_RTX;
+      for (insn = BB_END (loop->predecessor);
+          insn != PREV_INSN (BB_HEAD (loop->predecessor));
+          insn = PREV_INSN (insn))
+       {
+         if (!INSN_P (insn))
+           continue;
+         if (reg_mentioned_p (iter_reg, PATTERN (insn)))
+           {
+             rtx set = single_set (insn);
+
+             if (set && rtx_equal_p (iter_reg, SET_DEST (set)))
+               init_insn = insn;
+             break;
+           }
+       }
+
+      if (!init_insn)
+       {
+         if (dump_file)
+           fprintf (dump_file, ";; loop %d has no initializer\n",
+                    loop->loop_no);
+         continue;
+       }
+      if (dump_file)
+       {
+         fprintf (dump_file, ";; loop %d initialized by\n",
+                  loop->loop_no);
+         print_rtl_single (dump_file, init_insn);
+       }
+
+      init_val = PATTERN (init_insn);
+      if (GET_CODE (init_val) == SET)
+       init_val = SET_SRC (init_val);
+      if (GET_CODE (init_val) != CONST_INT || INTVAL (init_val) >= 65535)
+       {
+         if (dump_file)
+           fprintf (dump_file, ";; loop %d has complex initializer\n",
+                    loop->loop_no);
+         continue;
+       }
+      
+      /* Scan all the blocks to make sure they don't use iter_reg.  */
+      if (ms1_scan_loop (loop, iter_reg, loop->dbnz))
+       {
+         if (dump_file)
+           fprintf (dump_file, ";; loop %d uses iterator\n",
+                    loop->loop_no);
+         continue;
+       }
+
+      /* The loop is good for replacement.  */
+      
+      /* loop is 1 based, dbnz is zero based.  */
+      init_val = GEN_INT (INTVAL (init_val) + 1);
+      
+      iter_reg = gen_rtx_REG (SImode, LOOP_FIRST + loop->depth - 1);
+      end_label = gen_label_rtx ();
+      head_label = XEXP (SET_SRC (XVECEXP (PATTERN (loop->dbnz), 0, 0)), 1);
+      loop_end = gen_loop_end (iter_reg, head_label);
+      loop_init = gen_loop_init (iter_reg, init_val, end_label);
+      loop->init = init_insn;
+      loop->end_label = end_label;
+      loop->loop_init = loop_init;
+      loop->loop_end = loop_end;
+      replaced = true;
+      
+      if (dump_file)
+       {
+         fprintf (dump_file, ";; replacing loop %d initializer with\n",
+                  loop->loop_no);
+         print_rtl_single (dump_file, loop->loop_init);
+         fprintf (dump_file, ";; replacing loop %d terminator with\n",
+                  loop->loop_no);
+         print_rtl_single (dump_file, loop->loop_end);
+       }
+    }
+
+  /* Now apply the optimizations.  Do it this way so we don't mess up
+     the flow graph half way through.  */
+  for (loop = loops; loop; loop = loop->next)
+    if (loop->loop_init)
+      {
+       emit_jump_insn_after (loop->loop_init, BB_END (loop->predecessor));
+       delete_insn (loop->init);
+       emit_label_before (loop->end_label, loop->dbnz);
+       emit_jump_insn_before (loop->loop_end, loop->dbnz);
+       delete_insn (loop->dbnz);
+      }
+
+  /* Free up the loop structures */
+  while (loops)
+    {
+      loop = loops;
+      loops = loop->next;
+      VEC_free (loop_info, heap, loop->loops);
+      VEC_free (basic_block, heap, loop->blocks);
+      XDELETE (loop);
+    }
+
+  if (replaced && dump_file)
+    {
+      fprintf (dump_file, ";; Replaced loops\n");
+      print_rtl (dump_file, get_insns ());
+    }
+}
 
 /* Structures to hold branch information during reorg.  */
 typedef struct branch_info
@@ -1959,6 +2465,9 @@ ms1_reorg_hazard (void)
 static void
 ms1_machine_reorg (void)
 {
+  if (cfun->machine->has_loops)
+    ms1_reorg_loops (dump_file);
+
   if (ms1_flag_delayed_branch)
     dbr_schedule (get_insns (), dump_file);
   
index 68f6eac..eff36c7 100644 (file)
@@ -41,7 +41,7 @@ extern enum processor_type ms1_cpu;
 /* A C string constant that tells the GCC driver program options to pass to
    the assembler.  */
 #undef  ASM_SPEC
-#define ASM_SPEC "%{march=*} %{!march=*: -march=ms1-16-002}"
+#define ASM_SPEC "%{march=*} %{!march=*: -march=ms2}"
 
 /* A string to pass to at the end of the command given to the linker.  */
 #undef  LIB_SPEC
@@ -55,7 +55,7 @@ march=ms1-16-003:-T 16-003.ld%s; \
 march=MS1-16-003:-T 16-003.ld%s; \
 march=ms2:-T ms2.ld%s; \
 march=MS2:-T ms2.ld%s; \
-        : -T 16-002.ld}"
+        : -T ms2.ld}"
 
 /* A string to pass at the very beginning of the command given to the
    linker.  */
@@ -69,7 +69,7 @@ march=ms1-16-003:%{!mno-crt0:crt0-16-003.o%s} startup-16-003.o%s; \
 march=MS1-16-003:%{!mno-crt0:crt0-16-003.o%s} startup-16-003.o%s; \
 march=ms2:%{!mno-crt0:crt0-ms2.o%s} startup-ms2.o%s; \
 march=MS2:%{!mno-crt0:crt0-ms2.o%s} startup-ms2.o%s; \
-        :%{!mno-crt0:crt0-16-002.o%s} startup-16-002.o%s} \
+        :%{!mno-crt0:crt0-ms2.o%s} startup-ms2.o%s} \
 crti.o%s crtbegin.o%s"
 
 /* A string to pass at the end of the command given to the linker.  */
@@ -83,7 +83,7 @@ march=ms1-16-003:exit-16-003.o%s; \
 march=MS1-16-003:exit-16-003.o%s; \
 march=ms2:exit-ms2.o%s; \
 march=MS2:exit-ms2.o%s; \
-        :exit-16-002.o%s} \
+        :exit-ms2.o%s} \
  crtend.o%s crtn.o%s"
 \f
 /* Run-time target specifications.  */
@@ -243,10 +243,13 @@ march=MS2:exit-ms2.o%s; \
                                           seen  by the caller */
 #define GPR_INTERRUPT_LINK 15          /* hold return addres for interrupts */
 
+#define LOOP_FIRST         (GPR_LAST + 1)
+#define LOOP_LAST         (LOOP_FIRST + 3)
+
 /* Argument register that is eliminated in favor of the frame and/or stack
    pointer.  Also add register to point to where the return address is
    stored.  */
-#define SPECIAL_REG_FIRST              (GPR_LAST + 1)
+#define SPECIAL_REG_FIRST              (LOOP_LAST + 1)
 #define SPECIAL_REG_LAST               (SPECIAL_REG_FIRST)
 #define ARG_POINTER_REGNUM             (SPECIAL_REG_FIRST + 0)
 #define SPECIAL_REG_P(R)               ((R) == SPECIAL_REG_FIRST)
@@ -258,7 +261,7 @@ march=MS2:exit-ms2.o%s; \
 /* The register used to hold functions return value */
 #define RETVAL_REGNUM          11
 
-#define FIRST_PSEUDO_REGISTER (GPR_FIRST + 17)
+#define FIRST_PSEUDO_REGISTER (SPECIAL_REG_LAST + 1)
 
 #define IS_PSEUDO_P(R) (REGNO (R) >= FIRST_PSEUDO_REGISTER)
 
@@ -270,7 +273,7 @@ march=MS2:exit-ms2.o%s; \
    R15 IRA     interrupt return address.  */
 #define FIXED_REGISTERS { 1, 0, 0, 0, 0, 0, 0, 0, \
                          0, 0, 0, 0, 1, 1, 1, 1, \
-                         1                       \
+                         1, 1, 1, 1, 1           \
                         }
 
 /* Like `FIXED_REGISTERS' but has 1 for each register that is clobbered (in
@@ -279,7 +282,7 @@ march=MS2:exit-ms2.o%s; \
    allocation of values that must live across function calls.  */
 #define CALL_USED_REGISTERS    { 1, 1, 1, 1, 1, 0, 0, 1, \
                                  1, 1, 1, 1, 1, 1, 1, 1, \
-                                 1                       \
+                                 1, 1, 1, 1, 1           \
                                }
 
 \f
@@ -310,9 +313,9 @@ enum reg_class
 #define REG_CLASS_NAMES {"NO_REGS", "ALL_REGS" }
 
 #define REG_CLASS_CONTENTS \
-   {                                                                   \
-     { 0x0, 0x0 },                                                     \
-     { (((1 << (GPR_LAST + 1)) - 1) & ~(1 << GPR_FIRST)), 0x0 },       \
+   {                                                           \
+     { 0x0 },                                                  \
+     { 0x000fffff },                                           \
    }
 
 /* A C expression whose value is a register class containing hard register
@@ -736,7 +739,7 @@ extern struct ms1_frame_info current_frame_info;
 #define REGISTER_NAMES                                                 \
 { "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7",                      \
   "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",                        \
-  "ap" }
+  "LOOP1", "LOOP2", "LOOP3", "LOOP4", "ap" }
 
 /* If defined, a C initializer for an array of structures containing a name and
    a register number.  This macro defines additional names for hard registers,
index 50e704c..104f282 100644 (file)
@@ -25,6 +25,7 @@
     (UNSPEC_BLOCKAGE 0)
     (UNSPEC_EI 1)
     (UNSPEC_DI 2)
+    (UNSPEC_LOOP 3)
   ])
 
 ;; Attributes
   "")
 
 \f
+;; Loop instructions.  ms2 has a low overhead looping instructions.
+;; these take a constant or register loop count and a loop length
+;; offset.  Unfortunately the loop can only be up to 256 instructions,
+;; We deal with longer loops by moving the loop end upwards.  To do
+;; otherwise would force us to to be very pessimistic right up until
+;; the end.
+
+;; This instruction is a placeholder to make the control flow explicit.
+(define_insn "loop_end"
+  [(set (pc) (if_then_else
+                         (ne (match_operand:SI 0 "register_operand" "")
+                             (const_int 1))
+                         (label_ref (match_operand 1 "" ""))
+                         (pc)))
+   (set (match_dup 0) (plus:SI (match_dup 0) (const_int -1)))
+   (unspec [(const_int 0)] UNSPEC_LOOP)]
+  "TARGET_MS2"
+  ";loop end %0,%l1"
+  [(set_attr "length" "0")])
+
+;; This is the real looping instruction.  It is placed just before the
+;; loop body.  We make it a branch insn, so it stays at the end of the
+;; block it is in.
+(define_insn "loop_init"
+  [(set (match_operand:SI 0 "register_operand" "=r,r")
+       (match_operand:SI 1 "uns_arith_operand" "r,K"))
+   (unspec [(label_ref (match_operand 2 "" ""))] UNSPEC_LOOP)]
+  "TARGET_MS2"
+  "@
+   loop  %1,%l2 ;%0%#
+   loopi %1,%l2 ;%0%#"
+  [(set_attr "length" "4")
+   (set_attr "type" "branch")])
+
+; operand 0 is the loop count pseudo register
+; operand 1 is the number of loop iterations or 0 if it is unknown
+; operand 2 is the maximum number of loop iterations
+; operand 3 is the number of levels of enclosed loops
+; operand 4 is the label to jump to at the top of the loop
+(define_expand "doloop_end"
+  [(parallel [(set (pc) (if_then_else
+                         (ne (match_operand:SI 0 "nonimmediate_operand" "")
+                             (const_int 0))
+                         (label_ref (match_operand 4 "" ""))
+                         (pc)))
+             (set (match_dup 0)
+                  (plus:SI (match_dup 0)
+                           (const_int -1)))
+             (clobber (match_scratch:SI 5 ""))])]
+  "TARGET_MS1_16_003 || TARGET_MS2"
+  {ms1_add_loop ();})
+\f
 ;; Moves
 
 (define_expand "loadqi"