OSDN Git Service

* pa.c (following_call): Fail if the CALL_INSN is an indirect
[pf3gnuchains/gcc-fork.git] / gcc / stupid.c
index f9179f5..b68b196 100644 (file)
@@ -1,5 +1,5 @@
 /* Dummy data flow analysis for GNU compiler in nonoptimizing mode.
-   Copyright (C) 1987, 1991 Free Software Foundation, Inc.
+   Copyright (C) 1987, 91, 94, 95, 96, 1997 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -15,7 +15,8 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 
 /* This file performs stupid register allocation, which is used
@@ -64,15 +65,10 @@ static int *uid_suid;
 
 static int last_call_suid;
 
-/* Record the suid of the last JUMP_INSN
-   so we can tell whether a pseudo reg crosses any jumps.  */
+/* Record the suid of the last NOTE_INSN_SETJMP
+   so we can tell whether a pseudo reg crosses any setjmp.  */
 
-static int last_jump_suid;
-
-/* Record the suid of the last CODE_LABEL
-   so we can tell whether a pseudo reg crosses any labels.  */
-
-static int last_label_suid;
+static int last_setjmp_suid;
 
 /* Element N is suid of insn where life span of pseudo reg N ends.
    Element is  0 if register N has not been seen yet on backward scan.  */
@@ -83,10 +79,6 @@ static int *reg_where_dead;
 
 static int *reg_where_born;
 
-/* Element N is 1 if pseudo reg N lives across labels or jumps.  */
-
-static char *reg_crosses_blocks;
-
 /* Numbers of pseudo-regs to be allocated, highest priority first.  */
 
 static int *reg_order;
@@ -96,6 +88,15 @@ static int *reg_order;
 
 static char *regs_live;
 
+/* Indexed by reg number, nonzero if reg was used in a SUBREG that changes
+   its size.  */
+
+static char *regs_change_size;
+
+/* Indexed by reg number, nonzero if reg crosses a setjmp.  */
+
+static char *regs_crosses_setjmp;
+
 /* Indexed by insn's suid, the set of hard regs live after that insn.  */
 
 static HARD_REG_SET *after_insn_hard_regs;
@@ -105,9 +106,10 @@ static HARD_REG_SET *after_insn_hard_regs;
 #define MARK_LIVE_AFTER(INSN,REGNO)  \
   SET_HARD_REG_BIT (after_insn_hard_regs[INSN_SUID (INSN)], (REGNO))
 
-static void stupid_mark_refs ();
-static int stupid_reg_compare ();
-static int stupid_find_reg ();
+static int stupid_reg_compare  PROTO((const GENERIC_PTR,const GENERIC_PTR));
+static int stupid_find_reg     PROTO((int, enum reg_class, enum machine_mode,
+                                      int, int, int));
+static void stupid_mark_refs   PROTO((rtx, rtx));
 \f
 /* Stupid life analysis is for the case where only variables declared
    `register' go in registers.  For this case, we mark all
@@ -125,7 +127,7 @@ stupid_life_analysis (f, nregs, file)
 {
   register int i;
   register rtx last, insn;
-  int max_uid;
+  int max_uid, max_suid;
 
   bzero (regs_ever_live, sizeof regs_ever_live);
 
@@ -148,41 +150,44 @@ stupid_life_analysis (f, nregs, file)
   last = 0;                    /* In case of empty function body */
   for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
     {
-      if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN
-         || GET_CODE (insn) == JUMP_INSN)
+      if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
        last = insn;
+
       INSN_SUID (insn) = ++i;
     }
 
   last_call_suid = i + 1;
-  last_jump_suid = i + 1;
-  last_label_suid = i + 1;
+  last_setjmp_suid = i + 1;
+  max_suid = i + 1;
 
   max_regno = nregs;
 
   /* Allocate tables to record info about regs.  */
 
   reg_where_dead = (int *) alloca (nregs * sizeof (int));
-  bzero (reg_where_dead, nregs * sizeof (int));
+  bzero ((char *) reg_where_dead, nregs * sizeof (int));
 
   reg_where_born = (int *) alloca (nregs * sizeof (int));
