]> gcc.gnu.org Git - gcc.git/blobdiff - gcc/stupid.c
Merge in gcc2-ss-010999
[gcc.git] / gcc / stupid.c
index 74ad7be2cf4a9369fab333ae9ae7331b3f1da73d..42b57106d6df567a5543758db03d5f9f6f4b298e 100644 (file)
@@ -1,5 +1,5 @@
 /* Dummy data flow analysis for GNU compiler in nonoptimizing mode.
-   Copyright (C) 1987, 1991, 1994, 1995 Free Software Foundation, Inc.
+   Copyright (C) 1987, 91, 94-96, 1998, 1999 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -22,7 +22,7 @@ Boston, MA 02111-1307, USA.  */
 /* This file performs stupid register allocation, which is used
    when cc1 gets the -noreg switch (which is when cc does not get -O).
 
-   Stupid register allocation goes in place of the the flow_analysis,
+   Stupid register allocation goes in place of the flow_analysis,
    local_alloc and global_alloc passes.  combine_instructions cannot
    be done with stupid allocation because the data flow info that it needs
    is not computed here.
@@ -42,12 +42,18 @@ Boston, MA 02111-1307, USA.  */
    pseudo reg is computed.  Then the pseudo regs are ordered by priority
    and assigned hard regs in priority order.  */
 
-#include <stdio.h>
 #include "config.h"
+#include "system.h"
+
 #include "rtl.h"
 #include "hard-reg-set.h"
+#include "basic-block.h"
 #include "regs.h"
+#include "function.h"
+#include "insn-config.h"
+#include "reload.h"
 #include "flags.h"
+#include "toplev.h"
 \f
 /* Vector mapping INSN_UIDs to suids.
    The suids are like uids but increase monotonically always.
@@ -65,14 +71,31 @@ static int *uid_suid;
 
 static int last_call_suid;
 
+/* Record the suid of the last NOTE_INSN_SETJMP
+   so we can tell whether a pseudo reg crosses any setjmp.  */
+
+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.  */
 
 static int *reg_where_dead;
 
+/* Likewise, but point to the insn_chain structure of the insn at which
+   the reg dies.  */
+static struct insn_chain **reg_where_dead_chain;
+
 /* Element N is suid of insn where life span of pseudo reg N begins.  */
+static int *reg_where_born_exact;
 
-static int *reg_where_born;
+/* Element N is 1 if the birth of pseudo reg N is due to a CLOBBER, 
+   0 otherwise.  */
+static int *reg_where_born_clobber;
+
+/* Return the suid of the insn where the register is born, or the suid
+   of the insn before if the birth is due to a CLOBBER.  */
+#define REG_WHERE_BORN(N) \
+  (reg_where_born_exact[(N)] - reg_where_born_clobber[(N)])
 
 /* Numbers of pseudo-regs to be allocated, highest priority first.  */
 
@@ -88,6 +111,10 @@ static char *regs_live;
 
 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;
@@ -97,10 +124,46 @@ 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 int stupid_reg_compare  PROTO((int *, int *));
+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));
+static void stupid_mark_refs   PROTO((rtx, struct insn_chain *));
+static void find_clobbered_regs        PROTO((rtx, rtx));
+\f
+/* For communication between stupid_life_analysis and find_clobbered_regs.  */
+static struct insn_chain *current_chain;
+
+/* This function, called via note_stores, marks any hard registers that are
+   clobbered in an insn as being live in the live_after and live_before fields
+   of the appropriate insn_chain structure.  */
+
+static void
+find_clobbered_regs (reg, setter)
+     rtx reg, setter;
+{
+  int regno, nregs;
+  if (setter == 0 || GET_CODE (setter) != CLOBBER)
+    return;
+
+  if (GET_CODE (reg) == SUBREG)
+    reg = SUBREG_REG (reg);
+
+  if (GET_CODE (reg) != REG)
+    return;
+  regno = REGNO (reg);
+  if (regno >= FIRST_PSEUDO_REGISTER)
+    return;
+
+  if (GET_MODE (reg) == VOIDmode)
+    abort ();
+  else
+    nregs = HARD_REGNO_NREGS (regno, GET_MODE (reg));
+  while (nregs-- > 0)
+    {
+      SET_REGNO_REG_SET (current_chain->live_after, regno);
+      SET_REGNO_REG_SET (current_chain->live_before, regno++);
+    }
+}
 \f
 /* Stupid life analysis is for the case where only variables declared
    `register' go in registers.  For this case, we mark all
@@ -120,9 +183,11 @@ stupid_life_analysis (f, nregs, file)
   register rtx last, insn;
   int max_uid, max_suid;
 
+  current_function_has_computed_jump = 0;
+
   bzero (regs_ever_live, sizeof regs_ever_live);
 
-  regs_live = (char *) alloca (nregs);
+  regs_live = (char *) xmalloc (nregs);
 
   /* First find the last real insn, and count the number of insns,
      and assign insns their suids.  */