-  bzero (reg_where_born, nregs * sizeof (int));
-
-  reg_crosses_blocks = (char *) alloca (nregs);
-  bzero (reg_crosses_blocks, nregs);
+  bzero ((char *) reg_where_born, nregs * sizeof (int));
 
   reg_order = (int *) alloca (nregs * sizeof (int));
-  bzero (reg_order, nregs * sizeof (int));
+  bzero ((char *) reg_order, nregs * sizeof (int));
+
+  regs_change_size = (char *) alloca (nregs * sizeof (char));
+  bzero ((char *) regs_change_size, nregs * sizeof (char));
 
-  reg_renumber = (short *) oballoc (nregs * sizeof (short));
+  regs_crosses_setjmp = (char *) alloca (nregs * sizeof (char));
+  bzero ((char *) regs_crosses_setjmp, nregs * sizeof (char));
+
+  /* Allocate the reg_renumber array */
+  allocate_reg_info (max_regno, FALSE, TRUE);
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     reg_renumber[i] = i;
 
-  for (i = FIRST_VIRTUAL_REGISTER; i <= LAST_VIRTUAL_REGISTER; i++)
-    reg_renumber[i] = -1;
+  after_insn_hard_regs
+    = (HARD_REG_SET *) alloca (max_suid * sizeof (HARD_REG_SET));
 
-  after_insn_hard_regs = (HARD_REG_SET *) alloca (max_uid * sizeof (HARD_REG_SET));
-  bzero (after_insn_hard_regs, max_uid * sizeof (HARD_REG_SET));
+  bzero ((char *) after_insn_hard_regs, max_suid * sizeof (HARD_REG_SET));
 
   /* Allocate and zero out many data structures
      that will record the data from lifetime analysis.  */
@@ -190,9 +195,7 @@ stupid_life_analysis (f, nregs, file)
   allocate_for_life_analysis ();
 
   for (i = 0; i < max_regno; i++)
-    {
-      reg_n_deaths[i] = 1;
-    }
+    REG_N_DEATHS (i) = 1;
 
   bzero (regs_live, nregs);
 
@@ -209,14 +212,24 @@ stupid_life_analysis (f, nregs, file)
     {
       register HARD_REG_SET *p = after_insn_hard_regs + INSN_SUID (insn);
 
-      /* Copy the info in regs_live
-        into the element of after_insn_hard_regs
+      /* Copy the info in regs_live into the element of after_insn_hard_regs
         for the current position in the rtl code.  */
 
       for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
        if (regs_live[i])
          SET_HARD_REG_BIT (*p, i);
 
+      /* Update which hard regs are currently live
+        and also the birth and death suids of pseudo regs
+        based on the pattern of this insn.  */
+
+      if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+       stupid_mark_refs (PATTERN (insn), insn);
+
+      if (GET_CODE (insn) == NOTE
+         && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
+       last_setjmp_suid = INSN_SUID (insn);
+
       /* Mark all call-clobbered regs as live after each call insn
         so that a pseudo whose life span includes this insn
         will not go in one of them.
@@ -228,26 +241,15 @@ stupid_life_analysis (f, nregs, file)
          last_call_suid = INSN_SUID (insn);
          IOR_HARD_REG_SET (after_insn_hard_regs[last_call_suid],
                            call_used_reg_set);
+
          for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
            if (call_used_regs[i])
              regs_live[i] = 0;
-       }
-
-      if (GET_CODE (insn) == JUMP_INSN)
-       last_jump_suid = INSN_SUID (insn);
-
-      if (GET_CODE (insn) == CODE_LABEL)
-       last_label_suid = INSN_SUID (insn);
-
-      /* Update which hard regs are currently live
-        and also the birth and death suids of pseudo regs
-        based on the pattern of this insn.  */
 
-      if (GET_CODE (insn) == INSN
-         || GET_CODE (insn) == CALL_INSN
-         || GET_CODE (insn) == JUMP_INSN)
-       {
-         stupid_mark_refs (PATTERN (insn), insn);
+         /* It is important that this be done after processing the insn's
+            pattern because we want the function result register to still
+            be live if it's also used to pass arguments.  */
+         stupid_mark_refs (CALL_INSN_FUNCTION_USAGE (insn), insn);
        }
     }
 
@@ -265,35 +267,29 @@ stupid_life_analysis (f, nregs, file)
   for (i = LAST_VIRTUAL_REGISTER + 1; i < max_regno; i++)
     {
       register int r = reg_order[i];
-      enum reg_class class;
 
-      /* Some regnos disappear from the rtl.  Ignore them to avoid crash.  */
-      if (regno_reg_rtx[r] == 0)
+      /* Some regnos disappear from the rtl.  Ignore them to avoid crash. 
+        Also don't allocate registers that cross a setjmp.  */
+      if (regno_reg_rtx[r] == 0 || regs_crosses_setjmp[r])
        continue;
 
       /* Now find the best hard-register class for this pseudo register */
       if (N_REG_CLASSES > 1)
-       {
-         class = reg_preferred_class (r);
-
-         reg_renumber[r] = stupid_find_reg (reg_n_calls_crossed[r], class,
-                                            PSEUDO_REGNO_MODE (r),
-                                            reg_where_born[r],
-                                            reg_where_dead[r],
-                                            reg_crosses_blocks[r]);
-       }
-      else
-       reg_renumber[r] = -1;
+       reg_renumber[r] = stupid_find_reg (REG_N_CALLS_CROSSED (r), 
+                                          reg_preferred_class (r),
+                                          PSEUDO_REGNO_MODE (r),
+                                          reg_where_born[r],
+                                          reg_where_dead[r],
+                                          regs_change_size[r]);
 
-      /* If no reg available in that class,
-        try any reg.  */
-      if (reg_renumber[r] == -1)
-       reg_renumber[r] = stupid_find_reg (reg_n_calls_crossed[r],
-                                          GENERAL_REGS,
+      /* If no reg available in that class, try alternate class.  */
+      if (reg_renumber[r] == -1 && reg_alternate_class (r) != NO_REGS)
+       reg_renumber[r] = stupid_find_reg (REG_N_CALLS_CROSSED (r),
+                                          reg_alternate_class (r),
                                           PSEUDO_REGNO_MODE (r),
                                           reg_where_born[r],
                                           reg_where_dead[r],
-                                          reg_crosses_blocks[r]);
+                                          regs_change_size[r]);
     }
 
   if (file)
@@ -305,18 +301,21 @@ stupid_life_analysis (f, nregs, file)
 
 static int
 stupid_reg_compare (r1p, r2p)
-     int *r1p, *r2p;
+     const GENERIC_PTR r1p;
+     const GENERIC_PTR r2p;
 {
-  register int r1 = *r1p, r2 = *r2p;
+  register int r1 = *(int *)r1p, r2 = *(int *)r2p;
   register int len1 = reg_where_dead[r1] - reg_where_born[r1];
   register int len2 = reg_where_dead[r2] - reg_where_born[r2];
   int tem;
 
   tem = len2 - len1;
-  if (tem != 0) return tem;
+  if (tem != 0)
+    return tem;
 
-  tem = reg_n_refs[r1] - reg_n_refs[r2];
-  if (tem != 0) return tem;
+  tem = REG_N_REFS (r1) - REG_N_REFS (r2);
+  if (tem != 0)
+    return tem;
 
   /* If regs are equally good, sort by regno,
      so that the results of qsort leave nothing to chance.  */
@@ -326,24 +325,25 @@ stupid_reg_compare (r1p, r2p)
 /* Find a block of SIZE words of hard registers in reg_class CLASS
    that can hold a value of machine-mode MODE
      (but actually we test only the first of the block for holding MODE)
-   currently free from after insn whose suid is BIRTH
-   through the insn whose suid is DEATH,
+   currently free from after insn whose suid is BORN_INSN
+   through the insn whose suid is DEAD_INSN,
    and return the number of the first of them.
    Return -1 if such a block cannot be found.
 
    If CALL_PRESERVED is nonzero, insist on registers preserved
    over subroutine calls, and return -1 if cannot find such.
-   If CROSSES_BLOCKS is nonzero, reject registers for which
-   PRESERVE_DEATH_INFO_REGNO_P is true.  */
+
+   If CHANGES_SIZE is nonzero, it means this register was used as the
+   operand of a SUBREG that changes its size.  */
 
 static int
 stupid_find_reg (call_preserved, class, mode,
-                born_insn, dead_insn, crosses_blocks)
+                born_insn, dead_insn, changes_size)
      int call_preserved;
      enum reg_class class;
      enum machine_mode mode;
      int born_insn, dead_insn;
-     int crosses_blocks;
+     int changes_size;
 {
   register int i, ins;
 #ifdef HARD_REG_SET
@@ -354,6 +354,13 @@ stupid_find_reg (call_preserved, class, mode,
   static struct {int from, to; } eliminables[] = ELIMINABLE_REGS;
 #endif
 
+  /* If this register's life is more than 5,000 insns, we probably
+     can't allocate it, so don't waste the time trying.  This avoids
+     quadratic behavior on programs that have regularly-occurring
+     SAVE_EXPRs.  */
+  if (dead_insn > born_insn + 5000)
+    return -1;
+
   COPY_HARD_REG_SET (used,
                     call_preserved ? call_used_reg_set : fixed_reg_set);
 
@@ -372,6 +379,12 @@ stupid_find_reg (call_preserved, class, mode,
 
   IOR_COMPL_HARD_REG_SET (used, reg_class_contents[(int) class]);
 
+#ifdef CLASS_CANNOT_CHANGE_SIZE
+  if (changes_size)
+    IOR_HARD_REG_SET (used,
+                     reg_class_contents[(int) CLASS_CANNOT_CHANGE_SIZE]);
+#endif
+
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     {
 #ifdef REG_ALLOC_ORDER
@@ -380,13 +393,6 @@ stupid_find_reg (call_preserved, class, mode,
       int regno = i;
 #endif
 
-      /* If we need reasonable death info on this hard reg,
-        don't use it for anything whose life spans a label or a jump.  */
-#ifdef PRESERVE_DEATH_INFO_REGNO_P
-      if (PRESERVE_DEATH_INFO_REGNO_P (regno)
-         && crosses_blocks)
-       continue;
-#endif
       /* If a register has screwy overlap problems,
         don't use it at all if not optimizing.
         Actually this is only for the 387 stack register,
@@ -414,10 +420,11 @@ stupid_find_reg (call_preserved, class, mode,
              return regno;
            }
 #ifndef REG_ALLOC_ORDER
-         i += j;                       /* Skip starting points we know will lose */
+         i += j;               /* Skip starting points we know will lose */
 #endif
        }
     }
+
   return -1;
 }
 \f
@@ -429,34 +436,51 @@ static void
 stupid_mark_refs (x, insn)
      rtx x, insn;
 {
-  register RTX_CODE code = GET_CODE (x);
+  register RTX_CODE code;
   register char *fmt;
   register int regno, i;
 
+  if (x == 0)
+    return;
+
+  code = GET_CODE (x);
+
   if (code == SET || code == CLOBBER)
     {
-      if (SET_DEST (x) != 0 && GET_CODE (SET_DEST (x)) == REG)
+      if (SET_DEST (x) != 0
+         && (GET_CODE (SET_DEST (x)) == REG
+             || (GET_CODE (SET_DEST (x)) == SUBREG
+                 && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG
+                 && (REGNO (SUBREG_REG (SET_DEST (x)))
+                     >= FIRST_PSEUDO_REGISTER))))
        {
          /* Register is being assigned.  */
-         regno = REGNO (SET_DEST (x));
+         /* If setting a SUBREG, we treat the entire reg as being set.  */
+         if (GET_CODE (SET_DEST (x)) == SUBREG)
+           regno = REGNO (SUBREG_REG (SET_DEST (x)));
+         else
+           regno = REGNO (SET_DEST (x));
 
          /* For hard regs, update the where-live info.  */
          if (regno < FIRST_PSEUDO_REGISTER)
            {
              register int j
                = HARD_REGNO_NREGS (regno, GET_MODE (SET_DEST (x)));
+
              while (--j >= 0)
                {
                  regs_ever_live[regno+j] = 1;
                  regs_live[regno+j] = 0;
+
                  /* The following line is for unused outputs;
                     they do get stored even though never used again.  */
-                 MARK_LIVE_AFTER (insn, regno);
+                 MARK_LIVE_AFTER (insn, regno+j);
+
                  /* When a hard reg is clobbered, mark it in use
                     just before this insn, so it is live all through.  */
                  if (code == CLOBBER && INSN_SUID (insn) > 0)
                    SET_HARD_REG_BIT (after_insn_hard_regs[INSN_SUID (insn) - 1],
-                                     regno);
+                                     regno+j);
                }
            }
          /* For pseudo regs, record where born, where dead, number of
@@ -470,24 +494,42 @@ stupid_mark_refs (x, insn)
              int where_born = INSN_SUID (insn) - (code == CLOBBER);
 
              reg_where_born[regno] = where_born;
+
              /* The reg must live at least one insn even
                 in it is never again used--because it has to go
                 in SOME hard reg.  Mark it as dying after the current
                 insn so that it will conflict with any other outputs of
                 this insn.  */
              if (reg_where_dead[regno] < where_born + 2)
-               reg_where_dead[regno] = where_born + 2;
+               {
+                 reg_where_dead[regno] = where_born + 2;
+                 regs_live[regno] = 1;
+               }
 
              /* Count the refs of this reg.  */
-             reg_n_refs[regno]++;
+             REG_N_REFS (regno)++;
 
              if (last_call_suid < reg_where_dead[regno])
-               reg_n_calls_crossed[regno] += 1;
-             if (last_jump_suid < reg_where_dead[regno]
-                 || last_label_suid < reg_where_dead[regno])
-               reg_crosses_blocks[regno] = 1;
+               REG_N_CALLS_CROSSED (regno) += 1;
+
+             if (last_setjmp_suid < reg_where_dead[regno])
+               regs_crosses_setjmp[regno] = 1;
+
+             /* If this register is only used in this insn and is only
+                set, mark it unused.  We have to do this even when not 
+                optimizing so that MD patterns which count on this
+                behavior (e.g., it not causing an output reload on
+                an insn setting CC) will operate correctly.  */
+             if (GET_CODE (SET_DEST (x)) == REG
+                 && REGNO_FIRST_UID (regno) == INSN_UID (insn)
+                 && REGNO_LAST_UID (regno) == INSN_UID (insn)
+                 && (code == CLOBBER || ! reg_mentioned_p (SET_DEST (x),
+                                                           SET_SRC (x))))
+               REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_UNUSED,
+                                           SET_DEST (x), REG_NOTES (insn));
            }
        }
+
       /* Record references from the value being set,
         or from addresses in the place being set if that's not a reg.
         If setting a SUBREG, we treat the entire reg as *used*.  */
@@ -500,9 +542,18 @@ stupid_mark_refs (x, insn)
       return;
     }
 
+  else if (code == SUBREG
+          && GET_CODE (SUBREG_REG (x)) == REG
+          && REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER
+          && (GET_MODE_SIZE (GET_MODE (x))
+              != GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+          && (INTEGRAL_MODE_P (GET_MODE (x))
+              || INTEGRAL_MODE_P (GET_MODE (SUBREG_REG (x)))))
+    regs_change_size[REGNO (SUBREG_REG (x))] = 1;
+
   /* Register value being used, not set.  */
 
-  if (code == REG)
+  else if (code == REG)
     {
       regno = REGNO (x);
       if (regno < FIRST_PSEUDO_REGISTER)
@@ -520,7 +571,7 @@ stupid_mark_refs (x, insn)
          /* Pseudo reg: record first use, last use and number of uses.  */
 
          reg_where_born[regno] = INSN_SUID (insn);
-         reg_n_refs[regno]++;
+         REG_N_REFS (regno)++;
          if (regs_live[regno] == 0)
            {
              regs_live[regno] = 1;