@@ -132,7 +197,7 @@ stupid_life_analysis (f, nregs, file)
       i = INSN_UID (insn);
 
   max_uid = i + 1;
-  uid_suid = (int *) alloca ((i + 1) * sizeof (int));
+  uid_suid = (int *) xmalloc ((i + 1) * sizeof (int));
 
   /* Compute the mapping from uids to suids.
      Suids are numbers assigned to insns, like uids,
@@ -148,43 +213,38 @@ stupid_life_analysis (f, nregs, file)
     }
 
   last_call_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 ((char *) reg_where_dead, nregs * sizeof (int));
-
-  reg_where_born = (int *) alloca (nregs * sizeof (int));
-  bzero ((char *) reg_where_born, nregs * sizeof (int));
-
-  reg_order = (int *) alloca (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));
+  reg_where_dead = (int *) xcalloc (nregs, sizeof (int));
+  reg_where_born_exact = (int *) xcalloc (nregs, sizeof (int));
+  reg_where_born_clobber = (int *) xcalloc (nregs, sizeof (int));
+  reg_where_dead_chain = (struct insn_chain **)
+    xcalloc (nregs, sizeof (struct insn_chain *));
+  reg_order = (int *) xcalloc (nregs, sizeof (int));
+  regs_change_size = (char *) xcalloc (nregs, sizeof (char));
+  regs_crosses_setjmp = (char *) xcalloc (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 < max_regno; i++)
-    reg_renumber[i] = -1;
-
-  after_insn_hard_regs
-    = (HARD_REG_SET *) alloca (max_suid * sizeof (HARD_REG_SET));
-
-  bzero ((char *) after_insn_hard_regs, max_suid * sizeof (HARD_REG_SET));
+  after_insn_hard_regs =
+    (HARD_REG_SET *) xcalloc (max_suid, sizeof (HARD_REG_SET));
 
   /* Allocate and zero out many data structures
      that will record the data from lifetime analysis.  */
 
-  allocate_for_life_analysis ();
+  allocate_reg_life_data ();
+  allocate_bb_life_data ();
 
   for (i = 0; i < max_regno; i++)
-    reg_n_deaths[i] = 1;
+    REG_N_DEATHS (i) = 1;
 
   bzero (regs_live, nregs);
 
@@ -195,11 +255,15 @@ stupid_life_analysis (f, nregs, file)
      Also find where each hard register is live
      and record that info in after_insn_hard_regs.
      regs_live[I] is 1 if hard reg I is live
-     at the current point in the scan.  */
+     at the current point in the scan.  
+   
+     Build reload_insn_chain while we're walking the insns.  */
 
+  reload_insn_chain = 0;
   for (insn = last; insn; insn = PREV_INSN (insn))
     {
       register HARD_REG_SET *p = after_insn_hard_regs + INSN_SUID (insn);
+      struct insn_chain *chain;
 
       /* Copy the info in regs_live into the element of after_insn_hard_regs
         for the current position in the rtl code.  */
@@ -208,34 +272,83 @@ stupid_life_analysis (f, nregs, file)
        if (regs_live[i])
          SET_HARD_REG_BIT (*p, i);
 
+      if (GET_CODE (insn) != NOTE && GET_CODE (insn) != BARRIER)
+       {
+         chain = new_insn_chain ();
+         if (reload_insn_chain)
+           reload_insn_chain->prev = chain;
+         chain->next = reload_insn_chain;
+         chain->prev = 0;
+         reload_insn_chain = chain;
+         chain->block = 0;
+         chain->insn = insn;
+         for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+           if (regs_live[i])
+             SET_REGNO_REG_SET (chain->live_before, 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);
+       stupid_mark_refs (PATTERN (insn), chain);
+
+      if (GET_CODE (insn) == NOTE
+         && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
+       last_setjmp_suid = INSN_SUID (insn);
+
+      /* Mark all call-clobbered regs as dead after each call insn so that
+        a pseudo whose life span includes this insn will not go in one of
+        them.  If the function contains a non-local goto, mark all hard
+        registers dead (except for stack related bits).
 
-      /* 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.
         Then mark those regs as all dead for the continuing scan
         of the insns before the call.  */
 
       if (GET_CODE (insn) == CALL_INSN)
        {
          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 (current_function_has_nonlocal_label)
+           {
+             IOR_COMPL_HARD_REG_SET (after_insn_hard_regs[last_call_suid],
+                                     fixed_reg_set);
+             for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+               if (! fixed_regs[i])
+                 regs_live[i] = 0;
+           }
+         else
+           {
+             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;
+           }
 
          /* 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);
+         stupid_mark_refs (CALL_INSN_FUNCTION_USAGE (insn), chain);
        }
+
+      if (GET_CODE (insn) != NOTE && GET_CODE (insn) != BARRIER)
+       {         
+         for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+           if (regs_live[i])
+             SET_REGNO_REG_SET (chain->live_after, i);
+
+         /* The regs_live array doesn't say anything about hard registers
+            clobbered by this insn.  So we need an extra pass over the
+            pattern.  */
+         current_chain = chain;
+         if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+           note_stores (PATTERN (insn), find_clobbered_regs);
+       }
+
+      if (GET_CODE (insn) == JUMP_INSN && computed_jump_p (insn))
+       current_function_has_computed_jump = 1;
     }
 
   /* Now decide the order in which to allocate the pseudo registers.  */
@@ -253,31 +366,76 @@ stupid_life_analysis (f, nregs, file)
     {
       register int r = reg_order[i];
 
-      /* 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, or live across
+        a call if this function receives a nonlocal goto.
+        Also ignore registers we didn't see during the scan.  */
+      if (regno_reg_rtx[r] == 0 || regs_crosses_setjmp[r]
+         || (reg_where_born_exact[r] == 0 && reg_where_dead[r] == 0)
+         || (REG_N_CALLS_CROSSED (r) > 0 
+             && current_function_has_nonlocal_label))
        continue;
 
       /* Now find the best hard-register class for this pseudo register */
       if (N_REG_CLASSES > 1)
-       reg_renumber[r] = stupid_find_reg (reg_n_calls_crossed[r]
+       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_BORN (r),
                                           reg_where_dead[r],
                                           regs_change_size[r]);
 
       /* 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_renumber[r] = stupid_find_reg (REG_N_CALLS_CROSSED (r),
                                           reg_alternate_class (r),
                                           PSEUDO_REGNO_MODE (r),
-                                          reg_where_born[r],
+                                          REG_WHERE_BORN (r),
                                           reg_where_dead[r],
                                           regs_change_size[r]);
     }
 
+  /* Fill in the pseudo reg life information into the insn chain.  */
+  for (i = LAST_VIRTUAL_REGISTER + 1; i < max_regno; i++)
+    {
+      struct insn_chain *chain;
+      int regno;
+
+      regno = reg_renumber[i];
+      if (regno < 0)
+       continue;
+
+      chain = reg_where_dead_chain[i];
+      if (reg_where_dead[i] > INSN_SUID (chain->insn))
+       SET_REGNO_REG_SET (chain->live_after, i);
+
+      while (INSN_SUID (chain->insn) > reg_where_born_exact[i])
+       {
+         SET_REGNO_REG_SET (chain->live_before, i);
+         chain = chain->prev;
+         if (!chain)
+           break;
+         SET_REGNO_REG_SET (chain->live_after, i);
+       }
+
+      if (INSN_SUID (chain->insn) == reg_where_born_exact[i]
+         && reg_where_born_clobber[i])
+       SET_REGNO_REG_SET (chain->live_before, i);
+    }
+
   if (file)
     dump_flow_info (file);
+
+  free (regs_live);
+  free (uid_suid);
+  free (reg_where_dead);
+  free (reg_where_born_exact);
+  free (reg_where_born_clobber);
+  free (reg_where_dead_chain);
+  free (reg_order);
+  free (regs_change_size);
+  free (regs_crosses_setjmp);
+  free (after_insn_hard_regs);
 }
 
 /* Comparison function for qsort.
@@ -285,18 +443,19 @@ 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 len1 = reg_where_dead[r1] - reg_where_born[r1];
-  register int len2 = reg_where_dead[r2] - reg_where_born[r2];
+  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;
 
-  tem = reg_n_refs[r1] - reg_n_refs[r2];
+  tem = REG_N_REFS (r1) - REG_N_REFS (r2);
   if (tem != 0)
     return tem;
 
@@ -308,8 +467,8 @@ 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.
 
@@ -326,7 +485,7 @@ stupid_find_reg (call_preserved, class, mode,
      enum reg_class class;
      enum machine_mode mode;
      int born_insn, dead_insn;
-     int changes_size;
+     int changes_size ATTRIBUTE_UNUSED;
 {
   register int i, ins;
 #ifdef HARD_REG_SET
@@ -337,11 +496,18 @@ 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);
 
 #ifdef ELIMINABLE_REGS
-  for (i = 0; i < sizeof eliminables / sizeof eliminables[0]; i++)
+  for (i = 0; i < (int)(sizeof eliminables / sizeof eliminables[0]); i++)
     SET_HARD_REG_BIT (used, eliminables[i].from);
 #if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
   SET_HARD_REG_BIT (used, HARD_FRAME_POINTER_REGNUM);
@@ -353,6 +519,12 @@ stupid_find_reg (call_preserved, class, mode,
   for (ins = born_insn; ins < dead_insn; ins++)
     IOR_HARD_REG_SET (used, after_insn_hard_regs[ins]);
 
+#ifdef STACK_REGS
+  if (current_function_has_computed_jump)
+    for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++)
+      SET_HARD_REG_BIT (used, i);
+#endif
+  
   IOR_COMPL_HARD_REG_SET (used, reg_class_contents[(int) class]);
 
 #ifdef CLASS_CANNOT_CHANGE_SIZE
@@ -409,12 +581,14 @@ stupid_find_reg (call_preserved, class, mode,
    INSN is the current insn, supplied so we can find its suid.  */
 
 static void
-stupid_mark_refs (x, insn)
-     rtx x, insn;
+stupid_mark_refs (x, chain)
+     rtx x;
+     struct insn_chain *chain;
 {
   register RTX_CODE code;
-  register char *fmt;
+  register const char *fmt;
   register int regno, i;
+  rtx insn = chain->insn;
 
   if (x == 0)
     return;
@@ -469,7 +643,11 @@ stupid_mark_refs (x, insn)
                 the clobbering insn.  When setting, just after.  */
              int where_born = INSN_SUID (insn) - (code == CLOBBER);
 
-             reg_where_born[regno] = where_born;
+             reg_where_born_exact[regno] = INSN_SUID (insn);
+             reg_where_born_clobber[regno] = (code == CLOBBER);
+
+             if (reg_where_dead_chain[regno] == 0)
+               reg_where_dead_chain[regno] = chain;
 
              /* The reg must live at least one insn even
                 in it is never again used--because it has to go
@@ -483,10 +661,29 @@ stupid_mark_refs (x, insn)
                }
 
              /* 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;
+               REG_N_CALLS_CROSSED (regno) += 1;
+
+             if (last_setjmp_suid < reg_where_dead[regno])
+               regs_crosses_setjmp[regno] = 1;
+
+             /* If this register is clobbered or it 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
+                 && (code == CLOBBER
+                     || (REGNO_FIRST_UID (regno) == INSN_UID (insn)
+                         && REGNO_LAST_UID (regno) == INSN_UID (insn)
+                         && ! reg_mentioned_p (SET_DEST (x),
+                                               SET_SRC (x)))))
+               REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_UNUSED,
+                                                     SET_DEST (x),
+                                                     REG_NOTES (insn));
            }
        }
 
@@ -495,9 +692,9 @@ stupid_mark_refs (x, insn)
         If setting a SUBREG, we treat the entire reg as *used*.  */
       if (code == SET)
        {
-         stupid_mark_refs (SET_SRC (x), insn);
+         stupid_mark_refs (SET_SRC (x), chain);
          if (GET_CODE (SET_DEST (x)) != REG)
-           stupid_mark_refs (SET_DEST (x), insn);
+           stupid_mark_refs (SET_DEST (x), chain);
        }
       return;
     }
@@ -530,12 +727,14 @@ 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_where_born_exact[regno] = INSN_SUID (insn);
+         reg_where_born_clobber[regno] = 0;
+         REG_N_REFS (regno)++;
          if (regs_live[regno] == 0)
            {
              regs_live[regno] = 1;
              reg_where_dead[regno] = INSN_SUID (insn);
+             reg_where_dead_chain[regno] = chain;
            }
        }
       return;
@@ -547,12 +746,12 @@ stupid_mark_refs (x, insn)
   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
     {
       if (fmt[i] == 'e')
-       stupid_mark_refs (XEXP (x, i), insn);
+       stupid_mark_refs (XEXP (x, i), chain);
       if (fmt[i] == 'E')
        {
          register int j;
          for (j = XVECLEN (x, i) - 1; j >= 0; j--)
-           stupid_mark_refs (XVECEXP (x, i, j), insn);
+           stupid_mark_refs (XVECEXP (x, i, j), chain);
        }
     }
 }
This page took 0.047781 seconds and 5 git commands to generate.